挑灯夜读——HW笔试算法总结(Java版)

24 篇文章 0 订阅
11 篇文章 0 订阅

算法题目总结

递归:

  • LeetCode70

青蛙跳楼梯的题目:一共n阶的楼梯需要爬,一次可以爬1或者2个,一共有几种方法。

//思想,就是找到递归的思想,既然只有两种方法,那么跳到终点可能就有可能是从n-1和n-2跳到的,一共就是这两种方法。
//同样的n-1和n-2的点可能是从n-3和n-2以及n-4和n-3来的。
//于是最直接的方法就是递归如下

//递归方法
public static int dump(int n){
    if (n <= 2){
        return n;
    } 
    int a = 1,b = 1,c = 0;
    for (int i = 1;i < n;i ++){
        c = a + b;
        b = a;
        a = c;
    }
    return a;
}
//不好理解,再来.
//可以想象a、b、c依次排列,然后向右滑动,a获得b的值,b获得c,c获得a+b
public static int dump(int n){
    int a = 0,b = 0,c = 1;
    for (int i = 0;i < n;i ++){
        a = b;
        b = c;
        c = a + b;
    }
    return c;
}
  • 112:路径总和:求二叉树根节点到叶节点有没有和为目标值targetSum的路径存在

这里也是一个递归的方法,这道题一上来的感觉就是很麻烦,算不上难,但是如果直接上手递归的方法,就OK啦。

把所有的节点遍历一遍,只要保证是向下遍历即可,也就是向左右子节点走。且值也需要减掉父节点值。最后的返回的是该节点既无左右子节点,且值还为最终剩余的节点值。

public static void find(ListNode root,int target){
    //先排除root为空
    if (root == null){
        return false;
    }
    if (root.left == null && root.right == null && root.val == target){
        return true;
    }
    //此时返回或组成的两个调用,是因为只要找到一个即可。
    return find(root.left,target - root.val) || find(root.right,target - root.val);
}
  • 509 斐波那契数列

这个算法思想就不用多说了,直接对其进行调用叠加就行。

public static int fib(int n){
    if (n <= 1){
        return n;
    }
    return fib(n-1)+fib(n-2);
}

分治:

  • LeetCode23:合并k个升序链表

本次使用的是分治法,也就是将k个链表分为单个链表进行连接,然后合并。思想有点像:

​ 将10个链表分为5组链表,每组两个,每组先进行排序,然后形成5个更长的链表,然后将五个链表分为3组(有一个无序拼接),就形成了三个更更长的链表,最后形成一个最长的链表。也就结束了。

public ListNode mergerKLists(ListNode[] lists){
    if (lists == null || lists.length == 0){
        return null;
    }
    //返回下面的切割方法
    return merger(lists,0,lists.length -1);
}
public ListNode merger(ListNode[] lists,int left,int right){
    if (left == right) return lists[left];
    int mid = left + (right - left) / 2;
    ListNode l1 = merger(lists,left,mid);
    ListNode l2 = merger(lists,mid+1,right);
    //返回下面的排序方法
    return mergerTwoList(l1,l2)
}
public ListNode mergerTwoList(ListNode l1,ListNode l2){
    if (l1 == null) return l2;
    if (l2 == null) return l1;
    if (l1.val < l2.val){
        l1.next = mergerTwoList(l1.next,l2);
    }else{
        l2.next = mergerTwoList(l1,l2.next);
    }
}
  • 169:多数元素

这道题就是求相同的数大于整个数组一半的元素,这里的方法为,使用hashmap来存储数据,这样的好处是,可以先判断hash的key值存储数字,value存储次数。

每次加入时,只需要简单的判断,如果在hash中,就get到它的value,然后+1即可,如果没有存在的话,就直接添加并赋value为1。

public static void find(int[] arr){
    HashMap<Integer,Integer> hash = new HashMap<>();
    for (int i = 0;i < arr.length;i ++){
        if (hash.containsKey(arr[i])){
            hash.put(arr[i],hash.get(arr[i])+1);
        }else{
            hash.put(arr[i],1);
        }
    }
    //然后找寻hash中是否存在value大于len/2的值,有的话,返回key
    int len = arr.length / 2;
    Set<Integer> keySet = hash.keyset();
    for (Integer i : keyset){
        if (i > len){
            System.out.println(hash.getKey(i));
        }
    }
}
  • 240:搜索二维矩阵

