LeetCode笔记01

  1. 两数之和(Easy)

问题:

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

思路:

这道题想要解出来很简单, 循环暴力破解就行, 但是想要使算法复杂度小, 就要去换个方法了, 这里使用哈希表(HashMap)这个数据结构, 特点是键不可重复。

我们遍历数组的时候, 将target - nums[i]当作key(这样只需要看后面有没有等于这个key的, 有的话我们就可以直接找到, 不用像之前那样那两个数的和取进行对比, 相当于拿着答案去找有没有), 将索引i作为value存入map, 下次进入循环时, 先看map里的keys有没有num[i], 有的话直接返回结果。

示例:

public int[] twoSum(int[] nums, int target) {
    int[] indexs = new int[2];
    HashMap<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int num = nums[i];
        if (map.containsKey(num)) {
            indexs[0] = map.get(num);
            indexs[1] = i;
            return indexs;
        }
        map.put(target - num, i);
    }
    return indexs;
}

2. 两数相加(Medium)

问题:

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

思路:

这道题是一道中等难度的题, 思路并不是很难, 就是中间有一些细节需要注意:

  1. 使用临时变量来保存参数, 方便操作
  2. 由于最后要返回头节点, 所以对于我们要返回的result也进行"备份"(这里的备份并不是真正的备份, 如果p = result, 其实p和result是一样的, 他们指向同一个地址), 后面队p操作不管到了那个next, reslut都还是头节点
  3. 两数相加可能会大于10, 大于10就要取余, 并且把多出来的进给下一位
  4. 当两个链表都遍历完时, 也不能忘记查看是否有进位, 有进位需要把进位给到下个节点再结束

示例:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode p1 = l1, p2 = l2;
    ListNode result = new ListNode(0);
    ListNode p = result;
    int extra = 0;
    while (p1 != null || p2 != null || extra > 0) {
        int val = extra;
        if (p1 != null) {
            val += p1.val;
            p1 = p1.next;
        }
        if (p2 != null) {
            val += p2.val;
            p2 = p2.next;
        }
        p.next = new ListNode(val % 10);
        extra = val / 10;
        p = p.next;
    }
    return result.next;
}

3. 无重复字符的最长字串(Medium)

问题:

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,
"pwke" 是一个子序列,不是子串。

思路:

这是一个典型的滑动窗口问题, 我推荐大家去看labuladong大佬的文章, 讲的十分清晰, 我就简单复述一下:

使用左右双指针, 右指针先滑动, 当条件达到window need shrink时, 左指针开始滑动, 当达到目标结果时, 保存结果, 以便最后进行对比, 找出最优结果。

int left = 0, right = 0;

while (right < s.size()) {
    // 增大窗口
    window.add(s[right]);
    right++;
    
    while (window needs shrink) {
        // 缩小窗口
        window.remove(s[left]);
        left++;
    }
}

单就这道题而言, 右指针不断滑动增大窗口, 当发现重复值后, 左指针滑动缩小窗口, 期间将窗口内没有重复值的结果不断记录更新其最大值。

示例:

// 这里先给一个比较清晰的示例
public int lengthOfLongestSubstring(String s) {
    if (s.length() <= 1) {
        return s.length();
    }
    HashMap<Character, Integer> window = new HashMap<>();
    int res = 0;
    int left = 0, right = 0;
    while (right < s.length()) {
        char c = s.charAt(right);
        window.put(c, window.getOrDefault(c, 0) + 1);
        right++;
        while (window.get(c) > 1) {
            char d = s.charAt(left);
            window.replace(d, window.get(d) - 1);
            left++;
        }
        res = res > right - left ? res : right - left;
    }
    return res;
}
// 再来一个刷记录的
/**
 * 这种做法就是将窗口抽象成两层for循环
 * 将左右指针变成了for循环中的i和j
 * 每次发现窗口内有重复值, 就直接将low指针收缩到没有重复值的位置
 * 而 i - low + 1 才是我们需要的窗口大小
 */
