各种排序算法详解C++实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37895339/article/details/79404681

1.冒泡排序
时间复杂度 O ( n 2 ) O(n^2) ,空间复杂度 O ( 1 ) O(1)
a.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
b.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
c.针对所有的元素重复以上的步骤,除了最后一个。
d.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
若为n个数,一共需遍历n-1次,每次讲最大的数放到最后一位。第i次需遍历前n-i+1个数。

#include<iostream>
#include<thread>
using std::cout;
using std::endl;

void swap(int &a, int &b) {
	a = a + b;
	b = a - b;
	a = a - b;
}

void bubbleSort(int *unsortArray, const int &length) {
	for (int i = 0; i < length; ++i) {
		for (int j = 0; j < length - 1 - i; ++j) {
			if (unsortArray[j] > unsortArray[j + 1]) {
				swap(unsortArray[j], unsortArray[j + 1]);
			}
		}
	}
}

int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	bubbleSort(sort, length);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

2.选择排序
时间复杂度为 O ( n 2 ) O(n^2) ,空间复杂度 O ( 1 ) O(1)
第1趟,在待排序记录r[1]r[n]中选出最小的记录,将它与r[1]交换;第2趟,在待排序记录r[2]r[n]中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。

#include<iostream>
#include<thread>
using std::cout;
using std::endl;

void swap(int &a, int &b) {
	int tem = 0;
	tem = a;
	a = b;
	b = tem;
}

void selectionSort(int *unsortArray, const int &length) {
	int minIndex = -1;
	for (int i = 0; i < length; ++i) {
		minIndex = i;
		for (int j = 0; j < length - i; ++j) {
			if (unsortArray[j + i] < unsortArray[minIndex]) {
				minIndex = j + i;
			}
		}
		swap(unsortArray[i], unsortArray[minIndex]);
	}
}

int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	selectionSort(sort, length);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

3.插入排序
时间复杂度 O ( n 2 ) O(n^2) ,空间复杂度 O ( 1 ) O(1)
通过扫描前面已排序的子列表,将位置i处的元素定位到从0到i的子列表之内的正确的位置上。

#include<iostream>
#include<thread>
using std::cout;
using std::endl;

void insertSort(int *unsortArray, const int &length) {
	for (int i = 1; i < length; ++i) {
		for (int j = 0; j < i + 1; ++j) {
			if (unsortArray[j] > unsortArray[i]) {
				int tem = unsortArray[i];
				for (int k = 0; k < i - j; ++k) {
					unsortArray[i - k] = unsortArray[i - k - 1];
				}
				unsortArray[j] = tem;
				break;
			}
		}
	}
}

int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	insertSort(sort, length);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

4.归并排序
时间复杂度 O ( N l o g N ) O(N*logN) ,空间复杂度为 O ( N ) O(N)
先将所有数两个一组排序,再将两组四个数一起排序为新的一组,循环下去每次合并两组,直到所有数有序。
递归法:

#include<iostream>
#include<thread>
#include<vector>
using std::cout;
using std::endl;

void merge_sort(int *unsortArray, const int &start, const int &mid, const int &end) {
	std::vector<int> tempArray;
	int i = start;
	int j = mid + 1;
	int k = end - start + 1;
	while (i<=mid && j<=end)
	{
		if (unsortArray[i] <= unsortArray[j]) {
			tempArray.push_back(unsortArray[i++]);
		}
		else{
			tempArray.push_back(unsortArray[j++]);
		}
	}
	while (i<=mid){
		tempArray.push_back(unsortArray[i++]);
	}
	while (j <= end) {
		tempArray.push_back(unsortArray[j++]);
	}
	for (int i = 0; i < k; ++i) {
		unsortArray[start + i] = tempArray[i];
	}
}

void mergeSort(int *unsortArray, const int &start, const int &end) {
	if (start < end)
	{
		int mid = (start + end) / 2;
		mergeSort(unsortArray, start, mid);
		mergeSort(unsortArray, mid + 1, end);
		merge_sort(unsortArray, start, mid, end);
	}
}