一个从左到右的升序,每列从小到大排列,每行也是从小到大排序。给定一个数字,判断该数组中是否存在该数组

public static boolean find(int[][] nums,int target){
    for (int i = 0;i < nums[0].length;i ++){
        if (nums[0][i] > target){
            return false;
        }
        for (int j = 0;j < nums.length;j ++){
            if (nums[j][i] == target){
                return true;
            }else if (nums[j][i] > target){
                break;
            }
        }
    }
    return false;
}

单调栈:

  • LeetCode84:

给定n个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻且宽度为1.

public int largesArea(int[] heights){
    int len = heights.length;
    Stack<Integer> stack = new Stack<>();
    int[] arr = new int[len + 2];
    for (int i = 0;i < len;i ++){
        arr[i+1] = heights[i];
    }
    int area = 0;
    for (int j = 0;j <= len+1;j ++){
        while(!stack.isEmpty() && arr[stack.peek()] > arr[j]){
            int height = arr[stack.pop()];
            int l = stack.peek();
            //这里有误,应该改为j - l - 1
            area = Math.max(area,(j - l - 1)*height);
        }
        stack.push(j);
    }
    return area;
}
  • 85:求二维数组中的最大矩阵面积,当然可以利用上一题的计算。

这道题定义同样也为 ,但是仔细考虑这道题就会发现也是使用单调栈的思想进行计算,唯一不同的是,这道题の计算不简简单单是计算一次,而是每行至最顶层的区间都要计算,就是从顶层,依次向下移动一行,但是上面不动。

image.png

这样就很好理解了,是不是发现,就变成上面的单调栈计算了。但是有一点需要注意的是,加入该层不存在’1‘,那么它的高度就为0.

public static int maxRectangle(char[][] matrix){
    if (matrix.length = 0){
        retrurn 0;
    }
    int area = 0;
    int[] row = new int[matrix[0].length];
    for (int i = 0;i < matrix.length;i ++){
        for (int j = 0;j < matrix[0].length;j ++){
            if (matrix[i][j] == '1'){
                row[j] += 1; 
            }else{
                row[j] = 0;
            }
        }
        //将该数组传给上一题的方法进行计算最大面积。
    	area = Math.max(area,largeArea(row));    
    }
    return area;
}
  • 739:求每日温度中,每天需要再等几天就能到更高的温度。

也就是说温度如所示:[23,25,27,25,24,26,28]。其中第一天23到第二天温度就高于23度了,所以第一天为1,同理第二天也是1,第三天27℃,后面都小于它直到28℃,那么第三天就为4,依次类推

public static int findHigh(int[] nums){
    int len = nums.length();
    Stack<Integer> stack = new Stack<>();
    int[] arr = new int[len];
    for (int i = 0;i < len;i ++){
        while (!stack.isEmpty() && nums[stack.peek()] < nums[i]){
            int pre = stack.pop();
            arr[pre] = i - pre;
        }
        stack.push(i);
    }
    return arr;
}
  • 503:下一个更大的元素

给定一个循环的数组(最后一个元素的下一个元素为第一个元素),输出每一个元素の下一个更大的元素。

public static int find(int[] nums){
    int len = nums.length;
    int[] arr = new int[len];
    Stack<Integer> stack = new Stack<>();
    Arrays.fill(-1);
    for (int i = 0;i < len;i ++){
        while (! stack.isEmpty() && nums[stack.peek()] < nums[i % len]){
            int temp = stack.pop();
            arr[temp%len] = nums[i%len];
        }
        stack.push(i%len);
    }
    return arr;
}

并查集:

  • LeetCode547:找寻不同の省份

也就是找寻图中有几块相互不连通的区域,我们可以通过很多的方式来找寻,比如深度优先算法,广度优先算法以及并查集。

