各种排序算法详解C++实现
2018-02-28 22:26
316 查看
1.冒泡排序
时间复杂度O(n2),空间复杂度O(1)。
a.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
b.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
c.针对所有的元素重复以上的步骤,除了最后一个。
d.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
若为n个数,一共需遍历n-1次,每次讲最大的数放到最后一位。第i次需遍历前n-i+1个数。
2.选择排序
时间复杂度为O(n2),空间复杂度O(1)。
第1趟,在待排序记录r[1]~r
中选出最小的记录,将它与r[1]交换;第2趟,在待排序记录r[2]~r
中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r
中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。
3.插入排序
时间复杂度O(n2),空间复杂度O(1)。
通过扫描前面已排序的子列表,将位置i处的元素定位到从0到i的子列表之内的正确的位置上。
4.归并排序
时间复杂度O(N∗logN),空间复杂度为O(N)。
先将所有数两个一组排序,再将两组四个数一起排序为新的一组,循环下去每次合并两组,直到所有数有序。
递归法:
非递归方法:
5.快速排序
时间复杂度O(N∗logN),空间复杂度O(logN) O(N)。
a.先从数列中取出一个数作为基准数。
b.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
c.再对左右区间重复第二步,直到各区间只有一个数。
在每次分区的过程中,可以先将选取的比较数放到数组的最后位置,并设定一个新的区间表示小于比较数的范围,然后从前到后依次遍历,若小于比较数则放到新的区间,若大于则不动。
递归方法:
非递归方法:
6.堆排序
时间复杂度O(N∗logN),空间复杂度O(1)。
每次讲大根堆的堆顶元素从后往前排列,然后将最后元素放到堆顶,对堆重新序列化,然后再取出堆顶元素。
7.希尔排序,空间复杂度O(1)。
时间复杂度O(N∗logN),时间复杂度依赖于步长的选择。
8.基数排序
时间复杂度为O(N),空间复杂度为O(M),M为桶的数量。
不是基于比较的排序,基于桶排序的思想。先将个位桶排序再依次倒出,然后十位。。。
2.O(NlogN):归并排序,快速排序,希尔排序,堆排序
3.O(N):计数排序,基数排序
2.O(logN)−O(N):快速排序
3.O(N):归并排序
4.O(M):桶排序
不稳定排序:选择排序,快速排序,希尔排序,堆排序。
稳定排序:冒泡排序,插入排序,归并排序,计数排序,基数排序,桶排序。
数组较小时,是插入排序
数组较大时,快速排序或者其他O(NlogN)的排序。
分析:
时间复杂度为O(N)的计数与基数排序,因为不知道是否连续,不考虑。
时间复杂度为O(N2),冒泡排序与选择排序的复杂度与原始顺序无关,插入排序可以考虑时间复杂度为O(N∗K)。
时间复杂度为O(N∗logN),归并排序与快速排序的复杂度与原始顺序无关。
答案:改进后的堆排序。每次建立k个元素的小根堆,然后取堆顶元素。时间复杂度为O(N∗logK)。
分析: 若没有空间复杂度限制,使用哈希表。
先排序,后判断。
希尔排序,或堆排序。
分析:从后往前比较,可以避免第一个数组的元素往后平移。
分析:调整过程与快排划分过程类似,时间复杂度为O(N),额外空间复杂度为O(1)。
遍历数组之前,在数组两边设立0区,与2区,每次将0,2分别放入各自对应区域。当当前位置与2区位置重合时,停止。
分析:时间复杂度为O(M+N),M行,N列,空间复杂度为O(1)。
从左下角或者右上角开始找。
分析:最优解时间复杂度为O(N),额外空间复杂度为O(1)。
首先从左到右遍历数组,用一个max变量记录遍历过的最大值,若遍历过的最大值大于当前数,则为反序,此时只记录出现反序的最右位置。
然后从右往左遍历数组,用一个min变量记录遍历过的最小值,若遍历过的最小值小于当前数,则为反序,此时只记录出现反序的最左位置。
最右与最左位置中间的范围(包括最右最左)为需要排序的范围。
分析:最优解时间复杂度为O(N),空间复杂度为O(N)。首先遍历一遍数组,选出最大值与最小值,然后将最大最小之间划分为N个桶,代表N个区间,然后将最大值放到第N+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; }
相关文章推荐
- 各种排序算法的C++实现与性能比较
- 【排序算法】用C++实现各种排序算法
- 各种排序算法的C++实现与性能比较
- 各种常见排序算法的C++实现
- 各种排序算法的C++实现
- 各种排序算法的C++实现与性能比较
- 各种排序算法的C++实现
- 各种排序算法C++实现
- C++实现各种排序算法
- 各种排序算法的C++实现与性能比较
- 各种排序算法的实现及其比较(c++实现)
- 各种排序算法的C++实现
- 各种排序算法的C++实现
- 各种排序算法的C++实现与性能比较
- 各种排序算法的C++实现
- 各种排序算法学习整理 C++实现
- 各种排序算法的C++实现与性能比较
- 使用c++实现并分析各种排序算法
- 数据结构——算法之(043)(c++各种排序算法实现)
- 各种排序算法的C++实现与性能比较