【自我救赎--牛客网Top101 4天刷题计划】 第二天 强化训练

第二天

声明:本系列文章仅适合二刷有经验的算法er学习,以后会出详细的每一题的讲解,这里只是简单的说明思路来帮助大家快速刷完Top101,另外博主的算法全程跟着 labuladong 大佬学习,这里特此声明

11.链表相加

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zODYert-1650684403418)(picture/11.png)]

解题思路

  1. 首先将两个链表反转,这样符合加法的规范,即从个位开始相加
  2. 加法做完之后得到的结果是一个逆序的数字,需要再进行反转将其变为正序

代码实现

public class Solution {
    /**
     * 
     * @param head1 ListNode类 
     * @param head2 ListNode类 
     * @return ListNode类
     */
    public ListNode addInList (ListNode head1, ListNode head2) {
        // 进行判空处理
        if(head1 == null)
            return head2;
        if(head2 == null){
            return head1;
        }
        // 反转h1链表
        head1 = reverse(head1);
        // 反转h2链表
        head2 = reverse(head2);
        // 创建新的链表头节点
        ListNode head = new ListNode(-1);
        ListNode nHead = head;
        // 记录进位的数值
        int tmp = 0;
        while(head1 != null || head2 != null){
            // val用来累加此时的数值(加数+加数+上一位的进位=当前总的数值)
            int val = tmp;
            // 当节点不为空的时候,则需要加上当前节点的值
            if (head1 != null) {
                val += head1.val;
                head1 = head1.next;
            }
            // 当节点不为空的时候,则需要加上当前节点的值
            if (head2 != null) {
                val += head2.val;
                head2 = head2.next;
            }
            // 求出进位
            tmp = val/10;
            // 进位后剩下的数值即为当前节点的数值
            nHead.next = new ListNode(val%10);
            // 下一个节点
            nHead = nHead.next;

        }
        // 最后当两条链表都加完的时候,进位不为0的时候,则需要再加上这个进位
        if(tmp > 0){
            nHead.next = new ListNode(tmp);
        }
        // 重新反转回来返回
        return reverse(head.next);
    }

    // 反转链表
    ListNode reverse(ListNode head){
        if(head == null)
            return head;
        ListNode cur = head;
        ListNode node = null;
        while(cur != null){
            ListNode tail = cur.next;
            cur.next = node;
            node = cur;
            cur = tail;
        }
        return node;
    }
}

12.单链表的排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JeiASmg-1650684403419)(picture/12.png)]

解题思路

把链表每个节点的值都取出来存到一个数组之中,然后对数组进行排序,之后按照顺序把值分别赋给原链表即可

代码实现

public class Solution {
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList(ListNode head) {
        // write code here
        ArrayList<Integer> arrayList = new ArrayList<>();
        ListNode p = head;
        while (p != null) {
            arrayList.add(p.val);
            p = p.next;
        }
        p = head;
        Collections.sort(arrayList);
        for (int i = 0; i < arrayList.size(); i++) {
            p.val = arrayList.get(i);
            p = p.next;
        }
        return head;
    }
}

13.判断链表是否是回文结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2buZKrc-1650684403419)(picture/13.png)]

解题思路

  1. 使用双指针先找到链表的中点(如果链表长度是奇数,则 slow 指针还需要在前进一位)
  2. 反转以 slow 开头的链表
  3. 使用两个新指针分别指向链表头部和尾部,当尾部指针不为 null 时每次对比两个指针指向的节点值是否相同,不同则立刻退出,直到尾部指针为 null 时返回 true

代码实现

public class Solution {
    /**
     * 
     * @param head ListNode类 the head
     * @return bool布尔型
     */
    public boolean isPail(ListNode head) {
        // write code here
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }

        //fast不为null说明长度为奇数,fast需要再往后走一位
        if (fast != null) {
            fast = fast.next;
        }

        ListNode left = head;
        ListNode right = reverse(slow);
        while (right != null) {
            if (left.val != right.val) {
                return false;
            }
            left = left.next;
            right = right.next;
        }
        return true;
    }

    public ListNode reverse(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode last = reverse(head.next);
        head.next.next = head;
        head.next = null;
        return last;
    }
}

14.链表的奇偶重排

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31jJ6RZK-1650684403420)(picture/14.png)]

解题思路

  1. 用双指针 odd 和 even 分别遍历奇数节点和偶数节点,并给偶数节点一个头结点
  2. 每轮循环使用 even 来判断其后两个元素是否为 null
  3. 将偶数节点连接在奇数节点后

