本篇簡單梳理了下常見的七種排序算法:冒泡排序、簡單選擇排序、直接插入排序、希爾排序、堆排序、歸併排序、快速排序,以圖例形式,解釋如何進行排序。
該圖爲幾種排序算法的複雜度、穩定性等。
1. 冒泡排序
- 思路:將鍵值較大的記錄向序列的尾部移動,鍵值較小的記錄向序列的前部移動。
void BubbleSort(int* a, int n) //冒泡排序
{
int end = n - 1;
while (end > 0)
{
for (int i = 0; i < end; ++i)
{
if (a[i] > a[i + 1])
Swap(&a[i], &a[i + 1]);
}
--end;
}
}
2. 簡單選擇排序
- 思路:每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數據元素排完。
void SelectSort(int* a, int n) //選擇排序
{
int begin = 0;
int end = n - 1;
int max_index, min_index; //index->指針
while (begin <= end)
{
min_index = max_index = begin;
for (int i = begin + 1; i < end; ++i)
{
if (a[i] > a[max_index])
max_index = i; //將大的元素地址給與max,方可經行下一輪比較
if (a[i] < a[min_index])
min_index = i;
Swap(&a[i], &a[max_index]); //&
if (max_index == begin) //當最大值在開始時,需要將最小地址交換給最大地址,否則最大值的位置會有衝突
{
max_index = min_index;
}
Swap(&a[i], &a[min_index]); //&
}
--end;
++begin;
}
}
3. 直接插入排序
- 思路:當插入第i(i>=1)個元素時,前面的array[0],array[1],…,array[i-1]已經排好序,此時用array[i]的排序碼與array[i-1],array[i-2],…的排序碼順序進行比較,找到插入位置即將array[i]插入,原來位置上的元素順序後移。
void InsertSort(int* a, int n) //直接插入排序
{
//思路:在已排好的序列中插入數據,大於的數往後移動覆蓋,插入的數要大於前一個或者小於後一個(插入a[0]不用比較前一個)
//插入的數據的位置爲a[0]-a[n-1],最多比較到n-2的位置
//時間 O(n*n):最好O(n)、最壞O(n*n), 空間 O(1)、穩定
for (int i = 0; i < n - 1; ++i)
{
int end = i;
int tmp = a[end + 1]; //要插入的數
while (end >= 0) //=0 往原序列的頭插入
{
if (a[end] > tmp) //小往前插
{
a[end + 1] = a[end]; //覆蓋數
--end;
}
else
{
break;
}
}
a[end + 1] = tmp; //前面已排好序,直接插入最後一個w位置
}
}
4. 希爾排序
- 思路:設置一個gap,gap的數值代表數據之間間隔gap,然後預排序,預排序完成後基本接近於排序,再進行一次插入排序。
void ShellSort(int* a, int n) //哈希排序
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1; //設置間距,+1確保進行插入排序 gap = 1 表示直接插入排序
for (int i = 0; i < n - gap; ++i) //當i走到n-1-gap進行最後比較數據交換
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + gap] = a[end];
end = end - gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
5. 堆排序
- 排升序要建大堆,排降序建小堆。
void AdujustDown(int* a, int n, int parent) //向下調整
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child] < a[child + 1]) //選擇小的孩子
++child;
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]); //交換數據
parent = child;
child = parent * 2 + 1; //迭代
}
else
{
break;
}
}
}
void HeapSort(int* a, int n) //堆排序
{
for (int i = (n - 2) / 2; i >= 0; --i) //建堆
{
AdujustDown(a, n, i);
}
int end = n - 1; //堆排序
while (end > 0)
{
Swap(&a[0], &a[end]); //*已建立大堆,將a[0]與a[end]交換,再經行堆調整
AdujustDown(a, end, 0);
--end;
}
}
6. 歸併排序
- 將已有序的子序列合併,得到完全有序的序列,即先使每個子序列有序,再使子序列段間有序;若將兩個有序表合併成一個有序表,稱爲二路歸併。
void _MergeSort(int* a, int left, int right, int* tmp)
{
//分解
if (left == right) //子問題只左右兩個數
return;
int mid = left + (right - left) / 2;
_MergeSort(a, left, mid, tmp); //區間劃分[left, mid] 、[mid+1, right]
_MergeSort(a, mid + 1, right, tmp);
//合併
//左右之間有序,歸併
int begin1 = left; int end1 = mid;
int begin2 = mid + 1; int end2 = right;
int i = left; //tmp
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
//拷回原數組中,開始將數據均放於臨時數組中
memcpy(a + left, tmp + left, sizeof(int)*(i - left)); //原、目標、大小(right-left+1)但針對每個小區間
}
void MergeSort(int* a, int n) //歸併排序
{
int* tmp = (int*)malloc(sizeof(int)*n); //合併時,先把數放到tmp數組中,經排序後再覆蓋到原數組,直到最後一趟完成即排序完成
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
7. 快速排序
- 思路:任取待排序元素序列中的某元素作爲基準值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小於基準值,右子序列中所有元素均大於基準值,然後最左右子序列重複該過程,直到所有元素都排列在相應位置上爲止。
int OneSort(int* a, int left, int right)
{
int key = a[right];
int key_index = right;
while (left < right)
{
while (left < right && a[left] <= key) //a[left] <= key,進入循環++,大於時出來與右指針所找的數經行交換
++left;
while (left < right && a[right] >= key)
--right;
Swap(&a[left], &a[right]);
if (left < right)
{
Swap(&a[left], &a[right]);
++left;
--right;
}
}
Swap(&a[left], &a[key_index]); //left = right時,選取右key,則a[left] 與 key所指的數值交換
return left;
}
void QuickSort1(int* a, int left, int right) //快速排序
{
//思路:左右指針法,在最左或最右選取一個key
//左指針向左找大於key所對應的數,右指針找小於key所對應的數值,找到左右指針對應的數值交換
//右邊作key,先走左,後走右;左邊爲key,先走右,後走左;指針相遇時,left與右key交換、right與左key交換
if (left >= right) //判斷左右區間大小
return;
int key_index = OneSort1(a, left, right); //區間分爲三部分:[left, key_index-1] key [key_index + 1, right]
QuickSort1(a, left, key_index-1);
QuickSort1(a, key_index + 1, right);
}
【Yang】
- 哪裏有問題,希望大家提出來,我會及時改正 o( ̄▽ ̄)ブ 。