1. 程式人生 > >第4節、排序實戰

第4節、排序實戰

算法基礎 排序 習題 實戰

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 &lt;stdio.h&gt; 
    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節、排序實戰