【算法】常见的七种排序及其算法优化

54 篇文章 0 订阅
34 篇文章 1 订阅

1、插入排序

有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

大概步骤如上图,下面实现代码

void InsertSort(int* arr,size_t n)//插入排序
{
	assert(arr);
	for(size_t index=1;index<n;index++)
	{
		size_t tmp=arr[index];
		int pos=index-1;
		while (pos>=0&&arr[pos]>tmp)
		{
		arr[pos+1]=arr[pos];
		pos--;
		}
	
			arr[pos+1]=tmp;
		}
}


2、希尔排序

希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

大概过程如上图,代码如下

void ShellSort(int* arr,size_t n)//希尔排序
{
	int gap=n/3+1;
	for(size_t index=gap;index<n;index++)
	{
		size_t tmp=arr[index];
		int pos=index-gap;
		while(pos>=0&&arr[pos]>tmp)
		{
			arr[pos+gap]=arr[pos];
			pos-=gap;
		}
		arr[pos+gap]=tmp;
	}
}


3、冒泡排序

重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。

不再做过多解释,直接上代码

void PopSort(int* arr,size_t n)//冒泡排序
{
	for(int i=0;i<n;i++)
	{
		for(int j=i+1;j<n;j++)
		{
			if(arr[i]>arr[i+1])
			{
				int tmp=arr[i];
				arr[i]=arr[i+1];
				arr[i+1]=tmp;
			}
		}
	}
}


4、选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。



代码如下

void selectSort(int*arr ,size_t n)
{
	int min;
	int tmp;
	for(int i=0;i<n;i++)
	{
		
		for(int j=0;j<n;j++)
		{
			min=i;
			if(arr[min]>arr[j])
			{
				min=j;
			}
		
			tmp=arr[min];
			arr[min]=arr[j];
			arr[j]=tmp;
		}
	}
}



优化的选择排序

每次选一个最小值放最前面,选最大一个值放后面


代码如下

void SelectSort2(int* arr,int n)
{
	assert(arr);
	int left=0;
	int right=n-1;
    while (left<right)
    {
			int min=left;
			int max=right;
			for (int i=left;i<=right;++i)
			{
				if (arr[min]>=arr[i])
				{
					min=i;
				}
                
				if(arr[max]<=arr[i])
				{
					max=i;
				}
			
				
			}
		    swap(arr[left],arr[min]);
			if (left==max)
			{
				max=min;
			}
			swap(arr[right],arr[max]);
		    left++;
			right--;
    }

	}



5、快速排序

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列


挖坑法块排,选第一数做比较

void FastSort(int* arr,int low,int high)//设置一个低位一个高位,进行快速排序
{
	if(low>=high)
		return;
	int first=low;
	int last=high;
	int mid=ThreeMid(arr,first,last);
	swap(arr[mid],arr[first]);
	int key=arr[first];//用挖坑法
	while (first<last)
	{
		while(first<last&&arr[last]>=key)
		{
			--last;
		}
		arr[first]=arr[last];
		while (first<last&&arr[first]<=key)
		{
			++first;
		}
		arr[last]=arr[first];

	}
	arr[first]=key;
	FastSort(arr,low,first-1);
	FastSort(arr,first+1,high);
}


挖坑法选最后数做比较

单趟:

int PartSort2(int* arr,int first,int last)//挖坑法
{
	int left=first;
	int right=last;
	int key=ThreeMid(arr,first,last);
	swap(arr[key],arr[last]);
	int tmp=arr[last];
	while (left<right)
	{
		while (left<right&&arr[left]<=tmp)
		{
			++left;
		}
			arr[right]=arr[left];
		while(left<right&&arr[right]>=tmp)
		{
			--right;
		}
			arr[left]=arr[right];
	}
	arr[left]=tmp;
	return left;

}


递归

void QuickSort2(int* arr,int first,int last)
{

	if(first<last)
	{
		int key=PartSort2(arr,first,last);
		QuickSort2(arr,first,key-1);
		QuickSort2(arr,key+1,last);
	}
	
}


快排优化

当数组数超过13个进行快排,否则插入排序

void QuickSort2(int* arr,int first,int last)
{

	if(first<last)
	{
		if ((last-first)>=13)
		{
			int key=PartSort2(arr,first,last);
			QuickSort2(arr,first,key-1);
			QuickSort2(arr,key+1,last);
		}
		else
			InsertSort(arr,sizeof(arr)/sizeof(arr[0]));
		
	}
	
}


前后指针法,数组前后个一个指针,同时进行排序

