算法刷题-动态规划21题+链表9题-2025年02月25日上午

import uitls.Node;
import uitls.TreeNode;

import java.util.Arrays;
import java.util.Stack;

public class test_02_25 {
    //动态规划21题
    //爬楼梯-动态规划
    public static int test6(int n){
        //建立dp数组
        int[] dp= new int[n];
        //初始化dp数组
        dp[0]=1;
        dp[1]=2;
        //进行状态转移
        for (int i=2;i<n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n-1];
    }
    //爬楼梯-采取动态规划O(1)
    public static int test7(int n){
        int a=1;
        int b=2;
        for (int i=3;i<=n;i++){
            int temp=a;
            a=b;
            b=temp+b;
        }
        return b;
    }
    //爬楼梯-递归
    public static int test8(int n){
        if (n==1){
            return 1;
        }
        if (n==2){
            return 2;
        }
        return test7(n-1)+test7(n-2);
    }

    //路径问题
    public static int test9(int[][] arr){
        int n=arr.length;
        int m=arr[0].length;
        //建立dp数组
        int[][] dp=new int[n][m];

        //初始化dp数组
        for (int i=0;i<n;i++){
            dp[i][0]=1;
        }
        for (int i=0;i<m;i++){
            dp[0][i]=1;
        }
        //进行状态转移
        for (int i=1;i<n;i++){
            for (int j=1;j<m;j++){
                //只能从上面或者从左边走过来
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[n-1][m-1];
    }

    //不同路径-存在障碍物
    public static int test10(int[][] arr){
        int n=arr.length;
        int m=arr[0].length;
        //建立dp数组
        int[][] dp=new int[n][m];
        //初始化dp数组
        dp[0][0]=1-arr[0][0];
        if (dp[0][0]==1){
            return 0;
        }
        //进行状态转移
        for (int i=0;i<n;i++){
            for (int j=0;j<m;j++){
                //判断一下是不是第一行
                if (i==0&&j>0){
                    //处理第一行
                    if (arr[0][j]==1){
                        dp[0][j]=0;
                    }else{
                        dp[0][j]=dp[0][j-1];
                    }
                }else if (i>0&&j==0){
                    //处理第一列
                    if (arr[i][0]==1){
                        dp[i][0]=0;
                    }else {
                        dp[i][0]=dp[i-1][0];
                    }
                }else if (i>0&&j>0){
                    //不处理原点 非第一行且非第一列
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
        }
        return dp[n-1][m-1];
    }
    //最小路径和
    public static int test11(int[][] grid){
        int n=grid.length;
        int m=grid[0].length;
        //建立dp数组
        int[][] dp=new int[n][m];
        //初始化dp数组
        dp[0][0]=grid[0][0];
        //状态转移
        for (int i=0;i<n;i++){
            for (int j=0;j<m;j++){
                //处理第一行
                if (i==0&&j>0){
                    dp[0][j]=dp[0][j-1]+grid[i][j];
                }else if (j==0&&i>0){
                    //处理第一列
                    dp[i][0]=dp[i-1][0]+grid[i][j];
                }else if (i>0&&j>0){
                    dp[i][j]=dp[i-1][j]+dp[i][j-1]+grid[i][j];
                }
            }
        }
        return dp[n-1][m-1];
    }
    //三角形的最小路径和 找出最小的路径之和-只能由自己的上面或者左上角
    //2
    //3 4
    //6 5 7
    //4 1 8 3
    public static int test12(int[][] arr){
        int n=arr.length;
        int m=arr[0].length;
        //建立dp数组
        int[][] dp= new int[n][m];

        dp[0][0]=arr[0][0];

        //进行状态转移 只要求下三角就可以
        for (int i=0;i<n;i++){
            for (int j=0;j<=i;j++){
                //进行判断 第一行
                if (i>0&&j==0){
                    dp[i][j]=dp[i-1][j]+arr[i][j];
                }else if (i==j&&i>0&&j>0){
                    //对角线 保证不是第一个值
                    dp[i][j]=dp[i-1][j-1]+arr[i][j];
                }else if (i>0&&j>0&&j<i){
                    dp[i][j]=Math.min(dp[i-1][j-1],dp[i-1][j])+arr[i][j];
                }
            }
        }
        int minValue = dp[n-1][0];
        for (int i=1;i<m;i++){
            if (dp[n-1][i]<minValue){
                minValue=dp[n-1][i];
            }
        }
        return minValue;
    }

    //买卖股票[7 1 5 3 6 4]
    public static int test13(int[] arr){
        int n=arr.length;
        //建立dp数组 一个针对于卖出 一个买入
        int[][] dp=new int[n][2];
        //初始化dp数组
        //卖出的利润
        dp[0][0]=0;
        //买入的利润
        dp[0][1]=-arr[0];

        //建立状态转移方程
        for (int i=1;i<n;i++){
            //卖出 则为上次买入价格+这次卖出价格,上次卖出价格
            dp[i][0] = Math.max(dp[i-1][1]+arr[i],dp[i-1][0]);
            //买入 这次买入和上次买入的最小值
            dp[i][1]=Math.max(-arr[i],dp[i-1][1]);
        }
        return dp[n-1][0];
    }

    //买卖股票I-优化-采取
    public static int test14(int[] arr){
        //采取两个变量
        int a = 0;
        int b=-arr[0];
        //进行状态转移
        for (int i=1;i<arr.length;i++){
            //卖出
            a=Math.max(b+arr[i],a);
            //买入
            b=Math.max(-arr[i],b);
        }
        return a;
    }
    //买卖股票的最佳时机II-无线次交易
    public static int test15(int[] arr){
        int sum=0;
        for (int i=0;i<arr.length-1;i++){
            if (arr[i+1]>arr[i]){
                sum+=arr[i+1]-arr[i];
            }
        }
        return sum;
    }
    //买卖股票的最佳时机II-无线次交易-采取状态机
    public static int test16(int[] arr){
        int[][] dp= new int[arr.length][2];
        dp[0][0]=0;
        dp[0][1]=-arr[0];
        for (int i=1;i<arr.length;i++){
            //卖出的利润
            //昨天买入的利润+今天卖出的钱,昨天卖出的利润
            dp[i][0]=Math.max(dp[i-1][1]+arr[i],dp[i-1][0]);
            //买入的利润
            //昨天卖出的利润-今天买入的钱,昨天买入的利润
            dp[i][1]=Math.max(dp[i-1][0]-arr[i],dp[i-1][1]);
        }
        return dp[arr.length-1][0];
    }
    //买卖股票的最佳时机III
    public static void test17(int[] arr){
        //只有五种状态
        //1:一笔都没有买入 2:买入一笔 没有卖出 3:买入一笔并卖出一笔 4:买入两笔,卖出一笔 5:买入两笔并卖出
        int n=arr.length;
        int[][] dp=new int[n][5];

        //2:进行初始化
        dp[0][0]=0;
        dp[0][1]=-arr[0];
        //保证dp[i][2]=Math.max(dp[i-1][1]+arr[i],dp[i-1][2]);这里的状态能正确转移
        dp[0][2]=-arr[0];
        dp[0][3]=0;
        dp[0][4]=0;

        //3:进行状态转移
        for (int i=1;i<n;i++){
            //买入一笔
            dp[i][1]=Math.max(-arr[i],dp[i-1][1]);
            //买入一笔并卖出
            dp[i][2]=Math.max(dp[i-1][1]+arr[i],dp[i-1][2]);
            //买入两笔,卖出一笔
            dp[i][3]=Math.max(dp[i][2]-arr[i],dp[i-1][3]);
            //买入两笔并卖出
            dp[i][4]=Math.max(dp[i][3]+arr[i],dp[i-1][4]);
        }
    }
    //TODO 买卖股票-可以交易K次


    //打家劫舍问题[1,2,3,1]
    public static void test19(int[] arr){
        int n=arr.length;
        //建立dp数组
        int[] dp=new int[n];

        //初始化dp数组
        dp[0]=arr[0];
        dp[1]=Math.max(dp[0],arr[1]);
        //进行状态转移
        for (int i=2;i<n;i++){
            dp[i]=Math.max(arr[i]+dp[i-2],dp[i-2]);
        }
    }

    //打家劫舍-首尾相连
    public static int test20(int[] arr){
        int n=arr.length-1;
        int[] dp=new int[n];
        dp[0]=arr[0];
        dp[1]=Math.max(dp[0],arr[1]);
        //进行状态转移
        for (int i=2;i<n;i++){
            dp[i]=Math.max(dp[i-2]+arr[i],dp[i-1]);
        }

        dp[0]=arr[1];
        dp[1]=Math.max(dp[0],arr[2]);
        int x=dp[n-1];
        for (int i=2;i<n;i++){
            dp[i]=Math.max(dp[i-2]+arr[i+1],dp[i-1]);
        }
        return Math.max(x, dp[n-1]);
    }

    //打家劫舍问题III-就是不能同时盗窃子父两个房子
    public static int[] test21(TreeNode root){
        if (root==null){
            return new int[]{0,0};
        }

        int[] left = test21(root.getLeft());
        int[] right = test21(root.getLeft());

        return new int[]{left[1]+right[1]+root.getData(),
                Math.max(left[0],left[1])+Math.max(right[0],right[1])};
    }

    //回文子串-统计有多少个回文子串-一个串中有多少个回文子串
    public static int test22(String s){
        int n = s.length();
        int[][] dp = new int[n][n];
        for (int i=0;i<n;i++){
            dp[i][i]=1;
        }
        //从对角线到左上角
        for (int k=0;k<n;k++){
            int i=0;
            while (i+k+1<n){
                //是回文串
                if (test23(s, i, i+k+1)){
                    dp[i][i+k+1]=dp[i][i+k]+dp[i+1][i+k+1]-1+1;
                }else{
                    dp[i][i+k+1]=dp[i][i+k]+dp[i+1][i+k+1]-1;
                }
                i++;
            }
        }
        return dp[0][n-1];
    }
    //判断是不是回文子串
    public static boolean test23(String s, int i, int j){
        while (i<j){
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
            i++;
            j--;
        }
        return true;
    }

    //最长回文子串-暴力解法
    public static String test24(String s){
        int n=s.length();
        //dp[i]表示以i结尾的最长回文子串大小
        int[] dp=new int[n];
        //找出最长回文子串
        for (int i=0;i<n;i++){
            int max=1;
            for (int j=i;j>=0;j--){
                //是回文子串
                if (test23(s,j,i)){
                    max=i-j+1;
                }
            }
            dp[i]=max;
        }
        int max=1;
        int maxIndex = 0;
        for (int i=1;i<n;i++){
            if (dp[i]>max){
                max=dp[i];
                maxIndex=i;
            }
        }
        return s.substring(maxIndex-max,maxIndex+1);
    }
    //最长回文子串-动态规划
    public static String test25(String s){
        int n=s.length();
        //建立dp数组
        boolean[][] dp = new boolean[n][n];

        //初始化dp数组
        for (int i=0;i<n;i++){
            dp[i][i]=true;
        }
        for (int k=1;k<n;k++){
            int i=0;
            if (s.charAt(i)==s.charAt(i+k)&&k==1){
                //第二个对角
                dp[i][i+k]=true;
            }else if (s.charAt(i)!=s.charAt(i+k)&&k==1){
                dp[i][i+k]=false;
            }else if (s.charAt(i)==s.charAt(i+k)&&k!=1){
                dp[i][i+k]=dp[i+1][i+k-1];
            }else if (s.charAt(i)!=s.charAt(i+k)&&k!=1){
                dp[i][i+k]=false;
            }
        }
        int max = 1;
        int maxIndex = 0;
        for (int i=0;i<n;i++){
            for (int j=i;j<n;j++){
                if (dp[i][j]&&j-i+1>max){
                    max=j-i+1;
                    maxIndex=j;
                }
            }
        }
        return s.substring(maxIndex-max,maxIndex+1);
    }

    //最长回文子序列
    public static int test26(String s){
        int n=s.length();
        //建立dp数组
        int[][] dp=new int[n][n];
        for (int i=0;i<n;i++){
            dp[i][i]=1;
        }
        for (int i=0;i<n-1;i++){
            if (s.charAt(i)==s.charAt(i+1)){
                dp[i][i+1]=2;
            }else{
                dp[i][i+1]=1;
            }
        }

        for (int k=2;k<n;k++){
            int i=0;
            while (i+k<n){
                //相等
                if (s.charAt(i)==s.charAt(i+k)){
                    dp[i][i+k]=dp[i+1][i+k-1];
                }else{
                    dp[i][i+k]=Math.max(dp[i][i+k-1],dp[i+1][i+k]);
                }
                i++;
            }
        }
        return dp[0][n-1];
    }

    //最大子序和
    public static int test27(int[] nums){
        int n = nums.length;
        //建立dp数组
        int[] dp=new int[n];
        dp[0]=nums[0];
        //进行状态转移
        for (int i=1;i<n;i++){
            //找出以这个结尾的最大和
            dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
        }
        return dp[n-1];
    }
    //最大子序和-采取O(1)的时间复杂度
    public static int test28(int[] nums){
        //存储上一个数的最大和
        int a= Math.max(0,nums[0]);
        //存储选择上一个数或者
        int b = nums[0];
        for (int i=1; i<nums.length;i++){
            //以这个结尾的最大和
            b=Math.max(b+nums[i],nums[i]);
            //从a和b中找出最大值
            a=Math.max(b,a);
        }
        return a;
    }
    //最长递增子序列
    public static int test29(int[] nums){
        int n=nums.length;
        //建立dp数组
        int[] dp=new int[n];
        //初始化dp数组
        dp[0]=1;
        //进行状态转移
        for (int i=1;i<n;i++){
            int max=1;
            //从后往前找出最大的
            for (int j=i-1;j>=0;j--){
                if (nums[i]>nums[j]){
                    max=Math.max(dp[j]+1,max);
                }
            }
            dp[i]=max;
        }
        return Arrays.stream(dp).max().getAsInt();
    }

    //乘积最大的子数组-连续的
    public static int test30(int[] nums){
        int n=nums.length;
        //找出乘积最大的连续子数组

        //建立dp数组 dp[i]表示以这个结尾的最大乘积
        int[] dp=new int[n];

        //初始化dp数组
        dp[0]=nums[0];
        //状态转移
        for (int i=1;i<n;i++){
            dp[i]=Math.max(dp[i-1]*nums[i],nums[i]);
        }
        return Arrays.stream(dp).max().getAsInt();
    }

    //最长重复子数组-看左上角
    public static int test31(int[] A, int[] B){
        int n=A.length;
        int m=B.length;
        //建立dp数组
        int[][] dp= new int[n][m];
        //初始化dp数组
        for (int i=0;i<n;i++){
            if (A[i]==B[0]){
                dp[i][0]=1;
            }else{
                dp[i][0]=0;
            }
        }
        for (int i=0;i<m;i++){
            if (A[0]==B[i]){
                dp[0][i]=1;
            }else{
                dp[0][i]=0;
            }
        }
        //进行状态转移
        for (int i=1; i<n;i++){
            for (int j=1;j<m;j++){
                if (A[i]==B[j]){
                    dp[i][j]=Math.max(dp[i-1][j-1]+1,Math.max(dp[i-1][j],dp[i][j-1]));
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[n-1][m-1];
    }

    //爬楼梯
    public static int test32(int n){
        //建立dp数组
        int[] dp= new int[n];
        //初始化dp数组
        dp[0]=1;
        dp[1]=2;
        //进行状态转移
        for (int i=2;i<n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n-1];
    }

    //最大正方形-找出只包含1的正方形-暴力破解法
    public static int test33(int[][] arr){
        int n = arr.length;
        int m=arr[0].length;

        //找出最小的边
        int z = Math.min(n,m);//z=4 n=4 m=5
        while (z-->0){
            //进行求解 不能到边界
            for (int i=0;i<n-z;i++){
                for (int j=0;j<m-z;j++){
                    boolean b = test34(arr, i, j, z);
                    //如果找到了
                    if (b){
                        return z;
                    }
                }
            }
        }
        //没有找到
        return -1;
    }
    //判断是不是正方形
    public static boolean test34(int[][] arr, int i, int j, int size){
        for (int m=i;m<i+size;m++){
            for (int n=j;n<j+size;n++){
                if (arr[m][n]!=1){
                    return false;
                }
            }
        }
        return true;
    }

    //采取动态规划
    public static int test35(int[][] arr){
        int n=arr.length;
        int m = arr[0].length;
        int[][] dp=new int[n][m];
        //初始化dp数组
        for (int i=0;i<n;i++){
            dp[i][0]=1;
        }
        for (int i=0;i<m;i++){
            dp[0][i]=1;
        }
        int res = 0;
        //进行状态转移
        for (int i=1;i<n;i++){
            for (int j=1;j<m;j++){
                //表明为1
                if (arr[i][j]==1){
                    dp[i][j]=Math.max(dp[i-1][j],Math.min(dp[i][j-1],dp[i-1][j-1]))+1;
                    res=Math.max(dp[i][j],res);
                }
            }
        }
        return res;
    }

    //接雨水-采取一个栈进行求解
    public static void test36(int[] arr){
        Stack<Integer> stack = new Stack<>();
        int i=0;
        while (arr[i]==0){
            i++;
        }
        //存储第一个节点
        stack.push(i);
        int sum=0;
        for (int j=i+1;j<arr.length;j++){
            //查看当前栈顶元素下标
            Integer peek = stack.peek();
            if (arr[j]>arr[peek]){
                //开始计算雨水
                Integer pop = stack.pop();
                //只有当栈不为空时才计算
                if (!stack.isEmpty()){
                    //栈不为空
                    Integer left = stack.peek();
                    //进行累加
                    sum += (Math.min(arr[j],arr[left])-arr[pop])*(j-left-1);
                    //判断当前元素是否大于当前栈顶元素
                    j--;
                }else{
                    //入栈当前j
                    stack.push(j);
                }
            }else if(arr[j]<arr[peek]){
                //小于 直接入栈
                stack.push(j);
            }

        }
        System.out.println(sum);
    }

    public static void main(String[] args) {
        int i = test33(new int[][]{
                {1, 0, 1, 0, 0},
                {1, 0, 1, 1, 1},
                {1, 1, 1, 1, 1},
                {1, 0, 0, 1, 0}
        });
        System.out.println(i);
    }


    //树递归六种

    //七种排序

    //两种二分-非递归
    public static int test1(int[] nums, int target){
        int left=0;
        int right = nums.length-1;
        while (left<=right){
            int mid = (left+right);
            if (nums[mid]>target){
                right=mid-1;
            }else if(nums[mid]<target){
                left=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }

    public static int test2(int[] nums, int target){
        return test3(nums, 0, nums.length - 1, target);
    }
    public static int test3(int[] nums, int left, int right, int target){
        if (left>right){
            return -1;
        }
        int mid = (left+right)/2;
        if (nums[mid]>target){
            return test3(nums, left, mid-1, target);
        }else if (nums[mid]<target){
            return test3(nums, mid+1, right, target);
        }else{
            return mid;
        }
    }

    //链表反转-两题
    public static Node test4(Node head){
        Node pre=null;
        Node p=head;
        Node pp = head.getNext();
        while (pp!=null){
            p.setNext(pre);
            pre=p;
            p=pp;
            pp=pp.getNext();
        }
        p.setNext(pre);
        return p;
    }
    //链表反转递归
    public static Node test5(Node head){
        //如果判定到倒数第一个节点 则直接返回这个节点 作为头节点
        if (head.getNext()==null){
            return head;
        }
        //返回一个新的头节点 返回最后递归的一个节点
        Node newHead = test5(head.getNext());
        head.getNext().setNext(head);
        //将这个节点设置为null
        head.setNext(null);
        return newHead;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值