做網站設計的成都市seo網站公司
文章目錄
- 排序概念
- 直接插入排序
- 希爾排序
- 冒泡排序
- 堆排序
- 選擇排序
- 驗證不同排序的運行時間
排序概念
排序指的是通過某一特征關鍵字(如信息量大小,首字母等)來對一連串的數據進行重新排列的操作,實現遞增或者遞減的數據排序。
穩(wěn)定性:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩(wěn)定的;否則稱為不穩(wěn)定的。
在實際的應用中是非常常見的。
文件的排序
購物的商品排序
在我們常見的排序算法中,有這幾種:
這些排序算法都是通過自身空間,通過不斷交換來實現排序的。
直接插入排序
思想:當我們拿到了一組數組時,先將第一個元素定為前序序列,讓第二個元素與它對比,以升序為例,大的就放在第一個元素之后,小的就放在第一個元素之前,放完之后,兩個元素將成為新的前序序列;接著就是將第三個元素與前序序列的元素比較,比較最大的元素,也就是前序序列的最后一個元素,比它大就將元素向后挪移,為插入數騰出一個元素空間;依此類推。
玩斗地主時從小排到大的就是這種思想:
#include"Sort.h"
void PrintfArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}//直接插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){//將數組看作是一個個數插入進去的,從第二個數開始插入//比較插入數和前序序列最后一個數的大小//不符合條件就前序序列縮短,一直比較到大于end值停下來if (a[end] >tmp ){a[end + 1] = a[end];}else{break;}end--;}a[end + 1] = tmp;}}
內循環(huán)就是將新插入的數找到合適位置,讓出空間,讓新的數插入;
時間復雜度:O(N^2)
驗證:
void TestInsertSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };InsertSort(a, sizeof(a) / sizeof(a[0]));PrintfArray(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestInsertSort();return 0;
}
希爾排序
在上面的直接插入排序中,如果插入數一直大于前序序列,會發(fā)現內循環(huán)會走的比較快;因為都排序好了,只需要比較前序序列的最后一個元素即可;
思想:希爾排序中,就是先將一組數組分成幾等份,將每一份都進行排序,這樣對于下次進行直接插入排序就預先做好了排序,簡稱預排序;接著不斷縮短每一份的長度,一直做著預排序,直到每一份的長度為1時,就相當于上面的直接插入排序。
希爾排序就是對直接插入排序進行優(yōu)化,通過預排序,讓數組的排序比較有序,這樣在再次排序時,就會省出會很多時間。
對于gap的取值,一般習慣直接對半取,但現在也有將gap取成三等份的;但實際效果都差不多;
void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}//希爾排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){//gap=gap/2;gap = gap/3 + 1;for (int i=0; i< n - gap; i ++){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}}
1.將tmp與前序序列相比大小,交換,最后將空出的位插入tmp;
2.前序序列擴大了,新增一個數;
3.在不同組中進行直插;
4.進行不斷的預排序;
在這里,不是將每一組排完再進行下一組的排序,而是排一組的前序序列之后,跳掉下一組去進行排序到前序序列;gap若取3等份,要加上1,不然可能會出現達不到gap==1的情況,如為8時,gap取到2就停止了;
這里不要看著有很多層循環(huán)進行嵌套,實際上它的算法效率是遠遠高于直接插入排序的,以gap一直取二等份為例,1000個數據也就取10次gap,100萬數據也就取20次gap,10億才取24次gap,所以外層的循環(huán)實際次數是不大的,相對如此大的數據幾乎可以忽略不計;
驗證:
void TestShellSort()
{int a[]= { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 }; ShellSort(a, sizeof(a)/sizeof(a[0]));PrintfArray(a, sizeof(a) / sizeof(a[0]));
}
int main()
{TestShellSort();return 0;
}
時間復雜度:O(N^1.25) ~ O(1.6*N^1.25)
冒泡排序
思想:通過循環(huán),不斷的比較相鄰的兩個值,將最大的值往后放到最后一個位置,再通過一層循環(huán),進行多躺的比較,總是將最大數往后放即可;
這樣最大的數就排好了,以此類推排其他的數字;
//冒泡排序
void BubbleSort(int* a, int n)
{int mark = 0;for (int i = 0; i < n - 1; i++){for (int j = 0; j < n -1- i; j++){if (a[j] > a[j + 1]){Swap(&a[j], &a[j + 1]);mark = 0;}else{mark = 1;}}//表示沒有進行交換,已排序好了if (mark == 1){break;}}}
時間復雜度:O(N^2)
堆排序
堆排序鏈接處
堆排序之前寫過了,這里就不多解釋;
void AdjustDown(int* a, int n, int parent)
{//左孩子int child = parent * 2 + 1;while (child<n){//右孩子比左孩子大if (child + 1 < n && a[child + 1] > a[child]){child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
//堆排序
void HeapSort(int* a, int n)
{//建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}//交換int end = n - 1;while (end>0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}
時間復雜度:O(N*logN)
選擇排序
思想:在數組中找出最大值和最小值的下標,記錄它,然后分別與起始與結尾位置進行交換,這樣一次就能找出最大值和最小值了,接著縮短數組起始和結尾位置,然后再通過循環(huán)依次進行此步驟;
void SelectSort(int* a,int n)
{int begin = 0;int end = n - 1;while (begin < end){int min = begin;int max = begin;//找出區(qū)間里的max和minfor (int i = begin + 1; i <= end; i++){if (a[i] < a[min]){min = i;}if (a[i] > a[max]){max = i;}}//將最小數放在起始位置Swap(&a[begin], &a[min]);//max位置的數一旦被改變,max也需跟隨改變if (max == begin){max = min;}Swap(&a[end], &a[max]);begin++;end--;}
}
這里需要注意的是,如果起始點就是最大數時,當最小數與起始位置交換后,那么max表示下標,max不變,仍然指著起始位置的下標,所以需要跟隨max的值改變而改變;
O(N^2)
驗證不同排序的運行時間
通過10000個數據來驗證它們的排序運行時間;
void TestOP()
{srand(time(NULL));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);//賦值for (int i = 0; i < N; i++){a1[i] = rand();a2[i] = a1[i];a3[i] = a2[i];a4[i] = a3[i];a5[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();BubbleSort(a3, N);int end3 = clock();int begin4 = clock();HeapSort(a4, N);int end4 = clock();int begin5 = clock();SelectSort(a5, N);int end5 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("BubbleSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("SelectSort:%d\n", end5 - begin5);}
int main()
{TestOP();return 0;
}