hazy的leetcode刷题笔记(二)

leetcode.222:完全二叉树的节点个数-每日一题
给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例:
输入:
1
/
2 3
/ \ /
4 5 6
输出: 6

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int countNodes(TreeNode root) {
        /*
        来自评论区,相对于暴力递归更好的解法:
        完全二叉树的高度可以直接通过不断地访问左子树就可以获取
        判断左右子树的高度: 
        如果相等说明左子树是满二叉树, 然后进一步判断右子树的节点数(最后一层最后出现的节点必然在右子树中)
        如果不等说明右子树是深度小于左子树的满二叉树, 然后进一步判断左子树的节点数(最后一层最后出现的节点必然在左子树中)
        */
        if(root == null) return 0;
        //获得左右子树的深度
        int ld = getDepth(root.left);
        int rd = getDepth(root.right);
        // 1(根节点) + (1 << ld)-1(左完全左子树节点数) + 右子树节点数量
        if(ld == rd) return (1 << ld) + countNodes(root.right);
        // 1(根节点) + (1 << rd)-1(右完全右子树节点数) + 左子树节点数量
        else return(1 << rd) + countNodes(root.left);
 
    }
    //获得左右子树的深度
    private int getDepth(TreeNode root) {
        int dept = 0;
        while(root != null) {
            root = root.left;
            dept++;
        }
        return dept;
    }
}

在这里插入图片描述

leetcode-309:最佳买卖股票时机含冷冻期-hot100
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

class Solution {
    public int maxProfit(int[] prices) {
    /*基本思路:动态规划法:如果今天不持有股票,说明是 昨天也不持有 / 昨天持有今天卖掉
    如果今天持有股票,说明是 昨天本就持有 / 前天卖掉今天买入
    只要每次选择两个情况中的较大者即可
    */
        //特殊输入判断
        if(prices.length == 0 || prices.length == 1) return 0;

        //分别对应昨天不持有 昨天持有 前天的不持有
        int dp_0_i, dp_1_i, dp_0_i_2;
        dp_0_i = 0;
        dp_1_i = - prices[0];
        dp_0_i_2 = 0;

        for(int i = 1; i < prices.length; i++) {
            //记录昨天的不持有,到明天就会变成前天的不持有
            int temp = dp_0_i;
            dp_0_i = Math.max(dp_0_i, dp_1_i + prices[i]);
            dp_1_i = Math.max(dp_1_i, dp_0_i_2 - prices[i]);
            dp_0_i_2 = temp;
        }
        return dp_0_i;
    }
}

在这里插入图片描述

leetcode.454:四数相加II-每日一题
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:
2
解释:
两个元组如下:

  1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0 4
  2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        /*基本思路:如果直接四层循环那肯定复杂度太高了,因此可以拆分成两个n^2的问题:
        A + B = - (C + D) 利用哈希表求得所有A+B的情况,再求得所有C+D同时和哈希表进行比较,如果存在就加上对应的次数 
        */
        if(A.length == 0) return 0;
        //记录A+B中某种情况出现的次数
        Map<Integer, Integer> record = new HashMap<Integer, Integer>();
        //最后的组合结果
        int result = 0;
        //求A+B的所有情况,存在map中
        for(int i = 0; i < A.length; i++) {
            for(int j = 0; j < A.length; j++) {
                if(record.get(A[i] + B[j]) == null) record.put(A[i] + B[j], 1);
                else record.put(A[i] + B[j], record.get(A[i] + B[j]) + 1);
            }
        }
        //求C+D的所有情况并让结果加上对应的次数
        for(int i = 0; i < A.length; i++) {
            for(int j = 0; j < A.length; j++) {
                if(record.get(-(C[i] + D[j])) != null) result += record.get(-(C[i] + D[j]));
            }
        }
        return result;
    }
}

在这里插入图片描述

leetcode.976:三角形的最大周长-每日一题
给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。
如果不能形成任何面积不为零的三角形,返回 0。
示例 1:
输入:[2,1,2]
输出:5
示例 2:
输入:[1,2,1]
输出:0

