1. 程式人生 > 實用技巧 >常用演算法

常用演算法

目錄

常用演算法

1.搜尋演算法

以當前節點兄弟節點優先路徑的探索方法
應用:最短路徑查詢
案例:查詢某個目錄下所有的檔案

//深度搜索遍歷資料夾下所有檔案
public static void dfsListFile(String dirPath, List<String> list) {
    File file = new File(dirPath);
    File[] files = file.listFiles();
    for (File tmpFile : files) {
        if (tmpFile.isDirectory()) {
            dfsListFile(tmpFile.getAbsolutePath(), list);
        } else {
            list.add(tmpFile.getAbsolutePath());
        }
    }
}

以深度路徑為優先路徑的探索方法
案例:查詢某個目錄下所有的檔案

//廣度搜索遍歷資料夾
public static void bfsListFile(String dirPath, List<String> list) {
    File file = new File(dirPath);
    File[] fs = file.listFiles();
    Queue<File> queue = new LinkedList<>();

    // 遍歷第一層
    for (File f : fs) {
        // 把第一層資料夾加入佇列
        if (f.isDirectory())
            queue.offer(f);
        else
            list.add(f.getAbsolutePath());
    }
    // 逐層搜尋下去
    while (!queue.isEmpty()) {
        // 從佇列頭取一個元素
        File fileTemp = queue.poll();
        File[] fileListTemp = fileTemp.listFiles();
        for (File f : fileListTemp) {
            if (f.isDirectory())
                queue.offer(f);
            else
                list.add(f.getAbsolutePath());
        }
    }

}

bfs是浪費空間節省時間,dfs是浪費時間節省空間

2.動態規劃

動態規劃背後的基本思想非常簡單。大致上,若要解一個給定問題,我們需要解其不同部分(即子問題),再根據子問題的解以得出原問題的解

//動態規劃框架
//初始化 base case
dp[0][0][...] = base
//進行狀態轉移
for '狀態1' in '狀態1的所有取值':
    for '狀態2' in '狀態2的所有取值':
        for ...
            dp[狀態1][狀態2][...] = 求最值(選擇1,選擇2...)

通俗的解釋:通過子問題的解逐步累積推匯出原問題的解。

例項1:求最長迴文子串

//dp[j][i] 字串從j->i是否為迴文數
//動態迴歸方程d[i-1][j+i]是否為迴文數
public String longestPalindrome(String s) {
    int len=s.length();
    boolean[][] dp = new boolean[len][len];
    int max=-1;
    String str="";
    for (int i = 0; i < len; i++) {
        for (int j = 0; j <= i; j++) {
            if (s.charAt(j) == s.charAt(i) && (i - j <= 2 || dp[j+1][i-1]))
                dp[j][i] = true;
            if(dp[j][i] && i-j>max) {
                max=i-j;
                str=s.substring(j,i+1);
            }

        }
    }
    return str;
}

例項2:輸入一個整型陣列,數組裡有正數也有負數。陣列中的一個或連續多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)

/*
字串問題一般都是分解子問題的過程,假設陣列長度只有1個,不用求就只能是這個一元素的大小了,如果是2個元素,3個元素呢

元素個數 最大和

1 		[0]
2 		max([0]+[1],[1])
3 		max([1]+[2],[2])
4 		max([2]+[3],[3])
...
...
n max([n-1]+[n],[n])

分析:
1.當[n]>=[n-1]+[n]的時候說明[n-1]對結果產生了副作用,以n-1結尾的錢n項和還不如n項大,那麼n項就作為起點位置了
2.當[n]<[n-1]+[n]的時候說明[n-1]對結果產生了正面作用,繼續判斷下一部分
3.綜合以上分析這明顯就是動態規劃的特徵(核心思想是把原問題分解成子問題進行求解,也就是分治的思想)
4.綜合起來dp[i] 作為以i結尾連續字串最大和,本質就只有三行程式碼
	max = dp[0] = nums[0];
	dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
	max = Math.max(max, dp[i]);
5. 當[n]>[n-1]+[n]時記錄起始位置下標,當[n-1]+[n]>[n]時記錄結束座標,dp[i]>max時,則記錄最大和
*/
 //java程式碼
 public int maxSubArray(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len];
        dp[0] = nums[0];
        int max = dp[0];
        for (int i = 1; i < len; i++) {
            dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
            max = Math.max(max, dp[i]);
        }
        return max;
    }

3.貪心演算法

  • 貪心演算法(又稱貪婪演算法)是指,在對問題求解]時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,演算法得到的是在某種意義上的區域性最優解

  • 案例
    給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
    設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)

    /*
    分析:股票的價格既然是給出的,那麼相當於提前預測了未來股票的價格,既然這樣,那麼假設明天股價比今天高,就買入,假設明天價格比今天低就賣出
    */
    public int maxProfit(int[] prices) {
        int len=prices.length;
        int total=0;
        //表示買入時股價,-1表示沒買
        int buyPrice=-1;
        for(int i=0;i<len-1;i++){
            //當明天比今天股價高且未買股票時買入
            if(buyPrice<0 && prices[i+1]>prices[i]){
                buyPrice=prices[i];
                total-=buyPrice;
            }
            //當明天比今天股價低且買股票時賣出
            if(buyPrice>-1 && prices[i+1]<prices[i]){
                total+=prices[i];
                buyPrice=-1;
            }
        }
         //買入狀態下賣出最後一股
        if(buyPrice>-1){
              total+=prices[len-1];
          }
        return total;
    }
    

4. 分治演算法

分治演算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程式演算法,簡單問題可用二分法完成。

案例:快速排序

public static void quickSort(int[] data, int low, int high) {
    if (low > high) {
        return;
    }
    int i, j, swap, temp;
    // 基準數
    temp = data[low];
    // 低位起始掃描位置
    i = low;
    // 高位起始掃描位置
    j = high;
    while (i < j) {
        // 每次必須從右邊開始探測
        // 從右向左掃描找到小於基準數的位置停下來
        while (temp <= data[j] && i < j) {
            j--;
        }
        // 從左向右掃描找到大於基準數的位置停下來
        while (temp >= data[i] && i < j) {
            i++;
        }
        // 滿足條件交換
        if (i < j) {
            swap = data[i];
            data[i] = data[j];
            data[j] = swap;
        }
    }
    // 最後將基準為與i和j相等位置的數字交換
    // 最低位(基準位置)和i位置交換
    data[low] = data[i];
    data[i] = temp;
    // 繼續分治
    // 遞迴呼叫左半陣列
    quickSort(data, low, j - 1);
    // 遞迴呼叫右半陣列
    quickSort(data, j + 1, high);
}