參考:十大排序
目錄
一.交換排序
1.冒泡排序
時間複雜度: ( / )
空間複雜度:1
穩定
思想:
- 比較相鄰兩個元素大小,左邊大就交換
- 每次再從第一個開始比較,第一次比較完最大的在隊尾
- 比較length-1次
void BubbleSort(int *arr,int len)
{
int i, j;
int temp;
for(i = 0; i < len - 1; i++)
{
for(j = 0; j < len - i - 1; j++)
{
if(arr[j] > arr [j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
雙冒泡排序
思想:
- 每輪都進行兩次排序,一次從低到高,將最大的排到最右端,再從高到底,把最小的排到最左端。(當然每一次都是冒泡排序)
void BubbleSort2(int *arr, int len)
{
int i, left, right, temp;
left = 0;
right = len - 1;
while(left < right)
{
for(i = left; i < right; i++)
{
if(arr[i] > arr[i + 1])
{
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
right--;
for(i = right; i > left; i--)
{
if(arr[i] < arr[i - 1])
{
temp = arr[i];
arr[i] = arr[i - 1];
arr[i - 1] = temp;
}
}
left++;
}
}
雙冒泡排序改進版:
再設一個左標兵 l 和右標兵 r ,記錄一下萬一排序有幾個已經排好了,就不用在排了。但一開始要設個初值,萬一正好是個完全有序的數列,不設初值會出錯。
void BubbleSort3(int *arr, int len)
{
int i, left, right, l, r, temp;
left = 0;
right = len - 1;
l = right;
r = left;
while(left < right)
{
for(i = left; i < right; i++)
{
if(arr[i] > arr[i + 1])
{
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
r = i;
}
}
right = r;
for(i = right; i > left; i--)
{
if(arr[i] < arr[i - 1])
{
temp = arr[i];
arr[i] = arr[i - 1];
arr[i - 1] = temp;
l = i;
}
}
left = l;
}
}
2.快速排序
時間複雜度: (,)
的出處?:
最好情況:每次都恰好五五分,一次遞歸共需比較n次,遞歸深度爲
空間複雜度:
不穩定
思想:
- 將第一個數作爲基準數,將比它小的放它左邊,比它大的放它右邊
- 分別從頭(i++)和從尾(j--)依次遍歷,直到i = j爲止
- 在對基準數的左右兩邊再重複這樣的過程(分治法)
void QuickSort(int *arr,int left,int right)
{
if(left >= right)
return;
int i = left;
int j = right;
int key = arr[i];
while(i < j)
{
while(i < j && key <= arr[j])
{
j--;
}
arr[i] = arr[j];
while(i < j && key >= arr[i])
{
i++;
}
arr[j] = arr[i];
}
arr[i] = key;
QuickSort(arr,left,i-1);
QuickSort(arr,i+1,right);
}
二.選擇排序
1.簡單選擇排序
時間複雜度: (,)
表現最穩定的排序算法之一,因爲無論什麼數據進去都是O(n2)的時間複雜度,所以用到它的時候,數據規模越小越好。
空間複雜度:1
不穩定
思想:
- 從待排序序列中,找到關鍵字最小的元素
- 如果最小元素不是待排序序列的第一個元素,將其和第一個元素互換
- 從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束
-
void SelectionSort(int *arr,int len) { int i, j, k, temp; for(i = 0; i < len - 1; i++) { k = i; for(j = i + 1; j < len; j++) { if(arr[j] < arr[k]) { k = j; } } if(k != i) { temp = arr[i]; arr[i] = arr[k]; arr[k] = temp; } } }
2.堆排序
能實現從海量數據中找出k個最大或最小值,創建一個結點爲k的樹即可,每次跟裏面的樹比較,大於就放進去,小於就下一個。但插入排序不行,如果最大數最後纔出現,那麼不到最後一步完成,不敢保證說前面k個就是答案了。
面對這種問題,快速排序歸併排序也可以解決。
時間複雜度: (,)
堆排序可分細分爲兩部分:建堆過程+排序過程。建堆過程時間複雜度爲O(n),即將一個無序數組建立成堆只需要線性時間。排序過程需要對n個數據進行篩選時,每次篩選需要O(logn)時間,所以整個排序過程的時間爲O(nlogn)因此堆排序總的運行時間爲: O(nlogn) = O(n) + O(nlogn)
空間複雜度:
不穩定
什麼是堆:
任意的葉子節點小於(或大於)它所有的父節點。對此,又分爲大頂堆和小頂堆,大頂堆要求節點的元素都要大於其孩子,小頂堆要求節點元素都小於其左右孩子,兩者對左右孩子的大小關係不做任何要求。
思想:
- 首先將序列構建稱爲大頂堆
- 取出當前大頂堆的根節點,將其與序列末尾元素進行交換
- 對交換後的n-1個序列元素進行調整,使其滿足大頂堆的性質
- 重複2.3步驟,直至堆中只有1個元素爲止
void MaxHeap(int *arr, int start, int end)
{
int dad = start; //父節點
int son = dad * 2 + 1; //子節點(樹的根從編號0開始算)
int temp;
while(son <= end) //如果子節點在範圍之外就結束排序
{
//先比較兩個子節點大小,選出最大的子節點
if(son + 1 <= end && arr[son] < arr[son + 1])
son++;
//如果父節點比最大的子節點大,則沒有再排序的必要
if(arr[dad] > arr[son])
return;
//否則交換內容,再對孫節點繼續比較,糾正這個交換帶來的影響
else
{
temp = arr[dad];
arr[dad] = arr[son];
arr[son] = temp;
dad = son;
son = dad * 2 + 1;
}
}
}
void HeapSort(int *arr,int len)
{
int i, temp;
//這整個循環先將整個數組都變成一個大頂堆
//len/2-1 就是最後一個父節點的位置(注意這邊樹的編號從0開始)
for(i = len / 2 - 1; i >= 0; i--)
{
MaxHeap(arr, i, len - 1);
}
//每一次循環都將大頂堆最大的數和末尾的數進行交換
//再對剩下的數進行大頂堆排序
//最後一個不用排
for(i = len - 1; i > 0; i--)
{
temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
MaxHeap(arr, 0, i - 1); //下一輪大頂堆排序的範圍
}
}
三.插入排序
1.直接插入排序
時間複雜度: (,)
空間複雜度:1
穩定
思想:
- 將數組中的所有元素依次跟前面已經排好的元素相比較,如果選擇的元素比已排序的元素小,則交換,直到全部元素都比較過。
- 從第一個元素開始,該元素可以認爲已經被排序;
- 取出下一個元素,在已經排序的元素序列中從後向前掃描;
- 如果該元素(已排序)大於新元素,將該元素移到下一位置;
- 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;
- 將新元素插入到該位置後;
void InsertSort(int *arr,int len)
{
int i, j, temp;
for(i = 1; i < len; i++)
{
temp = arr[i];
for(j = i - 1; j >= 0; j--)
{
if(arr[j] > temp)
{
arr[j + 1] = arr[j];
}
else
{
break;
}
}
arr[j + 1] = temp;
}
}
2.希爾排序
時間複雜度: (,)
空間複雜度:1
不穩定
思想:
- 將待排序數組按照步長gap進行分組,然後將每組的元素利用直接插入排序的方法進行排序;每次將gap折半減小,循環上述操作;當gap=1時,利用直接插入,完成排序。
void ShellSort(int *arr,int len)
{
int i, j, k, gap, temp;
for(gap = len / 2; gap > 0; gap /= 2)
{
for(i = 0; i < gap; i++) //確定執行多少次直接選擇排序
{
for(j = i + gap; j < len; j += gap) //執行分組後的單個的選擇排序
{
temp = arr[j];
for(k = j - gap; k >= 0; k -= gap)
{
if(arr[k] > temp)
{
arr[k + gap] = arr[k];
}
else
{
break;
}
}
arr[k + gap] = temp;
}
}
}
}
再簡化版:
void ShellSort2(int *arr,int len)
{
int i, j, k, gap, temp;
for(gap = len / 2; gap > 0; gap /= 2)
{
for(i = gap; i < len; i++) //確定執行多少次直接選擇排序
{
temp = arr[i]; //從這步開始簡化了方法一中的j和k的循環
for(j = i - gap; j >=0 && temp < arr[j]; j -= gap)
{
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
}
}
四.歸併排序
思想:
- 利用遞歸與分治的思想
- 把長度爲n的輸入序列分成兩個長度爲n/2的子序列;
- 對這兩個子序列分別採用歸併排序,直到劃分爲長度爲1的子序列
- 將每兩個相鄰的長度爲1的子序列進行歸併,得到n/2(向上取整)個長度爲2或者1的有序子序列,再將其兩兩歸併,反覆執行此過程,直到得到一個有序序列。
- 需要額外存儲空間(之後的幾個應該都需要)
void Merge(int* arr, int left, int mid,int right)
{
int low = left; //左半部分起始位置
int hight = mid + 1; //右半部分起始位置
int length = right - left + 1;
vector<int> temp(length);//輔助數組
int index = 0; // 輔助數組的下標
while (low <= mid && hight <= right)
{ //挑選兩部分中最小的元素放入輔助數組中
if (arr[low] < arr[hight])
temp[index++] = arr[low++];
else
temp[index++] = arr[hight++];
}
//如果還有剩餘,直接放入到輔助數組中
while (low <= mid)
temp[index++] = arr[low++];
while (hight <= right)
temp[index++] = arr[hight++];
//更新原始數組元素
for (int i = 0; i < length; i++)
{
arr[left + i] = temp[i];
}
}
void MergeSort(int* arr, int left, int right)
{
if (left < right)
{
int mid = (left + right) / 2; //分割序列
MergeSort(arr, left, mid); //對序列左半部分進行歸併排序
MergeSort(arr, mid + 1, right); //對序列右半部分進行歸併排序
Merge(arr, left, mid, right); //合併已經有序的兩個序列
}
}
五.桶排序
思想:
- .設置一個定量的數組當作空桶
- 將待排序元素劃分到不同的桶。先掃描一遍序列求出最大值 maxV 和最小值 minV ,設桶的個數爲 k ,則把區間 [minV, maxV] 均勻劃分成 k 個區間,每個區間就是一個桶。將序列中的元素分配到各自的桶。(分成不同桶就好,至於規則自己定)
- 對每個桶內的元素進行排序。可以選擇任意一種排序算法。
- 將各個桶中的元素合併成一個大的有序序列。
- 桶排序的時間複雜度就可以近似認爲是 O(n) 的。即桶越多,時間效率就越高,而桶越多,空間就越大。
六.基數排序
思想:
- 有點像桶排序
- 現根據個位大小,都是1的放一個桶,都是2的放一個桶,(桶內不用排序?按先後進就好?)然後把他們從前到後按順序放進數組
- 再根據十位大小,百位,依次進行
七.計數排序
思想:
- 最大值是k,最小數是m,就分k - m + 1個桶,每個桶裏計算重複出現的數據的個數,然後根據這個頻率新建一個數組
不過計數排序只能用在數據範圍不大的情況,如果數據範圍 k 比要排序的數據 n 大很多,就不適合用計數排序了。而且計數排序只能給非負整數排序,如果要排序的數據是其他類型的,要講其在不改變相對大小的情況下,轉化爲非負整數。
八.查找方式
- 順序查找
- 二分查找
- 差值查找