八大排序总结
八大排序
1.直接插入排序
基本思想:
1.就像我们平时玩扑克牌时,摸牌阶段的排序就用到了插入排序的思想
2.用这第n个数与前面的n-1个数进行比较,找到要插入的位置,将其插入(原来位置上的数不会被覆盖,因为提前保存了)
3.原来位置上的数据,依次后移。
代码实现:
void InsertSort(int* a, int n)
{
assert(a);
for (int i = 0; i < n - 1; ++i)
{
int end = i;
int x=a[end+1];//将end后面的值保存到x里面了
//将x插入到[0,end]的有序区间
while (end >= 0)
{
if (a[end] > x)
{
a[end + 1] = a[end]; //往后挪动一位
--end;
}
else
{
break;
}
}
a[end + 1] = x; //x放的位置都是end的后一个位置
}
}
2.希尔排序
希尔排序是对直接插入排序的优化。
逆序有序的数组排序时,时间复杂度为O ( n *n ),此时效率最低
顺序有序的数组排序时,时间复杂度为O ( n ) ,此时效率最高
我们发现,当被排序的对象越接近有序的时候,插入排序的效率最高,那我们是否有办法将数组变成有序后再用插入排序,此时希尔大佬就发现了这个排序算法,并命名为希尔排序。
代码实现:
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap /= 2;
for (int i = 0; i < n - gap; i++)
{
int end = i;
int x = a[end + gap];
while (end >= 0)
{
if (a[end] > x)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = x;
}
}
}
3.选择排序
选择排序就是每次从未排好的序列中找到最大或者最小的,将他放在最右边或者最左边,知道全部有序
void SelectSort(int* a, int n)
{
int begin = 0,end = n - 1;
while (begin<end)
{
int mini = begin, maxi = begin;
for (int i = begin; i <= end; i++)
{
if (a[i] < a[mini])
{
mini = i;
}
if (a[i] > a[maxi])
{
maxi = i;
}
}
Swap(&a[mini], &a[begin]);
//当begin==maxi时,最大值会被换走,修正一下
//这里就是如果我们最大值就在最开始的位置,那么我们在上面已经将他交换位置了,所以要找到现在最大值所在的位置
if (begin==maxi)
{
maxi=mini;
}
Swap(&a[maxi], &a[end]);
begin++;
end--;
}
}//这个代码要比之前大一写的那个好的多,那时候只是找到最小值,一直排下去。
4.堆排序(需要再看看)
基本思想:
1、将待排序的序列构造成一个大堆,根据大堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大堆;
3、重复步骤2,如此反复,从第一次构建大堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大堆的尾部。最后,就得到一个有序的序列了。
小结论:
排升序,建大堆
排降序,建小堆
#include <iostream>
#include <algorithm>
using namespace std;
int n;
const int N = 100010;
int h[N], size_;
void down(int u) {
int t = u; // t记录最小值
if (2 * u <= size_ && h[2 * u] < h[t]) t = 2 * u; // 左儿子存在,且值比父亲小
if (2 * u + 1 <= size_ && h[2 * u + 1] < h[t]) t = 2 * u + 1; // // 右儿子存在,且值比父亲小
if (t != u) {
swap(h[t], h[u]);
down(t);
}
return;
}
void up(int u) {
if (u / 2 > 0 && h[u / 2] > h[u]) {
swap(h[u / 2], h[u]);
up(u / 2);
}
return;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> h[i];
size_ = n;
// 初始化堆
for (int i = n / 2; i > 0; i --) down(i);
while(n --) {
cout << h[1] << " ";
h[1] = h[size_];
size_ --;
down(1);
}
return 0;
}
5.冒泡排序
冒泡排序的基本思想:
一趟过程中,前后两个数依次比较,将较大的数字往后推,下一次只需要比较剩下的n-1个数,如此往复
//优化版本的冒泡排序
void BubbleSort(int* a, int n)
{
int end = n-1;
while (end>0)
{
int exchange = 0;
for (int i = 0; i < end; i++)
{
if (a[i] > a[i + 1])
{
Swap(&a[i], &a[i + 1]);
exchange = 1;
}
}
if (exchange == 0)//单趟过程中,若没有交换过,证明已经有序,没有必要再排序
{
break;
}
end--;
}
}
6.快速排序
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
7.归并排序
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
8.计数排序
又叫非比较排序,又称为鸽巢原理,是对哈希直接定址法的变形应用。
计数排序就是对于一个序列中,找到他的最小值和最大值,在开创一个数组空间,用来统计,其他数字出现的次数,这时我们的关键字不再是i,而是原数组中其他数字,统计数字,最后统一输出,每个数字出现几次就输出几次。
模板一:
void CountSort(int* a, int n)
{
int min = a[0], max = a[0];//如果不赋值,min和max就是默认随机值,最好给赋值一个a[0]
for (int i=1;i<n;i++)//修正 找出A数组中的最大值和最小值
{
if (a[i] < min)
{
min=a[i];
}
if (a[i]>max)
{
max=a[i];
}
}
int range = max - min + 1;//控制新开数组的大小,以免空间浪费
int* count = (int*)malloc(sizeof(int) * range);
memset(count,0, sizeof(int) * range);//初始化为全0
if (count==NULL)
{
printf("malloc fail\n");
exit(-1);
}
//1、统计数据个数
for (int i=0;i<n;i++)
{
count[a[i]-min]++;
}
//2、拷贝回A数组
int j = 0;
for (int i=0;i<range;i++)
{
while (count[i]--)
{
a[j++] = i + min;
}
}
free(count);
count = NULL;
}
模板二:
//朴素版
void CountSort(int a[], int n){
int minval = a[0], maxval = a[0];
for(int i = 0; i < n; i ++){//遍历数组求得最大值和最小值
minval = min(minval, a[i]);
maxval = max(maxval, a[i]);
}
int cnt[maxval + 1] = {0};//根据最大值开辟新数组空间
for(int i = 0; i < n; i ++) cnt[a[i]] ++;//统计原数组中元素出现的次数
for(int i = minval, k = 0; i <= maxval; i ++){
while(cnt[i] != 0){
data[k ++] = i;//将排序后的序列赋给原数组
cnt[i] --;//i出现的次数减1
}
}
}
堆排序、快速排序、希尔排序、直接选择排序是不稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。

浙公网安备 33010602011771号