日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

五大常用算法学习笔记

發布時間:2023/12/10 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 五大常用算法学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一。分治算法:快速排序、歸并排序、大整數乘法、二分查找、遞歸(漢諾塔)

  • 基本概念:把一個復雜的問題分成若干個相同或相似的子問題,再把子問題分成更小的子問題… , 知道最后子問題可以簡單的直接求解,原問題的解即子問題的解的合并。
  • 看上去有點類似Fork/Join框架,或map-reduce。

    排序算法中的快速排序、歸并排序都是使用的分治算法。

  • 分治算法的適用場景:
    1)當問題規模縮小到一定的程度就可以很容易解決
    2)該問題可以分解為若干個規模較小的相同問題
    3)該問題分解出的若干子問題的解可以合并為該問題的解
    4)每個子問題都是獨立的,相互之間沒有交集。

  • 使用分治法的經典場景:
    1)二分搜索
    2)大整數乘法
    3)合并排序
    4)快速排序
    5)漢諾塔

  • 分治算法經典例題:
    輸入一組整數,求出這組數字子序列和中最大值。也就是求出最大子序列的和,不必求出最大的那個序列。例如:序列:-2, 11, -1, 13, -5, -2 , 則最大子序列的和為20。

  • public static void main(String[] args) {int[] a = { -2, 11, -4, 13, -5, -2 };// 最大子序列和為20int[] b = { -6, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2 };// 最大子序列和為16System.out.println(maxSubSum1(a));System.out.println(maxSubSum4(b)); }// 最大子序列求和算法一 public static int maxSubSum1(int[] a) {int maxSum = 0;// 從第i個開始找最大子序列和for (int i = 0; i < a.length; i++) {// 找第i到j的最大子序列和for (int j = i; j < a.length; j++) {int thisSum = 0;// 計算從第i個開始,到第j個的和thisSumfor (int k = i; k <= j; k++) {thisSum += a[k];}// 如果第i到第j個的和小于thisSum,則將thisSum賦值給maxSumif (thisSum > maxSum) {maxSum = thisSum;}}}return maxSum; }// 時間復雜度O(n的平方) public static int maxSubSum2(int[] a) {int maxSum = 0;for (int i = 0; i < a.length; i++) {// 將sumMax放在for循環外面,避免j的變化引起i到j的和sumMax要用for循環重新計算int sumMax = 0;for (int j = i; j < a.length; j++) {sumMax += a[j];if (sumMax > maxSum) {maxSum = sumMax;}}}return maxSum; }// 遞歸,分治策略 // 2分logn,for循環n,時間復雜度O(nlogn) public static int maxSubSum3(int[] a) {return maxSumRec(a, 0, a.length - 1); }public static int maxSumRec(int[] a, int left, int right) {// 遞歸中的基本情況if (left == right) {if (a[left] > 0)return a[left];elsereturn 0;}int center = (left + right) / 2;// 最大子序列在左側int maxLeftSum = maxSumRec(a, left, center);// 最大子序列在右側int maxRightSum = maxSumRec(a, center + 1, right);// 最大子序列在中間(左邊靠近中間的最大子序列+右邊靠近中間的最大子序列)int maxLeftBorderSum = 0, leftBorderSum = 0;for (int i = center; i >= left; i--) {leftBorderSum += a[i];if (leftBorderSum > maxLeftBorderSum)maxLeftBorderSum = leftBorderSum;}int maxRightBorderSum = 0, rightBorderSum = 0;for (int i = center + 1; i <= right; i++) {rightBorderSum += a[i];if (rightBorderSum > maxRightBorderSum)maxRightBorderSum = rightBorderSum;}// 返回最大子序列在左側,在右側,在中間求出的值中的最大的return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum); }public static int max3(int a, int b, int c) {return a > b ? (a > c ? a : c) : (b > c ? b : c); }// 時間復雜度:O(n); 任何a[i]為負時,均不可能作為最大子序列前綴;任何負的子序列不可能是最有子序列的前綴 public static int maxSubSum4(int[] a) {int maxSum = 0, thisSum = 0;for (int j = 0; j < a.length; j++) {thisSum += a[j];if (thisSum > maxSum)maxSum = thisSum;else if (thisSum < 0)thisSum = 0;}return maxSum; }

    二。動態規劃算法

  • 基本概念:將待求解的問題分解為若干個子問題,按順序求解子問題,前一問題的解,為后一問題的求解提供有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各種子問題,最后一個子問題就是初始問題的解。
    由于動態規劃算法解決的問題是有重疊的子問題,為了減少重復計算,對每一個子問題只解一次,將其不同階段的不同狀態保存在一個二維數組中。
  • 動態規劃算法以分治算法類似,不同在于:適合于動態規劃算法求解的問題,經分解后得到的子問題,往往不是互相獨立的,而是下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步求解的。

  • 動態規劃的適用場景
    1)最優化原理:該問題的最優解所包含的子問題的解也是最優的。
    2)無后效性:某階段狀態一旦確定,就不受這個狀態以后決策的影響。即某狀態以后的過程不會影響以前的狀態,只與當前狀態有關。
    3)有重疊子問題:即子問題之間不相互獨立,一個子問題的解在下一階段決策中可能被多次使用到。

  • 經典問題:
    給定兩個字符串str1和str2,返回兩個字符串的最長公共子序列,例如:str1=“1A2C3D4B56",str2=“B1D23CA45B6A”,"123456"和"12C4B6"都是最長公共子序列,返回哪一個都行。

  • 分析:這事經典的動態規劃問題,假設str1的長度為M, str2的長度為N, 則生成M*N的二維數組dp, dp[i][j]的含義是str1[0…i]與str2[0…j]的最長公共子序列的長度。

    dp值的求法如下:
    dp[i][j]的值比如和dp[i-1][j], dp[i][j-1]相關,結合下面的代碼來看,實際上從第一行和第一列開始計算的,

    public static void main(String[] args) {String A = "1A2C3D4B56";String B = "B1D23CA45B6A";System.out.println(findLCS(A, A.length(), B, B.length())); }public static int findLCS(String A, int Alen, String B, int Blen) {int dp[][] = new int[Alen+1][Blen+1];for (int i = 0; i < Alen; i++) {for (int j = 0; j < Blen; j++) {if (A.charAt(i) == B.charAt(j)) {dp[i + 1][j + 1] = dp[i][j] + 1;} else {dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]);}}}return dp[Alen][Blen]; }

    三。貪心算法

    貪心算法是通過局部最優解來達到全局最優解。

  • 下面是一個可以試用貪心算法解的題目,貪心解的確不錯,可惜不是最優解。

    [背包問題]有一個背包,背包容量是M=150。有7個物品,物品可以分割成任意大小。

    要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。

    物品 A B C D E F G

    重量 35 30 60 50 40 10 25

    價值 10 40 30 50 35 40 30

    分析:
    目標函數: ∑pi最大

  • 約束條件是裝入的物品總重量不超過背包容量,即∑wi<=M( M=150)
    1)根據貪心的策略,每次挑選價值最大的物品裝入背包,得到的結果是否最優呢?
    2)每次挑選重量最小的物品裝入背包,是否能得到最優解?
    3)每次選取單位重量價值最大的物品,成為解本地的策略?

    貪心算法簡單易行,但貪心算法需要證明后才能真正運用到算法中。一般來說,貪心算法的證明圍繞著整個問題的最優解一定由在貪心策略中存在的子問題的最優解得來的。

    對于本題的三種貪心策略,都無法成立,即無法被證明。解釋如下:

    1)選取價值最大者。
    反例:W=30
    物品:A B C
    重量:28 12 12
    價值:30 20 20

    根據策略,選A, 但選BC最合適

    2)選取重量最小,反例與上述類似

    3)選取單位重量價值最大的物品。反例:
    W=30
    物品:A B C
    重量:28 20 10
    價值:28 20 10

    根據策略,三種物品單位重量價值一樣,程序無法依據現有策略作出判斷,如果選擇A,則答案錯誤。

    2)貪心算法的經典案例:
    設有n個正整數,將他們連成一排,組成一個最大的多位整數。
    例如:n=3時,3個整數13,312,343,連成的最大整數為34331213。
    又如:n=4時,4個整數7,13,4,246,連成的最大整數為7424613。

    要求輸入:n, N個數
    輸出:連成的多位數

    算法分析:該題很容易想到貪心法,但解題時很多人把整數按從大到小的順序連接起來,測試題目的例子也都符合,但結果不對。例如:我們很容易找到12, 121應該組成12121而非12112, 但如12, 123呢,就是12312,而不是12123。所以,通過整數排序的方法是不對的。

    結論:該題可以用貪心算法,只是剛才采用的貪心策略不對。正確的貪心算法的標準是:先把整數轉成字符串,然后再比較a+b和b+a, 如果a+b >= b+a, 就把a排在b前面, 反之把a排在b后面。

    四。回溯算法

  • 基本概念:回溯算法是一個類似枚舉的搜索嘗試過程,主要在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。深度優先。
  • 回溯算法是一種選優搜索法,按選優條件向前搜索,以達到目標。當搜索到某一步時,發現原先選擇并不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術成為回溯法,而滿足回溯條件的某個狀態的點成為“回溯點”。

    對于回溯算法的簡單描述是:把問題的解空間轉化為圖或樹的結構,然后使用深度優先進行遍歷,遍歷過程中尋找所有可行解,從而找到最優解。

    其基本思想類似于:圖的深度優先搜索;二叉樹的后序遍歷。

    回溯法的詳細描述為:回溯法按深度優先搜索解空間樹。首先從根節點出發,當搜索到解空間數的某一節點時,先利用剪枝函數判斷該節點是否可行(即能得到問題的解)。如果不可行,則跳過對該節點為根的子樹的搜索,逐層向其祖先節點回溯;否則,進入該子樹,繼續按深度優先策略搜索。
    2. 回溯算法的實現:遞歸和迭代

  • 回溯算法的經典問題:0-1背包問題
  • 有n種物品和一個背包,第i種物品的重量是wi,其價值為pi,背包的容量為c. 問:怎樣把物品裝入背包,背包中的物品的總價值最大。

    分析:從n種物品中選擇部分物品,這樣的題目解空間是子集樹。例如,當物品的種類數為3是,其解空間如下圖:

    邊1表示選擇該物品,邊0表示不選擇該物品。這樣,這個樹從根節點到葉子節點,包含了把物品放入背包的所有可能性。
    回溯搜索過程,如果來到了葉子節點,表示一條搜索路徑結束,如果該路徑上存在更優的解,則保存下來,如果不是葉子節點,是中間的節點(如B), 就遍歷其子節點(D和E), 如果子節點滿足剪枝條件,就繼續回溯搜索子節點。

    五。分支限界算法

  • 基本概念
  • 類似于回溯法,也是一種在問題的解空間樹T上搜索問題解的算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出解空間樹中滿足約束條件的所有解,而分支限界法的求解目標則是滿足約束條件的一個解,或是從滿足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。

    分支限界法的基本思想是對有約束條件的最優化問題的所有可行解(數目有限)空間進行搜索。該算法在具體執行時,把全部可行的解空間不斷分割為越來越小的自己(成為分支),并為每個自己內的解的值計算一個下界或上界(成為界定)。

    在每次分支后,對凡是界限超出已知可行解值的那些子集不再做進一步分支。這樣,解的許多子集(即搜索樹上的許多節點)就可以不必考慮了,從而縮小了搜索范圍。這一過程一直進行到找出可行解為止,該可行解的值不大于任務子集的界限,因此這種算法一般可以求得最優解。

    將問題分支為子問題,并對這些子問題定界的步驟稱為分支定界法。

    知識附錄:深度優先搜索和廣度優先搜索

    1)廣度優先搜索:使用隊列的先進先出

    2)深度優先搜索:

    2. 分支限界法的經典問題:旅行售貨員問題

    1)問題描述:某售貨員要到若干城市去賣商品,已知各城市之間的路程。他要選定一條從駐地出發,經過每個城市一次,最后回到駐地的路線,使總的路程最小。

    下圖為1,2,3,4 四個城市及路線圖,任意兩個城市之間不一定都有路可達。

    2)問題理解:
    分支限界法利用:廣度優先搜索和最優值策略
    利用二位數組保存圖信息,一旦一個城市沒有通向另外城市的路,則不可能有回路,不用再找下去了。
    我們任意選擇一個城市,作為出發點(因為最后都是一個回路,從哪出發都一樣)

    3)關鍵思路:
    想象一下,我們就是旅行員,從城市1出發,根據廣度優先搜索的思路,我們要把從城市1能到達的下一個城市,都要作為一種路徑走一下試試。可在程序里面怎樣實現這種“試試”呢?

    答案是:可以利用一種數據結構,保存我們每走一步后,當前的一些狀態參數。比如,我們已經走過的城市數目(這樣就知道,我們有沒有走完,比如上圖,當我們走了四個城市之后,無論從第四個城市是否能回到起點城市,都意味著我們走完了,只是這條路徑合不合約束以及能不能得到最優解的問題)。我們把這種保存狀態參數的數據結構定義成Node。需要另一個數據結構用來保存我們每次試出來的路徑,這就是MiniHeap

    Node{
    int s; //結點深度,即當前走過了幾個城市
    int x[MAX_SIZE]; //保存走到這個結點時的路徑信息
    }
    MiniHeap{
    //保存所有結點并提供一些結點的操作
    }

    總結

    以上是生活随笔為你收集整理的五大常用算法学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。