int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	mergeSort(sort,0,9);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

非递归方法:

#include<iostream>
#include<thread>
#include<vector>
using std::cout;
using std::endl;

void merge(int *unsortArray, const int &start, const int &step, const int &length) {
	const int start2 = start + step;//第二组的起始位置
	int rightLength = -1;//第二组的长度
	int i = 0;
	int j = 0;
	std::vector<int> tempArray;
	if (start2 + step - 1 >= length - 1) {
		rightLength = length - start2;
	}
	else {
		rightLength = step;
	}
	while (i < step && j < rightLength) {
		if (unsortArray[start + i] <= unsortArray[start2 + j]) {
			tempArray.push_back(unsortArray[start + i]);
			++i;
		}
		else {
			tempArray.push_back(unsortArray[start2 + j]);
			++j;
		}
	}
	while (i < step) {
		tempArray.push_back(unsortArray[start + i]);
		++i;
	}
	while (j < rightLength) {
		tempArray.push_back(unsortArray[start2 + j]);
		++j;
	}
	for (i = 0; i < step + rightLength; ++i) {
		unsortArray[start + i] = tempArray[i];
	}

}

void mergeSort(int *unsortArray, const int &length) {//归并排序,非递归方法
	int step = 1;
	while (step < length) {
		for (int i = 0; i <= length - step - 1; i += (step * 2)) {
			merge(unsortArray, i, step, length);
		}
		step *= 2;
	}
}


int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	mergeSort(sort, 10);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

5.快速排序
时间复杂度 O ( N l o g N ) O(N*logN) ,空间复杂度 O ( l o g N )   O ( N ) O(logN)~O(N)
a.先从数列中取出一个数作为基准数。
b.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
c.再对左右区间重复第二步,直到各区间只有一个数。
在每次分区的过程中,可以先将选取的比较数放到数组的最后位置,并设定一个新的区间表示小于比较数的范围,然后从前到后依次遍历,若小于比较数则放到新的区间,若大于则不动。
递归方法:

#include<iostream>
#include<thread>
#include<vector>
using std::cout;
using std::endl;


void swap(int &a, int &b) {
	int tem = 0;
	tem = a;
	a = b;
	b = tem;
}


void quickSort(int *unsortArray, const int &start, const int &end) {//快速排序,递归方法
	if (start < end) {
		swap(unsortArray[start], unsortArray[end]);
		int index = start;
		for (int i = start; i < end; ++i) {
			if (unsortArray[i] < unsortArray[end]) {
				swap(unsortArray[i], unsortArray[index++]);
			}
		}
		swap(unsortArray[index], unsortArray[end]);
		quickSort(unsortArray, start, index - 1);
		quickSort(unsortArray, index + 1, end);
	}
}


int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	quickSort(sort, 0, 9);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

非递归方法:

#include<iostream>
#include<thread>
#include<vector>
#include<stack>
using std::cout;
using std::endl;
void swap(int &a, int &b) {
	int tem = 0;
	tem = a;
	a = b;
	b = tem;
}

int quick_sort(int *unsortArray, const int &start, const int &end) {//快速排序划分区间部分
	int index = 0;
	if (start < end) {
		swap(unsortArray[start], unsortArray[end]);
		index = start;
		for (int i = start; i < end; ++i) {
			if (unsortArray[i] < unsortArray[end]) {
				swap(unsortArray[i], unsortArray[index++]);
			}
		}
		swap(unsortArray[index], unsortArray[end]);
	}
	return index;
}


void quickSort(int *unsortArray, const int &length) {//
	std::stack<int> st;
	st.push(0);
	st.push(length - 1);
	while (!st.empty()) {
		int end = st.top();
		st.pop();
		int start = st.top();
		st.pop();
		int tempIndex = quick_sort(unsortArray, start, end);
		if (start < tempIndex-1) {
			st.push(start);
			st.push(tempIndex - 1);
		}
		if (end > tempIndex+1) {
			st.push(tempIndex + 1);
			st.push(end);
		}
	}	
}


