算法名称 | 描述(升序) | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
冒泡 | 从无序区通过不断交换找出大元素放到有序区前面(无序, 有序) | 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) | √ |
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;
}
};
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;
}
};
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;
}
};
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;
}
};
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];
}
}
};
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;
}
};
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;
}
}
}
};
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;
}
};
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;
}
};
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;
}
};
难度: 中等
题目表述:
给定整数数组 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)
难度: 简单
题目表述:
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
代码(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());降序
难度: 简单
题目表述:
给你一个整型数组 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
自定义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/