常见排序算法总结(排序算法&&刷题笔记)

冒泡===》快排

//--------------------------------冒泡排序

//-------简单冒泡
void BubbleSort1(SqList *L)  //传地址进去的,内容是可以改变的,不用返回值
{
	//下标【小==》大】
	for (int i = 1; i < L->length; i++) //长度-1 趟
	{
		for (int j=1; j<= L->length-i; j++)  //第一趟是长度 -1 次  ==//第一趟是长度-1次====r[0]作为temp变量
		{
			if (L->r[j] > L->r[j + 1])
				swap(L, j, j + 1);
		}
	}
}

//真正的冒泡排序===下标从上到下

void BubbleSort2(SqList *L)  //下标0空出来了  都是从1开始才有数字
{
	for (int i = 1; i < L->length; i++) //长度-1趟
	{
		//下标大往下标小冒泡
		for (int j = L->length-1 ; j > i; j--)  //每趟 长度-1次
		{
			if (L->r[j] > L->r[j + 1])
				swap(L, j, j + 1);
			break;
		}
	}
}

//------------------------快速排序
====快速排序(二分思)====非常重要。。。。。平均是nlog2^n 最好是nlog2^n 最坏是n^2

从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,
所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

相对比于冒泡的优点
这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,
交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了

最坏的情况是:
仍可能是相邻的两个数进行了交换。
因此快速排序的最差时间复杂度和冒泡排序是一样的都是 O(N2),它的平均时间复杂度为 O(NlogN)。

给定数组:5 1 9 3 7 4 8 6 2

实现的步骤:

第一步:默认取第一个值为枢轴,从而经过partion函数找到此值所对应的序号pivot

第二步:开始对在上面的序号pivot左右分别进行递归排序

第三步:由于是递归,重复上面的步骤

int Partion(std::vector<int>& src, int low, int high)
{
	int pivot = src[low];
	while (low < high)
	{
		while (low < high && src[high] >= pivot )
			high--;
		vec[low]=vec[high];
		while (low < high && src[low] <= pivot )
			low++;
		vec[high]=vec[low];
	}
	vec[low]=pivot; 
	return low;
}
void Qsort(std::vector<int>& src, int low, int high)
{
	if (low >= high)
		return;
	int pivot = Partion(src,low,high);
	Qsort(src, low, pivot - 1);
	Qsort(src,pivot+1,high);
}

直接插入===希尔

