java實現十大經典算法

1.二分查找算法(非遞歸)html

/**
 * @desc 二分查詢(非遞歸方式)
 * 案例:
 * {1,3,8,10,11,67,100},編程實現二分查找,要求使用非遞歸方式完成。
 * @Author xw
 * @Date 2019/9/27
 */
public class BinarySearchNonRecursive {
    public static void main(String[] args) {
        int[] arr = {1, 3, 8, 10, 11, 67, 100};
        int index = binarySearch(arr, 1);
        if (index != -1) {
            System.out.println("找到了,下標爲:" + index);
        } else {
            System.out.println("沒有找到--");
        }
    }
    private static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] > target) {
                right = mid - 1; // 向左找
            } else {
                left = mid + 1; // 向右找
            }
        }
        return -1;
    }
}

2.分治算法git

/**
 * @desc 分治算法案例:漢諾塔
 * (1)基本概念
 * 分治算法是一種很重要的算法,字面上的解釋是「分而治之」,就是把一個複雜的問題
 * 分解成兩個或更多的相同或類似的子問題...直到最後子問題能夠簡單的直接求解,原
 * 問題的解即子問題的解的合併,這個技巧就是不少高效算法的基礎,如排序算法(快速排序,歸併排序),傅里葉變換(快速傅里葉變換)...
 * (2)基本步驟
 * 1)分解:將原問題分解爲若干個規模較小的問題,相互獨立,與原問題形式相同的子問題
 * 2)解決:若子問題規模較小則直接解決,不然遞歸地解各個子問題
 * 3)合併:將各個子問題的解合併爲原問題的解
 * (3)分治算法設計模式
 * if |P|<=n0
 * then return (ADHOC(P))
 * // 將P分解爲較小的問題P1,P2...PK
 * for i <- 1 to k
 * do yi <- Divide-and-Conquer(Pi) 遞歸解決Pi
 * T <- MERGE(y1,y2...yk) 合併子問題
 * return (T)
 * <p>
 * |P|:表示問題P的規模
 * n0:表示閾值,表示當問題P的規模不超過n0時,問題已容易直接解出,沒必要再繼續分解。
 * ADHOC(P):是該分治法中的基本子算法,用於直接解小規模的問題P。所以,當P的規模不超過n0時直接用算法ADHOC(P)求解
 * 算法MERGE(y1,y2...yk):是該分治算法中的合併子算法,用於將P的子問題P1,P2...PK的相應的解y1,y2,..yk合併爲P的解。
 * <p>
 * 經典案例:漢諾塔
 * 思路分析:
 * (1)若是有一個盤,A->C
 * n0=2
 * if (n<=n0) {
 * // 直接解出來
 * }
 * // 將P分解爲較小的問題P1,P2...PK
 * while(n>n0) {
 * 分(n);
 * n--;
 * }
 * // T <- MERGE(y1,y2...yk) 合併子問題
 * @Author xw
 * @Date 2019/9/27
 */
