LeetCode高频100题刷题笔记(十五)排序算法

基础知识

算法名称 描述(升序) 时间复杂度 空间复杂度 稳定性
冒泡 从无序区通过不断交换找出大元素放到有序区前面(无序, 有序) O(n^2) O(1)
选择 从无序区找最小的元素放到有序区后面(有序, 无序) O(n^2) O(1) ×
插入 把无序区的第一个元素插到有序区的合适位置(有序, 无序) O(n^2) O(1)
希尔 每一轮按照事先决定的间隔插入排序,间隔依次缩小直至为1(如0123 0123 0123 …,初始间隔为4,数字相同代表属于同一插入排序组) O(nlogn) O(1) ×
归并 分而治之:(分)把数据逐级分为两段,直至仅有1个数字返回,(治)从两段中逐个选最小的放入新数据段末尾 O(nlogn) O(n)
快速 挑选基准元素 base,初始可选左基 left / 右基 right,< 基放前,> 基放后, = 选左基就跟左,右基就跟右,形如(小,基,大),从 base 位置切割成两个小数组(不含 base),利用递归对这两个小数组进行同样的操作,直至数组大小为1。(左基先移右,右基先移左,未遇就交换,相遇就是基 O(nlogn) O(logn) ×
按层序初始化并构建大根堆,从堆顶把根移出放入有序区前,再恢复大根堆(无序,有序)。最大堆/大根堆:完全二叉树,非叶节点的值大于其左右孩子节点的值 O(nlogn) O(1) ×
计数 用数组元素作为下标,统计某元素的个数 ,再把统计数据从小到大汇总起来 O(n+k) O(k)
将一组数据分到几个有序的桶里,每个桶里的数据单独排序,排序后再顺序倒出每个桶内元素 O(n+k) O(n+k)
基数 先以个位数的大小来对数据进行排序,接着以十位数、百位数…以某位数进行排序的时候是用桶排实现 O(nk) O(n+k)

代码(C++):

1.冒泡

class Solution {
public:
    vector<int> bubbleSort(vector<int>& nums) {
    	int n = nums.size();
        for (int i = 0; i < n; i++) {
        	for (int j = 0; j < n - i - 1; j++) {
        		if (nums[j] > nums[j + 1]) {
        			swap(nums[j], nums[j + 1]);
        		}
        	}
        }
        return nums;
    }
};

2.选择

class Solution {
public:
    vector<int> selectSort(vector<int>& nums) {
    	int n = nums.size();
        for (int i = 0; i < n - 1; i++) {
        	int min_idx = nums[i];
        	for (int j = i + 1; j < n; j++) {
        		if (nums[j] < nums[min_idx]) {
        			min_idx = j;
        		}
        	}
        	if (min_idx != i) swap(nums[i], nums[min_idx]); 
        }
        return nums;
    }
};

3.插入

class Solution {
public:
    vector<int> insertSort(vector<int>& nums) {
    	int n = nums.size();
        for (int i = 1; i < n; i++) {
        	int base = nums[i];
        	int j = i - 1;
        	while (j >= 0 && base < nums[j]) {
        		nums[j + 1] = nums[j];
        		j--;
        	}
        	nums[j + 1] = base;
        }
        return nums;
    }
};

4.希尔

class Solution {
public:
    vector<int> shellSort(vector<int>& nums) {
    	int n = nums.size();
        for (int h = n / 2; h >= 1; h /= 2) {
        	for (int i = h; i < n; i ++) {
        		int base = nums[i];
        		int j = i - h;
        		while (j >= 0 && base < nums[j]) {
        			nums[j + h] = nums[j];
        			j -= h;
        		}
        	}
        	nums[j + h] = base;
        	
        }
        return nums;
    }
};

5.归并

class Solution {
public:
	// 递归式
	vector<int> mergeSortMain(vector<int>& nums) {
		mergeSort(nums, 0, nums.size() - 1);
        return nums;	
    }
    void mergeSort(vector<int>& nums, int l, int r) {
    	if (l >= r) return; 
        int mid = l + (r - l) / 2;
        mergeSort(nums, l, mid);
        mergeSort(nums, mid + 1, r);
        merge(nums, l, mid, r);
        return nums;	
    }
    // 非递归式
	vector<int> mergeSort(vector<int>& nums) {
		int n = nums.size(), i = 0;
		for (int k = 1; k < n; k += k) {
			int l = 0;
			int mid = l + k - 1;
			int r = min(mid + k, n - 1);
			while (mid < n) {
				merge(nums, l, mid, r);
				l = r + 1;
				mid = l + k - 1;
				r = min(mid + k, n - 1);
			}
		}
        return nums;	
    }
    void merge(vector<int>& nums, int l, int mid, int r) {
    	int i = l, j = mid + 1, k = 0;
    	vector<int> tmp(r - l + 1);
    	while (i <= mid && j <= r) {
    		if (nums1[i] < nums2[j]) nums[k++] = nums1[i++];
    		else  nums[k++] = nums2[j++];
    	}
    	while (i <= mid) nums[k++] = nums1[i++];
    	while (j <= r) nums[k++] = nums2[j++];	
    	for (int i = 0; i < k; i++) {
    		nums[l + i] = tmp[i];
    	}
    }
};

6.快速

class Solution {
public:
	vector<int> quickSortMain(vector<int>& nums) {
		quickSort(nums, 0, nums.size() - 1);
		return nums;
    }
    void quickSort(vector<int>& nums, int l, int r) {
    	if (l >= r) return;
		int mid = partition(nums, l, r);
		quickSort(nums, l, mid - 1);
		quickSort(nums, mid + 1, r);
    }
    int partition(vector<int>& nums, int left, int right) {
    	int l = left, r = right;
    	while (l < r) {
    		while (l < r && nums[r] > nums[left]) {
    			r--;
    		}
    		while (l < r && nums[l] <= nums[left]) {
    			l++;
    		}
    		if (l < r) {
    			swap(nums[l], nums[r]);
    		}
    	}
    	swap(nums[left], nums[l]);
    	return l;
    }
};

7.堆

class Solution {
public:
	vector<int> heapSort(vector<int>& nums) {
		int n = nums.size();
		for (int i = n / 2 - 1; i >= 0; i--) {
			adjust(nums, i, n);
		}
		for (int i = n - 1; i > 0; i--) {
			swap(nums[0], nums[i]);
			adjust(nums, 0, i);
		}
		return nums;
    }
    void adjust(vector<int>& nums, int parent, int n) {
    	int child = 2 * parent + 1;
    	while (child < n) {
    		// 如果右孩子节点比左孩子大,则定位到右孩子
    		if (child + 1 < n && nums[child + 1] > nums[child]) {
    			child++;
    		}
    		if (nums[child] <= nums[parent]) {
    			return;
    		} else {
    			swap(nums[child], nums[parent]);
    			parent = child;
    			child = 2 * parent + 1;
    		}
    	}
    }
};

8.计数

class Solution {
public:
	vector<int> countSort(vector<int>& nums) {
		int n = nums.size();
		int maxNum = nums[0], minNum = nums[0];
		for (int i = 1; i < n; i++) {
			if (nums[i] > maxNum) {
				maxNum = nums[i];
			} 
			if (nums[i] < minNum) {
				minNum = nums[i];
			}
		}
		int size = maxNum - minNum + 1;
		vector<int> tmp(size);
		for (int i = 0; i < n; i++) {
			tmp[nums[i] - minNum]++;
		}
		int k = 0;
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < tmp[i]; j++) {
				nums[k++] = i + minNum;
			} 
		}
		return nums;
    }
 };

