排序算法的总结,思考和优化

稳定的
冒泡排序(bubble sort) — O(n^2)
鸡尾酒排序(Cocktail sort,双向的冒泡排序) — O(n^2)
插入排序(insertion sort)— O(n^2)
桶排序(bucket sort)— O(n); 需要 O(k) 额外空间
计数排序(counting sort) — O(n+k); 需要 O(n+k) 额外空间
合并排序(merge sort)— O(nlog n); 需要 O(n) 额外空间
原地合并排序— O(n^2)
二叉排序树排序 (Binary tree sort) — O(nlog n)期望时间; O(n^2)最坏时间; 需要 O(n) 额外空间
鸽巢排序(Pigeonhole sort) — O(n+k); 需要 O(k) 额外空间
基数排序(radix sort)— O(n·k); 需要 O(n) 额外空间
Gnome 排序— O(n^2)
图书馆排序— O(nlog n) with high probability,需要 (1+ε)n额外空间
不稳定的
选择排序(selection sort)— O(n^2)
希尔排序(shell sort)— O(nlog n) 如果使用最佳的现在版本
组合排序— O(nlog n)
堆排序(heapsort)— O(nlog n)
平滑排序— O(nlog n)
快速排序(quicksort)— O(nlog n) 期望时间,O(n^2) 最坏情况; 对于大的、乱数列表一般相信是最快的已知排序
Introsort— O(nlog n)
耐心排序— O(nlog n+ k) 最坏情况时间,需要 额外的 O(n+ k) 空间,也需要找到最长的递增子串行(longest increasing subsequence)
不实用的
Bogo排序— O(n× n!) 期望时间,无穷的最坏情况。
Stupid sort— O(n^3); 递归版本需要 O(n^2) 额外存储器
珠排序(Bead sort) — O(n) or O(√n),但需要特别的硬件
Pancake sorting— O(n),但需要特别的硬件

stooge sort——O(n^2.7)很漂亮但是很耗时

--摘自百度百科(排序算法)

冒泡排序:

(1)原版

for(int i = 0; i < n-1; i++)
	for(int j = i; j < n;j++)
		if(arr[j] > arr[j+1])
			swap(arr[j],arr[j+1]);




选择排序:(每次选出第i小/大的数)
(1)原版

for(int i = 0; i < n; i++)
{
	//选择当前最小的元素
	minIndex = i; 
	for(int j = i+1; j < n; j++)
		if(arr[j] < arr[minIndex])
			minIndex = j;
	swap(arr[i],arr[minIndex]);
}




插入排序:(每次跟前一个数据比较)

(1)原版

for(int i = 1; i < n; i++)
	//寻找arr[i]合适的插入位置
	for(int j = i; j > 0; j--)
		if(arr[j] < arr[j-1])
			swap(arr[j],arr[j-1]);
		else
		        break;

(2)代码简化版(将判断条件放入了if语句中,节省了代码量)

for(int i = 1; i < n; i++)
	for(int j = i; j > 0 && arr[j] < arr[j-1]; j--)
		swap(arr[j],arr[j-1]);