代码实现

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode oddEvenList(ListNode head) {
        // write code here
        if (head == null) {
            return head;
        }

        ListNode odd = head, even = head.next, evenHead = even;
        while (even != null && even.next != null) {
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;
        }
        odd.next = evenHead;
        return head;
    }
}

15.删除有序链表的重复元素-1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-viSFofOJ-1650684403420)(picture/15.png)]

解题思路

双指针,如果快指针的值不等于慢指针的值,让慢指针的下一个节点的值被快指针指向节点的值覆盖,否则快指针 +1就行,最后记得吧慢指针后面的节点断开

代码实现

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode deleteDuplicates(ListNode head) {
        // write code here
        if (head == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        while (fast != null) {
            if (slow.val != fast.val) {
                slow.next = fast;
                slow = slow.next;
            }
            fast = fast.next;
        }
        slow.next = null;
        return head;
    }
}

16.删除有序链表的重复元素-2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MgpPi7Jo-1650684403420)(picture/16.png)]

解题思路

  1. 设置虚拟头结点 dummy 防止第一个节点被删除报错的问题发生
  2. 用一个指针遍历链表,如果相邻两个位置的节点的值是相等的,记录下这个值 temp
  3. 进入最里层的 while 循环判断当前节点的下一个节点值是否等于 temp ,如果等于就再前进一步,直到不等于
  4. 返回虚拟头结点的下一个节点即可

代码实现

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode deleteDuplicates(ListNode head) {
        // write code here
        if (head == null) {
            return null;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode p = dummy;
        while (p.next != null && p.next.next != null) {
            if (p.next.val == p.next.next.val) {
                int temp = p.next.val;
                while (p.next != null && p.next.val == temp) {
                    p.next = p.next.next;
                }
            } else {
                p = p.next;
            }
        }
        return dummy.next;
    }
}

18.二维数组中的查找

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WyGLsXB6-1650684403421)(picture/18.png)]

解题思路

板子题,建议记一下

代码实现

public class Solution {
    public boolean Find(int target, int[][] array) {
        if (array.length == 0) {
            return false;
        }
        int n = array.length;

        if (array[0].length == 0) {
            return false;
        }
        int m = array[0].length;

        for (int i = n - 1, j = 0; i >= 0 && j < m; ) {
            if (array[i][j] > target) {
                i--;
            } else if (array[i][j] < target) {
                j++;
            } else {
                return true;
            }
        }
        return false;
    }
}

19.寻找峰值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fECm6IG-1650684403421)(picture/19.png)]

解题思路

  1. 二分查找首先从数组首尾开始,每次取中间值,直到首尾相遇
  2. 如果中间值大于等于其右边的元素,说明往右是向下,不会遇到波峰,应该往左
  3. 如果中间值大于右边的元素,此时往右是向上的,一定会遇到波峰

代码实现

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int findPeakElement(int[] nums) {
        // write code here
        if (nums == null || nums.length == 0) {
            return -1;
        }

        int n = nums.length;
        int lo = 0, hi = n - 1;
        while (lo < hi) {
            int mid = lo + (hi - lo) / 2;
            if (nums[mid] > nums[mid + 1]) {
                hi = mid;
            } else {
                lo = mid + 1;
            }
        }
        return hi;
    }
}

20.数组中的逆序对

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uu4aqxOR-1650684403421)(picture/20.png)]

解题思路

求逆序对就是统计有多少个前大后小的数对,问题和归并两个有序数组求前大后小数对一样。所以现在的问题变为统计子问题中逆序对的个数。

代码实现

public class Solution {
    
    int[] nums,temp;
  
    public int merge_sort(int l,int r){
        if(l>=r)return 0;
        int m, i, j, k;
        long res;
        
        m = (l+r)/2;
        res = (merge_sort(l,m)+merge_sort(m+1,r))%1000000007;
        
        //merge
        for(k=l;k<=r;k++)temp[k]=nums[k];
        i = l;j = m+1;k = l;
        for(k=l;k<=r;k++){
            if(i==m+1)nums[k]=temp[j++];
            else if(j==r+1||temp[i]<=temp[j])nums[k] = temp[i++];
            else {
                res=(res + m-i+1)%1000000007;
                nums[k]=temp[j++];
            }
        }
        return (int)res;
    }
   
    public int InversePairs(int [] array) {
        nums = array;
        temp = new int[array.length];
        return merge_sort(0,array.length-1);
    }
}

21.旋转数组的最小数字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AStkV8wV-1650684403421)(picture/21.png)]

解题思路

