您的位置:首页 > 编程语言 > C语言/C++

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

2018-02-28 22:26 260 查看
1.冒泡排序

时间复杂度O(n2),空间复杂度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 +
1fff8
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(n2),空间复杂度O(1)。

第1趟,在待排序记录r[1]~r
中选出最小的记录,将它与r[1]交换;第2趟,在待排序记录r[2]~r
中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r
中选出最小的记录,将它与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(n2),空间复杂度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∗logN),空间复杂度为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∗logN),空间复杂度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∗logN),空间复杂度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(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),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(N2):冒泡排序,选择排序,插入排序

2.O(NlogN):归并排序,快速排序,希尔排序,堆排序

3.O(N):计数排序,基数排序

空间复杂度

1.O(1):冒泡排序,选择排序,插入排序,希尔排序,堆排序

2.O(logN)−O(N):快速排序

3.O(N):归并排序

4.O(M):桶排序

排序算法的稳定性

稳定性:相同元素,元素顺序在排序前与排序后保持不变。

不稳定排序:选择排序,快速排序,希尔排序,堆排序。

稳定排序:冒泡排序,插入排序,归并排序,计数排序,基数排序,桶排序。

工程应用

工程上的排序是综合排序

数组较小时,是插入排序

数组较大时,快速排序或者其他O(NlogN)的排序。

题目一

已知一个几乎有序的数组,几乎有序是指,如果把数组排好序的话,每个元素移动的距离不超过k,并且k相对于数组长度来说很小。请问选择什么方法对其排序比较好。

分析:

时间复杂度为O(N)的计数与基数排序,因为不知道是否连续,不考虑。

时间复杂度为O(N2),冒泡排序与选择排序的复杂度与原始顺序无关,插入排序可以考虑时间复杂度为O(N∗K)。

时间复杂度为O(N∗logN),归并排序与快速排序的复杂度与原始顺序无关。

答案:改进后的堆排序。每次建立k个元素的小根堆,然后取堆顶元素。时间复杂度为O(N∗logK)。

题目二

判断数组中是否有重复值,必须保证额外空间复杂度为O(1).

分析: 若没有空间复杂度限制,使用哈希表。

先排序,后判断。

希尔排序,或堆排序。

题目三

把两个有序数组合并为一个数组,第一个数组空间正好可以容纳两个数组的元素。

分析:从后往前比较,可以避免第一个数组的元素往后平移。

题目四

荷兰国旗问题。只包含0,1,2的整数数组进行排序,要求使用交换,原地排序,而不是利用计数进行排序。

分析:调整过程与快排划分过程类似,时间复杂度为O(N),额外空间复杂度为O(1)。

遍历数组之前,在数组两边设立0区,与2区,每次将0,2分别放入各自对应区域。当当前位置与2区位置重合时,停止。

题目五

在行与列都有序的二维数组中找数。

分析:时间复杂度为O(M+N),M行,N列,空间复杂度为O(1)。

从左下角或者右上角开始找。

题目六

找出需要排序的最短子数组长度。例如[1,5,4,3,2,6,7],返回4,因为只有[5,4,3,2]需要排序。

分析:最优解时间复杂度为O(N),额外空间复杂度为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)。首先遍历一遍数组,选出最大值与最小值,然后将最大最小之间划分为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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: