排序演算法程式碼詳解
#include <iostream>
using namespace std;
void print(int a[], int len)
{
for(int i = 0; i < len; i++)
cout << a[i] << " ";
cout << endl;
}
void swap(int &a, int &b)
{
if( a > b)
{
int t = a;
a = b;
b = t;
}
}
void bubble_sort(int a[], int len)
{
//氣泡排序
int exchange = 0; //設定標記
for(int i = 0; i < len - 1; i++) //總共有 n-1 次排序
{
exchange = 0; //每次把標記置 0
for(int j = len - 2; j >= i; j--) //從後往前比較
{
if(a[j] > a[j+1]) //比較大小,交換資料
{
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
exchange = 1; //發生資料交換,標記置1
}
}
if(exchange != 1) //標記不為1,跳過這次迴圈
break;
}
}
void select_sort(int a[], int len)
{
//簡單選擇排序,每次找到最小的那個數和第一個數交換位置
int i, j, tmp, min;
for(i = 0; i < len -1 ;i++) // n-1 次 迴圈
{
tmp = a[i]; //記錄每次第一個元素的值和下標
min = i;
for(j = i+1; j < len; j++)
{
if(a[j] < tmp) //如果當前位置的值小於第一個元素的值
{
tmp = a[j]; //把當前元素的值和下標記錄下來
min = j;
}
}
if(min != i) //如果flag標記發生變化,表示有元素的值比第一個數小
{
a[min] = a[i]; //交換兩個數的值
a[i] = tmp;
}
}
}
void insert_sort(int a[], int len)
{
//直接插入排序:把第一個數當成一個有序陣列,後面的數一個一個有序的插入的陣列中
int i, j, tmp;
for(i = 1; i < len; i++)
{
tmp = a[i]; //tmp用來記錄要插入的數的值
//滿足條件,往後挪。當 tmp > a[j] 小,tmp 放在 a[j] 後面
for(j = i-1; j >= 0 && tmp < a[j]; j--)
a[j+1] = a[j];
a[j+1] = tmp;
}
}
void shell_sort(int a[], int len)
{
int d = len;
while(d > 1)
{
d = (d + 1)/2; //設定增量
for(int i = 0; i < len -d; i++)
{
if(a[i] > a[i+d]) //交換相隔距離 d 的兩個數的值
{
int tmp = a[i];
a[i] = a[i+d];
a[i+d] = tmp;
}
}
}
}
void heap_adjust(int a[], int len, int index)
{
int left = 2*index + 1; //父節點左孩子下標
int right = 2*index + 2; //父節點有孩子下標
int maxindex = index; //令最大值的下標為當前父節點下標
if(left < len && a[left] > a[maxindex]) //在陣列範圍內且左孩子的值大於父節點的值
maxindex = left; //最大值下標變為左孩子下標
if(right < len && a[right] > a[maxindex]) //在陣列範圍內且右孩子的值大於父節點的值
maxindex = right; //最大值下標變為右孩子下標
if(maxindex != index) //當最大值下標更新
{
swap(a[maxindex], a[index]); //交換最大值下標對應的值和父節點的值
heap_adjust(a, len, maxindex); //遞迴調整其他不滿足堆性質的部分
}
}
void heap_sort(int a[], int len)
{
int i; //最後一個非葉子節點的下標為 len/2 -1
for(i = len/2 -1; i >= 0; i--) //對每一個父節點進行調整,使堆變成大頂堆(從最後一個父節點開始) 非葉子結點即父節點
{
heap_adjust(a, len, i); // i 為當前父節點下標
}
for(i = len -1; i > 0; i--) //從後往前,儲存最大的數
{
swap(a[0], a[i]); //把經過調整的大頂堆的最大值和最後一個元素互換位置,放到陣列末尾
heap_adjust(a, i, 0); //將剩下的部分繼續進行堆排序
}
}
void merge(int a[], int begin, int middle, int end)
{
int i, j, k, leftlen, rightlen;
leftlen = middle - begin + 1; //左區間元素個數
rightlen = end - middle; //右區間元素個數
int *L = new int(leftlen); //申請兩個陣列用來分別存放 左區間 和 右區間 的元素
int *R = new int(rightlen);
for(i = 0, k = begin; i < leftlen; i++, k++) //對 陣列 L 進行賦值
{
L[i] = a[k];
}
for(i = 0, k = middle + 1; i < rightlen; i++, k++) //對 陣列 R 進行賦值
{
R[i] = a[k];
}
for(k = begin, i = 0, j = 0; i < leftlen && j < rightlen; k++) //對原陣列進行重新排序
{
if(L[i] < R[j]) //左區間當前下標的值小於右區間的值
{
a[k] = L[i]; //陣列當前下標元素的值為左區間的值
i++; //左區間下標加1
}
else //反之
{
a[k] = R[j]; //陣列當前下標元素的值為右區間的值
j++; //右區間下標加1
}
}
if(i < leftlen) //這兩個 if 語句 是將 左區間 和 右區間 歸併剩下的資料 放到陣列的最後面
{
for(j = i; j < leftlen; j++, k++) // j = i 屬於習慣問題 可以寫成 for (; i < n1; i++, k++)
{
a[k] = L[j];
}
}
if(j < rightlen)
{
for(i = j; i < rightlen; i++, k++) // i = j 屬於習慣問題 可以寫成 for (; j < n2; j++, k++)
{
a[k] = R[i];
}
}
delete [] L;
delete [] R;
}
void merge_sort(int a[], int begin, int end)
{
if(begin < end)
{
int middle = (begin + end) / 2; //定義分界點,將陣列分為兩部分
merge_sort(a, begin, middle); //對左區間 [begin, middle] 遞迴做歸併排序
merge_sort(a, middle + 1, end); //對右區間 [middle + 1, end] 遞迴做歸併排序
merge(a, begin, middle, end); //組合,將兩個有序的區塊合併成一個有序的區塊
}
}
void quick_sort(int a[], int begin, int end)
{
if(begin < end)
{
int pivot = a[begin]; //將陣列第一個數作為比較關鍵字,儲存下來
int i = begin; //從前往後
int j = end; //從後往前
while(i < j)
{
while(i < j && a[j] >= pivot) // i < j 且 a[j] 大於等於 關鍵字 時
j--; // j--, 下標向前挪一位
if(i < j && a[j] < pivot) // i < j 且 a[j] 小於 關鍵字 時
a[i++] = a[j]; // 令 a[i] = a[j] a++ 下標向後挪一位
while(i < j && a[i] <= pivot) // i < j 且 a[i] 小於等於 關鍵字 時
i++; // i++, 下標向後挪一位
if(i < j && a[i] > pivot) // i < j 且 a[i] 大於 關鍵字 時
a[j--] = a[i]; // 令 a[j] = a[i] j-- 下標向前挪一位
}
a[i] = pivot;
quick_sort(a, begin, i - 1);
quick_sort(a, i+1, end);
}
}
int main()
{
int a[] = {7,1,3,2,5,6,4,9,8};
int len = sizeof(a)/sizeof(a[0]);
cout << "排序前" << endl;
print(a, len);
// 氣泡排序:兩輛比較關鍵字,反序則交換
// cout << "氣泡排序" << endl;
// bubble_sort(a, len);
// print(a, len);
// 簡單選擇排序:通過(n - i)次關鍵字間的比較, 從(n - i - 1)個記錄中選擇關鍵字最小的記錄,並和第 i 個記錄交換位置
// cout << "選擇排序" << endl;
// select_sort(a, len);
// print(a, len);
// 直接插入排序:將第一個數視為有序陣列,把後面每一個數都有序插入到陣列中,使整個序列有序
// cout << "直接插入排序" << endl;
// insert_sort(a, len);
// print(a, len);
// 希爾排序:把相距某一個增量 d( d = len; d = (d+1)/ 2;) 的記錄組成子序列,然後在子序列內進行直接插入排序,再對全體記錄進行一次直接插入排序
// cout << "Shell排序" << endl;
// shell_sort(a, len);
// print(a, len);
// 堆排序:將待排序的序列構造成一個大頂堆(完全二叉樹,根節點最大),將根節點和堆末尾元素交換,然後將剩餘的元素重新構造堆,再選出最大值,反覆執行
// cout << "堆排序" << endl;
// heap_sort(a, len);
// print(a, len);
// 將序列拆分成多個單個數組,再兩兩歸併,得到有序的多個子序列,反覆執行直至只有一個有序的序列為止
// cout << "歸併排序" << endl;
// merge_sort(a, 0, len -1);
// print(a, len);
//快速排序:在一堆待排序記錄中選擇關鍵字,將序列分為兩組,一組比關鍵字都小,另一組比關鍵字都大,遞迴執行,可得到一個有序序列
cout << "快速排序" << endl;
quick_sort(a, 0, len - 1);
print(a, len);
return 0;
}