C++实现十大排序算法

1 简单选择排序

存在一乱序数组A[0~n] 第一步:以A[0]作为参考点,比较A[1~n], 遍历完数组后,寻找到最小值A[k](或最大值)的下标k值,然后交换swap(A[0],A[k]); 第二步:以A[1]作为参考点,比较A[2~n], 遍历完数组后,寻找到最小值A[k](或最大值)的下标k值,然后交换swap(A[1],A[k]); 第i步:以A[i]作为参考点,比较A[(i+1)~n], 遍历完数组后,寻找到最小值A[k](或最大值)的下标k值,然后交换swap(A[i],A[k]); 时间复杂度:O(n^2) 空间复杂度:O(1)
#include<iostream>
using namespace std;

void selectSort(int arr[], int len)
{
	if (len <= 0)return;
	int temp = 0;
	int index;
	bool flag = false;//寻找到最小元素标志位
	for (int i = 1; i < len; i++) {//选择趟数
		flag = false;
		temp = arr[i - 1];
		for (int j = i; j < len; j++) {//从当前位置开始寻找最小元素,然后与当前位置元素交换
			if (temp > arr[j]) {
				temp = arr[j];
				index = j;
				flag = true;
			}
		}
		if (flag) {
			arr[index] = arr[i - 1];
			arr[i - 1] = temp;
		}		
	}
}
void main()
{
	int arr[] = { 12,3,54,2,6,17,14,43,13,23,32,43,25 ,19};
	int len = sizeof(arr) / sizeof(arr[0]);
	selectSort(arr, len);
	for (int i = 0; i < len; ++i) {
		cout << arr[i] << " ";
	}
}

2 冒泡排序

要对元素个数为n(n>=1)的数组进行从小到大的顺序排序,从左到右两两对比,大的往后移,每轮完成之后,数组中最大的数都会沉到数组末尾。 时间复杂度:O(n^2) 空间复杂度:O(1)
#include<iostream>
using namespace std;

void bubbleSort(int arr[], int len)
{
	if (len <= 0)return;
	int temp = 0;
	bool flag;//标记当前一趟比较有无数据交换(false表示无交换)
	for (int i = 1; i < len; i++) {//比较趟数
		flag = false;
		for (int j = 0; j < len-i;j++) {//每趟比较次数len-i
			if (arr[j] > arr[j + 1]) {
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				flag = true;
			}
		}
		if (flag == false)break;//若该趟无任何元素交换,说明已经有序,直接跳出循环
	}
}
void main()
{
	int arr[] = { 12,3,54,2,6,38,17,14,32,43,25};
	int len = sizeof(arr) / sizeof(arr[0]);
	bubbleSort(arr, len);
	for (int i = 0; i < len; ++i) {
		cout << arr[i] << " ";
	}
}

3 插入排序

要对元素个数为n(n>=1)的数组进行从小到大的顺序排序,首先可以把第一个元素arr[0]当做是一个初始有序序列,然后从这个有序序列之外的下一个元素开始,分别自后向前和前面已经有序的每一个元素进行比较,将该无序元素插入到第一次小于前面有序序列的某个元素的前一个位置。 时间复杂度:O(n^2) 空间复杂度:O(1)
#include<iostream>
using namespace std;

void insertionSort(int arr[], int len)
{
	if (len <= 0)return;
	int currentvalue;
	int currentindex;
	for (int i = 1; i < len; i++) {
		currentvalue = arr[i];
		currentindex = i;
		while (currentindex>0&&arr[currentindex - 1] > currentvalue) {//从当前位置依次向前比较
			arr[currentindex] = arr[currentindex - 1];//若当前元素小于前面某一个元素,则前面元素后移一位
			--currentindex;
		}
		arr[currentindex] = currentvalue;
	}
}

void main()
{
	int arr[] = { 12,3,54,2,6,17,14,32,43,25 ,19};
	int len = sizeof(arr) / sizeof(arr[0]);
	insertionSort(arr, len);
	for (int i = 0; i < len; ++i) {
		cout << arr[i] << " ";
	}
}

4 快速排序

