數據結構算法淺談

1.貪心算法

(1)基本要素

貪心選擇
貪心選擇是指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。這是貪心算法可行的第一個基本要素,也是貪心算法與動態規劃算法的主要區別。貪心選擇是採用從頂向下、以迭代的方法做出相繼選擇,每做一次貪心選擇就將所求問題簡化爲一個規模更小的子問題。對於一個具體問題,要確定它是否具有貪心選擇的性質,我們必須證明每一步所作的貪心選擇最終能得到問題的最優解。通常可以首先證明問題的一個整體最優解,是從貪心選擇開始的,而且作了貪心選擇後,原問題簡化爲一個規模更小的類似子問題。然後,用數學歸納法證明,通過每一步貪心選擇,最終可得到問題的一個整體最優解。
最優子結構
當一個問題的最優解包含其子問題的最優解時,稱此問題具有最優子結構性質。運用貪心策略在每一次轉化時都取得了最優解。問題的最優子結構性質是該問題可用貪心算法或動態規劃算法求解的關鍵特徵。貪心算法的每一次操作都對結果產生直接影響,而動態規劃則不是。貪心算法對每個子問題的解決方案都做出選擇,不能回退;動態規劃則會根據以前的選擇結果對當前進行選擇,有回退功能。動態規劃主要運用於二維或三維問題,而貪心一般是一維問題。

(2)基本思路

貪心算法的基本思路是從問題的某一個初始解出發一步一步地進行,根據某個優化測度,每一步都要確保能獲得局部最優解。每一步只考慮一個數據,他的選取應該滿足局部優化的條件。若下一個數據和部分最優解連在一起不再是可行解時,就不把該數據添加到部分解中,直到把所有數據枚舉完,或者不能再添加算法停止。
過程:
1.建立數學模型來描述問題;
2.把求解的問題分成若干個子問題;
3.對每一子問題求解,得到子問題的局部最優解;
4.把子問題的解局部最優解合成原來解問題的一個解。

(3)算法特性

貪婪算法可解決的問題通常大部分都有如下的特性:
1.隨着算法的進行,將積累起其它兩個集合:一個包含已經被考慮過並被選出的候選對象,另一個包含已經被考慮過但被丟棄的候選對象。
2.有一個函數來檢查一個候選對象的集合是否提供了問題的解答。該函數不考慮此時的解決方法是否最優。
3.還有一個函數檢查是否一個候選對象的集合是可行的,也即是否可能往該集合上添加更多的候選對象以獲得一個解。和上一個函數一樣,此時不考慮解決方法的最優性。
4.選擇函數可以指出哪一個剩餘的候選對象最有希望構成問題的解。
5.最後,目標函數給出解的值。
6.爲了解決問題,需要尋找一個構成解的候選對象集合,它可以優化目標函數,貪婪算法一步一步的進行。起初,算法選出的候選對象的集合爲空。接下來的每一步中,根據選擇函數,算法從剩餘候選對象中選出最有希望構成解的對象。如果集合中加上該對象後不可行,那麼該對象就被丟棄並不再考慮;否則就加到集合裏。每一次都擴充集合,並檢查該集合是否構成解。如果貪婪算法正確工作,那麼找到的第一個解通常是最優的。

(4)例題分析

揹包問題

有一個揹包,揹包容量是M=150kg。有7個物品,物品不可以分割成任意大小。要求儘可能讓裝入揹包中的物品總價值最大,但不能超過總容量。
物品 A B C D E F G
重量 35kg 30kg 6kg 50kg 40kg 10kg 25kg
價值 10$ 40$ 30$ 50$ 35$ 40$ 30$

C++代碼示例:

#defian N 8
void fds(int x, int y, int count)
{
       int   i,tx,ty;
       if(count > N*N)
       {
            output_solution();
            return;
       }
       for(i = 0; i < 8; i++)
       {
             tx = hn[i].x;
             ty = hn[i],y;
             s[tx][ty] = count;
             fds(tx, ty, count+1);
             s[tx][ty] = 0;
       }
}

2.窮舉算法

(1)基本概述

廣度優先搜索

廣度優先搜索(Breadth- First- Search)也稱爲寬度優先搜索,它是一種按”先產生的節點先擴展”的原則進行的搜索。搜索的過程是:從初始節點A開始,逐層地對節點進行擴展並考察它是否爲目標節點,在第n層節點沒有全部擴展並考察之前,不對第n十1層節點進行擴展。
廣度搜索是逐層進行的。它把起始節點放到OPEN中(如果該起始節點爲一目標節點,則求得一個解答);如果OPEN表是個空表,則沒有解,失敗退出;否則繼續;把第一個節點(節點n)從OPEN表移出,並把它放入CLOSED擴展節點表中;擴展節點n如果沒有後繼節點,則轉回;把n的所有後繼節點放到OPEN表的末端,並提供從這些後繼節點回到n指針;如果n的任一個後繼節點是個目標節點,則找到解,成功退出;否則轉回。
廣度優先搜索這種策略是完備的,即如果問題的解存在,用它則一定能找到解,且找到的解還是最優解(即最短的路徑),但它的缺點是搜索效率低。

