常用演算法
阿新 • • 發佈:2020-07-15
目錄
常用演算法
1.搜尋演算法
1).BFS(breadth first search)
以當前節點兄弟節點優先路徑的探索方法
應用:最短路徑查詢
案例:查詢某個目錄下所有的檔案
//深度搜索遍歷資料夾下所有檔案 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()); } } }
2).DFS(Depth first search)
以深度路徑為優先路徑的探索方法
案例:查詢某個目錄下所有的檔案
//廣度搜索遍歷資料夾 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);
}