int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	quickSort(sort, 10);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

6.堆排序
时间复杂度 O ( N l o g N ) O(N*logN) ,空间复杂度 O ( 1 ) O(1)
每次讲大根堆的堆顶元素从后往前排列,然后将最后元素放到堆顶,对堆重新序列化,然后再取出堆顶元素。

#include<iostream>
#include<thread>

using std::cout;
using std::endl;

template <typename T>void swap(T &a, T &b) {
	T tem =a;
	a = b;
	b = tem;
}


template <typename T> class PQ_ComplHeap {
public:
	T * sortArray;
	int length;
	PQ_ComplHeap(const int &len);
	~PQ_ComplHeap() {
		delete[] sortArray;
		cout<<"我被析构了!!!"<<endl;
	}
	void display();
	void heapify();
	bool inHeap(const int &i,const int &len) {
		return (i > (-1)) && (i < len);
	}
	int lChild(const int &i) {
		return (2 * i + 1);
	}
	int rChild(const int &i) {
		return 2 * (i + 1);
	}
	bool lChildValid(const int &i, const int &len) {//判断是否有左孩子
		return inHeap(lChild(i), len);
	}
	bool rChildValid(const int &i, const int &len) {  //判断是否有右孩子
		return inHeap(rChild(i), len);
	}
	int bigger(const int &i, const int &j) {  //判断哪个节点大
		if (sortArray[i]>=sortArray[j])
		{
			return i;
		}
		else
		{
			return j;
		}
	}
	int percolateDown(const int &aimIndex,const int &len);
	int properParent(const int &father,const int &len);
	void heapSort();

};
template <typename T> int PQ_ComplHeap<T>::properParent(const int &father,const int &len) {  //判断一个节点以及他的子节点哪个适合做为大根堆顶节点
	if (rChildValid(father,len)) {
		return bigger(father, bigger(lChild(father), rChild(father)));
	}
	else if (lChildValid(father,len)) {
		return bigger(father, lChild(father));
	}
	else
	{
		return father;
	}
}
template <typename T> int PQ_ComplHeap<T>::percolateDown(const int &aimIndex,const int &len) {  //堆元素的下滤
	int temp = aimIndex;
	int j;
	while (temp != (j = properParent(temp,len))) {
		swap(sortArray[temp], sortArray[j]);
		temp = j;
	}
	return temp;
}
template <typename T> void PQ_ComplHeap<T>::heapify() {       //大根堆化
	for (int i = length - 1; inHeap(i,length); --i) {
		percolateDown(i,length);
	}
}

template <typename T> PQ_ComplHeap<T>::PQ_ComplHeap(const int &len) {   //构造函数
	sortArray = new T[length = len];
	for (int i = 0; i < len; ++i) {
		sortArray[i] = i;
	}
}

template <typename T> void PQ_ComplHeap<T>::display() {    //依次打印堆中元素
	for (int i = 0; i < length; ++i) {
		cout << sortArray[i] << endl;
	}
}

template <typename T> void PQ_ComplHeap<T>::heapSort() {  //堆排序
	heapify();
	display();
	cout << "start::" << endl;
	for (int i = 0; i < length; ++i) {
		swap(sortArray[0], sortArray[length - 1 - i]);
		percolateDown(0,length-i-1);
	}
}

int main() {

	PQ_ComplHeap<int> heapSort(10);
	heapSort.heapSort();
	heapSort.display();
	while (1) {
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	}
	return 1;
}

7.希尔排序,空间复杂度 O ( 1 ) O(1)
时间复杂度 O ( N l o g N ) O(N*logN) ,时间复杂度依赖于步长的选择。

#include<iostream>
#include<thread>
#include<vector>
#include<stack>
using std::cout;
using std::endl;
void swap(int &a, int &b) {
	int tem = 0;
	tem = a;
	a = b;
	b = tem;
}