(3)效率优化版(将复杂的交换换成了比较,直接赋值

for(int i = 1; i < n; i++)
	int e = arr[i];
	for(int j = i; j > 0 && arr[j-1] > e; j--)//寻找e应该插入的位置
		arr[j]=arr[j-1];
	arr[j] = e;
总结:虽然插入排序时间复杂度为O(n2),但是在处理有规律的数据的时候用效率优化版的代码甚至可以达到O(n)的程度,因为数据有规律时其实只有少部分数据需要进行赋值操作,并且比较次数也减少了,而在现实生活中其实有很多数据是有规律的,所以用插入排序可以达到很好的效果。



希尔排序

(1)原版

void shell_sort(int arr[],int lenth)
{
	int incre = lenth;
	while(true)
	{
		incre/=2;
		for(int k =0; k < incre;k++)//根据增量分为若干子序列
		{
			for(int i = k+incre;i<lenth;i+=incre)
			{
				for(int j = i; j>k; j-=incre)
				{
					if(arr[j] < arr[j-incre])
						swap(arr[j],arr[j-incre]);
					else
						break;
				}
			}
		}
		if(incre==1)
			break;
	}
}




归并排序

(1)原版

void Merge(int arr[],int lenth)//调用归并排序
{
	MergeSort(arr,0,n-1);
	return;
}

int MergeSort(int arr[],int l, int r)//递归排序
{
	if(l >= r)
		return;
	int mid = (l+r)/2;
	MergeSort(arr,l,mid);
	MergeSort(arr,mid,r);
	merge(arr,l,mid,r);
}

void merge(int arr[],int l,int mid, int r)
{
	int temp[r-l+1];
	for(int i = l; i < r; i++)
		temp[i-l] = arr[i];
	
	int i = l, j = mid +1;
	for(int k = l; k <= r; k++)
	{
		if(i > mid)						//如果i和j超出范围,则直接赋值无需比较
			{arr[k] = arr[j-l];j++;}
		else if(j > r)
			{arr[k] = arr[i-l];i++}
		else if(temp[i-l] < temp[j-l])
			{arr[k] = temp[i-1];i++;}
		else
			{arr[k] = temp[j-1];j++}
	}
}


(2)优化

void Merge(int arr[],int lenth)//调用归并排序
{
	MergeSort(arr,0,n-1);
	return;
}

int MergeSort(int arr[],int l, int r)//递归排序
{
	if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
	{
		insertionSort(arr,l,r);
		return;
	}
	int mid = (l+r)/2;
	MergeSort(arr,l,mid);
	MergeSort(arr,mid,r);
	//因为两边的数组有序,先判断边界值是否也有序然后在决定是否要合并,这样效率更高
	if(arr[mid] > arr[mid+1])
		merge(arr,l,mid,r);
}

void merge(int arr[],int l,int mid, int r)
{
	int temp[r-l+1];
	for(int i = l; i < r; i++)
		temp[i-l] = arr[i];
	
	int i = l, j = mid +1;
	for(int k = l; k <= r; k++)
	{
		if(i > mid)						//如果i和j超出范围,则直接赋值无需比较
			{arr[k] = arr[j-l];j++;}
		else if(j > r)
			{arr[k] = arr[i-l];i++}
		else if(temp[i-l] < temp[j-l])
			{arr[k] = temp[i-1];i++;}
		else
			{arr[k] = temp[j-1];j++}
	}
}

void insertionSort(int arr[], int l, int r)
{
	int lenth = r-1+1;
	for(int i =l+1; i < lenth; i++)
	{
		int e = arr[i]
		for(int j = i; j > l && arr[j-1] > e; j--)
			arr[j]=arr[j-1];
		arr[j]= e;
	}
}

(3)自底向上

void MergeBU(int arr[], int lenth)
{
	for(int sz = 1; sz <lenth; sz+=sz)
		for(int i = 0; i+sz< lenth; i+=sz+sz)
		//对arr[i,···i+sz-1]和arr[i+sz,···i+sz+sz-1]进行归并
			merge(arr,i,i+sz-1,min(i+sz+sz-1,n-1));//对数组边界进行限定,防止越界
}
void merge(int arr[],int l,int mid, int r)
{
	int temp[r-l+1];
	for(int i = l; i < r; i++)
		temp[i-l] = arr[i];
	
	int i = l, j = mid +1;
	for(int k = l; k <= r; k++)
	{
		if(i > mid)						//如果i和j超出范围,则直接赋值无需比较
			{arr[k] = arr[j-l];j++;}
		else if(j > r)
			{arr[k] = arr[i-l];i++}
		else if(temp[i-l] < temp[j-l])
			{arr[k] = temp[i-1];i++;}
		else
			{arr[k] = temp[j-1];j++}
	}
}



总结:归并排序先用二分法降低排序规模再进行排序达到了O(nlogn)的时间复杂度,在排序大量无序数据的时候,效率肯定是要比O(n2)的算法要高。不过原版的归并排序其实也有可以改进的地方,比如说在用递归拍好了两边数组的顺序后,如果能先判断一下这两个数组连起来是否已经有序,若无序在进行归并,有序则直接跳过归并数组的操作。而且因为两边数组已经有序,所以判断连起来是否有序只需判断边界值,一个if语句就能实现,在性能上是可以接受的。

另外一个优化的地方是数组长度为1的时候直接return,这样肯定会出现大量的递归操作,如果在长度比较小的时候,利用优化的插入排序直接排序,则可以避免这种损失。而且在较小规模的时候,插入排序算法确实能起到加速的作用!




快速排序

(1)原版

//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition(int arr[],int l,int r)
{
	int k = arr[l];
	int j = l;
	for(i = l+1; i < r;i++)
		if(arr[i] < k)
			swap(arr[++k],arr[i]);
	swap(arr[l],arr[j]);
	
	return j;
	
}
//对arr[l,····r]进行快速排序
void quicksort(int arr[],int l,int r)
{
	if(l>=r)
		return;
	int p = partition(arr,l,r);
	
	quicksort(arr,l,p-1);
	quicksort(arr,p+1,r);
}

void quick(int arr[],int lenth)
{
	quicksort(arr,0,lenth-1 );
}

(2)优化

//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition(int arr[],int l,int r)
{
	//随机选择第一个数,避免了算法复杂度退化为O(n2)
	swap(arr[l],arr[rand()%(r-l+1)]);
	
	int k = arr[l];
	int j = l;
	for(i = l+1; i < r;i++)
		if(arr[i] < k)
			swap(arr[++k],arr[i]);
	swap(arr[l],arr[j]);
	
	return j;
	
}
//对arr[l,····r]进行快速排序
void quicksort(int arr[],int l,int r)
{

	if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
	{
		insertionSort(arr,l,r);
		return;
	}
	int p = partition(arr,l,r);
	
	quicksort(arr,l,p-1);
	quicksort(arr,p+1,r);
}

void quick(int arr[],int lenth)
{
	quicksort(arr,0,lenth-1 );
}

(3)再优化

//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition(int arr[],int l,int r)
{
	//随机选择第一个数,避免了算法复杂度退化为O(n2)
	swap(arr[l],arr[rand()%(r-l+1)]);
	int k = arr[l];
	//arr[l+1···i]<=k,arr[j···r]>=k
	int i=l+1,j=r;
	while(true)//这样处理是为了出现大量等于k的元素时,依旧能平均的分在两边,这样就能避免算法在处理大量重复元素的时候退化到O(n2)
	{
		while(i <=r && arr[i]<=k) i++;
		while(j > l && arr[j]>=k) j--;
		if(i > j)	break;
		swap(arr[i],arr[j]);
		i++;
		j--;
	}
	return j;
	
}
//对arr[l,····r]进行快速排序
void quicksort(int arr[],int l,int r)
{

	if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
	{
		insertionSort(arr,l,r);
		return;
	}
	int p = partition(arr,l,r);
	
	quicksort(arr,l,p-1);
	quicksort(arr,p+1,r);
}

void quick(int arr[],int lenth)
{
	quicksort(arr,0,lenth-1 );
}

快速三路排序

//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition3ways(int arr[],int l,int r)
{
	//随机选择第一个数,避免了算法复杂度退化为O(n2)
	swap(arr[l],arr[rand()%(r-l+1)]);
	int k = arr[l];
	//arr[l+1···i]<=k,arr[j···r]>=k
	int lt= l;//arr[l+1···lt]<k
	int gt = r+1;arr[gt···r]>k
	int i = l+1;//arr[lt+1···i]==k
	
	while(i < gt)
	{
		if(arr[i] < k)
			{swap(arr[++lt],arr[i]);i++;}
		else if(arr[i]>k)
			swap(arr[--gt],arr[i]);
		else
			i++;
	}
	swap(arr[l],arr[lt]);
}

//对arr[l,····r]进行快速排序
void quicksort3ways(int arr[],int l,int r)
{

	if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
	{
		insertionSort(arr,l,r);
		return;
	}
	int p = partition(arr,l,r);
	
	quicksort3ways(arr,l,p-1);
	quicksort3ways(arr,p+1,r);
}

void quick3ways(int arr[],int lenth)
{
	quicksort3ways(arr,0,lenth-1 );
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值