反正就是打乱一个有序数组,直接遍历求最值也行,不过考虑到时间复杂度,还是需要用到二分的思想

  1. 双指针指向旋转后数组的首位,作为区间的端点
  2. 若区间中点值大于右界值,则最小的数字一定在中点右边
  3. 若是区间中点值等于区间右界值,则应逐个缩减右界
  4. 若是区间中点值小于右界值,则最小的数字一定在中点左边

代码实现

public class Solution{
    public int minNumberInRotateArray(int[] array){
        int left = 0;
        int right = array.length-1;
        if(array[mid] > array[right]){
            left = mid + 1;
        }else if(array[mid] == array[right]){
            right--;
        }else{
            right = mid;
        }
        return array[left];
    }
}

22.比较版本号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFUJ9PcK-1650684403422)(picture/22.png)]

解题思路

  1. 使用 split 函数按照“ . ”将两个原始的字符串分割,使每个修订号的数字单独呈现在数组中
  2. 遍历数组,每次各自取出一个数字比较,较短的版本号没有可取的数字就直接取0
  3. 遍历取出的数字字符串,将其转换成数字,然后比较数字大小,根据大小关系返回1或者-1,如果全部比较完都无法比较出大小,则返回0

代码实现

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 比较版本号
     * @param version1 string字符串 
     * @param version2 string字符串 
     * @return int整型
     */
    public int compare(String version1, String version2) {
        // write code here
        String[] nums1 = version1.split("\\.");
        String[] nums2 = version2.split("\\.");
        for (int i = 0; i < nums1.length || i < nums2.length; i++) {
            String str1 = i < nums1.length ? nums1[i] : "0";
            String str2 = i < nums2.length ? nums2[i] : "0";
            long num1 = 0;
            for (int j = 0; j < str1.length(); j++) {
                num1 = num1 * 10 + (str1.charAt(j) - '0');
            }
            long num2 = 0;
            for (int j = 0; j < str2.length(); j++) {
                num2 = num2 * 10 + (str2.charAt(j) - '0');
            }
            if (num1 > num2) {
                return 1;
            }
            if (num1 < num2) {
                return -1;
            }
        }
        return 0;
    }
}

26.二叉树的层序遍历

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GhjenumA-1650684403422)(picture/26.png)]

解题思路

  1. 如果 res 结果集的大小小于 depth ,说明此时 res 还在上一层,建立数组 row 并将 depth 层的节点的值添加到 row
  2. 否则节点为空直接返回
  3. 然后递归处理子树

代码实现

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @return int整型ArrayList<ArrayList<>>
     */
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();

    public void traverse(TreeNode root, int depth) {
        if (root != null) {
            if (res.size() < depth) {
                ArrayList<Integer> temp = new ArrayList<>();
                res.add(temp);
                temp.add(root.val);
            } else {
                ArrayList<Integer> temp = res.get(depth - 1);
                temp.add(root.val);
            }
        } else {
            return;
        }

        traverse(root.left, depth + 1);
        traverse(root.right, depth + 1);
    }

    public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
        // write code here
        if (root == null) {
            return res;
        }
        traverse(root, 1);
        return res;
    }
}

27.按之字形打印二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1oUN2sPy-1650684403422)(picture/27.png)]

解题思路

设置一个标志位,通过每遍历一层就取反的方式来判断是否将该层的节点的值反转

代码实现

public class Solution {
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();

    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        TreeNode head = pRoot;
        if (head == null) {
            return res;
        }
        //辅助队列,帮助层序遍历
        Queue<TreeNode> temp = new LinkedList<>();
        temp.offer(head);
        TreeNode p;
        Boolean flag = true;
        while (!temp.isEmpty()) {
            int n = temp.size();
            //存储一行的节点的值
            ArrayList<Integer> row = new ArrayList<>();
            flag = !flag;
            for (int i = 0; i < n; i++) {
                p = temp.poll();
                row.add(p.val);
                if (p.left != null) {
                    temp.offer(p.left);
                }
                if (p.right != null) {
                    temp.offer(p.right);
                }
            }

            if (flag) {
                Collections.reverse(row);
            }
            res.add(row);
        }
        return res;
    }

}

28.二叉树的最大深度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-daOwEhy4-1650684403423)(picture/28.png)]

解题思路

记录左孩子的深度,记录右孩子的深度,返回 max(左孩子的深度,右孩子的深度)+ 根节点的深度(1)

代码实现

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     */
    public int maxDepth(TreeNode root) {
        // write code here
        if (root == null) {
            return 0;
        }

        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

29.二叉树和为某一值得路径-1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isP2GZ0E-1650684403423)(picture/29.png)]