//主方法
public staic void find(int[][] connected){
    int n = connected.length;
    UnionFind uf = new UnioFind(n);
    for (int i = 0;i < n;i ++){
        for (int j = i + 1;j < n;j ++){
            if (connected[i][j] == 1){
                uf.unio(i,j);
            }
        }
    }
    return uf.size;
}

//类
class UnionFind{
    int[] roots;
    int size;
    
    public UnionFind(int n){
        roots = new int[n];
        for (int i = 0;i < n;i ++){
            roots[i] = i;
        }
        size = n;
    }
    
    public int find(int i){
        if (i == roots[i]){
            return i;
        }
        return roots[i] = find(root[i]);
    }
    
    public void unio(int p,int q){
        int pRoot = find(p);
        int qRoot = find(q);
        if (pRoot != qRoot){
            roots[pRoot] = qRoot;
            size--;
        }
    }
}
  • 200:岛屿数量

给一个有’1‘和’0‘组成的二维网格,计算网格中的岛屿数量,直接使用深度优先遍历即可

public static void find(int[][] land){
   	int row = land.length;
    int col = land[0].length;
    int count = 0;
    for (int i = 0;i < row;i ++){
        for (int j = 0;j < col;j ++){
            if (land[i][j] == 1){
                count++;
                dfs(i,j,land);
            }
        }
    }
    
    public static void dfs(int m,int n,int[][] land){
        if (m < 0 || n < 0 || m > land.length || n > land[0].length){
            return;
        }
        if (land[m][n] != 1){
            return;
        }
        land[m][n] = 2;
        dfs(m,n-1,land);
        dfs(m-1,n,land);
        dfs(m,n+1,land);
        dfs(m+1,n,land);
    }
}
  • 684:冗余连接

找寻这个图变成有环图的第一根边,并将其返回,无则返回[0,0];

class Solution {
    private int n;  // 节点数量3 到 1000
    private int[] father;
    public Solution() {
        n = 1005;
        father = new int[n];

        // 并查集初始化
        for (int i = 0; i < n; ++i) {
            father[i] = i;
        }
    }

    // 并查集里寻根的过程
    private int find(int u) {
        if(u == father[u]) {
            return u;
        }
        father[u] = find(father[u]);
        return father[u];
    }

    // 将v->u 这条边加入并查集
    private void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v) return ;
        father[v] = u;
    }

    // 判断 u 和 v是否找到同一个根,本题用不上
    private Boolean same(int u, int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }

    public int[] findRedundantConnection(int[][] edges) {
        for (int i = 0; i < edges.length; i++) {
            if (same(edges[i][0], edges[i][1])) {
                return edges[i];
            } else  {
                join(edges[i][0], edges[i][1]);
            }
        }
        return null;
    }
}

滑动窗口:

  • LeetCode209:找出数组中大于目标数的最短长度

给定一个数组和一个目标数,找出数组中大于目标数的最短数组,并返回其长度。

public static int find(int[] nums,int target){
    int left = 0,right = 0,sum = 0,min = Integer.MAX_VALUE;
    while (right < nums.length){
        sum += nums[right++];
        while (sum >= target){
            min = Math.min(min,right-left);
            sum -= nums[left++];
        }
    }
    return min == MAX_VALUE ? 0 : min;
}
  • 3:无重复字符的最长子串

这里想着使用一个hashmap来存储字符,这样就可以判断字符是否存在里面来决定是否移动

public static int fin(String s){
    if (s.length() <= 0){
        return 0;
    }
    Map<Integer,Integer> hash = new HashMap<>();
    int left = 0,sum = 0;
    for (int i = 0;i < s.length();i ++){
        if (hash.containsKey(s.charAt(i))){
            left = Math.min(left,hash.get(s.charAt(i))+1);
        }else{
            sum = Math.max(sum,i-left+1);
        }
    }
    return sum;
}
  • 1004:最大连续1的个数

这次是升级版,也就是会给出一个目标数,该数为最多将0转变为1的个数,转变后求出最长的1子串个数

public static int longestOnes(int[] nums, int k) {
        if (nums.length == 0){
            return 0;
        }
        if (nums.length <= k){
            return k;
        }
        int zero = 0,left = 0,right = 0;
        int sum = 0;
        while (right < nums.length){
            if (nums[right++] == 0){
                zero ++;
            }
            while (zero > k){
                if (nums[left++] == 0){
                    zero--;
                }
            }
            // System.out.println(right + "------" + left);
            sum = Math.max(sum,right - left);
        }
        return sum;
 }
  • 1208:字符串近可能相近的长度

