排序算法总结-选择排序、插入排序、归并排序和快速排序
前言:
感觉好久没写博客了,十月份的计划是:要开始深入攻克数据结构和算法,耽误好久了,这都月末了,抓紧时间又学习了一波,赶紧来分享了一下,使用的语言是C++,最开始学数据结构一定要用C,掌握扎实之后,想学算法,用C++比较好,C封装没有那么好,写起来没有那么容易了。
一、准备工作
这部分会封装一些接口,如生成数组、测试排序算法执行时间等,便于比较和调试。封装在.h中,如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef SORTTESTHELPER_H_ #define SORTTESTHELPER_H_ #include <iostream> #include <ctime> #include <cassert> #include <string> using namespace std; namespace SortTestHelper { // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] int *generateRandomArray(int n, int rangeL, int rangeR) { assert(rangeL <= rangeR); int *arr = new int[n]; srand(time(NULL)); for (int i = 0; i < n; i++) arr[i] = rand() % (rangeR - rangeL + 1) + rangeL; return arr; } //生成很相近的数组 int *generateNearlyOrderedArray(int n, int swapTimes) { int *arr = new int[n]; for (int i = 0; i < n; i++) arr[i] = i; srand(time(NULL)); for (int i = 0; i < swapTimes; i++) { int posx = rand() % n; int posy = rand() % n; swap(arr[posx], arr[posy]); } return arr; } //copy一个数组 int *copyIntArray(int a[], int n) { int *arr = new int[n]; copy(a, a + n, arr); return arr; } //打印数组 template<typename T> void printArray(T arr[], int n) { for (int i = 0; i < n; i++) cout << arr[i] << " "; cout << endl; return; } //是否已排好序 template<typename T> bool isSorted(T arr[], int n) { for (int i = 0; i < n - 1; i++) if (arr[i] > arr[i + 1]) return false; return true; } //测试算法时间 template<typename T> void testSort(const string &sortName, void(*sort)(T[], int), T arr[], int n) { clock_t startTime = clock(); sort(arr, n); clock_t endTime = clock(); assert(isSorted(arr, n)); cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl; return; } }; #endif //SORTTESTHELPER_H_
二、选择排序
选择相对简单,就是两次循环,假如排序:从小到大,每次循环把小的移到前面,程序如下:
#include <iostream> #include "test.h" using namespace std; //选择排序 template<typename T> void selectionSort(T arr[], int n) { for (int i = 0; i < n; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) if (arr[j] < arr[minIndex]) minIndex = j; swap(arr[i], arr[minIndex]); } } int main() { int n = 10000; int *arr = SortTestHelper::generateRandomArray(n, 0, n); SortTestHelper::testSort("Selection Sort", selectionSort, arr, n); delete[] arr; system("pause"); return 0; }
三、插入排序
就是将较小的数据插入到前面,程序如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <algorithm> //#include "SortTestHelper.h" //#include "SelectionSort.h" #include"test.h" using namespace std; template<typename T> void insertionSort(T arr[], int n){ for( int i = 1 ; i < n ; i ++ ) { // 寻找元素arr[i]合适的插入位置 // 写法1 // for( int j = i ; j > 0 ; j-- ) // if( arr[j] < arr[j-1] ) // swap( arr[j] , arr[j-1] ); // else // break; // 写法2 // for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- ) // swap( arr[j] , arr[j-1] ); // 写法3 T e = arr[i]; int j; // j保存元素e应该插入的位置 for (j = i; j > 0 && arr[j-1] > e; j--) arr[j] = arr[j-1]; arr[j] = e; } return; } int main() { int n = 10000; // 测试1 一般测试 cout<<"Test for Random Array, size = "<<n<<", random range [0, "<<n<<"]"<<endl; int *arr1 = SortTestHelper::generateRandomArray(n,0,n); int *arr2 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n); //SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n); delete[] arr1; delete[] arr2; cout<<endl; // 测试2 有序性更强的测试 cout<<"Test for More Ordered Random Array, size = "<<n<<", random range [0, 3]"<<endl; arr1 = SortTestHelper::generateRandomArray(n,0,3); arr2 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n); //SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n); delete[] arr1; delete[] arr2; cout<<endl; // 测试3 测试近乎有序的数组 int swapTimes = 100; cout<<"Test for Random Nearly Ordered Array, size = "<<n<<", swap time = "<<swapTimes<<endl; arr1 = SortTestHelper::generateNearlyOrderedArray(n,swapTimes); arr2 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n); //SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n); delete(arr1); delete(arr2); system("pause"); return 0; }
四、归并排序
归并排序就比较复杂了,时间复杂度也从前两种的O(n^2)变为O(nlogn),代码如下:
MergeSort.h如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef INC_03_MERGE_SORT_ADVANCE_MERGESORT_H #define INC_03_MERGE_SORT_ADVANCE_MERGESORT_H #include <iostream> using namespace std; // 将arr[l...mid]和arr[mid+1...r]两部分进行归并 template<typename T> void __merge(T arr[], int l, int mid, int r) { // 经测试,传递aux数组的性能效果并不好 T aux[r - l + 1]; for (int i = l; i <= r; i++) aux[i - l] = arr[i]; int i = l, j = mid + 1; for (int k = l; k <= r; k++) { if (i > mid) { arr[k] = aux[j - l]; j++; } else if (j > r) { arr[k] = aux[i - l]; i++; } else if (aux[i - l] < aux[j - l]) { arr[k] = aux[i - l]; i++; } else { arr[k] = aux[j - l]; j++; } } } // 递归使用归并排序,对arr[l...r]的范围进行排序 template<typename T> void __mergeSort(T arr[], int l, int r) { if (l >= r) return; int mid = (l + r) / 2; __mergeSort(arr, l, mid); __mergeSort(arr, mid + 1, r); __merge(arr, l, mid, r); } template<typename T> void mergeSort(T arr[], int n) { __mergeSort(arr, 0, n - 1); } #endif //INC_03_MERGE_SORT_ADVANCE_MERGESORT_H
merge.cpp如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include "test.h" //#include "InsertionSort.h" #include "MergeSort.h" using namespace std; // 递归使用归并排序,对arr[l...r]的范围进行排序 template<typename T> void __mergeSort2(T arr[], int l, int r) { // 对于小规模数组,使用插入排序 if (r - l <= 15) { insertionSort(arr, l, r); return; } int mid = (l + r) / 2; __mergeSort2(arr, l, mid); __mergeSort2(arr, mid + 1, r); // 对于arr[mid] <= arr[mid+1]的情况,不进行merge // 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失 if (arr[mid] > arr[mid + 1]) __merge(arr, l, mid, r); } template<typename T> void mergeSort2(T arr[], int n) { __mergeSort2(arr, 0, n - 1); } int main() { int n = 50000; // 测试1 一般性测试 cout << "Test for Random Array, size = " << n << ", random range [0, " << n << "]" << endl; int* arr1 = SortTestHelper::generateRandomArray(n, 0, n); int* arr2 = SortTestHelper::copyIntArray(arr1, n); int* arr3 = SortTestHelper::copyIntArray(arr1, n); //SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n); SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n); SortTestHelper::testSort("Merge Sort 2", mergeSort2, arr3, n); delete[] arr1; delete[] arr2; delete[] arr3; cout << endl; // 测试2 测试近乎有序的数组 int swapTimes = 10; cout << "Test for Random Nearly Ordered Array, size = " << n << ", swap time = " << swapTimes << endl; arr1 = SortTestHelper::generateNearlyOrderedArray(n, swapTimes); arr2 = SortTestHelper::copyIntArray(arr1, n); arr3 = SortTestHelper::copyIntArray(arr1, n); //SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n); SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n); SortTestHelper::testSort("Merge Sort 2", mergeSort2, arr3, n); delete[] arr1; delete[] arr2; delete[] arr3; return 0; }
五、快速排序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
template <typename T> int _partition2(T arr[], int l, int r){ swap( arr[l] , arr[rand()%(r-l+1)+l] ); T v = arr[l]; // arr[l+1...i) <= v; arr(j...r] >= v int i = l+1, j = r; while( true ){ while( i <= r && arr[i] < v ) i ++; while( j >= l+1 && arr[j] > v ) j --; if( i > j ) break; swap( arr[i] , arr[j] ); i ++; j --; } swap( arr[l] , arr[j]); return j; } template <typename T> void _quickSort(T arr[], int l, int r){ // if( l >= r ) // return; if( r - l <= 15 ){ insertionSort(arr,l,r); return; } int p = _partition2(arr, l, r); _quickSort(arr, l, p-1 ); _quickSort(arr, p+1, r); } template <typename T> void quickSort(T arr[], int n){ srand(time(NULL)); _quickSort(arr, 0, n-1); }
总结:
数据结构和算法是基本功,必须深入学习,也是大厂总考的原因
作者:逆袭之路
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!