//-----------------直接插入
//直接插入排序===在已经排列好的序列(这个已经排好的序列在慢慢的变长中,,进行元素的 插入
/*

定义的数组 : {23,34,56,78,65,90,88,92,18,21}

过程如下所示:

【23】 34 56 78 65 90 88 92 18 21

第 1 次排序结果: 【23 34】 56 78 65 90 88 92 18 21

——用34插入到【23】序列中,34>23,故插入后的顺序是【23,34】

第 2 次排序结果: 【 23 34 56 】78 65 90 88 92 18 21

——用56插入到【23,34】序列中,56>34,故插入后的顺序是【23,34,56】

第 3 次排序结果: 【23 34 56 78】65 90 88 92 18 21

——用78插入到【23,34,56】序列中,78>56,故插入后的顺序是【23,34,56,78】

第 4 次排序结果: 【23 34 56 65 78 】90 88 92 18 21

——用65插入到【23,34,56,78】序列中,65<78,所以78后移一位,和56比较,65>56,故插入后的顺序是【23,34,56,65, 78】

第 5 次排序结果: 【23 34 56 65 78 90 】 88 92 18 21

——用90插入到【23,34,56,65, 78】序列中,78<90 ,故插入后的顺序是【23 34 56 65 78 90 】

第 6 次排序结果: 23 34 56 65 78 88 90 92 18 21

——用88插入到【23 34 56 65 78 90 】序列中,90>88 ,所以90后移一位,故插入后的顺序是【23 34 56 65 78 88 90】

第 7 次排序结果: 23 34 56 65 78 88 90 92 18 21

——用92插入到【23 34 56 65 78 90 】序列中,90<92 ,故插入后的顺序是【23 34 56 65 78 90 92 】

第 8 次排序结果: 18 23 34 56 65 78 88 90 92 21

——用18插入到【23 34 56 65 78 90 92 】序列中,18<92,所以92后移一位,和90比较,90>18,依次后移,直到第一位23,因为18<23, 故插入后的顺序是【18 23 34 56 65 78 88 90 92】

第 9 次排序结果: 18 21 23 34 56 65 78 88 90 92

——用21插入到【23 34 56 65 78 90 92 】序列中,21<92,故92后移一位,和90比较,90>21,依次后移,直到第一位18,因为18<21, 故插入后的顺序是【18 21 23 34 56 65 78 88 90 92】

void insertSort(std::vector<int>& vec)
{
	for (int i = 1; i < vec.size(); i++)
	{
		for (int j = i; j-1>=0 && vec[j-1]<vec[j]; j--)
		{
			int temp = vec[j - 1];
			vec[j - 1] = vec[j];
			vec[j] = temp;
		}	
	}
}

//-------------------------希尔排序,,,缩小增量
//最好是n 不稳定
//希尔排序===升级的插排—实际还是在一起排序都是一个数组 就是有间隔
/*

原始数组:8 9 1 7 2 3 5 4 6 0

8 9 1 7 2 3 5 4 6 0
第一步:
此我们选择增量gap=length/2 10/2=5
分为5组://跨度为5
[8,3] [9,5] [1,4] [7,6] [2,0]

3 5 1 6 0 8 9 4 7 2
第二步:
此我们gap=5/2=2
分为2组//跨度为2
[3 1 0 9 7] [5 6 8 4 2]

0 2 1 4 3 5 7 6 9 8
第三步://跨度为1
此时我们gap=2/2=1
分为1组:
[ 0 2 1 4 3 5 7 6 9 8]

直接插排:
0 1 2 3 4 5 6 7 8 9

void sortxwier2(std::vector<int>& vec)//推荐
{
	for (int gap = vec.size() / 2; gap >= 1; gap /= 2)
	{
		//把距离为gap的元素编为一个组
		for (int i = gap; i < vec.size(); i++)//这里的i是区分不同的数组且i都是指向数组的第一个
		{	
				for (int j = i; j - gap >= 0 && vec[j - gap] < vec[j]; j = j - gap)
				{
					int tmp = vec[j - gap];
					vec[j - gap] = vec[j];
					vec[j] = tmp;
				}
		}
	}
}

选择===》堆排

//----------------------------------------------------------选择排序

//简单选择排序==最好是n
第一个数和后面的一坨比较,取最值交换
第二个数和后面的一坨比较,取最值交换
。。。
标签(代表最值的下标),其实标签就是 第一位

不断的比较判断,标签,当判断完就要出手, 直到最后一次的交换

template <typename T>
void Sortchoose(std::vector<T>& vec)
{
	int pos;
	for (int i = 0; i < vec.size() - 1; i++)
	{
		pos = i;
		for (int j = 1; j <= vec.size() - i; j++)
		{
			if (vec[pos] > vec[j])
				pos = j;
		}
		if (i != pos)
		{
			T tmp = vec[i];
			vec[i] = vec[pos];
			vec[pos] = tmp;
		}
	}
}

//---------------------------------------------------------堆排序
堆排序:
使用堆这种数据结构所设计的一种排序算法。

完全二叉树:
设一个高度为h,有n个结点的二叉树,
当且仅当每个结点都与高度为h的满二叉树中编号为1~n的结点一一对应时,
称为完全二叉树。

作用:
可以完全映射到一个数组中

完全二叉树的性质:
堆顶的的节点(root节点,,序号为1)。左孩子是2i 右孩子是2i

堆顶的的节点(root节点,,序号为0)。左孩子是2i+1 右孩子是2i+2 ==========符合数组的

堆排序的实现的步骤:
第一步:重组
把排序的序列构造成一个大顶堆(小顶堆)=====从下往上,从右往左的方法。

第二步:交换
将堆顶的的节点(root节点,,序号为1)。左孩子是2i 右孩子是2i
将根节点移走(就是其与堆数组的末尾元素交换,此时末尾(倒数第一个)元素就是最大值。)

第三步:再重组,再交换
将剩余的n-1个序列重新构造成一个新的堆,继续交换堆顶的值和末尾(倒数第二个)的值。。。。。。

第四步:直到结束
如此反复执行,变可以得到一个有序的序列(升序和降序关键是看你的堆是大顶堆还是小顶堆)

void println(std::vector<int>& vec)
{
	for (int i = 0; i < vec.size(); i++)
	{
		std::cout << vec[i] << "\t";
	}
	std::cout << std::endl;
}
void swap(std::vector<int>& vec,int i,int j)
{
	int tmp = vec[i];
	vec[i] = vec[j];
	vec[j] = tmp;
}
void HeapAdjust(std::vector<int>& vec,int s,int n)
{
	int tmp = vec[s];
	for (int j = 2 * s + 1; j < n; j = 2 * j + 1)//起始就是左孩子,则下一个也是左孩子
	{
		if (j<n-1 && vec[j] < vec[j+1])
			j++;
		if (tmp >= vec[j])
			break;
		
		vec[s] = vec[j];
		s = j;
	}
	vec[s] = tmp;
}
void HeapSort(std::vector<int>& vec)
{
	if (vec.size() < 2)
		return;
	for (int i = vec.size() / 2 - 1; i >= 0; i--)
	{
		HeapAdjust(vec,i,vec.size()-1);
	}
	for (int i = vec.size()-1; i > 0; i--)
	{
		swap(vec, 0, i);
		HeapAdjust(vec,0,i);
	}
}

二路归并

归并排序(分治算法)-----这里减小数组的规模使用递归,但是要注意栈的频繁使用
===将有序的的子序列合并,得到完全有序的序列

归并的过程中消耗了多余的空间

原始数据:8 4 5 7 1 3 6 2

===========================分----分之前搞个临时数组

第一步:一分为二 搞一个临时数组,留着合并回来
[8 4 5 7] [1 3 6 2]

第二步:再分 搞两个临时数组
[8 4] [5 7] [1 3] [6 2]

第三步:再分 搞四个临时数组
[8] [4] [5] [7] [1] [3] [6] [2]

分到一定细度的时候,每一个部分就只有一个元素了,
那么我们此时不用排序,对他们进行一次简单的归并就好了
============================治(合)在这过程中需要比较

第一步:先比较再合
[4 8] [5 7] [1 3] [2 6]

第二步:先比较再合
[4 5 7 8] [1 2 3 6]

第二步:先比较再合
[1 2 3 4 5 6 7 8]

这里再合并的时候,有个细节,我们需要构建一个临时的数组,
用来比较我们存放后的的值,牺牲了空间,提升时间

void merge(std::vector<int>& src, int head, int mid, int rear)
{
	std::vector<int>tmp(rear - head + 1);
	int i=head, j=mid+1, k=head;
	while ( i <= mid && j <= rear)
	{
		if (src[i] < src[j])
			tmp[k] = src[i++];
		else
			tmp[k] = src[j++];
	}
	while (i <= mid)
		tmp[k++] = src[i++];
	while (j <= rear  )
		tmp[k++] = src[j++];

	for (int i = head; i <=rear; i++)//防止越界
		src[i] = tmp[i];
}
//递归实现=====像是树的后序递归
void msort(std::vector<int>& src, int head, int rear)//其实分开的两端也是一起连续的数组
{
	if (head >= rear)//递归终结者
		return;
	int mid = (head + rear) / 2;
	msort(src, head, mid);//左有序
	msort(src, mid + 1, rear);//右有序
	merge(src, head, mid, rear);//合并
}

非交换-------------------------------------------------------

计数排序----桶排序的特例

找出待排序的数组中最大和最小的元素;
统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

//==============================不太现实,假如是 95 95  96 98 92 91 90
// 会浪费辅助的空间
void jishu(std::vector<int> &src)
{
	int len = src.size();
	int max = src[0];
	int min = src[0];
	for (int i = 0; i < len; i++)
	{
		if (src[i] > max)
			max = src[i];
		if (src[i] < max)
			min = src[i];
	}

	//开始计数对应填入book
	std::vector<int> book(max+1,0);
	for (int i = 0;i < len; i++)
	{
		book[src[i]]++;
	}
	//开始拼接
	int index = 0;//原始数组的序号
	for (int i = min; i <max+1; i++)//
	{
		for (int j = 0; j < book[i]; j++)//重复的个数
		{
			src[index] = i;
			index++;
		}
	}
}

//=================================真正的计数排序
void jishugood(std::vector<int> &src)
{
	int len = src.size();
	int max = src[0];
	int min = src[0];
	for (int i = 0; i < len; i++)
	{
		if (src[i] > max)
			max = src[i];
		if (src[i] < max)
			min = src[i];
	}

	//开始计数对应填入book
	std::vector<int> book(max-min+1, 0);//标记数组的容量
	for (int i = 0; i < len; i++)
	{
		book[src[i]-min]++;
	}

	//开始拼接
	int index = 0;//原始数组的序号
	for (int i = 0; i < max-min + 1; i++)//遍历标记数组,长度max-min + 1
	{
		for (int j = 0; j < book[i]; j++)//重复的个数
		{
			src[index++] = i+min;
		}
	}
}

桶排序–

步骤描述
每一个桶(bucket)代表一个区间范围,里面可以承载一个或多个元素。

桶排序的第一步,
就是创建这些桶,确定每一个桶的区间范围:
具体建立多少个桶,如何确定桶的区间范围,有很多不同的方式。
我们这里创建的桶数量等于原始数列的元素数量*******,
公式:桶数=(最大值-最小值)/跨度gap +1
**********一个桶中两个元素间的最大差值为gap-1
公式:区间跨度gap= (最大值-最小值)/ 桶的数量 + 1
**********************对应的桶的序号
公式:桶的序号=(arr[i]-最小值)/gap

第二步:
遍历原始数列,把元素对号入座放入各个桶中:

第三步:
每个桶内部的元素分别排序

第四步
遍历所有的桶,输出所有元素:

void bubleSort(std::vector<int> &src)
{
	int len = src.size();
	if (src.size() < 2)
		return;

	for (int i = 0; i < len-1; i++)
	{
		for (int j = 1; j < len - i; j++)
		{
			if (src[j - 1] > src[j])
			{
				int tmp = src[j - 1];
				src[j - 1] = src[j];
				src[j] = tmp;
			}
		}
	}
}
//--------------------------桶排序
void BucketSort(vector<int> &src)
{
	int min = src[0], max = src[0];
	for (auto it : src)
	{
		if (it > max) max = it;
		if (it < min) min = it;
	}

	//===========================初始化空筒
	int BuketGap = (max - min) / src.size() + 1;
	int BuketNum = (max - min) / BuketGap + 1;
	vector<vector<int>> Bucket(BuketNum,vector<int>());

	//===========================把元素放进对应的桶中
	for (int i = 0; i < src.size(); i++)
	{
		int BuketIndex = (src[i] - min) / BuketGap;
		Bucket[BuketIndex].push_back(src[i]);
	}

	src.clear();//清空容器,方便后面的添加


	//==============================各自排序   再合并
	vector<vector<int>>::iterator start=Bucket.begin();
	vector<vector<int>>::iterator end= Bucket.end();
	for(auto iter=start;iter!=end;iter++)
	{
		vector<int> tempvec = *iter;

		bubleSort(tempvec);

		for (auto it : tempvec)
		{
			src.push_back(it);
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值