这道题难以理解,但是找到诀窍后就很好理解了。如“abc”和“bcd”每个字符对应的ASCII码差值为转变需要的能量,给定一个能量值,求出能量值范围内最长能转变为相近的子串。

public int equalSubstring(String s, String t, int maxCost) {
        int len = s.length();
        int[] arr = new int[len];
        for (int i = 0;i < len;i ++){
            arr[i] = Math.abs(Integer.valueOf(s.charAt(i)) - Integer.valueOf(t.charAt(i)));
        }
        int sum = 0,left = 0,right = 0,chang = 0;
        while (right < len){
            sum += arr[right++];
            while (sum > maxCost){
                sum -= arr[left++];
            }
            chang = Math.max(chang,right - left);
        }
        return chang;
    }

前缀和:

  • LeetCode724:寻找数组的中心下标

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

//应该使用前缀和的思想
public int pivoIndex(int[] nums){
    int sum = 0;
	for (int i = 0;i < nums.length;i ++){
		sum += nums[i];
	}
    int proSum = 0;
    for (int j = 0;j < nums.length;j ++){
        if ((proSum * 2 + nums[j]) == sum){
            return j;
        }
        proSum += nums[j];
    }
    return -1;
}
  • 560:和为k的子数组

给定一个整数数组和一个整数 **k,**你需要找到该数组中和为 k 的连续的子数组的个数。

public int subarraySum(int[] nums, int k) {
        //前缀和的方式计算该题
        int len = nums.length;
        int[] preNum = new int[len+1];
        for (int i = 0;i < len;i ++){
            preNum[i + 1] = preNum[i] + nums[i];
        }
        int count = 0;
        for (int j = 0;j < len;j ++){
            for (int js = j;js < len;js ++){
                if (preNum[js+1] - preNum[j] == k)
                    count++;
            }
        }
        return count;
    }
  • 437:路径总和

求一棵二叉树上一段路径的和为target,有几条这样的路段。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int pathSum(TreeNode root, int sum) {
        // key是前缀和, value是大小为key的前缀和出现的次数
        Map<Integer, Integer> prefixSumCount = new HashMap<>();
        // 前缀和为0的一条路径
        prefixSumCount.put(0, 1);
        // 前缀和的递归回溯思路
        return recursionPathSum(root, prefixSumCount, sum, 0);
    }

   
    private int recursionPathSum(TreeNode node, Map<Integer, Integer> prefixSumCount, int target, int currSum) {
        // 1.递归终止条件
        if (node == null) {
            return 0;
        }
        // 2.本层要做的事情
        int res = 0;
        // 当前路径上的和
        currSum += node.val;

        //---核心代码
        // 看看root到当前节点这条路上是否存在节点前缀和加target为currSum的路径
        // 当前节点->root节点反推,有且仅有一条路径,如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
        // currSum-target相当于找路径的起点,起点的sum+target=currSum,当前点到起点的距离就是target
        res += prefixSumCount.getOrDefault(currSum - target, 0);
        // 更新路径上当前节点前缀和的个数
        prefixSumCount.put(currSum, prefixSumCount.getOrDefault(currSum, 0) + 1);
        //---核心代码

        // 3.进入下一层
        res += recursionPathSum(node.left, prefixSumCount, target, currSum);
        res += recursionPathSum(node.right, prefixSumCount, target, currSum);

        // 4.回到本层,恢复状态,去除当前节点的前缀和数量
        prefixSumCount.put(currSum, prefixSumCount.get(currSum) - 1);
        return res;
    }
}


作者:burning-summer
链接:https://leetcode-cn.com/problems/path-sum-iii/solution/qian-zhui-he-di-gui-hui-su-by-shi-huo-de-xia-tian/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 1248:优美子数组

数组包含多少个奇数字和给定目标数相同,这样的数组段应该有多少个

