C++(數據結構與算法)

一、分而治之的思想
分而治之方法與軟件設計的模塊化方法非常相似
分而治之通常不用於解決問題的小實例,而要解決一個問題的大實例。一般步驟爲:
①把一個大實例分爲兩個或多個更小的實例
②分別解決每個小實例
③把這些小實例的解組合成原始大實例的解
二、實際應用之找出假幣
問題描述
一個袋子有16個硬幣,其中只有一個是假幣,這個假幣比其他的真幣重量輕(其他所有真幣的重量都是相同的),現在要找出這個假幣
普通的解決方法
一般的方法就是逐個的進行比較,一旦遇到質量比較輕的就找到假幣了
步驟爲:
①比較硬幣1與硬幣2,如果某個硬幣比較輕,那麼這個輕的硬幣就是假幣,終止尋找;否則進行下一步
②繼續比較硬幣2與硬幣3,如果某個硬幣比較輕,那麼這個輕的硬幣就是假幣,終止尋找;否則進行下一步
......以此類推,直到找到假幣終止尋找
分而治之的解決方法
分而治之的思想是把一個問題的大實例分解爲兩個或更多的小實例,然後進行比較
此處我們將大實例分解爲兩個小實例,步驟爲:
①把16個硬幣分爲兩組A和B,每組8個硬幣,計算每組硬幣的總質量並進行比較,較輕的那一組肯定含有假幣
②將較輕的那一組繼續分組,分爲A和B,每組4個硬幣,然後計算每組硬幣的總質量並進行比較,同理,較輕的那一組肯定含有假幣
③將較輕的那一組繼續分組,分爲A和B,每組2個硬幣,然後計算每組硬幣的總質量並進行比較,同理,較輕的那一組肯定含有假幣
④最終只剩下兩個硬幣,因此不需要再進行分組了,直接比較,較輕的那一個硬幣肯定是假幣
三、實際應用之金塊問題
問題描述
一個老闆有一袋金塊,每塊金塊的重量都不同,現在想要找出最重的那個金塊與最輕的那個金塊
普通的解決方法
普通的解決辦法就是逐個比較,找出最重和最輕的金塊
步驟一般爲:
假設金塊的總數爲n
先逐個比較每個金塊,找出最重的金塊
找出最重的金塊之後,從剩餘的n-1個金塊中再找出最輕的金塊
比較次數:因爲找出最重的金塊的比較次數爲n-1次,從剩餘的金塊中再找出最輕的金塊用了n-2次,所以總的比較次數爲2n-3

template<typename T>
bool find_max_min(T arr[], int n,int &maxIndex,int &minIndex)
{
    if (n <= 0)
        return false;
 
    //先找出最大的
    maxIndex = 0;
    for (int i = 1; i < n; ++i) {
        if (arr[maxIndex] < arr[i])
            maxIndex = i;
    }
 
    //再從剩餘的當中找出最小的
    minIndex = 0;
    for (int j = 1; j < n; j++) {
        if (j == maxIndex)
            continue;
        if (arr[minIndex] > arr[j])
            minIndex = j;
    }
    
    return true;
}
 
 
int main()
{
    int arr[] = { 1,4,2,5,1,8,5 };
    int maxIndex, minIndex;
 
    if (find_max_min(arr, sizeof(arr) / sizeof(int), maxIndex, minIndex)) {
        std::cout << "max:" << arr[maxIndex] << endl;
        std::cout << "min:" << arr[minIndex] << endl;
    }
 
    return 0;
}

 

