排序算法有很多,在这我先介绍冒泡排序、插入排序、归并排序、桶排序、基数排序和堆排序
冒泡排序和插入排序
- 冒泡排序:比较相邻元素,直到序列变为有序为止
- 插入排序:将无序的元素插入到有序的元素序列中,插入后仍然有序
归并排序
分治和归并
- 分治:1、分解:将原问题分解成一系列子问题;2、解决:递归地求解各子问题。3、合并:将子问题的结果合并原问题的解。
- 归并:关键在于归并两个相邻的子序列,使其变成一个排序好的新序列
那么代码如下
1 | void merge(int arr[],int left,int mid,int right,int temp[]){ |
1 | void mergesort(int arr,int left, int right, int temp[]){ |
也就是说归并算法,就包括了分治策略和合并。同时分治算法也是比较常用的一种算法
桶排序
- 桶排序:将数据均匀分配到有限数量的桶中,然后对每个桶再分别排序,对每个桶再使用插入排序算法,最后将每个桶中的数据有序的组合起来。
- 桶数量越多,时间效率越高,然而占用的空间也就越大
- 初始化装入连续区间元素的n个桶,每个桶用来装一段区间的元素
- 遍历待排序的数据,将其映射到对应的桶中,保证每个桶中的元素都在同一个区域范围中。
- 对每个桶进行排序,最终将所有的桶中排好序的元素连起来参考文献1
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//桶排序
//b表示需要nums.size()/b个桶;
void BucketSort(vector<int>& nums, int b=1) {
if (nums.size() < 2) return;
int maxVal = INT32_MIN;
int minVal = INT32_MAX;
for (int i = 0; i < nums.size(); ++i) {
maxVal = max(maxVal, nums[i]);
minVal = min(minVal, nums[i]);
}
int k = (maxVal - minVal + b) / b;
vector<vector<int>>buckets(k);
//分配
for (int i = 0; i < nums.size(); ++i) {
int index = (nums[i] - minVal) / b;
buckets[index].push_back(nums[i]);
}
int index = 0;
//桶内排序与收集
for (int i = 0; i < buckets.size(); ++i) {
//使用其他排序方法进行桶内排序
sort(buckets[i].begin(), buckets[i].end());
for (auto& val : buckets[i])
nums[index++] = val;
}
}
参考文献2基排序
- 基排序:假设输入数据属于一个小区间内的整数,对每一位上进行进桶,出桶;根据关键字中各位的值,通过对排序的N个元素进行若干躺“分配”与“收集”来实现排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//基数排序
void RadixSort(vector<int>&nums) {
//最大位数
int k = 0;
for (int i = 0; i < nums.size(); ++i)
k = max(int(log(nums[i])) + 1, k);
for (int i = 0; i < k; ++i) {
//十进制
vector<vector<int>>radix(10);
//分配
for (int j = 0; j < nums.size(); ++j) {
int index = int(nums[j] / pow(10, i)) % 10;
radix[index].push_back(nums[j]);
}
int index = 0;
//收集
for (int j = 0; j < radix.size(); ++j) {
for (auto& r : radix[j]) {
nums[index++] = r;
}
}
}
}
计数排序
- 先扫描得到待排序数组中的最大值maxVal与最小值minVal,之后分配一个长度为 maxVal-minVal+1 的计数数组,之后扫描待排序元素,将其所在的计数数组的位置加一。最后再扫描一遍计数器数组,按顺序把值收集起来。注:计数排序实际上是桶排序的一种特例,即k=maxVal-minVal+1;
- 时间复杂度:平均:O(n+k);最好:O(n+k);最坏:O(n+k)
空间复杂度:O(k)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//计数排序
void CountSort(vector<int>& nums) {
if (nums.size() < 2) return;
int maxVal = INT32_MIN;
int minVal = INT32_MAX;
for (int i = 0; i < nums.size(); ++i) {
maxVal = max(maxVal, nums[i]);
minVal = min(minVal, nums[i]);
}
int k = maxVal - minVal + 1;
vector<int>count(k);
//计数
for (int i = 0; i < nums.size(); ++i)
++count[nums[i] - minVal];
int index = 0;
//收集
for (int i = 0; i < count.size(); ++i) {
while (count[i] > 0) {
nums[index++] = i + minVal;
--count[i];
}
}
}
堆排序
- 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子结点的值,成为小顶堆。参考文献
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void maxHeapify(vector<int>& a,int i,int heapSize){
int l = i*2 + 1;
int r = i*2 + 2;
int large = i;
if(l < heapSize && a[l] > a[large]){
large = l
}
if(r < heapSize && a[r] > a[large]){
large = r;
}g
if(large != i){
swap(a[i], a[large]);
maxHeapify(a, large, heapSize);
}
}
void buildMaxHeap(vector<int>& a,int heapSize){
for(int i = heapSize/2;i >= 0;--i){
maxHeapify(a,i,heapSize);
}
}