public int lengthOfLongestSubstring(String s) {
    if (s.length() <= 1) {
        return s.length();
    }
    int res = 0;
    int low = 0;
    char[] chars = s.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        for (int j = low; j < i; j++) {
            if (chars[i] == chars[j]) {
                low = j + 1;
                break;
            }
        }
        res = res > (i - low + 1) ? res : (i - low + 1);
    }
    return res;
}

4. 寻找两个正序数组的中位数(Hard)

问题:

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

思路:

对于一道Hard题而言, 我的思路属实不值一提, 这里给大家简述一下力扣评论区一位大佬的思路, 感谢Wait想念大佬:

  1. 对于两个数组而言, 我们分别找第 (m+n+1) / 2 个, 和 (m+n+2) / 2 个, 然后求其平均值即可, 这对奇偶数均适用。
  2. 定义一个函数, 这个函数的目的就是找到第K位元素, 函数的具体实现思路如下:
  3. 对于边界值而言, 如果起始值大于等于数组长度, 说明此数组已经没有我们要找的数了, 看作是空数组, 直接在另一个数组中找到第k位数进行返回, 而如果k=1, 说明两个数组的起始位置的数最小的那个就是我们要的(为什么是最小的呢? 两个数那个最小, 那个就是第一个嘛)。
  4. 对于一般情况来说, 我们对两个数组都去找第k/2位元素, 但是如果有一个数组没有k/2个元素, 那么说明这个数组目前是影响不到结果的, 直接对另一个数组进行剪枝(减去k/2个元素, 也就是使数组的起始位置加k/2), 并递归进入下一轮寻找; 如果都有第k/2个元素, 那么比较两个数组的第k/2个元素的大小, 对小的数组剪枝(因为第k/2个元素小的数组的前k/2个元素是肯定比中位数小的), 并继续递归。
  5. 每次剪枝完进入下一次的时候, 此时的数组已经减去了k/2个比中位数小的元素, 那么现在我们要找的就不是第K个元素了, 而是第k-k/2个元素

示例:

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int m = nums1.length;
    int n = nums2.length;
    int left = (m + n + 1) / 2;
    int right = (m + n + 2) / 2;
    return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2;
}

// 找到第K个元素
public double findKth(int[] nums1, int i, int[] nums2, int j, int k) {
    /**
     * 如果起始值大于等于数组长度,
     * 说明此数组已经没有我们要找的数了, 看作是空数组,
     * 直接在另一个数组中找到第k位数进行返回
     */
    if (i >= nums1.length) {
        return nums2[j + k - 1];
    }
    if (j >= nums2.length) {
        return nums1[i + k - 1];
    }

    /**
     * 如果k=1, 说明两个数组的起始位置的数最小的那个就是我们要的
     */
    if (k == 1) {
        return Math.min(nums1[i], nums2[j]);
    }

    /**
     * 但是如果有一个数组没有k/2个元素,
     * 那么说明这个数组目前是影响不到结果的, 直接对另一个数组进行剪枝
     * 这里是给没有k/2个元素的数组赋了一个最大值, 在下面进行统一剪枝递归
     */
    int midNum1 = (i + k / 2 - 1 < nums1.length) ? nums1[i + k / 2 - 1] : Integer.MAX_VALUE;
    int midNum2 = (j + k / 2 - 1 < nums2.length) ? nums2[j + k / 2 - 1] : Integer.MAX_VALUE;

    /**
     * 比较两个数组的第k/2个元素的大小, 对小的数组剪枝
     * 剪枝完我们要找的就不是第K个元素了, 而是第k-k/2个元素
     */
    if (midNum1 < midNum2) {
        return findKth(nums1, i + k / 2, nums2, j, k - k / 2);
    } else {
        return findKth(nums1, i, nums2, j + k / 2, k - k / 2);
    }
}

我的个人主页: www.ayu.link
本文连接: [┏ (゜ω゜)=☞]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值