public int numberOfSubarrays(int[] nums, int k) {
        int left = 0,right = 0,jishu = 0,res = 0;
        while (right < nums.length){
            if ((nums[right] & 1 )== 1){
                jishu++;
            }
            if (jishu == k){
                int temp = right;
                while (right < nums.length && (nums[right] & 1) == 0){
                    right++;
                }
                int rightOdd = right - temp;
                int leftOdd = 0;
                while ((nums[left] & 1 )== 0){
                    leftOdd++;
                    left++;
                }               
                res += (leftOdd + 1) * (rightOdd + 1); 
            }
            jishu--;
            left++;
        }
        return res;
}

差分:

  • LeetCode1094:

121、122
拓扑排序:LeetCode210

字符串:

  • LeetCode5:最长的回文字符串

就是一段字符串中最长的回文字符串,返回其长度

//我使用了暴力解法,可以实现
//但是可以对暴力进行优化计算
  • 20:有效的括号

就是给出一些括号的标志,求出是否合法。加入栈的思想,如下所示:

public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (char ss:s.toCharArray()){
            if (ss == '('){
                stack.push(')');
            }else if (ss == '['){
                stack.push(']');
            }else if (ss == '{'){
                stack.push('}');
            }else if (stack.isEmpty() || ss != stack.pop()){
                return false;
            }
        }
        return stack.isEmpty();
    }
  • 43:字符串相乘

其实就和我们小时候一样去计算,将两个数一个一个的拆分出来,用第二位的每一位去遍历相乘第一位的每一位。

需要注意的点:第一位可能存储的是0,所以要清除它

public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0"))
            return "0";
        int sum = 0;
        int len1 = num1.length();
        int len2 = num2.length();
        int[] arr = new int[len1 + len2];
        for (int i = len2 - 1;i >= 0;i --){
            int index2 = num2.charAt(i) - '0';
            for (int j = len1 - 1;j >= 0;j --){
                int index1 = num1.charAt(j) - '0';
                sum = arr[i + j + 1] + index1 * index2;
                arr[i + j + 1] = sum % 10;
                arr[i + j] += sum/10;
            }
        }
        StringBuilder str = new StringBuilder();
        for (int i = 0;i < len1 + len2;i ++){
            if (i == 0 && arr[i] == 0) continue;
            str.append(arr[i]);
        }
        return str.toString();
    }
  • 93:

二分查找:LeetCode33、34

BFS:

  • LeetCode127:单词接龙

有点像最小单词转换路径消耗


  • 139:

130、529、815

DFS&回溯:

  • LeetCode934:最短的桥

思路是,先使用dfs找出所有的桥,然后,使用bfs不断向外扩张,然后检验扩张的格子是否含有桥,如果有,那么就到达桥了,直接返回扩张了多少次。

  • 685:冗余连接

找出有向图中,将树型结构变为图结构的那条边

class Solution {
private:
    static const int N = 1010; // 如题:二维数组大小的在3到1000范围内
    int father[N];
    int n; // 边的数量
    // 并查集初始化
    void init() {
        for (int i = 1; i <= n; ++i) {
            father[i] = i;
        }
    }
    // 并查集里寻根的过程
    int find(int u) {
        return u == father[u] ? u : father[u] = find(father[u]);
    }
    // 将v->u 这条边加入并查集
    void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v) return ;
        father[v] = u;
    }
    // 判断 u 和 v是否找到同一个根
    bool same(int u, int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }
    // 在有向图里找到删除的那条边,使其变成树
    vector<int> getRemoveEdge(const vector<vector<int>>& edges) {
        init(); // 初始化并查集
        for (int i = 0; i < n; i++) { // 遍历所有的边
            if (same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边
                return edges[i];
            }
            join(edges[i][0], edges[i][1]);
        }
        return {};
    }

    // 删一条边之后判断是不是树
    bool isTreeAfterRemoveEdge(const vector<vector<int>>& edges, int deleteEdge) {
        init(); // 初始化并查集
        for (int i = 0; i < n; i++) {
            if (i == deleteEdge) continue;
            if (same(edges[i][0], edges[i][1])) { // 构成有向环了,一定不是树
                return false;
            }
            join(edges[i][0], edges[i][1]);
        }
        return true;
    }
