几种常用排序算法的比较

在计算机科学所使用的排序算法通常被分类为:
1-计算的复杂度(最差、平均、和最好性能),依据列表(list)的大小(n)。一般而言,好的性能是O(n log n),且坏的性能是O(n2)。对于一个排序理想的性能是O(n)。仅使用一个抽象关键比较运算的排序算法总平均上总是至少需要O(n log n)。
2-存储器使用量(以及其他电脑资源的使用)
3-稳定度:稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序。也就是一个排序算法是稳定的,就是当有两个有相等关键的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前。(简单来说,判断依据是一个在一个存在相同元素或者键值的数组里面排序,只要同一方向,他们之间排序后的前后位置不变,则这个排序是稳定的)
4-一般的方法:插入、交换、选择、合并等等。交换排序包含冒泡排序和快速排序。选择排序包含希尔排序和堆排序。

按照稳定性分

稳定排序:冒泡、插入、桶、归并、二叉树

不稳定排序:选择、希尔(改进版插入排序)、堆、快排

冒泡排序bubble sort 时间复杂度O(n2)

是稳定的,如 1 2 3 1 4 8 91中,排序后,第一个1和第二个1的前后位置不变

void bubbleSort(int ibubbleArr[],const int iArrSize){
	for (int i = 0; i<iArrSize ; i++){
		for (int j = iArrSize - 1; j>i; j--){
			if (ibubbleArr[j] > ibubbleArr[j - 1]){
				ibubbleArr[j] ^= ibubbleArr[j - 1];
				ibubbleArr[j - 1] ^= ibubbleArr[j];
				ibubbleArr[j] ^= ibubbleArr[j - 1];
			}
		}
	}
}

插入排序 insertion sort时间复杂度O(n)

是稳定的,基于已经有序的数列中,插入一个元素,如 1 2 4 8 9 10中 插入4,只要插入方向一定,那么这两个4的位置也是一定的

int* insertSort(int *iInsertArr,int *iArrSrc,const int iArrSize){//原数组,目标数组,数组大小
	int i = 0;
	for (; i<iArrSize; i++){
		iArrSrc[i] = iInsertArr[i];
		for (int j= i; j>0; j--){
			if(iArrSrc[j] > iArrSrc[j - 1]){
				iArrSrc[j] ^= iArrSrc[j - 1];
				iArrSrc[j - 1] ^= iArrSrc[j];
				iArrSrc[j] ^= iArrSrc[j - 1];
			}else break;
		}
	}
	for ( i = 0; cout<<iArrSrc[i++]<<" ",i<iArrSize; ) ;cout<<endl;
	return iArrSrc;
}



归并排序 merge sort O(n log n);需要O(n)额外空间

稳定的,归并排序把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

void mergeSort(int i_src[],int i_rs[],int i_stIndex,int i_midIndex,int i_enIndex){//归并src这个数组的前+中和中+后合并
	int i,j,k;
	//int st,mid,en;st = i_stIndex,mid = i_midIndex,en = i_enIndex;
	for (i = i_stIndex,j = i_midIndex + 1;i_stIndex <= i_midIndex && j <= i_enIndex; i++){
		if (i_src[i_stIndex] < i_src[j]){
			i_rs[i] = i_src[i_stIndex++];
		}else{
			i_rs[i] = i_src[j++];
		}
	}

	if (i_stIndex <= i_midIndex){
		for (k = 0; k <= i_midIndex - i_stIndex; k++){
			i_rs[i+k] = i_src[i_stIndex + k];
		}
	}

	if (j <= i_enIndex){
		for (k = 0; k<= i_enIndex - j;k++){
			i_rs[i + k] = i_src[j + k];
		}
	}
	return;
}
void merge(int i_src[],int i_rs[],int i_stIndex,int i_enIndex){//划分阶段参数:原数组,存放结果的数组,起点,终点
	int i_temprs[i_max_size + 100];
	if (i_stIndex == i_enIndex){
		i_rs[i_stIndex] = i_src[i_stIndex];
	}else{
		int i_midIndex = (i_stIndex + i_enIndex) / 2;
		merge(i_src,i_temprs,i_stIndex,i_midIndex);
		merge(i_src,i_temprs,i_midIndex + 1,i_enIndex);
		mergeSort(i_temprs,i_rs,i_stIndex,i_midIndex,i_enIndex);
	}
	return;
}
桶排序bucket sort  O(n);需要O(k)额外空间

稳定的,因数组下标已经排好序,根据数组下标来排序

//桶排序
//

#include "stdafx.h"
#include <iostream>

using namespace std;
#define iBucketSize sizeof(iBucketSort)/sizeof(iBucketSort[0])

void bucketSort(int *iBucketSort,int *iBucketRs,const int size){
	for (int i = 0; i<size; i++){
		iBucketRs[iBucketSort[i]]++;
	}//排序完毕
	for (int i = 0; i< iBucketSize+1000; i++){
		while (iBucketRs[i] > 0){
			cout<<i<<" ";
			iBucketRs[i]--;
		}
	}
	cout<<endl;
	return;
}
int main(){
	int iBucketSort[] = {1,4,7,5,1,2,8};
	int iBucketRs[iBucketSize + 1000];
	memset(iBucketRs,0,iBucketSize + 1000);//初始化桶
	bucketSort(iBucketSort,iBucketRs,iBucketSize);
	return 0;
}


二叉查找树Binary treeO(n log n)期望时间; O(n2)最坏时间;需要O(n)额外空间

稳定的,规定了左右节点的大小,

