1、簡單的選擇排序n^2
void simple( vector<int> &a)
{
for (auto it = a.begin(); it != a.end(); it++)
{
for (auto it1 = it+1; it1 != a.end(); it1++)
{
if (*it1<*it)
{
int a = *it;
*it = *it1;
*it1 = a;
}
}
}
}
2、冒泡n^2
void bubbleSort(vector<int> &a)
{
for (auto it = a.begin(); it != a.end()-1; it++)
{
for (auto it1 = a.begin(); it1 <a.begin()+( a.end()-it)-1; it1++)
{
vector<int>::iterator it2 = it1+1;
if (*it2<*it1)
{
int a = *it1;
*it1 = *it2;
*it2 = a;
}
}
}
}
3、插入排序n^2
void insertSort(vector<int> &a)
{
for (auto it = a.begin()+1; it != a.end(); it++)
{
while (a.begin()<it)
{
vector<int>::iterator it2 = it - 1;
if (*it2>*it)
{
int a = *it;
*it = *it2;
*it2 = a;
}
else
{
break;
}
--it;
}
}
}
4、快速排序N*logN
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //將中間的這個數和第一個數交換 參見注1
int i = l, j = r, x = s[l];
while (i < j)
{
while (i < j && s[j] >= x) // 從右向左找第一個小於x的數
j--;
if (i < j)
s[i++] = s[j];
while (i < j && s[i] < x) // 從左向右找第一個大於等於x的數
i++;
if (i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 遞歸調用
quick_sort(s, i + 1, r);
}
}
5、堆排序n*LOGn
堆排序
堆排序是利用堆這種數據結構而設計的一種排序算法,堆排序是一種選擇排序,它的最壞,最好,平均時間複雜度均爲O(nlogn),它也是不穩定排序。首先簡單瞭解下堆結構。
堆
堆是具有以下性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱爲大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱爲小頂堆。如下圖:
同時,我們對堆中的結點按層進行編號,將這種邏輯結構映射到數組中就是下面這個樣子
該數組從邏輯上講就是一個堆結構,我們用簡單的公式來描述一下堆的定義就是:
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
ok,瞭解了這些定義。接下來,我們來看看堆排序的基本思想及基本步驟:
堆排序基本思想及步驟
堆排序的基本思想是:將待排序序列構造成一個大頂堆,此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就爲最大值。然後將剩餘n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值。如此反覆執行,便能得到一個有序序列了
步驟一 構造初始堆。將給定無序序列構造成一個大頂堆(一般升序採用大頂堆,降序採用小頂堆)。
a.假設給定無序序列結構如下
2.此時我們從最後一個非葉子結點開始(葉結點自然不用調整,第一個非葉子結點 arr.length/2-1=5/2-1=1,也就是下面的6結點),從左至右,從下至上進行調整。
4.找到第二個非葉節點4,由於[4,9,8]中9元素最大,4和9交換。
這時,交換導致了子根[4,5,6]結構混亂,繼續調整,[4,5,6]中6最大,交換4和6。
此時,我們就將一個無需序列構造成了一個大頂堆。
步驟二 將堆頂元素與末尾元素進行交換,使末尾元素最大。然後繼續調整堆,再將堆頂元素與末尾元素交換,得到第二大元素。如此反覆進行交換、重建、交換。
a.將堆頂元素9和末尾元素4進行交換
b.重新調整結構,使其繼續滿足堆定義
c.再將堆頂元素8與末尾元素5進行交換,得到第二大元素8.
後續過程,繼續進行調整,交換,如此反覆進行,最終使得整個序列有序
再簡單總結下堆排序的基本思路:
a.將無需序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;
b.將堆頂元素與末尾元素交換,將最大元素"沉"到數組末端;
c.重新調整結構,使其滿足堆定義,然後繼續交換堆頂元素與當前末尾元素,反覆執行調整+交換步驟,直到整個序列有序。
常用排序算法的時間複雜度和空間複雜度表格
1.選擇排序
思想:每次找一個最小值。
-
#include <iostream>
-
using namespace std;
-
//從小到大排序
-
void SelectSort(int a[], int n)
-
{
-
int index, temp;
-
for (int i = 0; i < n - 1; i++) //執行(n-1) 次
-
{
-
index = i;
-
for (int j = i + 1; j<n; j++)//執行(n-1)次 每個a[i]都要與a[i+1]至a[n-1]做比較
-
{
-
if (a[index] > a[j]) //記錄序列中最小值的位置
-
{
-
index = j;
-
}
-
}
-
if (index != i) //如果無序序列中第一個記錄不是最小值,則進行交換
-
{
-
temp = a[index];
-
a[index] = a[i];
-
a[i] = temp;
-
}
-
}
-
}
-
int main()
-
{
-
int a[10], i, n = 10, num = 10;
-
for (i = 0; i < n; i++)
-
a[i] = num--;
-
cout << "原序列:\n";
-
for (i = 0; i < n; i++)
-
cout << a[i] << " ";
-
SelectSort(a, n); cout << "排序後:\n"; for (i = 0; i < n; i++)cout << a[i] << " ";
-
return 0;
-
}
-
//優化排序
-
//如果在每一次查找最小值的時候,也可以找到一個最大值,然後將兩者分別放在它們應該出現的位置,這樣遍歷的次數就比較少了,下邊
-
//給出代碼實現:
-
void SelectSort2(int a[], int n)
-
{
-
int left = 0; int right = n - 1; int min = left;//存儲最小值的下標
-
int max = left;//存儲最大值的下標
-
while (left <= right)
-
{
-
min = left;
-
max = left;
-
for (int i = left; i <= right; ++i)
-
{
-
if (a[i] < a[min])
-
min = i;
if (a[i] > a[max])
-
max = i;
-
}
-
swap(a[left], a[min]);
-
if (left == max)
-
max = min;
-
swap(a[right], a[max]);
-
++left;
-
--right;
-
}
-
}
-
//遞歸版
-
void RecursiveSelectSort(int a[], int start, int end)
-
{
-
if (start < end)
-
{
-
int temp = a[start];
-
int index = start;
-
for (int i = start + 1; i < end; i++)
-
{
-
if (a[index] > a[i])
-
{
-
index = i;
-
}
-
}
-
if (start != index)
-
{
-
temp = a[start];
-
a[start] = a[index];
-
a[index] = temp;
-
}
-
start++;
-
RecursiveSelectSort(a, start, end);
-
}
-
}
2.堆排序
思想:一是建立堆,二是堆頂與堆的最後一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,
二是反覆調用滲透函數實現排序的函數。
-
void swap(int *a, int *b)
-
{
-
int tmp = *a;
-
*a = *b;
-
*b = tmp;
-
}
-
void HeapAdjust(int *a, int i, int size) //調整堆
-
{
-
int lchild = 2 * i; //i的左孩子節點序號
-
int rchild = 2 * i + 1; //i的右孩子節點序號
-
int max = i; //臨時變量
-
if (i <= size / 2) //如果i不是葉節點就不用進行調整
-
{
-
if (lchild <= size&&a[lchild] > a[max])
-
{
-
max = lchild;
-
}
-
if (rchild <= size&&a[rchild] > a[max])
-
{
-
max = rchild;
-
}
-
if (max != i)
-
{
-
swap(a[i], a[max]);
-
HeapAdjust(a, max, size); //避免調整之後以max爲父節點的子樹不是堆
-
}
-
}
-
}
-
void BuildHeap(int *a, int size) //建立堆
-
{
-
int i;
-
for (i = size / 2; i >= 1; i--) //非葉節點最大序號值爲size/2
-
{
-
HeapAdjust(a, i, size);
-
}
-
}
-
void HeapSort(int *a, int size) //堆排序
-
{
-
int i;
-
BuildHeap(a, size);
-
for (i = size; i >= 1; i--)
-
{
-
swap(a[1], a[i]); //交換堆頂和最後一個元素,即每次將剩餘元素中的最大者放到最後面
-
HeapAdjust(a, 1, i - 1); //重新調整堆頂節點成爲大頂堆
-
}
-
}
3.冒泡排序
思想:通過兩兩交換,像水中的泡泡一樣,小的先冒出來,大的後冒出來,每一次都有一個相對的最大值沉底。
-
void BubbleSort(int a[], int n)
-
{
-
int temp;
-
for (int i = 0; i < n - 1; i++) //執行(n-1)次 每一次冒泡 都有一個最大值沉底
-
{
-
for (int j = 0; j< n - i - 1; j++) //執行(n-1)次 冒泡的次數 決定冒泡的位置
-
{
-
if (a[j]>a[j + 1])
-
{
-
temp = a[j];
-
a[j] = a[j + 1];
-
a[j + 1] = temp;
-
}
-
}
-
}
-
}
-
//改進的冒泡排序
-
//最佳運行時間:O(n)
-
//最壞運行時間:O(n^2)
-
void BubbleSort2(int a[], int n)
-
{
-
int temp,flag = 0;
-
for (int i = 0; i < n - 1; i++) //每一次冒泡 都有一個最大值沉底
-
{
-
for (int j = 0; j<n - i - 1; j++) //冒泡的次數 決定冒泡的位置
-
{
-
if (a[j]>a[j + 1])
-
{
-
flag = 1;
-
temp = a[j];
-
a[j] = a[j + 1];
-
a[j + 1] = temp;
-
}
-
}
-
if (flag == 0)
-
break; //沒有數據交換 已經排好序了
-
}
-
}
-
//改進2傳統冒泡排序中每一趟排序操作只能找到一個最大值或最小值,我們考慮利用在每趟排序中進行正向和反向兩遍冒泡的方法一次
-
//可以得到兩個最終值(最大者和最小者) , 從而使排序趟數幾乎減少了一半。
-
void Bubble_2(int a[], int n)
-
{
-
int low = 0;
-
int high = n - 1; //設置變量的初始值
-
int tmp, j;
-
while (low < high)
-
{
-
for (j = low; j< high; ++j) //正向冒泡,找到最大者
-
if (a[j]> a[j + 1])
-
{
-
tmp = a[j];
-
a[j] = a[j + 1];
-
a[j + 1] = tmp;
-
}
-
--high; //修改high值, 前移一位
-
for (j = high; j>low; --j) //反向冒泡,找到最小者
-
if (a[j] < a[j - 1])
-
{
-
tmp = a[j];
-
a[j] = a[j - 1];
-
a[j - 1] = tmp;
-
}
-
++low; //修改low值,後移一位
-
}
-
}
-
//遞歸冒泡排序
-
void RecursiveBubbleSort(int a[], int start, int end)
-
{
-
if (start < end) //循環結束條件一爲start == end
-
{
-
int temp = 0;
-
int length = end - start + 1;
-
for (int i = start; i < length - 1; i++) //循環結束條件二爲i < length - 1;
-
{
-
if (a[i] < a[i + 1])
-
{
-
temp = a[i];
-
a[i] = a[i + 1];
-
a[i + 1] = temp;
-
}
-
}
-
end--;
-
RecursiveBubbleSort(a, start, end);
-
}
-
}
4.快速排序
思想:選擇一個基準元素,通常選擇第一個元素或者最後一個元素,通過一趟排序講待排序的記錄分割成獨立的兩部分,其中一部分記錄的元素值均比基準元素值小。另一部分記錄的 元素值比基準值大。用同樣的方法繼續進行排序,直到整個序列有序。
-
void Swap(int *p1, int *p2)
-
{
-
int temp = *p1;
-
*p1 = *p2;
-
*p2 = temp;
-
}
-
void QuickSort(int *arr, int ileft, int iright, int length)
-
{
-
int i = ileft;//從左邊開始循環
-
int j = iright + 1;//從右邊開始循環
-
if (i < j)
-
{
-
do
-
{
-
do
-
{
-
i++;
-
} while (arr[i] <= arr[ileft] && i <= iright);
-
do
-
{
-
j--;
-
} while (arr[j] >= arr[ileft] && j > ileft);
-
if (i < j)
-
{
-
Swap(&arr[i], &arr[j]);
-
}
-
} while (i < j);
-
Swap(&arr[ileft], &arr[j]);
-
QuickSort(arr, ileft, j - 1, 0);
-
QuickSort(arr, j + 1, iright, 0);
-
}
-
}
-
//改進快排
-
void Swap(int *p1, int *p2)
-
{
-
int temp = *p1;
-
*p1 = *p2;
-
*p2 = temp;
-
}
-
int partition(int a[], int low, int high)
-
{
-
int privotKey = a[low]; //基準元素
-
while (low < high){ //從表的兩端交替地向中間掃描
-
while (low < high && a[high] >= privotKey)
-
--high; //從high 所指位置向前搜索,至多到low+1 位置。將比基準元素小的交換到低端
-
Swap(&a[low], &a[high]);
-
while (low < high && a[low] <= privotKey)
-
++low;
-
Swap(&a[low], &a[high]);
-
}
-
return low;
-
}
-
void qsort_improve(int r[], int low, int high, int k)
-
{
-
if (high - low > k)
-
{ //長度大於k時遞歸, k爲指定的數
-
int pivot = partition(r, low, high); // 調用的Partition算法保持不變
-
qsort_improve(r, low, pivot - 1, k);
-
qsort_improve(r, pivot + 1, high, k);
-
}
-
}
-
void quickSort(int r[], int n, int k)
-
{
-
qsort_improve(r, 0, n, k);//先調用改進算法Qsort使之基本有序
-
//再用插入排序對基本有序序列排序
-
for (int i = 1; i <= n; i++){
-
int tmp = r[i];
-
int j = i - 1;
-
while (tmp < r[j])
-
{
-
r[j + 1] = r[j];
-
j = j - 1;
-
}
-
r[j + 1] = tmp;
-
}
-
}
5.插入排序
思想:假設待排序的記錄存放在數組R[1..n]中。初始時,R[1]自成1個有序區,無序區爲R[2..n]。從i=2起直至i=n爲止,依次將R[i]插入當前的有序區R[1..i-1]中,生成含n個記錄的有序區。
-
void InsertSort(int a[], int n)
-
{
-
for (int i = 2; i <=n; i++) //外循環(n-1)次
-
{
-
int j = i - 1; // 從下標爲1開始
-
a[0] = a[i]; //每個數都要與a[0](相當於key)比較
-
while (a[0]<a[j] && j >0) //比a[0]大,則替換
-
{
-
a[j + 1] = a[j];
-
j--; //向前移動一位,再進行比較
-
}
-
a[j + 1] = a[0];
-
}
-
}
-
int main()
-
{
-
int a[20], i, n =10;
-
int num = 10;
-
for (i = 1; i <=10; i++)
-
a[i] = num--;
-
cout << "原序列:\n";
-
for (i = 1; i <=n; i++)
-
cout << a[i] << " ";
-
cout << endl;
-
InsertSort(a, 10);
-
cout << "排序後:\n";
-
for (i = 1; i <=n; i++)
-
cout << a[i] << " ";
-
return 0;
-
}
6.希爾排序
思想:先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分成d1個組。所有距離爲d1的倍數的記錄放///在同一個組中。先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有記錄放在同一組中進行直接插入排序爲止。
-
void ShellSort(int a[], int n)
-
{
-
int d = n / 2;
-
while (d >= 1)
-
{
-
for (int i = 2 + d; i <= n; i++)
-
{
-
int j = i - d;
-
a[0] = a[i];
-
while (j > 0 && a[0] < a[j])
-
{
-
a[j + d] = a[j];
-
j = j - d;
-
}
-
a[j + d] = a[0];
-
}
-
d = d / 2;
-
}
-
}
-
int main()
-
{
-
int a[20], i, n =10;
-
int num = 10;
-
for (i = 1; i <=10; i++)
-
a[i] = num--;
-
cout << "原序列:\n";
-
for (i = 1; i <=n; i++)
-
cout << a[i] << " ";
-
cout << endl;
-
ShellSort(a, 10);
-
cout << "排序後:\n";
-
for (i = 1; i <=n; i++)
-
cout << a[i] << " ";
-
return 0;
-
}
7.歸併排序法
思想:將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每個子序列是有序的。然後再把有序子序列合併爲整體有序序列。
-
//將r[i…m]和r[m +1 …n]歸併到輔助數組b[i…n]
-
void Merge(int *a, int *b, int i, int m, int n)
-
{
-
int j, k;
-
for (j = m + 1, k = i; i <= m && j <= n; ++k)
-
{
-
if (a[j] < a[i])
-
b[k] = a[j++];
-
else
-
b[k] = a[i++];
-
}
-
while (i <= m)
-
b[k++] = a[i++];
-
while (j <= n)
-
b[k++] = a[j++];
-
}
-
void MergeSort(int *a, int *b, int lenght)
-
{
-
int len = 1;
-
int *q = a;
-
int *tmp;
-
while (len < lenght)
-
{
-
int s = len;
-
len = 2 * s;
-
int i = 0;
-
while (i + len < lenght)
-
{
-
Merge(q, b, i, i + s - 1, i + len - 1); //對等長的兩個子表合併
-
i = i + len;
-
}
-
if (i + s < lenght)
-
Merge(q, b, i, i + s - 1, lenght - 1); //對不等長的兩個子表合併
-
tmp = q; q = b; b = tmp; //交換q,b,以保證下一趟歸併時,仍從q 歸併到b
-
}
-
}
-
int main()
-
{
-
int a[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
-
int b[10];
-
cout << "原序列:\n";
-
for (int i = 0; i < 10; i++)
-
cout << a[i] << " ";
-
cout << endl;
-
MergeSort(a, b, 10);
-
cout << "排序後:\n";
-
for (int i = 0; i < 10; i++)
-
cout << a[i] << " ";
-
return 0;
-
}
-
//兩路歸併
-
//思想設兩個有序的子文件(相當於輸入堆)放在同一向量中相鄰的位置上:R[low..m],R[m+1..high],先將它們合併到
-
//一個局部的暫存向量R1(相當於輸出堆)中,待合併完成後將R1複製回R[low..high]中。
-
//merge two subArray,one is A[i1]~A[j1],another is A[i2]~A[j2]
-
void MergeTwoArray(int A[], int i1, int j1, int i2, int j2)
-
{
-
int *tmp = new int[j2 - i1 + 1];
-
int i = i1, j = i2, k = 0;
-
while (i <= j1 && j <= j2)
-
{
-
//add samller one into tmp arrary
-
if (A[i] <= A[j])
-
{
-
tmp[k++] = A[i++];
-
}
-
else
-
{
-
tmp[k++] = A[j++];
-
}
-
}
-
while (i <= j1)
-
tmp[k++] = A[i++];
-
while (j <= j2)
-
tmp[k++] = A[j++];
-
for (i = 0; i < k; i++)
-
{
-
A[i1++] = tmp[i];
-
}
-
delete[]tmp;
-
}
-
void MergeSort(int A[], int n)
-
{
-
int i1, j1, i2, j2 = 0;
-
int size = 1;
-
while (size < n)
-
{
-
i1 = 0;
-
while (i1 + size < n)//存在兩個序列,那就需要合併
-
{
-
//確定兩個序列的邊界
-
j1 = i1 + size - 1;
-
i2 = i1 + size;
-
if (i2 + size - 1 > n - 1)
-
{
-
j2 = n - 1;
-
}
-
else
-
j2 = i2 + size - 1;
-
MergeTwoArray(A, i1, j1, i2, j2);
-
//更新i1
-
i1 = j2 + 1;
-
}
-
size *= 2;
-
}
-
}
-
int main()
-
{
-
int a[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
-
cout << "原序列:\n";
-
for (int i = 0; i < 10; i++)
-
cout << a[i] << " ";
-
cout << endl;
-
MergeSort(a, 10);
-
cout << "排序後:\n";
-
for (int i = 0; i < 10; i++)
-
cout << a[i] << " ";
-
return 0;
-
}
8.(桶)基數排序
思想:基數排序是通過“分配”和“收集”過程來實現排序。
[cpp] view plain copy
- <code class="language-cpp">int getdigit(int x, int d)
- {
- int a[] = { 1, 1, 10 }; //因爲待排數據最大數據也只是兩位數,所以在此只需要到十位就滿足
- return ((x / a[d]) % 10); //確定桶號
- }
- void msdradix_sort(int arr[], int begin, int end, int d)
- {
- const int radix = 10;
- int count[radix], i, j;
- //置空
- for (i = 0; i < radix; ++i)
- {
- count[i] = 0;
- }
- //分配桶存儲空間
- int *bucket = (int *)malloc((end - begin + 1) * sizeof(int));
- //統計各桶需要裝的元素的個數
- for (i = begin; i <= end; ++i)
- {
- count[getdigit(arr[i], d)]++;
- }
- //求出桶的邊界索引,count[i]值爲第i個桶的右邊界索引+1
- for (i = 1; i < radix; ++i)
- {
- count[i] = count[i] + count[i - 1];
- }
- //這裏要從右向左掃描,保證排序穩定性
- for (i = end; i >= begin; --i)
- {
- j = getdigit(arr[i], d); //求出關鍵碼的第d位的數字, 例如:576的第3位是5
- bucket[count[j] - 1] = arr[i]; //放入對應的桶中,count[j]-1是第j個桶的右邊界索引
- --count[j]; //第j個桶放下一個元素的位置(右邊界索引+1)
- }
- //注意:此時count[i]爲第i個桶左邊界
- //從各個桶中收集數據
- for (i = begin, j = 0; i <= end; ++i, ++j)
- {
- arr[i] = bucket[j];
- }
- //釋放存儲空間
- free(bucket);
- //對各桶中數據進行再排序
- for (i = 0; i < radix; i++)
- {
- int p1 = begin + count[i]; //第i個桶的左邊界
- int p2 = begin + count[i + 1] - 1; //第i個桶的右邊界
- if (p1 < p2 && d > 1)
- {
- msdradix_sort(arr, p1, p2, d - 1); //對第i個桶遞歸調用,進行基數排序,數位降 1
- }
- }
- }
- int main()
- {
- int a[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
- cout << "原序列:\n";
- for (int i = 0; i < 10; i++)
- cout << a[i] << " ";
- cout << endl;
- msdradix_sort(a, 0, 10 - 1, 2);
- cout << "排序後:\n";
- for (int i = 0; i < 10; i++)
- cout << a[i] << " ";
- return 0;
- }</code>