深度優先搜索

深度優先搜索(Depth- first- Search)亦稱爲縱向搜索,它是從樹根開始一枝一枝逐漸生成,是一種後生成的節點先擴展的搜索方法。首先,擴展最深的節點的結果使得搜索沿着狀態空間某條單一的路徑從起始節點向下進行;只有當搜索到一個沒有後裔的狀態時,它才考慮另一條替代的路徑(替代路徑與前面已經試過的路徑不同之處僅僅在於改變最後n步,而且保持n儘可能小)。
深度優先搜索所遵循的搜索策略是儘可能”深”地搜索圖,它把起始節點放到未擴展節點OPEN表中,如果此節點爲一目標節點,則得到一個解;如果OPEN爲一空表,則失敗退出;把第一個節點(節點n)從OPEN表移到。,OSED表;如果節點n的深度等於最大深度,則轉回;擴展節點n,產生其全部後裔,並把它們放入OPEN表的前頭,如果沒有後裔,則轉回;如果後繼節點中有任一個爲目標節點,則求得一個解,成功退出;否則轉回。深度優先搜索策略是不完備的,帶有一定的冒險性,並且應用此策略得到的解不一定是最優解(最短路徑)。

有界深度優先搜索

對於許多複雜問題,其狀態空間搜索樹的深度可能爲無限深,或者可能至少要比某個可接受的解答序列的己知深度上限還要深。爲了這種情況,常給出一個節點擴展的最大深度——深度界限,即在深度優先策略中引入深度限制,稱之爲有界深度優先搜索。當從初始節點出發沿某一分枝擴展到限制深度,但還沒有找到目標時,就不能再繼續向下擴展,而只能改變方向繼續搜索。若在限度內沒有找到問題的解,且CLOSED表中仍有待擴展的節點,就將這些節點送回OPEN表,同時增大深度限制。

一致代價搜索

在許多實際問題中,狀態空間搜索樹中的各個邊的代價不是完全相同的,爲此,需要在搜索樹中考慮每條邊的代價,根據”代價最小”的原則,優先選用最小代價的搜索路徑。寬度優先搜索可被推廣用來解決尋找從起始狀態至目標狀態的具有最小代價的路徑問題,這種推廣了的寬度優先搜索算法稱爲一致代價搜索算法。

(2)例題分析

將A、B、C、D、E、F這六個變量排成如圖所示的三角形,這六個變量分別取[1,6]上的整數,且均不相同。求使三角形三條邊上的變量之和相等的全部解。如圖就是一個解。
程序引入變量a、b、c、d、e、f,並讓它們分別順序取1至6的整數,在它們互不相同的條件下,測試由它們排成的如圖所示的三角形三條邊上的變量之和是否相等,如相等即爲一種滿足要求的排列,把它們輸出。當這些變量取盡所有的組合後,程序就可得到全部可能的解。細節見下面的程序。

C代碼示例:

#include &lt;stdio.h&gt;
void main()
{
     int a,b,c,d,e,f;
     for(a = 1; a&lt = 6; a++)
     for(b = 1; b&lt = 6; b++)
     {
          if(b==a) continue;
          for(c = 1; c&lt = 6; c++)
          {
                 if(c==a)||(c==b)continue;
                 for(d == 1; d&lt = 6; d++)
                 {
                      if(d = a)||(d==b)||(d==c)continue;
                      for(e == 1; e&lt = 6; e++)
                      {
                          if(e==a)||(e==b)||(e==c)||(e==d)continue;
                          f=21-(a+b+c+d+e+);
                          if((a+b+c=c+d+e)&amp;&amp;(a+b+c=e+))
                          {
                                printf("%6d", a);
                                printf("%4d%4d", b,f);
                                printf("%2d%4d%4d", c,d,e);
                                scanf(""%*c);
                                
                          }
                      }
                 }
          }
          
     }
}

3.二分查找

(1)基本概念

二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。但是,折半查找要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列

(2)查找過程

首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查找關鍵字比較,如果兩者相等,則查找成功;否則利用中間位置記錄將表分成前、後兩個子表,如果中間位置記錄的關鍵字大於查找關鍵字,則進一步查找前一子表,否則進一步查找後一子表。重複以上過程,直到找到滿足條件的記錄,使查找成功,或直到子表不存在爲止,此時查找不成功。

(3)C代碼示例

 public static int Method(int[] nums, int low, int high, int target);
 {
     while(low <= high)
     {
          int middle = (low + high) / 2;
          if(target == nums[middle]);
          {
              return middle;
          }
          else if( target > nums[middle])
          {
               low = middle+1;
          }
          else if(target < nums[middle])
          {
               high = middle -1;
          }
        }
        return -1;
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章