重新总结排序算法C++

想复习一波,发现之前的帖子不见了。好吧,重新弄一遍。

 

稳定排序和非稳定排序

稳定排序——保证排序前两个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。

 

稳定排序

  • 插入排序
  • 冒泡排序
  • 归并排序
  • 基数排序

 

不稳定排序

  • 希尔排序
  • 直接选择排序
  • 堆排序
  • 快速排序

 

 

插入排序

对于数组,从前向后遍历。对于遍历的每个元素a[i],如果元素a[i]比a[i-1]小,则不断向前寻找该元素适合的位置。在查找位置的过程中,不断将大于a[i]的数组元素向后移动一个位置。

void insert_sort(vector<int>&vec){
	vector<int>res(vec.size());
	res[0]=vec[0];
	int index=0;
	for(int i=1; i<vec.size(); i++){
		if(vec[i]>=res[i-1])
			res[i]=vec[i];
		else{
			index=i-1;
			/*index必须有等于0的时候,比如vec[i]=2,res[index]=4时,假定此时i=1;
			如果不允许index的值为0,则res[0]位置的值没机会向后移动
			*/
			while(index>=0 && vec[i]<res[index]){
				res[index+1]=res[index];
				index--;
			}
			if(index<-1){
				cout<<"查找过程出现问题"<<endl;
				return;	
			}
			res[index+1]=vec[i];
		}
	}

	cout<<"插入排序结果:";
	for(auto i:res)
		cout<<i<<" ";
	cout<<endl;
}

 

 

希尔排序

是插入排序的一种高速而稳定的改进版本。希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 1.插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
  • 2.但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。

希尔排序的一般步骤为:

  1. 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中,在各组内进行直接插人排序。
  2. 取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

步长的选择是希尔排序的重要部分。只要最终步长为1任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。

void shell_sort(vector<int>vec){
	int gap=vec.size()/2;
	int target=0, index=0;
	while(gap){ //最后一次比较的时候,gap=1
		//多组插入排序
		for(int i=gap; i<vec.size(); i++){
			target=vec[i];
			index=i;
			while(index>=gap && target<vec[index-gap]){
				vec[index]=vec[index-gap];
				index-=gap;
			}
			vec[index]=target;
		}
		gap/=2;
	}
	cout<<"希尔排序结果:";
	for(auto i: vec)
		cout<<i<<" ";
	cout<<endl;
}

 

 

直接选择排序

选择排序的基本思想: 
每一趟在n到i+1(i=1,2,3…,n-1)个记录中选取关键字最小的记录与第i个记录交换,并作为有序序列中的第i个记录。

例如: 
待排序列: 43,65,4,23,6,98,2,65,7,79 
第一趟: 2,65,4,23,6,98,43,65,7,79 
第二趟: 2,4,65,23,6,98,43,65,7,79 
第三趟: 2,4,6,23,65,98,43,65,7,79 
第四趟: 2,4,6,7,43,65,98,65,23,79 
第五趟: 2,4,6,7,23,65,98,65,43,79 
第六趟: 2,4,6,7,23,43,98,65,65,79 
第七趟: 2,4,6,7,23,43,65,98,65,79 
第八趟: 2,4,6,7,23,43,65,65,98,79 
第九趟: 2,4,6,7,23,43,65,65,79,98

