快速排序

219 阅读1分钟

算法思想

  1. 快速排序在每一轮挑选一个基准元素,并让其他比它大的元素移动到右边,比它小的移动到左边,从而把数列拆分成两部分.
  2. 与归并排序算法一样,快速排序算法也是分治策略的典型应用,但二者之间也有本质的区别.归并排序的计算量主要消耗于有序子向量的归并操作,而子向量的划分却几乎不费时间.快速排序恰恰相反,它可以在O(1)时间内,由子问题的解直接得到原问题的解;但为了原问题划分为两个子问题,却需要O(n)时间.

算法模版

递归版本:

void quickSort(vector<int>& nums,int l,int r){ 
    if(l >= r) return ; 
    int i = l - 1,j = r + 1,pivot = nums[rand() % (r - l + 1) + l]; // 小于区域,大于区域,基准 
    while(i < j){ 
        do i++; while(nums[i] < pivot); 
        do j--; while(nums[j] > pivot); 
        if(i < j) swap(nums[i],nums[j]); 
    } 
    quickSort(nums,l,j); 
    quickSort(nums,j + 1,r); 
}

非递归版本(使用栈来模拟递归的调用栈):

void quick_nr(vector<int>& nums,int l,int r) {
        // struct Element { int l; int r; };
        stack<pair<int,int>> s; // l , r
        s.push({l , r}); // 初始状态
        
        while(!s.empty()){
            auto [l , r] = s.top();
            s.pop(); // 取出栈顶
            if(l >= r) continue;
            
            int i = l - 1,j = r + 1;
            int pivot = nums[rand() % (r - l + 1) + l];

            while(i < j){
                do i++; while(nums[i] < pivot);
                do j--; while(nums[j] > pivot);
                if(i < j) swap(nums[i],nums[j]);
            }
            s.push({j + 1,r});
            s.push({l , j});
        }
}

算法应用

  1. 荷兰国旗问题
75. 颜色分类
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int l = -1,r = nums.size(), pivot = 1; // 小于区域,大于区域,pivot
        int i = 0;
        while(i < r){
            if(nums[i] == pivot) i++;
            else if(nums[i] < pivot) swap(nums[i++],nums[++l]);
            else swap(nums[i],nums[--r]);
        }
    }
};
将数组中的大小写字母分开
void partition(vector<char>& nums,int l,int r){
	if(l >= r) return ;

	int i = l - 1,j = r + 1;
	while(i < j){
		do i++; while(islower(nums[i]));
		do j--; while(isupper(nums[j]));
		if(i < j) swap(nums[i],nums[j]);
	}
}
  1. Top K问题