1、二分算法
進行二分查找的數組必須是有序的
二分查找法是對一組有序的數字中進行查找,傳遞相應的數據,進行比較查找到與原數據相同的數據,查找到了返回對應的數組下標,沒有找到返回-1;
遞歸實現二分查找:
/**
* 遞歸實現
*
* @param arr
* @param left
* @param right
* @param index
* @return
*/
public static int binarySearch(int[] arr, int left, int right, int index) {
if (left <= right) {
int mid = (right + left) / 2;
if (arr[mid] == index) return mid;
if (index > arr[mid]) return binarySearch(arr, mid + 1, right, index);
else return binarySearch(arr, left, mid - 1, index);
}
return -1;
}
非遞歸實現二分查找:
public static int binarySearch02(int[] arr, int index) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == index) return mid;
else if (arr[mid] > index) right = mid - 1;
else left = mid + 1;
}
return -1;
}
2、分治算法(解決漢諾塔問題)
漢諾塔(港臺:河內塔)是根據一個傳說形成的數學問題:
有三根杆子A,B,C。A杆上有 N 個 (N>1) 穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至 C 杆:
每次只能移動一個圓盤;
大盤不能疊在小盤上面。
提示:可將圓盤臨時置於 B 杆,也可將從 A 杆移出的圓盤重新移回 A 杆,但都必須遵循上述兩條規則。
問:如何移?最少要移動多少次?
求解:
解法的基思想是遞歸。假設有 A、B、C 三個塔,A 塔有 {\displaystyle N}N 塊盤,目標是把這些盤全部移到 C 塔。那麼先把 A 塔頂部的 {\displaystyle N-1}{\displaystyle N-1} 塊盤移動到 B 塔,再把 A 塔剩下的大盤移到 C,最後把 B 塔的 {\displaystyle N-1}{\displaystyle N-1} 塊盤移到 C。
* 如此遞歸地使用下去, 就可以求解。
代碼實現:
/**
* @param num 盤子數
* @param a 塔名
* @param b
* @param c //移動的目標
*/
public static void fun(int num, char a, char b, char c) {
if (num == 1) {
System.out.println("第一個盤子" + a + "->" + c);
} else {
//有大於等於2的盤子,則把這些盤子看成兩部分。1、最底下一部分的盤子-->a-c
//2、上面所有盤子 這些盤子先移動到全部b
fun(num - 1, a, c, b);//把上面部分的盤子從a移動到b,藉助c
System.out.println("第" + num + "個盤子 " + a + "->" + c);
fun(num - 1, b, a, c);//b->c
}
}
3、動態規劃(解決0-1揹包問題)
什麼是0-1揹包問題?
小偷從入門到精通(滑稽)
首先得知道什麼是0-1揹包問題(knapsack problem)
◆ 賊,夜入豪宅,可偷之物甚多,而負重能力有限,偷哪些才更加不枉此行?
◆ 抽象的話,就是:
給定一組多個([公式])物品,
每種物品都有自己的重量([公式])和價值([公式]),在限定的總重量/總容量([公式])內,
選擇其中若干個(也即每種物品可以選0個或1個),設計選擇方案使得物品的總價值最高。
代碼實現:
public static void main(String[] args) {
int[] weight = {1, 4, 3};//表示物品的重量
int[] value = {1500, 3000, 2000};//物品的價值
int m = 4;//揹包的容量
int n = value.length;//物品的個數
int[][] v = new int[n + 1][m + 1];
int[][] path = new int[n + 1][m + 1];//放入商品的記錄
for (int i = 0; i < v.length; i++) {
v[i][0] = 0;//二維數組第一列爲0
}
for (int i = 0; i < v[0].length; i++) {
v[0][i] = 0;//讓二維數組第一行爲0
}
//放入商品進行動態規劃
//二維數組列表示的是商品,行表示揹包的容量逐漸增加
//第二列,雖然揹包的容量雖然增加,商品種類只有一種,所以沒有比較
for (int i = 1; i < v.length; i++) {
for (int j = 1; j < v[0].length; j++) {
if (weight[i - 1] > j) {//當前商品的重量大於了揹包的容量,就從上一各取結果
v[i][j] = v[i - 1][j];
} else {
// weight[i]<=j 我們首先嚐試把當前商品放進去,如果還有多餘空間,我們再放入其他商品
//與上一個表格價值進行比較,取最大的價值填入當前表格
//v[i][j] = Math.max(v[i - 1][j], value[i - 1] + v[i - 1][j - weight[i - 1]]);
int v1 = (value[i - 1] + v[i - 1][j - weight[i - 1]]);
int v2 = v[i - 1][j];
if(v1>v2){
v[i][j] = v1;
path[i][j] = 1;
}else {
v[i][j] = v2;
}
}
}
}
show(v);
System.out.println("======");
show(path);
}
4、暴力匹配算法
假設現在我們面臨這樣一個問題:有一個文本串 S,和一個模式串 P,現在要查找 P 在 S 中的位置,怎麼查找呢?
如果用暴力匹配的思路,並假設現在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置,則有:
- 如果當前字符匹配成功(即 S[i] == P[j]),則 i++,j++,繼續匹配下一個字符;
- 如果失配(即 S[i]! =P[j]),令 i = i - (j - 1),j = 0。相當於每次匹配失敗時,i 回溯,j 被置爲0。
理清楚了暴力匹配算法的流程及內在的邏輯,咱們可以寫出暴力匹配的代碼,如下:
public static int violenceMath(String str1, String str2) {
char[] s1 = str1.toCharArray();
char[] s2 = str2.toCharArray();
int s1len = s1.length;
int s2len = s2.length;
int i = 0;//表示匹配s1的索引
int j = 0;//表示匹配s2的索引
while (i < s1len && j < s2len) {
if (s1[i] == s2[j]) {//匹配成功
i++;
j++;
} else {//匹配失敗
i = i - (j - 1);
}
}
if (j == s2len) {//完全匹配
return i - s2len;
} else {
return -1;
}
}
5、KMP算法:
定義:
Knuth-Morris-Pratt 字符串查找算法,簡稱爲 “KMP 算法”,常用於在一個文本串 S 內查找一個模式串 P 的出現位置,這個算法由 Donald Knuth、Vaughan Pratt、James H. Morris 三人於 1977 年聯合發表,故取這三人的姓氏命名此算法。
1、部分匹配表求法:
部分匹配值就是前綴和後綴的最長的共有元素的長度
以"ABCDABD"爲例:
A 的前綴和後綴都爲空,共有長度爲0;
AB 的前綴A 後綴B ,共有長度 0;
ABC 前綴[A,AB]後綴[BC,C],共有長度 0
ABCD 前綴[A,AB,ABC]後綴[BCD,CD,D],共有長度 0
ABCDA 前綴[A,AB,ABC,ABCD]後綴[BCDA,CDA,DA,A],共有元素A,共有長度 1
…
public static int[] kmpNext(String dest) {
int[] next = new int[dest.length()];
next[0] = 0;//只有一個字母的字符串,部分匹配值爲0
for (int i = 1,j = 0; i < next.length; i++) {
while (j > 0 && dest.charAt(i) != dest.charAt(j)) j = next[j - 1];
if (dest.charAt(i) == dest.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
匹配算法:
/**
* @param str1 字符串
* @param str2 //子串
* @param next //部分匹配值表
* @return
*/
public static int kmpSearch(String str1, String str2, int[] next) {
for (int i = 0, j = 0; i < str1.length(); i++) {
while (j > 0 && str1.charAt(i) != str2.charAt(j)){
j = next[j - 1];
}
if (str1.charAt(i) == str2.charAt(j)) {
j++;
}
if (j == str2.length()) {
return i - j + 1;
}
}
return -1;
}