十大排序算法的c++实现

接下来实现使用的交换函数为:

void swap(vector<int>& arr, int a, int b) {
	int temp = arr[a];
	arr[a] = arr[b];
	arr[b] = temp;
}

冒泡排序:时间复杂度O(n^{2}) 空间复杂度O(1),稳定

//冒泡排序
void Bubble_Sort(vector<int>& arr) {
	for (int i = 0; i < arr.size() - 1; i++) {
		for (int j = 0; j < arr.size() - i - 1; j++) {
			if (arr[j] > arr[j + 1]) {
				swap(arr, j, j + 1);
			}
		}
	}
}

选择排序:时间复杂度O(n^{2}) 空间复杂度O(1),不稳定

//选择排序
void Selection_Sort(vector<int>& arr) {
	for (int i = 0; i < arr.size() - 1; i++) {
		int min = i;
		for (int j = i + 1; j < arr.size(); j++) {
			if (arr[min] > arr[j]) {
				min = j;
			}
		}
		swap(arr, min, i);
	}
}

直接插入排序:最坏时间复杂度O(n^{2}),最好时间复杂度O(n),空间复杂度O(1),稳定

//插入排序
void Insertion_Sort(vector<int>& arr) {
	//外层循环的含义:保证0~i是有序的,插入排序实际上是倒着进行比较
	//故当数据比较好的时候,它的时间复杂度也挺好
	//并不像选择和冒泡一样,固定了排序
	for (int i = 1; i < arr.size(); i++) {
		for (int j = i - 1; j >= 0; j--) {
			if (arr[j + 1] < arr[j]) {
				swap(arr, j + 1, j);
			}
		}
	}
}

二路归并排序:时间复杂度O(n\log n) 空间复杂度O(n),稳定

//归并排序
void Merge(vector<int>& arr, int l, int r,int m) {
	int p1 = l;
	int p2 = m + 1;
	int* help = new int[r - l + 1];
	int index = 0;
	while (p1 <= m && p2 <= r) {
		help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
	}
	while (p1 <= m) {
		help[index++] = arr[p1++];
	}
	while (p2 <= r) {
		help[index++] = arr[p2++];
	}
	for (index = 0; index < r - l + 1; index++) {
		arr[index + l] = help[index];
	}
}
void Merge_Sort(vector<int>& arr,int left,int right) {
	if (left == right) {
		return;
	}
	int mid = left + ((right - left) >> 1);
	Merge_Sort(arr, left, mid);
	Merge_Sort(arr, mid + 1, right);
	Merge(arr, left, right, mid);
}

快速排序:最快的排序算法,时间复杂度O(n\log n),空间复杂度(logn),不稳定

//快速排序
int* Partition(vector<int>& arr, int L, int R) {
	int less = L - 1;//左边界
	int more = R;//右边界
	while (L < more) {
		if (arr[L] < arr[R]) {
			swap(arr, ++less, L++);
		}
		else if (arr[L] > arr[R]) {
			swap(arr, --more, L);
		}
		else {
			L++;
		}
	}
	int* ret = new int[2];
	swap(arr, R, more);
	ret[0] = less + 1;
	ret[1] = more;
	return ret;
}
void Quick_Sort(vector<int>& arr,int L,int R) {
	if (L < R) {
		swap(arr, R, L + rand() % (R - L + 1));//随机取一个值作为partition的根据值
		int* ret = Partition(arr, L, R);
		Quick_Sort(arr, L, ret[0] - 1);
		Quick_Sort(arr, ret[1] + 1, R);
	}
}

为什么quickSort中的递归base case是L>=R就终止递归?

比如现在分割到只剩两个元素的情况下,那么partition返回的数组中的两个元素的值其实是一样的(都指向同一个位置),那么ret[0] - 1就可能变成一个比L小的数,这样肯定是不合理的(注意是可能,也有可能变成和L相同的数,但如果是这种条件的话,ret[1]+1就会变得比R大,那么还是造成了L > R)

而在归并中是不会这样的,故归并的base case只要是L == R即可

堆排序:时间复杂度O(n\log n),空间复杂度O(1),不稳定

//堆排
void heapify(vector<int>& arr, int index, int heapsize) {
	int left = index * 2 + 1;
	while (left < heapsize) {
		int max = left + 1 < heapsize && arr[left] < arr[left + 1] ? left + 1 : left;
		max = arr[index] > arr[max] ? index : max;
		if (max == index) {
			return;
		}
		swap(arr, max, index);
		index = max;
		left = index * 2 + 1;
	}
}
void heapInsert(vector<int>& arr, int index) {
	while (arr[index] > arr[(index - 1) / 2]) {
		swap(arr, index, (index - 1) / 2);
		index = (index - 1) / 2;
	}
}
void Heap_Sort(vector<int>& arr) {
	int heapsize;
	for (heapsize = 0; heapsize < arr.size(); heapsize++) {
		heapInsert(arr, heapsize);
	}
	swap(arr, 0, --heapsize);
	while (heapsize > 0) {
		heapify(arr, 0, heapsize);
		swap(arr, 0, --heapsize);
	}
}

