北航軟件工程專業考研991數據結構總結:
八、內排序
1.排序的基本概念,各種內排序方法的基本原理和特點,包括排序過程中進行的元素之間的比較次數,排序總趟數、排序穩定性以及時間複雜度與空間複雜度計算;
2.插入排序法(含折半插入排序法);
3.選擇排序法;
4.(起)泡排序法;
5.謝爾(Shell)排序法;
6.快速排序法;
7.堆積(Heap)排序法,包括堆積的定義與構造;
1、插入排序法
原理:第i趟將第 i+1 個元素插入到前面已經排序的 i 個元素序列中,並保持排序
void InsertSort(int *a, int n)
{
int i, j, temp;
for(i = 1; i < n-1; i++)
{
temp = a[i];
j = i-1;
while(j >= 0 && a[j] > temp) //依次向前比較,將大於i的數後移
a[j+1] = a[j--];
a[j+1] = temp;
}
}
最好的情況下,需要比較n-1次,即已經排序好,時間複雜度就是O(n)
最壞的情況下,需要比較你n(n-1)/2次,即逆序排列,時間複雜度爲O(n^2)
穩定排序
2、選擇排序法
原理:第i趟排序,從後面的 n-i+1 個元素中選擇一個最小的,放置到 n-i+1 個未排序元素的最前面
void SelectSort(int *a, int n)
{
int i, j, d, temp;
for(i = 0; i < n-1; i++)
{
d = i;
for(j = i + 1; j < n; j++) //從後面的元素中找到最小的元素
{
if(a[j] < a[d])
d = j;
}
if(d != i)
{
temp = a[i];
a[i] = a[d];
a[d] = temp;
}
}
}
無論好壞,都需要比較n(n-1)/2次,時間複雜度爲O(n^2)
穩定排序
3、泡排序
原理:第i趟排序,將前面的 n-i+1 個元素從第一個開始左右比較,如果左邊大於右邊,則進行互換,否則不進行換序
當沒有出現換序的時候,則排序完成,或者只有第一個元素的時候排序完成
void BubbleSort(int *a, int n)
{
int i, j, temp, flag;
flag = 1;
i = n-1;
while(i > 0 && flag == 1)
{
flag = 0;
for(j = 0; j < i; j++)
{
if(a[j] > a[j+1])
{
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = 1;
}
}
i--;
}
}
當元素已經排序好的時候,不需要交換 比較次數爲 n-1 時間複雜度O(n)
當元素沒有排序時,至少交換一個
當元素第一個爲最大,剩餘的都是排序時,交換n-1次
最壞的情況下,比較次數爲 n(n-1)/2 時間複雜度O(n^2)
適用於序列基本有序的情況下
穩定排序
*****************
前面的這3中排序簡直就是玩
搞定後面的這些才牛逼呢
*****************
4、謝爾Shell排序 縮小增量排序法
設置一個間隔d,每隔間隔d對元素進行排序
然後縮小間隔d,繼續進行排列
知道d < 1 排列完成
時間複雜度介於 nlog2n 到 n^2之間
最外層的趟數爲log2n
非穩定排序
算法:
void SelectSort(int *a, int n)
{
int i,j,d,temp, flag;
d = n;
while(d > 1)
{
d = d/2;
do
{
for(i = 0; i < n - d; i++)
{
j = i + d;
if(a[i] > a[j]
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
flag = 1;
}
}
}while{flag != 0);
}
}
5、快速排序法
原理:確定一個比較元素,讓所有大於該數的元素排列在它後面,小於它的數排列在它後面,在對長度大於1的前後子列進行遞歸運算
時間複雜度:O(nlog2n)
非穩定排序
算法步驟:
void QuickSort(int *a, int n)
{
Quick(a, 0, n-1);
}
void Quick(int *a, int s, int t)
{
int i, j, temp;
if(s < t)
{
i = s;
j = t+1;
while(1)
{
do i++;
while(!(a[s] < a[i] || i == t));
do j--;
while(!(a[s] > a[j]) || j == s);
if(i < j)
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}else{
break;
}
}
temp = a[s];
a[s] = a[j];
a[j] = temp;
Quick(a, s, j-1);
Quick(a, j+1, t);
}
}
兩邊出發 前小後大
6、堆積(Heap)排序,包括堆積的定義與構造
堆積的定義
使得一組序列滿足 ki >= k2i 並且 ki >= k(2i+1) 大頂堆積
就是一個完全二叉樹,使用順序存儲方式(數組)進行存儲
排序:
首先將一個序列構造成大頂堆積,然後,將收尾互換,再將前 n-i+1 個元素構造大頂堆積,繼續進行
問題是,如何構造大頂堆積呢?
先看一個左右序列已經是大頂堆積的,也就是一個大頂堆積首位互換之後的剩下的堆進行構造
將頂元素與子元素進行比較,如果小於最大的那個數,進行互換,再在子樹中繼續進行比較
這樣,構造一個初始大頂堆積,可以從中間的 n/2 個元素開始,遞減的調用上述方式即可
代碼:
void Adjust(int *a, int i, int n)
{
int j, temp;
j = 2 * i;
temp = a[i];
while(j <= n)
{
if(j < n && a[j] < a[j+1])
j++;
if(temp > a[j])
break;
a[j/2] = a[j]; //將子樹中較大的元素移到父樹中去
j = 2 * j;
}
a[j/2] = temp;
}
注意:上述是理論代碼,不是C語言的可執行代碼,因爲C語言中,數組從1開始,所以,j和i的關係應該是 j = 2 * i + 1; i = (j - 1)/2;
可執行的代碼:
void Adjust(int *a, int i, int n)
{
int j, temp;
j = 2 * i + 1;
temp = a[i];
while(j <= n)
{
if(j < n && a[j] < a[j+1])
j++;
if(temp > a[j])
break;
a[(j-1)/2] = a[j]; //將子樹中較大的元素移到父樹中去
j = 2 * j + 1;
}
a[(j-1)/2] = temp;
}
堆積排序主函數:
void HeapSort(int *a, int n)
{
int i = 0;
for(i = n / 2; i >= 1; i--)
{
Adjust(a, i, n);
}
for(i = n-1; i >= 1; i--)
{
int temp = a[i+1];
a[i+1] = a[1];
a[1] = temp;
Adjust(a, 1, i);
}
}
同樣,這是理論的函數,C語言中的可執行函數如下:
void HeapSort(int *a, int n)
{
int i = 0;
for(i = (n-1) / 2; i >= 0; i--)
{
Adjust(a, i, n-1);
}
for(i = n-2; i >= 0; i--)
{
int temp = a[i + 1];
a[i + 1] = a[0];
a[0] = temp;
Adjust(a, 0, i);
}
}
非穩定 nlog2n
總結一下:
插入排序法
最好的情況下,需要比較n-1次,即已經排序好,時間複雜度就是O(n)
最壞的情況下,比較次數爲n(n-1)/2,即逆序排列,時間複雜度O(n^2)
穩定的排序
選擇排序法
O(n^2) 與好壞無關
穩定排序
泡排序
已經排序的情況下不需要交換
最大的元素在首,其他排序,交換n-1次
穩定排序
適用於序列基本有序的情況下
謝爾Shell排序
需要一個間隙d,劃分子列,
介於 nlog2n 與 n^2之間
不穩定排序
循環嵌套三層
快速排序法
選擇一個元素作爲比較依據,前大後小,兩端出發
nlog2n
不穩定排序
堆積排序
構造堆積
nlog2n
不穩定排序