数据结构与算法st13:程序员常用的十种算法

1.二分查找算法

非递归版:

    public 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;//如果没有这样的数据就返回-1

    }

递归版:

    public static int binary(int[] arr,int target,int left,int right){
        if(left<=right){
            int mid=(left+right)/2;
            if(arr[mid]>target){
                return binary(arr,target,left,mid-1);//继续递归,如果递归结束时也会返回一个数,这个数才是真正的结果
            }else if(arr[mid]<target){
                return binary(arr,target,mid+1,right);
            }else if(arr[mid]==target){
                return mid;
            }
        }
            return -1;
    }

2.分治算法(如何分—递归算法)

递归的三部曲:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

汉诺塔

分治算法案例实现—64层的汉诺塔实现
在这里插入图片描述
在这里插入图片描述
解题思路:每次将盘查分为1和(num-1)个盘进行移动,1为目标盘,(num-1)在辅助盘

    /**
     *
     * @param num 总共的塔的成熟
     * @param a 表示a盘,一般是放着汉诺塔的盘
     * @param b 表示辅助作用的盘
     * @param c 表示等待接收移动后的潘
     */
    public static void hanoTower(int num,char a,char b,char c){
        if(num==1){
            System.out.println("把顶盘从"+a+"移动到"+c);
            return;
        }
        if(num>1){
            //每次将盘查分为1和(num-1)个盘进行移动,1为目标盘,(num-1)在辅助盘

            //首先将上边(num-1)盘移动到b盘(作为辅助盘)
            hanoTower(num-1,a,c,b);
            //然后将最后的盘从a移动到c
            hanoTower(1,a,b,c);
            //将b盘中的(num-1)个移动到c盘(没有用的盘作为辅助盘)
            hanoTower(num-1,b,a,c);
        }
    }
}

3.回溯算法

递归----纵向深度----终止条件,回溯----横向宽度----for循环----位于递归函数的下方进行撤销操作)

因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。

在这里插入图片描述
在这里插入图片描述

回溯的模板-----void backtracking()函数:
在这里插入图片描述

4.动态规划算法(前一个子问题与后一个子问题之间不相互独立,依赖关系)

动态规划5部曲(carl哥):

在这里插入图片描述

在这里插入图片描述

背包问题(01背包)

背包问题的解释(案例:01背包,每个物件最多只能用1次)
在这里插入图片描述
01背包的过程填表(dp数组)
在这里插入图片描述
根据上表来推导出下面的递推公式:
在这里插入图片描述
韩顺平老师的扩展,继续输出最佳的组合物品放置情况

public class DynamicProgram {
    public static void main(String[] args) {
        int[] val={1500,3000,2000};//物品的价值(序号从零开始)
        int[] w={1,4,3};//物品的重量(序号从零开始)
        int m=4;//背包的最大容量
        int[][] v=new int[val.length+1][m+1];//存放前i个物品时的总重量j(new 的时候默认为0)
        int[][] path=new int[val.length+1][m+1];

        //默认初始化了没有物品v[0][:]=0,初始化没有重量v[:][0]=0
        //用于下面的延续与比较或者继承
        for (int i = 0; i <v.length ; i++) {
            v[i][0]=0;//第一列为零
        }
        for (int j = 0; j < v[0].length; j++) {
            v[0][j]=0;//第一行为0
        }



        for (int i = 1; i <v.length ; i++) {
            //外循环:先遍历完所有的前i个物品的时候
            for (int j = 1; j <v[i].length ; j++) {
               //内循环:遍历所有的背包,即重量
                if(w[i-1]>j){
                    //当前这个物品的重量超过了背包总重量j(继承上一个值)
                    v[i][j]=v[i-1][j];

                }else{

                    //继承上一个值
                    if(v[i-1][j]>val[i-1]+v[i-1][j-w[i-1]]){
                        v[i][j]=v[i-1][j];

                    }else{

                        //针对同一列时,相对于上一行的值有更新,那么就需要path置1:就说明有新的物品放入背包里(最佳值一定会经过)
                        v[i][j]=val[i-1]+v[i-1][j-w[i-1]];
                        path[i][j]=1;
                    }
                }
            }
        }

        System.out.println("_______动态规划表_____");
        for (int i = 0; i <v.length ; i++) {
            for (int j = 0; j < v[i].length; j++) {
                System.out.print(v[i][j]+" ");
            }
            System.out.println();

        }

        System.out.println("_____动态规划有更新的路径_______");
        for (int i = 0; i <path.length ; i++) {
            System.out.println(Arrays.toString(path[i]));
        }


        System.out.println("________最佳的组合结果_________");
        //利用“倒序”,原因最佳值都是最后找到的
        int i= path.length-1;//前i个物品(物品越多越好,价值量越大)
        int j= path[0].length-1;//可允许的最大重量(j越大越好,背包的最大容量)
        while(i>0&&j>0){
            if(path[i][j]==1){
                System.out.println("第"+i+"物品放入背包");
                j=j-w[i-1];//这个很关键,减去当前物品的重量,剩余可装入的物品的重量
            }
            i--;
        }
        
    }
}

