各种排序算法思想
快速排序
主要思想: 主要是基于分治。(分治解讀)
基本步驟:
1.確定分界點x ,常用方式q[l]? q[l + r >> 1] , q[r] , 左右部分未必長度相等
2.根據分界點x調整區間,使得滿足小于等于x的在左邊,大于等于x的在右邊
3.左右兩端,遞歸縮小規模處理,然后進行拼接,即兩個區間合并
注釋:其中使用了雙指針算法思想中的,從兩側向中間移動來維護一段區間的方法
// 快速排序算法模板 void quick_sort(int q[], int l, int r) {if (l >= r) return;int i = l - 1, j = r + 1, x = q[l + r >> 1];while (i < j){do i ++ ; while (q[i] < x);do j -- ; while (q[j] > x);if (i < j) swap(q[i], q[j]);}quick_sort(q, l, j);quick_sort(q, j + 1, r); }歸并排序
主要思想: 主要是基于分治。(分治解讀)
基本步驟:
1.確定分界點mid,常用方式l + r >> 1?
2.遞歸排序左邊和右邊
3.歸并合二為一,雙指針算法。兩個有序的序列進行二合一排序存到額外空間
注釋:其中使用了雙指針算法思想中的,從兩側向中間移動來維護一段區間的方法
// 歸并排序算法模板 void merge_sort(int q[], int l, int r) {if (l >= r) return;int mid = l + r >> 1;merge_sort(q, l, mid);merge_sort(q, mid + 1, r);int k = 0, i = l, j = mid + 1;while (i <= mid && j <= r)if (q[i] < q[j]) tmp[k ++ ] = q[i ++ ];else tmp[k ++ ] = q[j ++ ];while (i <= mid) tmp[k ++ ] = q[i ++ ];while (j <= r) tmp[k ++ ] = q[j ++ ];for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j]; }?歸并排序 自帶時間復雜度測試
//時間復雜度 O(N*log2N) //穩定程度: 穩定 /* 確定分界點,中間位置 兩端排序 歸并,合二為一 */#include<iostream> #include<time.h> using namespace std; int tmp[250001]; void Sort(int List[], int l, int r);int main() {int a[250000];int k, j;// 設置種子srand((unsigned)time(NULL));/* 生成 10 個隨機數 */for (k = 0; k < 250000; k++){// 生成實際的隨機數j = rand();a[k] = j;}clock_t start_time = clock();Sort(a,0,250000-1);clock_t end_time = clock();//for (int i = 0; i < 200000; i++)//{// cout << a[i] << " ";//}cout << "\n程序段運行時間:" << static_cast<double> (end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << endl;system("pause"); } void Sort(int List[], int l, int r) {if (l >= r) return;int mid = l + r >> 1; //取中間數Sort(List, l, mid), Sort(List, mid + 1, r); //左右遞歸排序int k = 0, i = l, j = mid + 1; //k表示已合并數組中有幾個元素,分開兩個有序數組while (i <= mid && j <= r) //進行雙指針比較if (List[i] <= List[j]) tmp[k++] = List[i++]; else tmp[k++] = List[j++];while (i <= mid) tmp[k++] = List[i++]; //分別處理剩余部分while (j <= r) tmp[k++] = List[j++];for (i = l, j = 0; i <= r; i++, j++) List[i] = tmp[j]; //拷入原空間}快速排序 自帶時間復雜度檢測
//時間復雜度 O(N*log2N //穩定性:不穩定 //來源于分治思想 /* 確定分界點 調整區間 遞歸處理兩端 算法思想,快排是基于冒泡排序的優化,冒泡排序從一側開始進行,而快排是兩邊同時進行從而時間復雜度折半,同時包含了二分的思想在里面 */#include<iostream> #include<time.h> using namespace std;void Sort(int List[], int l, int r);int main() {int a[80000];int k, j;// 設置種子srand((unsigned)time(NULL));/* 生成 10 個隨機數 */for (k = 0; k < 80000; k++){// 生成實際的隨機數j = rand();a[k] = j;}clock_t start_time = clock();Sort(a,0,80000-1);clock_t end_time = clock();for (int i = 0; i < 80000; i++){cout << a[i] << " ";}cout << "\n程序段運行時間:" << static_cast<double> (end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << endl;system("pause"); } void Sort(int List[], int l, int r) {if (l >= r) return; //邊界判斷int i = l - 1, j = r + 1, x = List[l]; //x為分界點while (i < j){//兩次do,主要在于找到左右兩側<x和>x的第一個數do i++; while (List[i] < x); do j--; while (List[j] > x);if (i < j) swap(List[i], List[j]);else break;}Sort(List, l, j), Sort(List, j + 1, r);}冒泡排序 自帶時間復雜度測試
#include<iostream> #include<time.h> using namespace std;void Sort(int List[], int n);int main() {int a[10000];int k, j;// 設置種子srand((unsigned)time(NULL));/* 生成 10 個隨機數 */for (k = 0; k < 10000; k++){// 生成實際的隨機數j = rand();a[k] = j;}clock_t start_time = clock();Sort(a, 10000);clock_t end_time = clock();for (int i = 0; i < 10000; i++){cout << a[i] << " ";}cout << "\n程序段運行時間:" << static_cast<double> (end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << endl;system("pause"); } void Sort(int List[], int n) {for (int i = 0; i < n - 1; i++){for (int j = 0; j < n - i - 1; j++) //j<n-i-1:首先j不與自己比較所以-1,其次每次外循環都會產生一個已經排序的最大數,所以內循環要排除已經排好的,即總數為n-i。if (List[j] > List[j + 1])swap(List[j], List[j + 1]);} }冒泡排序優化
#include<iostream> #include<time.h> using namespace std;void Sort(int List[], int n);int main() {int a[10000];int k, j;// 設置種子srand((unsigned)time(NULL));/* 生成 10 個隨機數 */for (k = 0; k < 10000; k++){// 生成實際的隨機數j = rand();a[k] = j;}clock_t start_time = clock();Sort(a, 10000);clock_t end_time = clock();for (int i = 0; i < 10000; i++){cout << a[i] << " ";}cout << "\n程序段運行時間:" << static_cast<double> (end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << endl;system("pause"); } void Sort(int List[], int n) {bool sorted = false; //整體排序標志標志,首先假定尚未排序while (!sorted){sorted = true; //假定已經排序for (int j = 1; j < n -1; j++) //j<n-i-1:首先j不與自己比較所以-1,其次每次外循環都會產生一個已經排序的最大數,所以內循環要排除已經排好的,即總數為n-i。if (List[j - 1] > List[j]){swap(List[j - 1], List[j]);sorted = false;//因整體排序不能保證,需要清除排序標志}}n--;//至此元素必然就位,故可以縮短待排序序列的有效長度}//借助布爾型標志位sorted,可及時提前退出,而丌致總是蠻力地做n - 1趟掃描交換選擇排序 自帶時間復雜度分析
從當前未排序的整數中找到最小的整數,將它放在已排序的整數列表的最后。 #include<iostream> #include<time.h> using namespace std;void Sort(int List[], int n);int main() {int a[10000];int k, j;// 設置種子srand((unsigned)time(NULL));/* 生成 10 個隨機數 */for (k = 0; k < 10000; k++){// 生成實際的隨機數j = rand();a[k] = j;}clock_t start_time = clock();Sort(a, 10000);clock_t end_time = clock();for (int i = 0; i < 10000; i++){cout << a[i] << " ";}cout << "\n程序段運行時間:" << static_cast<double> (end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << endl;system("pause"); } void Sort(int List[], int n) {for (int i = 0; i < n - 1; i++){int min = i;//min處,假設第一個是最小的,是;數組的下標for (int j = i + 1; j < n; j++) //j=i+1是因為之前已經掃描過了{if (List[j] < List[min]){min = j; //移動記錄下來}}swap(List[i], List[min]); //掃描一遍結束后,交換一次} }選擇排序與冒泡排序的區別
冒泡排序:
?
冒泡排序(BubbleSort)的基本概念是:依次比較相鄰的兩個數,將小數放在前面,大數放在后面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數 放后。
?
然后比較第2個數和第3個數,將小數放前,大數放后,如此繼續,直至比較最后兩個數,將小數放前,大數放后。至此第一趟結束,將最大的數放到了最后。
?
在第二趟:仍從第一對數開始比較(因為可能由于第2個數和第3個數的交換,使得第1個數不再小于第2個數),將小數放前中,大數放后,一直比較到倒數第二個數(倒數第一的位置上已經是最大的),第二趟結束,在倒數第二的位置上得到一個新的最大數(其實在整個數列中是第二大的數)。如此下去,重復以上過程,直至最終完成排序。
?
選擇排序
?
第一次從下標為0的開始下標為0的這個數與后面的n-1個進行比較;找出最小或者最大的放在下標為0的這個位置;第二次從下標為1的開始比較;查詢剩下的最大或者最小值;放在下標為1的位置;以此類推;直到排序完成。
?
總結
?
從上兩段代碼可以看出,它們處于同一個數量級,即時間復雜度是相同的,都用了兩層循環,為O(n^2)(n:排序個數); 但是內層循環中,冒泡排序的互換位置的操作從概率上講要明顯多于選擇排序. 整個排序算法,選擇排序換位操作為O(n),冒泡排序為O(n^2/2). 所以綜合來講選擇排序的時間效率要高于冒泡排序.
總結