解题思路

写个出口条件,当左孩子为空,有孩子为空,且 sum 与路径和的差为 0 时,返回 true , 如果遇到 root 为 null 的情况,返回 false

代码实现

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
    public boolean hasPathSum(TreeNode root, int sum) {
        // write code here
        if (root == null) {
            return false;
        }
        if (root.left == null && root.right == null && sum - root.val == 0) {
            return true;
        }
        return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
    }
}

42.用两个栈实现队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ffFDUjM-1650684403423)(picture/42.png)]

解题思路

  1. 入栈的话就正常入栈
  2. 出栈的话先将元素放在另一个栈中过一遍,达到一个逆序的效果,然后将栈顶元素出栈,之后将未出栈的元素重新压回最开始的栈,回复顺序,为下次出栈做好准备

代码实现

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);
    }

    public int pop() {
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        int res = stack2.pop();
        while (!stack2.isEmpty()) {
            stack1.push(stack2.pop());
        }
        return res;
    }
}

43.包含min的栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FiS7kjjQ-1650684403423)(picture/43.png)]

解题思路

依然是双栈法,第一个栈正常进出和查看栈顶,第二个栈专门用来存储min

代码实现

public class Solution {

    
    Stack<Integer> s1 = new Stack<>();
    Stack<Integer> s2 = new Stack<>();

    public void push(int node) {
        s1.push(node);
        if (s2.isEmpty() || s2.peek() > node) {
            s2.push(node);
        } else {
            s2.push(s2.peek());
        }
    }

    public void pop() {
        s1.pop();
        s2.pop();
    }

    public int top() {
        return s1.peek();
    }

    public int min() {
        return s2.peek();
    }
}

44.有效括号序列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-424nET4e-1650684403423)(picture/44.png)]

解题思路

当遍历到‘(’‘【’‘{’的时候往栈中压入其对应的另一半,当栈为空或者出栈的括号与 s 对应位置上的不一致,就返回 false ,否则最后再判断一次栈是否非空, 空了就返回 true ,不空说明有多余的括号,依然要返回 false

代码实现

public class Solution {
    /**
     * 
     * @param s string字符串 
     * @return bool布尔型
     */
    Stack<Character> stack = new Stack<>();

    public boolean isValid(String s) {
        // write code here
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push(')');
            } else if (s.charAt(i) == '[') {
                stack.push(']');
            } else if (s.charAt(i) == '{') {
                stack.push('}');
            } else if (stack.isEmpty() || stack.pop() != s.charAt(i)) {
                return false;
            }
        }
        return stack.isEmpty();
    }
}

66.最长公共子串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rY1iBFrX-1650684403424)(picture/66.png)]

解题思路

  1. dp[i][j] 表示 str1[…i] 和 str2[…j] 的公共子串长度
  2. 状态转移
    • dp[i][j] = dp[i - 1][j - 1] + 1;
    • dp[i][j] = 0;
  3. 最后遍历dp数组找出一个最大值返回即可

代码实现

public class Solution {
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    public String LCS(String str1, String str2) {
        // write code here
        int m = str1.length(), n = str2.length();
        //str1[...i] , str2[...j]两个字符串的最长公共子串是 dp[i][j]
        int[][] dp = new int[m + 1][n + 1];
        //维护一个截取位置的结尾
        int pos = 0;
        //维护一个最大值
        int max = 0;
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = 0;
                }

                if (dp[i][j] > max) {
                    max = dp[i][j];
                    pos = i - 1;
                }
            }
        }
        return str1.substring(pos - max + 1, pos + 1);
    }
}

67.不同路径的数目-1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tBZ6Y73r-1650684403424)(picture/67.png)]

解题思路

简单题,写出状态转移方程就结束了

代码实现

public class Solution {
    /**
     * 
     * @param m int整型 
     * @param n int整型 
     * @return int整型
     */
    public int uniquePaths(int m, int n) {
        // write code here
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                //base case
                //处理边缘条件,第一行和第一列只能有一种到达办法
                if (i == 1 || j == 1) {
                    dp[i][j] = 1;
                    continue;
                }

                dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
            }
        }
        return dp[m][n];
    }
}

68.矩阵的最小路径和

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MBF0EJF8-1650684403424)(picture/68.png)]

解题思路

注意 dp[0][0]和第一行,第一列都需要经过特殊处理,然后剩下的就是常规的dp

代码实现