先从数列中取出一个数作为枢轴。 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。 再对左右区间重复第二步,直到各区间只有一个数。 时间复杂度为O(nlogn) 空间复杂度为O(nlogn)
#include<iostream>
#include<vector>
using namespace std;

void quickSort(vector<int>& v,int left,int right) {
	int l = left;//记录左哨兵
	int r=right;//记录右哨兵
	if (left < right) {
		int base = v[left];//将左边第一个数作为基准
		while (left < right) {//最终当左哨兵与右哨兵相遇时退出循环
			while (left < right&&v[right] >= base)right--;//若右侧哨兵不小于基准,则左移
			v[left] = v[right];//将上一次循环左侧大于基准的位置赋值为当前右侧小于基准的值
			while (left < right&&v[left] <= base)left++;//若左侧哨兵不大于基准,则右移
			v[right] = v[left];//将上一次循环右侧小于基准的位置赋值为当前左侧大于基准的值
		}
		v[left] = base;//将基准放置在正确的位置

		quickSort(v, l, left - 1);
		quickSort(v, left + 1, r);
	}	
}
void main()
{
	vector<int> v{ 1,45,2,34,45,23 ,11,15,9};
	quickSort(v,0,8);
	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << " ";
	}
}

5 归并排序

归并排序是采用分治法的一个非常典型的应用,它要做两件事情: 第一: “分”, 就是将数组尽可能的分,一直分到原子级别。 第二: “并”,将原子级别的数两两合并排序,最后产生结果。 至于二个有序数列合并,只要比较二个数列的第一个数,谁小就先取谁安放到临时队列中, 取了后将对应数列中这个数删除,直到一个数列为空,再将另一个数列的数据依次取出即可。 时间复杂度为O(nlogn) 空间复杂度为O(n)
#include<iostream>
#include<vector>
using namespace std;

void Merge(int *arr, int n)
{
	//int temp[n];    // 辅助数组
	vector<int> temp(n, 0);
	int b = 0;  // 辅助数组的起始位置
	int mid = n / 2;    // mid将数组从中间划分,前一半有序,后一半有序
	int first = 0, second = mid;    // 两个有序序列的起始位置

	while (first < mid && second < n)
	{
		if (arr[first] <= arr[second])   // 比较两个序列
			temp[b++] = arr[first++];
		else
			temp[b++] = arr[second++];
	}

	while (first < mid)              // 将剩余子序列复制到辅助序列中
		temp[b++] = arr[first++];
	while (second < n)
		temp[b++] = arr[second++];
	for (int i = 0; i < n; ++i)//辅助序列复制到原序列
		arr[i] = temp[i];
}

void MergeSort(int *arr, int n)
{
	if (n <= 1)//递归出口
		return;
	MergeSort(arr, n / 2);//对前半部分进行归并排序
	MergeSort(arr + n / 2, n - n / 2);//对后半部分进行归并排序(n-n/2不能直接用n/2替代,因为n/2有一个类型转换的过程,n-n/2与n/2不一定相等)
	Merge(arr, n);//归并两部分
}
void main()
{
	int arr[9]={ 1,45,2,34,45,23 ,11,15,9 };
	MergeSort(arr, 9);
	for (int i = 0; i < 9; i++) {
		cout << arr[i] << " ";
	}
}

6 希尔排序

是改进的直接插入排序法,希尔排序是一种按照增量排序的方法。其中增量值是小于n的正整数。 shell排序的基本思想是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2 < d1重复上述的分组和排序,直至所取的增量dt=1(dt < dt-l< …< d2< d1),即所有记录放在同一组中进行直接插入排序为止。 原理:又称增量缩小排序。先将序列按增量划分为元素个数相同的若干组,使用直接插入排序法进行排序,然后不断缩小增量直至为1,最后使用直接插入排序完成排序。 时间复杂度为O(nlogn) 空间复杂度为O(1)
#include<iostream>
using namespace std;