以上都是基于比较的排序,基于比较的意思是都有用到大于、小于、等于的操作符,所以这些排序都可以改成通用的排序算法,让用户提供一个二元谓词的比较器,然后根据这个比较器来进行排序

工程上的排序算法是如何进行优化的呢?

首先我们写一个排序算法的接口,经过实验我们得知,当数据量较小时,使用O(n^{2})的算法实际所花费时间更少,当数据量多的时候,再使用快排等时间复杂度为O(nlogn)的算法

想要常数时间快,那就选择快速排序;想要稳定,那就使用归并排序;想要节省空间,那就使用堆排序

稳定:若记录序列中有两个或两个以上相等的元素,那么其排序后的相对位置是否改变叫做稳定或者不稳定

目前已知的排序算法最快也就nlogn,你可能会看到许多改进的算法,但请你记住,有失必有得,你想更快,那么空间复杂度和稳定性可能就会变差

如下面的希尔排序,是插入排序的改进算法,它变得更快了,但不再稳定

以下是其他的排序算法的补充(学习数据结构ing)

希尔排序:时间复杂度O(n^{2}),空间复杂度O(1),不稳定

直接插入排序只要数据越有序,那么其排序速度越快,那么如果有一种方法能使得待排序序列越来越有序,此时使用直接插入排序那不就变快了嘛,这就是希尔排序的核心思想

利用希尔增量将待排序序列分成增量个组,组内进行直接插入排序,直到增量 = 1时,即对整个序列进行一次直接插入排序

//希尔排序
//缩小增量法,将整个待排序序列进行分组,组内进行直接插入排序
//数据越有序,直接插入排序越快
void Shell_Sort(vector<int>& arr) {
	int gap = arr.size();
	while (gap > 1) {
		gap = gap / 3 + 1;//希尔增量进行变化
		for (int i = 0; i < arr.size() - gap; i++) {
			int end = i;//有序序列
			int temp = end + gap;//无序序列首元素
			while (end >= 0) {
				if (arr[end] > arr[temp]) {
					swap(arr, end, temp);
					temp = end;
					end -= gap;
				}
				else {
					break;
				}
			}
		}
	}
}
#include<stdio.h>
#include <stdlib.h>
#include<vector>
#include <iostream>
using namespace std;
template<class T>
void shell_Sort(vector<T>& arr) {
	int gap = arr.size();
	while (gap > 1) {
		gap = gap / 3 + 1;
		//这里i从gap开始,也就是每个组的第二个元素
		//然后进行插入排序,也是一个不完全的插入排序
		//在希尔增量为1时,就是一个从数组1位置开始的插入排序了
		for (int i = gap; i < arr.size(); i++) {
			for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap) {
				T temp = arr[j];
				arr[j] = arr[j + gap];
				arr[j + gap] = temp;
			}
		}
	}
}

基数排序:时间复杂度O(n),空间复杂度O(n),稳定

一种不基于比较的排序算法,也被称为数字排序或者桶排序(因为有入桶出桶操作),针对于全是数字的有效的排序算法

先根据每一位(个位十位百位等)来进行局部排序,入桶和出桶操作遵循先进先出的准则

为什么要先进先出,比如10和12,如果从左往右入桶的话,个位排完出桶是10、12,而十位也是10先进去,只要是十位数相同的,就会根据个位数来进行排序

这也侧面说明了其是稳定的(因为先进先出嘛)

//基数排序/桶排序
int maxbits(vector<int>& arr) {
	int res = 0;
	int max = INT_MIN;
	for (int i : arr) {
		if (i > max) {
			max = i;
		}
	}
	while (max != 0) {
		res++;
		max /= 10;
	}
	return res;
}
int getdigit(int num, int digit) {
	int res;
	while (digit > 0) {
		res = num % 10;
		num /= 10;
		digit--;
	}
	return res;
}
void radix_sort(vector<int>& arr, int L, int R, int digit) {
	const int radix = 10;
	int* help = new int[R - L + 1];
	for (int d = 1; d < digit; d++) {
		int* count = new int[radix]();
		int j;
		for (int i = L; i <= R; i++) {
			j = getdigit(arr[i], d);
			count[j]++;
		}
		for (int i = 1; i < radix; i++) {
			count[i] = count[i] + count[i - 1];
		}
		for (int i = R; i >= L; i--) {
			j = getdigit(arr[i], d);
			help[--count[j]] = arr[i];
		}
		for (int i = 0; i < R - L + 1; i++) {
			arr[i + L] = help[i];
		}
	}
}
void Radix_Sort(vector<int>& arr) {
	radix_sort(arr, 0, arr.size() - 1, maxbits(arr));
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值