分而治之的解決方法
當n<=2時,一次比較就足夠了
當n>2時,總體步驟如下:
①將金塊分爲A和B兩個部分
②分別找出A和B中最重的和最輕的,設A中最重和最輕的金塊分別爲Ha和La,A中最重和最輕的金塊分別爲Hb和Lb(這一步可以使用遞歸來實現)
③比較Ha與Hb就可以找出最重的,比較La與Lb就可以找出最輕的
演示案例:
①假設n=8,有8塊金塊
②將8個金塊分爲兩個部分A和B,各有4個金塊
③因爲A中有4個金塊,我們將其再分爲兩個部分A1和A2,每個部分有2個金塊
然後通過一次比較可以找出A1中較重的金塊Ha1和La1
再通過一次比較可以找出A2中較輕的金塊Ha2和La2
然後再通過一次比較Ha1與Ha2找出A中最重的金塊Ha,通過一次比較La1與La2找出A中最輕的金塊La
④因爲B中有4個金塊,我們將其再分爲兩個部分B1和B2,每個部分有2個金塊
然後通過一次比較可以找出B1中較重的金塊Hb1和Lb1
再通過一次比較可以找出B2中較輕的金塊Hb2和Lb2
然後再通過一次比較Hb1與Hb2找出A中最重的金塊Hb,通過一次比較Lb1與Lb2找出A中最輕的金塊Lb
⑤最後進行一次比較Ha和Hb找出最重的金塊,再進行一次比較La和Lb找出最輕的金塊。步驟結束
可以看出在上面的分而治之中總共需要比較10次

設c(n)爲所需要的比較次數。爲了方便,假設n是2的冪:
如果是分而治之法:當n=2時,c(n)=1;對於較大的n,c(n)=2c(n/2)+2
如果是逐個比較方法:c(n)=3n/2-2
因此,使用分而治之方法比逐個比較的方法少用了25%的比較次數
分而治之編碼實現

如果使用遞歸,則步驟如下:
①在上圖的二叉樹中,沿着根至葉子的路徑,把一個大實例劃分成爲若干個大小爲1或2的小實例
②在每個大小爲2的實例中,比較確定哪一個較重和哪一個較輕。在節點D、E、F、G完成這種比較。大小爲1的實例只有一個金塊,它既是最輕的也是最重的
③對較輕的金塊進行比較以確定哪一個最輕,對較重的金塊進行比較以確定安一個最重。對節點A、B、C執行這種比較
如果使用非遞歸的方法,代碼如下:
複雜度分析:當n爲偶數時,在for循環外部又一次比較,內部有3(n/2-1)次比較。總的比較次數爲3n/2。當n爲奇數時,在for循環外部沒有比較,內部有n(n-1)/2次比較。因此,無論n爲奇數或偶數,當n>0時,總的比較次數爲[3n/2]-2。這是在最早最大值最小值的算法中,比較次數最少的算法

template<typename T>
bool find_max_min(T arr[], int n,int &maxIndex,int &minIndex)
{
    //如果數組大小小於等於0,直接退出
    if (n <= 0)
        return false;
    //如果只有一個金塊,那麼它既是最重的也是最輕的
    if (n == 1) {
        maxIndex = minIndex = 0;
        return true;
    }
    //如果金塊數量大於等於2,開始下面的部分
 
    int s = 0;//用於標識比較的開始起點
    if (n % 2 == 1) {
        //如果金塊數量爲奇數,設定最大索引與最小索引爲0,然後從s=1索引處開始進行比較
        maxIndex = minIndex = 0;
        s = 1;
    }
    else {
        //如果金塊數量爲偶數,從前兩個元素中提取出較小元素與較大元素的索引,然後從s=2索引處開始比較
        if (arr[0] > arr[1]) {
            maxIndex = 0;
            minIndex = 1;
        }
        else {
            maxIndex = 1;
            minIndex = 0;
        }
        s = 2;
    }
 
    //從s開始進行比較,每兩個元素比較一次
    for (int index = s; index < n; index += 2) {
        if (arr[index] > arr[index +1]) {
            if (arr[index] > arr[maxIndex])
                maxIndex = index;
            if (arr[index + 1] < arr[minIndex])
                minIndex = index + 1;
        }
        else {
            if (arr[index] < arr[minIndex])
                 minIndex = index;
            if (arr[index + 1] > arr[maxIndex])
                maxIndex = index + 1;
        }
    }
 
    return true;
}
 
 
int main()
{
    int arr[] = { 1,4,2,5,0,1,8,3,8 };
    int maxIndex, minIndex;
 
    if (find_max_min(arr, sizeof(arr) / sizeof(int), maxIndex, minIndex)) {
        std::cout << "max:" << arr[maxIndex] << endl;
        std::cout << "min:" << arr[minIndex] << endl;
    }
 
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章