public:

    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        int inDegree[N] = {0}; // 记录节点入度
        n = edges.size(); // 边的数量
        for (int i = 0; i < n; i++) {
            inDegree[edges[i][1]]++; // 统计入度
        }
        vector<int> vec; // 记录入度为2的边(如果有的话就两条边)
        // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
        for (int i = n - 1; i >= 0; i--) {
            if (inDegree[edges[i][1]] == 2) {
                vec.push_back(i);
            }
        }
        // 处理图中情况1 和 情况2
        // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
        if (vec.size() > 0) {
            if (isTreeAfterRemoveEdge(edges, vec[0])) {
                return edges[vec[0]];
            } else {
                return edges[vec[1]];
            }
        }
        // 处理图中情况3
        // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
        return getRemoveEdge(edges);

    }
};
  • 1102:得分最高的路径

找出矩阵从0,0走到右下角的最高得分路径,可以使用动态规划,也可以使用dfs,这里使用dfs

int[][] dic = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
public static void DFS(int i, int j, int t,int[][] A,int[][] v){
        int len1 = A.length;
        int len2 = A[0].length;
        if(i == len1-1 && j == len2-1){
            res = Math.max(res, t);
        }
        for(int index = 0; index<4; index++){
            int x = i + dic[index][0];
            int y = j + dic[index][1];
            if(x>=0 && x<len1 && y>=0 && y<len2 && v[x][y]==0){
                v[x][y]=1;
                DFS(x,y,A[x][y],A,v);
                v[x][y]=0;
            }
        }
}
  • 531:孤独像素1

求孤独像素的个数,孤独像素的意思就是1,在它的列和都没有相同的数字

//具体不用说了,直接用两个数组存储各行各列的1的个数,只要该行该列的1个数为1,孤独像素加一即可。
  • 533:孤独像素2,升级版,就是涉及到一块区域,不是很有dfs特征

  • 113:二叉树中路径和为target的路径

class Solution {
    List<List<Integer>> res = new LinkedList<List<Integer>>();
    Deque<Integer> arr = new LinkedList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        findPath(root,targetSum);
        return res; 
    }
	//找寻路径
    public void findPath(TreeNode root,int target){
        if (root == null){
            return;
        }
        //双端队列加入该节点
        arr.offerLast(root.val);
        //target再减去root的值
        target -= root.val;
        if (root.left == null && root.right == null && target == 0){
            res.add(new LinkedList<Integer>(arr));
        }
        findPath(root.left,target);
        findPath(root.right,target);
        //很关键,需要清楚掉这个不是最终结果的路径,回溯
        arr.pollLast();
    }
}
  • 332:重新安排行程

  • 337:打家劫舍2

家变成了一个树型结构,让我们来打劫

public int rob(TreeNode root){
    HashMap<TreeNode,Integer> hash = new HashMap<>();
    return find(hash,root);
}
public int find(HashMap<TreeNode,Integer> hash,TreeNode root){
    if (root == null){
        return 0;
    }
    if (hash.containsKey(root)){
        return map.get(root);
    }
    int money = root.val;
    if (root.left != null){
        money += (find(map,root.left.left) + find(map,root.left.right));
    }
    if (root.right != null){
        money += (find(map,root.right.left) + find(map,root.right.right));
    }
    int result = Math.max(money,find(map,root.left) + find(map,root.right));
    map.push(root,result);
    return result;
}

动态规划:

  • LeetCode213:打家劫舍升级版

就是说大家劫舍首位相连,也就是投了第一家就不能投最后一家,他们形成了一个闭环。

public static int find(int[] nums){
    int len = nums.length;
    //二维数组表示,i家被劫或者未被劫,分别用0,1表示
    int[] dp = new int[len];
    dp[0] = nums[0];
    for (int i = 1;i < len;i ++){
      	if (i < 2){
            dp[i] = Math.max(dp[i-1],dp[i-2]+0);
        }else{
            dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
        }
    }
    return dp[len-1];
}
public static void main(String[] args){
    //这里是升级版
    int[] nums = {1,24,5,3,12,43};
    //要么不要第一家,要么不要最后一家
    int result = Math.max(find(String.copyOfRange(0,len-1,nums)),find(String.copyOfRange(1,len,nums)))
}
  • 123:买入股票:

