【C++】排序算法大总结

以下是我自己总结有关排序算法的重要知识点,有兴趣的小伙伴可以下载图片观看呀!
在这里插入图片描述
下载链接在此!
以下是我学习时,写的相关排序的C/C++代码(混合着用了/(ㄒoㄒ)/,只是用到了C的printf,两者基本上一样的,挺容易看懂的)

#include<stdio.h>
#include<iostream>
//#include<algorithm> //c中没有,所以自己写了swap函数
using namespace std;

void swap(int *a, int *b);
//冒泡
void pop_sort(int *a, int len);
//插入排序
void Insert_sort(int *a, int N);
void Insert_zheban(int a[], int n);
//希尔排序及优化算法
void Shell_sort(int *a, int N);
void Sedgewick_shell_sort(int *a, int N);
void Hibbard_shell_sort(int *A, int N);
//简单选择排序
void Selection_sort(int *a, int N);
int ScanForMin(int *a, int i,int N);
//堆排序
void Heap_sort(int *a, int N);
void adjust_node(int *arr, int n, int len);
//归并排序
void merge(int *a, int L, int mid, int R);
void Merge_sort(int *a, int l, int r);
//快速排序
int getpivot(int *a, int L, int R);
void Quicksort(int *a, int left, int right);
void Quick_sort(int *a, int N);
void BaseSort(int *a, int N);

