距上一次熱血澎湃看算法已經過去兩年了,如果不是看到馬老師最近開始講算法了估計還會繼續遺忘下去。
先備份一張圖,來源:http://www.mashibing.com(歡迎大家訪問馬老師網站)
這次來學習選擇排序,顧名思義,選擇排序當然是選擇爲主。舉例說明。
5 6 1 4 3
↑
選擇這幾個數中的最小(大)的數:1,放到最前(後)面即和首(尾)位交換
1 6 5 4 3
↑
然後再除了1之後選擇最小的數:3,放到第二的位置。
1 3 5 4 6
↑
循環下去,最後得到1 3 4 5 6的數組即可。
用代碼實現:
//選擇排序
void selectSort(int num[],int len)
{
int i = 0,j = 0;
int minIndex = 0;
for(i = 0;i < len;i++)
{
//假設首部數值爲最小值
minIndex = i;
for(j = i + 1;j < len;j++)
{
if(num[j] < num[minIndex])
{
//找到最小的值
minIndex = j;
}
}
//把最小的值和首部進行交換
int temp = num[i];
num[i] = num[minIndex];
num[minIndex] = temp;
}
}
非常簡單易懂的算法,但是由馬老師的圖可知該算法是不穩定的,什麼叫做不穩定呢?就是兩個相等的值,在排序之後位置可能發生變化,讓我們寫個例程測試一下
struct student
{
int age;
char name[20];
};
void main()
{
int i = 0,j = 0,minIndex = 0;
//注意在初始化的時候,有兩個結構體的age都是4,但是test4 first位置在 test4 last前面
struct student all[] = {
{4,"test4 first"},
{3,"test3"},
{4,"test4 last"},
{2,"test2"},
{1,"test1"},
};
for(i = 0;i < 5;i++)
{
minIndex = i;
for(j = i + 1;j < 5;j++)
{
if(all[j].age < all[minIndex].age)
{
minIndex = j;
}
}
struct student temp = all[minIndex];
all[minIndex] = all[i];
all[i] = temp;
}
for(i = 0;i < 5;i++)
{
printf("%s,",all[i].name);
}
printf("\n");
}
運行結果
test1,test2,test3,test4 last,test4 first
很明顯,test4 first和test4 last的位置反了,這就是不穩定造成的結果,舉個例子,A現在銀行存了5萬,B也在銀行存了5萬,理論上查詢存5萬的第一人應該是A,但是經過這個排序算法之後查詢到的卻是B,後面的問題可想而知。
優化:馬老師提示其中一種優化方法可以是一次找到最大值和最小值,把最小值放到前面,最大值放到後面不就把循環次數縮小一半了嗎。
用代碼實現:
//優化選擇排序
void selectSortFindMinAndMax(int num[],int len)
{
int i = 0,j = 0,k = 0,m = 0;
int minIndex = 0,maxIndex = 0;
for(i = 0;i < len / 2;i++)
{
minIndex = i;
maxIndex = i;
for(j = i + 1;j < len - k;j++)
{
if(num[j] < num[minIndex])
{
//找到最小值
minIndex = j;
}
if(num[j] > num[maxIndex])
{
//找到最大值
maxIndex = j;
}
}
//考慮如果最大值就是i下標,如果先交換最小值,則會把最大值位置的數值改變
//這種情況應該先交換最大值。
if((maxIndex == i) && (minIndex != len - k - 1))
{
swap(num,len - k - 1,maxIndex);
swap(num,i,minIndex);
}
//考慮如果最大值是i下標,最小值是循環尾,則只用交換一次即可。
else if((maxIndex == i) && (minIndex == len - k - 1))
{
swap(num,maxIndex,minIndex);
}
//其餘情況先交換最小值,再交換最大值
else
{
swap(num,i,minIndex);
swap(num,len - k - 1,maxIndex);
}
//循環一次之後,循環尾向前移
k++;
}
}
兩種方法的循環次數以及時間比較(數組大小10000,有rand()隨機產生):
系統函數0.001000ms,普通選擇0.111000ms循環49995000次,優化選擇0.083000ms循環25000000次
系統函數0.002000ms,普通選擇0.115000ms循環49995000次,優化選擇0.071000ms循環25000000次
系統函數0.002000ms,普通選擇0.112000ms循環49995000次,優化選擇0.077000ms循環25000000次
系統函數0.002000ms,普通選擇0.106000ms循環49995000次,優化選擇0.081000ms循環25000000次
系統函數0.001000ms,普通選擇0.111000ms循環49995000次,優化選擇0.077000ms循環25000000次
系統函數0.001000ms,普通選擇0.119000ms循環49995000次,優化選擇0.093000ms循環25000000次
系統函數0.002000ms,普通選擇0.113000ms循環49995000次,優化選擇0.088000ms循環25000000次
系統函數0.001000ms,普通選擇0.110000ms循環49995000次,優化選擇0.074000ms循環25000000次
系統函數0.001000ms,普通選擇0.112000ms循環49995000次,優化選擇0.075000ms循環25000000次
系統函數0.001000ms,普通選擇0.114000ms循環49995000次,優化選擇0.076000ms循環25000000次