public class HanoiTower {
    public static void main(String[] args) {
        hanoiTower(3, 'A', 'B', 'C');
    }
    private static void hanoiTower(int num, char a, char b, char c) {
        if (num == 1) { // 只有一個盤,直接解出
            System.out.println("第1個盤從" + 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塔全部盤從B->C,移動過程使用到A
            hanoiTower(num - 1, b, a, c);
        }
    }
}

 

3.動態規劃算法github

/**
 * @desc 動態規劃算法案例:揹包問題
 * 思路分析:
 * (1)假設:
 * 用w[i],v[i]來肯定是否須要將該物品放入揹包中;
 * 即對於給定的n個物品,設v[i],w[i]分別爲第i個物品的價值和重量,C爲揹包的容量。
 * 再令v[i][j] 表示在前i個物品中可以裝入容量j的揹包的最大價值。則咱們有下面的結果:
 * (2)結論:
 * 1)當v[i][0]=v[0][j]=0; // 表示填入表 第一行和第一列是0
 * 2)當w[i]>j時;v[i][j]=v[i-1][j] // 當準備加入新增的商品的容量大於當前揹包的容量時,就直接使用上一個單元格的裝入策略
 * 3)當j>=w[i]時;v[i][j]=max{v[i-1][j], v[i]+v[i-1][j-w[i]]}
 * // 當準入的新增的商品的容量小於等於當前揹包的容量,裝入方式:
 * v[i-1][j]:就是上一個單元格的裝入的最大值
 * v[i]:表示當前商品的價值
 * v[i-1][j-w[i]]:裝入i-1商品,到剩餘空間j-w[i]的最大值
 * 當j>=w[i]時:v[i][j] = max{v[i-1][j], v[i-1][j-w[i]]}
 * <p>
 * 案例:
 * 物品      重量  價格
 * 吉他(G)   1   1500
 * 音響(S)   4   3000
 * 電腦(L)   3   2000
 * @Author xw
 * @Date 2019/9/27
 */
public class KnapsackProblem {
    public static void main(String[] args) {
        int[] w = {1, 4, 3}; // 物品重量
        int[] val = {1500, 3000, 2000}; // 物品價值
        int m = 4; // 揹包的容量
        int n = val.length; // 物品個數
        // 建立二維數據
        int[][] v = new int[n + 1][m + 1];
        // 1)當v[i][0]=v[0][j]=0; // 表示填入表 第一行和第一列是0
        for (int i = 0; i < v.length; i++) {
            v[0][i] = 0; // 第一列爲0
        }
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0; // 第一行爲0
        }
        int[][] path = new int[n + 1][m + 1];
        for (int i = 1; i < v.length; i++) {
            for (int j = 1; j < v[0].length; j++) { // 不處理第1列
                // 當w[i]>j時;v[i][j]=v[i-1][j] // 當準備加入新增的商品的容量大於當前揹包的容量時,就直接使用上一個單元格的裝入策略
                if (w[i - 1] > j) {
                    v[i][j] = v[i - 1][j];
                } else {
                    // 當j>=w[i]時;v[i][j]=max{v[i-1][j], v[i]+v[i-1][j-w[i]]}
                    // v[i-1][j]:就是上一個單元格的裝入的最大值
                    // v[i]:表示當前商品的價值
                    // v[i-1][j-w[i]]:裝入i-1商品,到剩餘空間j-w[i]的最大值
                    // 當準入的新增的商品的容量小於等於當前揹包的容量,裝入方式:
                    if (v[i - 1][j] < val[i - 1] + v[i - 1][j - w[i - 1]]) { // w[i]->w[i-1]替換?
                        v[i][j] = val[i - 1] + v[i - 1][j - w[i - 1]];
                        // 把當前的狀況記錄到path
                        path[i][j] = 1;
                    } else {
                        v[i][j] = v[i - 1][j];
                    }
                }
            }
        }
        // 輸出一把
        for (int i = 0; i < v.length; i++) {
            for (int j = 0; j < v[i].length; j++) {
                System.out.print(v[i][j] + "\t");
            }
            System.out.println();
        }
        System.out.println("========================");
        /*for (int i = 0; i < path.length; i++) {
            for (int j = 0; j < path[i].length; j++) {
                if (path[i][j] == 1) {
                    System.out.println(String.format("第%d個商品放入揹包", i));
                }
            }
        }*/
        // 其實咱們只須要最後的放入
        int i = path.length - 1;
        int j = path[0].length - 1;
        while (i > 0 && j > 0) {
            if (path[i][j] == 1) {
                System.out.println(String.format("第%d個商品放入到揹包", i));
                j -= w[i - 1];
            }
            i--;
        }
    }
}
View Code

 

4.KMP算法算法