#include "stdafx.h"
#include <iostream>
using namespace std;
#define iArrSize sizeof(iArr)/sizeof(iArr[0])
//二叉树中的节点编号,树顶是从1开始,则,每个节点的左孩子编号是父节点的编号值*2,右孩子的编号是*2+1,因为数组从0开始,所以,要再+1
void inertValue(int *iBinTree, int iNode, int iValue){
	if (iBinTree[iNode] == 0){//默认设定不对0插入
		iBinTree[iNode] = iValue;
	}else{
		if (iBinTree[iNode] > iValue){
			inertValue(iBinTree,iNode * 2 + 2,iValue);
		}else if (iBinTree[iNode] < iValue){
			inertValue(iBinTree,iNode * 2 + 1,iValue);
		}else return;//不插入已经存在的节点
	}
	非递归方法
	//while(iNode < 10000){
	//	if (iBinTree[iNode] == 0){//默认设定不对0插入
	//		iBinTree[iNode] = iValue;
	//		return;
	//	}
	//	if (iBinTree[iNode] > iValue){
	//		iNode = iNode * 2 + 2;
	//	}else if (iBinTree[iNode] < iValue){
	//		iNode = iNode * 2 + 1;
	//	}else return;//不插入已经存在的节点
	//}
	return ;
}

void printRs(int *iArr,int iNode){
	if (iArr[iNode] != 0){//默认设定不对0插入
		printRs(iArr,iNode * 2 + 1);
		cout<<iArr[iNode]<<" ";
		printRs(iArr,iNode * 2 + 2);
	}
	return;
}
int main(){
	int iArr[] = {9,10,200,3,400,12,-1};
	int iArrRs[iArrSize+10000];

	memset(iArrRs,0,iArrSize + 10000);
	for (int i = 0;i<iArrSize; i++){
		inertValue(iArrRs,0,iArr[i]);
	}

	//中序遍历
	printRs(iArrRs,0);
	cout<<endl;

	return 0;
}


-----------------------------------------我是不稳定的分割线-----------------------------------------------------

选择排序selection sort O(n2)

不稳定的,选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

void selectSort(int iselectArr[],const int iArrSize){
	for (int i = 0; i<iArrSize ; i++){
		for (int j = i+1; j>iArrSize; j++){
			if (iselectArr[j] > iselectArr[i]){
				iselectArr[j] ^= iselectArr[i];
				iselectArr[i] ^= iselectArr[j];
				iselectArr[j] ^= iselectArr[i];
			}
		}
	}
}
希尔排序 shell sortO(n log n)如果使用最佳的现在版本
不稳定的。  希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

 暂不实现

堆排序heapsort O(n log n)

不稳定的。我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n /2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了

void BuildMaxHeap(int iArrList[] ,int iArrSize,int index){//原数组,大小,节点编号
	int iLeftIndex = 2*index + 1;
	int iRightIndex = 2*index + 2;
	int largest = index;
	if((iRightIndex < iArrSize) ){
		//在建立大根堆时,如果父节点比两个子节点都小,则交换最大的一个子节点
		if((iArrList[iLeftIndex] < iArrList[iRightIndex])){
			largest = iRightIndex;
		}else{
			largest = iLeftIndex;
		}
	}else{
		if(iLeftIndex < iArrSize){
			largest = iLeftIndex;
		}
	}
	if((iArrList[index] < iArrList[largest]) && (largest != index)){
		iArrList[index] ^= iArrList[largest];
		iArrList[largest] ^= iArrList[index];
		iArrList[index] ^= iArrList[largest];
		//如果交换了某个节点的值,则需要递归交换其子树的节点
		BuildMaxHeap(iArrList,iArrSize,largest);
	}
}
void HeapSort(int iArrList[],int iArrSize){
	if(iArrSize < 0){
		return ;
	}
	for(int i=0;i<iArrSize;i++){
		for(int j = ((iArrSize - i)/2-1);j>=0;j--){//从最后一个孩子的父节点开始
			BuildMaxHeap(iArrList,iArrSize - i,j);
		}
		int tmp = iArrList[0];
		iArrList[0] = iArrList[iArrSize -1 - i];
		iArrList[iArrSize -1 - i] = tmp;
		/*
		iArrList[0] ^= iArrList[iArrSize -1 - i];
		iArrList[iArrSize -1 - i] ^= iArrList[0];
		iArrList[0] ^= iArrList[iArrSize -1 - i];*/
		for(int i=0;i<iArrSize;i++){
			cout<<iArrList[i]<<" ";
		}
		cout<<endl;
	}
	return ;
}

快速排序 Quickly Sort O(n log n)期望时间, O(n2)最坏情况;对于大的、乱数列表一般相信是最快的已知排序

 不稳定的。快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 910 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。

void quickly(int i_src[],int i_stIndex,int i_enIndex){//需要排序的数组
	if ( i_stIndex >= i_enIndex){
		return;
	}else{
		int i_pointIndex = i_stIndex;//以第一个元素为中间点
		for ( int i = i_stIndex + 1 ; i <= i_enIndex ; i++){
			if (i_src[i_pointIndex] < i_src[i]){//大于这个中间点的,就交换
				int i_tempValue = i_src[i];
				int i_temp = i;
				while( i_temp > i_pointIndex){
					i_src[i_temp ] = i_src[i_temp - 1];
					i_temp--;
				}
				i_src[i_pointIndex] = i_tempValue;
				i_pointIndex++;
				
				//i_src[i_pointIndex] ^= i_src[i];
				//i_src[i] ^= i_src[i_pointIndex];
				//i_src[i_pointIndex] ^= i_src[i];
				//i_pointIndex = i;
			}
		}
		quickly(i_src,i_stIndex,i_pointIndex);//分治
		quickly(i_src,i_pointIndex + 1,i_enIndex);
	}
	return ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值