在这里插入图片描述

斐波那契数列(动归思想)

在这里插入图片描述

4. 贪心算法(局部最优–>全局最优的近似)——没有套路----刷题去

在这里插入图片描述

在这里插入图片描述

5.KMP算法

(字符串的匹配查找问题carl哥讲解

字符串的前缀和后缀
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最终的部分匹配表:
在这里插入图片描述

字符串匹配问题

字符串匹配的实际案例-----利用前缀表(部分匹配表):
在这里插入图片描述
在这里插入图片描述
构造前缀表的原理没有看懂:利用"前缀表"(可以是整体减1的数组表,据说一共有3种)直接作为next数组(即下面代码的KMPtable)

j回溯的过程就是在递减,也就是在arr[i]与arr[j]不相同,需要重新计算最长相同的前后缀长度,不相同时就继续递减这个j的长度(j代表了最长相同的前后缀长度)。充分理解i与j具体的含义,arr[i]始终代表了当前的前i个字符组成的字符串的最长相同的前后缀长度。(下图是carl哥的手)
在这里插入图片描述

在这里插入图片描述

public class KMPsearchString {
    public static void main(String[] args) {
        String s1="aabaabaafa";
        String s2="aabaaf";
        System.out.println(Arrays.toString(KMPtable(s2)));
        int i = KMPsearch(s1, s2, KMPtable(s2));
        System.out.println(i);


    }

    //KMP字符串搜索匹配(返回匹配到的首个元素的位置)
    public static int KMPsearch(String s1,String s2,int[] KMPtable){
        /**
         * i 相当于是文本串的字符串位置
         * j 相当于是模式串的字符串指针的位置
         */

        for (int i = 0,j=0; i <s1.length() ; i++) {
            //如果初始连第一个字符都没有一直匹配上,此时j始终为0

            if( j>0 && s1.charAt(i)!=s2.charAt(j)){
                j=KMPtable[j-1];//将j移动至不匹配字符的前一个字符的前缀表下标的位置,下回比较时直接从模式串的这个位置开始比较
            }

            if(s1.charAt(i)==s2.charAt(j)){
                j++;
            }

            if(j==s2.length()){//说明匹配上了
                return i-j+1;
            }
        }

        return -1;//没有找到
    }


    //KMP字符串部分匹配表
    public static int[] KMPtable(String des){
        int[] matchTable=new int[des.length()];
        matchTable[0]=0;//字符串的第一个字符没有前缀和后缀
        /**
         * i指向即将判断的“后缀末尾”的位置(此时next[i]即将表示对应的最大公共前后缀的长度)
         * j指向即将判断的“前缀末尾”的位置==也表示最长相等(前缀与后缀之间)的前缀的长度)
         */
        for (int i = 1,j=0; i <des.length() ; i++) {

            while (j>0 && des.charAt(i)!=des.charAt(j)){
                //开始回退j,一直回退到有相同的元素,实际回退的过程就是在递减j
                j=matchTable[j-1];//设置j大于0,防止出现,数组越界
            }

            if(des.charAt(i)==des.charAt(j)){
                j++;
            }

            matchTable[i]=j;
        }
        return matchTable;
    }
}

最小生成树(Prim和Kruskal)

在这里插入图片描述

Prim算法

解决修路问题:
在这里插入图片描述

Kruskal算法(排序+不允许构成回路)

在这里插入图片描述

公交站问题:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如何判断新加入的边后有无形成回路:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值