void shellSort(int * arrs,int arrLen)
{
	int step = arrLen / 2;//初始增量(初始状态每个希尔小组的元素下标间隔为arrLen/2)
	while (step > 0) {
		//无序部分
		for (int i = step; i < arrLen; i++) {
			int temp = arrs[i];
			int j=i - step;
			//子序列中的插入排序,这是有序部分
			for (; j >= 0 && temp < arrs[j]; j = j - step)//若当前元素较小,往前移(j<0表示一个希尔小组已经比到头了,在希尔小组中当前元素的前面所有元素都已经有序,因此一旦当前元素大于了前面的某个元素,直接跳出循环)
				//在找到当前元素合适位置前,元素后移
				arrs[j + step] = arrs[j];
			arrs[j + step] = temp;
		}
		step /= 2;//每遍历一次数组,每个希尔小组的元素下标间隔缩小一倍
	}
}
void main()
{
	int arr[9] = { 1,45,2,34,45,23 ,11,15,9 };
	shellSort(arr, 9);
	for (int i = 0; i < 9; i++) {
		cout << arr[i] << " ";
	}
}

7 堆排序

堆:①完全二叉树②所有父节点都大于子节点(大顶堆),所有父节点都小于子节点(小顶堆) 堆排序(Heap Sort),是利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构, 并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。是改进的简单选择排序。 通常堆是通过一维数组来实现的,在起始数组为0的情形中,对于节点i: 其左子节点的下标为 (2*i+1); 其右子节点的下标为 (2*i+2); 其父节点的下标为 floor((i-1)/2)(向下取整)。 时间复杂度为O(nlogn) 空间复杂度为O(1)
#include<iostream>
#include<vector>
using namespace std;

void swap(vector<int>& tree, int i, int j) {
	int temp = tree[i];
	tree[i] = tree[j];
	tree[j] = temp;
}
void heapify(vector<int>& tree, int index) {//将索引为index的当前节点堆化
	int size = tree.size();
	if (index >= size)return;
	int c1 = 2 * index + 1;//左子节点索引
	int c2 = 2 * index + 2;//右子节点索引
	int max = index;
	if (c1 < size&&tree[max] < tree[c1])max = c1;
	if (c2 < size&&tree[max] < tree[c2])max = c2;
	if (max != index) {
		swap(tree, index, max);//找到索引为index的当前节点与其左右节点中的最大值,然后将最大值放在index位置
		heapify(tree, max);//由于之前最大值索引处的值已经改变,所以该索引节点的堆被破坏,需要从该节点开始再次堆化
	}
}
void heapBuild(vector<int>& tree) {//将整棵二叉树构建成堆
	int size = tree.size();
	if (size <= 1)return;
	int parent = (size - 2) / 2;//从数组最后一个节点的父节点开始堆化(向下取整)
	for (; parent >= 0; parent--) {
		heapify(tree,parent);
	}
}
void heapSort(vector<int>& tree){//排序
	int size = tree.size();
	vector<int> temp;
	heapBuild(tree);//将整棵二叉树构建成堆
	int lastindex = size - 1;//待排序的索引(从后向前排)
	for (; lastindex >= 0; lastindex--) {
		temp.push_back(tree[0]);//整棵二叉堆顶一定是最大值,将堆顶压入temp中
		swap(tree, 0, lastindex);//将堆顶与待排序索引结点数值相互交换
		tree.pop_back();//删除二叉堆最后一个结点(方便下一步重新堆化时)
		heapify(tree,0);//重新对堆顶进行堆化
	}
	//for (int i = 0; i < size; i++) {//从大到小排序1		
	//	tree.push_back(temp[i]);
	//}
	//tree = temp;//从大到小排序2

	for (int i = size-1; i >= 0; i--) {//从小到大排序	1	
		tree.push_back(temp[i]);
	}
	//tree=temp;//从小到大排序2
	//reverse(tree.begin(),tree.end());
}
void main()
{
	vector<int> tree{ 1,5,9,35,23,19,45,24,6,9,0 };
	heapSort(tree);//传引用
	for (auto nums : tree) {
		cout << nums << " ";
	}
}

8 计数排序

