七种排序C++版本(冒泡,选择,插入,希尔,归并,堆,快排)

冒泡排序

基本思想是相邻的两个数比较,逆序则交换,直到没有逆序的记录为止

常见解法
比较n-1趟,每趟都将最大的数放在当前的尾部,然后尾部前移
改进算法
每一趟比较时记录当前是否有元素发生了交换,如果有就继续下一趟的交换,如果没有说明所有的元素都有序了,不用进行下一趟比较了
eg:1,2,3,4,5,6,7,9,8;
第一轮结束后数组为:1,2,3,4,5,6,7,8,9
第二轮比较的时候发现:这一轮没有数据交换,所有的数据都有序
这里看上去第二轮也有点多余,那么此时将flag作为一轮比较后的逆序数,当逆序数小于等于1的时候就可以不用继续比较了,因为在这一轮比较中,已经把数组中唯一的一个逆序对变成了有序,数组中所有的元素都应该有序了
时间复杂度为O(n^2),空间复杂度为O(n);稳定,每一轮有多少逆序交换多少次

//冒泡排序
void bubbleSort(vector<int>& vec)
{
	//int flag = vec.size()
	bool flag = true;											//记录当前这趟比较是否有数据交换
	//for (int i = 0; i < vec.size() - 1 && flag <= 1; i++)	逆序数小于等于1 说明有序
	for (int i = 0; i < vec.size() - 1 && flag; i++)			//当某一趟走完发现没有交换任何元素,说明所有元素都有序,没必要继续判断
	{
		//flag = 0		开始逆序为0
		flag = false;											//开始交换时标志为false
		for (int j = 1; j < vec.size() - i; j++)
		{
			if (vec[j] < vec[j - 1])
			{
				swap(vec[j], vec[j - 1]);
				//flag++;	存在逆序就 +1
				flag = true;									//说明这趟存在逆序的元素即还要继续走下一趟
			}
		}
	}
}

选择排序

基本思想是 每一轮比较记录当前最大数的下标,然后与当前尾部元素交换,尾部前移(也可以记录最小数的下标与当前的首位交换,首部后移)

时间复杂度为O(n^2),空间复杂度为O(n);不稳定,每一轮最多只用交换一次,效率略优于冒泡

//选择排序
void selectSort(vector<int>& vec)
{
	int maxIndex,j;
	for (int i = 0; i < vec.size() - 1; i++)
	{
		maxIndex = 0;								//初始化最大数的索引
		for (j = 1; j < vec.size() - i; j++)		//找到这一轮的最大数索引
		{					
			if (vec[j] > vec[maxIndex])
				maxIndex = j;
		}
		if(maxIndex != j - 1)
			swap(vec[maxIndex], vec[j - 1]);		//将最大数与数组尾部元素交换
	}
}

插入排序

基本思想是 将当前元素插入到前面有序的数组中,然后有序记录 + 1

时间复杂度为O(n^2),空间复杂度为O(n);稳定,效率略优于冒泡和选择排序

void insertSort(vector<int>& vec)
{
	int tmp, j;
	for (int i = 1; i < vec.size(); i++)
	{
		if (vec[i] < vec[i - 1])
		{
			tmp = vec[i];					//记录当前要插入的元素
			for (j = i - 1; j >= 0; j--)
			{
				if (vec[j] < tmp)			//找到插入的位置
				{
					vec[j + 1] = tmp;
					break;
				}
				vec[j+1] = vec[j];
			}
			if (j < 0)						//插入到首位
				vec[0] = tmp;
		}
	}
}

希尔排序

基本思想是 将所有元素根据当前的gap大小分组,将每一组插入排序,然后gap - 1,继续对每组进行插入排序,直到gap < 1

时间复杂度为O(n^3/2),空间复杂度为O(n);跳跃式交换,不稳定,效率优于冒泡和选择和直接插入排序

