import java.util.*;
/**
* Created by qililong on 2020/7/6.
* 程序員常用的十種算法
*/
public class ShiSuanFa {
//1、非遞歸方式二分查找,arr有序的數組,target要查找的目標
public static int binarySearch(int[] arr , int target){
int left =0;
int right = arr.length-1;
while (left<=right){//說明繼續查找
int mind = (left+right)/2;
if(arr[mind]==target){
return mind;
}else if(arr[mind]<target){
left=mind+1;
}else if(arr[mind]>target){
right=mind-1;
}
}
return -1;//表示沒找到
}
//2分治算法,大問題分解成小問題,合併排序、快速排序、漢諾塔、循環賽日程表、棋盤覆蓋等經典問題都可利用
//漢諾塔:不管有多少個,最大的上邊那些可以看做一個
public static void hanoiTower(int num ,char a, char b, char c){
if(num ==1){//如果只有一個塔
System.out.println("第一個盤從"+a+"->"+c);
}else{
//如果我們有n>2個盤
//1先把最上邊的所有盤A-》B,移動過程會使用到C
hanoiTower(num-1, a, c, b);
//2將A最下邊的移動到C
System.out.println("第"+num+"個盤從"+a+"->"+c);
//3將B塔的搬到C塔,中間可能用到A塔
hanoiTower(num-1, b, a, c);
}
}
//動態規劃;
// 揹包問題:一個揹包,三個物品,吉他重1價值1500,音響重4價值3000,電腦重3,價值2000,
// 重量不超出的情況下,總價值最大(物品不重複是01揹包,如果每件物品可以無限件使用則是完全揹包)
//求解過程類似分治算法,但子問題不是互相獨立的,可用填表的方式得到最優解。
//w表示每個物品的重量,v表示每個物品的價值,m表示揹包的最大容量,dp[i][j]表示在前i個物品中裝入容量爲j的揹包的最大價值。
public static void KnapsackProblem(int[] w, int[] v, int m, int[][] dp){
for(int i =1;i<dp.length;i++){
for(int j=1;j<dp[1].length;j++){
//公式
if(w[i-1]>j){
dp[i][j]=dp[i-1][j];
}else{
//公式:dp[i][j]=Math.max(dp[i-1][j],v[i]+dp[i-1][j-w[i]]);第一行和第一列都是0,所以i和j從1開始
// 因爲我們i和j從1開始的所以v[i]+dp[i-1][j-w[i]]換成v[i-1]+dp[i-1][j-w[i-1]]
dp[i][j]=Math.max(dp[i-1][j],v[i-1]+dp[i-1][j-w[i-1]]);
}
}
}
}
//暴力匹配算法
public static int baoliSearch(String str1, String str2){
int i=0;
int j=0;
while (i<str1.length() && j<str2.length()){
if(str1.charAt(i)==str2.charAt(j)){
i++;
j++;
}else{
i=i-(j-1);
j=0;
}
}
if(j==str2.length()){
return i-j;
}else {
return -1;
}
}
//KMP算法,匹配兩個字符串(暴力匹配算法或者KMP算法,暴力匹配算法會有很多回溯:如果str1[i]=Str2[j]則i++,j++;否則i=i-(j-1),j=0)
//KMP算法是爲了不讓i回退這麼多,利用部分匹配表進行回退:移動位置=已匹配的字符數-對應的部分匹配值。部分匹配值通過分析前綴後綴得到
//ABCDAB的前綴是[A,AB,ABC,ABCD,ABCDA];後綴是[BCDAB,CDAB,DAB,AB,B],共有元素AB,AB的長長度爲2
public static int kmpSearch(String str1, String str2, int[] next){//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;//沒找到
}
//生成部分匹配表
public static int[] kmpNext(String dest){
int[] next = new int[dest.length()];
next[0]=0;//字符串長度是0則匹配值是0
for(int i=1,j=0;i<dest.length();i++){
while (j>0 && dest.charAt(i)!=dest.charAt(j)){
j=next[j-1];//獲取新的j
}
if(dest.charAt(i)==dest.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}
//貪心算法,每一步選擇都選擇最優解,從而期望得到最優結果,但結果不一定是最優解;
//集合覆蓋問題,不同的廣播臺可以覆蓋不同的地區,如何選擇最少的廣播臺讓左右地區都接收到信號
//遍歷所有電視臺,每次都找覆蓋了最多爲覆蓋地區的電臺,電臺加入集合中,電臺覆蓋的地區則去掉,直到覆蓋了全部地區
public static void GreedyAlgorithm(HashMap<String ,HashSet<String>> broadcasts, HashSet<String> allAreas, ArrayList<String> selects){
//臨時的集合,保存當前電臺覆蓋的還沒有覆蓋的地區
HashSet<String> tempSet = new HashSet<>();
while (allAreas.size()>0){
//定義maxKey保存每次遍歷中覆蓋最大區域的電臺的Key
String maxKey=null;
for(String key : broadcasts.keySet()){
tempSet.clear();
tempSet.addAll(broadcasts.get(key));
tempSet.retainAll(allAreas);//這個電臺能覆蓋的地區
//如果覆蓋的城市大於0,並且是初次遍歷或者本次覆蓋的城市比上一次循環覆蓋的城市多則取本電臺
if(tempSet.size()>0 && (maxKey==null || tempSet.size()>broadcasts.get(maxKey).size())){
maxKey=key;
}
}
if(maxKey!=null){
selects.add(maxKey);
allAreas.removeAll(broadcasts.get(maxKey));
}
}
System.out.println("選擇的是:"+selects);//[K1,K2,K3,K5]
}
//馬踏棋盤算法(騎士周遊問題)
//弗洛伊德算法,加權圖最短路徑問題
//迪傑斯特拉算法,加權圖最短路徑問題,利用了廣度優先
//普里姆算法(求最小生成樹)帶權的無向連接圖,選擇一顆生成樹,使樹上的所有邊上權的總和爲最小,就叫最小生成樹
//最小生成樹有普里姆算法和克魯斯卡爾算法,一個從頂點考慮,一個從邊考慮
//普里姆算法從頂點考慮,修路問題:7個村莊{A,B,C,D,E,F,G},修路將7村聯通,路最短
public static void main(String[] args){
/*int[] arr = {1,3,4,7,9,134,666};
int index = binarySearch(arr,1);
System.out.println("########"+index);*/
// hanoiTower(5,'A','B','C');
/*int[] w={1,4,3};
int[] v = {1500,3000,2000};
int m = 4;
int n = v.length;//物品個數
int[][] dp = new int[n+1][m+1];
KnapsackProblem(w,v,m,dp);
System.out.println(dp[n][m]);*/
/*String str1= "BBC ABCDAB ABCDABCDABDE";
String str2 = "ABCDABD";
//求部分匹配表
int[] next = kmpNext(str2);
System.out.println("next="+ Arrays.toString(next));
int index = kmpSearch(str1,str2,next);
System.out.println("index="+index);
//暴力匹配算法
int index2 = baoliSearch(str1,str2);
System.out.println("index="+index2);*/
//每個電臺覆蓋的地區
/*HashMap<String ,HashSet<String>> broadcasts = new HashMap<String,HashSet<String>>();
HashSet<String> hash1 = new HashSet<>();
hash1.add("北京");
hash1.add("上海");
hash1.add("天津");
HashSet<String> hash2 = new HashSet<>();
hash2.add("廣州");
hash2.add("北京");
hash2.add("深圳");
HashSet<String> hash3 = new HashSet<>();
hash3.add("成都");
hash3.add("上海");
hash3.add("杭州");
HashSet<String> hash4 = new HashSet<>();
hash4.add("上海");
hash4.add("天津");
HashSet<String> hash5 = new HashSet<>();
hash5.add("杭州");
hash5.add("大連");
broadcasts.put("K1",hash1);
broadcasts.put("K2",hash2);
broadcasts.put("K3",hash3);
broadcasts.put("K4",hash4);
broadcasts.put("K5",hash5);
//所有地區
HashSet<String> allAreas = new HashSet<String>();
allAreas.add("北京");
allAreas.add("上海");
allAreas.add("天津");
allAreas.add("廣州");
allAreas.add("深圳");
allAreas.add("成都");
allAreas.add("杭州");
allAreas.add("大連");
//所選電臺
ArrayList<String> selects = new ArrayList<>();
GreedyAlgorithm(broadcasts,allAreas,selects);*/
}
}