LeetCode HOT 100Ⅰ

目录

DAY1

1:两数之和

2:两数相加

3:无重复字符的最长子串

DAY2

5:最长回文子串

DAY3

11:盛最多水的容器(太秒了 多看这道题)

15:三数之和

DAY4

53:最大子数组和

20:有效的括号

70:爬楼梯

DAY5

21:合并两个有序链表

DAY6

22:括号生成

DAY7

215:数组中的第k个最大元素

DAY8

206:反转链表

45:跳跃游戏-Ⅱ

DAY9

104:二叉树的深度

121:买股票的最佳时机

136:只出现一次的数字

DAY10

94:二叉树的中序遍历

101:对称的二叉树

DAY11

226:反转二叉树

534:二叉树的直径

DAY12

69:x的平方根

539:最小时间差(大疆笔试)


DAY1

1:两数之和

思路:哈希表

用哈希表存放数组nums里的元素和它对应的索引值,每遍历一个元素就去哈希表李查找是否有满足与它相加和为target的值,若有的话就返回两个key对应的value(索引);若没有的话将其添加至哈希表中继续遍历。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(target - nums[i]))
                return new int[]{map.get(target - nums[i]), i}; //get是获取key的value值,即nums[i]对应的索引值
            map.put(nums[i], i);
        }
        return new int[0];
    }
}

2:两数相加

思路:如果两个链表长短不一,给短的前面补0。新链表节点 = l1.val + l2.val + 进位。然后得到的和求出进位和进位之后剩下的数,将个位数放进新链表中。

如果最后一位加完之后还有进位,则需要为它单独创建一个val为1的节点。

tips:新建链表的话一定要设置一个pre指向新链表的头节点,这样防止在创建的时候头节点移动导致最后无法输出整个链表。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(0); //指向新列表的头节点
        ListNode cur = pre;
        int c = 0; //进位
        while(l1 != null || l2 != null){
            //如果两个链表长短不一,短的可以补0
            int a = l1 == null ? 0 : l1.val;
            int b = l2 == null ? 0 : l2.val;
            int sum = a + b + c;

            c = sum / 10; 
            sum %= 10; //进位后留下个位数
            cur.next = new ListNode(sum);
            //相加的两个链表和新链表都要移动
            cur = cur.next;
            if(l1 != null) l1 = l1.next;
            if(l2 != null) l2 = l2.next;
        }
        if(c == 1) cur.next = new ListNode(c); //如果相加到最后还有进位,则需要新建一个val为1的节点
        return pre.next;
    }
}

3:无重复字符的最长子串

同剑指offer48:最长不含重复字符的子字符串

https://blog.youkuaiyun.com/weixin_45876739/article/details/125970441?spm=1001.2014.3001.5502

  • 定义一个 map 数据结构存储 (k, v),其中 key 值为字符,value 值为字符位置 +1,加 1 表示从字符位置后一个才开始不重复
  • 我们定义不重复子串的开始位置为 start,结束位置为 end
  • 随着 end 不断遍历向后,会遇到与 [start, end] 区间内字符相同的情况,此时将字符作为 key 值,获取其 value 值,并更新 start,此时 [start, end] 区间内不存在重复字符
  • 无论是否更新 start,都会更新其 map 数据结构和结果 ans。

作者:画手大鹏
链接:https://leetcode.cn/leetbook/read/illustrate-lcof/e2wq47/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

DAY2

5:最长回文子串

思路:中心扩展算法。回文串有两种形式:奇数和偶数,所以需要分别以i,i开始和i,i+1开始,判断是哪种(哪个长度长就是哪个),每循环一次更新start和end的位置,这里需要注意:

  • 对于奇数串移动start和end位置时,len/2和(len-1)/2的结果是一样
  • 对于偶数串,i 指向的是相同字符前面的,所以start 的位置是(len-1)/2,end位置是len/2