int PartSort(int* arr,int left,int right)
{	
    int mid=ThreeMid(arr,left,right);
	swap(arr[mid],arr[right]);
	int key=arr[right];
	int first=left;
	int last=right-1;
	while(first<last)
	{
		while (first<last&&arr[first]<=key)
		{
			++first;
		}
		while (first<last&&arr[last]>=key)
		{
			--right;
		}
		swap(arr[first],arr[last]);
	}
	if(arr[first]>arr[right]);
	swap(arr[first],arr[right]);
	return first;
}
void QuickSort3(int* arr,int left,int right)
{
	while (left<right)
	{
		int mid=PartSort(arr,left,right);
		QuickSort3(arr,left,mid-1);
		QuickSort3(arr,mid+1,right);
	
	}
}




6、堆排序

堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

建堆

void AdjustDown(int* arr,int index,int n)
{
	int parent=index;
	int child=2*parent+1;
	while (child<n)
	{
		if(child<n&&arr[child]<arr[child+1])
			child++;
	    if (child<n&&arr[child]>arr[parent])
		{
			swap(arr[parent],arr[child]);
			parent=child;
			child=2*parent+1;
		}
		else
			break;
	}
	child=parent;
}


堆排序

void HeapSort(int* arr,int n)
{
	for(int i=n/2-1;i>=0;--i)
	{
      AdjustDown(arr,i,n);
	}
	for(int j=n-1;j>0;--j)
	{
		swap(arr[j],arr[0]);
        AdjustDown(arr,0,j);
	}
	if(arr[0]>arr[1])
	swap(arr[0],arr[1]); 
}


7、归并排序

归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。



单趟排序

void Merge(int* arr,int* tmp,int first,int mid,int last)//归并
{
	int i=first;int j=mid+1;int k=first;
	while (i!=mid+1&&j!=last+1)
	{
		if(arr[i]>arr[j])
			tmp[k++]=arr[j++];
		else
			tmp[k++]=arr[i++];
	}
	while(i!=mid+1)
	{
		tmp[k++]=arr[i++];
	}
	while (j!=last+1)
	{
		tmp[k++]=arr[j++];
	}
	for(i=first;i<=last;i++)
		arr[i]=tmp[i];
}


递归归并

void MergeSort(int* arr,int* tmp,int first,int last)//归并排序
{
	int mid;
	if (first<last)
	{
		mid= first+((last-first)>>1);          
		MergeSort(arr,tmp,first,mid);
		MergeSort(arr,tmp,mid+1,last);
		Merge(arr,tmp,first,mid,last);
	}
}

三数求中法
int ThreeMid(int* arr,int left,int right)//找首位,末尾和中点中中间的数
{
	int mid=left+((right-left)>>1);
	while (left<right)
	{
		if(arr[left]<arr[right])
		{
			if(arr[mid]<arr[left])
				return left;
			else if(arr[right]<arr[mid])
				return right;
			else
				return mid;
		}
		if(arr[left]>arr[right])
		{
			if(arr[mid]<arr[right])
				return right;
			else if(arr[mid]>arr[left])
				return left;
			else
				return mid;
		}
	}
	return mid;
}


三数中中间值进行比较避免了用最后一位比较时最后一位是最大值或用第一位比较时第一位是最小值,降低了时间复杂度。

测试代码

int main()
{
	int arr[]={9,3,5,1,4,7,8,0};
	int tmp[8];
	//int arr[]={2,3,6,5,4,13,56};
	cout<<"原数组  : ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	InsertSort(arr,sizeof(arr)/sizeof(arr[0]));
	cout<<"插入排序: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	ShellSort(arr,sizeof(arr)/sizeof(arr[0]));
	cout<<"希尔排序: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	PopSort(arr,sizeof(arr)/sizeof(arr[0]));
	cout<<"冒泡排序: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	selectSort(arr,sizeof(arr)/sizeof(arr[0]));
	cout<<"选择排序: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	FastSort(arr,0,sizeof(arr)/sizeof(arr[0])-1);
	cout<<"快速排序: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	SelectSort2(arr,sizeof(arr)/sizeof(arr[0]));
	cout<<"选择排序2:";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	HeapSort(arr,sizeof(arr)/sizeof(arr[0]));
	cout<<"堆排序:   ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	QuickSort2(arr,0,sizeof(arr)/sizeof(arr[0])-1);
	cout<<"快速排序2: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	MergeSort(arr,tmp,0,sizeof(arr)/sizeof(arr[0])-1);
	cout<<"归并排序: ";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	QuickSort2(arr,0,sizeof(arr)/sizeof(arr[0])-1);
	cout<<"快速排序3:";
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	system("pause");                
	return 0;
}



测试用例

int arr[]={9,3,5,1,4,7,8,0};






测试用例

int arr[]={2,3,6,5,4,13,56};



欢迎补充

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值