快速排序 & 快速选择

快速排序的框架

912. 排序数组

给你一个整数数组 nums,请你将该数组升序排列。

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

partition函数
  • arr[left, right]选择中枢点arr[mid],双指针相向移动,进行交换排序
  • left > right时停止,此时有两种可能:
    • arr = [1, 2, 3, 4, 5], pivot = 3leftright都指向3,那么移动后left指向4,right指向2。两个指针隔一个数,且中间那个数就是pivot
    • arr = [1, 3, 3', 5], pivot = 3leftright分别指向3和3',那么swap后arr = [1, 3', 3, 5]left指向3, right指向3'。两个指针相邻。
  • 所以,令index1 = rightindex2 = left,返回
quickSort函数
  • arr[left, index1]都是小于等于pivotarr[index2, right]都是大于等于pivot。在这两个区间分别quickSort。
注意
  • 如果只返回一个index,可能会死循环...
  • 两个函数可以合并写
  • 也可以选择arr[left]arr[right]pivot,就有另一种写法,似乎是同向双指针,没仔细研究。
时间复杂度

画出递归树

           n                       1 * n
        /     \
    n/2         n/2               2 * n/2
  /     \      /    \
n/4   n/4     n/4  n/4            4 * n/4

1 * n + 2 * n/2 + ... + 2^i * n/2^i = n * i = nlog(n)

代码
class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        # 面试中可能不允许修改原数组,所以把nums拷贝成arr
        arr = [0] * len(nums)
        for i in range(len(nums)):
            arr[i] = nums[i]
        # 进行快速排序
        self.quickSort(arr, 0, len(nums) - 1)
        return arr

    # 归并排序函数:将nums[left...right]排序
    def quickSort(self, arr: List[int], left: int, right: int):
        if left >= right:
            return 
        # 找到两个中枢点
        index1, index2 = self.partition(arr, left, right)
        # 左边排序
        self.quickSort(arr, left, index1)
        # 右边排序
        self.quickSort(arr, index2, right)
    
    def partition(self, arr: List[int], left: int, right: int):
        pivot = arr[left + (right- left) // 2]
        # 当left == right时也要做处理
        while left <= right:
            # 找到左边第一个大于等于pivot的数
            while left <= right and arr[left] < pivot:
                left += 1
            # 找到右边第一个小于等于pivot的数
            while left <= right and arr[right] > pivot:
                right -= 1
            # 交换
            if left <= right:
                arr[left], arr[right] = arr[right], arr[left]
                left += 1
                right -= 1
        # [..., right]都是小于等于pivot的
        # [left, ...]都是大于等于pivot的
        # 有两种可能:right + 1 == left or right + 2 == left
        return right, left

快速选择的框架

215. 数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

partition函数

和快排一样。

quickSelect函数
  • 快排是:1. 找中枢点 2. 快排左和右
  • 快选是:1. 找中枢点 2. 快选左或右
时间复杂度
  • 平均: n + n/2 + n/4 +... = O(n)
  • 最差: n + n-1 + n-2 + ... + 1 = O(n^2)
代码
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        # 面试中可能不允许修改原数组,所以把nums拷贝成arr
        arr = [0] * len(nums)
        for i in range(len(nums)):
            arr[i] = nums[i]
        # 进行快速排序
        self.quickSelect(arr, 0, len(nums) - 1, len(nums) - k)
        return arr[len(nums) - k]

    # 归并排序函数:将nums[left...right]排序
    def quickSelect(self, arr: List[int], left: int, right: int, k: int):
        # 也可以写 if left == right
        if left >= right:
            return 
        # 找到两个中枢点
        index1, index2 = self.partition(arr, left, right)
        # 左边排序
        if k <= index1:
            self.quickSelect(arr, left, index1, k)
        # 右边排序
        elif k >= index2:
            self.quickSelect(arr, index2, right, k)
        else:
            return
    
    def partition(self, arr: List[int], left: int, right: int):
        pivot = arr[left + (right- left) // 2]
        # 当left == right时也要做处理
        while left <= right:
            # 找到左边第一个大于等于pivot的数
            while left <= right and arr[left] < pivot:
                left += 1
            # 找到右边第一个小于等于pivot的数
            while left <= right and arr[right] > pivot:
                right -= 1
            # 交换
            if left <= right:
                arr[left], arr[right] = arr[right], arr[left]
                left += 1
                right -= 1
        # [..., right]都是小于等于pivot的
        # [left, ...]都是大于等于pivot的
        # 有两种可能:right + 1 == left or right + 2 == left
        return right, left 

你可能感兴趣的:(快速排序 & 快速选择)