int main(void) {
	int a[10] = {3,5,7,6,8,9,1,4,2,0};
	for (int i=0; i<10; ++i) {
		printf("%d ", a[i]);
	}
	printf("\n");
	///
	//Heap_sort(a, 10);
	Merge_sort(a, 0, 9);
	///
	for (int i=0; i<10; ++i) {
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}
void swap(int *a, int *b) {
	int t;
	t = *a;
	*a = *b;
	*b = t;
}
//冒泡排序,加入flag标志,优化效率
void pop_sort(int *a, int len) {//*(a+i)等价于 a[i]
	int i,j;
	for (i=0; i<len-1; i++) {
		int flag = 0;
		for (int j=0; j<len-1-i; j++) {
			if (a[j]>a[j+1]) {
				swap(&a[j], &a[j+1]);
				flag = 1;
			} 
		}
		if (!flag) break;
	}
}
//折半-插入排序
void Insert_zheban(int a[], int n) {
	int i,j,low = 0,high = 0,mid;
    int temp = 0;
    for (i=1; i<n; i++) {
        low=0;
        high=i-1;
        temp=a[i];
        //采用折半查找法判断插入位置,最终变量 low 表示插入位置
        while (low<=high) {
            mid=(low+high)/2;
            if (a[mid]>temp) {
                high=mid-1;
            }else{
                low=mid+1;
            }
        }
        //有序表中插入位置后的元素统一后移
        for (j=i; j>low; j--) {
            a[j]=a[j-1];
        }
        a[low]=temp;//插入元素
       
    }

}
//插入排序
void Insert_sort(int a[], int N) {
	int tmp,j;
	for (int i=1; i<N; i++) {
		tmp = a[i];
		for (j=i; j>=1&&a[j-1]>tmp; j--)
			a[j] = a[j-1];
		a[j] = tmp;
	}
}
//基础的希尔排序
void Shell_sort(int a[], int N) {
	int i,j,tmp;
	for (int D=N/2; D>0; D/=2) {
		for (i=D; i<N; i++) {
			tmp = a[i];
			for (j=i; j>=D&&a[j-D]>tmp; j-=D)
				a[j] = a[j-D];
			a[j] = tmp;
		}
	}
}
// Hibbard增量序列希尔排序
void Hibbard_shell_sort(int *A, int N) {
	int add[]= {32767,16383,8191,4095,2047,1023,511,255,127,63,31,15,7,3,1,0};
	int i=0;
	for(int D=add[i]; D>0; D=add[++i]) {
		for(int p=D; p<N; p++) {
			int tmp = A[p];
			int k=p;
			for(; k>=D && tmp<A[k-D] ; k-=D) // j>=D 在前,因为也许 A[j-D]已经越界
				A[k] = A[k-D];
			A[k] = tmp;
		}
	}
}
// Sedgewick增量序列哈希排序
void Sedgewick_shell_sort(int *A, int N) {
	int add[]= {587521,260609,146305,64769,36289,16001,8929,3905,2161,929,505,209,109,41,19,5,1,0};
	int i=0;
	for(int D=add[i]; D>0; D=add[++i]) {
		for(int p=D; p<N; p++) {
			int tmp = A[p];
			int k = p;
			for(; k>=D && tmp<A[k-D]; k-=D)
				A[k] = A[k-D];
			A[k] = tmp;
		}
	}
}
void Selection_sort(int *a, int N) {
	int Minorder;
	for (int i=0; i<N; i++) {
		Minorder = ScanForMin(a, i, N);
		swap(a[i], a[Minorder]);
	}
}
int ScanForMin(int *a, int i, int N) {
	int Order,min=1000000;
	for(int n=i; n<N; n++) {
		if (a[n]<min) {
			min = a[n];
			Order = n;
		}
	}
	return Order;
}

void Heap_sort(int a[], int N) { //堆排序
	int i,temp;
	for (i=N/2; i>=0; i--)
		adjust_node(a, i, N);//第一步是构造大根堆
	for (i=N-1; i>0; i--) {
		swap(&a[0], &a[i]);
		adjust_node(a, 0, i);//不断输出堆顶的最大元素
	}
}
void adjust_node(int *a, int i, int N) {
	int l, r, Tmp, max;
	l = 2 * i + 1; //左右孩子的索引,注意数组下标从0开始。
	r = 2 * i + 2;
	max = i;

	if (l<N && a[l]>a[i])
		max = l;
	if (r<N && a[r]>a[max])
		max = r;

	if (max != i) {
		swap(&a[i], &a[max]);
		adjust_node(a, max, N); //保证最大堆
	}
}
//归并排序
int b[10] = {0};
void merge(int *a, int L, int mid, int R) {
	int i,j,m;
	for (int k=L; k<=R; k++) {
		b[k] = a[k];
	}
	for (i=L, j=mid+1, m=i; i<=mid&&j<=R; m++) {
		if (b[i]<=b[j])
			a[m] = b[i++];
		else
			a[m] = b[j++];
	}
	while (i<=mid) a[m++] = b[i++];
	while (j<=R) a[m++] = b[j++];
}
void Merge_sort(int *a, int l, int r) { //在外排序的时候是一个比较有用的工具
	/*
	时间复杂度:O(nlogn)
	空间复杂度:O(N),归并排序需要一个与原数组相同长度的数组做辅助来排序
	稳定性:归并排序是稳定的排序算法
	*/
	if (l<r) {
		int mid = (l+r)/2;
		Merge_sort(a, l, mid);
		Merge_sort(a, mid+1, r);
		merge(a, l, mid, r);
	}

}
//快排
int getpivot(int *a, int L, int R) { //快速排序选主元
	int center = (L+R)/2;
	if (a[R]<a[L]) 		swap(&a[R], &a[L]);
	if (a[R]<a[center]) swap(&a[R], &a[center]);
	if (a[center]<a[L]) swap(&a[center], &a[L]);
	swap(&a[center], &a[R-1]);
	return a[R-1];
}
void Quicksort(int *a, int left, int right) {
	int cutoff = 100;
	if (cutoff < right-left) {
		int pivot = getpivot(a, left, right);
		int i = left;
		int j = right-1;
		for(;;) /*for(;;)死循环里的两个;;代表两个空语句,编译器一般会优化掉它们,直接进入循环体。
				while(1)死循环里的1被看成表达式,每循环一次都要判断常量1是不是等于零。
				即,相对来说for式死循环更加高效一点*/
		{
			while(a[++i] < pivot);
			while(a[--j] > pivot);
			if (i<j) swap(&a[i], &a[j]);
			else break;
		}
		swap(&a[i], &a[right-1]);
		Quicksort(a, left, i-1);
		Quicksort(a, i+1, right);
	} else
		Insert_sort(a+left, right-left+1);
}
/*或者**************************************/
void Quick_Sort(int *arr, int begin, int end){
    if(begin > end)
        return;
    int tmp = arr[begin];
    int i = begin;
    int j = end;
    while(i != j){
        while(arr[j] >= tmp && j > i)
            j--;
        while(arr[i] <= tmp && j > i)
            i++;
        if(j > i){
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    arr[begin] = arr[i];
    arr[i] = tmp;
    Quick_Sort(arr, begin, i-1);
    Quick_Sort(arr, i+1, end);
}
/******************************************/

void Quick_sort(int *a, int N) {
	Quicksort(a, 0 ,N-1);
}
//基数排序
void BaseSort(int *a, int N) {
	//暂未完成
	
} 

如果根据数据量选择对应排序算法?


数据0:只有1个元素;

    数据量太少,没有差别。

数据1:11个不相同的整数,测试基本正确性;

    数据量太少,没有差别。

数据2:10^3个随机整数;

   插入排序最慢,理所当然;

   shell排序竟然也慢,你知道吗? 是不是数据量太小看不出差距,或者这是一次随机的数据导致的?

数据3:10^4个随机整数;

   最慢的是插入,理所当然,

数据4:10^5个随机整数;

   插入排序拖后腿非常明显

   性能最好的是segwick shell排序。

数据5:10^5个顺序整数;

   使用的是快排,遇到随机的数据,碰到了最坏的情况n^2

   插入排序竟然当第一啦。见解说明了生成的数据是接近有序的数据。

   待优化快排(轴的选择,递归的区间选择,小区间和插入排序的结合等),

   其他优化的方法:直接在输入的时候,识别是否是顺序或者逆序

数据6:10^5个逆序整数;

    快排慢的原因见数据6,细心发现快排这里非常吃空间。

    次之,插入排序也遇到了最坏的情况,竟然花销比快速排序少,个人认为递归的性能导致了本快排慢。

    优化:在输入的时候,识别是否是或者逆序

数据7:10^5个基本有序的整数;

     快速排序基础版依然慢,其他三个是一个数量级。

数据8:10^5个随机正整数,每个数字不超过1000。

      Ps:其实这道题在10^5的数量级上上面可以非常自由的使用空间,用空间换时间

     使用hash数组是最快的,


-----------数量级初步估算-----------

以上从数据层面分析了,下面初略量化分析了下花销:

以cpu 1Ghz位列,那么一秒执行10^9次,

n=10^5

n*logn=1151292.546497023 ~=11ms (初略计算,没有考虑n&logN的系数等)

所以基于比较的排序算法最优性能维持在 10ms数量级左右,综合上面的结果(表格最优的是20ms左右 10^5)一致。



-------纵向比较

    以上从数据角度,数量级角度,我们还可以从算法角度,

    没有优化的快速排序算法这里性能很差,下一周我们是不是要优化啊??o(^▽^)o

    插入排序的表现在期望之中,适合在接近有序的条件下运行。

    最后剩下堆排序和shell排序啦,这里可以看到segwick shell表现更出色,原因嘛,就是姥姥说的每次swap操作减少多个逆序对。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郝同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值