9.桶

class Solution {
public:
	vector<int> bucketSort(vector<int>& nums) {
		int n = nums.size();
		int maxNum = nums[0], minNum = nums[0];
		for (int i = 1; i < n; i++) {
			if (nums[i] > maxNum) {
				maxNum = nums[i];
			} 
			if (nums[i] < minNum) {
				minNum = nums[i];
			}
		}
		int bucketSize = 5; // 设置桶的容量
		int bucketNum = (maxNum - minNum) / bucketSize + 1; //计算桶的数量
		vector<vector<int>> buckets(bucketNum);
		for (int i = 0; i < n; i++) {
			buckets[(nums[i] - minNum) / bucketSize].push_back(nums[i]);
		}
		for (int i = 0; i < bucketNum; i++) {
			sort(buckets[i].begin(), buckets[i].end());
		}
		int k = 0;
		for (int i = 0; i < bucketNum; i++) {
			for (int j = 0; j < buckets[i].size(); j++) {
				nums[k++] = buckets[i][j];
			}
		}
		return nums;
    }
 };

10.基数

class Solution {
public:
	vector<int> bucketSort(vector<int>& nums) {
		int n = nums.size();
		int maxNum = nums[0], minNum = nums[0];
		for (int i = 1; i < n; i++) {
			if (nums[i] > maxNum) {
				maxNum = nums[i];
			} 
			if (nums[i] < minNum) {
				minNum = nums[i];
			}
		}
		int cnt = 0;
		while (maxNum > 0) {
			maxNum /= 10;
		 	cnt++;
		}
		vector<vector<int>> buckets(10);
		for (int i = 0; i < cnt; i++) {
			for (int j = 0; j < n; j++) {
				int digit = (nums[j] / pow(10, i))  % 10;
				buckets[digit].push_back(nums[j]);
			}
			int k = 0;
			for (int j = 0; j < 10; j++){
				for (int t: buckets[j]) {
					nums[k++] = t;	
				}
				buckets[j].clear();
			}
		}
		return nums;
    }
 };