void select_sort(vector<int>vec){
	int index=0, smallest=0, change_index=0;
	while(index<vec.size()){
		//赋值最小值的初值为当前位置的值
		smallest=vec[index];
		//遍历数组后边的元素,并记录最小值的位置
		for(int i=index+1; i<vec.size(); i++){
			if(smallest>vec[i]){
				smallest=vec[i];
				change_index=i;
			}
		}
		//说明最小值的位置不是当前值的位置。将元素进行交换
		if(vec[index]!=smallest){
			int temp=vec[index];
			vec[index]=smallest;
			vec[change_index]=temp;

		}
		index++;	
	}		
	cout<<"选择排序结果:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

 

 

堆排序

堆排序比较有意思。堆排序的底层是一个数组,只不过数组中元素的值通过一定的规则进行维护罢了。

stl里面的优先队列底层就是大顶堆,可以通过修改优先队列的比较规则,将大顶堆修改成小顶堆。堆的详细介绍见传送门——来堆的概念,heapinsert、heapify的过程

(算了,还是老老实实的总结一下吧)

堆是一棵完全二叉树(满二叉树的每一层都是满的,完全二叉树允许叶节点那一层是节点不满,但是节点必须从左到右的排布,中间出现空缺,就不是完全二叉树了,如下图中左侧为完全二叉树,右侧就不是)

堆是通过数组来实现的。

位置的(如下图所示,画成图好记一点)

  • 左孩子的下标是  2*i+1 
  • 右孩子的下标是 2*i+2 .(前提是不越界)。
  • 节点的父节点下标为 (i-1)/2 

从始至终只有数组结构,堆是脑补出来的结构,自始至终真实存在的只有数组。

 

 

堆分为两种:大根堆和小根堆

 

针对大顶堆,进行如下讨论。小顶堆同理。

1、heapinsert原理——每次都与父节点进行比较

将数组中的数字依次加入到堆中。对于每一个加入到堆中的数字,将其与其父节点的值进行比较,如果他大,则将他和他父节点的值进行交换。他的索引位置为  i ,则其父节点的索引位置为  (i-1)/2

注:-1/2的结果为0,所以index=0的时候也不用怕

#include<iostream>
#include<vector>
using namespace std;
 
void heapinsert(vector<int>&nums){
	if(!nums.size()) return;
 
	int temp=0, index=0;
	for(int i=0; i<nums.size(); i++){
 
		index=i;
		// -1/2的结果为0,所以index=0的时候也不用怕
		//不断的与父节点进行比较
		while(nums[index]>nums[(index-1)/2]){
			temp=nums[index];
			nums[index]=nums[(index-1)/2];
			nums[(index-1)/2]=temp;
			index=(index-1)/2;
		}
	}
	
	for(auto i:nums)
		cout<<i<<endl;
}
 
int main(){
	vector<int>nums={1,2,3,4,5,6,7,8,9};
	heapinsert(nums);
 
	return 0;
}

 

2、分析建立大根堆过程的复杂度

当一个数加进来,他最多只和这棵树的高度个数进行比较(调整)。任何一个点加进来,我只和我沿途的这些个点进行比较,和其他的点没什么关系

建立一个大根堆的时间复杂度是O(N)=log1+log2+...+log(N-1)

 

3、heapify过程——每次都与子节点进行比较

堆中某个值发生了变化,如何调整。

比如大根堆中,索引位置为 i 的元素的值发生了改变(变小),则将其与左右两个孩子进行比较,将其与较大的孩子进行交换。 左孩子的索引位置为 2*i+1 , 右孩子的索引位置为 2*i+2.

#include<iostream>
#include<vector>
using namespace std;
 
void heapinsert(vector<int>&nums){
	if(!nums.size()) return;
 
	int largest=0, i=0, temp=0;
	for(int index=0; index<nums.size(); index++){
		i=index;
		while((2*i+1)<nums.size()){
			
			//先判断右孩子是否越界,再选择两个孩子中的较大者,将大者的索引位置存入largest中
			largest=(2*i+2)<nums.size()?
				nums[2*i+1]>nums[2*i+2]?2*i+1:2*i+2
			:2*i+1;		
		
			//说明nums[i]已经不能再向下移动了
			if(nums[i]>=nums[largest]) break;
 
			//交换两个元素的值
			temp=nums[i];
			nums[i]=nums[largest];
			nums[largest]=temp;
 
			//此时该继续向下比较了
			i=largest;
		}
	}
	
	for(auto i:nums)
		cout<<i<<endl;
}
 
int main(){
	vector<int>nums={1,9,8,7,6,5,4,3,2};
	heapinsert(nums);
 
	return 0;
}

 

4、对于stl中的优先队列

使用的时候需要引入头文件

#include<queue>

默认是大顶堆,可以通过将用于比较的仿函数进行修改,使其变成小顶堆(要把模板的3个参数都带进去。STL里面定义了一个仿函数greater<>)。如果是比较自己写的类对象,则可以自己写仿函数。

priority_queue<int, vector<int>,greater<int>> minHeap;

如果使用默认的,可以这么声明

priority_queue<int> q;

 

 

冒泡排序

一趟冒泡排序的过程为:首先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两个记录交换之,然后比较第二个记录的关键字和第三个记录的关键字,依次类推,直至第n-1个记录和第n个记录的关键字进行过比较为止; 
在冒泡排序的过程中,关键字较小的记录好比水中气泡逐趟向上漂浮,而关键字较大的记录好比石头往下沉,每一趟有一块“最大”的石头沉到水底。

例如: 
待排序列:43, 65, 4, 23, 6, 98, 2, 65, 7, 79 
第一趟: 43, 4,23,6,65,2,65,7,79,98 
第二趟: 4,23,6,43,2,65,7,65,79,98 
第三趟: 4,6,23,2,43,7,65,65,79,98 
第四趟: 4,6,2,23,7,43,65,65,79,98 
第五趟: 4,2,6,7,23,43,65,65,79,98 
第六趟: 2,4,6,7,23,43,65,65,79,98

void bubble_sort(vector<int>vec){
	int temp=0;
	for(int i=0; i<vec.size(); i++){
		//每次遍历,最后就有一个位置的元素被固定,因此搜索空间在不断降低
		for(int j=0; j<vec.size()-i-1; j++){
			if(vec[j]>vec[j+1]){
				temp=vec[j];
				vec[j]=vec[j+1];
				vec[j+1]=temp;
			}
		}
	}
	cout<<"冒泡排序结果:";
	for(auto i: vec)
		cout<<i<<" ";
	cout<<endl;
}

 

 

快速排序

快排是面试经常让手写的,不过我没被面过手写快排。一般喜欢面非递归手写快排。

假设我们现在对“ 1  2 7  9  3  4  5 10  8”这个10个数进行排序。首先在这个序列中随便找一个数作为基准数。

为了方便,就让第一个数6作为基准数。接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,类似下面这种排列。

       3  1  2 5  4  6  9 7  10  8

现在基准数6已经归位,它正好处在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3  1 2  5  4”,右边的序列是“9  7  10  8”。接下来还需要分别处理这两个序列。

左边的序列是“3  1  2 5  4”。请将这个序列以3为基准数进行调整,使得3左边的数都小于等于3,3右边的数都大于等于3。调整完毕之后的序列的顺序是:

        2  1  3  5  4

 OK,现在3已经归位。接下来需要处理3左边的序列“2 1”和右边的序列“5 4”。对序列“2 1”以2为基准数进行调整,处理完毕之后的序列为“1 2”,到此2已经归位。序列“1”只有一个数,也不需要进行任何处理。至此我们对序列“2 1”已全部处理完毕,得到序列是“1 2”。序列“5 4”的处理也仿照此方法,最后得到的序列如下。

        1  2  3 4  5  6 9  7  10  8

对于序列“9  7  10  8”也模拟刚才的过程,直到不可拆分出新的子序列为止。最终将会得到这样的序列,如下。

        1  2  3 4  5  6  7  8 9  10

 到此,排序完全结束。快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止,排序就结束了

 

1、代码递归版

int findPos(vector<int>&vec, int start, int end){
	int temp=vec[start];
	while(start<end){
		//从后往前找比标兵值小的元素位置
		while(start<end && vec[end]>=temp)
			end--;
		//将该值移动到标兵左边
		vec[start]=vec[end];
		//从前往后找到比标兵值大的元素
		while(start<end && vec[start]<=temp)
			start++;
		//将该值移动到标兵右边
		vec[end]=vec[start];
	}
	//将标兵放置在其正确的位置
	vec[start]=temp;
	return start;
}

void quick_sort_recu(vector<int>&vec, int start, int end){
	if(start>end) return;
	int pos=findPos(vec, start, end);
	quick_sort_recu(vec, start, pos-1);
	quick_sort_recu(vec, pos+1, end);
}

 

2、代码非递归版

非递归的主要实现方式就是利用栈来模拟函数递归调用时候的入栈出栈操作。

对于非递归快排。记录每一次的合理区间。以栈的容量作为排序是否结束的衡量依据,因为递归版运行结束之后,栈就是空的。

递归版每次传入的参数就是非递归每次需要通过栈来存储的参数。好好想想递归与非递归的关系,转化起来不说很困难。

int findPos(vector<int>&vec, int start, int end){
	int temp=vec[start];
	while(start<end){
		//从后往前找比标兵值小的元素位置
		while(start<end && vec[end]>=temp)
			end--;
		//将该值移动到标兵左边
		vec[start]=vec[end];
		//从前往后找到比标兵值大的元素
		while(start<end && vec[start]<=temp)
			start++;
		//将该值移动到标兵右边
		vec[end]=vec[start];
	}
	//将标兵放置在其正确的位置
	vec[start]=temp;
	return start;
}

//快排,非递归
void quick_sort(vector<int>vec,int start, int end){
	stack<int>st;
	int k=0;
	if(start<end){
		st.push(start);
		st.push(end);
		while(st.size()){
			int j=st.top(); st.pop();
			int i=st.top(); st.pop();
			
			//每轮确定一个值的位置
			k=findPos(vec, i, j);

			//将接下来要遍历的区间压栈
			if(i<k-1){
				st.push(i);
				st.push(k-1);
			}
			if(k+1<j){
				st.push(k+1);
				st.push(j);
			}
		}
	}
	cout<<"非递归快排:";
	for(auto i:vec){
		cout<<i<<" ";
	}
	cout<<endl;
}

 

快排还有改进方式。先占位,以后补一下。

 

 

归并排序

这个讲得好——https://www.cnblogs.com/skywang12345/p/3602369.html

归并排序常用于外排序,因为他需要一个辅助数组来进行来回来回的复制,因此内排序常常不用归并排序。(emmm,什么是来回来回复制?看下面的代码就懂了)

 

1. 从下往上的归并排序(非递归实现)

将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就得到了我们想要的排序结果。

实现代码(非递归)

//传入三个点,因为待归并的两个有序数组长度可能不同
void merge_un(vector<int>&vec, vector<int>&ans, 
		int left_start, int right_start, int right_end){
	int left_end=right_start-1;
	int len=right_end-left_start;
	int index=left_start;

	while(left_start<=left_end && right_start<=right_end){
		ans[index++]=vec[left_start]<=vec[right_start]?
				vec[left_start++]:vec[right_start++];
	}

	while(left_start<=left_end) ans[index++]=vec[left_start++];
	while(right_start<=right_end) ans[index++]=vec[right_start++];

	while(len--)
		vec[right_end--]=ans[right_end];
}

//两两归并相邻有序子列
void merge_pass(vector<int>&vec, vector<int>&ans, int len){
	int i, j, size=ans.size();
	//每次循环,i=i+2*len,因为归并的两段总长度为2*len
	//每次都跳过归并好的那一段,然后去寻找下一个两段进行归并
	//i<ans.size()-2*len,保证了前面成对的都处理完
	//对于尾巴,要另外处理。尾巴可能是两块不一样长的,也可能只有一块
	for(i=0; i<=size-2*len; i+=2*len)
		merge_un(vec, ans, i, i+len, i+2*len-1);
	//说明尾巴有两块,一块长度为len,另一块长度为vec.size()-i-len
	if(i+len<size)
		merge_un(vec, ans, i, i+len, size-1);
	else //说明尾巴只有一块,那么直接拷贝即可
		for(j=i; j<size; j++) ans[j]=vec[j];	
}

//自底向上,非递归实现
void merge_sort_un(vector<int>vec){
	vector<int>ans(vec.size());
	int len=1;

	while(len<vec.size()){
		//此次循环之后,归并的结果存在vec里面
		merge_pass(vec, ans, len);
		len*=2;
		//此次循环之后,将循环结果拷贝回vec中。
		//每次while循环都执行两边merge_pass,使得最终结果保证在vec中存储
		merge_pass(ans, vec, len);
		len*=2;
	}
	cout<<"归并排序非递归版本:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

 

2. 从上往下的归并排序(递归实现)

它与"从下往上"在排序上是反方向的。它基本包括3步:

  • ① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2; 
  • ② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
  • ③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。

从上往下归并的实现代码(递归版) 

思路:先递归拆分,当当前元素不可继续拆分时,再调用归并函数进行归并。每次归并结束后,需要把成果拷贝到原始数组处,因为每次都是根据原始数组中的元素进行归并,不拷贝,那么归并这步相当于没做。这与上面的非递归代码存在差异(非递归直接把结果拷贝到辅助数组中,不动原始数组中的元素)

//同理于两个有序数组归并
//利用三个指针,分别指向三个数组的首地址。每次将两个有序数组中较小的元素插入到新数组中
void merge(vector<int>&vec, vector<int>&ans, int left_start, int right_end){
	int left_end=(right_end+left_start)/2;
	int right_start=left_end+1;
	//结果数组的索引下标
	int index=left_start;
	int len=right_end-left_start+1;

	while(left_start<=left_end && right_start<=right_end){
		ans[index++]=vec[left_start]<=vec[right_start]?
				vec[left_start++]:vec[right_start++];
	}

	//此时两个有序数组,至少有一个已经全部纳入结果数组中
	while(left_start<=left_end) ans[index++]=vec[left_start++];
	while(right_start<=right_end) ans[index++]=vec[right_start++];

	//将辅助数组中的元素拷贝到原数组中
	//因为每次比较的使用,是用原数组进行比较的。不修改原数组,那每次归并就都在做无用功
	while(len--){
		vec[right_end--]=ans[right_end];
	}
}

//递归拆分数组
void msort(vector<int>&vec, vector<int>&ans, int start, int end){
	int mid=(start+end)/2;
	//若待拆区间长度大于1,则继续进行拆分
	if(start<end){
		msort(vec, ans, start, mid);
		msort(vec, ans, mid+1, end);
		merge(vec, ans, start, end);
	}
}

//归并排序,定义统一接口
void merge_sort(vector<int>vec){
	vector<int>ans(vec.size());
	msort(vec, ans, 0, vec.size()-1);
	cout<<"归并排序结果:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

 

 

各个排序放在一起的代码

#include<iostream>
#include<vector>
#include<stack>
using namespace std;

void insert_sort(vector<int>&vec){
	vector<int>res(vec.size());
	res[0]=vec[0];
	int index=0;
	for(int i=1; i<vec.size(); i++){
		if(vec[i]>=res[i-1])
			res[i]=vec[i];
		else{
			index=i-1;
			/*index必须有等于0的时候,比如vec[i]=2,res[index]=4时,假定此时i=1;
			如果不允许index的值为0,则res[0]位置的值没机会向后移动
			*/
			while(index>=0 && vec[i]<res[index]){
				res[index+1]=res[index];
				index--;
			}
			if(index<-1){
				cout<<"查找过程出现问题"<<endl;
				return;	
			}
			res[index+1]=vec[i];
		}
	}

	cout<<"插入排序结果:";
	for(auto i:res)
		cout<<i<<" ";
	cout<<endl;
}

void shell_sort(vector<int>vec){
	int gap=vec.size()/2;
	int target=0, index=0;
	while(gap){ //最后一次比较的时候,gap=1
		//多组插入排序
		for(int i=gap; i<vec.size(); i++){
			target=vec[i];
			index=i;
			while(index>=gap && target<vec[index-gap]){
				vec[index]=vec[index-gap];
				index-=gap;
			}
			vec[index]=target;
		}
		gap/=2;
	}
	cout<<"希尔排序结果:";
	for(auto i: vec)
		cout<<i<<" ";
	cout<<endl;
}

void select_sort(vector<int>vec){
	int index=0, smallest=0, change_index=0;
	while(index<vec.size()){
		smallest=vec[index];
		for(int i=index+1; i<vec.size(); i++){
			if(smallest>vec[i]){
				smallest=vec[i];
				change_index=i;
			}
		}
		if(vec[index]!=smallest){
			int temp=vec[index];
			vec[index]=smallest;
			vec[change_index]=temp;

		}
		index++;	
	}		
	cout<<"选择排序结果:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

void bubble_sort(vector<int>vec){
	int temp=0;
	for(int i=0; i<vec.size(); i++){
		for(int j=0; j<vec.size()-i-1; j++){
			if(vec[j]>vec[j+1]){
				temp=vec[j];
				vec[j]=vec[j+1];
				vec[j+1]=temp;
			}
		}
	}
	cout<<"冒泡排序结果:";
	for(auto i: vec)
		cout<<i<<" ";
	cout<<endl;
}

int findPos(vector<int>&vec, int start, int end){
	int temp=vec[start];
	while(start<end){
		//从后往前找比标兵值小的元素位置
		while(start<end && vec[end]>=temp)
			end--;
		//将该值移动到标兵左边
		vec[start]=vec[end];
		//从前往后找到比标兵值大的元素
		while(start<end && vec[start]<=temp)
			start++;
		//将该值移动到标兵右边
		vec[end]=vec[start];
	}
	//将标兵放置在其正确的位置
	vec[start]=temp;
	return start;
}

void quick_sort_recu(vector<int>&vec, int start, int end){
	if(start>end) return;
	int pos=findPos(vec, start, end);
	quick_sort_recu(vec, start, pos-1);
	quick_sort_recu(vec, pos+1, end);
}

void quick_sort_re(vector<int>vec,int start, int end){
	quick_sort_recu(vec, start, end);
	cout<<"递归快排结果:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

//快排,非递归
void quick_sort(vector<int>vec,int start, int end){
	stack<int>st;
	int k=0;
	if(start<end){
		st.push(start);
		st.push(end);
		while(st.size()){
			int j=st.top(); st.pop();
			int i=st.top(); st.pop();
			
			k=findPos(vec, i, j);

			if(i<k-1){
				st.push(i);
				st.push(k-1);
			}
			if(k+1<j){
				st.push(k+1);
				st.push(j);
			}
		}
	}
	cout<<"非递归快排:";
	for(auto i:vec){
		cout<<i<<" ";
	}
	cout<<endl;
}

//同理于两个有序数组归并
void merge(vector<int>&vec, vector<int>&ans, int left_start, int right_end){
	int left_end=(right_end+left_start)/2;
	int right_start=left_end+1;
	//结果数组的索引下标
	int index=left_start;
	int len=right_end-left_start+1;

	while(left_start<=left_end && right_start<=right_end){
		ans[index++]=vec[left_start]<=vec[right_start]?
				vec[left_start++]:vec[right_start++];
	}

	//此时两个有序数组,至少有一个已经全部纳入结果数组中
	while(left_start<=left_end) ans[index++]=vec[left_start++];
	while(right_start<=right_end) ans[index++]=vec[right_start++];

	//将辅助数组中的元素拷贝到原数组中
	while(len--){
		vec[right_end--]=ans[right_end];
	}
}

//递归拆分数组
void msort(vector<int>&vec, vector<int>&ans, int start, int end){
	int mid=(start+end)/2;
	//若待拆区间长度大于1,则继续进行拆分
	if(start<end){
		msort(vec, ans, start, mid);
		msort(vec, ans, mid+1, end);
		merge(vec, ans, start, end);
	}
}

//归并排序,定义统一接口
void merge_sort(vector<int>vec){
	vector<int>ans(vec.size());
	msort(vec, ans, 0, vec.size()-1);
	cout<<"归并排序结果:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

//传入三个点,因为待归并的两个有序数组长度可能不同
void merge_un(vector<int>&vec, vector<int>&ans, 
		int left_start, int right_start, int right_end){
	int left_end=right_start-1;
	int len=right_end-left_start;
	int index=left_start;

	while(left_start<=left_end && right_start<=right_end){
		ans[index++]=vec[left_start]<=vec[right_start]?
				vec[left_start++]:vec[right_start++];
	}

	while(left_start<=left_end) ans[index++]=vec[left_start++];
	while(right_start<=right_end) ans[index++]=vec[right_start++];

	while(len--)
		vec[right_end--]=ans[right_end];
}

//两两归并相邻有序子列
void merge_pass(vector<int>&vec, vector<int>&ans, int len){
	int i, j, size=ans.size();
	//每次循环,i=i+2*len,因为归并的两段总长度为2*len
	//每次都跳过归并好的那一段,然后去寻找下一个两段进行归并
	//i<ans.size()-2*len,保证了前面成对的都处理完
	//对于尾巴,要另外处理。尾巴可能是两块不一样长的,也可能只有一块
	for(i=0; i<=size-2*len; i+=2*len)
		merge_un(vec, ans, i, i+len, i+2*len-1);
	//说明尾巴有两块,一块长度为len,另一块长度为vec.size()-i-len
	if(i+len<size)
		merge_un(vec, ans, i, i+len, size-1);
	else //说明尾巴只有一块,那么直接拷贝即可
		for(j=i; j<size; j++) ans[j]=vec[j];	
}

//自底向上,非递归实现
void merge_sort_un(vector<int>vec){
	vector<int>ans(vec.size());
	int len=1;

	while(len<vec.size()){
		//此次循环之后,归并的结果存在vec里面
		merge_pass(vec, ans, len);
		len*=2;
		//此次循环之后,将循环结果拷贝回vec中。
		//每次while循环都执行两边merge_pass,使得最终结果保证在vec中存储
		merge_pass(ans, vec, len);
		len*=2;
	}
	cout<<"归并排序非递归版本:";
	for(auto i:vec)
		cout<<i<<" ";
	cout<<endl;
}

int main(){
	vector<int>vec={4,2,7,55,1,9,0,8,10};
	//插入排序
	insert_sort(vec);
	//希尔排序
	shell_sort(vec);
	//选择排序
	select_sort(vec);
	//冒泡排序
	bubble_sort(vec);
	//快速排序,递归版
	quick_sort_re(vec, 0, vec.size()-1);
	//快排,非递归
	quick_sort(vec,0, vec.size()-1);
	//归并排序
	merge_sort(vec);
	//归并排序,非递归
	merge_sort_un(vec);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值