class Solution {
    public int largestPerimeter(int[] A) {
        /* 基本思路:暴力算法不可取  先进行排序,取前三大的长度,如果前三大长度不符合
        说明 第二大和第三大加起来没有第一大,因此整体直接往后移一位
         */

        Arrays.sort(A);

        for(int i = A.length - 1; i >= 2; i--) {
            if(A[i - 1] + A[i - 2] > A[i]) return A[i - 1] + A[i - 2] + A[i];
        }

        return 0;
    }
}

在这里插入图片描述

leetcode.56:合并区间-hot100
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: intervals = [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间

class Solution {
    public int[][] merge(int[][] intervals) {
        /* 基本思路:按区间的左边进行排序(一开始我以为得用右边进行排序)
        每次选取最左边的区间,然后进行合并,接下来继续选择生下的区间最左边的进行合并
        */
        if(intervals == null || intervals.length == 0 || intervals.length == 1) return intervals;
        //对区间左边进行排序
        Arrays.sort(intervals, (a1, a2) -> a1[0] - a2[0]);
        //记录当前的位置和总长度
        int now = 0;
        int n = intervals.length;
        ArrayList<int[]> record = new ArrayList<int[]>();

        while(now < n) {
            //当前区间的左右范围
            int left = intervals[now][0];
            int right = intervals[now][1];
            //如果接下来有区间在当前区间范围内就进行合并,如果没有就说明当前是一个独立区间
            while(now < n - 1 && right >= intervals[now + 1][0]) {
                right = Math.max(right, intervals[now + 1][1]);
                now++;
            }
            record.add(new int[] {left, right});
            now++;
        }
        return record.toArray(new int[record.size()][2]);
    }
}

leetcode.767:重构字符串-每日一题
给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。
若可行,输出任意可行的结果。若不可行,返回空字符串。
示例 1:
输入: S = “aab”
输出: “aba”
示例 2:
输入: S = “aaab”
输出: “”
注意: S 只包含小写字母并且长度在[1, 500]区间内。

class Solution {
    public String reorganizeString(String S) {
        /*基本思路:扫描所有的字符,获得每个字符出现的次数
        接下来拼接字符串:每次都选取:与上个字符不同且出现次数最多的字符
        选取完后拼接起来然后次数减一,再标记这个字符为上个字符
        如果找不到这个字符,说明无法生成最终的字符,直接返回空串
        */
        //字符记录次数
        int[] record = new int[26];
        Arrays.fill(record, 0);
        for(int i = 0; i < S.length(); i++) {
            record[S.charAt(i) - 'a'] += 1;
        }
        //拼接字符串
        StringBuilder result = new StringBuilder();
        //选取第一个字符
        int last = -1;
        int max = 0;
        for(int i = 0; i < 26; i++) {
            if(record[i] > max) {
                max = record[i];
                last = i;
            }
        }
        result.append((char)('a' + last));
        record[last]--;

        while(result.length() < S.length()) {
            int index = -1;
            max = 0;
            //循环,找出下一个要的字符
            for(int i = 0; i < 26; i++) {
                if(record[i] > max && i != last) {
                    max = record[i];
                    index = i;
                } 
            }
            //如果找到就拼接,找不到就直接返回
            if(index != -1) {
                result.append((char)('a' + index));
                record[index]--;
                last = index;                
            }
            else {
                return "";
            }
        }
        return result.toString();
    }
}

在这里插入图片描述

leetcode.34:在排序数组中查找元素的第一个和最后一个位置-每天一题
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

class Solution {
    public int[] searchRange(int[] nums, int target) {
        /*基本思路:时间复杂度为O(log n)那一定是二分查找啦
        不过其中要有一个找到目标值后,向两端扩散找最左和最右的过程
        */
        int left = 0; 
        int right = nums.length;
        int[] result = new int[2];
        Arrays.fill(result, -1);
        while(left < right) {
            if(target < nums[(left + right) / 2]) {
                right = (left + right) / 2;
            }
            else if(target > nums[(left + right) / 2]) {
                left = (left + right) / 2 + 1;
            }
            else {
                result[0] = (left + right) / 2;
                result[1] = (left + right) / 2;
                //向两端扩散找最左、最右
                while(result[0] - 1 >= 0 && nums[result[0] - 1] == target) result[0]--;
                while(result[1] + 1 < nums.length && nums[result[1] + 1] == target) result[1]++;
                return result;
            }
        }
        return result;
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值