void shellSort(int *unsortArray, const int &length, int step) {
	while (step > 0) {
		for (int i = step; i < length; ++i) {
			int tem = i;
			while (tem >= step) {
				if (unsortArray[tem] < unsortArray[tem - step]) {
					swap(unsortArray[tem], unsortArray[tem - step]);
				}
				tem -= step;
			}
		}
		--step;
	}
}

int main() {
	int sort[] = { 9,8,7,6,5,4,3,2,1,0 };
	int length = sizeof(sort) / sizeof(int);
	shellSort(sort, 10, 1);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}
	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

8.基数排序
时间复杂度为O(N),空间复杂度为 O ( M ) O(M) ,M为桶的数量。
不是基于比较的排序,基于桶排序的思想。先将个位桶排序再依次倒出,然后十位。。。

#include<iostream>
#include<thread>
#include<vector>
#include<stack>
#include<queue>
using std::cout;
using std::endl;



int pickNumber(const int &number, const int &loc) {  //返回一个十进制数的第几位,个位为0
	int tem = number;
	if (loc > 0) {
		tem = number / (loc * 10);
	}
	return tem % 10;
}

void radixSort(int *unsortArray, const int &length) {  //基数排序
	std::queue<int> bucket[10];
	for (int i = 0; i < 2; ++i) {
		for (int j = 0; j < length; ++j) {
			
			int k = pickNumber(unsortArray[j], i);
			bucket[k].push(unsortArray[j]);
		}
		int k = 0;
		for (int j = 0; j < length; ++j) {
			while (!bucket[j].empty())
			{
				unsortArray[k++] = bucket[j].front();
				bucket[j].pop();
			}
		}
	}
}

int main() {
	int sort[] = { 39,8,27,6,45,4,33,2,41,20 };
	int length = sizeof(sort) / sizeof(int);
	radixSort(sort, 10);

	for (int i = 0; i < length; ++i) {
		cout << sort[i] << endl;
	}

	while (1) {
		std::this_thread::sleep_for(std::chrono::microseconds(1000));
	}
}

排序算法复杂度总结

时间复杂度

1. O ( N 2 ) O(N^2) :冒泡排序,选择排序,插入排序
2. O ( N l o g N ) O(NlogN) :归并排序,快速排序,希尔排序,堆排序
3. O ( N ) O(N) :计数排序,基数排序

空间复杂度

1. O ( 1 ) O(1) :冒泡排序,选择排序,插入排序,希尔排序,堆排序
2. O ( l o g N ) O ( N ) O(logN)-O(N) :快速排序
3. O ( N ) O(N) :归并排序
4. O ( M ) O(M) :桶排序

排序算法的稳定性

稳定性:相同元素,元素顺序在排序前与排序后保持不变。
不稳定排序:选择排序,快速排序,希尔排序,堆排序。
稳定排序:冒泡排序,插入排序,归并排序,计数排序,基数排序,桶排序。

工程应用

工程上的排序是综合排序
数组较小时,是插入排序
数组较大时,快速排序或者其他 O ( N l o g N ) O(NlogN) 的排序。

题目一

已知一个几乎有序的数组,几乎有序是指,如果把数组排好序的话,每个元素移动的距离不超过k,并且k相对于数组长度来说很小。请问选择什么方法对其排序比较好。
分析:
时间复杂度为 O ( N ) O(N) 的计数与基数排序,因为不知道是否连续,不考虑。
时间复杂度为 O ( N 2 ) O(N^2) ,冒泡排序与选择排序的复杂度与原始顺序无关,插入排序可以考虑时间复杂度为 O ( N K ) O(N*K)
时间复杂度为 O ( N l o g N ) O(N*logN) ,归并排序与快速排序的复杂度与原始顺序无关。
答案:改进后的堆排序。每次建立k个元素的小根堆,然后取堆顶元素。时间复杂度为 O ( N l o g K ) O(N*logK)

题目二

判断数组中是否有重复值,必须保证额外空间复杂度为 O ( 1 ) O(1) .
分析: 若没有空间复杂度限制,使用哈希表。
先排序,后判断。
希尔排序,或堆排序。

题目三