class Solution {
    public String longestPalindrome(String s) {
        if(s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for(int i = 0; i < s.length(); i++){
            int len1 = expandAroundCenter(s, i, i); //回文串形如aba,起始是单个字符
            int len2 = expandAroundCenter(s, i, i + 1); //回文串形如dbbd,起始是两个相同字符
            int len = Math.max(len1, len2);
            if(len > end - start){
                //改变start和end位置需要格外注意
                start = i - (len - 1) / 2; //除法向下取整
                end = i + len / 2; 
                //当起始是一个字符时len/2的结果是相同的,
                //当是两个字符的时候i是前面的,所以start需要i减去(len-1)/2
            }
        }
        return s.substring(start, end + 1);
        
    }
    
    int expandAroundCenter(String s, int left, int right){
        while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
            left--;
            right++;
        }
        return right - left - 1; //返回子串长度
    }
}

DAY3

11:盛最多水的容器(太秒了 多看这道题)

思路:双指针。如果向内移动短板,水槽的短板 min(h[i], h[j])可能变大,所以水面积可能变大;如果向内移动长板,水槽的短板 min(h[i], h[j])可能变小或不变,面积一定变小。所以循环过程中只移动短板。res保存最大值。

class Solution {
    public int maxArea(int[] height) {
        int i = 0, j = height.length - 1;
        int res = 0;
        while(i < j){
            //移动短板
            res = height[i] < height[j] ?
                Math.max(res, (j - i) * height[i++]) :
                Math.max(res, (j - i) * height[j--]); //这一步简直是神来之笔
        }
        return res;
    }
}

15:三数之和

思路:排序,双指针。排序后k从头开始遍历到nums[k]=0,大于0之后和一定不为0。对我而言本题中最难的是跳过重复的数字。

  • sum>0,移动指针 j ,减小sum值
  • sum<0,移动指针 i ,增大sum值
  • 等于的时候将三个数以数组形式存入res中,跳过重复的元素

while(i<j&&nums[i]==nums[++i]) 等价于 i++; while(i<j&&nums[i-1]==nums[i]) i++

将i++之后要判断这个数和他前面的是不是一样的,如果是一样的 那么我们i继续后移一位,然后再判断,一直到不同为止。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums); //先排序
        List<List<Integer>> res = new ArrayList<>();
        for(int k = 0; k < nums.length - 2; k++){ //i,j在k的右面,所以是-2
            if(nums[k] > 0) break;
            //已经将 nums[k - 1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合
            if( k > 0 && nums[k] == nums[k - 1]) continue; 
            int i = k + 1, j = nums.length - 1;
            while(i < j){
                int sum = nums[i] + nums[j] + nums[k];
                if(sum < 0) while(i < j && nums[i] == nums[++i]); //跳过重复的数字
                else if(sum > 0) while(i < j && nums[j] == nums[--j]);
                else{
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
                    while(i < j && nums[i] == nums[++i]);
                    while(i < j && nums[j] == nums[--j]);
                }
            }
        }
        return res;
    }
}

DAY4

53:最大子数组和

思路:动态规划。我们要判断的是当以nums[ i ]结尾的时候连续最大和数组。而当nums[ i - 1 ]小于等于0的时候,那么答案就是nums[ i ]这个数。(这里之际二用nums动态存储了前面的最大和,所以可以直接比较)。

其实这里用nums[ i ]小于0的话对nums[ i + 1 ]产生负反馈更好理解,此时就从nums[ i + 1 ]开始作为结果数组的首元素继续判断。

同剑指offer42

class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        for(int i = 1; i < nums.length; i++){
            nums[i] += Math.max(nums[i - 1], 0); //判断i-1是正反馈还是负反馈
            res = Math.max(res, nums[i]); //如果是负反馈,那么从i开始重新为和最大数组的首元素
        }
        return res;
    }
}

20:有效的括号

思路:栈。遇到左括号就在栈里放入该对应的右括号,如果按顺序遍历到的括号与pop出的元素相等那就是正确的。

class Solution {
    public boolean isValid(String s) {
        if(s.isEmpty()) return true;
        Stack<Character> stack = new Stack<>();
        for(char c : s.toCharArray()){
            if(c == '(') stack.push(')');
            else if(c == '{') stack.push('}');
            else if(c == '[') stack.push(']');
            else if(stack.empty() || c != stack.pop()) return false; //pop出的就是该有的闭括号
        }
        if(stack.empty()) return true;
        return false;
    }
}

70:爬楼梯

思路:动态规划。斐波那契函数。

同剑指offer10:青蛙跳台阶

