代码改变世界

Data structure - Sort & quick sort 小结及leetcode相关题目

2023-10-21 02:48  Johnson_强生仔仔  阅读(16)  评论(0编辑  收藏  举报

Sort 主要有以下几种常见的sort, 面试中最有可能考的是quick sort, 关于k largest or 什么相关的。

  • Bubble sort
  • Insertion sort
  • Merge sort
  • Quicksort
  • Selection sort
  • Counting sort
  • Bucket sort

 

1. Bubble sort, 可以理解为每一次找最大的值放在最后面,然后再找第二个最大的值放在倒数第二个位置上,以此类推。

T: O(n ^ 2) ; S: O(1)

def bubbleSort(self, nums):
    n = len(nums)
    for i in range(n - 1, 0, -1): # from n - 1 to 1, because j starts from 0
        swapped = False
        for j in range(0, i):
            if nums[j] > nums[j + 1]:
                swapped = True
                nums[j], nums[j + 1] = nums[j + 1], nums[j]
        if not swapped:
            return

 

2. Insertion Sort, 将arr分为左边(sorted array)和右边(unsorted array),然后依次将unsorted array的元素insert到sorted array里面。

T: O(n ^ 2), S: O(1)

同样的思路 [LeetCode] 147. Insertion Sort List_Middle tag: Linked List

 

3. Merge Sort, 将array 分成两半,然后在merge两个sorted array, 每次merge是O(n)的operation, 然后总共可以分O(lgn) 次,所以

T: O(n * lgn). S: O(n) worst case

同样的思路[LeetCode] 148. Sort List_Middle tag: Linked List

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        n = len(nums)
        if n < 2: return nums
        mid = n //2
        left= self.sortArray(nums[:mid])
        right = self.sortArray(nums[mid:])
        return self.mergeTwoLists(left, right)
    

    def mergeTwoLists(self, nums1, nums2):
        n1, n2 = len(nums1), len(nums2)
        i1, i2 = 0, 0
        nums = []
        while i1 < n1 and i2 < n2:
            if nums1[i1] < nums2[i2]:
                nums.append(nums1[i1])
                i1 += 1
            else:
                nums.append(nums2[i2])
                i2 += 1
        while i1 < n1:
            nums.append(nums1[i1])
            i1 += 1
        while i2 < n2:
            nums.append(nums2[i2])
            i2 += 1
        return nums

 

4. Quick sort

T:

- worst case O(n ^ 2), 如果每一次pivot都选到的是最大的或者最小的,然后就相当于1 + 2 + 3 + .. n + 1 => O(n ^ 2)

- average O(n lgn)  比如每次如果是分为1/4 和3/4, 最后是n lg (3/4) ^ n , 用big O的话就还是O(n lgn)

- best O(n lgn) 当每次pivot都能选到靠中间的位置,实际上就跟merge sort很像了。

S: O(1)

quick sort的主要思路是partition, 就是选一个element作为pivot,比如总是选最右边的element,然后将array走一遍,使得最左边的subarray都是小于或者等于pivot的值,接着都是大于pivot的值,最后将第一个大于pivot的element和pivot互换。然后返回这个pivot的index,接着再recursively 去sort pivot左边的array和pivot右边的array。

logic, sort nums[p: r + 1]

  1. q = j =  p
  2. while loop , j < r
  3. if nums[j] <= nums[r] (which is pivot), swap(p, j) and p += 1, j += 1; 
  4. else: j += 1
  5. 最后out of loop, 当j == r之后,我们将swap(p, r) ==》 相当于将第一个大于pivot的值和pivot换了一下
  6. return p (因为这个就是pivot应该在的位置)
# the logic behind of quick sort
class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        if len(nums) <= 1:
            return nums
        pivot = random.choice(nums)
        lt = [v for v in nums if v < pivot]
        eq = [v for v in nums if v == pivot]
        gt = [v for v in nums if v > pivot]
        return self.sortArray(lt) + eq + self.sortArray(gt)