/**
 * @desc KMP算法
 * 基本介紹:
 * (1)暴力匹配算法
 *      1)若是當前字符匹配成功(即str1[i]=str2[i]),則i++,j++,繼續匹配下一個字符
 *      2)若是失敗,令i=i-(j-1),j=0,至關於每次匹配失敗時,i回溯,j被轉爲0
 *      3)用暴力方法解決的話就會有大量的回溯,每次只移動一位,如果不匹配,移動到下一位接着判斷,浪費大量時間。(不可行)
 *      4)暴力匹配實現
 * (2)KMP算法介紹
 *      1)KMP是一個解決模式串在文本串是否出現過,若是出現過,最先出現的位置就經典算法。
 *      2)Knuth-Morris-Pratt字符串查找法,簡稱KMP。
 *      3)KMP算法就是利用以前判斷過信息,經過一個next數組,保存模式串中先後最長公共序列的長度,每次回溯時,經過next數組找到,
 *          前面匹配的位置,省去了大量的計算時間
 *      4)參考資料:https://www.cnblogs.com/ZuoAndFutureGirl/p/9028287.html
 * @Author xw
 * @Date 2019/9/27
 */
public class KMPAlgorithm {
    public static void main(String[] args) {
        // 暴力匹配
        String str1 = "ABCDE";
        String str2 = "CD";
        int index = violenceMatch(str1, str2);
        if (index != -1) {
            System.out.println("找到了,位置:" + index);
        } else {
            System.out.println("沒有找到!");
        }
        // KMP算法介紹
        // 字符串模板匹配值
        str1 = "BBC ABCDAD ABCDABCDABDE";
        str2 = "ABCDABD";
        /*int[] next = kmpNext("ABCDABD");
        System.out.println("next=" + Arrays.toString(next));*/
        index = kmpMatch(str1, str2, kmpNext(str2));
        if (index != -1) {
            System.out.println("找到了,位置:" + index);
        } else {
            System.out.println("沒有找到!");
        }
    }
}
View Code

 

5.貪心算法編程

/**
 * @desc 貪心算法
 * 思路分析
 * (1)使用窮舉法,列出每一個可能廣播臺集合,這被稱爲冪集。
 * (2)假設有n個廣播臺,則廣播臺的組合共有2^n-1個,假設每秒能夠計算10個子集
 *      廣播臺數量   子集總數    須要的時間
 *      5               32          3.2秒
 *      10              1024        102.4秒
 *      ...
 *
 *  案例:集合覆蓋問題
 *      假設存在下面須要付費的廣播臺,以及廣播信號能夠覆蓋的地區,如何選擇
 *      最少的廣播臺,讓全部的地區均可以接收信息
 *      廣播臺     覆蓋地區
 *      K1          "北京","上海","天津"
 *      K2          "廣州","北京","深圳"
 *      K3          "成都","上海","杭州"
 *      K4          "上海","天津"
 *      K5          "杭州","大連"
 * @Author xw
 * @Date 2019/9/27
 */