计数排序的思想比较新颖,不再是基于元素之间的比较,而是将待排序序列的元素当作容器的索引,记录索引出现的次数; 比如临时容器的a[i] = n,表示元素i出现n次; 记录完出现次数之后,将临时容器从小到大将元素汇总起来,则变为有序; 计数排序的缺点:只能对整数序列进行排序,而且不适合最大元素和最小元素差得很大的情况(若相差很大则算法需要申请非常大的数据存储空间,并且该空间很大一部分在实际计算中并没有用到) 很多人都会说计数排序的时间复杂度为O(n),说性能优于快速排序,但其实并不是,因为差值d一般都不是一个小的数,是不容忽视的,这就是为什么说计数排序时间复杂度优于快速排序但快速排序的应用广的一部分原因。 另一部分原因是因为计数排序的缺陷,它只适用于整数序列,而且差值d太大的话空间复杂比较差,该缺陷有一个弥补的排序方法,那就是桶排序 时间复杂度为O(n+d) 空间复杂度为O(d)
#include<iostream>
#include<vector>
using namespace std;

void countSort(vector<int>& base) {
	int size = base.size();
	if (size <= 1)return;
	int max = base[0];
	int min = base[0];
	for (int i = 0; i < size; i++) {//获取数组中的最大值和最小值
		if (max < base[i])max = base[i];
		if (min > base[i])min = base[i];
	}
	if (max == min)return;
	int d = max - min;//求base数组最大值与最小值的差值
	vector<int> temp(d + 1, 0);
	for (int i = size - 1; i >= 0; i--) {//遍历base数组,从后往前
		temp[base[i] - min]++;//将base数组中的数转为temp数组中的引用并计数
		base.pop_back();//不断尾删base数组中的元素直至为空
	}
	for (int i = 0; i < d + 1; i++) {
		while (temp[i]) {//temp数组每个索引对应的数值可映射到base数组中对应数值的数目
			base.push_back(i+min);//将temp的索引映射到原base数组中的数值,并压入base数组
			temp[i]--;
		}
	}
}
void main()
{
	vector<int> base{ 1,5,9,35,23,19,45,24,6,9,0 };
	countSort(base);//传引用
	for (auto nums : base) {
		cout << nums << " ";
	}
}

9 桶排序

桶排序是计数排序的一个优化,它解决了计数排序只适用于整数排序的限制,更是解决了不适用于最大值和最小值差值很大的情况. 桶排序的思想就是对最小值和最小值之间的元素进行瓜分,将他们分为多个区间,每个区间用一个桶(其实就是一个容器)来装. 前一个桶里的元素全都小于后一个桶里的元素,只需要利用别的常规排序算法将桶里面的元素进行排序使之有序即可使得原序列有序. 设桶个数为m,一般我们把时间复杂的记为O(n+m)。 空间复杂度:O(n+m)
#include<iostream>
#include<vector>
using namespace std;
//快速排序,从小到大
void QuickSort(vector<int>& v, int l, int r) {
	int size = v.size();
	if (size <= 1)return;
	if (l < r) {
		int left = l;
		int right = r;
		int base = v[l];
		while (l < r) {
			while (l<r&&v[r]>=base)r--; v[l] = v[r];
			while (l < r&&v[l] <= base)l++; v[r] = v[l];
		}
		v[l] = base;
		QuickSort(v, left, l - 1);
		QuickSort(v, l + 1, right);
	}
}

void bucketSort(vector<int>& base) {
	int size = base.size();
	if (size <= 1)return;
	int max = base[0];
	int min = base[0];
	for (int i = 1; i < size; i++) {
		if (max < base[i])max = base[i];
		if (min > base[i])min = base[i];
	}	
	int d = max - min;//求极差
	int bucketnum = base.size();//桶的数量(可调)
	vector<vector<int>> bucket(bucketnum+1);//易错点!!!!!
	for (int i = size - 1; i >= 0; i--) {//从后往前遍历base数组
		int index = (base[i] - min) / (d / (bucketnum-1));//(d/bucket-1)表示相临两个桶的间隔,
													  //index表示当前元素所在桶的索引
		bucket[index].push_back(base[i]);//在对应索引的桶中压入元素
		base.pop_back();//从后往前依次从base数组中弹出元素
	}
	for (int i = 0; i <= bucketnum; i++) {//将每一个桶中元素做快排
		QuickSort(bucket[i], 0, bucket[i].size()-1);
	}
	for (int i = 0; i < bucketnum+1; i++) {//将每一个桶中的元素从前到后依次装入base数组中
		for (int nums : bucket[i])base.push_back(nums);
	}
}
void main()
{
	vector<int> base{ 1,5,9,35,23,19,45,24,6,9,0 ,63};
	bucketSort(base);//传引用
	for (auto nums : base) {
		cout << nums << " ";
	}
}