//希尔
void shellSort(vector<int>& vec)
{
	int len = vec.size();
	int gap = len / 3 + 1;
	while (gap > 0)
	{
		for (int i = 0; i < gap; i++)
		{
			int tmp, k;
			for (int j = i + gap; j < len; j += gap)		//每间隔gap的元素为一组,对当前组进行插入排序
			{
				if (vec[j] < vec[j - gap])
				{
					tmp = vec[j];
					for (k = j - gap; k >= i; k -= gap)
					{
						if (vec[k] < tmp)				//插入位置在当前组中间
						{	
							vec[k + gap] = tmp;
							break;
						}
						vec[k + gap] = vec[k];
					}
					if (k < i)							//插入位置在当前组的首位
						vec[i] = tmp;
				}
			}
		}
		gap--;	
	}
}

堆排序

基本思想是 将数组想象成一个完全二叉树的数组存储结构,每次将最大的元素调整到根节点,然后将根节点与数组尾部元素交换,尾部前移,再次调整堆进行交换直到尾部前移到首部

时间复杂度为O(nlogn),空间复杂度为O(n);跳跃式交换,不稳定,效率优于冒泡,选择,直接插入排序,希尔

void adjustHeap(vector<int>& vec, int root, int end)
{
	int left = root * 2 + 1;
	int tmp = vec[root];
	while(left < end)
	{
		int right = left + 1;	//当前根节点的右孩子
		if(right < end && vec[right] > vec[left])	//右孩子存在且大于左孩子
			left++;
		if(tmp > vec[left])
			break;
		vec[root] = vec[left];			//当前的根节点值小于孩子节点中的最大值则交换
		root = left;					//更新当前的根节点为left,继续往下调整
		left = root * 2 + 1;
	}
	vec[root] = tmp;					//更新当前根节点的值
}

void heapSort(vector<int>& vec)
{
	//构建大顶堆
	for(int i = vec.size() / 2 - 1; i >=0; i--)
		adjustHeap(vec,i,vec.size());
	//调整n-1次,每次从顶部拿走一个元素
	for(int i = vec.size() - 1; i > 0; i--)
	{
		swap(vec[i], vec[0]);
		adjustHeap(vec, 0, i);
	}
}

归并排序

基本思想是 将数组不断二分,直到数组中只有一个元素开始合并成一个有序数组tmp,然后将tmp中的数复制到原数组中

时间复杂度为O(nlogn),空间复杂度为O(n),额外申请了一个临时数组,稳定

void merge(vector<int>& vec, vector<int>& tmp, int left, int mid, int right)
{
	int i = left;
	int j = mid;
	int k = 0;
	//将两个有序的数组合并到临时数组tmp中
	while(i < mid && j < right)
	{
		if(vec[i] < vec[j])
			tmp[k++] = vec[i++];
		else
			tmp[k++] = vec[j++];
	}
	//将原数组中未合并的数添加到tmp后面
	while(i != mid)
		tmp[k++] = vec[i++];
	while(j != right)
		tmp[k++] = vec[j++];
	//将合并的有序数组复制到原数组
	k = 0;
	while(left < right)
		vec[left++] = tmp[k++];
}
void mergeSort(vector<int>& vec, vector<int>& tmp, int left, int right)
{
	//前闭后开区间,当数组中只有一个数时返回
	if(left + 1 == right)
		return;
	int mid = left + (right - left) / 2;
	mergeSort(vec, tmp, left, mid);
	mergeSort(vec, tmp, mid, right);
	merge(vec, tmp, left, mid, right);
}

快速排序

基本思想是 将数组根据当前的基准数进行二分,小的在左侧,大的在右侧,然后对左右侧继续二分直到数组中只有一个元素

时间复杂度为O(nlogn),空间复杂度为O(n),不稳定
改进:
选择数组中的三个数取中位数作为基准元素,可以尽可能让二分后的左右两侧元素数量差不多

int partition(vector<int>& vec, int low, int high)
{
	int pivotkey = vec[low];
	while(low < high)
	{
		while(low < high && vec[high] >= pivotkey) high--;
		vec[low] = vec[high];
		while(high < low && vec[low] <= pivotkey) low++;
		vec[high] = vec[low];
	}
	vec[low] = pivotkey;
	return low;
}

void quickSort(vector<int>& vec, int left, int right)
{
	if(left == right)
		return ;
	int mid = partition(vec, left, right);
	partition(vec, left, mid + 1);
	partition(vec, mid - 1, right);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值