第五章-排序算法模板(快速排序、归并排序、快速选择)

排序算法模板(快速排序、归并排序)

记录一下现在学过的算法模板,免得后面又忘了然后找不到……

Quick Sort 快速排序

题目: Sort Colors

class Solution:
    """
    @param nums: A list of integer which is 0, 1 or 2 
    @return: nothing
    """
    def sortColors(self, nums):
        # write your code here
        if not nums:
            return None
        
        return self.quicksort(nums, 0, len(nums) - 1)
        
    def quicksort(self, nums, start, end):
        if start >= end:
            return
        
        left, right = start, end
        pivot = nums[(start + end) // 2]
        
        while left <= right:
            while left <= right and nums[left] < pivot:
                left += 1 
            while left <= right and nums[right] > pivot:
                right -= 1 
            if left <= right:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1 
                right -= 1 
        
        self.quicksort(nums, start, right)
        self.quicksort(nums, left, end)

助教的QS模板:

def quickSort(self, A, start, end):
    if start >= end:
        return
    
    left, right = start, end
    pivot = A[(start + end) // 2];

    while left <= right:
        while left <= right and A[left] < pivot:
            left += 1
        
        while left <= right and A[right] > pivot:
            right -= 1
        
        if left <= right:
            A[left], A[right] = A[right], A[left]
            left += 1
            right -= 1
    
    self.quickSort(A, start, right)
    self.quickSort(A, left, end)

Merge Sort 归并排序

题目:Sort Integers

class Solution:
    """
    @param A: an integer array
    @return: nothing
    """
    def sortIntegers2(self, A):
        temp = [0] * len(A)
        self.merge_sort(A, 0, len(A) - 1, temp)
        
    def merge_sort(self, A, start, end, temp):
        if start >= end:
            return 
        
        left, right = start, end
        mid = (start + end) // 2
        self.merge_sort(A, start, mid, temp)
        self.merge_sort(A, mid + 1, end, temp)
        self.merge(A, start, mid, end, temp)

    def merge(self, A, start, mid, end, temp):
        left, right = start, mid + 1
        index = start
        while left <= mid and right <= end:
            if A[left] < A[right]:
                temp[index] = A[left]
                index += 1
                left += 1
            else:
                temp[index] = A[right]
                index += 1
                right += 1
        while left <= mid:
            temp[index] = A[left]
            index += 1
            left += 1
        while right <= end:
            temp[index] = A[right]
            index += 1
            right += 1
        
        for i in range(start, end + 1, 1):
            A[i] = temp[i]

Quick Select 快速选择

题目: Kth Largest Element
Quick Select本质还是Quick Sort,只是它根据位置关系有选择地对一部分数组进行排序。
这道题用两种方式做,第一种是排序从小到大,第二种是排序从大到小。注意观察条件的细微差别~

老师笔记:
快速选择算法的 Partition 的实质:
快速选择/快速排序中的 partition 是 可左可右 的partition,也就是说,对于nums[i] == pivot 时,这个数字既可以放在左边,也可以放在右边。

为什么这样划分数组呢?
原因是为了避免出现类似 [1,1,1,1,1,1] 的数组中的元素,全部被分到一边的情况。我们让 nums[i] == pivot 的情况既不属于左边也不属于右边,这样就能够让 partition 之后的结果稍微平衡一些。 如果 quick select / quick sort 写成了nums[i] < pivot 在左侧,nums[i] >= pivot 在右侧这种形式,就会导致划分不平均,从而导致错误或者超时。

为什么问题《partition array》不能使用同样的代码?
对于问题《partition array》来说,题目的要求是将数组划分为两个部分,一部分满足一个条件,另外一部分不满足这个条件,所以可以严格的把 nums[i] < pivot 放在左侧,把 nums[i] >= pivot 放在右侧,这样子做完一次 partition 之后,就能够将这两部分分开。

总结
简单的说就是,quick select 和 quick sort 的 partition 目标不是将数组严格的按照 nums[i] < pivot 和nums[i] >= pivot 去拆分开,而是只要能够让左半部分 <= 右半部分即可。这样子 nums[i] == pivot 放在哪儿都无所谓,两边都可以放。

(1)排序从小到大

class Solution:
    # @param k & A a integer and an array
    # @return ans a integer
    def kthLargestElement(self, k, A):
        if not A or k < 1 or k > len(A):
            return None
        return self.partition(A, 0, len(A) - 1, len(A) - k)
        
    def partition(self, nums, start, end, k):
        """
        During the process, it's guaranteed start <= k <= end
        """
        if start == end:
            return nums[k]
            
        left, right = start, end
        pivot = nums[(start + end) // 2]
        while left <= right:
            while left <= right and nums[left] < pivot:
                left += 1
            while left <= right and nums[right] > pivot:
                right -= 1
            if left <= right:
                nums[left], nums[right] = nums[right], nums[left]
                left, right = left + 1, right - 1
                
        # left is not bigger than right
        if k <= right:
            return self.partition(nums, start, right, k)
        if k >= left:
            return self.partition(nums, left, end, k)
        
        return nums[k]

(2)排序从大到小(折腾了好久……突然某一次改对了 迷幻)

class Solution:
    """
    @param n: An integer
    @param nums: An array
    @return: the Kth largest element
    """
    def kthLargestElement(self, k, A):
        # write your code here

        if not A or k < 1 or k > len(A):
            return None
        return self.partition(A, 0, len(A) - 1, k) #这里输入的k应该指的是index!所以如果是从大到小,输入的应该是k - 1比较容易想
        
    def partition(self, nums, start, end, k):
        """
        During the process, it's guaranteed start <= k <= end
        """
        if start == end:
            return nums[start] # 也可以是nums[k]如果主函数中输入的是k-1
            
        left, right = start, end
        pivot = nums[(start + end) // 2]
        while left <= right:
            while left <= right and nums[left] > pivot:
                left += 1
            while left <= right and nums[right] < pivot:
                right -= 1
            if left <= right:
                nums[left], nums[right] = nums[right], nums[left]
                left, right = left + 1, right - 1
                
        # left is not bigger than right
        if start + k - 1 <= right: # if k <= right
            return self.partition(nums, start, right, k)
        if start + k - 1 >= left: # k <= left
            return self.partition(nums, left, end, k - (left - start)) # k
        
        return nums[right + 1] # nums[k]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值