public class GreedyAlgorithm {
    public static void main(String[] args) {
        Map<String, Set<String>> broadcasts = new HashMap<>(); // 廣播電臺
        broadcasts.put("K1", Arrays.stream(new String[]{"北京", "上海", "天津"}).collect(Collectors.toSet()));
        broadcasts.put("K2", Arrays.stream(new String[]{"廣州", "北京", "深圳"}).collect(Collectors.toSet()));
        broadcasts.put("K3", Arrays.stream(new String[]{"成都", "上海", "杭州"}).collect(Collectors.toSet()));
        broadcasts.put("K4", Arrays.stream(new String[]{"上海", "天津"}).collect(Collectors.toSet()));
        broadcasts.put("K5", Arrays.stream(new String[]{"杭州", "大連"}).collect(Collectors.toSet()));
        // [上海, 天津, 北京, 廣州, 深圳, 成都, 杭州, 大連]
        List<String> allAreas = broadcasts.values().stream().flatMap(Collection::stream).distinct().collect(Collectors.toList()); // 表示全部須要覆蓋的地區
        System.out.println("allAreas=" + allAreas);
        List<String> selects = new ArrayList<>(); // 選擇的地區集合
        // 定義一個臨時的集合,在遍歷過程當中,存放遍歷過程當中的電臺覆蓋的地區和當前尚未覆蓋的地區的交集
        Set<String> tempSet = new HashSet<>();
        String maxKey; // 最大的電臺,保存在一次遍歷過程當中,可以覆蓋最大未覆蓋的地區對應的電臺key
        while (allAreas.size() != 0) {
            maxKey = null; // 置空
            // 遍歷broadcasts,取出對應key
            for (String key : broadcasts.keySet()) {
                tempSet.clear(); // 清空
                Set<String> areas = broadcasts.get(key);
                tempSet.addAll(areas);
                tempSet.retainAll(allAreas); // tempSet = tempSet與allAreas的交集
                if (tempSet.size() > 0 && (maxKey == null
                        || tempSet.size() > broadcasts.get(maxKey).size())) {
                    maxKey = key;
                }
            }
            if (maxKey != null) {
                selects.add(maxKey);
                // 將maxKey指向的廣播電臺覆蓋地區,從allAreas去掉
                System.out.println("maxKey=" + maxKey);
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }
        System.out.println("獲得的選擇結果是:" + selects);
    }
}
View Code

 

6.普利姆算法設計模式

/**
 * @desc 普利姆算法
 * 應用案例:修路問題
 * <p>
 * 思路分析
 *  1.從<A>頂點開始處理=><A,G> 2
 *      A,C[7] A-G[2] A-B[5] =>
 *  2.<A,G>開始,將A和G頂點和他們相鄰的尚未訪問的頂面進行處理=> <A,G,B>
 *      A-C[7] A-B[5] G-B[3] G-F[6]
 *  3.<A,G,B>開始,將A,G,B頂點和他們相鄰的尚未訪問的頂面進行處理=> <A,G,B>
 *      A-C[7] G-E[4] G-F[6] B-D[9]
 *  ...
 *  4.{A,G,B,E,F,D} -> C // 第6次大循環,對應邊<A,C>權值:7 => <A,G,B,E,F,D,C>
 * @Author xw
 * @Date 2019/10/4
 */
public class PrimAlgorithm {
    public static void main(String[] args) {
        char[] data = {'A','B','C','D','E','F','G'};
        int verxs = data.length;
        // 鄰接矩陣
        int[][] weight = new int[][] {
                {10000,5,7,10000,10000,10000,2},
                {5,10000,10000,9,10000,10000,3},
                {7,10000,10000,10000,8,10000,10000},
                {10000,9,10000,10000,10000,4,10000},
                {10000,10000,8,10000,10000,5,4},
                {10000,10000,10000,4,5,10000,6},
                {2,3,10000,10000,4,6,10000}
        };
        // 建立MGraph對象
        MGraph graph = new MGraph(verxs);
        // 建立最小樹
        MinTree minTree = new MinTree();
        minTree.createGraph(graph, verxs, data, weight);
        // 輸出
        minTree.showGraph(graph);
        // 測試普利姆算法
        minTree.prim(graph, 0);
    }
}
View Code

 

7.克魯斯卡爾算法數組

/**
 * @desc 克魯斯卡爾算法
 * 案例:公交車問題
 * 1. 某城市新增7個站點,A,B,C,D,E,F,G,如今須要修路7個站點連通
 * 2. 各個站點距離用連線表示,好比A-B距離12千米
 * 3. 問:如何修路保證各個站點都能連通,而且總的修建公路總里程最短
 * @Author xw
 * @Date 2019/10/8
 */
public class KruskalCase {
    private static final int INF = Integer.MAX_VALUE;
    private char[] vertexs;
    private int[][] matrix;
    private int edgeNums; // 邊的數量
    public KruskalCase(char[] vertexs,int[][] matrix ) {
        this.vertexs = vertexs;
        this.matrix = matrix;
        // 統計邊
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = i + 1; j < vertexs.length; j++) { // 每次少一條邊,因此是i+1
                if (this.matrix[i][j] != INF) {
                    edgeNums++;
                }
            }
        }
    }
    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = {
                     /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
                /*A*/{ 0,   12, INF,  INF, INF, 16,  14 },
                /*B*/{ 12,  0,   10,  INF, INF, 7,   INF},
                /*C*/{ INF, 10,  0,   3,    5,  6,   INF },
                /*D*/{ INF, INF, 3,   0,    4,  INF, INF },
                /*E*/{ INF, INF, 5,   4,    0,  2,   8 },
                /*F*/{ 16,  7,   6,   INF,  2,  0,   9 },
                /*G*/{ 14,  INF, INF, INF,  8,  9,   0 }
        };
        // 建立KruskalCase對象實例
        KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
        //
        kruskalCase.print();
        kruskalCase.kruskal();
    }
}
View Code

 