public class Solution {
    /**
     * 
     * @param matrix int整型二维数组 the matrix
     * @return int整型
     */
    public int minPathSum(int[][] matrix) {
        // write code here
        int n = matrix.length;
        int m = matrix[0].length;
        int[][] dp = new int[n + 1][m + 1];
        //base case
        dp[0][0] = matrix[0][0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = matrix[i][0] + dp[i - 1][0];
        }
        for (int j = 1; j < m; j++) {
            dp[0][j] = matrix[0][j] + dp[0][j - 1];
        }

        for (int i = 1; i < n; i++) {
            for (int j = 1; j < m; j++) {
                dp[i][j] = matrix[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[n - 1][m - 1];
    }
}

69.把数字翻译成字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VrmLjsrX-1650684403424)(picture/69.png)]

解题思路

此题对于面试意义不大,博主就看看思路就过了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8HMk5of0-1650684403425)(picture/%E2%80%94%E2%80%940.png)]

代码实现(搬运)

public class Solution {
    /**
     * 解码
     * @param nums string字符串 数字串
     * @return int整型
     */
    public int solve(String nums) {
        // write code here
        if (nums.equals("0")) {
            return 0;
        }
        if (nums.equals("10") || nums.equals("20")) {
            return 1;
        }
        for (int i = 1; i < nums.length(); i++) {
            if (nums.charAt(i) == '0') {
                if (nums.charAt(i - 1) != '1' && nums.charAt(i - 1) != '2') {
                    return 0;
                }
            }
        }

        int[] dp = new int[nums.length() + 1];
        Arrays.fill(dp, 1);
        for (int i = 2; i <= nums.length(); i++) {
            if ((nums.charAt(i - 2) == '1' && nums.charAt(i - 1) != '0') ||
                    (nums.charAt(i - 2) == '2' && nums.charAt(i - 1) > '0' && nums.charAt(i - 1) < '7')) {
                dp[i] = dp[i - 1] + dp[i - 2];
            } else {
                dp[i] = dp[i - 1];
            }
        }
        return dp[nums.length()];
    }
}

70.兑换零钱

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4K9SZSj-1650684403425)(picture/70.png)]

解题思路

背包问题前缀题,很经典,建议背下来

代码实现

public class Solution {
    /**
     * 最少货币数
     * @param arr int整型一维数组 the array
     * @param aim int整型 the target
     * @return int整型
     */
    public int minMoney(int[] arr, int aim) {
        // write code here
        if (aim < 1) //⼩于1的都返回0
            return 0;
        int[] dp = new int[aim + 1];
        Arrays.fill(dp, aim + 1); //dp[i]表示凑⻬i元最少需要多少货币数
        dp[0] = 0;
        for (int i = 1; i <= aim; i++) { //遍历1-aim元
            for (int j = 0; j < arr.length; j++) { //每种⾯值的货币都要枚举
                if (arr[j] <= i) //如果⾯值不超过要凑的钱才能⽤
                    dp[i] = Math.min(dp[i], dp[i - arr[j]] + 1); //维护最⼩值
            }
        }
        return dp[aim] > aim ? -1 : dp[aim]; //如果最终答案⼤于aim代表⽆解
    }
}

71.最长上升子序列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHuwKUag-1650684403425)(picture/71.png)]

解题思路

  1. 以 arr[…i] 结尾的最长上升子序列的长度为dp[i]
  2. 通过遍历的方式找到当前以 i 结尾的数组的最长子序列
  3. 遍历 dp 拿到最大值并返回

代码实现

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS(int[] arr) {
        // write code here
        //以 arr[...i] 结尾的最长上升子序列的长度为dp[i]
        int n = arr.length;
        int[] dp = new int[n];
        //base case
        Arrays.fill(dp, 1);

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (arr[j] < arr[i]) {
                    dp[i] = Math.max(dp[i], 1 + dp[j]);
                }
            }
        }

        int res = 0;
        for (int i = 0; i < dp.length; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

72.连续子数组的最大和

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uVPxyeWo-1650684403425)(picture/72.png)]

解题思路

  1. 以下标i结尾的子数组的最大和为dp[i]
  2. 每次遇到新的元素要么是它本身,要么是它加上前面的最大和,取一个max
  3. 遍历数组找到最大值

代码实现

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int n = array.length;
        //以下标i结尾的子数组的最大和为dp[i]
        int[] dp = new int[n];
        //base case
        dp[0] = array[0];

        for (int i = 1; i < n; i++) {
            dp[i] = Math.max(array[i], dp[i - 1] + array[i]);
        }

        int res = dp[0];
        for (int i = 0; i < dp.length; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值