优质资源
:https://www.cnblogs.com/onepixel/p/7674659.html
比较类排序
排序算法稳定:稳不稳定看交换过程中,相同的元素会不会改变前后顺序
- 冒泡排序:稳定
- 选择排序:不稳定,5、8、5、2、6,第一趟下来5和5的前后顺序改变
- 插入排序:稳定
- 希尔排序:不稳定,因为增量为gap的排序,排的过程中可能已经发生了变化。
- 归并排序:稳定,分治。(前后顺序不变)
- 快速排序:不稳定,元素左右交换时,会发生改变
- 堆排序:不稳定,构造堆时发生改变
冒泡,插入、归并
稳定:冒泡,插入、归并、非比较类排序
冒泡,插入、归并
不稳定:选择、希尔、快排、堆
1.冒泡排序
直接两两比较,n次遍历,进行进行交换,将最大的元素移至最后。
时间复杂度O(n^2)、空间O(1)
1 | //1.冒泡------------逐步把最大的放后面去 |
2.选择排序
不直接交换,两两比较,进行下标的交换,记录最小元素下标
时间复杂度O(n^2)、空间O(1)
1 | //2选择--------------记录最小元素下标、然后进行元素交换 |
3.插入排序
增量为1,选取一个元素,将他元素插入到合适的位置。
时间复杂度O(n^2)、空间O(1)
cur是被挂起的,最后进行赋值
1 | //3插入排序--------------增量为1的希尔排序 |
4.希尔排序
增量为n的插入排序,当元素总体上有序时,插入排序很高效,所以先大致排序整齐。
时间复杂度O(n * logN)、空间O(1)
1 | //4.希尔排序,增量为gap的插入排序,先使其大致有序,然后再以增量减小的方式排序 |
5.归并排序
先分再和,先将元素分成单个个体,然后两两组合,最后合并成一个有序数组
- 先分再和,双指针一次将元素填入位置
时间复杂度O(n * logN)、空间O(N)
1 | //5.归并排序---分而治之的思想,先将元素分组,然后22相结合排序 |
6.快速排序
先和再分,以第一个元素为基准数,大的放右,小的放左,然后依次再以基准数排序
注意:这里包含往下取整还是往上,注意
时间复杂度O(n * logN)、空间O(logN)1
2
3
4while(arr[p2] >temp && p1 < p2)
p2--;
while (arr[p1] <temp && p1 < p2)
p1++;
1 | //6.快速排序---以不同的基数,将其分组,分别排序 |
7.堆排序
利用完全二叉树的特点,构造大根堆,然后首位元素交换
- 父节点:(i - 1)/ 2 (节点从0开始)
- 左孩子:i*2 + 1
- 右孩子:i*2 + 2
时间复杂度O(n * logN)、空间O(1)
1 | //堆是完全二叉树,满的二叉树,构造成大根堆,然后将上方最大的数和子节点交换 |
STL_优先队列
1.快排
2.堆排序
快排
数组中第K大的数(无序数组)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31int findKthLargest(vector<int>& nums, int k) {
int len = nums.size();
return quickSort(nums, 0, len - 1, len - k);
}
int quickSort(vector<int>& nums, int left, int right, int k) {
int len1 = left;
int len2 = right;
int base = nums[left];
while (len1 < len2) {
while (nums[len2] >= base && len1 < len2)
len2--;
while (nums[len1] <= base && len1 < len2)
len1++;
if (len1 < len2) {
int temp = nums[len1];
nums[len1] = nums[len2];
nums[len2] = temp;
}
}
//握手,元素换位
nums[left] = nums[len1];
nums[len1] = base;
if (k == len1)
return nums[len1];
else if (k < len1)
return quickSort(nums, left, len1 - 1, k);
else
return quickSort(nums, len1 + 1, right, k);
}
优先队列
堆排序:(剑指_41)
- #include
- priority_queue<int, vector, std::less> maxStack;//大顶堆—返回最大元素
- priority_queue<int, vector, std::greater> minStack;//小顶堆—返回最小元素
题目:数组中第K大的数
维护一个小顶堆即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23int findKthLargest(vector<int> &nums, int k)
{
int result = 0;
int numsSize = int(nums.size());
if (numsSize == 0 || k > numsSize)
{
return 0;
}
priority_queue<int, vector<int>, greater<int>> store;
//堆中维持k个最大数
for (int i = 0; i < numsSize; i++)
{
store.push(nums[i]);
if (int(store.size()) > k)
{
store.pop();
}
}
result = store.top();
return result;
}
二分查找库函数
头文件:algorithm
lower_bound( begin,end,num )
- 查找第一个大于或等于num的数字—— >= num
upper_bound( begin,end,num )
- 查找第一个大于num的数字 —— > num
例题LeetCode870:给定两个大小相等的数组 A 和 B,A 相对于 B 的优势可以用满足 A[i] > B[i] 的索引 i 的数目来描述。
返回 A 的任意排列,使其相对于 B 的优势最大化。
输入:A = [2,7,11,15], B = [1,10,4,11]
输出:[2,11,7,15]
输入:A = [12,24,8,32], B = [13,25,32,11]
输出:[24,32,8,12]
解题方法:田忌赛马
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16vector<int> advantageCount(vector<int>& A, vector<int>& B) {
sort(A.begin(), A.end());
vector<int> ans;
for (int i = 0; i < B.size(); i++) {
auto iter = upper_bound(A.begin(), A.end(), B[i]);
if (iter != A.end()) {
ans.push_back(*iter);
A.erase(iter);
}
else {
ans.push_back(A[0]);
A.erase(A.begin());
}
}
return ans;
}
非比较类排序
8.计数
1-100最快的排数,适用于数字范围不大地数字段。
- 1.找到数组最大值和最小值
- 2.count_arr[arr[i] ]++
- 3.依次将数组填入
1 | //8.计数排序,输入的数据值转化为键值存在于新开辟的数组空间中 |
9.基数
按照个位、十位、百位依次顺序排序。
1
int minimumTotal(vector<vector<int>>& triangle) {
10.桶排序
1.首先对元素进行分组 2.组内元素快速排序 3.依次将元素填充
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//9.桶排序
void sort9(vector<int>&arr){
int max = 0;
for (int i = 0; i < arr.size(); i++) {
if (arr[i] > max)
max = arr[i];
}
int count = 0;
vector<int>temp(max + 1, 0);
for(int i = 0; i < arr.size(); i++) {
temp[arr[i]]++;
}
for(int i = 0; i <= max; i++) {
if (temp[i] > 0) {
for(int j = 0; j < temp[i]; j++) {
arr[count] = i;
count++;
}
}
}
}
Top_K问题
第K大的数
- 快排思想
1 | int findKth(vector<int> a, int n, int K) { |
第K大的前K个数
- 1.排序
- 2.构造大根堆
- 3.快排 + 二分
1 |