题目:

1.数组中的第K个最大元素( LeetCode 215 )

难度: 中等
题目表述:
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
代码(C++):

class Solution {
public:
	//基于快排的选择方法 
    int findKthLargest(vector<int>& nums, int k) {
        return quickSelect(nums, 0, nums.size() - 1, nums.size() - k);
    }
    int quickSelect(vector<int>& nums, int l, int r, int k) {
    	if (l >= r) return nums[l];
        int mid = partition(nums, l, r);
        if(mid == k) return nums[mid];
        else if (mid > k) return quickSelect(nums, l, mid - 1, k);
        return quickSelect(nums, mid + 1, r, k);
    }
    int partition(vector<int>& nums, int left, int right) {
        int l = left, r = right;
        while (l < r) {
            while (l < r && nums[r] > nums[left]) {
                r--;
            }
            while (l < r && nums[l] <= nums[left]) {
                l++;
            }
            if (l < r) {
                swap(nums[l], nums[r]);
            }
        }
        swap(nums[left], nums[l]);
        return l;
    }
	// 基于堆排的选择方法 
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        for (int i = k / 2 - 1; i >= 0; i--) {
            adjust(nums, i, k);
        }
        for (int i = k; i < n; i++) {
            if (nums[i] > nums[0]) {
                swap(nums[i], nums[0]);
                adjust(nums, 0, k);
            }
        }
        return nums[0];
    }
    void adjust(vector<int>& nums, int parent, int n) {
        int child = parent * 2 + 1;
        while (child < n) {
            if (child + 1 < n && nums[child + 1] < nums[child]) {
                child++;
            }
            if (nums[parent] <= nums[child]) return;
            else {
                swap(nums[parent], nums[child]);
                parent = child;
                child = parent * 2 + 1;
            }
        }
    }
};

题解:
topk 问题是非常高频的考点,主要有这些方法:快速选择、建堆、排序、扫描+维护多变量、有序集合如 set、优先队列
快排:O(1) - O(N^2) 平均O(N)
堆排:O(NlogK)


2.第三大数( LeetCode414 )

难度: 简单
题目表述:
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
代码(C++):

class Solution {
public:
    int thirdMax(vector<int>& nums) {
        set<int> s;
        for (int i = 0; i < nums.size(); i++) {
            s.insert(nums[i]);
            if (s.size() > 3) s.erase(s.begin());
        }
        return s.size() == 3 ? *s.begin() : *s.rbegin();
    }
};

题解: 有序集合
set 默认less升序,set 降序
sort(.begin(), .end(), greater());降序
sort(.rbegin(), .rend());降序


3.三个数的最大乘积( LeetCode628 )

难度: 简单
题目表述:
给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。
代码(C++):

class Solution {
public:
    int maximumProduct(vector<int>& nums) {
        int max1 = INT_MIN, max2 = INT_MIN, max3 = INT_MIN;
        int min1 = INT_MAX, min2 = INT_MAX;
        for (int x: nums) {
            if (x > max1) {
                max3 = max2;
                max2 = max1;
                max1 = x;
            } else if (x > max2) {
                max3 = max2;
                max2 = x;
            } else if (x > max3) 
                max3 = x;
            if (x < min1) {
                min2 = min1;
                min1 = x;
            } else if (x < min2) {
                min2 = x;
            } 
        }
        return max(min1 * min2 * max1, max1 * max2 * max3);
    }
};

题解: 线性扫描


小结

topk 问题: 快速选择、建堆、排序、扫描+维护多变量、有序集合如 set、优先队列

其他C++知识点:
set< int > 默认less升序,set> 降序
sort(.begin(), .end(), greater< T > ()); 降序
sort(.rbegin(), .rend()); 降序

priority_queue< T > 默认是大根堆,priority_queue, greater< T >>小根堆
自定义priority_queue的cmp示例:decltype(cmp) “ > 小根堆,< 大根堆 ”

auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {
	return a.second == b.second ? a.first < b.first : a.second > b.second;
};
priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> que(cmp);

参考链接

https://leetcode.cn/circle/article/zLSmiB/

你可能感兴趣的:(LeetCode刷题专栏,leetcode,算法)