常見排序總結
/*
選擇排序
每一趟選取第i個關鍵字,與剩下的n-i關鍵字進行比較,找出最大的值與剛開始選擇的i下標對應的值交換
第一趟: 0 , 1~n-1
第二趟:1, 2~n-1
.....
應用場景
最好最壞情況時間複雜度都是O(n^2) 空間複雜度為O(1)
最壞情況:對相對有序的一組資料排序
不穩定 由其交換引起的
適用於數量不大並且交換次數少的情況下
*/
void Select_Sort(int *arr,int len) { if(arr == NULL || len < 0) return ; int i,j; int minIndex; for(i = 0;i < len -1 ;++i) { minIndex = i; for( j = i+1 ;j < len - 1;++j ) { if(arr[j] < arr[minIndex]) { minIndex = j; } } if(i != minIndex) { int tmp = arr[minIndex]; arr[minIndex] = arr[i]; arr[i] = tmp; } } }
/*
氣泡排序
每趟兩個相鄰的資料比較,如果前者大於後者就交換位置;每趟最後一個元素都是本趟元素裡最大的那個
所以在每趟的比較中,上一趟的最後一個元素不再參與排序
本程式碼是升序排列
給出的數字序列也是相對於升序 那麼交換次數少 最好情況下時間複雜度O(n)
給出的數字序列是完全降序 那麼交換次數duo 最壞情況下時間複雜度O(n^2)
*/
void Bulle_Sort(int *arr,int len) { if(arr == NULL || len < 0) return ; int i ,j; for( i = 0 ; i < len-1 ;++i) { for(j = 0;j < len -i-1 ;++j) { if(arr[j] > arr[j+1]) { int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } }
/*
直接插入排序
每趟選定一個標兵位,標兵位開始往前遍歷 找出第一個比標兵位小的位置
那麼這個位置的後面就是該標兵存放的位置
應用場景
給出的數字序列基本有序 最好情況下 時間複雜度O(n)
給出的數字基本無序 最壞情況下 時間複雜度O(n^2)
空間複雜度為O(1)
如果有少量或者大量資料都可以採用
*/
void Insert_Sort(int *arr,int len) { if(arr == NULL || len < 0) return ; int tmp = 0; int j; for(int i = 1;i < len - 1; i++) { tmp = arr[i]; for(j = i-1;j >= 0;j--) { if(arr[j] > tmp) { arr[j+1] = arr[j]; } else { break; } } arr[j+1] = tmp; } }
/*
歸併排序 採用二路歸併法
開闢另一個與舊陣列空間大小的陣列
兩個有序的歸併段 依次從頭開始比較 小的先放入到新開闢的陣列中 在繼續往後比較歸併 直至歸併為一個歸併段
每一次歸併歸併段的增量為上一次的二倍
時間複雜度 一次歸併O(logn),整體歸併時間複雜度O(nlogn)
空間複雜度 O(n)
*/
void Merge(int *arr,int len ,int gap)
{
int low1 = 0;
int high1 = low1+gap-1;
int low2 = high1+1;
int high2 = low2+gap-1 < len ? low2+gap-1:len-1;
int *brr = new int[len];
if(brr == NULL) return ;
int i = 0;
while(low2 < len)//保證第二個歸併段至少有一個數字
{
while(low1 <= high1 && low2 <= high2)
{
if(arr[low1]<=arr[low2])
{
brr[i++] = arr[low1++];
}
else
{
brr[i++] = arr[low2++];
}
}
//兩個歸併段比完之後還有可能某個歸併段剩餘資料
while(low1 <= high1)
{
brr[i++] = arr[low1++];
}
while(low2 <= high2)
{
brr[i++] = arr[low2++];
}
low1 = high2+1;
high1 = low1+gap-1;
low2 = high1+1;
high2 = low2+gap-1<len?low2+gap-1:len-1;
}
while(low1 < len)//不足兩個歸併段
{
brr[i++] = arr[low1++];
}
for(i = 0 ;i < len ;++i)
{
arr[i] = brr[i];
}
delete []brr;
}
void Merge_Sort(int *arr,int len)
{
if(arr == NULL ||len < 0)
return ;
for(int i = 1;i < len ;i = i*2)
{
Merge(arr,len,i);
}
}
/*
堆排序
將陣列中的值虛擬的建立成一個根堆
一次調整堆從上往下調整 時間複雜度為O(logn)
第一次建堆從下往上 建堆時間複雜度為O(n*lon)
堆排序的時間複雜度為 O(n*lon)
空間複雜度為O(1)
應用場景
在不要求完全有序的情況下得到資料中最大或者最小的部分資料 可以選擇堆排序
*/
void Adjust(int *arr,int start,int end)
{
//將開始的值儲存在臨時量中
int tmp = arr[start];
for(int i = 2*start+1;i < end ;i = 2*start+1)
{
//i+1 < end保證了有右孩子的存在 每次還需將i的左右孩子進行比較 i每次的落腳點都是左孩子處
if((i+1<=end) && arr[i] < arr[i+1])
{
i++;//i下標始終對應左右孩子中的最大值
}
//判斷父節點的值與左右孩子中最大值的大小關係 如果小於最大值 則兩個值進行交換
//交換後需要比較i所在節點它的左右孩子與其當前值的大小關係
if( tmp < arr[i])
{
arr[start] = arr[i];
start = i;
}
else
{
//左右孩子值都小於父節點的值
break;
}
}
arr[start] = tmp;
}
void Heap_Sort(int *arr,int len)
{
if(arr == NULL || len < 0)
return ;
int i;
//len-1是最後一個元素 根據最後一個元素下標推其父親節點下標 i-1/2 所以本題中即為len-1-1/2
for( i = (len-1-1)/2;i>=0;--i)
{
Adjust(arr,i,len-1);
}
int tmp = 0;
for( i = 0;i < len -1 ;++i)
{
tmp = arr[0];
arr[0] = arr[len-1-i];
arr[len-1-i] = tmp;
Adjust(arr,0,len-1-i-1);
//由於最後一個已經是有序了的 再一次調整時他就不參與了
//即就是每一次的交換後最後一個元素都不參與調整過程
}
}