前言
正文
一, 選擇排序
選擇排序是排序中最簡單的排序算法,它的操作是這樣的: 首先找到數組中最小那個的元素,其次,將它和數組的第一個元素交換位置(如果第一個元素就是最小元素那麼它就和自己交換)。再次,剩下的元素中找到最小的元素,將它與數組的第二個元素交換位置。如此往復,直到將整個數組排序。它在不斷地選擇剩餘元素之中的最小者。
命題A: 對於長度爲N的數組,選擇排序需要大約(N^2)/2次比較和N次交換
證明: 可以通過身份的排序軌跡來證明這一點。我們用一張N*N的表格來表示排序的軌跡(上圖),其中每個非紅色的數字都表示一次比較。表中大約一半的元素表示黃色的–即對角線和其上部分的元素。對角線上的每個元素都對應一次交換。看代碼我們可以更精準得到,0到N-1的任意i都會進行一次交換和N-1-i次比較,因此總共有N次交換以及(N-1)+(N-2)+(N-3)+…+2+1 = N(N-1)/2 ~(N^2)/2次比較。
總的來說,選擇排序是一種很容易理解和實現的簡單排序算法,它有兩個很鮮明的特點。
運行時間和輸入無關。爲了找出最小元素二掃描一個數組並不能爲下一遍掃描提供掃描信息。這種性質在某些情況下是缺點,因爲使用選擇排序的人可能會驚訝地發現,一個已經有序的數組或者是主鍵全部相等的數組和一個元素隨機排列的數組所用的排序時間竟然一樣長!我們將會看到,其他算法會更善於利用輸入的初始狀態。
數據移動是最小的,每次交換都會改變兩個數組元素的值,因此選擇排序用了N次交換—交換次數和數組的大小是線性關係。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void show(int * array, int len)
{
for (int i = 0; i < len; ++i)
{
printf(" %d , " , array[i]);
}
printf("\n");
}
/**
* 選擇排序
* @param array
* @param len
*/
void selection(int * array, int len)
{
for (int i = 0; i < len; ++i)
{
//最小元素的下標
int min_index = i;
for (int j = i; j < len; ++j)
{
if (array[min_index] > array[j])
{
min_index = j;
}
}
//交換元素
int temp = array[i];
array[i] = array[min_index];
array[min_index] = temp;
}
}
int main(int argc, char *argv[])
{
int array[] = { 78, 23, 56, 12, 1, 100, 23, 11, 9, 2};
int len = sizeof(array) / sizeof(int);
show(&array[0], len);
selection(&array[0], len);
show(&array[0], len);
system("pause");
return 0;
}
二,插入排序
通常人們整理牌的方法是一張一張來的,將每一張牌插入到其他已經有序的牌中的適當位置。在計算機的實現中,爲了給要插入的元素騰出空間,我們需要將其餘所有元素在插入之前都向右移動一位。這種說法叫做插入排序。
與選擇排序一樣,當前索引左邊的所有元素都是有序的,但它們的最終位置還不確定,爲了給更小的元素騰出空間,它們可能會被移動。但是當索引到達數組的右端時,數組排序就完成了。
和選擇排序不同的是,插入排序所需的時間取決於輸入中元素的初始順序。例如,對一個很大且其中元素已經有序(或接近有序)的數組進行排序將會比對隨機順序的數組或是逆序數組進行排序要快得多。
命題B: 對於隨機排列的長度爲N且主鍵不重複的數組,平均情況下插入排序需要(N2)/4次比較以及N2/4次交換。最壞情況下需要(N^2)/2次交換,最好情況下需要N-1次比較和0次交換。
證明:和命題A一樣,通過一個N*N的軌跡表可以很容易就得到交換和比較的次數。最壞情況下對角線之下所有的元素都需要移動位置,最好情況下都不需要。對於隨機排列的數組,在平均情況下每個元素都可能向後移動半個數組的長度,因此交換總數是對角線之下的元素總數的二分之一。
比較的總次數是交換的次數加上一個額外的項,該項爲N減去被插入的元素正好是已知的最小元素的次數。在最壞情況下(逆序數組),這一項相對總數可以忽略不計;在最好情況下(數組已經有序),這一項等於N-1.
/************************************************************************/
/* 插入排序 */
/************************************************************************/
void insertion(int* array, int len)
{
for (int i = 0; i < len; ++i)
{
//判斷j下標前面數據是否小於j-1的元素
for (int j = i; j > 0 && array[j] < array[j -1]; --j)
{
//交換元素
int temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
}
}
}