10 基数排序

使用了桶排序中桶的思想,但它比桶排序更精明,它只需要十个桶,因为他的排序思想是分别对元素中的个位,十位,百位....进行排序. 也就是说,首先对所有数以个位数的大小进行排序,然后再对所有数以他们的十位数进行排序,依次类推. 在整个过程中会使得原始序列逐渐趋近有序,待将最高位排完之后完全有序. 想想为什么是从个位开始而不是从最高位开始呢,按道理从最高位开始的话每次都能得出一部分数的正确大小关系. 确实可以从最高位开始,而且可以减少比较次数,但是从最高位开始会有一个致命缺点,那就是在如果从高位开始,在对高位相同的数继续排序时,又需要另外创建十个桶对他们排序,其实也就是说最终的结果就是真多每一个不同的元素都会为它创建一个桶, 如果待排序序列有10000个不同的元素,那么从高位开始比较的方法就需要创建10000个桶,而从个位开始比较的方法可以重复使用 那10个桶,如果序列个数更多那么这样的性能差异就更明显,所以虽然减少了比较次数但浪费了非常多的空间,得不偿失. 所以我们说基数排序的话都默认的是从个位开始比较的. 时间复杂度为O(d(n+r)) 空间复杂度为O(n+r)
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;

void radixSort(vector<int>& base) {
	int size = base.size();
	if (size <= 1)return;
	int max = base[0];//获取数组最大值
	for (int nums : base)max = max > nums ? max : nums;
	int bit = 0;
	while (max) {//获取数组最大值的位数
		bit++;
		max /= 10;
	}
	vector<vector<int>> bucket(10);
	for (int i = 0; i < bit; i++) {
		for (int j = 0; j <size; j++) {//每次最好从前往后遍历,否则在下面每次向原序列合并时每个桶需要从后往前遍历
			int radix = (base[j] / (int)(pow(10, i))) % 10;//依次根据个位、十位、百位...排序
			bucket[radix].push_back(base[j]);
		}
		int m = 0;
		for (int k = 0; k < 10; k++) {//每完成一轮便将桶里的元素按顺序合并放入原序列.
			for (int nums : bucket[k])base[m++] = nums;
			bucket[k].clear();
		}

		//for (int j = size-1; j >=0; j--) {//每次从后往前遍历
		//	int radix = (base[j] / (int)(pow(10, i))) % 10;
		//	bucket[radix].push_back(base[j]);
		//	base.pop_back();
		//}		
		//for (int k = 0; k < 10; k++) {//每完成一轮便将桶里的元素按顺序合并放入原序列.
		//	reverse(bucket[k].begin(), bucket[k].end());//当按十位、百位...排序时,每个桶已经按从大到小进行了排序,此时为了方便直接向base数组压入元素,需要将每个桶中的元素反序
		//	for (int nums : bucket[k])base.push_back(nums);
		//	bucket[k].clear();
		//}

		//cout << endl;
		//for (auto nums : base) {//第一遍0 1 23 63 24 5 35 45 6 9 19 9第二遍0 1 5 6 9 9 19 23 24 35 45 63
		//	cout << nums << " ";
		//}
		//cout << endl;
	}
}

void main()
{
	vector<int> base{ 1,5,9,35,23,19,45,24,6,9,0 ,63 };
	radixSort(base);//传引用
	for (auto nums : base) {
		cout << nums << " ";
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值