第4節、排序實戰
阿新 • • 發佈:2018-05-20
算法基礎 排序 習題 實戰 1、為什麽要實戰?
曾經有人問我,你寫代碼又快又好,是怎麽學的呀?
其實我的水平也才一般般的啦,之所以能把程序寫出來,只有多練習。
看別人的算法,很快就看完了,但往往看完就忘記了,只有自己親自寫一遍,調試運行一邊,才能夠掌握。
在練習幾道題,舉一反三,才算是掌握,才能夠實際應用。
剛開始的時候,面對題目編程,大多數人都是心有余而力不足。
那些熟練的“碼農”們,也只有非常努力,才能看起來毫不費力,最初的過程都是痛苦、緩慢的提升,慢慢走下去,才能成為合格的程序員!
2、試題一《折疊繩子》
題目:
給定一段一段的繩子,你需要把它們串成一條繩。每次串連的時候,是把兩段繩子對折,這樣得到的繩子又被當成是另一段繩子,可以再次對折去跟另一段繩子串連。每次串連後,原來兩段繩子的長度就會減半。
給定N段繩子的長度,繩子必須全部用上,你需要找出它們能串成的繩子的最大長度。
輸入:每個測試用例第1行給出正整數N (2 <= N <= 10000);第2行給出N個正整數,即原始繩段的長度,數字間以空格分隔。
輸出:
在一行中輸出能夠串成的繩子的最大長度。結果向下取整,即取為不超過最大長度的最近整數。
在看解析之前,開動腦筋,應該怎麽做呢?
解析:
最開始的繩子折疊的次數越多,所以應該先折疊短的繩子,之後折疊長的:
首先對繩子進行排序,之後按照要去折疊就可以了。
- 讀入數據
- 排序
- 折疊
- 輸出結果
參考代碼:
/* 輸入: 8 6 5 3 1 8 7 2 4 輸出: 1 2 3 4 5 6 7 8 */ #include <stdio.h> using namespace std; int a[100001]; //快速排序 void quicksort(int left, int right) { int i, j, temp;
//終止條件 if (left>right) return; temp = a[(left + right) / 2]; i = left; j = right; //從左到右找到比基準數大的,從右到左找到比基準數小的,交換位置,直至兩個過程相遇即全部比較完成 while (i != j) { while (a[j] >= temp && i<j) j--; while (a[i] <= temp && i<j) i++; if (i < j) { int l = a[i]; a[i] = a[j]; a[j] = l; } } //將基準數的位置放在大小分割的中間 a[(left + right) / 2] = a[i]; a[i] = temp; //左邊遞歸過程 quicksort(left, i - 1); //右邊遞歸過程 quicksort(i + 1, right);
}
int main() {
int n, length = 0;
scanf("%d", &n);
//讀入數據
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
//快速排序
quicksort(1, n);
//折疊,最開始將總長度設置為最短的一根,也就是a[1],之後折疊
length = a[1];
for (int i = 2; i <= n; i++) {
length = (a[i] + length) / 2;
}
//輸出結果
printf("%d ", length);
getchar(); getchar();
return 0;
}
### 3、試題二《判斷主元》
《折疊繩子》的題目是不是很有趣?程序的思路跟上一節的快速排序基本一致,只是修改了main主程序裏的幾行代碼。
下面,再寫一道題練練手!
#### 題目:
著名的快速排序算法裏有一個經典的劃分過程:我們通常采用某種方法取一個元素作為主元(基準數),通過交換,把比主元小的元素放到它的左邊,比主元大的元素放到它的右邊。
給定劃分後的N個互不相同的正整數的排列,請問有多少個元素可能是劃分前選取的主元?
>例如給定N = 5, 排列是1、3、2、4、5,則:
1的左邊沒有元素,右邊的元素都比它大,所以它可能是主元;
盡管3的左邊元素都比它小,但是它右邊的2它小,所以它不能是主元;
盡管2的右邊元素都比它大,但其左邊的3比它大,所以它不能是主元;
類似原因,4和5都可能是主元。
因此,有3個元素可能是主元。
**輸入:**
輸入在第1行中給出一個正整數N(<= 1000); 第2行是空格分隔的N個不同的正整數,每個數不超過100000。
**輸出:**
輸出有可能是主元的元素個數。
在看解析之前,開動腦筋,應該怎麽做呢?
#### 解析:
對於每個數字,分別判斷左邊的數字是不是比它小,右邊的數字是不是比它大,就可以了。
這種暴力求解的算法思路很清晰,但面對大量數據的情況下,運行時間就不容樂觀,所以必須有更好的解決方案。
考慮到主元的性質,它左邊的數字都比它小,它右邊的都比它大,這跟排序完成的順序是一樣的!
![輸入順序與排序](https://upload-images.jianshu.io/upload_images/1314054-2dd023d0cd68677d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
根據這個性質,可以快速求解。
>- 讀入數據,因為需要比較輸入順序和輸出順序,保存兩份
>- 排序
>- 比較重復
>- 輸出結果
#### 參考代碼:
**註意:**為了節省篇幅, quicksort()函數沒有寫進去,自己運行過程中,需要補全。
```c
void quicksort(int left, int right) {}
/*
輸入:
5
1 3 2 4 5
輸出:
3
*/
#include <stdio.h>
using namespace std;
int a[100000], b[100000], v[100000];
//快速排序
void quicksort(int left, int right) {}
int main() {
int n, max = 0, cnt = 0;
//讀入數據
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
//排序
quicksort(1, n);
//比較輸入順序與排序後的順序
for (int i = 0; i < n; i++) {
if (a[i] == b[i] && b[i] > max)
v[cnt++] = b[i];
if (b[i] > max)
max = b[i];
}
//輸出結果
printf("%d\n", cnt);
getchar(); getchar();
return 0;
}
4、結尾
常見的排序方法基本講完了,下一章將開啟新的內容,是不是有些期待?
第4節、排序實戰