class Solution {
    public int climbStairs(int n) {
        if(n == 1 || n == 2) return n;
        int a = 0, b = 0, f = 1;
        for(int i = 0; i < n; i++){
            a = b;
            b = f;
            f = a + b;
        }
        return f;
    }
}

DAY5

21:合并两个有序链表

同剑指offer25:https://blog.youkuaiyun.com/weixin_45876739/article/details/125698816

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode head = new ListNode(0), cur = head;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                cur.next = list1;
                list1 = list1.next;
            }else{
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        cur.next = list1 != null ? list1 : list2;
        return head.next; 
    }
}

DAY6

22:括号生成

思路:回溯算法(参考题解:笨猪爆破组)。首先下一个添加的是左括号还是右括号需要分情况判断

  • 当剩余的左括号>0时可以一直选左括号
  • 当剩余左括号<右括号时才可以选右括号,不然就是非法的

一共有n对,所以最后生成的每组长度应该是n*2

回溯过程

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        dfs(n, n, "", n * 2);
        return res;
    }
    public void dfs(int left, int right, String str, int len){
        //left和right分别表示剩余左右括号的个数
        if(len == str.length()){
            res.add(str);
            return;
        }
        if(left > 0) dfs(left - 1, right, str+"(", len);
        if(right > left) dfs(left, right - 1, str+")", len); //右括号剩余的多才能选它
    }
}

DAY7

215:数组中的第k个最大元素

思路:大顶堆和小顶堆,但不知道为什么我的这么慢。

//大顶堆 56ms
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //大顶堆
        PriorityQueue<Integer> heap = new PriorityQueue<>((x, y) -> (y - x));
        for(int n :nums) 
            heap.offer(n);
        for(int i = 0; i < k - 1; i++) //弹出前k-1个数字,最后顶部就是第k个最大元素
            heap.poll();
        return heap.peek();
    }
}
//小顶堆 39ms
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //小顶堆,堆顶是最小元素
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        int i = 0;
        for(; i < k; i++)
            heap.add(nums[i]);

        for(; i < nums.length; i++)
            if(heap.peek() < nums[i]){
                heap.poll();
                heap.add(nums[i]);
            }
        return heap.peek();
    }
}

DAY8

206:反转链表

思路:pre指向原链表中的cur的前一个节点,暂存cur.next,然后将cur的next指向pre,再将pre移动到cur,cur移动到刚刚暂存的next里即可。

最后cur指向了null,所以需要输出的是pre。

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = head, pre = null;
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

45:跳跃游戏-Ⅱ

思路:贪心算法。每走到一个元素,判断它可以移动到的范围,设置一个边界。maxPosition存放在边界范围内可以跳出的最大位置(也就是在比较上一个数的跳跃范围内,可以跳到下一个更远的位置最大是多少)。

class Solution {
    public int jump(int[] nums) {
        int end = 0, maxPosition = 0, steps = 0;
        for(int i = 0; i < nums.length - 1; i++){ //注意这里边界,最后一个不用判断
            maxPosition = Math.max(maxPosition, i + nums[i]); //边界就是索引值+该索引对应的值
            if(i == end){ //走到边界就更新
                end = maxPosition;
                steps++;
            }
        }
        return steps;
    }
}

DAY9

104:二叉树的深度

思路:就一个简单的递归,然后计数。

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        else{
            int left = maxDepth(root.left);
            int right = maxDepth(root.right);
            return Math.max(left, right) + 1;
        }
    }
}

121:买股票的最佳时机

思路:动态规划。同剑指offer63.

注意:minprices要保证第一步将第一个数无论大小都要存为min,所以应该设置为Integer.MAX_VALUE。

还有if里的语句顺序不能颠倒(弱智错误,颠倒了压根儿没存min)

class Solution {
    public int maxProfit(int[] prices) {
        int minprices = Integer.MAX_VALUE, maxprofit = 0;
        for(int i = 0; i < prices.length; i++){
            if(prices[i] < minprices)  minprices = prices[i];
            else if(prices[i] - minprices > maxprofit)
                maxprofit = prices[i] - minprices;
        }
        return maxprofit;
    }
}

136:只出现一次的数字

思路:异或。这两个字,深深敲击我的心。只能说,妙到家了。