8.迪傑斯特拉算法ide

/**
 * @desc 迪傑斯特拉算法
 * 案例:最短路徑問題
 * 1. 戰爭時期,勝利鄉有7個村莊(A,B,C,D,E,F,G),如今有6個郵差,從G點出發,須要分別把郵件分別送到A,B,C,D,E,F 六個村莊
 * 2. 各個村莊的距離用邊線表示(權),好比A-B距離5千米
 * 3. 問:如何計算最短距離
 *
 * @Author xw
 * @Date 2019/10/8
 */
public class DijkstraAlgorithm {
    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535;
        matrix[0] = new int[]{N,5,7,N,N,N,2};
        matrix[1] = new int[]{5,N,N,9,N,N,3};
        matrix[2] = new int[]{7,N,N,N,8,N,N};
        matrix[3] = new int[]{N,9,N,N,N,4,N};
        matrix[4] = new int[]{N,N,8,N,N,5,4};
        matrix[5] = new int[]{N,N,N,4,5,N,6};
        matrix[6] = new int[]{2,3,N,N,4,6,N};
        // 建立Graph對象
        Graph graph = new Graph(vertex, matrix);
        graph.showGraph();
        // 測試迪傑斯特拉算法
        graph.dsj(6); // G
        graph.showDijkstra();
    }
}
View Code

 

9.弗洛伊德算法測試

/**
 * @desc 弗洛伊德算法
 * @Author xw
 * @Date 2019/10/8
 */
public class FloydAlgorithm {
    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535;
        matrix[0] = new int[]{0, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, 0, N, 9, N, N, 3};
        matrix[2] = new int[]{7, N, 0, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, 0, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};
        FloydGraph graph = new FloydGraph(vertex.length, matrix, vertex);
        graph.floyd();
        graph.show();
    }
}
View Code

 

10.馬踏棋盤算法this

/**
 * @desc 馬踏棋盤算法
 * @Author xw
 * @Date 2019/10/8
 */
public class HorseChessboard {
    private static int X; // 棋盤的列數
    private static int Y; // 棋盤的行數
    //建立一個數組,標記棋盤的各個位置是否被訪問過
    private static boolean visited[];
    //使用一個屬性,標記是否棋盤的全部位置都被訪問
    private static boolean finished; // 若是爲true,表示成功
    public static void main(String[] args) {
        System.out.println("騎士周遊算法,開始運行~~");
        //測試騎士周遊算法是否正確
        X = 8;
        Y = 8;
        int row = 1; //馬兒初始位置的行,從1開始編號
        int column = 1; //馬兒初始位置的列,從1開始編號
        //建立棋盤
        int[][] chessboard = new int[X][Y];
        visited = new boolean[X * Y];//初始值都是false
        //測試一下耗時
        long start = System.currentTimeMillis();
        traversalChessboard(chessboard, row - 1, column - 1, 1);
        long end = System.currentTimeMillis();
        System.out.println("共耗時: " + (end - start) + " 毫秒");
        //輸出棋盤的最後狀況
        for(int[] rows : chessboard) {
            for(int step: rows) {
                System.out.print(step + "\t");
            }
            System.out.println();
        }
    }
}
View Code

gitee地址:https://gitee.com/linestyle007/jucdemo2

博客地址:https://linestyle007.gitee.io/

github地址:https://github.com/line007/jucdemo2

相關文章
相關標籤/搜索