这是一个限次数的购买股票的方法,当然这样的股票购买方法就可以延伸到k次购买机会,如果不限次数的购买股票的方法,那么就可以选择隔天购买的方法。或者动态规划。

  public int maxProfitDP(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int[][][] dp = new int[prices.length][2][3];
        int MIN_VALUE = Integer.MIN_VALUE / 2;
        //因为最小值再减去1就是最大值Integer.MIN_VALUE-1=Integer.MAX_VALUE
        //初始化
        dp[0][0][0] = 0;//第一天休息
        dp[0][0][1] = dp[0][1][1] = MIN_VALUE;//不可能
        dp[0][0][2] = dp[0][1][2] = MIN_VALUE;//不可能
        dp[0][1][0] = -prices[0];//买股票
        for (int i = 1; i < prices.length; i++) {
            dp[i][0][0] = 0;
            dp[i][0][1] = Math.max(dp[i - 1][1][0] + prices[i], dp[i - 1][0][1]);
            dp[i][0][2] = Math.max(dp[i - 1][1][1] + prices[i], dp[i - 1][0][2]);
            dp[i][1][0] = Math.max(dp[i - 1][0][0] - prices[i], dp[i - 1][1][0]);
            dp[i][1][1] = Math.max(dp[i - 1][0][1] - prices[i], dp[i - 1][1][1]);
            dp[i][1][2] = MIN_VALUE;
        }
        return Math.max(0, Math.max(dp[prices.length - 1][0][1], dp[prices.length - 1][0][2]));
    }
  • 62:不同路径

就是从左上角走到右下角得所有可能路径,只能向下和向右走。这里使用动态规划,也就是起点到该点的所有路径。

public int uniquePaths(int m, int n) {
    int[][] dp = new int[m][n];
    for (int i = 0;i < m;i ++){
        dp[i][0] = 1;
    }
    for (int j = 0;j < n;j ++){
        dp[0][j] = 1;
    }
    dp[0][0] = 1;
    for (int i = 1;i < m;i ++){
        for (int j = 1;j < n;j ++){
            dp[i][j] = dp[i][j-1]+dp[i-1][j];
        }
    }
    return dp[m-1][n-1];
}
  • 63:不同路径,但是有障碍物。

初步的想法是,将有障碍物的网格删除至为0。

public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        for (int i = 0;i < m;i ++){
            if (obstacleGrid[i][0] == 1){
                break;
            }
            dp[i][0] = 1;
        }
        for (int j = 0;j < n;j ++){
            if (obstacleGrid[0][j] == 1){
                break;
            }
            dp[0][j] = 1;
        }
        for (int i = 1;i < m;i ++){
            for (int j = 1;j < n;j ++){
                if (obstacleGrid[i][j] == 0){
                    dp[i][j] = dp[i][j-1]+dp[i-1][j];
                }
            }
        }
        return dp[m-1][n-1];
    }
  • 361:轰炸敌人

这还是个会员问题,就是一个炸弹在空地爆炸最多能够炸死多少人,遇到墙和边界就停止杀伤,且只能走上下左右的直线方向。

//暴力解发,
public static void main(){
    //建立四个数组,用来存储每个空旷节点向上、向下、向左和向右的杀伤人数。
}
  • 1230:抛掷硬币

不规则硬币,在抛掷过程中正反概论不一样,求k个正面的概论是多少?


贪心算法:

  • LeetCode55:跳跃游戏

就是遍历每一个节点,将它能从起点开始跳跃的最大距离计算处理,得出下面的解使用贪心算法求解最合适,也就是说通过逐步的求解来计算能否跳过终点

public boolean canJump(int[] nums) {
        int len = nums.length;
        int rightMost = 0;
        for (int i = 0;i < len;i ++){
            if (i <= rightMost){
                rightMost = Math.max(rightMost,i + nums[i]);
                if (rightMost >= len - 1){
                    return true;
                }
            }
        }
        return false;

  • 435:重叠区间

621、452
字典树:LeetCode820、208、648

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米店不卖你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值