class Solution {
    public int singleNumber(int[] nums) {
        //异或!!!
        int res = nums[0]; //如果数组中只有一个数字直接输出即可
        for(int i = 1; i < nums.length; i++){
            res = res ^ nums[i];
        }
        return res;
    }
}

DAY10

94:二叉树的中序遍历

思路一:递归,很简单。

思路二:迭代,用栈模拟中序遍历(先进后出)。先遍历左子树,将每个遍历过的节点放入栈中,当没有左孩子的时候弹出本身,然后遍历右子树。好理解但是不好写,多看看。

//思路一
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        dfs(list, root);
        return list;
    }
    void dfs(List<Integer> list, TreeNode root){
        if(root == null) return;
        dfs(list, root.left);
        list.add(root.val);
        dfs(list, root.right);
    }
}
//思路二
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        while(stack.size() > 0 || root != null){ //注意循环条件
            if(root != null){ //判断节点是否有左孩子,一直往左下走
                stack.add(root);
                root = root.left;
            }else{
                TreeNode temp = stack.pop();
                res.add(temp.val); //最后输出的是数组
                root = temp.right; //左遍历完输出本身后判断右节点
            }
        }
        return res;
    }
}

101:对称的二叉树

思路:递归。同剑指offer28。

  • 当左右孩子都为空则是对称的;
  • 当只有左孩子或右孩子或左右两个节点值不相等则不对称
  • 判断左右孩子的孩子是否对称。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return root == null ? true : check(root.left, root.right);
    }
    public boolean check(TreeNode left, TreeNode right){
        if(left == null && right == null) return true;
        else if(left == null || right == null || left.val != right.val) return false;
        return check(left.left, right.right) && check(left.right, right.left);
    }
}

DAY11

226:反转二叉树

思路:递归。万恶的递归啊真的看到这么简单的代码,自己半天p都憋不出来。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return null;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

534:二叉树的直径

思路:递归。递归计算左右子树的深度,每次比较直径和当前最大值选出最大的。注意diameter函数里调用dfs,但需要的返回值是max。而dfs函数则需要返回节点深度。

class Solution {
    int max = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return max;
    }
    int dfs(TreeNode root){
        if(root == null) return 0;
        int left = dfs(root.left);
        int right = dfs(root.right);
        max = Math.max(left + right, max); //最大直径和当前最大值比较取大的
        return Math.max(left, right) + 1; //返回节点深度
    }
}

DAY12

69:x的平方根

思路:二分查找。解答里的牛顿迭代法我实在是看不懂....这个好理解一些。

  • 若这个整数的平方=输入整数,则这个整数就是平方根
  • 若这个整数平方>输入整数,则这个整数一定不是它的平方根
  • 若这个整数<输入整数,则需要可能是它的平方根(例如8的平方根2.8取的是2不是3)

所以我们需要对最后一种情况进行判断。

class Solution {
    public int mySqrt(int x) {
        if(x == 0 || x == 1) return x;
        int left = 1;
        int right = x / 2;
        while(left < right){
            int mid = left + (right - left + 1) / 2;
            if(mid > x / mid){ // 相当于判断mid的平方是否大于x,但乘法可能越界
                right = mid - 1; //在前半部分搜索 不-1可能会越界
            }else{
                left = mid;
            }
        }
        return left;
    }
}

539:最小时间差(大疆笔试)

思路:这题太诡异了。列表里存储的时分,但是在表盘上可能存在指针左右两面夹角相同的,所以用数组存储当天的秒和下一天的秒(过程比较复杂)。然后将得到的秒数(就看做时间戳好理解)排序,然后选出相差最小的即为答案。(这是人类想出来的题目吗???)

class Solution {
    public int findMinDifference(List<String> timePoints) {
        int n = timePoints.size() * 2; //记录每天和下一天改时间点的偏移量
        int[] nums = new int[n];
        for(int i = 0, index = 0; i < n / 2; i++, index += 2){
            String[] str = timePoints.get(i).split(":");
            int hour = Integer.parseInt(str[0]), min = Integer.parseInt(str[1]);
            nums[index] = hour * 60 + min;
            nums[index + 1] = nums[index] +1440; //加一天24h的秒数
        }
        Arrays.sort(nums);
        int res = nums[1] - nums[0];
        for(int i = 0; i < n - 1; i++)
            res = Math.min(res, nums[i + 1] - nums[i]);
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值