把两个有序数组合并为一个数组,第一个数组空间正好可以容纳两个数组的元素。
分析:从后往前比较,可以避免第一个数组的元素往后平移。

题目四

荷兰国旗问题。只包含0,1,2的整数数组进行排序,要求使用交换,原地排序,而不是利用计数进行排序。
分析:调整过程与快排划分过程类似,时间复杂度为 O ( N ) O(N) ,额外空间复杂度为 O ( 1 ) O(1)
遍历数组之前,在数组两边设立0区,与2区,每次将0,2分别放入各自对应区域。当当前位置与2区位置重合时,停止。

题目五

在行与列都有序的二维数组中找数。
分析:时间复杂度为 O ( M + N ) O(M+N) ,M行,N列,空间复杂度为 O ( 1 ) O(1)
从左下角或者右上角开始找。

题目六

找出需要排序的最短子数组长度。例如[1,5,4,3,2,6,7],返回4,因为只有[5,4,3,2]需要排序。
分析:最优解时间复杂度为 O ( N ) O(N) ,额外空间复杂度为 O ( 1 ) O(1)
首先从左到右遍历数组,用一个max变量记录遍历过的最大值,若遍历过的最大值大于当前数,则为反序,此时只记录出现反序的最右位置。
然后从右往左遍历数组,用一个min变量记录遍历过的最小值,若遍历过的最小值小于当前数,则为反序,此时只记录出现反序的最左位置。
最右与最左位置中间的范围(包括最右最左)为需要排序的范围。

int minSortLength(const int *unsortArray, const int &length) {
	int max = unsortArray[0];
	int rightFlag = 0;
	int min = unsortArray[length - 1];
	int leftFlag = length;
	for (int i = 0; i < length; ++i) {
		if (unsortArray[i] >= max) {
			max = unsortArray[i];
		}
		else{
			rightFlag = i;
		}
	}
	for (int i = length - 1; i >= 0; --i) {
		if (unsortArray[i] <= min) {
			min = unsortArray[i];
		}
		else {
			leftFlag = i;
		}
	}
	return (rightFlag - leftFlag) > 0 ? (rightFlag - leftFlag + 1) : 0;
}

题目七

给定一个整数数组,返回排序之后,相邻两个数的最大差值。例如[1,2,3,7,8],返回4。
分析:最优解时间复杂度为 O ( N ) O(N) ,空间复杂度为 O ( N ) O(N) 。首先遍历一遍数组,选出最大值与最小值,然后将最大最小之间划分为N个桶,代表N个区间,然后将最大值放到第N+1个桶中。然后将所有数放到桶中,比较当前桶的最大值与下一个非空桶的最小值之间的差,选出最大的差值。

int maxDifference(const int *unsortArray, const int &length) {
	int max = unsortArray[0];
	int min = unsortArray[0];
	int len = length + 1;
	std::stack<int> *st = new std::stack<int>[len];
	for (int i = 0; i < length; ++i) {
		if (unsortArray[i] > max) {
			max = unsortArray[i];
		}
		if (unsortArray[i] < min) {
			min = unsortArray[i];
		}
	}
	double intervel = (max - min) / static_cast<double>(length);
	for (int i = 0; i < length; ++i) {
		if (unsortArray[i] < max) {
			int k = (unsortArray[i] - min) / intervel;
			st[k].push(unsortArray[i]);
		}
	}
	int lastMax = min;
	int nowMax = min;
	int nowMin = max;
	int maxLength = 0;
	for (int i = 0; i < len; ++i) {
		nowMin = max;
		lastMax = nowMax;
		while (!st[i].empty()){
			int tem = st[i].top();
			st[i].pop();
			if (tem < nowMin) {
				nowMin = tem;
			}
			if (tem > nowMax) {
				nowMax = tem;
			}
		}
		int temp = nowMin - lastMax;
		if (temp > maxLength) {
			maxLength = temp;
		}
	}
	delete[] st;
	return maxLength;
}

猜你喜欢

转载自blog.csdn.net/weixin_37895339/article/details/79404681
今日推荐