diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7625fb2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Python files +__pycache__/ +.DS_Store diff --git a/0001-Two-Sum/Animation/Animation.gif b/0001-Two-Sum/Animation/Animation.gif new file mode 100644 index 00000000..1f65bd3f Binary files /dev/null and b/0001-Two-Sum/Animation/Animation.gif differ diff --git a/0001-Two-Sum/Article/0001-Two-Sum.md b/0001-Two-Sum/Article/0001-Two-Sum.md new file mode 100644 index 00000000..95e8591c --- /dev/null +++ b/0001-Two-Sum/Article/0001-Two-Sum.md @@ -0,0 +1,148 @@ +# LeetCode 第 1 号问题:两数之和 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com +> + +题目来源于 LeetCode 上第 1 号问题:两数之和。题目难度为 Easy,目前通过率为 45.8% 。 + +### 题目描述 + +给定一个整数数组 `nums` 和一个目标值 `target`,请你在该数组中找出和为目标值的那 **两个** 整数,并返回他们的数组下标。 + +你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + +**示例:** + +``` +给定 nums = [2, 7, 11, 15], target = 9 + +因为 nums[0] + nums[1] = 2 + 7 = 9 +所以返回 [0, 1] +``` + +### 题目解析 + +使用查找表来解决该问题。 + +设置一个 map 容器 record 用来记录元素的值与索引,然后遍历数组 nums。 + +* 每次遍历时使用临时变量 complement 用来保存目标值与当前值的差值 +* 在此次遍历中查找 record ,查看是否有与 complement 一致的值,如果查找成功则返回查找值的索引值与当前变量的值 i +* 如果未找到,则在 record 保存该元素与索引值 i + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 +#### C++ +``` +// 1. Two Sum +// https://leetcode.com/problems/two-sum/description/ +// 时间复杂度:O(n) +// 空间复杂度:O(n) +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map record; + for(int i = 0 ; i < nums.size() ; i ++){ + + int complement = target - nums[i]; + if(record.find(complement) != record.end()){ + int res[] = {i, record[complement]}; + return vector(res, res + 2); + } + + record[nums[i]] = i; + } + return {}; + } +}; + +``` +#### C +```c +// 1. Two Sum +// https://leetcode.com/problems/two-sum/description/ +// 时间复杂度:O(n) +// 空间复杂度:O(n) +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* twoSum(int* nums, int numsSize, int target, int* returnSize){ + int *ans=(int *)malloc(2 * sizeof(int)); + int i,j; + bool flag=false; + for(i=0;i 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 2 号问题:两数相加。题目难度为 Medium,目前通过率为 33.9% 。 + +### 题目描述 + +给出两个 **非空** 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 **逆序** 的方式存储的,并且它们的每个节点只能存储 **一位** 数字。 + +如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 + +您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 + +**示例:** + +``` +输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) +输出:7 -> 0 -> 8 +原因:342 + 465 = 807 +``` + +### 题目解析 + +设立一个表示进位的变量`carried`,建立一个新链表,把输入的两个链表从头往后同时处理,每两个相加,将结果加上`carried`后的值作为一个新节点到新链表后面。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +#### C++ +```c++ +/// 时间复杂度: O(n) +/// 空间复杂度: O(n) +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + + ListNode *p1 = l1, *p2 = l2; + ListNode *dummyHead = new ListNode(-1); + ListNode* cur = dummyHead; + int carried = 0; + while(p1 || p2 ){ + int a = p1 ? p1->val : 0; + int b = p2 ? p2->val : 0; + cur->next = new ListNode((a + b + carried) % 10); + carried = (a + b + carried) / 10; + + cur = cur->next; + p1 = p1 ? p1->next : NULL; + p2 = p2 ? p2->next : NULL; + } + + cur->next = carried ? new ListNode(1) : NULL; + ListNode* ret = dummyHead->next; + delete dummyHead; + return ret; + } +}; + +``` +#### Java +```java +class Solution { + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode dummyHead = new ListNode(0); + ListNode cur = dummyHead; + int carry = 0; + + while(l1 != null || l2 != null) + { + int sum = carry; + if(l1 != null) + { + sum += l1.val; + l1 = l1.next; + } + if(l2 != null) + { + sum += l2.val; + l2 = l2.next; + } + // 创建新节点 + carry = sum / 10; + cur.next = new ListNode(sum % 10); + cur = cur.next; + + } + if (carry > 0) { + cur.next = new ListNode(carry); + } + return dummyHead.next; + } +} +``` +#### Python +```python +class Solution(object): + def addTwoNumbers(self, l1, l2): + res=ListNode(0) + head=res + carry=0 + while l1 or l2 or carry!=0: + sum=carry + if l1: + sum+=l1.val + l1=l1.next + if l2: + sum+=l2.val + l2=l2.next + # set value + if sum<=9: + res.val=sum + carry=0 + else: + res.val=sum%10 + carry=sum//10 + # creat new node + if l1 or l2 or carry!=0: + res.next=ListNode(0) + res=res.next + return head +``` + + +![](../../Pictures/qrcode.jpg) + diff --git a/0003-Longest-Substring-Without-Repeating-Characters/Article/0003-Longest-Substring-Without-Repeating-Characters.md b/0003-Longest-Substring-Without-Repeating-Characters/Article/0003-Longest-Substring-Without-Repeating-Characters.md new file mode 100644 index 00000000..2f465969 --- /dev/null +++ b/0003-Longest-Substring-Without-Repeating-Characters/Article/0003-Longest-Substring-Without-Repeating-Characters.md @@ -0,0 +1,66 @@ +# LeetCode 第 3 号问题:无重复字符的最长子串 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 3 号问题:无重复字符的最长子串。题目难度为 Medium,目前通过率为 29.0% 。 + +### 题目描述 + +给定一个字符串,请你找出其中不含有重复字符的 **最长子串** 的长度。 + +**示例 1:** + +```java +输入: "abcabcbb" +输出: 3 +解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 +``` + +### 题目解析 + +建立一个256位大小的整型数组 freg ,用来建立字符和其出现位置之间的映射。 + +维护一个滑动窗口,窗口内的都是没有重复的字符,去尽可能的扩大窗口的大小,窗口不停的向右滑动。 + +- (1)如果当前遍历到的字符从未出现过,那么直接扩大右边界; +- (2)如果当前遍历到的字符出现过,则缩小窗口(左边索引向右移动),然后继续观察当前遍历到的字符; +- (3)重复(1)(2),直到左边索引无法再移动; +- (4)维护一个结果res,每次用出现过的窗口大小来更新结果 res,最后返回 res 获取结果。 + +### 动画描述 + +![动画描述](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/20ahe.gif) + +### 代码实现 + +```c++ +// 滑动窗口 +// 时间复杂度: O(len(s)) +// 空间复杂度: O(len(charset)) +class Solution { +public: + int lengthOfLongestSubstring(string s) { + int freq[256] = {0}; + int l = 0, r = -1; //滑动窗口为s[l...r] + int res = 0; + // 整个循环从 l == 0; r == -1 这个空窗口开始 + // 到l == s.size(); r == s.size()-1 这个空窗口截止 + // 在每次循环里逐渐改变窗口, 维护freq, 并记录当前窗口中是否找到了一个新的最优值 + while(l < s.size()){ + if(r + 1 < s.size() && freq[s[r+1]] == 0){ + r++; + freq[s[r]]++; + }else { //r已经到头 || freq[s[r+1]] == 1 + freq[s[l]]--; + l++; + } + res = max(res, r-l+1); + } + return res; + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0004-median-of-two-sorted-arrays/Animation/Animation.gif b/0004-median-of-two-sorted-arrays/Animation/Animation.gif new file mode 100644 index 00000000..89f4b8a4 Binary files /dev/null and b/0004-median-of-two-sorted-arrays/Animation/Animation.gif differ diff --git a/0004-median-of-two-sorted-arrays/Animation/case1.png b/0004-median-of-two-sorted-arrays/Animation/case1.png new file mode 100644 index 00000000..c50983b0 Binary files /dev/null and b/0004-median-of-two-sorted-arrays/Animation/case1.png differ diff --git a/0004-median-of-two-sorted-arrays/Animation/case2.png b/0004-median-of-two-sorted-arrays/Animation/case2.png new file mode 100644 index 00000000..a9f05563 Binary files /dev/null and b/0004-median-of-two-sorted-arrays/Animation/case2.png differ diff --git a/0004-median-of-two-sorted-arrays/Animation/case3.png b/0004-median-of-two-sorted-arrays/Animation/case3.png new file mode 100644 index 00000000..3dff2b91 Binary files /dev/null and b/0004-median-of-two-sorted-arrays/Animation/case3.png differ diff --git a/0004-median-of-two-sorted-arrays/Animation/image1.PNG b/0004-median-of-two-sorted-arrays/Animation/image1.PNG new file mode 100644 index 00000000..1492f4c4 Binary files /dev/null and b/0004-median-of-two-sorted-arrays/Animation/image1.PNG differ diff --git a/0004-median-of-two-sorted-arrays/Animation/image2.PNG b/0004-median-of-two-sorted-arrays/Animation/image2.PNG new file mode 100644 index 00000000..f1c52b1f Binary files /dev/null and b/0004-median-of-two-sorted-arrays/Animation/image2.PNG differ diff --git a/0004-median-of-two-sorted-arrays/Article/0004-median-of-two-sorted-arrays.md b/0004-median-of-two-sorted-arrays/Article/0004-median-of-two-sorted-arrays.md new file mode 100644 index 00000000..f9ccfa7f --- /dev/null +++ b/0004-median-of-two-sorted-arrays/Article/0004-median-of-two-sorted-arrays.md @@ -0,0 +1,151 @@ +# LeetCode 第 4 号问题:寻找两个正序数组的中位数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 4 号问题:寻找两个正序数组的中位数。题目难度为 Hard,目前通过率为 29.0% 。 + +#### 题目描述 + +> 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。 +请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。 +你可以假设 nums1 和 nums2 不会同时为空。 + +```java +示例1: +nums1 = [1, 3] +nums2 = [2] + +则中位数是 2.0 + +示例2: +nums1 = [1, 2] +nums2 = [3, 4] + +则中位数是 (2 + 3)/2 = 2.5 +``` + +#### 题目解析 +这道题网络上的解析都非常“高深”,很难理解。私以为它们都将简单的问题复杂化了。本题在一些处理上确实会有些麻烦,比如数组边界的处理,和偶数个数的中位数的处理。但其核心思想并不复杂。 + +首先,我们可以只考虑数字总个数为奇数的情况。让我们看下下图: + +![](../Animation/image1.PNG) + +蓝框是中位数左边的数(包括中位数),而橘框则为中位数右边的数。 + +3个显然的规则: +1.两个数组的蓝框总个数=(数字总个数+1)/2; +2.所有蓝框内的数都小于橘框内的数 +3.中位数为蓝框中最大的那一位(即数组1蓝框最后一位,或数组2蓝框最后一位) +![](../Animation/image2.PNG) +如图,我们要找到一组A,B,满足上面3条规则。 +对于规则1,我们在数组1中找任意A,然后根据规则1就能推算出对应的B的位置。 +对于规则2,由于数组1和2都是有序数组,即X1=和<=。 +下面提供了作者的一份代码,leetcode上的结果为:执行用时:2 ms;内存消耗:40.3 MB,都超过了100%的用户。读者可以参考一下。 + + +#### 代码实现 + +Java语言 + +```java +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + // 使nums1成为较短数组,不仅可以提高检索速度,同时可以避免一些边界问题 + if (nums1.length > nums2.length) { + int[] temp = nums1; + nums1 = nums2; + nums2 = temp; + } + + int len1 = nums1.length; + int len2 = nums2.length; + int leftLen = (len1 + len2 + 1) / 2; //两数组合并&排序后,左半边的长度 + + // 对数组1进行二分检索 + int start = 0; + int end = len1; + while (start <= end) { + // 两个数组的被测数A,B的位置(从1开始计算) + // count1 = 2 表示 num1 数组的第2个数字 + // 比index大1 + int count1 = start + ((end - start) / 2); + int count2 = leftLen - count1; + + if (count1 > 0 && nums1[count1 - 1] > nums2[count2]) { + // A比B的next还要大 + end = count1 - 1; + } else if (count1 < len1 && nums2[count2 - 1] > nums1[count1]) { + // B比A的next还要大 + start = count1 + 1; + } else { + // 获取中位数 + int result = (count1 == 0)? nums2[count2 - 1]: // 当num1数组的数都在总数组右边 + (count2 == 0)? nums1[count1 - 1]: // 当num2数组的数都在总数组右边 + Math.max(nums1[count1 - 1], nums2[count2 - 1]); // 比较A,B + if (isOdd(len1 + len2)) { + return result; + } + + // 处理偶数个数的情况 + int nextValue = (count1 == len1) ? nums2[count2]: + (count2 == len2) ? nums1[count1]: + Math.min(nums1[count1], nums2[count2]); + return (result + nextValue) / 2.0; + } + } + + return Integer.MIN_VALUE; // 绝对到不了这里 + } + + // 奇数返回true,偶数返回false + private boolean isOdd(int x) { + return (x & 1) == 1; + } +} +``` + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 复杂度分析 + ++ 时间复杂度:对数组进行二分查找,因此为O(logN) ++ 空间复杂度:O(1) + + + + + +![](../../Pictures/qrcode.jpg) diff --git a/0004-median-of-two-sorted-arrays/Code/1.java b/0004-median-of-two-sorted-arrays/Code/1.java new file mode 100644 index 00000000..cfb36cfd --- /dev/null +++ b/0004-median-of-two-sorted-arrays/Code/1.java @@ -0,0 +1,54 @@ +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + // ʹnums1Ϊ϶,߼ٶ,ͬʱԱһЩ߽ + if (nums1.length > nums2.length) { + int[] temp = nums1; + nums1 = nums2; + nums2 = temp; + } + + int len1 = nums1.length; + int len2 = nums2.length; + int leftLen = (len1 + len2 + 1) / 2; //ϲ&,ߵij + + // 1жּ + int start = 0; + int end = len1; + while (start <= end) { + // ıA,Bλ(1ʼ) + // count1 = 2 ʾ num1 ĵ2 + // index1 + int count1 = start + ((end - start) / 2); + int count2 = leftLen - count1; + + if (count1 > 0 && nums1[count1 - 1] > nums2[count2]) { + // ABnextҪ + end = count1 - 1; + } else if (count1 < len1 && nums2[count2 - 1] > nums1[count1]) { + // BAnextҪ + start = count1 + 1; + } else { + // ȡλ + int result = (count1 == 0)? nums2[count2 - 1]: // num1ұ + (count2 == 0)? nums1[count1 - 1]: // num2ұ + Math.max(nums1[count1 - 1], nums2[count2 - 1]); // ȽA,B + if (isOdd(len1 + len2)) { + return result; + } + + // ż + int nextValue = (count1 == len1) ? nums2[count2]: + (count2 == len2) ? nums1[count1]: + Math.min(nums1[count1], nums2[count2]); + return (result + nextValue) / 2.0; + } + } + + return Integer.MIN_VALUE; // Ե + } + + // true,żfalse + private boolean isOdd(int x) { + return (x & 1) == 1; + } +} diff --git a/0005-Longest Palindromic Substring/Animation/LeetCode5.gif b/0005-Longest Palindromic Substring/Animation/LeetCode5.gif new file mode 100644 index 00000000..f5c49790 Binary files /dev/null and b/0005-Longest Palindromic Substring/Animation/LeetCode5.gif differ diff --git a/0005-Longest Palindromic Substring/Article/0005-Longest Palindromic Substring.md b/0005-Longest Palindromic Substring/Article/0005-Longest Palindromic Substring.md new file mode 100644 index 00000000..78e76895 --- /dev/null +++ b/0005-Longest Palindromic Substring/Article/0005-Longest Palindromic Substring.md @@ -0,0 +1,123 @@ +# LeetCode 第 5 号问题:最长回文串 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com +> + +题目来源于 LeetCode 上第 5 号问题:最长回文串。题目难度为 Medium,目前通过率为 29% 。 + +## 题目描述 + +给定一个字符串,要求这个字符串当中最长的回文串。 + +## 示例 + +``` +Input: "babad" +Output: "bab" +Note: "aba" is also a valid answer. +``` + +``` +Input: "cbbd" +Output: "bb" +``` + +## 题目分析 + +这道题目是典型的看着简单,但是实际上并不简单的问题。 + +我们先从简单的算法开始,最简单的方法当然是暴力。由于我们需要求出最长的回文串,一种方法是求出s串所有的子串,然后一一对比它们是否构成回文。这样当然是可行的,但是我们简单分析一下复杂度就会发现,这并不能接受。对于一个长度为n的字符串来说,我们任意选择其中两个位置,就可以找到它的一个子串,那么我们选择两个位置的数量就是$C_n^2 = \frac{n(n-1)}{2}$。对于每一个子串,我们需要遍历一遍才能判断是否回文,所以整体的复杂度是$O(n^3)$。 + +但是如果你对回文串非常熟悉的话,会发现其实这是可以优化的。因为我们要求的是最长的回文串,如果我们确定了对称中心的位置,它能够构成的最长回文串就是确定的。所以我们只需要遍历所有的回文串中心,和每个中心能找到的最长回文串。这样我们的复杂度就降低了一维,变成了$O(n^2)$。 + +回文串有两种形式,一种是奇回文,也就是回文中心是一个字符,比如aba。还有一种是偶回文,回文中心是两个字符之间,比如abba。这两种情况我们需要分开讨论。 + +我们写出代码: + +```python +class Solution: + def longestPalindrome(self, s: str) -> str: + n = len(s) + + ret = '' + for i in range(n): + # 奇回文的情况 + l, r = i, i + while s[l] == s[r]: + l -= 1 + r += 1 + if l < 0 or r >= n: + break + if r - l - 1 > len(ret): + ret = s[l+1: r] + + # 偶回文的情况 + l, r = i-1, i + while l >= 0 and s[l] == s[r]: + l -= 1 + r += 1 + if l < 0 or r >= n: + break + if r - l - 1 > len(ret): + ret = s[l+1: r] + + return ret +``` + +到这里还没有结束,接下来我们介绍一个经典的回文串求解算法——Manacher,也叫做马拉车算法。 + +首先,我们需要统一奇回文和偶回文这两种情况,这也很方便,我们把原串进行处理,在两个相邻字符当中插入一个分隔字符#,比如abcd转化成#a#b#c#d#。一般我们还会在首尾加入防止超界的字符,比如$&等。之后我们维护两个值,分别是id和mr。mr表示当前能够构成的回文串向右延伸最远的位置,id表示这个位置对应的对称中心。根据这个位置id以及mr我们可以快速地求解出当前位置i能够构成的合法回文串的长度。 + +我们假设每一个位置构成的合法回文串半径是p[i], 那么对于i这个位置,我们可以得到p[i] >= min(mr - i, p[id * 2 - i])。其中id * 2 - i是i这个位置关于id的对称位置,并且以i为中心对称的回文串小于mr位置的部分也关于id对称。所以如果p[id * 2 - i] < mr - i的话,说明i关于id的对称位置没能突破id对称的限制,既然i的对称点没有能突破限制,那么i显然也不行。同理,如果p[id * 2 - i] > mr - i的话,说明i的对称位置没有被id限制住,但是这恰恰说明i被限制住了。因为如果i也能突破mr这个限制的话,那么说明id的对称范围还能扩大,这和我们的前提假设矛盾了。所以只有p[id * 2 - i] == mr - i的情况,i才有可能继续延伸。 + +如果能理解上面的关系,整个算法已经很清楚了,如果没看懂也没有关系,可以看下下面的动图,会展示得更加清楚。 + +理解了上述的算法过程之后剩下的工作就简单了,我们只需要在求解p[i]的同时维护id和mr即可。 + +最后我们来看下算法的复杂度,为什么这是一个O(n)的算法呢?原因很简单,我们只需要关注mr这个变量即可。mr这个变量是递增的,mr每次递增的大小,其实就是p[i] - (mr - i)的长度。所以虽然看似我们用了两重循环,但是由于mr最多只能递增n次,所以它依然是O(n)的算法。 + +#### 动画描述 + +![](../Animation/LeetCode5.gif) + +#### 代码实现 + +```python +class Solution: + + def longestPalindrome(self, s: str) -> str: + # 在所有字符中间插入# + def transform(s): + return '$#' + '#'.join(list(s)) + '#&' + + if s == '': + return s + # 初始化 + s = transform(s) + p = [0 for _ in range(len(s)+1)] + mr, id_ = 0, 0 + # 首尾是特殊字符,所以下标从1到len(s)-2 + for i in range(1, len(s)-1): + # 计算p[i] + p[i] = 1 if mr <= i else min(p[2*id_-i], mr - i) + + # 只有当前i已经摆脱id限制,或者是第三种情况时,才有可能继续延伸 + # 这个只是优化,不加这个判断一样可以运行 + if mr <= i or p[2*id_-i] == mr - i: + while s[i - p[i]] == s[i + p[i]]: + p[i] += 1 + + if i + p[i] > mr: + mr, id_ = i + p[i], i + # 找到长度最长的下标 + id_ = p.index(max(p)) + # 获得整个回文的字符串 + palindromic = s[id_ - p[id_]+1: id_ + p[id_]] + # 过滤掉#,还原为原字符 + return ''.join(filter(lambda x: x != '#', list(palindromic))) + +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0006-ZigZag Conversion/Animation/LeetCode6.gif b/0006-ZigZag Conversion/Animation/LeetCode6.gif new file mode 100644 index 00000000..4b0f7747 Binary files /dev/null and b/0006-ZigZag Conversion/Animation/LeetCode6.gif differ diff --git a/0006-ZigZag Conversion/Article/0006-ZigZag Conversion.md b/0006-ZigZag Conversion/Article/0006-ZigZag Conversion.md new file mode 100644 index 00000000..17a522d0 --- /dev/null +++ b/0006-ZigZag Conversion/Article/0006-ZigZag Conversion.md @@ -0,0 +1,131 @@ +# LeetCode 第 6 号问题:蛇形矩阵 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com +> + +题目来源于 LeetCode 上第 6 号问题:蛇形矩阵。题目难度为 Medium,目前通过率为 35.1% 。 + + +## 题目描述 + +给定一个字符串,和一个整数n,将它排列成一个n行的蛇形返回。 + +## 示例 + +``` +P A H N +A P L S I I G +Y I R +And then read line by line: "PAHNAPLSIIGYIR" + +Write the code that will take a string and make this conversion given a number of rows: + +string convert(string s, int numRows); +Example 1: + +Input: s = "PAYPALISHIRING", numRows = 3 +Output: "PAHNAPLSIIGYIR" +Example 2: + +Input: s = "PAYPALISHIRING", numRows = 4 +Output: "PINALSIGYAHRPI" +Explanation: + +P I N +A L S I G +Y A H R +P I +``` + +给定字符串和蛇形排列占据的行数,要求返回重新排列后的串 + +## 题目解析 + +这道题是一道模拟题,题目的要求就是答案,我们只需要读懂题意就很容易实现。 + +我们最终要输出的是以蛇形摆放之后的字符串再按行串联在一起之后的结果,也就是说每一个字母摆放的列并不重要,重要的是摆放的行号。我们可以很容易想到通过数组维护每一行当中摆放的字母,最后将每一行的结果串联即可。所以问题就只剩下了,我们如何知道每一个字母应该摆放在哪一行? + +其实这也是有规律的,我们通过观察样例可以发现,我们每一个字母摆放的行号先是从0递增到n-1,再从n-1递减到0。我们就模拟这个过程,一个字符一个字符的放置即可。 + +比如字符串是“PAYPALISHIRING ”,rowNum=4。我们可以创建四个空串: + +“” +“” +“” +“” + +然后我们按照蛇形一个字母一个字母地放进这些空串当中: + +当放了第一个字母p之后,变成: + +“p” +“” +“” +“” + +接着放第二个: + +“p” +“a” +“” +“” + +接着第三个: + +“p” +“a” +“y” +“” + +当我们把所有字母都放完了之后,可以得到这样的四个串: + +“PIN” +“ALSIG” +“YAHR” +“PI” + +然后把这四串拼接在一起就行了。 + + +#### 动画描述 + +![](../Animation/LeetCode6.gif) + +#### 代码实现 + +```python +class Solution: + def convert(self, s: str, numRows: int) -> str: + # 记录每一行摆放的字母 + rows = ['' for _ in range(numRows)] + # 记录当前行号 + cur_row = 0 + # 记录当前摆放顺序是否从上往下, False代表从下往上 + forward = True + # numRows = 1直接返回 + if numRows == 1: + return s + + for i, c in enumerate(s): + rows[cur_row] += c + # 根据顺序变更行号 + if forward: + cur_row += 1 + else: + cur_row -= 1 + # 根据行号和当前顺序判断需不需要转向 + if cur_row == numRows - 1 and forward: + forward = False + + if cur_row == 0 and not forward: + forward = True + + ret = '' + for sc in rows: + ret += sc + return ret +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0009-Palindrome-Number/Article/0009-Palindrome-Number.md b/0009-Palindrome-Number/Article/0009-Palindrome-Number.md new file mode 100644 index 00000000..8f7047f1 --- /dev/null +++ b/0009-Palindrome-Number/Article/0009-Palindrome-Number.md @@ -0,0 +1,142 @@ +# LeetCode 第 9 号问题:回文数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 第 9 号问题:回文数。题目难度为 Easy,目前通过率为 56.0%。 + +## 题目描述 + +判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 + +**示例 1:** + +``` +输入: 121 +输出: true +``` + +**示例 2:** + +``` +输入: -121 +输出: false +解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。 +``` + +**示例 3:** + +``` +输入: 10 +输出: false +解释: 从右向左读, 为 01 。因此它不是一个回文数。 +``` + +**进阶:** + +你能不将整数转为字符串来解决这个问题吗? + + + +## 题目解析 + +### 解法一:普通解法 + +最好理解的一种解法就是先将 **整数转为字符串** ,然后将字符串分割为数组,只需要循环数组的一半长度进行判断对应元素是否相等即可。 + +#### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ods8b.gif) + +#### 代码实现 + +```java +///简单粗暴,看看就行 +class Solution { + public boolean isPalindrome(int x) { + String reversedStr = (new StringBuilder(x + "")).reverse().toString(); + return (x + "").equals(reversedStr); + } +} +``` + + + +### 解法二:进阶解法---数学解法 + +通过取整和取余操作获取整数中对应的数字进行比较。 + +举个例子:1221 这个数字。 + +- 通过计算 1221 / 1000, 得首位1 +- 通过计算 1221 % 10, 可得末位 1 +- 进行比较 +- 再将 22 取出来继续比较 + +#### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/v3tkl.gif) + +#### 代码实现 + +```java +class Solution { + public boolean isPalindrome(int x) { + //边界判断 + if (x < 0) return false; + int div = 1; + // + while (x / div >= 10) div *= 10; + while (x > 0) { + int left = x / div; + int right = x % 10; + if (left != right) return false; + x = (x % div) / 10; + div /= 100; + } + return true; + } +} +``` + + + +### 解法三:进阶解法---巧妙解法 + +直观上来看待回文数的话,就感觉像是将数字进行对折后看能否一一对应。 + +所以这个解法的操作就是 **取出后半段数字进行翻转**。 + +这里需要注意的一个点就是由于回文数的位数可奇可偶,所以当它的长度是偶数时,它对折过来应该是相等的;当它的长度是奇数时,那么它对折过来后,有一个的长度需要去掉一位数(除以 10 并取整)。 + +具体做法如下: + +- 每次进行取余操作 ( %10),取出最低的数字:`y = x % 10` +- 将最低的数字加到取出数的末尾:`revertNum = revertNum * 10 + y` +- 每取一个最低位数字,x 都要自除以 10 +- 判断 `x` 是不是小于 `revertNum` ,当它小于的时候,说明数字已经对半或者过半了 +- 最后,判断奇偶数情况:如果是偶数的话,revertNum 和 x 相等;如果是奇数的话,最中间的数字就在revertNum 的最低位上,将它除以 10 以后应该和 x 相等。 + +#### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0siv7.png) + +#### 代码实现 + +```java +class Solution { + public boolean isPalindrome(int x) { + //思考:这里大家可以思考一下,为什么末尾为 0 就可以直接返回 false + if (x < 0 || (x % 10 == 0 && x != 0)) return false; + int revertedNumber = 0; + while (x > revertedNumber) { + revertedNumber = revertedNumber * 10 + x % 10; + x /= 10; + } + return x == revertedNumber || x == revertedNumber / 10; + } +} +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0011-maxArea/Animation/maxArea.gif b/0011-maxArea/Animation/maxArea.gif new file mode 100644 index 00000000..5cec0867 Binary files /dev/null and b/0011-maxArea/Animation/maxArea.gif differ diff --git a/0011-maxArea/Animation/maxArea.mp4 b/0011-maxArea/Animation/maxArea.mp4 new file mode 100644 index 00000000..f6f5aaa5 Binary files /dev/null and b/0011-maxArea/Animation/maxArea.mp4 differ diff --git a/0011-maxArea/Article/0011-maxArea.md b/0011-maxArea/Article/0011-maxArea.md new file mode 100644 index 00000000..79b040a0 --- /dev/null +++ b/0011-maxArea/Article/0011-maxArea.md @@ -0,0 +1,87 @@ +## LeetCode第11号问题:盛水最多的容器 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步个人博客:www.zhangxiaoshuai.fun + +**本题选自leetcode的第11题,medium级别,目前通过率:61.3%** + +**题目描述:** + +```txt +给你n个非负整数a1,a2,...,an,每个数代表坐标中的一个点(i,ai)。在坐标内画n条垂直线, +垂直线i的两个端点分别为(i,ai)和(i,0)。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。 +说明:你不能倾斜容器,且n的值至少为2。 +示例: +输入:[1,8,6,2,5,4,8,3,7] +输出:49 +``` + +我们都应该听说过**木桶原理**,一个木桶可以装入多少水取决于最短的那块板;而这道题也可以与木桶装水的问题对应上。 + 很容易的可以得到---->**容器可以容纳水的容量=两条垂直线中最短的那条*两条线之间的距离** + 现在的情况是,有很多条线,让你计算两两之间能装的最多的水,其实暴力法之间就能解决这个问题,但是它的时间复杂度也达到了**O(n^2)** + +ok,那我们先试试用**暴力法**来解 决问题: + +### 1.暴力法 + +直接上代码: + +```java +public int maxArea(int[] height) { + int res = 0; + for(int i = 0;i < height.length;i++){ + for(int j = i+1;j < height.length;j++){ + int temp = Math.min(height[i],height[j]) * (j-i); + res = Math.max(res,temp); + } + } + return res; +} +``` + +暴力法是可以通过测试的,但是可以看到**程序执行用时**并不理想 + +``` +执行用时 :440 ms, 在所有 Java 提交中击败了17.44% 的用户 +内存消耗 :39.9 MB, 在所有 Java 提交中击败了37.86%的用户 +``` + +### 2.双指针 + +思路:使用两个指针(**resource**和**last**)分别指向数组的第一个元素和最后一个元素,然后我们计算这两条“线”之间能容纳的水的容量,并更新最大容量(初始值为0);接着我们需要将指向元素值小的那个指针前移一步,然后重复上面的步骤,直到**resource = last**循环截止。 + +**GIF动画演示:** + +![](../Animation/maxArea.gif) + +**来看看代码:** + +```java +public int maxArea(int[] height) { + int resource = 0; + int last = height.length - 1; + int res = 0; + while (resource < last) { + if (height[resource] >= height[last]) { + res = Math.max(res, (last - resource) * height[last]); + last--; + } else { + res = Math.max(res, (last - resource) * height[resource]); + resource++; + } + } + return res; +} +``` + +**可以很明显的看到,虽然内存消耗两者是差不多的,但是双指针的速度比暴力解法的速度可是高出好多倍。** + +时间复杂度:**O(n)** 空间复杂度:**O(1)** + +``` +执行用时 :3 ms, 在所有 Java 提交中击败了92.69% 的用户 +内存消耗 :40.3 MB, 在所有 Java 提交中击败了7.86%的用户 +``` + +[视频演示](../Animation/maxArea.mp4) \ No newline at end of file diff --git a/0015-3Sum/Animation/0015-3Sum.m4v b/0015-3Sum/Animation/0015-3Sum.m4v new file mode 100644 index 00000000..8876f8af Binary files /dev/null and b/0015-3Sum/Animation/0015-3Sum.m4v differ diff --git a/0015-3Sum/Animation/Animation.gif b/0015-3Sum/Animation/Animation.gif new file mode 100644 index 00000000..cb92a3fc Binary files /dev/null and b/0015-3Sum/Animation/Animation.gif differ diff --git a/0015-3Sum/Article/0015-3Sum.md b/0015-3Sum/Article/0015-3Sum.md new file mode 100644 index 00000000..cfb923de --- /dev/null +++ b/0015-3Sum/Article/0015-3Sum.md @@ -0,0 +1,54 @@ +# LeetCode 第 15 号问题:三数之和 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 15 号问题:三数之和。 + +### 题目描述 + +给定一个包含 *n* 个整数的数组 `nums`,判断 `nums` 中是否存在三个元素 *a,b,c ,*使得 *a + b + c =* 0 ?找出所有满足条件且不重复的三元组。 + +### 题目解析 + +题目需要我们找出三个数且和为 0 ,那么除了三个数全是 0 的情况之外,肯定会有负数和正数,所以一开始可以先选择一个数,然后再去找另外两个数,这样只要找到两个数且和为第一个选择的数的相反数就行了。也就是说需要枚举 a 和 b ,将 c 的存入 map 即可。 + +需要注意的是返回的结果中,不能有有重复的结果。这样的代码时间复杂度是 O(n^2)。在这里可以先将原数组进行排序,然后再遍历排序后的数组,这样就可以使用双指针以线性时间复杂度来遍历所有满足题意的两个数组合。 + +### 动画描述 + +待补充 + +### 代码实现 + +### + +```c++ +class Solution { +public: + vector> threeSum(vector& nums) { + vector> res; + sort(nums.begin(), nums.end()); + if (nums.empty() || nums.back() < 0 || nums.front() > 0) return {}; + for (int k = 0; k < nums.size(); ++k) { + if (nums[k] > 0) break; + if (k > 0 && nums[k] == nums[k - 1]) continue; + int target = 0 - nums[k]; + int i = k + 1, j = nums.size() - 1; + while (i < j) { + if (nums[i] + nums[j] == target) { + res.push_back({nums[k], nums[i], nums[j]}); + while (i < j && nums[i] == nums[i + 1]) ++i; + while (i < j && nums[j] == nums[j - 1]) --j; + ++i; --j; + } else if (nums[i] + nums[j] < target) ++i; + else --j; + } + } + return res; + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0015-3Sum/Article/0015-3Sum2.md b/0015-3Sum/Article/0015-3Sum2.md new file mode 100644 index 00000000..bf339cb1 --- /dev/null +++ b/0015-3Sum/Article/0015-3Sum2.md @@ -0,0 +1,101 @@ +# LeetCode 第 15 号问题:三数之和 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 15 号问题:三数之和。 + +### 题目描述 + +给定一个包含 *n* 个整数的数组 `nums`,判断 `nums` 中是否存在三个元素 *a,b,c ,*使得 *a + b + c =* 0 ?找出所有满足条件且不重复的三元组。 + +**注意:**答案中不可以包含重复的三元组。 + +#### 示例 + +``` +给定数组 nums = [-1, 0, 1, 2, -1, -4], + +满足要求的三元组集合为: +[ + [-1, 0, 1], + [-1, -1, 2] +] +``` + +### 题目解析 + +最容易想到的就是三重循环暴力法搜索,时间复杂度为 `O(n^3)`. 有点高啊,优化一下. + +通过题目我们了解到,主要问题在于 `搜索所有满足条件的情况` 和 `避免重复项`,那么我们可以使用 `升序数组 + 双指针` 有效处理问题并降低时间复杂度. + +你可能想知道为啥会选择使用这个方案 ? + +首先数组排序时间复杂度可以达到 `O(NlogN)`,这点时间消耗我们是能接受的,另外根据有序数组的特性,数组重复项会挨在一起,不需要额外的空间存储就能跳过重复项,由于是升序,当发现最左边的数值大于0,就可以及时跳出来结束运算. + +双指针可以用来`降维`. 通过遍历数组,取当前下标值为`定值`,双指针代表`定值`后面子数组的`首尾数值`,通过不断靠近双指针来判断三个值的和。 + +具体算法流程如下: + +1. 特判:对于数组长度 `n`,如果数组为 `null` 或者数组长度小于 `3`,返回`[ ]` ; +2. 数组升序排序; +3. 遍历数组: + - 若 `num[i] > 0`:因为是升序,所以结果不可能等于0,直接返回结果; + - 令左指针 `L = i + 1`,右指针 `R = n - 1`,当 `L < R` 时,执行循环: + - 当 `nums[i] + nums[L] + nums[R] == 0` ,执行循环,判断左指针和右指针是否和下一位置重复,`去除重复解`。并同时将 `L,R` 移到下一位置,寻找新的解; + - 若`和`大于 `0`,说明 `nums[R]` 太大,`R指针` 左移 + - 若`和`小于 `0`,说明 `nums[L]` 太小,`L指针` 右移 + +### 动画描述 + +![Animation](../Animation/Animation.gif) + +### 参考代码 + +```javascript + // lang = JavaScript +var threeSum = function(nums) { + let res = []; + if (nums == null || nums.length < 3) { + return res; + } + const len = nums.length; + nums.sort((a, b) => a - b); // 升序 + for (let i = 0; i < len - 2;) { + const element = nums[i]; + if (element > 0) { + // 如果当前数字大于0,则三数之和一定大于0,所以结束循环 + break; + } + let L = i + 1, + R = len - 1; + while (L < R) { + const sum = element + nums[L] + nums[R]; + if (sum == 0) { + res.push([element, nums[L], nums[R]]); + // 左右指针去重 & L+1 & R-1 + while (L < R && nums[L] == nums[++L]); + while (L < R && nums[R] == nums[--R]); + }else if (sum < 0) { + while (L < R && nums[L] == nums[++L]); + }else { + while (L < R && nums[R] == nums[--R]); + } + } + // 定值去重 + while (nums[i] == nums[++i]); + } + return res; +}; +``` + +### 复杂度分析 + +- 时间复杂度:`O(n^2)` + + 数组排序 `O(NlogN)`, 遍历数组`O(n)`, 双指针遍历 `O(n)`, 总体复杂度为 `O(NlogN) + O(n) * O(n)` ,`O(n^2)` + +- 空间复杂度:`O(1)` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0019-Remove-Nth-Node-From-End-of-List/Article/0019-Remove-Nth-Node-From-End-of-List.md b/0019-Remove-Nth-Node-From-End-of-List/Article/0019-Remove-Nth-Node-From-End-of-List.md new file mode 100644 index 00000000..86788d81 --- /dev/null +++ b/0019-Remove-Nth-Node-From-End-of-List/Article/0019-Remove-Nth-Node-From-End-of-List.md @@ -0,0 +1,78 @@ +# LeetCode 第 19 号问题:删除链表的倒数第 N 个节点 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 19 号问题:删除链表的倒数第 N 个节点。题目难度为 Medium,目前通过率为 34.4% 。 + +### 题目描述 + +给定一个链表,删除链表的倒数第 *n* 个节点,并且返回链表的头结点。 + +**示例:** + +``` +给定一个链表: 1->2->3->4->5, 和 n = 2. + +当删除了倒数第二个节点后,链表变为 1->2->3->5. +``` + +**说明:** + +给定的 *n* 保证是有效的。 + +**进阶:** + +你能尝试使用一趟扫描实现吗? + +### 题目解析 + +采取双重遍历肯定是可以解决问题的,但题目要求我们一次遍历解决问题,那我们的思路得发散一下。 + +我们可以设想假设设定了双指针`p`和`q`的话,当`q`指向末尾的`NULL`,`p`与`q`之间相隔的元素个数为`n`时,那么删除掉`p`的下一个指针就完成了要求。 + +- 设置虚拟节点`dummyHead`指向`head` +- 设定双指针`p`和`q`,初始都指向虚拟节点`dummyHead` +- 移动`q`,直到`p`与`q`之间相隔的元素个数为`n` +- 同时移动`p`与`q`,直到`q`指向的为`NULL` +- 将`p`的下一个节点指向下下个节点 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/r04hv.gif) + +### 代码实现 + +``` +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + ListNode* dummyHead = new ListNode(0); + dummyHead->next = head; + + ListNode* p = dummyHead; + ListNode* q = dummyHead; + for( int i = 0 ; i < n + 1 ; i ++ ){ + q = q->next; + } + + while(q){ + p = p->next; + q = q->next; + } + + ListNode* delNode = p->next; + p->next = delNode->next; + delete delNode; + + ListNode* retNode = dummyHead->next; + delete dummyHead; + + return retNode; + + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0020-Valid-Parentheses/Article/0020-Valid-Parentheses.md b/0020-Valid-Parentheses/Article/0020-Valid-Parentheses.md new file mode 100644 index 00000000..f99dc8e6 --- /dev/null +++ b/0020-Valid-Parentheses/Article/0020-Valid-Parentheses.md @@ -0,0 +1,113 @@ +# LeetCode 第 20 号问题:有效的括号 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 20 号问题:有效的括号。题目难度为 Easy,目前通过率为 37.8% 。 + +### 题目描述 + +给定一个只包括 `'('`,`')'`,`'{'`,`'}'`,`'['`,`']'` 的字符串,判断字符串是否有效。 + +有效字符串需满足: + +1. 左括号必须用相同类型的右括号闭合。 +2. 左括号必须以正确的顺序闭合。 + +注意空字符串可被认为是有效字符串。 + +**示例 1:** + +``` +输入: "()" +输出: true +``` + +**示例 2:** + +``` +输入: "()[]{}" +输出: true +``` + +**示例 3:** + +``` +输入: "(]" +输出: false +``` + +**示例 4:** + +``` +输入: "([)]" +输出: false +``` + +**示例 5:** + +``` +输入: "{[]}" +输出: true +``` + +### 题目解析 + +这道题让我们验证输入的字符串是否为括号字符串,包括大括号,中括号和小括号。 + +这里我们使用**栈**。 + +- 遍历输入字符串 +- 如果当前字符为左半边括号时,则将其压入栈中 +- 如果遇到右半边括号时,**分类讨论:** +- 1)如栈不为空且为对应的左半边括号,则取出栈顶元素,继续循环 +- 2)若此时栈为空,则直接返回false +- 3)若不为对应的左半边括号,反之返回false + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xu55u.gif) + +### 代码实现 + +``` +class Solution { +public: + bool isValid(string s) { + + stack stack; + for( int i = 0 ; i < s.size() ; i ++ ) + if( s[i] == '(' || s[i] == '{' || s[i] == '[') + stack.push(s[i]); + else{ + + if( stack.size() == 0 ) + return false; + + char c = stack.top(); + stack.pop(); + + char match; + if( s[i] == ')' ){ + match = '('; + } + else if( s[i] == ']' ){ + match = '['; + } + else{ + match = '{'; + } + + if(c != match) return false; + } + + if( stack.size() != 0 ) + return false; + + return true; + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0021-Merge-Two-Sorted-Lists/Article/0021-Merge-Two-Sorted-Lists.md b/0021-Merge-Two-Sorted-Lists/Article/0021-Merge-Two-Sorted-Lists.md new file mode 100644 index 00000000..2f86a6f5 --- /dev/null +++ b/0021-Merge-Two-Sorted-Lists/Article/0021-Merge-Two-Sorted-Lists.md @@ -0,0 +1,103 @@ +# LeetCode 第 21 号问题:合并两个有序链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 21 号问题:合并两个有序链表。题目难度为 Easy,目前通过率为 45.8% 。 + +### 题目描述 + +将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + +**示例:** + +``` +输入:1->2->4, 1->3->4 +输出:1->1->2->3->4->4 +``` + +### 题目解析 + +#### 一般方案 + +##### 1.1 解题思想 + +> (1)对空链表存在的情况进行处理,假如 pHead1 为空则返回 pHead2 ,pHead2 为空则返回 pHead1。(两个都为空此情况在pHead1为空已经被拦截) +> (2)在两个链表无空链表的情况下确定第一个结点,比较链表1和链表2的第一个结点的值,将值小的结点保存下来为合并后的第一个结点。并且把第一个结点为最小的链表向后移动一个元素。 +> (3)继续在剩下的元素中选择小的值,连接到第一个结点后面,并不断next将值小的结点连接到第一个结点后面,直到某一个链表为空。 +> (4)当两个链表长度不一致时,也就是比较完成后其中一个链表为空,此时需要把另外一个链表剩下的元素都连接到第一个结点的后面。 + +##### 1.2 代码实现 + +```c++ +ListNode* mergeTwoOrderedLists(ListNode* pHead1, ListNode* pHead2){ + ListNode* pTail = NULL;//指向新链表的最后一个结点 pTail->next去连接 + ListNode* newHead = NULL;//指向合并后链表第一个结点 + if (NULL == pHead1){ + return pHead2; + }else if(NULL == pHead2){ + return pHead1; + }else{ + //确定头指针 + if ( pHead1->data < pHead2->data){ + newHead = pHead1; + pHead1 = pHead1->next;//指向链表的第二个结点 + }else{ + newHead = pHead2; + pHead2 = pHead2->next; + } + pTail = newHead;//指向第一个结点 + while ( pHead1 && pHead2) { + if ( pHead1->data <= pHead2->data ){ + pTail->next = pHead1; + pHead1 = pHead1->next; + }else { + pTail->next = pHead2; + pHead2 = pHead2->next; + } + pTail = pTail->next; + + } + if(NULL == pHead1){ + pTail->next = pHead2; + }else if(NULL == pHead2){ + pTail->next = pHead1; + } + return newHead; +} +``` + +#### 2 递归方案 + +##### 2.1 解题思想 + +> (1)对空链表存在的情况进行处理,假如 pHead1 为空则返回 pHead2 ,pHead2 为空则返回 pHead1。 +> (2)比较两个链表第一个结点的大小,确定头结点的位置 +> (3)头结点确定后,继续在剩下的结点中选出下一个结点去链接到第二步选出的结点后面,然后在继续重复(2 )(3) 步,直到有链表为空。 + +##### 2.2 代码实现 + +```c++ +ListNode* mergeTwoOrderedLists(ListNode* pHead1, ListNode* pHead2){ + ListNode* newHead = NULL; + if (NULL == pHead1){ + return pHead2; + }else if(NULL ==pHead2){ + return pHead1; + }else{ + if (pHead1->data < pHead2->data){ + newHead = pHead1; + newHead->next = mergeTwoOrderedLists(pHead1->next, pHead2); + }else{ + newHead = pHead2; + newHead->next = mergeTwoOrderedLists(pHead1, pHead2->next); + } + return newHead; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0022-Generate-Parentheses/Animation/0022-Generate-Parentheses.m4v b/0022-Generate-Parentheses/Animation/0022-Generate-Parentheses.m4v new file mode 100644 index 00000000..e75bf91d Binary files /dev/null and b/0022-Generate-Parentheses/Animation/0022-Generate-Parentheses.m4v differ diff --git a/0022-Generate-Parentheses/Animation/0022-Generate_Parentheses.gif b/0022-Generate-Parentheses/Animation/0022-Generate_Parentheses.gif new file mode 100644 index 00000000..0aee39d8 Binary files /dev/null and b/0022-Generate-Parentheses/Animation/0022-Generate_Parentheses.gif differ diff --git a/0022-Generate-Parentheses/Article/0022-Generate-Parentheses.md b/0022-Generate-Parentheses/Article/0022-Generate-Parentheses.md new file mode 100644 index 00000000..b829ca63 --- /dev/null +++ b/0022-Generate-Parentheses/Article/0022-Generate-Parentheses.md @@ -0,0 +1,149 @@ +# LeetCode 第 22 号问题:生成所有的括号对 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 22 号问题:生成所有的括号对。题目难度为 Medium,目前通过率为 60.9% 。 + +### 题目描述 + +给定数字n,要求使用n对()括号生成所有合法的组合情况。 + +**示例** + +``` +3 + +[ +"((()))", +"(()())", +"(())()", +"()(())", +"()()()" +] +``` + +### 题目解析 + +#### 解法一: 暴力 + +暴力的解法应该最容易想到,因为n是确定的,也就是说一共有n个左括号和n个右括号。很容易想到,我们可以对这2n个字符进行排列组合,之后再对所有的组合进行过滤。留下合法的且不重复的即可。 + +伪代码很容易写: + +```python +def brute_force(str, l, r): + if l == n and r == n: + ans.append(str) + if l < n: + brute_force(str+'(', l+1, r) + if r < n: + brute_force(str+')', l, r+1) +``` + +写完了再根据结果判断是否合法,留下合法的所有情况即可。 + +这样编码的确不难,而且也很容易想到,但是计算n个字符的排列组合复杂度是 $O(2^n)$ 是一个指数级的算法,复杂度是我们不能接受的。而且根据上一题当中的结论,在匹配括号的时候是可以取巧的,我们其实没必要把所有的情况都枚举到。因为想要括号匹配合法,必须有一条,对于字符串当中的任何一个位置i,都必须有:前i个字符中所有左括号的数量大于等于右括号的数量。 +,否则就是非法的。 + +也就是说必须要保证任意一个位置右括号的数量小于等于左括号的数量,不然的话,多余的右括号永远也无法匹配。 + +#### 解法二: 回溯 + +既然左括号的数量必须大于右括号的数量,我们完全可以据此进行优化。我们在递归的时候对l和r进行大小判断,保证所有时刻都有l >= r即可。 + +代码: + +```C++ +class Solution { +public: + + void dfs(int n, int l, int r, string str, vector& vt) { + if (l+r == 2 *n) { + vt.push_back(str); + return ; + } + if (l < n) dfs(n, l+1, r, str+"(", vt); + if (r < l) dfs(n, l, r+1, str+")", vt); + } + + vector generateParenthesis(int n) { + vector vt; + dfs(n, 0, 0, "", vt); + return vt; + } +}; +``` + +#### 解法三: 构造 + +这个方法是我原创的,官方的题解当中没有收录。 + +我们直接求解n的答案的时候是比较困难的,这个时候我们可以把问题拆解,大问题变成小问题,通过小问题的答案构造大问题的答案。上述的两种方法本质上也是一样的思路,不过递归替我们做了问题的拆分。 + +实际上我们可以自己拆分问题,n的时候我们一下子不清楚答案。我们可以先从简单的观察一下结果:比如当n==1的时候,答案就是”()”。n==2有两种:”()()”, “(())”。n==3的时候是5种:”((()))”, “()(())”, “()()()”, “(()())”, “(())()”。 + +细心的读者已经可以总结出规律了,其实并不难想到。 + +solution(n) = solution(i) + solution(n-i) + '(' solution(n-1) ')' + +解释一下这个公式,这里的solution(n)表示n的所有答案串,也就是说n个括号的答案串是可以通过小于n的答案串进行组合的。比如n=1时答案是(),n=2则有两种,一种是用两个n=1拼接,第二种是在n=1的答案外层加上一个括号: + +solution(2) = [()(), (())] + +我们再来看solution(3),它可以用n=1和n=2拼接,以及通过n=2外层加上单独的括号得到所有答案: + +solution(3) = [()()(), ()(()), (())(), (()()), ((()))] + +前面3种是通过solution(2)和solution(1)拼接得到的,后面两种则是在solution(2)外面直接加上括号得到的,这种情况是无法通过拼接得到的情况。我们把这些情况全部汇总,然后去除掉重复的情况就是答案了。 + +这样我们就用小于n的所有结果构造出了n的结果,由于n=0 和n=1的情况是已知的。我们只需要把中间结果存储下来,通过递推就可以获取所有的答案。 + +### 动画描述 + +![](../Animation/0022-Generate_Parentheses.gif) + +### 代码实现 + +```C++ +class Solution { +public: + + vector generateParenthesis(int n) { + // 存储所有中间结果 + map> mp; + set st; + st.insert("()"); + mp[1] = st; + + for (int i = 2; i <= n; i++) { + // 使用set来去重 + set cur; + for (int j = 1; j <= i-1; j++) { + // 取出所有solution(j)和solution(i-j) + set vj = mp[j]; + set vk = mp[i-j]; + for (string str:vj) { + for (string stj : vk) { + cur.insert(str + stj); + } + } + } + // solution(i-1)最外层套上括号 + set vj = mp[i-1]; + for (string str : vj) { + cur.insert("(" + str + ")"); + } + // 得到solution(i) + mp[i] = cur; + } + vector vt; + st = mp[n]; + for (string str : st) vt.push_back(str); + return vt; + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0023-Merge-k-Sorted-Lists/Article/0023-Merge-k-Sorted-Lists.md b/0023-Merge-k-Sorted-Lists/Article/0023-Merge-k-Sorted-Lists.md new file mode 100644 index 00000000..f02b4cf5 --- /dev/null +++ b/0023-Merge-k-Sorted-Lists/Article/0023-Merge-k-Sorted-Lists.md @@ -0,0 +1,187 @@ +# LeetCode 第 23 号问题:合并 K 个排序链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 23 号问题:合并 K 个排序链表。题目难度为 Hard,目前通过率为 45.8% 。 + +### 题目描述 + +合并 *k* 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。 + +**示例:** + +``` +输入: +[ + 1->4->5, + 1->3->4, + 2->6 +] +输出: 1->1->2->3->4->4->5->6 +``` + +**输入** + +![图一](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/u2jnp.jpg) + +**输出** + +![图二](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/yc4ac.jpg) + +### 题目解析 + +### 题目分析一 + +这里需要将这 *k* 个排序链表整合成一个排序链表,也就是说有多个输入,一个输出,类似于漏斗一样的概念。 + +因此,可以利用最小堆的概念。如果你对堆的概念不熟悉,可以戳这先了解一下~ + +取每个 Linked List 的最小节点放入一个 heap 中,排序成最小堆。然后取出堆顶最小的元素,放入输出的合并 List 中,然后将该节点在其对应的 List 中的下一个节点插入到 heap 中,循环上面步骤,以此类推直到全部节点都经过 heap。 + +由于 heap 的大小为始终为 k ,而每次插入的复杂度是 logk ,一共插入了 nk 个节点。时间复杂度为 O(nklogk),空间复杂度为O(k)。 + +### 动画演示 + +![动画演示](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/iuxmh.gif) + +### 代码实现 + +```java +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + //用heap(堆)这种数据结构,也就是 java 里面的 PriorityQueue + PriorityQueue pq = new PriorityQueue<>(new Comparator() { + public int compare(ListNode a, ListNode b) { + return a.val-b.val; + } + }); + ListNode ret = null, cur = null; + for(ListNode node: lists) { + if(null != node) { + pq.add(node); + } + } + while(!pq.isEmpty()) { + ListNode node = pq.poll(); + if(null == ret) { + ret = cur = node; + } + else { + cur = cur.next = node; + } + if(null != node.next) { + pq.add(node.next); + } + } + return ret; + } +} +``` + + + + + +### 题目分析二 + +这道题需要合并 k 个有序链表,并且最终合并出来的结果也必须是有序的。如果一开始没有头绪的话,可以先从简单的开始:**合并 两 个有序链表**。 + +合并两个有序链表:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + +**示例:** + +``` +输入:1->2->4, 1->3->4 +输出:1->1->2->3->4->4 +``` + +这道题目按照题目描述做下去就行:新建一个链表,比较原始两个链表中的元素值,把较小的那个链到新链表中即可。需要注意的一点时由于两个输入链表的长度可能不同,所以最终会有一个链表先完成插入所有元素,则直接另一个未完成的链表直接链入新链表的末尾。 + +所以代码实现很容易写: + +```java +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + //新建链表 + ListNode dummyHead = new ListNode(0); + ListNode cur = dummyHead; + while (l1 != null && l2 != null) { + if (l1.val < l2.val) { + cur.next = l1; + cur = cur.next; + l1 = l1.next; + } else { + cur.next = l2; + cur = cur.next; + l2 = l2.next; + } + } + // 注意点:当有链表为空时,直接连接另一条链表 + if (l1 == null) { + cur.next = l2; + } else { + cur.next = l1; + } + return dummyHead.next; + } +``` + + + +现在回到一开始的题目:合并 K 个排序链表。 + +**合并 K 个排序链表** 与 **合并两个有序链表** 的区别点在于操作有序链表的数量上,因此完全可以按照上面的代码思路来实现合并 K 个排序链表。 + +这里可以参考 **归并排序 **的分治思想,将这 K 个链表先划分为两个 K/2 个链表,处理它们的合并,然后不停的往下划分,直到划分成只有一个或两个链表的任务,开始合并。 + +![归并-分治](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/74ush.gif) + +### 代码实现 + +根据上面的动画,实现代码非常简单也容易理解,先划分,直到不能划分下去,然后开始合并。 + +```java +class Solution { + public ListNode mergeKLists(ListNode[] lists){ + if(lists.length == 0) + return null; + if(lists.length == 1) + return lists[0]; + if(lists.length == 2){ + return mergeTwoLists(lists[0],lists[1]); + } + + int mid = lists.length/2; + ListNode[] l1 = new ListNode[mid]; + for(int i = 0; i < mid; i++){ + l1[i] = lists[i]; + } + + ListNode[] l2 = new ListNode[lists.length-mid]; + for(int i = mid,j=0; i < lists.length; i++,j++){ + l2[j] = lists[i]; + } + + return mergeTwoLists(mergeKLists(l1),mergeKLists(l2)); + + } + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) return l2; + if (l2 == null) return l1; + + ListNode head = null; + if (l1.val <= l2.val){ + head = l1; + head.next = mergeTwoLists(l1.next, l2); + } else { + head = l2; + head.next = mergeTwoLists(l1, l2.next); + } + return head; + } +} +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0024-Swap-Nodes-in-Pairs/Animation/0024-Swap-Nodes-in-Pairs-1.m4v b/0024-Swap-Nodes-in-Pairs/Animation/0024-Swap-Nodes-in-Pairs-1.m4v new file mode 100644 index 00000000..66c976c7 Binary files /dev/null and b/0024-Swap-Nodes-in-Pairs/Animation/0024-Swap-Nodes-in-Pairs-1.m4v differ diff --git a/0024-Swap-Nodes-in-Pairs/Animation/0024-Swap-Nodes-in-Pairs-2.m4v b/0024-Swap-Nodes-in-Pairs/Animation/0024-Swap-Nodes-in-Pairs-2.m4v new file mode 100644 index 00000000..0cb06700 Binary files /dev/null and b/0024-Swap-Nodes-in-Pairs/Animation/0024-Swap-Nodes-in-Pairs-2.m4v differ diff --git a/0024-Swap-Nodes-in-Pairs/Animation/Animation1.gif b/0024-Swap-Nodes-in-Pairs/Animation/Animation1.gif new file mode 100644 index 00000000..c83a3073 Binary files /dev/null and b/0024-Swap-Nodes-in-Pairs/Animation/Animation1.gif differ diff --git a/0024-Swap-Nodes-in-Pairs/Animation/Animation2.gif b/0024-Swap-Nodes-in-Pairs/Animation/Animation2.gif new file mode 100644 index 00000000..ad83b6a0 Binary files /dev/null and b/0024-Swap-Nodes-in-Pairs/Animation/Animation2.gif differ diff --git a/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs.md b/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs.md new file mode 100644 index 00000000..f85c9b6b --- /dev/null +++ b/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs.md @@ -0,0 +1,75 @@ +# LeetCode 第 24 号问题:两两交换链表中的节点 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 24 号问题:两两交换链表中的节点。题目难度为 Medium,目前通过率为 45.8% 。 + +### 题目描述 + +给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 + +**你不能只是单纯的改变节点内部的值**,而是需要实际的进行节点交换。 + +**示例:** + +``` +给定 1->2->3->4, 你应该返回 2->1->4->3. +``` + +### 题目解析 + +该题属于基本的链表操作题。 + +- 设置一个虚拟头结点`dummyHead ` +- 设置需要交换的两个节点分别为`node1 `、`node2`,同时设置`node2`的下一个节点`next` + +##### 在这一轮操作中 + +- 将`node2`节点的next设置为`node1`节点 +- 将`node1`节点的next设置为`next `节点 +- 将`dummyHead `节点的next设置为`node2 ` +- 结束本轮操作 + +接下来的每轮操作都按照上述进行。 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/6kpyu.gif) + +### 代码实现 + +``` +// 24. Swap Nodes in Pairs +// https://leetcode.com/problems/swap-nodes-in-pairs/description/ +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + + ListNode* dummyHead = new ListNode(0); + dummyHead->next = head; + + ListNode* p = dummyHead; + while(p->next && p->next->next){ + ListNode* node1 = p->next; + ListNode* node2 = node1->next; + ListNode* next = node2->next; + node2->next = node1; + node1->next = next; + p->next = node2; + p = node1; + } + + ListNode* retHead = dummyHead->next; + delete dummyHead; + + return retHead; + } +}; + +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs2.md b/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs2.md new file mode 100644 index 00000000..eecd352b --- /dev/null +++ b/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs2.md @@ -0,0 +1,128 @@ +# LeetCode 第 24 号问题:两两交换链表中的节点 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 24 号问题:两两交换链表中的节点。 + +### 题目描述 + +给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 + +**你不能只是单纯的改变节点内部的值**,而是需要实际的进行节点交换。 + +**示例:** + +``` +给定 1->2->3->4, 你应该返回 2->1->4->3. +``` + +### 题目解析 - 迭代法 + +由题目描述可知需要两两交换, 那么以两个为一组子链表交换指针即可, 在设置一个 **哨兵** 指向交换后的子链表 (或者哨兵提前指向子链表的第二个节点,因为第二个节点交换后就成了第一个节点); 然后让哨兵指向下一组子链表,继续交换,直至最后. + +设 **哨兵** 为 节点 `prev`, 子链表第一个节点为 `A`, 第二个节点为 `B`, 第三个节点为 `C`, 那么操作流程如下: + +- 终止条件 `head == null && head -> next == null` + 1. prev -> B ( A -> B -> C ) + 2. A - > C + 3. B -> A ( prev -> B -> A -> C ) + 4. prev -> A + 5. head -> C + 6. 循环以上步骤 + +### 动画描述 + +Animation1 + +### 代码实现 + +```javascript +/** + * JavaScript描述 + * 迭代法 + */ +var swapPairs = function(head) { + let dummy = new ListNode(0); + dummy.next = head; + + let prevNode = dummy; + + while (head !== null && head.next !== null) { + // Nodes to be swapped + let firstNode = head, + secondNode = head.next; + // Swapping + prevNode.next = secondNode; // 放到交换前后都可以 + firstNode.next = secondNode.next; + secondNode.next = firstNode; + // Reinitializing the head and prevNode for next swap + prevNode = firstNode; + head = firstNode.next; + } + return dummy.next; +}; +``` + +### 复杂度分析 + +- 时间复杂度:**O(N)**,其中 *N* 指的是链表的节点数量 +- 空间复杂度:**O(1)** + +### 题目解析 - 递归 + +递归的思路和迭代类似, 都是分组交换. 具体来说这里的递归不是针对一个问题深入进去,而是不断向后推进. + +- 每次递归只交换一对节点 +- 下一次递归则是交换下一对节点 +- 交换完成后返回第二个节点, 因为它是交换后的子链表新头 +- 递归完成后返回第一次递归的第二个节点, 这就是新链表的头结点 + +**注意:** 不要人肉递归, 更多关注整体逻辑 + +示例执行大致流程为: + +- 终止条件: `(head == null) || (head.next == null)` + 1. 1 -> 2 -> 3 -> 4 ( 原始链表 ) + 2. 1 -> 3 -> 4 + 3. ( 2 -> 1 ) -> 3 -> 4 ( 第一次递归完成后返回原来的第二个节点, 也就是值为 2 的节点 ) + 4. 2 -> 1 -> 3 -> null + 5. 2 -> 1 -> ( 4 -> 3 ) ( 第二次递归完成后返回原来的第二个节点, 也就是值为 4 的节点 ) + +### 动画描述 + +Animation2 + +### 代码实现 + +```javascript +/** + * JavaScript描述 + * 递归法 + */ +var swapPairs = function(head) { + if (head == null || head.next == null) { + return head; + } + // Nodes to be swapped + let firstNode = head, + secondNode = head.next; + // Swapping + firstNode.next = swapPairs(secondNode.next); + secondNode.next = firstNode; + + return secondNode; + +}; +``` + +### 复杂度分析 + +- 时间复杂度:**O(N)**,其中 *N* 指的是链表的节点数量 +- 空间复杂度:**O(N)**, 递归过程使用的堆栈空间 + + + +![](../../Pictures/qrcode.jpg) + diff --git a/0025-Reverse-Nodes-In-K-Group/Animation/0025-Reverse-Nodes-In-K-Group.m4v b/0025-Reverse-Nodes-In-K-Group/Animation/0025-Reverse-Nodes-In-K-Group.m4v new file mode 100644 index 00000000..c373cf9b Binary files /dev/null and b/0025-Reverse-Nodes-In-K-Group/Animation/0025-Reverse-Nodes-In-K-Group.m4v differ diff --git a/0025-Reverse-Nodes-In-K-Group/Animation/Animation.gif b/0025-Reverse-Nodes-In-K-Group/Animation/Animation.gif new file mode 100644 index 00000000..c3281a6f Binary files /dev/null and b/0025-Reverse-Nodes-In-K-Group/Animation/Animation.gif differ diff --git a/0025-Reverse-Nodes-In-K-Group/Article/0025-Reverse-Nodes-In-K-Group.md b/0025-Reverse-Nodes-In-K-Group/Article/0025-Reverse-Nodes-In-K-Group.md new file mode 100644 index 00000000..e3e72dd1 --- /dev/null +++ b/0025-Reverse-Nodes-In-K-Group/Article/0025-Reverse-Nodes-In-K-Group.md @@ -0,0 +1,169 @@ +# LeetCode 第 25 号问题:K 个一组翻转链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 25 号问题:K 个一组翻转链表。题目难度为 Hard + +### 题目描述 + +给你一个链表,每 *k* 个节点一组进行翻转,请你返回翻转后的链表。 + +*k* 是一个正整数,它的值小于或等于链表的长度。 + +如果节点总数不是 *k* 的整数倍,那么请将最后剩余的节点保持原有顺序。 + +**示例:** + +给你这个链表:`1->2->3->4->5` + +当 *k* = 2 时,应当返回: `2->1->4->3->5` + +当 *k* = 3 时,应当返回: `3->2->1->4->5` + +**说明:** + +- 你的算法只能使用常数的额外空间。 +- **你不能只是单纯的改变节点内部的值**,而是需要实际进行节点交换。 + +### 题目解析 + +这道算法题可以说是 [两两交换链表中的节点](https://github.com/MisterBooo/LeetCodeAnimation/blob/master/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs2.md) 的升级版, 区别就是反转的子链表节点个数变成了自定义. + +总体思路还是一样的, 具体可以分为两个处理模块: + +1. 根据 *k* 划分若干个需要反转的子链表, 连接反转后的子链表, 最后不足 *k* 的子链表保持不变 + + - 设置哨兵 `dummy` 指向 `head` , 为了能找到反转后的链表头结点; + + - 循环 *k* 确定需要 反转子链表 的范围: + + - 循环完成, 确定子链表可以反转 + + 假设 *A* , *B* 子链表邻接且都可以反转 + + - 指针 `start` 指向 *A* 的头结点, `end` 指向 *A* 的尾结点, `nxt` 指向 *B* 的头结点 + - `start -> end` 反转后, `start` 变成了 A 的尾结点, `start -> next = nxt` , 反转后的 *A* 链表指向了 *B* + - 重置 `start` 为 *B* 的头节点, `end` 为 *B* 的尾结点, `nxt` 为下一个子链表头节点, 反转 *B* 链表 + - 重复上面动作, 知道 循环终止 + + - 循环终止, 剩余节点不足 *k* , 终止反转, 返回链表 + +2. 反转子链表 + + 假设子链表前三个节点为 *a*, *b*, *c* ,设置指针 `pre`, `cur`, `nxt` , 初始化 `pre` 值为 `null`, `cur` 值为 *a* , `nxt` 值为 *a* , 这三个指针位置不变且相邻 + + 终止条件: `cur` 不为空 + + 将当前节点的指针指向上一个节点 + + 1. `cur` 指向 `nxt` ( `nxt` 值为 *b* ) + 2. `cur` 指向 `pre` ( `cur` 指向 `null` ) + 3. `cur` 赋值给 `pre` ( `pre` 值为 *a* ) , `nxt` 赋值给 `cur` ( `cur` 值为 *b* ) + 4. 在执行 步骤 `1` ( `nxt` 值为 *c* , 到此相当于 `pre`, `cur` , `nxt` 指向依次向后移动 `1` 位 ) + 5. 重复上面动作 + +### 动画描述 + +Animation + +### 参考代码 + +#### 反转链表 + +```javascript +/** + * JavaScript 描述 + * 反转区间 [start, end) 的元素, 注意不包含 end + */ +function reverse(start, end) { + let pre = null, + cur = start, + nxt = start; + while (cur != end) { + nxt = cur.next; + // 逐个节点反转 + cur.next = pre; + // 更新指针位置 + pre = cur; + cur = nxt; + } + // 反转后的头结点, start 移到了最后, end 没有发生改变 + return pre; +}; +``` + +#### 递归解法 + +```javascript +/** + * JavaScript 描述 + * 递归 + */ +var reverseKGroup = function(head, k) { + if (head == null) { + return null; + } + let start, end; + start = end = head; + for (let i = 0; i < k; i++) { + // 不足 k 个,不需要反转 + if (end == null) { + return head; + } + end = end.next; + } + // 反转前 k 个元素, 不包含 end + let reverseHead = reverse(start, end); + // 递归反转后面k个元素 , 并前后连接起来 + start.next = reverseKGroup(end, k); + return reverseHead; +}; +``` + +#### 迭代解法 + +```javascript +/** + * JavaScript 描述 + * 迭代 + */ +var reverseKGroup = function(head, k) { + let dummy = new ListNode(0); + dummy.next = head; + let pre, start ,end, nxt; + pre = start = end = nxt = dummy; + while (end.next != null) { + for (let i = 0; i < k && end != null; i++) { + end = end.next; + } + if (end == null) { + // 不足 k 个, 跳出循环 + break; + } + start = pre.next; + nxt = end.next; + // 反转前 k 个元素, 不包含 nxt + pre.next = reverse(start, nxt); + // 链接后面的链表 + start.next = nxt; + // pre , end 重置到 下一个 k 子链表 + pre = start; + end = pre; + } + return dummy.next; +}; +``` + +### 复杂度分析 + +- 时间复杂度: **O( nk )** , 最好情况 O( n ), 最坏情况 O( n^2 ) + +- 空间复杂度: **O( 1 )** + + + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0026-Remove-Duplicates-from-Sorted-Array/Article/0026-Remove-Duplicates-from-Sorted-Array.md b/0026-Remove-Duplicates-from-Sorted-Array/Article/0026-Remove-Duplicates-from-Sorted-Array.md new file mode 100644 index 00000000..42b6d163 --- /dev/null +++ b/0026-Remove-Duplicates-from-Sorted-Array/Article/0026-Remove-Duplicates-from-Sorted-Array.md @@ -0,0 +1,91 @@ +# LeetCode 第 26 号问题:删除排序数组中的重复项 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 26 号问题:删除排序数组中的重复项。题目难度为 Easy,目前通过率为 48.8% 。 + +### 题目描述 + +给定一个排序数组,你需要在**原地**删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + +不要使用额外的数组空间,你必须在**原地修改输入数组**并在使用 O(1) 额外空间的条件下完成。 + +**示例 1:** + +``` +给定数组 nums = [1,1,2], + +函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + +你不需要考虑数组中超出新长度后面的元素。 +``` + +**示例 2:** + +``` +给定 nums = [0,0,1,1,1,2,2,3,3,4], + +函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + +你不需要考虑数组中超出新长度后面的元素。 +``` + +**说明:** + +为什么返回数值是整数,但输出的答案是数组呢? + +请注意,输入数组是以**“引用”**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 + +你可以想象内部操作如下: + +``` +// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 +int len = removeDuplicates(nums); + +// 在函数里修改输入数组对于调用者是可见的。 +// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 +for (int i = 0; i < len; i++) { + print(nums[i]); +} +``` + +### 题目解析 + +使用快慢指针来记录遍历的坐标。 + +- 开始时这两个指针都指向第一个数字 +- 如果两个指针指的数字相同,则快指针向前走一步 +- 如果不同,则两个指针都向前走一步 +- 当快指针走完整个数组后,慢指针当前的坐标加1就是数组中不同数字的个数 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/4y1ec.gif) + + + +### 代码实现 + +``` +class Solution { +public: + int removeDuplicates(vector& nums) { + if (nums.empty()) return 0; + int pre = 0, cur = 0, n = nums.size(); + while (cur < n) { + if (nums[pre] == nums[cur]){ + cur++; + } else{ + ++pre; + nums[pre] = nums[cur]; + cur++; + } + } + return pre + 1; + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git "a/0034-find-first-and-last-position-of-element-in-sorted-array/Animation/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.gif" "b/0034-find-first-and-last-position-of-element-in-sorted-array/Animation/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.gif" new file mode 100755 index 00000000..a7591e26 Binary files /dev/null and "b/0034-find-first-and-last-position-of-element-in-sorted-array/Animation/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.gif" differ diff --git a/0034-find-first-and-last-position-of-element-in-sorted-array/Article/0034-find-first-and-last-position-of-element-in-sorted-array.md b/0034-find-first-and-last-position-of-element-in-sorted-array/Article/0034-find-first-and-last-position-of-element-in-sorted-array.md new file mode 100755 index 00000000..7acd7689 --- /dev/null +++ b/0034-find-first-and-last-position-of-element-in-sorted-array/Article/0034-find-first-and-last-position-of-element-in-sorted-array.md @@ -0,0 +1,81 @@ +# LeetCode 第 34 号问题:在排序数组中查找元素的第一个和最后一个位置 + + +题目来源于 LeetCode 上第 34 号问题:find-first-and-last-position-of-element-in-sorted-array。题目难度为 中等。 + +### 题目描述 + +给定一个按照升序排列的整数数组 **nums**,和一个目标值 **target**。找出给定目标值在数组中的开始位置和结束位置。 + +你的算法时间复杂度必须是 **O(log n)** 级别。 + +如果数组中不存在目标值,返回 [-1, -1]。 + + +**示例:** + +``` +输入: nums = [5,7,7,8,8,10], target = 8 +输出: [3,4] +``` +``` +输入: nums = [5,7,7,8,8,10], target = 6 +输出: [-1,-1] +``` +### 题目解析 + +题目中要求了时间复杂度为O(log n),这就很清楚要使用二分查找法了。 + +首先定义两个指针变量,分别存储左右两个位置的索引。首先去找目标值的最左面的索引,通过循环为了防止元素丢失,每次保留最右面的元素,左侧的指针移动时+1。在循环结束的时候判断一下数组中是否包括目标值,不包括的话直接退出。 +右面的跟左侧相同,只不过正好相反。 + + + +### 动画描述 + +![](..\Animation\在排序数组中查找元素的第一个和最后一个位置.gif) + +### 代码实现 + +```java +// 34. 下一个排列 +// https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/ +// 时间复杂度:O(n) +// 空间复杂度:O(1) +class Solution { + public int[] searchRange(int[] nums, int target) { + int[] res = new int[] { -1, -1 }; + int left = 0; + int right = nums.length - 1; + int l = left; + int r = right; + while (left < right) { + int mid = (left + right) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid; + } + } + if (left>right||nums[left]!=target) { + return new int[]{-1,-1}; + } + while (l < r) { + int mid = (l + r) / 2 + 1; + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid; + } + } + if (left > right || left > r) { + return new int[] { -1, -1 }; + } else { + return new int[] { left, r }; + } + } +} + +``` + + ![](../../Pictures/qrcode.jpg) diff --git a/0034-find-first-and-last-position-of-element-in-sorted-array/Code/1.java b/0034-find-first-and-last-position-of-element-in-sorted-array/Code/1.java new file mode 100755 index 00000000..0fdf1b31 --- /dev/null +++ b/0034-find-first-and-last-position-of-element-in-sorted-array/Code/1.java @@ -0,0 +1,33 @@ +class Solution { + public int[] searchRange(int[] nums, int target) { + int[] res = new int[] { -1, -1 }; + int left = 0; + int right = nums.length - 1; + int l = left; + int r = right; + while (left < right) { + int mid = (left + right) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid; + } + } + if (left>right||nums[left]!=target) { + return new int[]{-1,-1}; + } + while (l < r) { + int mid = (l + r) / 2 + 1; + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid; + } + } + if (left > right || left > r) { + return new int[] { -1, -1 }; + } else { + return new int[] { left, r }; + } + } +} \ No newline at end of file diff --git "a/0035-search-insert-position/Animation/~$\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.pptx" "b/0035-search-insert-position/Animation/~$\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.pptx" new file mode 100755 index 00000000..2dc2a068 Binary files /dev/null and "b/0035-search-insert-position/Animation/~$\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.pptx" differ diff --git "a/0035-search-insert-position/Animation/\344\272\214\345\210\206\346\237\245\346\211\276.gif" "b/0035-search-insert-position/Animation/\344\272\214\345\210\206\346\237\245\346\211\276.gif" new file mode 100755 index 00000000..ea0e2d64 Binary files /dev/null and "b/0035-search-insert-position/Animation/\344\272\214\345\210\206\346\237\245\346\211\276.gif" differ diff --git "a/0035-search-insert-position/Animation/\344\272\214\345\210\206\346\237\245\346\211\276.mp4" "b/0035-search-insert-position/Animation/\344\272\214\345\210\206\346\237\245\346\211\276.mp4" new file mode 100755 index 00000000..b6d41ed3 Binary files /dev/null and "b/0035-search-insert-position/Animation/\344\272\214\345\210\206\346\237\245\346\211\276.mp4" differ diff --git "a/0035-search-insert-position/Animation/\346\232\264\345\212\233\346\237\245\346\211\276.gif" "b/0035-search-insert-position/Animation/\346\232\264\345\212\233\346\237\245\346\211\276.gif" new file mode 100755 index 00000000..db609feb Binary files /dev/null and "b/0035-search-insert-position/Animation/\346\232\264\345\212\233\346\237\245\346\211\276.gif" differ diff --git "a/0035-search-insert-position/Animation/\346\232\264\345\212\233\346\237\245\346\211\276.mp4" "b/0035-search-insert-position/Animation/\346\232\264\345\212\233\346\237\245\346\211\276.mp4" new file mode 100755 index 00000000..03a22f14 Binary files /dev/null and "b/0035-search-insert-position/Animation/\346\232\264\345\212\233\346\237\245\346\211\276.mp4" differ diff --git a/0035-search-insert-position/Article/0035-search-insert-position.md b/0035-search-insert-position/Article/0035-search-insert-position.md new file mode 100755 index 00000000..4ac3ec65 --- /dev/null +++ b/0035-search-insert-position/Article/0035-search-insert-position.md @@ -0,0 +1,113 @@ +# LeetCode 第 35 号问题:搜索插入位置 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 第 35 号问题:搜索插入位置. + +## 题目 + +给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 +你可以假设数组中无重复元素。 + + +示例 1: + +``` +输入: [1,3,5,6], 5 +输出: 2 +``` + +示例 2: + + +``` +输入: [1,3,5,6], 2 +输出: 1 +``` + +示例 3: + + +``` +输入: [1,3,5,6], 7 +输出: 4 +``` + + +示例 4: + + +``` +输入: [1,3,5,6], 0 +输出: 0 +``` + + + +## 思路解析 + +### 暴力循环法 + +这个题看起来就是很简单的,就是一道考验查找算法的题目。最简单的就是暴力查找了。 + +#### 思路 + +遍历这个数组,然后如果当前值和目标值target一致或小于目标值target,那么就return 当前下标。这种解法的时间复杂度是O(N) + +### 动画理解 + +![](../Animation/暴力查找.gif) + +#### 代码实现 + + +```java +//时间复杂度:O(n) +//空间复杂度:O(1) +class Solution { + public int searchInsert(int[] nums, int target) { + int i=0; + for(;i=target){ + break; + } + } + return i; + } +} +``` + +### 二分法 + +#### 思路 + +除了暴力法,我们在排序数组中查找值还可以用的一种方法是二分法,思路还是和改良的暴力循环法一样,先找到左右边界,然后计算,每次可以省出一半的时间。时间复杂度为O(logn) + +#### 代码实现 + +```java +//时间复杂度:O(lon(n)) +//空间复杂度:O(1) +class Solution { + public int searchInsert(int[] nums, int target) { + if (target>nums[nums.length-1]) { + return nums.length; + } + int left=0; + int right=nums.length-1; + while (left < right) { + int mid = (left + right) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + + } +} +``` + ![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0035-search-insert-position/Code/1.java b/0035-search-insert-position/Code/1.java new file mode 100755 index 00000000..5d07563c --- /dev/null +++ b/0035-search-insert-position/Code/1.java @@ -0,0 +1,11 @@ +class Solution1 { + public int searchInsert(int[] nums, int target) { + int i=0; + for(;i=target){ + break; + } + } + return i; + } +} \ No newline at end of file diff --git a/0035-search-insert-position/Code/2.java b/0035-search-insert-position/Code/2.java new file mode 100755 index 00000000..f45f9bc8 --- /dev/null +++ b/0035-search-insert-position/Code/2.java @@ -0,0 +1,21 @@ +//时间复杂度:O(lon(n)) +//空间复杂度:O(1) +class Solution2 { + public int searchInsert(int[] nums, int target) { + if (target>nums[nums.length-1]) { + return nums.length; + } + int left=0; + int right=nums.length-1; + while (left < right) { + int mid = (left + right) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + + } +} \ No newline at end of file diff --git a/0036-valid-sudoku/Animation/HashMap.gif b/0036-valid-sudoku/Animation/HashMap.gif new file mode 100755 index 00000000..9dcb49b6 Binary files /dev/null and b/0036-valid-sudoku/Animation/HashMap.gif differ diff --git a/0036-valid-sudoku/Animation/HashMap.mp4 b/0036-valid-sudoku/Animation/HashMap.mp4 new file mode 100755 index 00000000..4036edfa Binary files /dev/null and b/0036-valid-sudoku/Animation/HashMap.mp4 differ diff --git a/0036-valid-sudoku/Article/0036-valid-sudoku.md b/0036-valid-sudoku/Article/0036-valid-sudoku.md new file mode 100755 index 00000000..e54fc38f --- /dev/null +++ b/0036-valid-sudoku/Article/0036-valid-sudoku.md @@ -0,0 +1,134 @@ +# LeetCode 第 36 号问题:有效的数独 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 第 36 号问题:有效的数独. + +## 题目 + +判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 + + + 数字 1-9 在每一行只能出现一次。 + 数字 1-9 在每一列只能出现一次。 + 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + + +示例 1: + +``` +输入: +[ + ["5","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"] +] +输出: true +``` + +示例 2: + + +``` +输入: +[ + ["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"] +] +输出: false +解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 + 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 +``` + +示例 3: + + +``` +输入: [1,3,5,6], 7 +输出: 4 +``` + + +示例 4: + + +``` +输入: [1,3,5,6], 0 +输出: 0 +``` + + + +## 思路解析 + +### 一次遍历法 + +#### 思路 + +这道题因为需要判断数值是否存在,所以用Hash Map是一个很好的选择。 +因为每一行、每一列、每一格都是需要单独进行判断的,所以需要建立三个长度为9的HashMap数组,分别存放行、列、格的数值。 + +通过一个二层循环遍历这个9*9的数组,把当前格的数值存放到对应的HashMap中,判断之前是否已经存放过了,如果已经存放过那就退出,返回false,如果是.的话那就跳过,这样只需要遍历一边就可以了。 + +### 动画理解 + +![](../Animation/HashMap.gif) + +#### 代码实现 + + +```java +//时间复杂度:O(n) +//空间复杂度:O(1) +class Solution { + public boolean isValidSudoku(char[][] board) { + HashMap[] row = new HashMap[9]; + HashMap[] column = new HashMap[9]; + HashMap[] box = new HashMap[9]; + for (int i = 0; i < 9; i++) { + row[i] = new HashMap(9); + column[i] = new HashMap(9); + box[i] = new HashMap(9); + } + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] == '.') { + continue; + } + int boxIndex=i / 3 * 3 + j / 3; + if ((boolean) row[i].getOrDefault(board[i][j], true)) { + return false; + } + if ((boolean) column[j].getOrDefault(board[i][j], true)) { + return false; + } + if ((boolean) box[boxIndex].getOrDefault(board[i][j], true)) { + return false; + } + row[i].put(board[i][j], false); + column[j].put(board[i][j], false); + box[boxIndex].put(board[i][j], false); + } + } + + return true; + } +} +``` + + ![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0036-valid-sudoku/Code/1.java b/0036-valid-sudoku/Code/1.java new file mode 100755 index 00000000..a41cbc1e --- /dev/null +++ b/0036-valid-sudoku/Code/1.java @@ -0,0 +1,34 @@ +class Solution { + public boolean isValidSudoku(char[][] board) { + HashMap[] row = new HashMap[9]; + HashMap[] column = new HashMap[9]; + HashMap[] box = new HashMap[9]; + for (int i = 0; i < 9; i++) { + row[i] = new HashMap(9); + column[i] = new HashMap(9); + box[i] = new HashMap(9); + } + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] == '.') { + continue; + } + int boxIndex=i / 3 * 3 + j / 3; + if ((boolean) row[i].getOrDefault(board[i][j], true)) { + return false; + } + if ((boolean) column[j].getOrDefault(board[i][j], true)) { + return false; + } + if ((boolean) box[boxIndex].getOrDefault(board[i][j], true)) { + return false; + } + row[i].put(board[i][j], false); + column[j].put(board[i][j], false); + box[boxIndex].put(board[i][j], false); + } + } + + return true; + } +} \ No newline at end of file diff --git a/0042-Trap/Animation/0042-Trap.gif b/0042-Trap/Animation/0042-Trap.gif new file mode 100644 index 00000000..fa1a3e3d Binary files /dev/null and b/0042-Trap/Animation/0042-Trap.gif differ diff --git a/0042-Trap/Animation/01.png b/0042-Trap/Animation/01.png new file mode 100644 index 00000000..3b0585ef Binary files /dev/null and b/0042-Trap/Animation/01.png differ diff --git a/0042-Trap/Animation/02.png b/0042-Trap/Animation/02.png new file mode 100644 index 00000000..10421cdc Binary files /dev/null and b/0042-Trap/Animation/02.png differ diff --git a/0042-Trap/Animation/03.png b/0042-Trap/Animation/03.png new file mode 100644 index 00000000..6fb2ca7d Binary files /dev/null and b/0042-Trap/Animation/03.png differ diff --git a/0042-Trap/Animation/resource.png b/0042-Trap/Animation/resource.png new file mode 100644 index 00000000..578e81eb Binary files /dev/null and b/0042-Trap/Animation/resource.png differ diff --git a/0042-Trap/Article/0042-Trap.md b/0042-Trap/Article/0042-Trap.md new file mode 100644 index 00000000..6a569a74 --- /dev/null +++ b/0042-Trap/Article/0042-Trap.md @@ -0,0 +1,136 @@ +## LeetCode第42号问题:接雨水 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 个人博客:www.zhangxiaoshuai.fun + +**本题选择leetcode中第42题,hard级别,目前通过率50.8%#** + +### 题目描述: + +```txt +给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 +示例: +输入: [0,1,0,2,1,0,1,3,2,1,2,1] +输出: 6 +``` + +![](../Animation/resource.png) + +### 题目分析: + +通过题意,一个“凹槽”可以存储的雨水的容量取决于它前后的柱子。 + +### 解法一: + +仔细想想,其实这跟木桶原理是有相似的地方的,针对每一个柱子,我们需要往前看和往后看,分别找出当前柱子前面最高的柱子和后面最高的柱子。 + +这里有**三种情况**我们需要了解: + +- **当前柱子小于前后两个柱子中最矮的那个** + ![01](../Animation/01.png) + + **当前位置可以存储的雨水容量 = leftMax - curr = 1** + + + +- **当前柱子等于前后两个柱子中最矮的那个** + ![02](../Animation/02.png) + + **当前位置可以存储的雨水容量为0** + + + +- **当前柱子大于前后两个柱子中最矮的那个**![03](../Animation/03.png) + + **因为curr < leftMax,所以当前位置无法存储雨水** + +**GIF动画演示:** + +![gif01](../Animation/0042-trap.gif) + +### 代码: + +```java +public int trap02(int[] height) { + int sum = 0; + //最两端的列不用考虑,因为一定不会有水。所以下标从 1 到 length - 2 + for (int i = 1; i < height.length - 1; i++) { + int max_left = 0; + //找出左边最高 + for (int j = i - 1; j >= 0; j--) { + if (height[j] > max_left) { + max_left = height[j]; + } + } + int max_right = 0; + //找出右边最高 + for (int j = i + 1; j < height.length; j++) { + if (height[j] > max_right) { + max_right = height[j]; + } + } + //找出两端较小的 + int min = Math.min(max_left, max_right); + //只有较小的一段大于当前列的高度才会有水,其他情况不会有水 + if (min > height[i]) { + sum = sum + (min - height[i]); + } + } + return sum; +} +``` + +可以看到,上面方法的时间复杂度达到了**O(n^2)** + +**那么有没有更好的办法来解决这个问题?** + +下面的方法巧妙的使用了**双指针**来解决问题: + +与上述解法的思路大致是相同的,都是单个地求出当前墙可以存储雨水的容量;这种解法也是非常的巧妙,是在浏览解题区的时候碰见的,大佬还做了视频(链接放在文末),讲解的非常清楚,我大概用自己的思路来作一文字叙述: + +既然使用的是**twoPointers**的思路,那么我们需要分别从数组的最前面和最后面开始,这两个指针是互不影响,都是各走各的,但是如何确定当前指针走过的地方能存放多少雨水量呢? + +这个时候,我们就需要两块挡板**leftMax**和**rightMax**,这两块挡板最开始都是挡在最外面的墙边,随着两个指针前进,**leftMax**代表的是**left**走过的路中最高的墙,**rightMax**同理。 + +**那么如何计算雨水量呢?** + +比较左右两个挡板的高度,然后根据两个挡板各自的指针配合计算。 + +- 如果左边挡板的高度小于右边的挡板高度,那么左边指针之前的雨水量取决于**leftMax**和height[left]的大小关系,如果前者大于后者,那么容量等与前者减去后者;反之,容量为0(可以参考解法一中的图来理解) +- 如果左边挡板的高度大于等于右边挡板的高度,与上一种情况基本相同,只不过是求的右边的雨水量。 +- 在每次移动指针之后,我们要将挡板更新到最大值。 + +**其实道理也是比较简单,用宏观的思维去看待整个问题,最起码先保证两边的墙的高度(两块挡板),然后依次去到其中各个墙之间能装多少雨水的问题上。(求每次更新最高的挡板和指针指向的墙之间可以存储的雨水量)** + +### 代码: + +```java +public int trap(int[] height) { + if (height.length == 0) return 0; + int left = 0; + int right = height.length-1; + int leftMax = 0; + int rightMax = 0; + int result = 0; + while (left <= right) { + if (leftMax < rightMax) { + result += leftMax - height[left] > 0 ? + leftMax - height[left] : 0; + leftMax = Math.max(leftMax, height[left]); + left++; + } else { + result += rightMax - height[right] > 0 ? + rightMax - height[right] : 0; + rightMax = Math.max(rightMax, height[right]); + right--; + } + } + return result; + } +``` + +**时间复杂度:O(n) 空间复杂度:O(1)** + +[leetcode配套视频入口](https://leetcode-cn.com/problems/trapping-rain-water/solution/javashi-pin-jiang-jie-xi-lie-trapping-rain-water-b/) + diff --git a/0048-Rotate-Image/Animation/48.m4v b/0048-Rotate-Image/Animation/48.m4v new file mode 100644 index 00000000..d570112d Binary files /dev/null and b/0048-Rotate-Image/Animation/48.m4v differ diff --git a/0048-Rotate-Image/Animation/Animation.gif b/0048-Rotate-Image/Animation/Animation.gif new file mode 100644 index 00000000..d459faf3 Binary files /dev/null and b/0048-Rotate-Image/Animation/Animation.gif differ diff --git a/0048-Rotate-Image/Article/0048-Rotate-Image.md b/0048-Rotate-Image/Article/0048-Rotate-Image.md new file mode 100755 index 00000000..ac9ecdb3 --- /dev/null +++ b/0048-Rotate-Image/Article/0048-Rotate-Image.md @@ -0,0 +1,93 @@ +# LeetCode 图解 | 48. 旋转图像 + +## 题目描述 + +给定一个 n × n 的二维矩阵表示一个图像。 + +将图像顺时针旋转 90 度。 + +**说明:** + +你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。**请不要**使用另一个矩阵来旋转图像。 + +**示例 1:** + +``` +给定 matrix = +[ + [1,2,3], + [4,5,6], + [7,8,9] +], + +原地旋转输入矩阵,使其变为: +[ + [7,4,1], + [8,5,2], + [9,6,3] +] +``` + +**示例 2:** + +``` +给定 matrix = +[ + [ 5, 1, 9,11], + [ 2, 4, 8,10], + [13, 3, 6, 7], + [15,14,12,16] +], + +原地旋转输入矩阵,使其变为: +[ + [15,13, 2, 5], + [14, 3, 4, 1], + [12, 6, 8, 9], + [16, 7,10,11] +] +``` + +## 题目解析 + +这道题的主要难点在于如何**原地**旋转矩阵。 + +我们发现,矩阵中的一个元素旋转四次之后会回到原先的位置。也就是说,这四个元素在旋转时位置互相交换了。例如元素 `(i, j)` 对应的四个位置分别是: + ++ `(i, j)` ++ `(N-1-j, i)` ++ `(N-1-i, N-1-j)` ++ `(j, N-1-i)` + +为了旋转这四个元素,我们可以用一个临时变量保存其中一个元素,然后让几个元素依次赋值。 + +那么,一共有多少个这样的四元素组呢?这要分情况来看。如果 $n$ 是偶数的话,这相当于把矩阵均分成四块,每块的元素个数是 $ (n/2) \times (n/2)$。如果 $n$ 是奇数,矩阵的中心元素是不随旋转移动的,而剩下的元素均分成四块,每块的元素个数是 $\lfloor n/2 \rfloor \times \lceil n/2 \rceil$。我们对一块中的所有元素做一次四元素旋转即可。 + +## 动画理解 + +![](../Animation/Animation.gif) + +## 参考代码 + +```Java +class Solution { + public void rotate(int[][] matrix) { + int N = matrix.length; + for (int i = 0; i < N/2; i++) { + for (int j = 0; j < (N+1)/2; j++) { + int t = matrix[i][j]; + matrix[i][j] = matrix[N-1-j][i]; + matrix[N-1-j][i] = matrix[N-1-i][N-1-j]; + matrix[N-1-i][N-1-j] = matrix[j][N-1-i]; + matrix[j][N-1-i] = t; + } + } + } +} +``` + +## 复杂度分析 + ++ 时间复杂度:$O(n^2)$。 ++ 空间复杂度:$O(1)$。 + diff --git a/0053 - I. Find number in sort arrayI/Animation/Animation.gif b/0053 - I. Find number in sort arrayI/Animation/Animation.gif new file mode 100644 index 00000000..4bbfbdc7 Binary files /dev/null and b/0053 - I. Find number in sort arrayI/Animation/Animation.gif differ diff --git a/0053 - I. Find number in sort arrayI/Animation/Interview Question 53 - I. Find number in sort arrayI.mp4 b/0053 - I. Find number in sort arrayI/Animation/Interview Question 53 - I. Find number in sort arrayI.mp4 new file mode 100644 index 00000000..7199c27e Binary files /dev/null and b/0053 - I. Find number in sort arrayI/Animation/Interview Question 53 - I. Find number in sort arrayI.mp4 differ diff --git a/0053 - I. Find number in sort arrayI/Article/Interview Question 53 - I. Find number in sort arrayI.md b/0053 - I. Find number in sort arrayI/Article/Interview Question 53 - I. Find number in sort arrayI.md new file mode 100644 index 00000000..01df939e --- /dev/null +++ b/0053 - I. Find number in sort arrayI/Article/Interview Question 53 - I. Find number in sort arrayI.md @@ -0,0 +1,151 @@ +# 面试题53 - I. 在排序数组中查找数字 I + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 面试题53 - I. 在排序数组中查找数字 I. 是算法入门的一道题。 + +## 题目 + +统计一个数字在排序数组中出现的次数。 + + +示例 1: + +``` +输入: nums = [5,7,7,8,8,10], target = 8 +输出: 2 +``` + +示例 2: + + +``` +输入: nums = [5,7,7,8,8,10], target = 6 +输出: 0 +``` + + +限制: + +``` +0 <= 数组长度 <= 50000 +``` + + +## 思路解析 + +### 暴力循环法 + +题目看上去是很简单,就是找到一个目标数字在数组中出现的次数,不管数组是有序还是无序的,我们都可以用的一种方法就是暴力循环法 + +#### 思路 + +定义一个count来记录目标值出现的次数,初始值为0,然后遍历这个数组,然后如果当前值和目标值target一致,那么count就加一,最后return count。这种解法的时间复杂度是O(N) + +#### 代码实现 + + +```javaScript +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + let count = 0; + for(let i of nums) { + if (i === target) { + count++ + } + } + return count +}; +``` + +### 改良的暴力循环 + +#### 思路 + +因为数组已排序了,所以我们其实可以不用遍历全部,用双指针分别从头部和尾部开始同时遍历,然后找到目标值的左右边界的位置,然后通过计算得到count。其实就是比全部遍历少了目标值出现的次数,它的算法复杂度还是O(n) + + count = 右边界的index - 左边界的index + 1 + +#### 代码实现 + + +```javaScript +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + let [left, right] = [0, nums.length - 1] + while(left <= right && (nums[left] !== target || nums[right] !== target)) { + if (left === right && nums[left] !== target) { + return 0; + }else if (nums[left] !== target) { + left++; + }else if (nums[right] !== target){ + right--; + } + } + return right - left + 1; +}; +``` + +### 二分法 + +#### 思路 + +除了遍历,我们在排序数组中查找值还可以用的一种方法是二分法,思路还是和改良的暴力循环法一样,先找到左右边界,然后做计算。时间复杂度为O(logn) + +#### 代码实现 + +```javaScript +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + let start = 0; + let mid = 0; + let end = nums.length - 1; + let left = 0; + let right = 0; + // 查找右边界 + while(start <= end) { + mid = Math.ceil((start + end) / 2) + if (nums[mid] <= target) { + start = mid + 1 + } else { + end = mid -1 + } + } + right = start - 1; // 右边界 + // 查找左边界 + start = 0; + mid = 0; + end = nums.length - 1; + while(start <= end) { + mid = Math.ceil((start + end) / 2) + if (nums[mid] < target) { + start = mid + 1 + } else { + end = mid -1 + } + } + left = end + 1 + return right - left + 1 +}; +``` + +## 动画理解 + +![](../Animation/Animation.gif) + + + ![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0058-length-Of-Last-Word/Animation/0058.gif b/0058-length-Of-Last-Word/Animation/0058.gif new file mode 100644 index 00000000..753bbf57 Binary files /dev/null and b/0058-length-Of-Last-Word/Animation/0058.gif differ diff --git a/0058-length-Of-Last-Word/Article/0058-length-Of-Last-Word.md b/0058-length-Of-Last-Word/Article/0058-length-Of-Last-Word.md new file mode 100644 index 00000000..668780bd --- /dev/null +++ b/0058-length-Of-Last-Word/Article/0058-length-Of-Last-Word.md @@ -0,0 +1,76 @@ +## LeetCode第58号问题:最后一个单词的长度 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 个人博客:www.zhangxiaoshuai.fun + +**本题选自leetcode第58题,easy难度,目前通过率33.0%** + +### 题目描述: +```txt +给定一个仅包含大小写字母和空格' '的字符串s,返回其最后一个单词的长度。 +如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。 +如果不存在最后一个单词,请返回0。 +说明:一个单词是指仅由字母组成、不包含任何空格字符的最大子字符串。 + +示例: +输入:"Hello World" +输出:5 +``` + +### 题目分析: + +既然需要求出最后一个单词的长度,那我们直接从**字符串的末尾**开始好了; +这里末尾有两种情况:有空格和没有空格 + +``` +(1)有空格:我们从末尾忽略掉空格,然后找到第一个遇见的字符(遇到第一个空格或者遍历完整个字符串为止) +(2)无空格:直接从末尾往前寻找即可(遇到第一个空格或者遍历完整个字符串为止) +``` + +### 动画gif演示: + +![](../Animation/0058.gif) + +### 代码: + +**The first version** + +```java +public int lengthOfLastWord(String s) { + if (s.length()==0) { + return 0; + } + int index = 0; + int temp = 0; + int p = s.length()-1; + while ((p-index >=0) && s.charAt(p-index) == 32) index++; + for (int i = p-index;i >= 0;i--) { + if (s.charAt(i) != 32){ + temp++; + continue; + } + break; + } + return temp; +} +``` + +**2.代码:** + +**The second version** + +```java +public int lengthOfLastWord(String s) { + int len = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) != ' ') { + len++; + } else if (len != 0) { + return len; + } + } + return len; +} +``` + diff --git a/0066-Plus-One/Article/0066-Plus-One.md b/0066-Plus-One/Article/0066-Plus-One.md new file mode 100644 index 00000000..259d2d33 --- /dev/null +++ b/0066-Plus-One/Article/0066-Plus-One.md @@ -0,0 +1,90 @@ +# LeetCode 第 66 号问题:加一 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享的题目来源于 LeetCode 上第 66 号问题:加一。题目难度为 Easy,目前通过率为 39.0% 。 + +### 题目描述 + +给定一个由**整数**组成的**非空**数组所表示的非负整数,在该数的基础上加一。 + +最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。 + +你可以假设除了整数 0 之外,这个整数不会以零开头。 + +**示例 1:** + +``` +输入: [1,2,3] +输出: [1,2,4] +解释: 输入数组表示数字 123。 +``` + +**示例 2:** + +``` +输入: [4,3,2,1] +输出: [4,3,2,2] +解释: 输入数组表示数字 4321。 +``` + +**示例 3:** + +``` +//为了更好理解题意,根据 LeetCode 评论区评论新增一个示例 +输入: [9,9] +输出: [1,0,0] +解释: 输入数组表示数字 100。 +``` + +### 题目解析 + +本题很简单,题目意思也很好理解,注意的点就是 **进位问题**。 + +* 如果数组末位(个位)小于 9 ,直接个位加 1 返回即可 + +* 如果数组末位(个位)等于 9,将该位(个位)设置为 0 ,并且产生了进位,接下来观察前一位(十位) + +* * 如果前一位(十位)小于 9 ,直接十位加 1 返回即可 + * 如果前一位(十位)等于 9,将该位(十位)设置为 0 ,并且产生了进位,接下来观察前一位(百位) + +* 以此类推,最后观察运算完的第一位是否为 0 ,如果为 0 ,则在最前面加 1 (**示例 3**) + + + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/iejo0.gif) + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/15na7.gif) + + + +### 代码实现 + +```java +public class Solution { + public int[] plusOne(int[] digits) { + int n = digits.length; + //从数组末尾开始向前遍历 + for (int i = digits.length - 1; i >= 0; --i) { + if (digits[i] < 9) { + digits[i]++; + //没有进位,直接返回 + return digits; + } + //产生进位,需要将该位赋值为 0 + digits[i] = 0; + } + //整体产生了进位,数组长度需要变化加 1 + int[] res = new int[n + 1]; + res[0] = 1; + return res; + } +} +``` + +![](../../Pictures/qrcode.jpg) + diff --git a/0070-Climbing-Stairs/Animation/0070-Climbing-Stairs.m4v b/0070-Climbing-Stairs/Animation/0070-Climbing-Stairs.m4v new file mode 100644 index 00000000..2e98a5b1 Binary files /dev/null and b/0070-Climbing-Stairs/Animation/0070-Climbing-Stairs.m4v differ diff --git a/0070-Climbing-Stairs/Animation/Animation.gif b/0070-Climbing-Stairs/Animation/Animation.gif new file mode 100644 index 00000000..cf07f344 Binary files /dev/null and b/0070-Climbing-Stairs/Animation/Animation.gif differ diff --git a/0070-Climbing-Stairs/Article/0070-Climbing-Stairs.md b/0070-Climbing-Stairs/Article/0070-Climbing-Stairs.md new file mode 100644 index 00000000..c4282c73 --- /dev/null +++ b/0070-Climbing-Stairs/Article/0070-Climbing-Stairs.md @@ -0,0 +1,102 @@ +## **LeetCode 第 70 号问题:爬楼梯** + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一. +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 70 号问题:爬楼梯。题目难度为 Easy。 + +### 题目描述 + +假设你正在爬楼梯。需要 `n` 阶你才能到达楼顶。 + +每次你可以爬 `1` 或 `2` 个台阶。你有多少种不同的方法可以爬到楼顶呢? + +**注意:给定 n 是一个正整数。** + +### 示例1 + +> 输入: 2 +> +> 解释: 有两种方法可以爬到楼顶。 +> +> 1. 1 阶 + 1 阶 +> +> 2. 2 阶 + +### 题目解析 + +试着倒推想一下,就能发现这个问题可以被分解为一些包含最优子结构的子问题,它的最优解可以从其子问题 +的最优解来有效地构建,因此我们可以使用`动态规划`解决这个问题. + +第 `i` 阶可以由以下两种方法得到: + +在第 `(i - 1)` 阶后向上爬 1 阶。 + +在第 `(i - 2)` 阶后向上爬 2 阶 + +所以到达第 `i` 阶的方法总数就是到第 `(i - 1)` 阶和第 `(i - 2)` 阶的方法数之和。 + +`dp[i]dp[i]` 表示能到达第 `i` 阶的方法总数,那么DP推导公式就是: + +> $$ +> dp[i] = dp[i − 1] + dp[i − 2] +> $$ + + + +### 动画理解 + +Animation + +### 参考代码 + +```javascript +/** + * JavaScript 描述 + */ +var climbStairs = function(n) { + let temp = new Array(n+1); + temp[1] = 1; + temp[2] = 2; + for (let i = 3; i < temp.length; i++) { + temp[i] = temp[i-1] + temp[i-2]; + } + return temp[n]; +} +``` + +#### 复杂度分析 + +- 时间复杂度:`O(n)`,单循环到 n。 +- 空间复杂度:`O(n)`,dpdp 数组用了 n 的空间。 + +### 进一步优化 + +根据推导公式不难发现,我们要求的结果就是数组的最后一项,而最后一项又是前面数值叠加起来的,那么我们只需要两个变量保存 `i - 1` 和 `i - 2` 的值就可以了. + +```javascript +/** + * JavaScript 描述 + */ +var climbStairs = function(n) { + if (n == 1) { + return 1; + } + let first = 1, + second = 2; + for (let i = 3; i <= n; i++) { + let third = first + second; + first = second; + second = third; + } + return second; +} +``` + +#### 复杂度分析 + +- 时间复杂度:O(n),单循环到 n。 +- 空间复杂度:O(1),用到了常量的空间。 + +![qrcode](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0075-Sort-Colors/Article/0075-Sort-Colors.md b/0075-Sort-Colors/Article/0075-Sort-Colors.md new file mode 100644 index 00000000..a0efb515 --- /dev/null +++ b/0075-Sort-Colors/Article/0075-Sort-Colors.md @@ -0,0 +1,75 @@ +# LeetCode 第 75 号问题:颜色分类 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 75 号问题:颜色分类。题目难度为 Medium,目前通过率为 51.8% 。 + +### 题目描述 + +给定一个包含红色、白色和蓝色,一共 *n* 个元素的数组,**原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 + +此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 + +**注意:** +不能使用代码库中的排序函数来解决这道题。 + +**示例:** + +``` +输入: [2,0,2,1,1,0] +输出: [0,0,1,1,2,2] +``` + +**进阶:** + +- 一个直观的解决方案是使用计数排序的两趟扫描算法。 + 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 +- 你能想出一个仅使用常数空间的一趟扫描算法吗? + +### 题目解析 + +结合三路快排 partition 思路的应用。 + +设定两个索引,一个从左往右滑动`zero`,一个从右往左滑动`two`。 + +* 遍历`nums`,当`nums[i]`的值为1时,`i++`; +* 当`nums[i]`的值为2时,`two`的值先减1,而后交换`nums[i]`与`nums[two]`,此时在观察`nums[i]`的值; +* 当`nums[i]`的值为0时,`zero++`,而后交换`nums[i]`与`nums[zero]`,`i++`;当 `i = two`时,结束循环。 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/6g5tm.gif) + +### 代码实现 + +``` +// 三路快速排序的思想 +// 对整个数组只遍历了一遍 +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { +public: + void sortColors(vector &nums) { + + int zero = -1; // [0...zero] == 0 + int two = nums.size(); // [two...n-1] == 2 + for(int i = 0 ; i < two ; ){ + if(nums[i] == 1){ + i ++; + }else if (nums[i] == 2){ + two--; + swap( nums[i] , nums[two]); + }else{ // nums[i] == 0 + zero++; + swap(nums[zero] , nums[i]); + i++; + } + } + } +}; + +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0084-Largest-Rectangle-In-Histogram/Animation/84.gif b/0084-Largest-Rectangle-In-Histogram/Animation/84.gif new file mode 100644 index 00000000..d25787ab Binary files /dev/null and b/0084-Largest-Rectangle-In-Histogram/Animation/84.gif differ diff --git a/0084-Largest-Rectangle-In-Histogram/Animation/84.m4v b/0084-Largest-Rectangle-In-Histogram/Animation/84.m4v new file mode 100644 index 00000000..213c60d3 Binary files /dev/null and b/0084-Largest-Rectangle-In-Histogram/Animation/84.m4v differ diff --git a/0084-Largest-Rectangle-In-Histogram/Animation/example.png b/0084-Largest-Rectangle-In-Histogram/Animation/example.png new file mode 100644 index 00000000..bda7f341 Binary files /dev/null and b/0084-Largest-Rectangle-In-Histogram/Animation/example.png differ diff --git a/0084-Largest-Rectangle-In-Histogram/Article/0084-Largest-Rectangle-In-Histogram.md b/0084-Largest-Rectangle-In-Histogram/Article/0084-Largest-Rectangle-In-Histogram.md new file mode 100644 index 00000000..ad157446 --- /dev/null +++ b/0084-Largest-Rectangle-In-Histogram/Article/0084-Largest-Rectangle-In-Histogram.md @@ -0,0 +1,169 @@ +# LeetCode 第 84 号问题:柱状图中的最大矩形 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 84 号问题:柱状图中的最大矩形。题目难度为 Hard,目前通过率为 39.2% 。 + + +
+ + +### 题目描述 + +给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 + +求在该柱状图中,能够勾勒出来的矩形的最大面积。 + +**示例 1:** + +![](../Animation/example.png) + + +
+ +### 题目解析 + +给一个条形图,让你找出这里面面积最大的矩形,条形图中每一个位置的高度不固定,但是宽度都是 1。因为矩形的方向,大小都不确定,直观去看的话思路并不明显,但是有一点很明确,一段区间形成的矩形总是和最短的高度有关,我们还是来看看题目给的例子: + +``` +[2,1,5,6,2,3] + +我们假定矩形可以由一个区间 [start, end] 确定, +那么这个区间的矩形的高度其实是由这个区间的最小值决定 + +[0,1] 区间内的矩形的高度是 1,面积 1 * 2 = 2 +[2,3] 区间内的矩形的高度是 5,面积 5 * 2 = 10 +[0,5] 区间内的矩形的高度是 1,面积 1 * 6 = 6 +[2,5] 区间内的矩形的高度是 2,面积 2 * 4 = 8 +... +``` + +如果你明白了上面的例子,这道题目思路就有了,也就是 “**找出数组的所有区间(子数组),区间中 最小元素的值 * 区间的长度 就是当前区间表示的矩形的面积,在所有区间中找最大面积的矩形即可**”。这个思路非常的直接,这么样下来,找出数组的所有子数组,这个时间复杂度是 O(n^2),有没有办法优化呢? + +上面的解法,你直观上看就是一个暴力的解法,因为我们不断地去找子数组,这里面其实有很多的重复计算,比如,还是刚刚那个例子: +``` +[2,1,5,6,2,3] + +[2,1,5,6,2] 我们遍历了一遍,得到矩阵的高度的最小值,然后求出面积 +[1,2,6,2] 我们又接着重新遍历一遍,得到矩阵的高度的最小值,然后求出面积 + +求第二个区间的时候,我们完全没有借鉴第一个区间的答案,存在着重复计算 +``` + +这个时候,我们可能需要换一个思路来看待这个问题,从上面的分析,我们已经得知,矩阵的高都来自于数组中元素的值,可以思考 “**如果以当前位置的元素作为矩形的高,最多向左向右分别延伸多少个位置**”,比如: + +``` +[2,1,5,6,2,3] + +如果我们取数组中第 2 个元素,也就是 1,作为矩阵的高 +它向左可以延伸到 2,向右可以延伸到 3,于是面积就是 1 * 6 = 6 + +如果我们取数组中第 5 个元素,也就是 2,作为矩阵的高 +它向左可以延伸到 5,向右可以延伸到 3,于是面积就是 2 * 4 = 8 + +如果我们取数组中第 3 个元素,也就是 5,作为矩阵的高 +它向左可以延伸到 5(它自己),向右可以延伸到 6,于是面积就是 5 * 2 = 10 + +... +``` + +好了,思路分析完了,现在如何实现这么一个思想呢?我们需要确定一个元素可以延伸到的左边界和右边界,这么说你可能不太好理解,换种说法,其实我们需要找 “**左边第一个比当前元素小的元素所在的位置,右边第一个比当前元素小的元素所在位置**”,还是来看看例子: + +``` +[2,1,5,6,2,3] + +数组第 2 个元素 1,左右均没有比它小的元素,因此它所确定的区间就是整个数组 + +数组第 5 个元素 2,左边第一个比它小的元素是 1,右边第一个比它小的元素是 -1(表示没有) +因此它所确定的区间就是 [2, 5] + +数组第 3 个元素 5,左边第一个比它小的元素是 1,右边第一个比它小的元素是 6 +因此它所确定的区间就是 [2, 3] + +... +``` + +在实现上面我们需要利用栈这个数据结构,栈里面存放的元素对应的值都是**单调递增**的,这样可以保证从左向右遍历数组,前一个入栈的元素是后一个入栈的元素的左边界,另外,如果下一个准备入栈的元素比栈顶元素的值小,说明栈顶元素的右边界也找到了,左右边界都找到了,栈顶元素出栈进行计算。这样下来,一个元素只会进栈一次,出栈一次,因此时间复杂度是 O(2 * n) 也就是 O(n) + +**单调栈**这个数据结构应用还是比较广泛的,如果发现题目中需要针对数组中一个元素向左右两边延伸去确定区间,而且延伸的条件跟元素的值有关,那么就可以考虑使用单调栈。 + +
+ +### 代码实现(暴力解法) + +```java +public int largestRectangleArea(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + + int n = heights.length; + int result = 0; + + for (int i = 0; i < n; ++i) { + int curMin = heights[i]; + + for (int j = i; j < n; ++j) { + curMin = Math.min(curMin, heights[j]); + result = Math.max(result, (j - i + 1) * curMin); + } + } + + return result; +} +``` + +
+ +### 代码实现(单调栈) + +```java +public int largestRectangleArea(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + + int n = heights.length; + + Stack stack = new Stack<>(); + + int result = 0; + + for (int i = 0; i <= n; ++i) { + // curElement 表示当前元素的值,用 -1 表示数组的结束 + int curElement = (i == n) ? -1 : heights[i]; + + // 元素的出栈操作,表明当前栈顶元素找到了右边界 + // 加上栈内存放的元素是单调递增的,因此左边界也找到了 + // while 里面针对栈顶元素这个高度的矩形计算面积即可 + while (!stack.isEmpty() && heights[stack.peek()] >= curElement) { + int high = heights[stack.pop()]; + int width = stack.isEmpty() ? i : i - stack.peek() - 1; + + result = Math.max(result, high * width); + } + + stack.push(i); + } + + return result; +} +``` + +
+ +### 动画描述 + +![](../Animation/84.gif) + +
+ +### 复杂度分析 + +暴力解法的时间复杂度是 `O(n^2)`,这个从上面的描述中不难看出。在单调栈的代码实现中,你可能会觉得这里有两个嵌套循环,因此时间复杂度是 `O(n^2)`,但其实不是这样,考虑时间复杂度仅仅看代码的形式是不够的,你可以这样考虑,因为数组中的每个元素仅入栈一次,出栈一次,因此正确的时间复杂度是 `O(2n)`,忽略常数项,也就是 `O(n)`,单调栈的解法中用到了栈,因此空间复杂度是 `O(n)`。 + +
+ +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0086-Partition-List/Article/0086-Partition-List.md b/0086-Partition-List/Article/0086-Partition-List.md new file mode 100644 index 00000000..b4afd65a --- /dev/null +++ b/0086-Partition-List/Article/0086-Partition-List.md @@ -0,0 +1,71 @@ +# LeetCode 第 86 号问题:分割链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 86 号问题:分割链表。题目难度为 Easy,目前通过率为 47.8% 。 + +### 题目描述 + +给定一个链表和一个特定值 *x*,对链表进行分隔,使得所有小于 *x* 的节点都在大于或等于 *x* 的节点之前。 + +你应当保留两个分区中每个节点的初始相对位置。 + +**示例:** + +``` +输入: head = 1->4->3->2->5->2, x = 3 +输出: 1->2->2->4->3->5 +``` + +### 题目解析 + +这道题要求我们划分链表,把所有小于给定值的节点都移到前面,大于该值的节点顺序不变,相当于一个局部排序的问题。 + +- 设定两个虚拟节点,`dummyHead1 `用来保存小于于该值的链表,`dummyHead2 `来保存大于等于该值的链表 +- 遍历整个原始链表,将小于该值的放于`dummyHead1 `中,其余的放置在`dummyHead2 `中 +- 遍历结束后,将`dummyHead2 `插入到`dummyHead1 `后面 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/t96zg.gif) + +### 代码实现 + +``` +class Solution { +public: + ListNode* partition(ListNode* head, int x) { + + ListNode* dummyHead1 = new ListNode(-1); + ListNode* dummyHead2 = new ListNode(-1); + ListNode* prev1 = dummyHead1; + ListNode* prev2 = dummyHead2; + + for(ListNode* cur = head ; cur != NULL ;){ + if(cur->val < x){ + prev1->next = cur; + cur = cur->next; + prev1 = prev1->next; + prev1->next = NULL; + } + else{ + prev2->next = cur; + cur = cur->next; + prev2 = prev2->next; + prev2->next = NULL; + } + } + + prev1->next = dummyHead2->next; + ListNode* ret = dummyHead1->next; + + delete dummyHead1; + delete dummyHead2; + return ret; + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git "a/0088-Merge-Sorted-Array/Animation/0088-Merge-Sorted-Array-\345\217\214\346\214\207\351\222\210+\344\273\216\345\220\216\345\220\221\345\211\215.m4v" "b/0088-Merge-Sorted-Array/Animation/0088-Merge-Sorted-Array-\345\217\214\346\214\207\351\222\210+\344\273\216\345\220\216\345\220\221\345\211\215.m4v" new file mode 100644 index 00000000..41a3cbd2 Binary files /dev/null and "b/0088-Merge-Sorted-Array/Animation/0088-Merge-Sorted-Array-\345\217\214\346\214\207\351\222\210+\344\273\216\345\220\216\345\220\221\345\211\215.m4v" differ diff --git a/0088-Merge-Sorted-Array/Animation/Animation.gif b/0088-Merge-Sorted-Array/Animation/Animation.gif new file mode 100644 index 00000000..af613154 Binary files /dev/null and b/0088-Merge-Sorted-Array/Animation/Animation.gif differ diff --git a/0088-Merge-Sorted-Array/Article/0088-Merge-Sorted-Array.md b/0088-Merge-Sorted-Array/Article/0088-Merge-Sorted-Array.md new file mode 100644 index 00000000..602e3b50 --- /dev/null +++ b/0088-Merge-Sorted-Array/Article/0088-Merge-Sorted-Array.md @@ -0,0 +1,196 @@ +# LeetCode 第 88 号问题:合并两个有序数组 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 88 号问题:合并两个有序数组。题目难度为 Easy + +### 题目描述 + +给你两个有序整数数组 *nums1* 和 *nums2*,请你将 *nums2* 合并到 *nums1* 中*,*使 *nums1* 成为一个有序数组。 + +**说明:** + +- 初始化 *nums1* 和 *nums2* 的元素数量分别为 *m* 和 *n* 。 +- 你可以假设 *nums1* 有足够的空间(空间大小大于或等于 *m* + *n*)来保存 *nums2* 中的元素。 + +**示例:** + +``` +输入: +nums1 = [1,2,3,0,0,0], m = 3 +nums2 = [2,5,6], n = 3 + +输出: [1,2,2,3,5,6] +``` + +### 题目解析 + +将这个题目放到现实中就容易多了, 不信你看看. + +假如你是玩具店的老板, 其中两个货架依次摆放了形状相同大小不同的小汽车, 这些小汽车都按照从小到大摆放着, 现在你想把第二个货架的小汽车移到第一个货架上, 为了顾客看起来直观, 这些小汽车要摆放的有序, 你会怎么做呢? + +**很显现, 你不会把所有小汽车放到一起, 然后在一个个排序好放到第一个货架上.** + +你肯定会比较两个货架小汽车的大小, 把第二个货架小汽车移到第一个货架的相应位置上. + +那么问题来了, 是从小的比较呢还是从大的比较呢? + +**先从小的比较来看**, 现在第二个货架第一个汽车是最小的, 那么你得把第一个货架所有玩具往后挪一位, 然后才能放下这个汽车, **好像有点费力了**. + +不想费力, 我们可以先把第一个货架的玩具移到第三个货架, 比较第二个货架和第三个货架, 把比较小的汽车放到第一个货架上. 看来还得先搬移第一个货架, **需要占用其他空间了**. + +**如果从后面比较呢**, **也就是先比较大的汽车**, 现在第二个货架最后一个是最大的汽车, 我只需要把最大的汽车拿到第一货架的最后面就可以了, 是不是很轻松, 这样依次比较, **不费力也不用费空间**就挪到了第一个货架后面了. 和第一个货架都比较完了, 发现第二个货架还剩一个最小的汽车, 这个时候你会发现第一个货架的第一个位置是空的, 我们直接拿过去就可以啦. + +故事讲完啦, 通过这几种方法的尝试, 你也许已经发现了: + +> 第一种方法 对应的算法是 **' 合并后排序 '**, 时间复杂度比较大; +> +> 第二种方法 对应的算法是 **' 双指针 + 从前向后比较 '** +> +> - 往后挪动汽车时间复杂度高 +> - 移到第三个货架空间复杂度高 +> +> 第三种方法 对应的算法是 **' 双指针 + 从后向前比较 '** , 省时又不占空间, 完美! + +下面说下 **' 双指针 + 从后向前比较 '** 的具体思路: + +1. 设置双指针, 分别指向有序数组的最后一位; + +2. 从后向前 + + - 终止条件: 其中一个指针不在指向数组 + + - 比较双指针指向的值 + + - 大的或相同的值放到 *num1* 空间的尾部( 尾部从后向前依次填充 ), 对应的指针向前挪一位 + - 循环上面步骤 + +3. 遍历完成后检查 + + - 若指向 *num2* 的指针还有效, 说明 *num2* 中还有小于 *num1* 最小值的存在 + - 将这些值搬移到 *num1* 最前面 + +### 动画描述 + +Animation + +### 参考代码 +C++ Code: + +```c++ +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int i=m-1, j=n-1, k=m+n-1; + // 合并 + while(i>=0 && j>=0) + { + if(nums1[i] > nums2[j]) + { + nums1[k--] = nums1[i--]; + } + else + { + nums1[k--] = nums2[j--]; + } + } + // 合并剩余的nums2 + while(j>=0) + { + nums1[k--] = nums2[j--]; + } + } +}; +``` + +Java Code: + +```java +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int i=m-1, j=n-1, k=m+n-1; + // 合并 + while(i>=0 && j>=0) + { + if(nums1[i] > nums2[j]) + { + nums1[k--] = nums1[i--]; + } + else + { + nums1[k--] = nums2[j--]; + } + } + // 合并剩余的nums2 + while(j>=0) + { + nums1[k--] = nums2[j--]; + } + } +} +``` + +Python Code: + +```python +class Solution(object): + def merge(self, nums1, m, nums2, n): + """ + :type nums1: List[int] + :type m: int + :type nums2: List[int] + :type n: int + :rtype: None Do not return anything, modify nums1 in-place instead. + """ + i,j,k = m-1, n-1, m+n-1 + + while i >= 0 and j >= 0: + # print(i,j,k, nums1) + # print(nums1[i], nums2[j]) + if nums1[i] > nums2[j]: + nums1[k] = nums1[i] + k-=1 + i-=1 + else: + nums1[k] = nums2[j] + k-=1 + j-=1 + while j >= 0: + nums1[k] = nums2[j] + k-=1 + j-=1 + +``` + +JavaScript Code: + +```javascript +/** + * JavaScript 描述 + * 双指针 + 从后向前 + */ +var merge = function(nums1, m, nums2, n) { + let len = m + n; + while(m > 0 && n > 0){ + // '>=' 相比 '>' 在某些值相同的情况下能少比较一次 + nums1[--len] = nums2[n-1] >= nums1[m-1] ? nums2[--n]: nums1[--m]; + } + if(n > 0){ + nums1.splice(0,n,...nums2.slice(0,n)); + } +}; +``` + +### 复杂度分析 + +- 时间复杂度: **O( m+n )** + +- 空间复杂度: **O( 1 )** + + + + + +![](../../Pictures/qrcode.jpg) diff --git a/0092-Reverse-Linked-List-II/Article/0092-Reverse-Linked-List-II.md b/0092-Reverse-Linked-List-II/Article/0092-Reverse-Linked-List-II.md new file mode 100644 index 00000000..098e81c1 --- /dev/null +++ b/0092-Reverse-Linked-List-II/Article/0092-Reverse-Linked-List-II.md @@ -0,0 +1,74 @@ +# LeetCode 第 92 号问题:反转链表 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 92 号问题:反转链表 II。题目难度为 Medium,目前通过率为 43.8% 。 + +### 题目描述 + +反转从位置 *m* 到 *n* 的链表。请使用一趟扫描完成反转。 + +**说明:** +1 ≤ *m* ≤ *n* ≤ 链表长度。 + +**示例:** + +``` +输入: 1->2->3->4->5->NULL, m = 2, n = 4 +输出: 1->4->3->2->5->NULL +``` + +### 题目解析 + +**[Reverse Linked List](https://xiaozhuanlan.com/topic/7513064892)**的延伸题。 + +可以考虑取出需要反转的这一小段链表,反转完后再插入到原先的链表中。 + +**以本题为例:** + +变换的是 2,3,4这三个点,那么我们可以先取出 2 ,用 front 指针指向 2 ,然后当取出 3 的时候,我们把 3 加到 2 的前面,把 front 指针前移到 3 ,依次类推,到 4 后停止,这样我们得到一个新链表 4 -> 3 -> 2 , front 指针指向4。 + +对于原链表来说,**有两个点的位置很重要**,需要用指针记录下来,分别是 1 和 5 ,把新链表插入的时候需要这两个点的位置。 + +- 用 pre 指针记录 1 的位置 +- 当 4 结点被取走后,5 的位置需要记下来 +- 这样我们就可以把倒置后的那一小段链表加入到原链表中 + + + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/rjjr0.gif) + +### 代码实现 + +``` +class Solution { +public: + ListNode *reverseBetween(ListNode *head, int m, int n) { + ListNode *dummy = new ListNode(-1); + dummy->next = head; + ListNode *cur = dummy; + ListNode *pre, *front, *last; + for (int i = 1; i <= m - 1; ++i) cur = cur->next; + pre = cur; + last = cur->next; + for (int i = m; i <= n; ++i) { + cur = pre->next; + pre->next = cur->next; + cur->next = front; + front = cur; + } + cur = pre->next; + pre->next = front; + last->next = cur; + return dummy->next; + } +}; +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0094-Binary-Tree-Inorder-Traversal/Animation/0094-Binary-Tree-Inorder-Traversal.m4v b/0094-Binary-Tree-Inorder-Traversal/Animation/0094-Binary-Tree-Inorder-Traversal.m4v new file mode 100644 index 00000000..4a71f5e8 Binary files /dev/null and b/0094-Binary-Tree-Inorder-Traversal/Animation/0094-Binary-Tree-Inorder-Traversal.m4v differ diff --git a/0094-Binary-Tree-Inorder-Traversal/Animation/Animation2.gif b/0094-Binary-Tree-Inorder-Traversal/Animation/Animation2.gif new file mode 100644 index 00000000..239cacc3 Binary files /dev/null and b/0094-Binary-Tree-Inorder-Traversal/Animation/Animation2.gif differ diff --git a/0094-Binary-Tree-Inorder-Traversal/Article/0094-Binary-Tree-Inorder-Traversal.md b/0094-Binary-Tree-Inorder-Traversal/Article/0094-Binary-Tree-Inorder-Traversal.md new file mode 100644 index 00000000..25f7dacb --- /dev/null +++ b/0094-Binary-Tree-Inorder-Traversal/Article/0094-Binary-Tree-Inorder-Traversal.md @@ -0,0 +1,67 @@ +# LeetCode 第 94 号问题:二叉树的中序遍历 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 94 号问题:二叉树的中序遍历。题目难度为 Medium,目前通过率为 35.8% 。 + +### 题目描述 + +给定一个二叉树,返回它的*中序* 遍历。 + +**示例:** + +``` +输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + +输出: [1,3,2] +``` + +**进阶:** 递归算法很简单,你可以通过迭代算法完成吗? + +### 题目解析 + +用**栈(Stack)**的思路来处理问题。 + +中序遍历的顺序为**左-根-右**,具体算法为: + +- 从根节点开始,先将根节点压入栈 +- 然后再将其所有左子结点压入栈,取出栈顶节点,保存节点值 +- 再将当前指针移到其右子节点上,若存在右子节点,则在下次循环时又可将其所有左子结点压入栈中 + + + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/v17b8.gif) + +### 代码实现 + +``` +class Solution { + public List inorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + stack.push(cur); + cur = cur.left; + } else { + cur = stack.pop(); + list.add(cur.val); + cur = cur.right; + } + } + return list; + } +} +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0094-Binary-Tree-Inorder-Traversal/Article/0094-Binary-Tree-Inorder-Traversal2.md b/0094-Binary-Tree-Inorder-Traversal/Article/0094-Binary-Tree-Inorder-Traversal2.md new file mode 100644 index 00000000..b36ea46c --- /dev/null +++ b/0094-Binary-Tree-Inorder-Traversal/Article/0094-Binary-Tree-Inorder-Traversal2.md @@ -0,0 +1,114 @@ +## LeetCode 第 94 号问题:二叉树的中序遍历 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 94 号问题:二叉树的中序遍历。题目难度为 Medium + +### 题目描述 + +给定一个二叉树,返回它的 **中序** 遍历. + +#### 示例: + +```cassandra +输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + +输出: [1,3,2] +``` + +**进阶:** 递归算法很简单,你可以通过迭代算法完成吗? + +### 题目解析 + +#### 第一种方法: 递归 + +二叉树的中序遍历相信大家已经很熟悉了.操作流程就是 **左 -> 打印 -> 右**. + +那就按照 **左 -> 打印 -> 右** 这种顺序遍历树就可以了,递归函数实现 + +- 终止条件:当前节点为空时 +- 函数内: 递归的调用左节点,打印当前节点,再递归调用右节点 + +##### 参考代码 + +```javascript +// lang=javascript +var inorderTraversal = function(root) { + let res = []; + handle(root,res); + return res; +}; +function handle(root, res) { + if (root !== null) { + handle(root.left, res); + res.push(root.val); + handle(root.right, res); + } +} +``` + +##### 复杂度分析 + +- 时间复杂度: O(n), +- 空间复杂度: O(h),h是树的高度 + +#### 第二种方法: 迭代 + +这题的真正难点在于如何用非递归的方式实现。 + +递归的调用过程是不断往左边走,当左边走不下去了,就打印节点,并转向右边,然后右边继续这个过程,是函数自己调用自己,一层层的嵌套下去,操作系统/虚拟机自动帮我们用 **栈 **来保存了每个调用的函数,现在我们需要自己模拟这样的调用过程。 + +栈的特性是**后进先出**, 那么我们将遍历左子树的节点压栈, 当找不到左子树时, 栈顶就是最底层的左子树, 出栈打印出来; 接着转向右子树父节点, 继续遍历父节点的左子树并压栈,循环如此. + +因此遍历的过程就是: + +1. 压栈根节点 +2. 遍历左子树, 压栈, 直到左子树为空 +3. 出栈栈顶元素, 打印 +4. 转向右子树, 重复 1, 2, 3步骤 + +##### 动画理解 + +Animation2 + +##### 参考代码 + +```javascript +// lang=javascript +var inorderTraversal = function(root) { + let res = [], + stack = [], + node = root; + while (stack.length > 0 || node !== null) { + while (node) { + stack.push(node); + node = node.left; + } + node = stack.pop(); + res.push(node.val); + node = node.right; + } + return res; +} +``` + +##### 复杂度分析 + +- 时间复杂度:O(n) +- 空间复杂度:O(n) + +![qrcode](../../Pictures/qrcode.jpg) + + + + + + + diff --git a/0101-Symmetric-Tree/Article/0101-Symmetric-Tree.md b/0101-Symmetric-Tree/Article/0101-Symmetric-Tree.md new file mode 100644 index 00000000..91e5cc77 --- /dev/null +++ b/0101-Symmetric-Tree/Article/0101-Symmetric-Tree.md @@ -0,0 +1,48 @@ +# LeetCode 第 101 号问题:对称二叉树 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 第 101 号问题:对称二叉树。 + +### 题目描述 + +给定一个二叉树,检查它是否是镜像对称的。 + +例如,二叉树 `[1,2,2,3,4,4,3]` 是对称的。 + +``` + 1 + / \ + 2 2 + / \ / \ +3 4 4 3 +``` + +### 题目解析 + +用递归做比较简单:一棵树是对称的**等价**于它的左子树和右子树两棵树是对称的,问题就转变为判断两棵树是否对称。 + +### 代码实现 + +```java +class Solution { + public boolean isSymmetric(TreeNode root) { + if(root == null) return true; + //把问题变成判断两棵树是否是对称的 + return isSym(root.left, root.right); + } + //判断的是根节点为r1和r2的两棵树是否是对称的 + public boolean isSym(TreeNode r1, TreeNode r2){ + if(r1 == null && r2 == null) return true; + if(r1 == null || r2 == null) return false; + //这两棵树是对称需要满足的条件: + //1.俩根节点相等。 2.树1的左子树和树2的右子树,树2的左子树和树1的右子树都得是对称的 + return r1.val == r2.val && isSym(r1.left, r2.right) + && isSym(r1.right, r2.left); + } +} +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0102-Binary-Tree-Level-Order-Traversal/Article/0102-Binary-Tree-Level-Order-Traversal.md b/0102-Binary-Tree-Level-Order-Traversal/Article/0102-Binary-Tree-Level-Order-Traversal.md new file mode 100644 index 00000000..f9f7f636 --- /dev/null +++ b/0102-Binary-Tree-Level-Order-Traversal/Article/0102-Binary-Tree-Level-Order-Traversal.md @@ -0,0 +1,97 @@ +# LeetCode 第 102 号问题:二叉树的层序遍历 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 102 号问题:二叉树的层序遍历。题目难度为 Medium,目前通过率为 55.8% 。 + +### 题目描述 + +给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 + +例如: +给定二叉树: `[3,9,20,null,null,15,7]`, + +``` + 3 + / \ + 9 20 + / \ + 15 7 +``` + +返回其层次遍历结果: + +``` +[ + [3], + [9,20], + [15,7] +] +``` + +### 题目解析 + +该问题需要用到**队列** + +- 建立一个queue +- 先把根节点放进去,这时候找根节点的左右两个子节点 +- 去掉根节点,此时queue里的元素就是下一层的所有节点 +- 用for循环遍历,将结果存到一个一维向量里 +- 遍历完之后再把这个一维向量存到二维向量里 +- 以此类推,可以完成层序遍历 + + + + + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/2elr5.gif) + + + +### 代码实现 + +``` +/// BFS +/// Time Complexity: O(n), where n is the number of nodes in the tree +/// Space Complexity: O(n) +class Solution { +public: + vector> levelOrder(TreeNode* root) { + + vector> res; + if(root == NULL) + return res; + + queue> q; + q.push(make_pair(root, 0)); + + while(!q.empty()){ + + TreeNode* node = q.front().first; + int level = q.front().second; + q.pop(); + + if(level == res.size()) + res.push_back(vector()); + assert( level < res.size() ); + + res[level].push_back(node->val); + if(node->left) + q.push(make_pair(node->left, level + 1 )); + if(node->right) + q.push(make_pair(node->right, level + 1 )); + } + + return res; + } +}; + +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0103-Binary-Tree-Zigzag-Level-Order-Traversal/Article/0103-Binary-Tree-Zigzag-Level-Order-Traversal.md b/0103-Binary-Tree-Zigzag-Level-Order-Traversal/Article/0103-Binary-Tree-Zigzag-Level-Order-Traversal.md new file mode 100644 index 00000000..5db97ad9 --- /dev/null +++ b/0103-Binary-Tree-Zigzag-Level-Order-Traversal/Article/0103-Binary-Tree-Zigzag-Level-Order-Traversal.md @@ -0,0 +1,56 @@ +# LeetCode 第 103 号问题:二叉树的锯齿形层次遍历 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 103 号问题:二叉树的锯齿形层次遍历。题目难度为 Medium,目前通过率为 43.8% 。 + +### 题目描述 + +给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 + +例如: +给定二叉树 `[3,9,20,null,null,15,7]`, + +``` + 3 + / \ + 9 20 + / \ + 15 7 +``` + +返回锯齿形层次遍历如下: + +``` +[ + [3], + [20,9], + [15,7] +] +``` + +### 题目解析 + +该问题需要用到**队列**,与之前的[二叉树的层次遍历](https://xiaozhuanlan.com/topic/8579460312)类似,不同点在于在偶数层需要翻转一下。 + +- 建立一个queue +- 先把根节点放进去,这时候找根节点的左右两个子节点 +- 去掉根节点,此时queue里的元素就是下一层的所有节点 +- 循环遍历,将结果存到一个一维向量里 +- 遍历完之后再把这个一维向量存到二维向量里 +- 如果该层为偶数层,则reverse翻转一下 +- 以此类推,可以完成层序遍历 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xuoqo.gif) + +### 代码实现 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/7mnmj.png) + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0104-Maximum-Depth-Of-Binary-Tree/Animation/0104-Maximum-Depth-Of-Binary-Tree-BFS.m4v b/0104-Maximum-Depth-Of-Binary-Tree/Animation/0104-Maximum-Depth-Of-Binary-Tree-BFS.m4v new file mode 100644 index 00000000..a34dcb75 Binary files /dev/null and b/0104-Maximum-Depth-Of-Binary-Tree/Animation/0104-Maximum-Depth-Of-Binary-Tree-BFS.m4v differ diff --git a/0104-Maximum-Depth-Of-Binary-Tree/Animation/0104-Maximum-Depth-Of-Binary-Tree-DFS.m4v b/0104-Maximum-Depth-Of-Binary-Tree/Animation/0104-Maximum-Depth-Of-Binary-Tree-DFS.m4v new file mode 100644 index 00000000..5d8fde60 Binary files /dev/null and b/0104-Maximum-Depth-Of-Binary-Tree/Animation/0104-Maximum-Depth-Of-Binary-Tree-DFS.m4v differ diff --git a/0104-Maximum-Depth-Of-Binary-Tree/Animation/Animation1.gif b/0104-Maximum-Depth-Of-Binary-Tree/Animation/Animation1.gif new file mode 100644 index 00000000..4c0e71e0 Binary files /dev/null and b/0104-Maximum-Depth-Of-Binary-Tree/Animation/Animation1.gif differ diff --git a/0104-Maximum-Depth-Of-Binary-Tree/Animation/Animation2.gif b/0104-Maximum-Depth-Of-Binary-Tree/Animation/Animation2.gif new file mode 100644 index 00000000..f1ef7212 Binary files /dev/null and b/0104-Maximum-Depth-Of-Binary-Tree/Animation/Animation2.gif differ diff --git a/0104-Maximum-Depth-Of-Binary-Tree/Article/0104-Maximum-Depth-Of-Binary-Tree.md b/0104-Maximum-Depth-Of-Binary-Tree/Article/0104-Maximum-Depth-Of-Binary-Tree.md new file mode 100644 index 00000000..b895ad6a --- /dev/null +++ b/0104-Maximum-Depth-Of-Binary-Tree/Article/0104-Maximum-Depth-Of-Binary-Tree.md @@ -0,0 +1,122 @@ +# LeetCode 第 104 号问题:二叉树的最大深度 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享的题目来源于 LeetCode 上第 104 号问题:二叉树的最大深度。题目难度为 Easy 。 + +### 题目描述 + +给定一个二叉树,找出其最大深度。 + +二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + +**说明:** 叶子节点是指没有子节点的节点。 + +**示例 :** + +给定二叉树 `[3,9,20,null,null,15,7]`, + +``` + 3 + / \ + 9 20 + / \ + 15 7 +``` + +返回它的最大深度 3 。 + +### 题目解析 - DFS + +最直接的办法就是使用DFS ( 深度优先搜索 ) 策略计算树的高度. 具体算法流程如下: + +- **终止条件:**当前节点为空 +- **返回值:** + - 节点为空时,所以返回 0 + - 节点不为空时, 返回左右子树高度的最大值 + 1 + +### 动画描述 + +![](../Animation/Animation1.gif) + +### 代码实现 + +```javascript +/** + * JavaScript 描述 + * DFS + */ +var maxDepth = function(root) { + if (root == null) { + return 0; + } + let leftHeight = maxDepth(root.left); + let rightHeight = maxDepth(root.right); + return Math.max(leftHeight, rightHeight) + 1; +}; +``` + +**精简版** + +```javascript +var maxDepth = function(root) { + return !root ? 0 : Math.max(maxDepth(root.left) + 1, maxDepth(root.right) + 1) ; +}; +``` + +### 复杂度分析 + +- 时间复杂度:**O(n)**, 我们每个结点只访问一次,因此时间复杂度为 O(N) +- 空间复杂度: + - 最坏情况下,树是完全不平衡的,例如每个结点只剩下左子结点,递归将会被调用 N 次(树的高度),因此保持调用栈的存储将是 O(N)。 + - 最好情况下(树是完全平衡的),树的高度将是 log(N)。因此,在这种情况下的空间复杂度将是 O(log(N)) + + + +### 题目解析 - BFS + +求二叉树的深度也就是求二叉树有几层了, 采用 BFS ( 广度优先搜索 ) 策略对二叉树按层遍历. + +实现 BFS 就要用到 '先进先出' 的队列了, 具体算法流程如下: + +- 遍历二叉树节点,依次将当前节点 和它的左右子节点入队 +- 依次出队, 出队子节点重复上一步操作 + +### 动画描述 + +![](../Animation/Animation2.gif) + +### 代码实现 + +```javascript +/** + * JavaScript 描述 + * BFS + */ +var maxDepth = function(root) { + let level = 0; + if (root == null) { + return level; + } + let queue = [root]; + while (queue.length) { + let len = queue.length; + while (len--) { + let curNode = queue.pop(); + curNode.left && queue.unshift(curNode.left); + curNode.right && queue.unshift(curNode.right); + } + level++; + } + return level; +}; +``` + +### 复杂度分析 + +- 时间复杂度:**O(n)** +- 空间复杂度:**O(N)** + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0107-Binary-Tree-Level-Order-Traversal-II/Article/0107-Binary-Tree-Level-Order-Traversal-II.md b/0107-Binary-Tree-Level-Order-Traversal-II/Article/0107-Binary-Tree-Level-Order-Traversal-II.md new file mode 100644 index 00000000..80fa1dec --- /dev/null +++ b/0107-Binary-Tree-Level-Order-Traversal-II/Article/0107-Binary-Tree-Level-Order-Traversal-II.md @@ -0,0 +1,59 @@ +# LeetCode 第 107 号问题:二叉树的层次遍历 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 107 号问题:二叉树的层次遍历 II。题目难度为 Easy,目前通过率为 55.8% 。 + +### 题目描述 + +给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) + +例如: +给定二叉树 `[3,9,20,null,null,15,7]`, + +``` + 3 + / \ + 9 20 + / \ + 15 7 +``` + +返回其自底向上的层次遍历为: + +``` +[ + [15,7], + [9,20], + [3] +] +``` + +### 题目解析 + +该问题需要用到**队列**,解法与上篇[每天一算:Binary Tree Level Order Traversal](https://xiaozhuanlan.com/topic/8579460312)类似,区别在于最后存储方式的不同。 + +- 建立一个 queue +- 先把根节点放进去,这时候找根节点的左右两个子节点 +- 去掉根节点,此时queue里的元素就是下一层的所有节点 +- 用 for 循环遍历,将结果存到一个一维向量里 +- 遍历完之后再把这个一维向量**插入**到二维向量里 +- 以此类推,可以完成层序遍历 + + + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/varp8.gif) + + + +### 代码实现 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/9iccc.png) + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git "a/0110-Balanced-Binary-Tree/Animation/0110-Balanced-Binary-Tree-\350\207\252\345\272\225\345\220\221\344\270\212.m4v" "b/0110-Balanced-Binary-Tree/Animation/0110-Balanced-Binary-Tree-\350\207\252\345\272\225\345\220\221\344\270\212.m4v" new file mode 100644 index 00000000..23b6c093 Binary files /dev/null and "b/0110-Balanced-Binary-Tree/Animation/0110-Balanced-Binary-Tree-\350\207\252\345\272\225\345\220\221\344\270\212.m4v" differ diff --git "a/0110-Balanced-Binary-Tree/Animation/0110-Balanced-Binary-Tree-\350\207\252\351\241\266\345\220\221\344\270\213.m4v" "b/0110-Balanced-Binary-Tree/Animation/0110-Balanced-Binary-Tree-\350\207\252\351\241\266\345\220\221\344\270\213.m4v" new file mode 100644 index 00000000..6bc5068b Binary files /dev/null and "b/0110-Balanced-Binary-Tree/Animation/0110-Balanced-Binary-Tree-\350\207\252\351\241\266\345\220\221\344\270\213.m4v" differ diff --git a/0110-Balanced-Binary-Tree/Animation/Animation1.gif b/0110-Balanced-Binary-Tree/Animation/Animation1.gif new file mode 100644 index 00000000..26ca8eb3 Binary files /dev/null and b/0110-Balanced-Binary-Tree/Animation/Animation1.gif differ diff --git a/0110-Balanced-Binary-Tree/Animation/Animation2.gif b/0110-Balanced-Binary-Tree/Animation/Animation2.gif new file mode 100644 index 00000000..75ef5778 Binary files /dev/null and b/0110-Balanced-Binary-Tree/Animation/Animation2.gif differ diff --git a/0110-Balanced-Binary-Tree/Article/0110-Balanced-Binary-Tree.md b/0110-Balanced-Binary-Tree/Article/0110-Balanced-Binary-Tree.md new file mode 100644 index 00000000..dd670cea --- /dev/null +++ b/0110-Balanced-Binary-Tree/Article/0110-Balanced-Binary-Tree.md @@ -0,0 +1,47 @@ +# LeetCode 第 110 号问题:平衡二叉树 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 110 号问题:平衡二叉树。 + +### 题目描述 + +给定一个二叉树,判断它是否是高度平衡的二叉树。 + +### 题目解析 + +采取**后序遍历**的方式遍历二叉树的每一个结点。 + +在遍历到一个结点之前已经遍历了它的左右子树,那么只要在遍历每个结点的时候记录它的深度(某一结点的深度等于它到叶结点的路径的长度),就可以一边遍历一边判断每个结点是不是平衡的。 + +### 动画描述 + +待补充 + +### 代码实现 + +```java +class Solution { + private boolean isBalanced = true; + public boolean isBalanced(TreeNode root) { + getDepth(root); + return isBalanced; + } + public int getDepth(TreeNode root) { + if (root == null) + return 0; + int left = getDepth(root.left); + int right = getDepth(root.right); + if (Math.abs(left - right) > 1) { + isBalanced = false; + } + return right > left ? right + 1 : left + 1; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0110-Balanced-Binary-Tree/Article/0110-Balanced-Binary-Tree2.md b/0110-Balanced-Binary-Tree/Article/0110-Balanced-Binary-Tree2.md new file mode 100644 index 00000000..73b7e380 --- /dev/null +++ b/0110-Balanced-Binary-Tree/Article/0110-Balanced-Binary-Tree2.md @@ -0,0 +1,165 @@ +# LeetCode 第 110 号问题:平衡二叉树 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 110 号问题:平衡二叉树。 + +### 题目描述 + +给定一个二叉树,判断它是否是高度平衡的二叉树。 + +本题中,一棵高度平衡二叉树定义为: + +> 一个二叉树*每个节点* 的左右两个子树的高度差的绝对值不超过1。 + +**示例 1:** + +``` + 3 + / \ + 9 20 + / \ + 15 7 +``` + +返回 `true` 。 + +**示例 2:** + +给定二叉树 `[1,2,2,3,3,null,null,4,4]` + +``` + 1 + / \ + 2 2 + / \ + 3 3 + / \ + 4 4 +``` + +返回 `false` 。 + +### 题目解析 - 自顶向下 + +这道题可以算是递归的充分使用了, 每一个子树都是子问题. + +根据题意, 直观的想法就是计算当前节点左右子树的高度差了, 具体算法流程如下: + +*定义* 方法 `depth(root)` 计算 root 最大高度 + +- **终止条件:** 当 `root` 为空,即越过叶子节点,则返回高度 0 +- **返回值:** Max(左子树高度, 右子树高度 ) + 1 + +*定义* 方法 `isBalanced(root)` 判断树 `root` 是否平衡 + +- **特例处理:** 若树根节点 `root` 为空,则直接返回 true +- **返回值:** 所有子树都需要满足平衡树性质,因此以下三者使用与 逻辑与 连接 + - `abs(depth(root.left) - depth(root.right)) < 2` :判断 **当前子树** 是否是平衡树 + - `isBalanced(root.left)` : 先序遍历递归,判断 **当前子树的左子树** 是否是平衡树; + - `isBalanced(root.right)` : 先序遍历递归,判断 **当前子树的右子树** 是否是平衡树; + +> 通过流程能发现, 暴力法虽然容易想到, 但是会产生大量冗余计算, 因此时间复杂度也就会高; +> +> 想避免这种情况, 移步向下看 自底向上 方法 + +### 动画描述 + +Animation1 + +### 参考代码 + +```javascript +/** + * JavaScript 描述 + * 自顶向下递归 + */ +function depth(root) { + if (root == null) { + return 0; + } + return Math.max(depth(root.left), depth(root.right)) + 1; +}; +var isBalanced = function(root) { + if (root == null) { + return true; + } + return Math.abs(depth(root.left) - depth(root.right)) < 2 && + isBalanced(root.left) && + isBalanced(root.right); +}; +``` + +### 复杂度分析 + +- 时间复杂度: **O(Nlog_2 N)** + + 最差情况下, isBalanced(root) 遍历树所有节点,占用 O(N)O(N) ;判断每个节点的最大高度 depth(root) 需要遍历 各子树的所有节点 ,子树的节点数的复杂度为 O(log_2 N) + +- 空间复杂度: **O(N)** + + 最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间 + +### 题目解析 - 自底向上 + +**自顶向下** 计算 `depth` 存在大量冗余, 每次调用 `depth` 时,要同时计算其子树高度。 + +**自底向上** 计算每个子树的高度只会计算一次。先递归计算当前节点的子节点高度,然后再通过子节点高度判断当前节点是否平衡,从而消除冗余。 + +**自底向上** 与 **自顶向下** 的逻辑相反,首先判断子树是否平衡,然后比较子树高度判断父节点是否平衡。算法如下: + +*定义* 方法 `recur(root):` : 判断子树是否平衡 | 返回当前节点高度 + +- **递归终止条件:** + - 当越过叶子节点时, 返回高度 0 + - 当左(右)子树高度 `left== -1` 时,代表此子树的 **左(右)子树** 不是平衡树, 因此直接返回 `-1` +- **递归返回值:** + - 当节点 `root` 左 / 右子树的高度差 < 2:返回以节点 root 为根节点的子树的最大高度Max( left, right ) + 1 + - 当节点 `root` 左 / 右子树的高度差 >= 2 :则返回 `-1` , 代表 **此子树不是平衡树** + +*定义* 方法 `isBalanced(root)` : 判断当前树是否平衡 + +- **返回值:** 若 `recur(root) != 1` , 则说明此树平衡, 返回 `true` , 否则返回 `false` + +### 动画描述 + +Animation2 + +### 参考代码 + +```javascript +/** + * JavaScript 描述 + * 自底向上递归 + */ +function recur(root) { + if (root == null) { + return 0; + } + let leftHeight = recur(root.left); + if (leftHeight == -1) { + return -1; + } + let rightHeight = recur(root.right); + if (rightHeight == -1) { + return -1; + } + return Math.abs(leftHeight - rightHeight) < 2 ? + Math.max(leftHeight,rightHeight) + 1 : -1; +}; +var isBalanced = function(root) { + return recur(root) != -1; +}; +``` + +### 复杂度分析 + +- 时间复杂度 **O(N)**: N为树的节点数;最差情况下,需要递归遍历树的所有节点。 +- 空间复杂度 **O(N)**: 最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间。 + + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0118-Generate/Animation/resource.gif b/0118-Generate/Animation/resource.gif new file mode 100644 index 00000000..c002af43 Binary files /dev/null and b/0118-Generate/Animation/resource.gif differ diff --git a/0118-Generate/Article/0118-Generate.md b/0118-Generate/Article/0118-Generate.md new file mode 100644 index 00000000..b6b6c6d1 --- /dev/null +++ b/0118-Generate/Article/0118-Generate.md @@ -0,0 +1,71 @@ +## LeetCode第118号问题:杨辉三角 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 个人博客:www.zhangxiaoshuai.fun + +**本题选自leetcode第118题,easy级别,目前通过率66.4%** +### 题目描述: +``` +给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。 +示例: + +输入: 5 +输出: +[ + [1], + [1,1], + [1,2,1], + [1,3,3,1], + [1,4,6,4,1] +] +``` + +### 题目分析: +初中时候学习的杨辉三角想不到又在这里出现了,题意很容易理解,每一行中的第一个数字和最后一个数字都是1,中间的数字都是通过上面相邻的两个数字相加得到。题目给我们一个杨辉三角的非负行数,然后我们生成对应的杨辉三角(集合)。 +既然返回的是一个List>,那么我们用一个大集合来放置每一行的数,每一行的数我们分别用一个小集合来存放,最后将每一个小集合添加进大集合中。 + +### gif动画演示: + +官方中已经有做的非常好的gif图解,这里直接展示: + +![](../Animation/resource.gif) + +### 代码: + +```java +public List> generate(int numRows) { + List> triangle = new ArrayList>(); + //给定的numRows为0时直接返回空集合即可 + if (numRows == 0) { + return triangle; + } + + //因为杨辉三角的第一行总是1,所以先新建一个list,并将1加入该list中 + triangle.add(new ArrayList<>()); + triangle.get(0).add(1); + + //从第二行开始,新建表示当前行的list,拿到当前行的前一行的list + for (int rowNum = 1; rowNum < numRows; rowNum++) { + List row = new ArrayList<>(); + List prevRow = triangle.get(rowNum-1); + + //一行中的第一个元素 + row.add(1); + + //针对每一行,都是上一行的相邻的两个元素相加得到两个1中间的数 + for (int j = 1; j < rowNum; j++) { + row.add(prevRow.get(j-1) + prevRow.get(j)); + } + + //一行中的最后一个元素 + row.add(1); + + //最后将“整行添加到大集合中” + triangle.add(row); + } + return triangle; +} +``` + + diff --git a/0120-Triangle/Animation/120.gif b/0120-Triangle/Animation/120.gif new file mode 100644 index 00000000..3f119602 Binary files /dev/null and b/0120-Triangle/Animation/120.gif differ diff --git a/0120-Triangle/Animation/120.m4v b/0120-Triangle/Animation/120.m4v new file mode 100644 index 00000000..24ae0571 Binary files /dev/null and b/0120-Triangle/Animation/120.m4v differ diff --git a/0120-Triangle/Article/0120-Triangle.md b/0120-Triangle/Article/0120-Triangle.md new file mode 100644 index 00000000..1256629b --- /dev/null +++ b/0120-Triangle/Article/0120-Triangle.md @@ -0,0 +1,129 @@ +# LeetCode 第 120 号问题:三角形最小路径和 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 120 号问题:三角形最小路径和。题目难度为 Medium,目前通过率为 64.7% 。 + + +
+ + +### 题目描述 + +给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 + +相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。 + + + +**示例 1:** + +``` +[ + [2], + [3,4], + [6,5,7], + [4,1,8,3] +] +``` +自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + +**说明:** + +如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 + +
+ +### 题目解析 + +给定一个三角形数组,需要求出从上到下的最小路径和,再确定这道题目可以用动态规划来解后,可以按照四个步骤来分析: + +* 问题拆解: + + 这里的总问题是求出最小的路径和,路径是这里的分析重点,路径是由一个个元素组成的,`[i][j]` 位置的元素,经过这个元素的路径肯定也会经过 `[i - 1][j]` 或者 `[i - 1][j - 1]`,因此经过一个元素的路径和可以通过这个元素上面的一个或者两个元素的路径和得到 + +* 状态定义 + + 状态的定义一般会和问题需要求解的答案联系在一起,这里其实有两种方式,一种是考虑路径从上到下,另外一种是考虑路径从下到上,因为元素的值是不变的,所以路径的方向不同也不会影响最后求得的路径和,如果是从上到下,你会发现,在考虑下面元素的时候,起始元素的路径只会从 [i - 1][j] 获得,每行当中的最后一个元素的路径只会从 [i - 1][j - 1] 获得,中间二者都可,这样不太好实现,因此这里考虑从下到上的方式,状态的定义就变成了 “**最后一行元素到当前元素的最小路径和**”,对于 [0][0] 这个元素来说,最后状态表示的就是我们的最终答案 + +* 递推方程 + + “状态定义” 中我们已经定义好了状态,递推方程就出来了 + ``` + dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j] + ``` + +* 实现 + + 这里初始化时,我们需要将最后一行的元素填入状态数组中,然后就是按照前面分析的策略,从下到上计算即可 + +这里有一个小小的空间上面的优化,就是每次我们更新状态(dp)数组都是基于之前的结果,我们并不需要知道之前的之前的结果,平行的状态之间也没有相互影响,因此只用开一维数组即可 + +
+ +### 代码实现(空间优化前) + +```java +public int minimumTotal(List> triangle) { + int n = triangle.size(); + + int[][] dp = new int[n][n]; + + List lastRow = triangle.get(n - 1); + + for (int i = 0; i < n; ++i) { + dp[n - 1][i] = lastRow.get(i); + } + + for (int i = n - 2; i >= 0; --i) { + List row = triangle.get(i); + for (int j = 0; j < i + 1; ++j) { + dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + row.get(j); + } + } + + return dp[0][0]; +} +``` + +
+ +### 代码实现(空间优化后) +```java +public int minimumTotal(List> triangle) { + int n = triangle.size(); + + int[] dp = new int[n]; + + List lastRow = triangle.get(n - 1); + + for (int i = 0; i < n; ++i) { + dp[i] = lastRow.get(i); + } + + for (int i = n - 2; i >= 0; --i) { + List row = triangle.get(i); + for (int j = 0; j < i + 1; ++j) { // i + 1 == row.size() + dp[j] = Math.min(dp[j], dp[j + 1]) + row.get(j); + } + } + + return dp[0]; +} +``` + +
+ +### 动画描述 + +![](../Animation/120.gif) + +
+ +### 复杂度分析 + +时空复杂度从代码中都清晰可见,我们必须遍历三角形中的每个元素。时间复杂度就是 `O(1 + 2 + ... + n)`,也就是 `O(n^2)`。空间复杂度经过优化后是 `O(n)`。 + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0124-Binary-Tree-Maximum-Path-Sum/Animation/124.gif b/0124-Binary-Tree-Maximum-Path-Sum/Animation/124.gif new file mode 100644 index 00000000..2b3ec423 Binary files /dev/null and b/0124-Binary-Tree-Maximum-Path-Sum/Animation/124.gif differ diff --git a/0124-Binary-Tree-Maximum-Path-Sum/Animation/124.m4v b/0124-Binary-Tree-Maximum-Path-Sum/Animation/124.m4v new file mode 100644 index 00000000..232a143e Binary files /dev/null and b/0124-Binary-Tree-Maximum-Path-Sum/Animation/124.m4v differ diff --git a/0124-Binary-Tree-Maximum-Path-Sum/Article/0124-Binary-Tree-Maximum-Path-Sum.md b/0124-Binary-Tree-Maximum-Path-Sum/Article/0124-Binary-Tree-Maximum-Path-Sum.md new file mode 100644 index 00000000..32097b3f --- /dev/null +++ b/0124-Binary-Tree-Maximum-Path-Sum/Article/0124-Binary-Tree-Maximum-Path-Sum.md @@ -0,0 +1,97 @@ +# LeetCode 第 124 号问题:二叉树中的最大路径和 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 124 号问题:二叉树中的最大路径和。题目难度为 Hard,目前通过率为 39.9% 。 + + +
+ + +### 题目描述 + +给定一个非空二叉树,返回其最大路径和。 + +本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。 + +**示例 1:** + +``` +输入: [1,2,3] + + 1 + / \ + 2 3 + +输出: 6 +``` + +**示例 2:** + +``` +输入: [-10,9,20,null,null,15,7] + + -10 + / \ + 9 20 + / \ + 15 7 + +输出: 42 +``` + +
+ +### 题目解析 + +二叉树问题,题目要求出一个二叉树的最大路径和,路径和就是把一条路径上面节点的值加起来,这一题的难点在于路径的方向不固定,只要是任意两点间的通路都算是有效路径,如果不提前列出合理的规划,这道题将无从下手。一般来说,解决树的问题都需要用到递归,**树上的搜索,本质上也是深度优先搜索**,但是这里会有两种考虑方式,一个是**自底向上的分治**,也就是进入递归,一开始不做任何节点上面的计算或者是处理,直接进入到下一层递归,直到到了最底层,然后再开始计算并返回答案,然后上层树节点的递归函数就会收到下层返回的结果,这样做的好处是,一个节点可以获知其子树的局部答案;另外一个是**自顶向下的遍历搜索**,这个和之前的思路完全相反,也就是先处理当前节点的内容,处理完后去到下一层节点,这种方法一般没有返回值,但是一般会有一个全局或者是引用变量,用来记录遍历过程中的内容。 + +我们再回过头来看这道题,在递归遍历的过程中,对于当前节点,其在路径中可以是路径尾,路径头(假设路径是从上到下的,其实在这道题中,没有头尾的概念),也可以是路径中的一个节点。那怎么判断呢?这时我们得需要当前节点左右子树的信息,所以我们可以考虑使用之前提到的 **自底向上** 的分治,有了当前节点,左右子树到当前节点的最大路径,我们可以看看这里会有几种情况,我用 **root** 表示当前节点,**left** 表示左子树到 root 的最大和的路径,**right** 表示右子树到 root 的最大和的路径: +* root 和左右路径形成路径(left - root - right) +* root 和左路径形成路径(left - root) +* root 和右路径形成路径(root - right) +* root 自成路径(root) + +你可以看到这四种情况都会把当前节点考虑在内,我们可以更新这里的最大值。但是需要注意的是,我们返回的时候,第一种情况是不能返回的,因为对于上一层节点来说,其无法形成有效的路径,因此我们只需要将 2,3,4 中的最大值返回即可,当然,更新全局答案的时候,这 4 种情况都需要考虑在内的。 + +
+ +### 代码实现 + +```java +private int maximum = Integer.MIN_VALUE; + +public int maxPathSum(TreeNode root) { + if (root == null) { + return 0; + } + + helper(root); + + return maximum; +} + +private int helper(TreeNode root) { + if (root == null) { + return 0; + } + // 如果左右子树返回的最大路径值小于 0 + // 直接将值设为 0,也就是不考虑对应的路径 + int leftMax = Math.max(0, helper(root.left)); + int rightMax = Math.max(0, helper(root.right)); + + maximum = Math.max(root.val + leftMax + rightMax, maximum); + + return Math.max(leftMax + root.val, rightMax + root.val); +} +``` + +
+ +### 动画描述 + +![](../Animation/124.gif) + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0125-Valid-Palindrome/Animation/animation.gif b/0125-Valid-Palindrome/Animation/animation.gif new file mode 100644 index 00000000..418f44d7 Binary files /dev/null and b/0125-Valid-Palindrome/Animation/animation.gif differ diff --git a/0125-Valid-Palindrome/Article/0125-Valid-Palindrome.md b/0125-Valid-Palindrome/Article/0125-Valid-Palindrome.md new file mode 100644 index 00000000..c07fafdb --- /dev/null +++ b/0125-Valid-Palindrome/Article/0125-Valid-Palindrome.md @@ -0,0 +1,78 @@ +# LeetCode 第 125 号问题:验证回文串 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 第 125 号问题:验证回文串。这道题目是 **初级程序员** 在面试的时候经常遇到的一道算法题,而且面试官喜欢面试者手写! + + + +### 题目描述 + +给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。 + +**说明:**本题中,我们将空字符串定义为有效的回文串。 + +**示例 1:** + +``` +输入: "A man, a plan, a canal: Panama" +输出: true +``` + +**示例 2:** + +``` +输入: "race a car" +输出: false +``` + +### 题目解析 + +先理解一个概念:所谓回文,就是一个正读和反读都一样的字符串。 + +先假设是验证单词 `level` 是否是回文字符串,通过概念涉及到 正 与 反 ,那么很容易想到使用双指针,从字符的开头和结尾处开始遍历整个字符串,相同则继续向前寻找,不同则直接返回 false。 + +而这里与单独验证一个单词是否是回文字符串有所区别的是加入了 空格 与 非字母数字的字符,但实际上的做法一样的: + +一开始先建立两个指针,left 和 right , 让它们分别从字符的开头和结尾处开始遍历整个字符串。 + +如果遇到非字母数字的字符就跳过,继续往下找,直到找到下一个字母数字或者结束遍历,如果遇到大写字母,就将其转为小写。 + +当左右指针都找到字母数字时,可以进行比较的时候,比较这两个字符,如果相等,则两个指针向它们的前进方向挪动,然后继续比较下面两个分别找到的字母数字,若不相等,直接返回 false。 + +### 动画描述 + +![](../Animation/animation.gif) + +### 代码实现 + +注:`isLetterOrDigit ` 方法确定指定的字符是否为字母或数字。 + +```java +class Solution { + public boolean isPalindrome(String s) { + if(s.length() == 0) + return true; + int l = 0, r = s.length() - 1; + while(l < r){ + //确定指定的字符是否为字母或数字 + if(!Character.isLetterOrDigit(s.charAt(l))){ + l++; + }else if(!Character.isLetterOrDigit(s.charAt(r))){ + r--; + }else{ + if(Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r))) + return false; + l++; + r--; + } + } + return true; + } +} + +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0128-Longest-Consecutive-Sequence/Animation/128-1.gif b/0128-Longest-Consecutive-Sequence/Animation/128-1.gif new file mode 100644 index 00000000..f93ad1dc Binary files /dev/null and b/0128-Longest-Consecutive-Sequence/Animation/128-1.gif differ diff --git a/0128-Longest-Consecutive-Sequence/Animation/128-1.m4v b/0128-Longest-Consecutive-Sequence/Animation/128-1.m4v new file mode 100644 index 00000000..5c922583 Binary files /dev/null and b/0128-Longest-Consecutive-Sequence/Animation/128-1.m4v differ diff --git a/0128-Longest-Consecutive-Sequence/Animation/128-2.gif b/0128-Longest-Consecutive-Sequence/Animation/128-2.gif new file mode 100644 index 00000000..de627a3d Binary files /dev/null and b/0128-Longest-Consecutive-Sequence/Animation/128-2.gif differ diff --git a/0128-Longest-Consecutive-Sequence/Animation/128-2.m4v b/0128-Longest-Consecutive-Sequence/Animation/128-2.m4v new file mode 100644 index 00000000..94bc5c84 Binary files /dev/null and b/0128-Longest-Consecutive-Sequence/Animation/128-2.m4v differ diff --git a/0128-Longest-Consecutive-Sequence/Article/0128-Longest-Consecutive-Sequence.md b/0128-Longest-Consecutive-Sequence/Article/0128-Longest-Consecutive-Sequence.md new file mode 100644 index 00000000..38e5accc --- /dev/null +++ b/0128-Longest-Consecutive-Sequence/Article/0128-Longest-Consecutive-Sequence.md @@ -0,0 +1,218 @@ +# LeetCode 第 128 号问题:最长连续序列 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 128 号问题:最长连续序列。题目难度为 Hard,目前通过率为 48.5% 。 + + +
+ + +### 题目描述 + +给定一个未排序的整数数组,找出最长连续序列的长度。 + +要求算法的时间复杂度为 O(n)。 + +**示例 1:** + +``` +输入: [100, 4, 200, 1, 3, 2] +输出: 4 +解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。 +``` + +
+ +### 题目解析 + +题目直接明了,给你一个未排序的数组,让你从中找出一些元素,使这些元素能够组成最长 **连续的递增序列**,输出这个序列的长度,元素的先后没有关系,比如: + +``` +[100, 4, 200, 1, 3, 2] + +可以找出 4, 1, 3, 2 组成连续递增序列 1, 2, 3, 4 + +输出这个序列长度 4 +``` +很直接的想法是把数组排序一下,然后遍历一遍就可以找到答案,但是这道题目的难点在于它限制时间复杂度为 O(n),这样一来,排序这条路走不通。 + +这道题目其实有一个特征,就是这道题目隐含着 **连通性** 这个性质在里面,怎么讲?我们还是拿上面那个例子来举例: + +``` +[100, 4, 200, 1, 3, 2] + +我们从左向右枚举数组里面的元素,你可以认为枚举过的元素是有效的: +................100 枚举第一个元素,此时有 1 个连通区域 +...4............100 枚举第二个元素,第二个元素和前面的元素互不相连,此时有 2 个连通区域 +...4............100............200 枚举第三个元素,三个元素互不相连,此时有 3 个连通区域 +1..4............100............200 枚举第四个元素,四个元素互不相连,此时有 4 个连通区域 +1.34............100............200 枚举第五个元素,这个元素和之前第二个连通区域相连,连通区域维持在 4 个 +1234............100............200 枚举第六个元素,这个元素和两个连通区域相连,连通区域变成 3 个 + +最后包含元素最多的那个连通区域所包含的元素个数就是我们要的答案 +``` + +知道了这些东西对我们解题有什么帮助呢?关于连通性的问题,首先要想到的一个数据结构就是 **并查集**,这个数据结构的设计初衷就是为了解决连通性的问题,而且它的两个操作,查找以及合并的时间复杂度可以近似看成是 O(1),因此用来解决这道题目再适合不过了。 + +如果你能想到并查集,那么这道题目其实就没有更多的难点,但我想说的是,这道题目其实还有一个比较有趣的解法,是利用 HashMap 来记录边界点所涵盖的连通区块长度,还是跟着例子走一遍: + +``` +[100, 4, 200, 1, 3, 2] + +我们还是从左向右枚举数组里面的元素,每次遍历都去看这个元素的左右是否存在,并更新 HashMap: +100 此时 99 以及 101 都没有任何区块,Map {100=1},表示 100 这个区块大小为 1 +4 此时 3 以及 5 都没有任何区块,Map {100=1, 4=1} +200 此时 199 以及 201 都没有任何区块,Map {100=1, 4=1, 200=1} +1 此时 0 以及 2 都没有任何区块,Map {100=1, 4=1, 200=1, 1=1} +3 发现 4 是存在的,4,3 形成一个新的区块, + 这个区块的左边界是 3,右边界是 4,区块大小是 2, + Map 中更新边界元素所代表的区块大小,Map {100=1, 4=2, 200=1, 1=1, 3=2} +2 发现左右边界同时存在,1, 2, 3, 4 形成一个新的区块 + 这个区块的左边界是 1,右边界是 4,区块大小是 4, + Map 中更新边界元素所代表的区块大小,并记录当前元素避免重复访问, + Map {100=1, 4=4, 200=1, 1=4, 3=2, 2=4} +``` + +可以看到,每次记录的时候,我们只需要保证区块的边界元素所表示的区块大小是正确的即可,至于区块中间的元素其实无所谓,因为这些元素并不会被再次访问到 + +这个方法其实挺巧妙的,通过利用哈希表的元素向左右延伸来确定区块的大小。 + +
+ +### 代码实现(并查集) + +```java +class Solution { + // roots 用来记录一个连通区域的代表元素 + private Map roots = new HashMap<>(); + + // counts 用来记录一个连通区域的元素个数 + private Map counts = new HashMap<>(); + + private int find(int a) { + if (roots.get(a) == a) { + return a; + } + + int root = find(roots.get(a)); + + // 路径压缩 + roots.put(a, root); + + return root; + } + + private void union(int a, int b) { + int rootA = find(a); + int rootB = find(b); + + if (rootA != rootB) { + roots.put(rootA, rootB); + + // 两个连通区域合并,更新整个区域的元素个数 + counts.put(rootB, counts.get(rootA) + counts.get(rootB)); + } + } + + public int longestConsecutive(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + for (int i = 0; i < nums.length; ++i) { + if (roots.containsKey(nums[i])) { + continue; + } + + roots.put(nums[i], nums[i]); + counts.put(nums[i], 1); + + // 查看相邻元素是否存在连通区块 + if (roots.containsKey(nums[i] - 1) && roots.containsKey(nums[i] + 1)) { + int root = find(roots.get(nums[i] - 1)); + + // 左右都存在连通区域,合并这三个区域 + union(nums[i], root); + union(root, roots.get(nums[i] + 1)); + } else if (roots.containsKey(nums[i] - 1)) { + int root = find(roots.get(nums[i] - 1)); + + // 左边存在连通区域,合并这这两个区域 + union(nums[i], root); + } else if (roots.containsKey(nums[i] + 1)) { + int root = find(roots.get(nums[i] + 1)); + + // 右边存在连通区域,合并这这两个区域 + union(nums[i], root); + } + } + + int result = 1; + + // 遍历所有连通区块,找到包含元素最多的区块 + for (int i : counts.keySet()) { + result = Math.max(result, counts.get(i)); + } + + return result; + } +} +``` + +
+ +### 动画描述(并查集) + +![](../Animation/128-1.gif) + +
+ +### 代码实现(哈希表) + +```java +public int longestConsecutive(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + Map distances = new HashMap<>(); + + int result = 1; + + for (int num : nums) { + if (distances.containsKey(num)) { + continue; + } + + // 查找向左能够延伸的最长距离 + int left = distances.getOrDefault(num - 1, 0); + + // 查找向右能够延伸的最长距离 + int right = distances.getOrDefault(num + 1, 0); + + // 更新此时的左右边界所表示的区块大小 + distances.put(num - left, left + right + 1); + distances.put(num + right, left + right + 1); + + // 数组中可能存在重复元素,记录当前元素,避免再次访问 + distances.put(num, left + right + 1); + + result = Math.max(result, left + right + 1); + } + + return result; +} +``` + +
+ +### 动画描述(哈希表) + +![](../Animation/128-2.gif) + +
+ +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0131-Palindrome-Partitioning/Animation/Animation.gif b/0131-Palindrome-Partitioning/Animation/Animation.gif new file mode 100644 index 00000000..4ea85d48 Binary files /dev/null and b/0131-Palindrome-Partitioning/Animation/Animation.gif differ diff --git a/0131-Palindrome-Partitioning/Article/0131-Palindrome-Partitioning.md b/0131-Palindrome-Partitioning/Article/0131-Palindrome-Partitioning.md new file mode 100644 index 00000000..8e634fcc --- /dev/null +++ b/0131-Palindrome-Partitioning/Article/0131-Palindrome-Partitioning.md @@ -0,0 +1,91 @@ +# LeetCode 第 131 号问题:分割回文串 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 131 号问题:分割回文串。题目难度为 Medium,目前通过率为 45.8% 。 + +### 题目描述 + +给定一个字符串 *s*,将 *s* 分割成一些子串,使每个子串都是回文串。 + +返回 *s* 所有可能的分割方案。 + +**示例:** + +```yaml +输入: "aab" +输出: +[ + ["aa","b"], + ["a","a","b"] +] +``` + +### + +### 题目解析 + +首先,对于一个字符串的分割,肯定需要将所有分割情况都遍历完毕才能判断是不是回文数。不能因为 **abba** 是回文串,就认为它的所有子串都是回文的。 + +既然需要将所有的分割方法都找出来,那么肯定需要用到DFS(深度优先搜索)或者BFS(广度优先搜索)。 + +在分割的过程中对于每一个字符串而言都可以分为两部分:左边一个回文串加右边一个子串,比如 "abc" 可分为 "a" + "bc" 。 然后对"bc"分割仍然是同样的方法,分为"b"+"c"。 + +在处理的时候去优先寻找更短的回文串,然后回溯找稍微长一些的回文串分割方法,不断回溯,分割,直到找到所有的分割方法。 + +举个🌰:分割"aac"。 + +1. 分割为 a + ac +2. 分割为 a + a + c,分割后,得到一组结果,再回溯到 a + ac +3. a + ac 中 ac 不是回文串,继续回溯,回溯到 aac +4. 分割为稍长的回文串,分割为 aa + c 分割完成得到一组结果,再回溯到 aac +5. aac 不是回文串,搜索结束 + + + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +```java +class Solution { + List> res = new ArrayList<>(); + + public List> partition(String s) { + if(s==null||s.length()==0) + return res; + dfs(s,new ArrayList(),0); + return res; + } + + public void dfs(String s,List remain,int left){ + if(left==s.length()){ //判断终止条件 + res.add(new ArrayList(remain)); //添加到结果中 + return; + } + for(int right=left;rightright是不是回文串 + if(isPalindroom(s,left,right)){ //判断是否是回文串 + remain.add(s.substring(left,right+1)); //添加到当前回文串到list中 + dfs(s,remain,right+1); //从right+1开始继续递归,寻找回文串 + remain.remove(remain.size()-1); //回溯,从而寻找更长的回文串 + } + } + } + /** + * 判断是否是回文串 + */ + public boolean isPalindroom(String s,int left,int right){ + while(left=right; + } +} +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0133-Clone-Graph/Animation/133.gif b/0133-Clone-Graph/Animation/133.gif new file mode 100644 index 00000000..8b41cf1d Binary files /dev/null and b/0133-Clone-Graph/Animation/133.gif differ diff --git a/0133-Clone-Graph/Animation/133.m4v b/0133-Clone-Graph/Animation/133.m4v new file mode 100644 index 00000000..778deaa0 Binary files /dev/null and b/0133-Clone-Graph/Animation/133.m4v differ diff --git a/0133-Clone-Graph/Article/0133-Clone-Graph.md b/0133-Clone-Graph/Article/0133-Clone-Graph.md new file mode 100644 index 00000000..706e3884 --- /dev/null +++ b/0133-Clone-Graph/Article/0133-Clone-Graph.md @@ -0,0 +1,65 @@ +# LeetCode 第 133 号问题:克隆图 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 133 号问题:克隆图。题目难度为 Medium,目前通过率为 54.8% 。 + +### 题目描述 + +给你无向连通图中一个节点的引用,请你返回该图的深拷贝(克隆)。图中的每个节点都包含它的值 val(int)和其邻居的列表(list[Node])。 + + +**示例 1:** + +``` +输入:adjList = [[2,4],[1,3],[2,4],[1,3]] +输出:[[2,4],[1,3],[2,4],[1,3]] +解释: +图中有 4 个节点。 +节点 1 的值是 1,它有两个邻居:节点 2 和 4 。 +节点 2 的值是 2,它有两个邻居:节点 1 和 3 。 +节点 3 的值是 3,它有两个邻居:节点 2 和 4 。 +节点 4 的值是 4,它有两个邻居:节点 1 和 3 。 +``` + +**示例 2:** + +``` +输入:adjList = [[]] +输出:[[]] +解释:输入包含一个空列表。该图仅仅只有一个值为 1 的节点,它没有任何邻居。 +``` + +**示例 3:** + +``` +输入:adjList = [[2],[1]] +输出:[[2],[1]] +``` + +### 题目解析 + +给你一个图,让你完整地拷贝一份。这道题目不难,但是在实际的工作项目中却时常遇到。这道题目有很多种解法,但是建议站在实际工作的角度去思考。 + +图是由一个个节点组成的,完整地拷贝一份图,那么就意味着我们要对每个节点进行拷贝,而且节点与节点的关系也要拷贝过来。做到这一点也不难,我们只需要遍历一遍图就可以了,这里的关键点在于题目强调是 **无向图**,也就是说我们可以从图上的任意点出发到达图上的所有节点。那么问题从而就转换到了如何遍历图,我们可以使用广度优先搜索,也可以使用深度优先搜索,从工作的角度出发,比较推荐广度优先搜索,因为理解容易,实现简单,而且不涉及栈溢出的问题,处理大规模数据比较安全。 + +
+ +### 动画演示 + +![](../Animation/133.gif) + +
+ +### 复杂度分析 + +一般使用广度优先搜索遍历图,时间复杂度是 `O(n + m)`,其中这里的 n 表示的是图上的节点数,m 表示的图上的边的数量。从广度优先搜索的 **由点及面** 的性质,你不难理解这个结果。极端情况下,当这张图是一张全联通的图,时间复杂度就会是 `O(n^2)`,解释起来也很容易,因为你每访问完一个节点,下面都会去访问相邻的节点,一个节点和所有的节点相连,那么在一个节点上花费的时间就是 n,在 n 节点上花费的时间就是 n^2。因为我们使用了队列存放接下来需要遍历的节点,空间复杂度就是 `O(n)`。 + + + + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0136-Single-Number/Animation/136.gif b/0136-Single-Number/Animation/136.gif new file mode 100644 index 00000000..bb325db8 Binary files /dev/null and b/0136-Single-Number/Animation/136.gif differ diff --git a/0136-Single-Number/Article/0136-Single-Number.md b/0136-Single-Number/Article/0136-Single-Number.md new file mode 100644 index 00000000..8bae68bb --- /dev/null +++ b/0136-Single-Number/Article/0136-Single-Number.md @@ -0,0 +1,142 @@ +# LeetCode 第 136 号问题:只出现一次的数字 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 136 号问题:只出现一次的数字。题目难度为 Easy,目前通过率为 66.8% 。 + +### 题目描述 + +给定一个**非空**整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 + +**说明:** + +你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? + +**示例 1:** + +``` +输入: [2,2,1] +输出: 1 +``` + +**示例 2:** + +``` +输入: [4,1,2,1,2] +输出: 4 +``` + +### 题目解析 + +根据题目描述,由于加上了时间复杂度必须是 O(n) ,并且空间复杂度为 O(1) 的条件,因此不能用排序方法,也不能使用 map 数据结构。 + +程序员小吴想了一下午没想出来,答案是使用 **位操作Bit Operation** 来解此题。 + +将所有元素做异或运算,即a[1] ⊕ a[2] ⊕ a[3] ⊕ …⊕ a[n],所得的结果就是那个只出现一次的数字,时间复杂度为O(n)。 + +### 异或 + +异或运算A ⊕ B的真值表如下: + +| A | B | ⊕ | +| :--- | :--: | ---: | +| F | F | F | +| F | T | T | +| T | F | T | +| T | T | F | + +### 动画演示 + +![](../Animation/136.gif) + +### 代码实现 +#### C +````c +int singleNumber(int* nums, int numsSize){ + int res=0; + for(int i=0;i& nums) { + int res=0; + for(auto n:nums) + { + // 异或 + res ^= n; + } + return res; + } +}; +```` + +#### Java +````java +class Solution { + public int singleNumber(int[] nums) { + int res = 0; + for(int n:nums) + { + // 异或 + res ^= n; + } + return res; + } +} +```` + +#### pyton +````python +class Solution(object): + def singleNumber(self, nums): + return reduce(lambda x,y:x^y, nums) +# reduce用法举例 +# 计算列表和:1+2+3+4+5 +# 使用 lambda 匿名函数 +# reduce(lambda x, y: x+y, [1,2,3,4,5]) +```` + +### 进阶版 + +有一个 n 个元素的数组,除了两个数只出现一次外,其余元素都出现两次,让你找出这两个只出现一次的数分别是几,要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关)。 + +#### 示例 : + +输入: [1,2,2,1,3,4] +输出: [3,4] + +### 题目再解析 + +根据前面找一个不同数的思路算法,在这里把所有元素都异或,那么得到的结果就是那两个只出现一次的元素异或的结果。 + +然后,因为这两个只出现一次的元素一定是不相同的,所以这两个元素的二进制形式肯定至少有某一位是不同的,即一个为 0 ,另一个为 1 ,现在需要找到这一位。 + +根据异或的性质 `任何一个数字异或它自己都等于 0 `,得到这个数字二进制形式中任意一个为 1 的位都是我们要找的那一位。 + +再然后,以这一位是 1 还是 0 为标准,将数组的 n 个元素分成两部分。 + +- 将这一位为 0 的所有元素做异或,得出的数就是只出现一次的数中的一个 +- 将这一位为 1 的所有元素做异或,得出的数就是只出现一次的数中的另一个。 + +这样就解出题目。忽略寻找不同位的过程,总共遍历数组两次,时间复杂度为O(n)。 + +### 动画再演示 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/5uz1n.gif) + + + + + +![](../../Pictures/qrcode.jpg) diff --git a/0137-Single-Number-II/Animation/137.gif b/0137-Single-Number-II/Animation/137.gif new file mode 100644 index 00000000..473a1d4e Binary files /dev/null and b/0137-Single-Number-II/Animation/137.gif differ diff --git a/0137-Single-Number-II/Animation/137.m4v b/0137-Single-Number-II/Animation/137.m4v new file mode 100644 index 00000000..9012a802 Binary files /dev/null and b/0137-Single-Number-II/Animation/137.m4v differ diff --git a/0137-Single-Number-II/Article/0137-Single-Number-II.md b/0137-Single-Number-II/Article/0137-Single-Number-II.md new file mode 100644 index 00000000..245e9abb --- /dev/null +++ b/0137-Single-Number-II/Article/0137-Single-Number-II.md @@ -0,0 +1,90 @@ +# LeetCode 第 137 号问题:只出现一次的数字 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 137 号问题:只出现一次的数字 II。题目难度为 Medium,目前通过率为 66.7% 。 + +### 题目描述 + +给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。 + +说明: + +你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? + +**示例 1:** + +``` +输入: [2,2,3,2] +输出: 3 +``` + +**示例 2:** + +``` +输入: [0,1,0,1,0,1,99] +输出: 99 +``` + +### 题目解析 + +相比 [Single Number](https://leetcode.com/problems/single-number/),输入数组的条件变了,数组中除了其中的一个元素只出现了一次,其余的元素都出现了 **三** 次,最后的问题还是让你找出这个只出现一次的元素。这道题目,一开始看起来从位运算思考貌似是不可能的,但如果你从集合的角度去思考或许可以想到解法。如果我们遍历数组里面的元素,在遍历的过程中,我们会发现 **对于每个元素来说只有三种情况,出现一次,出现两次,出现三次**。因为我们要找的是出现一次的那个元素,而且最终除了我们要找的元素,其他所有的元素都会出现三次,因此我们需要想办法排除掉出现三次的元素。一开始的时候可以想,我们用两个集合,集合 1 用于存放出现一次的元素,集合 2 用于存放出现两次的元素,于是我们可以发现下面的逻辑对应关系: + +``` +如果遍历到的元素不在集合 1 中,也不在集合 2 中: 该元素第一次出现,加入集合 1 +如果遍历到的元素在集合 1 中,不在集合 2 中: 该元素第二次出现,移出集合 1,加入集合 2 +如果遍历到的元素不在集合 1 中,在集合 2 中: 该元素第三次出现,移出集合 2 +``` + +上面的逻辑对应关系你应该很容易理解,但是我想说的是通过位操作可以做到这一点,我们不需要真正的集合,我们只需要用一个整数来代替集合即可。怎么解释呢?假设我们用整数 `ones` 表示集合 1,整数 `twos` 表示集合 2,这两个整数的值初始化均为 0。`ones ^ ele[i]` 表示把元素 `ele[i]` 加入到集合 1 中,如果说下一个元素 `ele[i + 1]` 来了,并且 `ele[i] != ele[i + 1]`,那么 `ones ^ ele[i] ^ ele[i + 1]` 肯定会产生一个不为零的值,至于这个值是多少,你不用关心。但如果 `ele[i] == ele[i + 1]`,那么 `ones ^ ele[i] ^ ele[i + 1]` 的结果肯定为 0,到这里,你应该知道通过异或运算,我们已经可以做到,将出现一次的元素加入集合 1,将出现两次的元素移出集合 1。但是这还不够,因为元素还有可能出现三次,如果仅仅是上面的异或表达式,第三次出现的元素还是会被加入到集合 1,我们还需要保证该元素不在集合 2 中,`(ones ^ ele[i]) & (~twos)` 就可以保证这一点。对集合 2 来说也是一样的,`(twos ^ ele[i]) & (~ones)` 保证将不存在于集合 1 中,且不存在集合 2 中的元素加入到集合 2。如果我们先更新集合 1,再更新集合 2,就可以实现我们之前说的逻辑对应关系。说到这里,如果你还是不理解,那么你 **可以尝试把一个元素看作是一堆值为 1 的 bit 位的组合**,比如 12 的二进制是 `0001 0100`,如果说 12 出现了三次,那么从右往左数第三位和第五位 bit 的就出现了三次。我们把这个结论放在数组中也是一样的,对于那些出现了 3 的整数倍次的 bits 位我们要进行消除,找到那些出现了 `3 * n + 1` 次的 bit 位,将它们组合在一起就是我们要找的元素,上面的位运算做的就是这个事情,与其说把元素放入集合中,我们也可以说 **将元素的所有值为 1 的 bit 位放入集合中**,这样会更好理解些。 + +
+ +### 动画演示 + +![](../Animation/137.gif) + +### 代码实现 +#### C++ +```c++ +class Solution { +public: + int singleNumber(vector& nums) { + int one=0, two=0; + for(int n:nums) + { + one = (one ^ n) & (~two); + two = (two ^ n) & (~one); + } + return one; + } +}; +``` +#### Java +```java +class Solution { + public int singleNumber(int[] nums) { + int one=0, two=0; + for(int n:nums) + { + one = (one ^ n) & (~two); + two = (two ^ n) & (~one); + } + return one; + } +} +``` +#### Python +```python +class Solution(object): + def singleNumber(self, nums): + one = two = 0 + for n in nums: + one = (one ^ n) & (~two) + two = (two ^ n) & (~one) + return one +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0138-Copy-List-with-Random-Pointer/Animation/Animation.gif b/0138-Copy-List-with-Random-Pointer/Animation/Animation.gif new file mode 100644 index 00000000..89209502 Binary files /dev/null and b/0138-Copy-List-with-Random-Pointer/Animation/Animation.gif differ diff --git a/0138-Copy-List-with-Random-Pointer/Article/0138-Copy-List-with-Random-Pointer.md b/0138-Copy-List-with-Random-Pointer/Article/0138-Copy-List-with-Random-Pointer.md new file mode 100644 index 00000000..0186bfbe --- /dev/null +++ b/0138-Copy-List-with-Random-Pointer/Article/0138-Copy-List-with-Random-Pointer.md @@ -0,0 +1,77 @@ +# LeetCode 第 138 号问题:复制带随机指针的链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 138 号问题:复制带随机指针的链表。题目难度为 Medium,目前通过率为 40.5% 。 + +### 题目描述 + +给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。 + +要求返回这个链表的**深拷贝**。 + +**示例:** + +``` +输入: +{"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1} + +解释: +节点 1 的值是 1,它的下一个指针和随机指针都指向节点 2 。 +节点 2 的值是 2,它的下一个指针指向 null,随机指针指向它自己。 +``` + +### 题目解析 + +1. 在原链表的每个节点后面拷贝出一个新的节点 + +2. 依次给新的节点的随机指针赋值,而且这个赋值非常容易 cur->next->random = cur->random->next + +3. 断开链表可得到深度拷贝后的新链表 + +之所以说这个方法比较巧妙是因为相较于一般的解法(如使用 Hash map )来处理,上面这个解法 **不需要占用额外的空间**。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +我发现带指针的题目使用 C++ 版本更容易描述,所以下面的代码实现是 C++ 版本。 + +```c++ +class Solution { +public: + RandomListNode *copyRandomList(RandomListNode *head) { + if (!head) return NULL; + RandomListNode *cur = head; + while (cur) { + RandomListNode *node = new RandomListNode(cur->label); + node->next = cur->next; + cur->next = node; + cur = node->next; + } + cur = head; + while (cur) { + if (cur->random) { + cur->next->random = cur->random->next; + } + cur = cur->next->next; + } + cur = head; + RandomListNode *res = head->next; + while (cur) { + RandomListNode *tmp = cur->next; + cur->next = tmp->next; + if(tmp->next) tmp->next = tmp->next->next; + cur = cur->next; + } + return res; + } +}; +``` + +![](../../Pictures/qrcode.jpg) + diff --git a/0139-Word-Break/Article/0139-Word-Break.md b/0139-Word-Break/Article/0139-Word-Break.md new file mode 100644 index 00000000..aadbf4c7 --- /dev/null +++ b/0139-Word-Break/Article/0139-Word-Break.md @@ -0,0 +1,68 @@ +# LeetCode 第 139 号问题:单词拆分 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 139 号问题:单词拆分。 + +### 题目描述 + +给定一个**非空**字符串 *s* 和一个包含**非空**单词列表的字典 *wordDict*,判定 *s* 是否可以被空格拆分为一个或多个在字典中出现的单词。 + +**说明:** + +- 拆分时可以重复使用字典中的单词。 +- 你可以假设字典中没有重复的单词。 + + + +### 题目解析 + +与 **分割回文串** 有些类似,都是拆分,但是如果此题采取 深度优先搜索 的方法来解决的话,答案是超时的,不信的同学可以试一下~ + +为什么会超时呢? + +因为使用 深度优先搜索 会重复的计算了有些位的可拆分情况,这种情况的优化肯定是需要 动态规划 来处理的。 + +如果不知道动态规划的,可以看一下小吴之前的万字长文,比较详细的介绍了动态规划的概念。 + +在这里,只需要去定义一个数组 boolean[] memo,其中第 i 位 memo[i] 表示待拆分字符串从第 0 位到第 i-1 位是否可以被成功地拆分。 + +然后分别计算每一位是否可以被成功地拆分。 + + + +### 动画描述 + +暂无~ + +### 代码实现 + + + +```java +class Solution { + public boolean wordBreak(String s, List wordDict) { + int n = s.length(); + int max_length=0; + for(String temp:wordDict){ + max_length = temp.length() > max_length ? temp.length() : max_length; + } + // memo[i] 表示 s 中以 i - 1 结尾的字符串是否可被 wordDict 拆分 + boolean[] memo = new boolean[n + 1]; + memo[0] = true; + for (int i = 1; i <= n; i++) { + for (int j = i-1; j >= 0 && max_length >= i - j; j--) { + if (memo[j] && wordDict.contains(s.substring(j, i))) { + memo[i] = true; + break; + } + } + } + return memo[n]; + } +} +``` + +![](../../Pictures/qrcode.jpg) diff --git a/01394-Find-out-the-lucky-number-in-the-array/Animation/01394.mp4 b/01394-Find-out-the-lucky-number-in-the-array/Animation/01394.mp4 new file mode 100644 index 00000000..9e6e429b Binary files /dev/null and b/01394-Find-out-the-lucky-number-in-the-array/Animation/01394.mp4 differ diff --git a/01394-Find-out-the-lucky-number-in-the-array/Article/01394 b/01394-Find-out-the-lucky-number-in-the-array/Article/01394 new file mode 100644 index 00000000..1f14b9e7 --- /dev/null +++ b/01394-Find-out-the-lucky-number-in-the-array/Article/01394 @@ -0,0 +1,106 @@ +# 1394. 找出数组中的幸运数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 1394题: 找出数组中的幸运数。,主要涉及哈希表。 + +## 题目 + +在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。 + +给你一个整数数组 arr,请你从中找出并返回一个幸运数。 + +如果数组中存在多个幸运数,只需返回 最大 的那个。 +如果数组中不含幸运数,则返回 -1 。 +  + +示例 1: + +``` +输入:arr = [2,2,3,4] +输出:2 +解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。 +``` + +示例 2: + +``` +输入:arr = [1,2,2,3,3,3] +输出:3 +解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。 +``` + +示例 3: + +``` +输入:arr = [2,2,2,3,3] +输出:-1 +解释:数组中不存在幸运数。 +``` + +示例 4: + +``` +输入:arr = [5] +输出:-1 +``` + +示例 5: + +``` +输入:arr = [7,7,7,7,7,7,7] +输出:7 +``` + +提示: + +1 <= arr.length <= 500 +1 <= arr[i] <= 500 + +## 题目解析 + +1. 遍历arr,用哈希表记录每个数组元素出现的次数 +2. 遍历哈希表,每次找到一个幸运数就和当前的幸运数对比,最后找到最大的幸运数,如果没有找到的话输出-1 + +## 动画理解 + + + + +## 参考代码 + + +```javaScript +/** + * @param {number[]} arr + * @return {number} + */ +var findLucky = function(arr) { + let map = new Map() + let maxLucky = -1 + arr.map(i => { + map.set(i, map.get(i)+1 || 1) + }) + map.forEach((key, value)=>{ + if(key == value){ + maxLucky = Math.max(maxLucky, key) + } + }) + return maxLucky +}; +``` + +## 复杂度分析 + +假设元素的个数是n个,那么哈希表中最多有 n 个键值对。 + +时间复杂度:这个算法里有两次遍历,遍历数组的时间复杂度是O(n),遍历哈希表的时间复杂度是O(n),所以最后的时间复杂度就是O(n)。 + +空间复杂度:额外只用了哈希表,哈希表中最多有 n 个键值对,所以空间复杂度是 O(n)。 + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0141-Linked-List-Cycle/Animation/Animation.gif b/0141-Linked-List-Cycle/Animation/Animation.gif new file mode 100644 index 00000000..e0fa992a Binary files /dev/null and b/0141-Linked-List-Cycle/Animation/Animation.gif differ diff --git a/0141-Linked-List-Cycle/Article/0141-Linked-List-Cycle.md b/0141-Linked-List-Cycle/Article/0141-Linked-List-Cycle.md new file mode 100644 index 00000000..4c3d50a3 --- /dev/null +++ b/0141-Linked-List-Cycle/Article/0141-Linked-List-Cycle.md @@ -0,0 +1,83 @@ +# 使用快慢指针求解「环形链表」so easy! + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享的题目来源于 LeetCode 上第 141 号问题:环形链表。题目难度为 Easy,目前通过率为 40.4% 。 + +使用快慢指针的方式去求解 **so easy** ! + +### 题目描述 + +给定一个链表,判断链表中是否有环。 + +为了表示给定链表中的环,我们使用整数 `pos` 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 `pos` 是 `-1`,则在该链表中没有环。 + +**示例 1:** + +``` +输入:head = [3,2,0,-4], pos = 1 +输出:true +解释:链表中有一个环,其尾部连接到第二个节点。 +``` + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/vweoq.png) + +**示例 2:** + +``` +输入:head = [1,2], pos = 0 +输出:true +解释:链表中有一个环,其尾部连接到第一个节点。 +``` + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/kxbrz.png) + +**示例 3:** + +``` +输入:head = [1], pos = -1 +输出:false +解释:链表中没有环。 +``` + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/w3vsg.png) + +**进阶:** + +你能用 O(1)(即,常量)内存解决此问题吗? + +### 题目解析 + +这道题是快慢指针的**经典应用**。 + +设置两个指针,一个每次走一步的**慢指针**和一个每次走两步的**快指针**。 + +* 如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环 +* 如果含有环,快指针会超慢指针一圈,和慢指针相遇,说明链表含有环。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +```java +//author:程序员小吴 +public class Solution { + public boolean hasCycle(ListNode head) { + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) return true; + } + return false; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0142-Linked-List-Cycle-ii/Animation/0142-Linked-List-Cycle-ii.m4v b/0142-Linked-List-Cycle-ii/Animation/0142-Linked-List-Cycle-ii.m4v new file mode 100644 index 00000000..28695565 Binary files /dev/null and b/0142-Linked-List-Cycle-ii/Animation/0142-Linked-List-Cycle-ii.m4v differ diff --git a/0142-Linked-List-Cycle-ii/Animation/Animation.gif b/0142-Linked-List-Cycle-ii/Animation/Animation.gif new file mode 100644 index 00000000..6c4a23ce Binary files /dev/null and b/0142-Linked-List-Cycle-ii/Animation/Animation.gif differ diff --git a/0142-Linked-List-Cycle-ii/Article/0142-Linked-List-Cycle-ii.md b/0142-Linked-List-Cycle-ii/Article/0142-Linked-List-Cycle-ii.md new file mode 100644 index 00000000..3fff27e5 --- /dev/null +++ b/0142-Linked-List-Cycle-ii/Article/0142-Linked-List-Cycle-ii.md @@ -0,0 +1,176 @@ +# LeetCode 第 142 号问题:环形链表 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享的题目来源于 LeetCode 上第 142 号问题:环形链表II。题目难度为 Medium 。 + +### 题目描述 + +给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 `null`。 + +为了表示给定链表中的环,我们使用整数 `pos` 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 `pos` 是 `-1`,则在该链表中没有环。 + +**示例 1:** + +``` +输入:head = [3,2,0,-4], pos = 1 +输出:tail connects to node index 1 +解释:链表中有一个环,其尾部连接到第二个节点。 +``` + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/vweoq.png) + +**示例 2:** + +``` +输入:head = [1,2], pos = 0 +输出:tail connects to node index 0 +解释:链表中有一个环,其尾部连接到第一个节点。 +``` + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/kxbrz.png) + +**示例 3:** + +``` +输入:head = [1], pos = -1 +输出:no cycle +解释:链表中没有环。 +``` + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/w3vsg.png) + +**进阶:** + +你是否可以不用额外空间解决此题? + +### 题目解析 - 哈希表 + +普通解法就是利用哈希表保存访问过的节点, 同时遍历过程中检查哈希表中是否已存在相同的节点 + +### 代码实现 + +```javascript +/** + * JavaScript 描述 + * 哈希表方法 + */ +var detectCycle = function(head) { + let res = [ ]; + while (head !== null) { + if (res.includes(head)) { + return head; + } + res.push(head); + head = head.next; + } + return null; +}; +``` + +### 复杂度分析 + +- 时间复杂度:**O(n)** +- 空间复杂度:**O(n)** + +### 题目解析 - Floyd 算法 + +Floyd算法 可以达到常量空间解决此问题. + +我在维基百科找到了这个算法描述, 在此引用一下. + +**Floyd判圈算法**(**Floyd Cycle Detection Algorithm**),又称 **龟兔赛跑算法**(**Tortoise and Hare Algorithm**),是一个可以在[有限状态机](https://zh.wikipedia.org/wiki/有限状态机)、[迭代函数](https://zh.wikipedia.org/wiki/迭代函数)或者[链表](https://zh.wikipedia.org/wiki/链表)上判断是否存在[环](https://zh.wikipedia.org/wiki/環_(圖論)),求出该环的起点与长度的算法。 + +如果有限状态机、迭代函数或者链表存在环,那么一定存在一个起点可以到达某个环的某处 ( 这个起点也可以在某个环上 )。 + +初始状态下,假设已知某个起点节点为节点 *S*。现设两个指针 `t` 和 `h` ,将它们均指向 *S*。 + +接着,同时让 `t` 和 `h` 往前推进,但是二者的速度不同:`t` 每前进 `1` 步, `h` 前进 `2` 步。只要二者都可以前进而且没有相遇,就如此保持二者的推进。当 `h` 无法前进,即到达某个没有后继的节点时,就可以确定从 *S* 出发不会遇到环。反之当 `t` 与 `h` 再次相遇时,就可以确定从 S 出发一定会进入某个环,设其为环 *C*。 + +如果确定了存在某个环,就可以求此环的起点与长度。 + +上述算法刚判断出存在环 *C* 时,显然 t 和 `h` 位于同一节点,设其为节点 *M*。显然,仅需令 `h` 不动,而t不断推进,最终又会返回节点 *M*,统计这一次t推进的步数,显然这就是环 *C* 的长度。 + +为了求出环 *C* 的起点,只要令h仍均位于节点 *M* ,而令t返回起点节点 *S* ,此时h与t之间距为环 *C* 长度的整数倍。随后,同时让 `t` 和 `h` 往前推进,且保持二者的速度相同:`t` 每前进 `1` 步,`h` 前进 `1` 步。持续该过程直至 `t` 与 `h` 再一次相遇,设此次相遇时位于同一节点 *P*,则节点 *P* 即为从节点 *S* 出发所到达的环 *C* 的第一个节点,即环 *C* 的一个起点。 + +**看完之后是不是很多疑点, 觉得为什么会这样呢?** + +下面用数学简单证明一下 + +假设 链表的节点数为 `num`, 从 head 到链表环入口节点数为 `m` (不包含入口节点), 环的节点数为 `n`, 链表环入口设点为 *P* + +由此可得 `num = m + n` + +假设 慢指针 `Tortoise` (乌龟) 每次走 `1` 个节点, 走了 `x` 步 + +假设 快指针 `Hare` (兔子) 每次走 `2` 个节点, 走了 `f` 步 + +那么 `f = 2x` + +当第一次相遇时, 必然是在环内, 设其点为 *M*, 兔子第一次到达 *M* 点后至少又在环内饶了一圈后追上乌龟, + +假设绕了 `k` 圈, 那么可以得到 + +`f = x + kn` + +兔子到达 *P* 点的步数为 + +`f = m + kn` + +由 `f = 2x` 和 `f = x + kn` 两个等式可以得到 `x = kn` + +由 `f = m + kn` 和 `x = kn` 可知, 乌龟到达 *P* 点还需要走 `m` 步 + +而 `m` 的长度正是从 head 到链表环入口节点数的长度, 这是未知的, + +那么让兔子从 head 以乌龟的速度走, 乌龟在 *M* 点走, 当兔子和乌龟相遇时即走了 `m` 步, 也就到达了 *P* 节点. + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +```java +/** + * JavaScript 描述 + * Floyd判圈算法 + */ +var detectCycle = function(head) { + if (head == null) { + return head; + } + // 设置快慢指针 + let tortoise = head, + hare = head; + // 检查链表是否有环 + while (true) { + if (hare == null || hare.next == null) { + return null; + } + hare = hare.next.next; + tortoise = tortoise.next; + if (hare == tortoise) { + break; + } + } + // 兔子和乌龟第二次相遇找到环入口 + hare = head; + while (hare != tortoise) { + hare = hare.next; + tortoise = tortoise.next; + } + return hare; +}; +``` + +### 复杂度分析 + +- 时间复杂度:**O(n)** + - 有环情况下, 第一次和第二次相遇, 乌龟步数都小于链表节点数, 因此与链表节点数成线性关系; + - 无环情况下, 兔子大约需要 n/2 步数到达最后, 因此也与链表节点数成线性关系. +- 空间复杂度:**O(1)** , 双指针使用常数大小的额外空间 + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0144-Binary-Tree-Preorder-Traversal/Animation/Animation.gif b/0144-Binary-Tree-Preorder-Traversal/Animation/Animation.gif new file mode 100644 index 00000000..4bec9b23 Binary files /dev/null and b/0144-Binary-Tree-Preorder-Traversal/Animation/Animation.gif differ diff --git a/0144-Binary-Tree-Preorder-Traversal/Article/0144-Binary-Tree-Preorder-Traversal.md b/0144-Binary-Tree-Preorder-Traversal/Article/0144-Binary-Tree-Preorder-Traversal.md new file mode 100644 index 00000000..8cf5da3d --- /dev/null +++ b/0144-Binary-Tree-Preorder-Traversal/Article/0144-Binary-Tree-Preorder-Traversal.md @@ -0,0 +1,75 @@ +# LeetCode 第 144 号问题:二叉树的前序遍历 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 144 号问题:二叉树的前序遍历。题目难度为 Medium,目前通过率为 59.8% 。 + +### 题目描述 + +给定一个二叉树,返回它的 *前序* 遍历。 + + **示例:** + +``` +输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + +输出: [1,2,3] +``` + +**进阶:** 递归算法很简单,你可以通过迭代算法完成吗? + +### 题目解析 + +用**栈(Stack)**的思路来处理问题。 + +前序遍历的顺序为**根-左-右**,具体算法为: + +- 把根节点 push 到栈中 +- 循环检测栈是否为空,若不空,则取出栈顶元素,保存其值 +- 看其右子节点是否存在,若存在则 push 到栈中 +- 看其左子节点,若存在,则 push 到栈中。 + + + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { + public List preorderTraversal(TreeNode root) { + //非递归前序遍历,需要借助栈 + Stack stack = new Stack<>(); + List list = new LinkedList<>(); + //当树为空树时,直接返回一个空list + if(root == null){ + return list; + } + //第一步是将根节点压入栈中 + stack.push(root); + //当栈不为空时,出栈的元素插入list尾部。 + //当它的孩子不为空时,将孩子压入栈,一定是先压右孩子再压左孩子 + while(!stack.isEmpty()){ + //此处的root只是一个变量的复用 + root = stack.pop(); + list.add(root.val); + if(root.right != null) stack.push(root.right); + if(root.left != null) stack.push(root.left); + } + return list; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) diff --git a/0145-Binary-Tree-Postorder-Traversal/Animation/Animation.gif b/0145-Binary-Tree-Postorder-Traversal/Animation/Animation.gif new file mode 100644 index 00000000..d79a13bf Binary files /dev/null and b/0145-Binary-Tree-Postorder-Traversal/Animation/Animation.gif differ diff --git a/0145-Binary-Tree-Postorder-Traversal/Article/0145-Binary-Tree-Postorder-Traversal.md b/0145-Binary-Tree-Postorder-Traversal/Article/0145-Binary-Tree-Postorder-Traversal.md new file mode 100644 index 00000000..93dda603 --- /dev/null +++ b/0145-Binary-Tree-Postorder-Traversal/Article/0145-Binary-Tree-Postorder-Traversal.md @@ -0,0 +1,70 @@ +# LeetCode 第 145 号问题:二叉树的后序遍历 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 145 号问题:二叉树的后序遍历。题目难度为 Hard,目前通过率为 25.8% 。 + +### 题目描述 + +给定一个二叉树,返回它的 *后序* 遍历。 + +**示例:** + +``` +输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + +输出: [3,2,1] +``` + +**进阶:** 递归算法很简单,你可以通过迭代算法完成吗? + +### 题目解析 + +用**栈(Stack)**的思路来处理问题。 + +后序遍历的顺序为**左-右-根**,具体算法为: + +- 先将根结点压入栈,然后定义一个辅助结点 head +- while 循环的条件是栈不为空 +- 在循环中,首先将栈顶结点t取出来 +- 如果栈顶结点没有左右子结点,或者其左子结点是 head,或者其右子结点是 head 的情况下。我们将栈顶结点值加入结果 res 中,并将栈顶元素移出栈,然后将 head 指向栈顶元素 +- 否则的话就看如果右子结点不为空,将其加入栈 +- 再看左子结点不为空的话,就加入栈 + + + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +public class Solution { + public List postorderTraversal(TreeNode root) { + List res = new ArrayList(); + if(root == null) + return res; + Stack stack = new Stack(); + stack.push(root); + while(!stack.isEmpty()){ + TreeNode node = stack.pop(); + if(node.left != null) stack.push(node.left);//和传统先序遍历不一样,先将左结点入栈 + if(node.right != null) stack.push(node.right);//后将右结点入栈 + res.add(0,node.val); //逆序添加结点值 + } + return res; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0146-LRU-Cache/Animation/Animation.gif b/0146-LRU-Cache/Animation/Animation.gif new file mode 100644 index 00000000..f2476548 Binary files /dev/null and b/0146-LRU-Cache/Animation/Animation.gif differ diff --git a/0146-LRU-Cache/Article/0146-LRU-Cache.md b/0146-LRU-Cache/Article/0146-LRU-Cache.md new file mode 100644 index 00000000..40e4ad43 --- /dev/null +++ b/0146-LRU-Cache/Article/0146-LRU-Cache.md @@ -0,0 +1,97 @@ +# LeetCode 第 146 号问题:LRU缓存机制 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 146 号问题:LRU缓存机制。题目难度为 Hard,目前通过率为 15.8% 。 + +### 题目描述 + +运用你所掌握的数据结构,设计和实现一个 [LRU (最近最少使用) 缓存机制](https://baike.baidu.com/item/LRU)。它应该支持以下操作: 获取数据 `get` 和 写入数据 `put` 。 + +获取数据 `get(key)` - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 +写入数据 `put(key, value)` - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 + +**进阶:** + +你是否可以在 **O(1)** 时间复杂度内完成这两种操作? + +**示例:** + +``` +LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // 返回 1 +cache.put(3, 3); // 该操作会使得密钥 2 作废 +cache.get(2); // 返回 -1 (未找到) +cache.put(4, 4); // 该操作会使得密钥 1 作废 +cache.get(1); // 返回 -1 (未找到) +cache.get(3); // 返回 3 +cache.get(4); // 返回 4 +``` + +### 题目解析 + +这道题是让我们实现一个 LRU 缓存器,LRU是Least Recently Used的简写,就是最近最少使用的意思。 + +这个缓存器主要有两个成员函数,get和put。 + +其中 get 函数是通过输入 key 来获得 value,如果成功获得后,这对 (key, value) 升至缓存器中最常用的位置(顶部),如果 key 不存在,则返回 -1 。 + +而 put 函数是插入一对新的 (key, value),如果原缓存器中有该 key,则需要先删除掉原有的,将新的插入到缓存器的顶部。如果不存在,则直接插入到顶部。 + +若加入新的值后缓存器超过了容量,则需要删掉一个最不常用的值,也就是底部的值。 + +具体实现时我们需要三个私有变量,cap , l 和 m,其中 cap 是缓存器的容量大小,l 是保存缓存器内容的列表,m 是 HashMap,保存关键值 key 和缓存器各项的迭代器之间映射,方便我们以 O(1) 的时间内找到目标项。 + +然后我们再来看 get 和 put 如何实现。 + +其中,get 相对简单些,我们在 m 中查找给定的key,若不存在直接返回 -1;如果存在则将此项移到顶部。 + +对于 put ,我们也是现在 m 中查找给定的 key,如果存在就删掉原有项,并在顶部插入新来项,然后判断是否溢出,若溢出则删掉底部项(最不常用项)。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +```c++ +class LRUCache{ +public: + LRUCache(int capacity) { + cap = capacity; + } + + int get(int key) { + auto it = m.find(key); + if (it == m.end()) return -1; + l.splice(l.begin(), l, it->second); + return it->second->second; + } + + void put(int key, int value) { + auto it = m.find(key); + if (it != m.end()) l.erase(it->second); + l.push_front(make_pair(key, value)); + m[key] = l.begin(); + if (m.size() > cap) { + int k = l.rbegin()->first; + l.pop_back(); + m.erase(k); + } + } + +private: + int cap; + list> l; + unordered_map>::iterator> m; +}; +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0150-Evaluate-Reverse-Polish-Notation/Animation/Animation.gif b/0150-Evaluate-Reverse-Polish-Notation/Animation/Animation.gif new file mode 100644 index 00000000..4b61fef5 Binary files /dev/null and b/0150-Evaluate-Reverse-Polish-Notation/Animation/Animation.gif differ diff --git a/0150-Evaluate-Reverse-Polish-Notation/Article/0150-Evaluate-Reverse-Polish-Notation.md b/0150-Evaluate-Reverse-Polish-Notation/Article/0150-Evaluate-Reverse-Polish-Notation.md new file mode 100644 index 00000000..70306382 --- /dev/null +++ b/0150-Evaluate-Reverse-Polish-Notation/Article/0150-Evaluate-Reverse-Polish-Notation.md @@ -0,0 +1,101 @@ +# LeetCode 第 150 号问题:逆波兰表达式求值 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 150 号问题:逆波兰表达式求值。题目难度为 Medium,目前通过率为 43.7% 。 + +### 题目描述 + +根据[逆波兰表示法](https://baike.baidu.com/item/%E9%80%86%E6%B3%A2%E5%85%B0%E5%BC%8F/128437),求表达式的值。 + +有效的运算符包括 `+`, `-`, `*`, `/` 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 + +**说明:** + +- 整数除法只保留整数部分。 +- 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 + +**示例 1:** + +``` +输入: ["2", "1", "+", "3", "*"] +输出: 9 +解释: ((2 + 1) * 3) = 9 +``` + +**示例 2:** + +``` +输入: ["4", "13", "5", "/", "+"] +输出: 6 +解释: (4 + (13 / 5)) = 6 +``` + +**示例 3:** + +``` +输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"] +输出: 22 +解释: + ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 += ((10 * (6 / (12 * -11))) + 17) + 5 += ((10 * (6 / -132)) + 17) + 5 += ((10 * 0) + 17) + 5 += (0 + 17) + 5 += 17 + 5 += 22 +``` + +### 题目解析 + +用数据结构`栈`来解决这个问题。 + +- 从前往后遍历数组 +- 遇到数字则压入栈中 +- 遇到符号,则把栈顶的两个数字拿出来运算,把结果再压入栈中 +- 遍历完整个数组,栈顶数字即为最终答案 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { +public: + int evalRPN(vector& tokens) { + + stack nums; + stack ops; + for(const string& s: tokens){ + if(s == "+" || s == "-" || s == "*" || s == "/"){ + int a = nums.top(); + nums.pop(); + int b = nums.top(); + nums.pop(); + + if(s == "+"){ + nums.push(b + a); + }else if(s == "-"){ + nums.push(b - a); + } else if(s == "*"){ + nums.push(b * a); + }else if(s == "/"){ + nums.push(b / a); + } + } + else{ + nums.push(atoi(s.c_str())); + } + } + return nums.top(); + } +}; +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0155-min-stack/Animation/1.mp4 b/0155-min-stack/Animation/1.mp4 new file mode 100644 index 00000000..a932bb5a Binary files /dev/null and b/0155-min-stack/Animation/1.mp4 differ diff --git a/0155-min-stack/Animation/Animation.gif b/0155-min-stack/Animation/Animation.gif new file mode 100644 index 00000000..91358be2 Binary files /dev/null and b/0155-min-stack/Animation/Animation.gif differ diff --git a/0155-min-stack/Article/0155-min-stack b/0155-min-stack/Article/0155-min-stack new file mode 100644 index 00000000..4bd9ac31 --- /dev/null +++ b/0155-min-stack/Article/0155-min-stack @@ -0,0 +1,77 @@ +#### 题目描述 + +> 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 +> +> + push(x) —— 将元素 x 推入栈中。 +> + pop() —— 删除栈顶的元素。 +> + top() —— 获取栈顶元素。 +> + getMin() —— 检索栈中的最小元素。 + +```java +示例: + MinStack minStack = new MinStack(); + minStack.push(-2); + minStack.push(0); + minStack.push(-5); + minStack.push(1) + minStack.getMin(); --> 返回 -5. + minStack.pop(); + minStack.top(); --> 返回 -5. + minStack.getMin(); --> 返回 -5. +``` + +#### 题目解析 + +为了能在常数时间内检测到栈中的最小元素,我们可以通过"空间换时间"的方式进行实现,为栈本身(数据栈\_data)增加一个辅助栈(最小值栈\_min)。每一次元素入 \_data 栈,则在 \_min 栈中增加对应的最小值;当 \_data 栈中的元素出栈,则 \_min 栈也进行出栈操作 + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 代码实现 + +```java +class MinStack { + + private Stack _data; + private Stack _min; + + /** initialize your data structure here. */ + public MinStack() { + _data = new Stack<>(); + _min = new Stack<>(); + } + + public void push(int x) { + _data.add(x); + if (_min.isEmpty()){ + _min.push(x); + } + else{ + if (x > _min.peek()){ + x = _min.peek(); + } + _min.push(x); + } + } + + public void pop() { + _data.pop(); + _min.pop(); + } + + public int top() { + return _data.peek(); + } + + public int getMin() { + return _min.peek(); + } +} +``` + +#### 复杂度分析 + ++ 时间复杂度:O(1)。 ++ 空间复杂度:O(n)。 + diff --git a/0155-min-stack/Code/1.java b/0155-min-stack/Code/1.java new file mode 100644 index 00000000..0917dba8 --- /dev/null +++ b/0155-min-stack/Code/1.java @@ -0,0 +1,37 @@ +class MinStack { + + private Stack _data; + private Stack _min; + + /** initialize your data structure here. */ + public MinStack() { + _data = new Stack<>(); + _min = new Stack<>(); + } + + public void push(int x) { + _data.add(x); + if (_min.isEmpty()){ + _min.push(x); + } + else{ + if (x > _min.peek()){ + x = _min.peek(); + } + _min.push(x); + } + } + + public void pop() { + _data.pop(); + _min.pop(); + } + + public int top() { + return _data.peek(); + } + + public int getMin() { + return _min.peek(); + } +} \ No newline at end of file diff --git a/0160-Intersection-of-Two-Linked-Lists/Animation/1.m4v b/0160-Intersection-of-Two-Linked-Lists/Animation/1.m4v new file mode 100644 index 00000000..8ca10284 Binary files /dev/null and b/0160-Intersection-of-Two-Linked-Lists/Animation/1.m4v differ diff --git a/0160-Intersection-of-Two-Linked-Lists/Animation/Animation.gif b/0160-Intersection-of-Two-Linked-Lists/Animation/Animation.gif new file mode 100644 index 00000000..d2725412 Binary files /dev/null and b/0160-Intersection-of-Two-Linked-Lists/Animation/Animation.gif differ diff --git a/0160-Intersection-of-Two-Linked-Lists/Article/0160-Intersection-of-Two-Linked-Lists.md b/0160-Intersection-of-Two-Linked-Lists/Article/0160-Intersection-of-Two-Linked-Lists.md new file mode 100644 index 00000000..f0520756 --- /dev/null +++ b/0160-Intersection-of-Two-Linked-Lists/Article/0160-Intersection-of-Two-Linked-Lists.md @@ -0,0 +1,68 @@ +题目来源于LeetCode上第160号问题:相交链表。题目难度为Easy,目前通过率54.4%。 +##题目描述 +编写一个程序,找到两个单链表相交的起始节点。 +如下面的两个链表: +![LeetCode图解|160.相交链表](https://upload-images.jianshu.io/upload_images/1840444-b62ea7eae24bf88e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +在节点 c1 开始相交。 +示例 1: +![LeetCode图解|160.相交链表 示例1](https://upload-images.jianshu.io/upload_images/1840444-59acbe2575d138b2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +``` +输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 +输出:Reference of the node with value = 8 +输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 +``` +注意: +- 如果两个链表没有交点,返回 null。 +- 在返回结果后,两个链表仍须保持原有的结构。 +- 可假定整个链表结构中没有循环。 +- 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。 + +##题目解析 +为满足题目时间复杂度和空间复杂度的要求,我们可以使用双指针法。 +- 创建两个指针pA和pB分别指向链表的头结点headA和headB。 +- 当pA到达链表的尾部时,将它重新定位到链表B的头结点headB,同理,当pB到达链表的尾部时,将它重新定位到链表A的头结点headA。 +- 当pA与pB相等时便是两个链表第一个相交的结点。 +这里其实就是相当于把两个链表拼在一起了。pA指针是按B链表拼在A链表后面组成的新链表遍历,而pB指针是按A链表拼在B链表后面组成的新链表遍历。举个简单的例子: +A链表:{1,2,3,4} +B链表:{6,3,4} +pA按新拼接的链表{1,2,3,4,6,3,4}遍历 +pB按新拼接的链表{6,3,4,1,2,3,4}遍历 + +##动画理解 + +![](../Animation/Animation.gif) + +##代码实现 +``` +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { + ListNode *pA = headA; + ListNode *pB = headB; + while(pA != pB){ + if(pA != NULL){ + pA = pA->next; + }else{ + pA = headB; + } + if(curB != NULL){ + pB = pB->next; + }else{ + pB = headA; + } + } + return pA; + } +}; +``` +##复杂度分析 +- 时间复杂度:O(m+n)。 +- 空间复杂度:O(1) diff --git a/0162-Find-Peak-Element/Animation/1.m4v b/0162-Find-Peak-Element/Animation/1.m4v new file mode 100644 index 00000000..70e21669 Binary files /dev/null and b/0162-Find-Peak-Element/Animation/1.m4v differ diff --git a/0162-Find-Peak-Element/Animation/2.gif b/0162-Find-Peak-Element/Animation/2.gif new file mode 100644 index 00000000..47d6e4d7 Binary files /dev/null and b/0162-Find-Peak-Element/Animation/2.gif differ diff --git a/0162-Find-Peak-Element/Animation/2.m4v b/0162-Find-Peak-Element/Animation/2.m4v new file mode 100644 index 00000000..b158deb0 Binary files /dev/null and b/0162-Find-Peak-Element/Animation/2.m4v differ diff --git a/0162-Find-Peak-Element/Animation/Animation.gif b/0162-Find-Peak-Element/Animation/Animation.gif new file mode 100644 index 00000000..3bafc8c9 Binary files /dev/null and b/0162-Find-Peak-Element/Animation/Animation.gif differ diff --git a/0162-Find-Peak-Element/Article/0162-Find-Peak-Element.md b/0162-Find-Peak-Element/Article/0162-Find-Peak-Element.md new file mode 100644 index 00000000..085788f6 --- /dev/null +++ b/0162-Find-Peak-Element/Article/0162-Find-Peak-Element.md @@ -0,0 +1,98 @@ +题目来源于LeetCode上第162号问题:寻找峰值。题目难度为中等,目前通过率46.3%。 +##题目描述 +峰值元素是指其值大于左右相邻值的元素。 +给定一个输入数组``` nums```,其中 ```nums[i] ≠ nums[i+1]```,找到峰值元素并返回其索引。 +数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。 +你可以假设 ```nums[-1] = nums[n] = -∞```。 + +``` +示例 1: + +输入:nums = [1,2,3,1] +输出: 2 +解释: 3 是峰值元素,你的函数应该返回其索引 2。 +示例 2: + +输入: nums = [1,2,1,3,5,6,4] +输出: 1 或 5 +解释: 你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。 +说明: +你的解法应该是 O(logN) 时间复杂度的。 +``` +##题目解析 +我们从题目中可以了解到以下三个关键信息: +- ```nums[i] ≠ nums[i+1]```,意味着数组中没有值相等的元素,要么```nums[i]>nums[i+1]```,要么```nums[i]nums[i+1]```,那么数组肯定有一个峰值,我们将他的索引返回就行了。 + +为了更好的理解解题思路,我们先从线性搜索方法开始解析,并且将数组分为三类,即升序数组,降序数组,无序数组。然后,由于我们只需要找到任意一个峰值,返回它的索引就行。所以我们还可以用二分查找法(**PS:凡是搜索查找类型的题,首先想到的应该是效率较高的二分查找方法**) +## 解法一:线性扫描 + +**1、假设数组是升序数组** +![nums为升序数组](https://upload-images.jianshu.io/upload_images/1840444-fd9855e123fd87a8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +那么很明显我们的峰值是最后一个元素5,因为```nums[0]>nums[1],nums[1]>nums[2], ......,nums[3]>nums[4]```,```nums[4]```是最后一个元素,所以它的峰值索引是4。 +**2、假设数组是降序数组** +![nums为降序数组](https://upload-images.jianshu.io/upload_images/1840444-df09e0d01139cd5f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +因为```nums[-1]=-∞```,并且```nums[0]>mums[1]```,所以```nums[0]```就是一个峰值,返回峰值索引是0。 +**3、假设数组是无序数组** +![nums为无序数组](https://upload-images.jianshu.io/upload_images/1840444-9be820e4a5c0d71d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +同样我们从```nums[0]```开始往后比较大小,因为```nums[0]mums[4]```,所以可以知道```mums[3]```是一个峰值,返回索引是3。 + +通过以上将数组分类解析,我们可以发现只要从```nums[0]```开始,与后一个元素比较,直到找到 ```nums[i]>nums[i+1]```,为止,我们就找到了一个峰值,这个峰值的索引就是```i```,如果一直都没有找到```nums[i]>nums[i+1]```的情况,那么峰值就是数组的最后一个元素,索引就是```nums.length-1```。 + +##动画理解 + +![](../Animation/Animation.gif) + +##代码实现 +``` +public class Solution { + public int findPeakElement(int[] nums) { + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) + return i; + } + return nums.length - 1; + } +} +``` +##复杂度分析 + +- 时间复杂度:O(n),我们对长度为 n 的数组 nums 只进行一次遍历。 +- 空间复杂度:O(1),仅用了常数空间 + +##解法二:二分查找 + +根据二分查找原理,我们假设左边索引```L=0```,右边索引```R=nums.length - 1```,中间索引```M=(L+R)/2```,现在主要就是判断这个峰值是在```M```的左边还是右边,然后移动```L```或者```R```来进一步缩小搜索范围。 + +我们找到中间元素,然后跟方法一线性扫描一样,与中间元素的右边元素比较。 +- 如果```nums[M]nums[M+1]```那么可以知道中间元素```M```的左边肯定会有一个峰值,所以我们把```R```移到```M```的位置,在```M```的左边查找,并且重新计算```M```的值。 +- 重复以上步骤,直到```R=L```,那么这个就是峰值元素。 + +##动画理解 + +![](../Animation/2.gif) + +##代码实现 + +``` +public class Solution { + public int findPeakElement(int[] nums) { + int l = 0, r = nums.length - 1; + while (l < r) { + int mid = (l + r) / 2; + if (nums[mid] > nums[mid + 1]) + r = mid; + else + l = mid + 1; + } + return l; + } +} +``` + +##复杂度分析 + +- 时间复杂度:O(log2(n)),每一步都将搜索空间减半,其中n为 nums 数组的长度。。 +- 空间复杂度:O(1),仅用了常数空间 diff --git a/0167-Two-Sum-II-Input-array-is-sorted/Animation/Animation.gif b/0167-Two-Sum-II-Input-array-is-sorted/Animation/Animation.gif new file mode 100644 index 00000000..6d65a197 Binary files /dev/null and b/0167-Two-Sum-II-Input-array-is-sorted/Animation/Animation.gif differ diff --git a/0167-Two-Sum-II-Input-array-is-sorted/Article/0167-Two-Sum-II-Input-array-is-sorted.md b/0167-Two-Sum-II-Input-array-is-sorted/Article/0167-Two-Sum-II-Input-array-is-sorted.md new file mode 100644 index 00000000..c1174a98 --- /dev/null +++ b/0167-Two-Sum-II-Input-array-is-sorted/Article/0167-Two-Sum-II-Input-array-is-sorted.md @@ -0,0 +1,120 @@ +# LeetCode 第 167 号问题:两数之和 II - 输入有序数组 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 167 号问题:两数之和 II - 输入有序数组。题目难度为 Easy,目前通过率为 48.2% 。 + +### 题目描述 + +给定一个已按照**升序排列** 的有序数组,找到两个数使得它们相加之和等于目标数。 + +函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2*。* + +**说明:** + +- 返回的下标值(index1 和 index2)不是从零开始的。 +- 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 + +**示例:** + +``` +输入: numbers = [2, 7, 11, 15], target = 9 +输出: [1,2] +解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。 +``` + +### 题目解析 + +初始化左指针 left 指向数组起始,初始化右指针 right 指向数组结尾。 + +根据**已排序**这个特性, + +- (1)如果 numbers[left] 与 numbers[right] 的和 tmp 小于 target ,说明应该增加 tmp ,因此 left 右移指向一个较大的值。 +- (2)如果 tmp大于 target ,说明应该减小 tmp ,因此 right 左移指向一个较小的值。 +- (3)tmp 等于 target ,则找到,返回 left + 1 和 right + 1。(注意以 1 为起始下标) + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 +#### C++ +```c++ +// 对撞指针 +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { +public: + vector twoSum(vector& numbers, int target) { + int n = numbers.size(); + int left = 0; + int right = n-1; + while(left <= right) + { + if(numbers[left] + numbers[right] == target) + { + return {left + 1, right + 1}; + } + else if (numbers[left] + numbers[right] > target) + { + right--; + } + else + { + left++; + } + } + return {-1, -1}; + } +}; +``` +#### Java +```java +class Solution { + public int[] twoSum(int[] numbers, int target) { + int n = numbers.length; + int left = 0; + int right = n-1; + while(left <= right) + { + if(numbers[left] + numbers[right] == target) + { + return new int[]{left + 1, right + 1}; + } + else if (numbers[left] + numbers[right] > target) + { + right--; + } + else + { + left++; + } + } + + return new int[]{-1, -1}; + } +} +``` +#### Python +```python +class Solution(object): + def twoSum(self, numbers, target): + n = len(numbers) + left,right = 0, n-1 + while left <= right: + if numbers[left]+numbers[right] == target: + return [left+1, right+1] + elif numbers[left]+numbers[right] > target: + right -=1 + else: + left +=1 + + return [-1, -1] +``` + + + + +![](../../Pictures/qrcode.jpg) diff --git a/0169-Majority-Element/Animation/Animation.gif b/0169-Majority-Element/Animation/Animation.gif new file mode 100644 index 00000000..f52752ee Binary files /dev/null and b/0169-Majority-Element/Animation/Animation.gif differ diff --git a/0169-Majority-Element/Animation/Animation2.gif b/0169-Majority-Element/Animation/Animation2.gif new file mode 100644 index 00000000..28c54aab Binary files /dev/null and b/0169-Majority-Element/Animation/Animation2.gif differ diff --git a/0169-Majority-Element/Animation/Animation3.gif b/0169-Majority-Element/Animation/Animation3.gif new file mode 100644 index 00000000..629be6e1 Binary files /dev/null and b/0169-Majority-Element/Animation/Animation3.gif differ diff --git a/0169-Majority-Element/Article/0169-Majority-Element.md b/0169-Majority-Element/Article/0169-Majority-Element.md new file mode 100644 index 00000000..7923c515 --- /dev/null +++ b/0169-Majority-Element/Article/0169-Majority-Element.md @@ -0,0 +1,171 @@ +# 【数组中超过一半的数字】三种解法,最后一个解法太牛逼了! + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享的题目来源于 LeetCode 上第 169 号问题:求众数(求数组中超过一半的数字)。题目难度为 Easy,目前通过率为 45.8% 。 + +最后一种解法 **Cool** !!! + +# 题目描述 + +给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 + +你可以假设数组是非空的,并且给定的数组总是存在众数。 + +**示例 1:** + +``` +输入: [3,2,3] +输出: 3 +``` + +**示例 2:** + +``` +输入: [2,2,1,1,1,2,2] +输出: 2 +``` + +# 题目解析 + +题目意思很好理解:给你一个数组,里面有一个数字出现的次数超过了一半,你要找到这个数字并返回。 + +## 解法一:暴力解法 + +遍历整个数组,同时统计每个数字出现的次数。 + +最后将出现次数大于一半的元素返回即可。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### **代码实现** + +```java +class Solution { + public int majorityElement(int[] nums) { + int majorityCount = nums.length/2; + + for (int num : nums) { + int count = 0; + for (int elem : nums) { + if (elem == num) { + count += 1; + } + } + if (count > majorityCount) { + return num; + } + + } + } +} +``` + +### 复杂度分析 + +**时间复杂度**:O(n2) + +暴力解法包含两重嵌套的 for 循环,每一层 n 次迭代,因此时间复杂度为 O(n2) 。 + +**空间复杂度**:O(1) + +暴力解法没有分配任何与输入规模成比例的额外的空间,因此空间复杂度为 O(1)。 + +## 解法二:哈希表法 + +这个问题可以视为查找问题,对于查找问题往往可以使用时间复杂度为 O(1) 的 **哈希表**,通过以空间换时间的方式进行优化。 + +直接遍历整个 **数组** ,将每一个数字(num)与它出现的次数(count)存放在 **哈希表** 中,同时判断该数字出现次数是否是最大的,动态更新 maxCount,最后输出 maxNum。 + +### 动画描述 + +![](../Animation/Animation2.gif) + +### 代码实现 + +```java +class Solution { + public int majorityElement(int[] nums) { + Map map = new HashMap<>(); + // maxNum 表示元素,maxCount 表示元素出现的次数 + int maxNum = 0, maxCount = 0; + for (int num: nums) { + int count = map.getOrDefault(num, 0) + 1; + map.put(num, count); + if (count > maxCount) { + maxCount = count; + maxNum = num; + } + } + return maxNum; + } +} +``` + +### 复杂度分析 + +**时间复杂度**:O(n) + +总共有一个循环,里面哈希表的插入是常数时间的,因此时间复杂度为 O(n)。 + +**空间复杂度**:O(n) + +哈希表占用了额外的空间 O(n),因此空间复杂度为 O(n)。 + +## 解法三:摩尔投票法 + +再来回顾一下题目:寻找数组中超过一半的数字,这意味着数组中**其他数字出现次数的总和都是比不上这个数字出现的次数** 。 + +即如果把 该众数记为 `+1` ,把其他数记为 `−1` ,将它们全部加起来,和是大于 0 的。 + +所以可以这样操作: + +* 设置两个变量 candidate 和 count,**candidate** 用来保存数组中遍历到的某个数字,**count** 表示当前数字的出现次数,一开始 **candidate** 保存为数组中的第一个数字,**count** 为 1 +* 遍历整个数组 +* 如果数字与之前 **candidate** 保存的数字相同,则 **count** 加 1 +* 如果数字与之前 **candidate** 保存的数字不同,则 **count** 减 1 +* 如果出现次数 **count** 变为 0 ,**candidate** 进行变化,保存为当前遍历的那个数字,并且同时把 **count** 重置为 1 +* 遍历完数组中的所有数字即可得到结果 + +### 动画描述 + +![](../Animation/Animation3.gif) + +### 代码实现 + +```java +class Solution { + public int majorityElement(int[] nums) { + int candidate = nums[0], count = 1; + for (int i = 1; i < nums.length; ++i) { + if (count == 0) { + candidate = nums[i]; + count = 1; + } else if (nums[i] == candidate) { + count++; + } else{ + count--; + } + } + return candidate; + } +} +``` + +### 复杂度分析 + +**时间复杂度**:O(n) + +总共只有一个循环,因此时间复杂度为 O(n)。 + +**空间复杂度**:O(1) + +只需要常数级别的额外空间,因此空间复杂度为 O(1)。 + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0172-Factorial-Trailing-Zeroes/Article/0172-Factorial-Trailing-Zeroes.md b/0172-Factorial-Trailing-Zeroes/Article/0172-Factorial-Trailing-Zeroes.md new file mode 100644 index 00000000..1d1bd126 --- /dev/null +++ b/0172-Factorial-Trailing-Zeroes/Article/0172-Factorial-Trailing-Zeroes.md @@ -0,0 +1,78 @@ +# LeetCode第 172 号问题:阶乘后的零 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 172 号问题:阶乘后的零。题目难度为 Easy,目前通过率为 38.0% 。 + +### 题目描述 + +给定一个整数 *n*,返回 *n*! 结果尾数中零的数量。 + +**示例 1:** + +``` +输入: 3 +输出: 0 +解释: 3! = 6, 尾数中没有零。 +``` + +**示例 2:** + +``` +输入: 5 +输出: 1 +解释: 5! = 120, 尾数中有 1 个零. +``` + +**说明:** 你算法的时间复杂度应为 *O*(log *n*) 。 + +### 题目解析 + +题目很好理解,数阶乘后的数字末尾有多少个零。 + +最简单粗暴的方法就是先乘完再说,然后一个一个数。 + +事实上,你在使用暴力破解法的过程中就能发现规律: **这 9 个数字中只有 2(它的倍数) 与 5 (它的倍数)相乘才有 0 出现**。 + +所以,现在问题就变成了这个阶乘数中能配 **多少对 2 与 5**。 + +举个复杂点的例子: + + ` 10! = 【 2 *( 2 * 2 )* 5 *( 2 * 3 )*( 2 * 2 * 2 )*( 2 * 5)】` + +在 10!这个阶乘数中可以匹配两对 2 * 5 ,所以10!末尾有 2 个 0。 + +可以发现,一个数字进行拆分后 2 的个数肯定是大于 5 的个数的,所以能匹配多少对取决于 5 的个数。(好比现在男女比例悬殊,最多能有多少对异性情侣取决于女生的多少)。 + +那么问题又变成了 **统计阶乘数里有多少个 5 这个因子**。 + +需要注意的是,像 25,125 这样的不只含有一个 5 的数字的情况需要考虑进去。 + +比如 `n = 15`。那么在 `15!` 中 有 `3` 个 `5` (来自其中的`5`, `10`, `15`), 所以计算 `n/5` 就可以 。 + +但是比如 `n=25`,依旧计算 `n/5` ,可以得到 `5` 个`5`,分别来自其中的`5, 10, 15, 20, 25`,但是在 `25` 中其实是包含 `2 `个 `5` 的,这一点需要注意。 + +所以除了计算 `n/5` , 还要计算 `n/5/5 , n/5/5/5 , n/5/5/5/5 , ..., n/5/5/5,,,/5`直到商为0,然后求和即可。 + +### 代码实现 + +```java +public class Solution { + public int trailingZeroes(int n) { + return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5); + } +} +``` + + + +![](../../Pictures/qrcode.jpg) + + + + + + + diff --git a/0189-Rotate-Array/Animation/189.gif b/0189-Rotate-Array/Animation/189.gif new file mode 100644 index 00000000..6086a6e2 Binary files /dev/null and b/0189-Rotate-Array/Animation/189.gif differ diff --git a/0189-Rotate-Array/Animation/189.m4v b/0189-Rotate-Array/Animation/189.m4v new file mode 100644 index 00000000..871654aa Binary files /dev/null and b/0189-Rotate-Array/Animation/189.m4v differ diff --git a/0189-Rotate-Array/Article/0189-Rotate-Array.md b/0189-Rotate-Array/Article/0189-Rotate-Array.md new file mode 100644 index 00000000..e67fbdef --- /dev/null +++ b/0189-Rotate-Array/Article/0189-Rotate-Array.md @@ -0,0 +1,95 @@ +# LeetCode第 189 号问题:旋转数组 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 189 号问题:旋转数组。题目难度为 Easy,目前通过率为 41.7% 。 + +### 题目描述 + +给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + +**示例 1:** + +``` +输入: [1,2,3,4,5,6,7] 和 k = 3 +输出: [5,6,7,1,2,3,4] +解释: +向右旋转 1 步: [7,1,2,3,4,5,6] +向右旋转 2 步: [6,7,1,2,3,4,5] +向右旋转 3 步: [5,6,7,1,2,3,4] +``` + +**示例 2:** + +``` +输入: [-1,-100,3,99] 和 k = 2 +输出: [3,99,-1,-100] +解释: +向右旋转 1 步: [99,-1,-100,3] +向右旋转 2 步: [3,99,-1,-100] +``` + +**说明:** + +* 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 +* 要求使用空间复杂度为 O(1) 的 原地 算法。 + +
+ +### 题目解析 + +如果没有空间复杂度为 `O(1)` 这个限制,这道题相对来说会简单很多,需要做的仅仅复制一份数组,然后将 `[n - k, n]` 区间上的元素覆盖在数组的开头,接着遍历并覆盖剩下的元素即可。 + +不能使用额外的空间意味着你只能从数组本身来入手,这里我们可以使用反转数组来解决这道题,这是一个 rotate 数组的小技巧。如果仔细观察,你会发现 **数组经过 rotate 后会变成两个连续的区间段**,这两个区间段中的元素顺序和 rotate 之前的顺序是一样的。首先我们对数组当中所有的元素进行反转,然后分别对这两个区间进行反转,这样就可以保证区间内的顺序和之前一样,你可以看看动图或者自己动手尝试一下,这里并没有复杂的知识点,只是数组操作上的小技巧,了解了之后,可以运用到其他 rotate 数组的场景中。 + +
+ +### 代码实现 + +```java +class Solution { + public void rotate(int[] nums, int k) { + if (nums.length < k) { + k %= nums.length; + } + + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int tmp = nums[start]; + nums[start] = nums[end]; + nums[end] = tmp; + } + } +} +``` + +
+ +### 动画描述 + +![](../Animation/189.gif) + +
+ +### 复杂度分析 + +空间:O(1) + +时间:O(n) + + +![](../../Pictures/qrcode.jpg) + + + + + + + diff --git a/0200-Number-of-Islands/Animation/Animation.gif b/0200-Number-of-Islands/Animation/Animation.gif new file mode 100644 index 00000000..2ad9c44f Binary files /dev/null and b/0200-Number-of-Islands/Animation/Animation.gif differ diff --git a/0200-Number-of-Islands/Animation/Animation.m4v b/0200-Number-of-Islands/Animation/Animation.m4v new file mode 100644 index 00000000..0d88acba Binary files /dev/null and b/0200-Number-of-Islands/Animation/Animation.m4v differ diff --git a/0200-Number-of-Islands/Article/0200-Number-of-Islands.md b/0200-Number-of-Islands/Article/0200-Number-of-Islands.md new file mode 100644 index 00000000..60427e6a --- /dev/null +++ b/0200-Number-of-Islands/Article/0200-Number-of-Islands.md @@ -0,0 +1,84 @@ +# LeetCode 图解 | 200. 岛屿数量 + +## 题目描述 + +给定一个由 `'1'`(陆地)和 `'0'`(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + +**示例 1:** + +``` +输入: +11110 +11010 +11000 +00000 + +输出: 1 +``` + +**示例 2:** + +``` +输入: +11000 +11000 +00100 +00011 + +输出: 3 +``` + +## 题目解析 + +这道题的主要思路是深度优先搜索。每次走到一个是 1 的格子,就搜索整个岛屿。 + +网格可以看成是一个无向图的结构,每个格子和它上下左右的四个格子相邻。如果四个相邻的格子坐标合法,且是陆地,就可以继续搜索。 + +在深度优先搜索的时候要注意避免重复遍历。我们可以把已经遍历过的陆地改成 2,这样遇到 2 我们就知道已经遍历过这个格子了,不进行重复遍历。 + +每遇到一个陆地格子就进行深度优先搜索,最终搜索了几次就知道有几个岛屿。 + +## 动画理解 + +![](../Animation/Animation.gif) + +## 参考代码 + +```Java +class Solution { + public int numIslands(char[][] grid) { + if (grid.length == 0 || grid[0].length == 0) { + return 0; + } + + int count = 0; + for (int r = 0; r < grid.length; r++) { + for (int c = 0; c < grid[0].length; c++) { + if (grid[r][c] == '1') { + dfs(grid, r, c); + count++; + } + } + } + return count; + } + + void dfs(char[][] grid, int r, int c) { + if (!(0 <= r && r < grid.length && 0 <= c && c < grid[0].length)) { + return; + } + if (grid[r][c] != '1') { + return; + } + grid[r][c] = '2'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } +} +``` + +## 复杂度分析 + +设网格的边长为 $n$,则时间复杂度为 $O(n^2)$。 \ No newline at end of file diff --git a/0201-Bitwise-And-Of-Numbers-Range/Article/0201-Bitwise-And-Of-Numbers-Range.md b/0201-Bitwise-And-Of-Numbers-Range/Article/0201-Bitwise-And-Of-Numbers-Range.md new file mode 100644 index 00000000..c0978c66 --- /dev/null +++ b/0201-Bitwise-And-Of-Numbers-Range/Article/0201-Bitwise-And-Of-Numbers-Range.md @@ -0,0 +1,60 @@ +# LeetCode 第 201 号问题:数字范围按位与 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 201 号问题:数字范围按位与。题目难度为 Medium,目前通过率为 39.1% 。 + +### 题目描述 + +给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。 + +**示例 1:** + +``` +输入: [5,7] +输出: 4 +``` + +**示例 2:** + +``` +输入: [0,1] +输出: 0 +``` + +### 题目解析 + +以 [ 26 ,30] 为例。 + +首先,将 [ 26 , 30 ] 的范围数字用二进制表示出来: + +**11**010  **11**011  **11**100  **11**101  **11**110 + +而输出 24 的二进制是 11000 。 + +可以发现,只要找到二进制的 **左边公共部分** 即可。 + +所以,可以先建立一个 32 位都是 1 的 mask,然后每次向左移一位,比较 m 和 n 是否相同,不同再继续左移一位,直至相同,然后把 m 和 mask 相与就是最终结果。 + +### 动画描述 + +暂无 + +### 代码实现 + +```c++ +class Solution { +public: + int rangeBitwiseAnd(int m, int n) { + unsigned int d = INT_MAX; + while ((m & d) != (n & d)) { + d <<= 1; + } + return m & d; + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0202-happy-number/Animation/202.mp4 b/0202-happy-number/Animation/202.mp4 new file mode 100644 index 00000000..490b1201 Binary files /dev/null and b/0202-happy-number/Animation/202.mp4 differ diff --git a/0202-happy-number/Article/0202-happy-number b/0202-happy-number/Article/0202-happy-number new file mode 100644 index 00000000..31a9f163 --- /dev/null +++ b/0202-happy-number/Article/0202-happy-number @@ -0,0 +1,103 @@ +# 202. 快乐数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 202 题,主要涉及**集合**。 + +## 题目 + +编写一个算法来判断一个数 n 是不是快乐数。 + +「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为  1,那么这个数就是快乐数。 + +如果 n 是快乐数就返回 True ;不是,则返回 False 。 + +  + +示例: + +``` +输入:19 +输出:true +解释: +1^2 + 9^2 = 82 +8^2 + 2^2 = 68 +6^2 + 8^2 = 100 +1^2 + 0^2 + 0^2 = 1 +``` + +## 题目解析 + +我们先举一个例子 + +``` +输入: 59 +1. 5^2 + 9^2 = 106 +2. 1^2 + 0^2 + 6^2 = 37 +3. 3^2 + 7^2 = 58 +4. 5^2 + 8^2 = 89 +5. 8^2 + 9^2 = 145 +6. 1^2 + 4^2 + 5^2 = 42 +7. 4^2 + 2^2 = 20 +8. 2^2 + 0^2 = 4 +9. 4^2 = 16 +10. 1^2 + 6^2 = 37 +``` + +这个例子,我们可以看到输入59,第10步的结果和第2步一样,一直进行计算的话,会死循环,所以这个数肯定不会是快乐树。 + +那么会不会有结果一直无穷大的时候,理论上是不会的。 + +所以我们就会有两种情况,一种是计算后能得到1的,还有就是进入循环的。 + + + 1. 创建Set + 2. 循环条件为当前值不为1,循环计算结果,并对每次结果进行判断 + 3. 在Set里出现的,那么说明循环了,所以不会是快乐数,返回false + 4. 没在Set里出现的,那么将当前值存入Set中 + 5. 循环结束,说明计算结果为1,所以return true + + + + +## 动画理解 + + + + +## 参考代码 + + +```javaScript +/** + * @param {number} n + * @return {boolean} + */ + +var isHappy = function(n) { + let set= new Set() + let sum = 0 + n += '' + while (sum !== 1) { + sum = 0 + for (let i = 0; i < n.length; i++) { + sum += n[i]*n[i] + } + n = sum + '' + if (set.has(sum)) return false + set.add(sum) + } + return true +}; +``` + +## 复杂度分析 + +时间复杂度为O(logn),空间复杂度为O(logn) + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0203-Remove-Linked-List-Elements/Animation/Animation.gif b/0203-Remove-Linked-List-Elements/Animation/Animation.gif new file mode 100644 index 00000000..d0006fe1 Binary files /dev/null and b/0203-Remove-Linked-List-Elements/Animation/Animation.gif differ diff --git a/0203-Remove-Linked-List-Elements/Article/0203-Remove-Linked-List-Elements.md b/0203-Remove-Linked-List-Elements/Article/0203-Remove-Linked-List-Elements.md new file mode 100644 index 00000000..1ad4555d --- /dev/null +++ b/0203-Remove-Linked-List-Elements/Article/0203-Remove-Linked-List-Elements.md @@ -0,0 +1,85 @@ +# LeetCode 第 203 号问题:移除链表元素 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 203 号问题:移除链表元素。题目难度为 Easy,目前通过率为 55.8% 。 + +### 题目描述 + +删除链表中等于给定值 **val** 的所有节点。 + +**示例:** + +``` +输入: 1->2->6->3->4->5->6, val = 6 +输出: 1->2->3->4->5 +``` + +### 题目解析 + +主要考察了基本的链表遍历和设置指针的知识点。 + +定义一个虚拟头节点`dummyHead `,遍历查看原链表,遇到与给定值相同的元素,将该元素的前后两个节点连接起来,然后删除该元素即可。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +#### 代码一 + +``` +// 203. Remove Linked List Elements +// https://leetcode.com/problems/remove-linked-list-elements/description/ +// 使用虚拟头结点 +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + + // 创建虚拟头结点 + ListNode* dummyHead = new ListNode(0); + dummyHead->next = head; + + ListNode* cur = dummyHead; + while(cur->next != NULL){ + if(cur->next->val == val){ + ListNode* delNode = cur->next; + cur->next = delNode->next; + delete delNode; + } + else + cur = cur->next; + } + + ListNode* retNode = dummyHead->next; + delete dummyHead; + + return retNode; + } +}; + +``` + +#### 代码二 + +用递归来解。 + +通过递归调用到链表末尾,然后回来,需要删的元素,将链表next指针指向下一个元素即可。 + +``` +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + if (!head) return NULL; + head->next = removeElements(head->next, val); + return head->val == val ? head->next : head; + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0206-Reverse-Linked-List/Animation/Animation.gif b/0206-Reverse-Linked-List/Animation/Animation.gif new file mode 100644 index 00000000..b6284712 Binary files /dev/null and b/0206-Reverse-Linked-List/Animation/Animation.gif differ diff --git a/0206-Reverse-Linked-List/Article/0206-Reverse-Linked-List.md b/0206-Reverse-Linked-List/Article/0206-Reverse-Linked-List.md new file mode 100644 index 00000000..ff3d3861 --- /dev/null +++ b/0206-Reverse-Linked-List/Article/0206-Reverse-Linked-List.md @@ -0,0 +1,58 @@ +# LeetCode 第 206 号问题:反转链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 206 号问题:反转链表。题目难度为 Easy,目前通过率为 45.8% 。 + +### 题目描述 + +反转一个单链表。 + +**示例:** + +``` +输入: 1->2->3->4->5->NULL +输出: 5->4->3->2->1->NULL +``` + +**进阶:** +你可以迭代或递归地反转链表。你能否用两种方法解决这道题? + +### 题目解析 + +设置三个节点`pre`、`cur`、`next` + +- (1)每次查看`cur`节点是否为`NULL`,如果是,则结束循环,获得结果 +- (2)如果`cur`节点不是为`NULL`,则先设置临时变量`next`为`cur`的下一个节点 +- (3)让`cur`的下一个节点变成指向`pre`,而后`pre`移动`cur`,`cur`移动到`next` +- (4)重复(1)(2)(3) + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { +public: + ListNode* reverseList(ListNode* head) { + ListNode* pre = NULL; + ListNode* cur = head; + while(cur != NULL){ + ListNode* next = cur->next; + cur->next = pre; + pre = cur; + cur = next; + } + + return pre; + } +}; +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0209-Minimum-Size-Subarray-Sum/Animation/Animation.gif b/0209-Minimum-Size-Subarray-Sum/Animation/Animation.gif new file mode 100644 index 00000000..f7c7dc00 Binary files /dev/null and b/0209-Minimum-Size-Subarray-Sum/Animation/Animation.gif differ diff --git a/0209-Minimum-Size-Subarray-Sum/Article/0209-Minimum-Size-Subarray-Sum.md b/0209-Minimum-Size-Subarray-Sum/Article/0209-Minimum-Size-Subarray-Sum.md new file mode 100644 index 00000000..7890affd --- /dev/null +++ b/0209-Minimum-Size-Subarray-Sum/Article/0209-Minimum-Size-Subarray-Sum.md @@ -0,0 +1,96 @@ +# LeetCode 第 209 号问题:长度最小的子数组 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 209 号问题:长度最小的子数组。题目难度为 Medium,目前通过率为 25.8% 。 + +### 题目描述 + +给定一个含有 **n** 个正整数的数组和一个正整数 **s ,**找出该数组中满足其和 **≥ s** 的长度最小的连续子数组**。**如果不存在符合条件的连续子数组,返回 0。 + +**示例:** + +``` +输入: s = 7, nums = [2,3,1,2,4,3] +输出: 2 +解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。 +``` + +**进阶:** + +如果你已经完成了*O*(*n*) 时间复杂度的解法, 请尝试 *O*(*n* log *n*) 时间复杂度的解法。 + +### 题目解析 + +定义两个指针 left 和 right ,分别记录子数组的左右的边界位置。 + + +* (1)让 right 向右移,直到子数组和大于等于给定值或者 right 达到数组末尾; + +* (2)更新最短距离,将 left 像右移一位,sum 减去移去的值; + +* (3)重复(1)(2)步骤,直到 right 到达末尾,且 left 到达临界位置 + + + +### 动画描述 + +![](../Animation/Animation.gif) + +设置滑动窗口的长度为 0 ,位于数轴的最左端。 + +##### 1 .滑动窗口右端 R 开始移动,直到区间满足给定的条件,也就是和大于 7 ,此时停止于第三个元素 2,当前的最优长度为 4 + +![图 1](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/lo41y.jpg) + + + +##### 2. 滑动窗口左端 L 开始移动,缩小滑动窗口的大小,停止于第一个元素 3,此时区间和为 6,使得区间和不满足给定的条件(此时不大于 7) + +![图片 2](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/j7qnc.jpg) + + + +#### 3. 滑动窗口右端 R 继续移动,停止于第四个元素 4,此时和位 10 ,但最优长度仍然为 4 + +![图片 3](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/q8dxy.jpg) + + + +### 代码实现 + +``` +// 滑动窗口的思路 +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { + public int minSubArrayLen(int s, int[] nums) { + int l= 0,r = -1; // nums[l...r]为我们的滑动窗口 + int sum = 0; + int result = nums.length + 1; + while (l < nums.length){ // 窗口的左边界在数组范围内,则循环继续 + + if( r+1 = s + sum -= nums[l]; + l++; + } + + if(sum >= s){ + result = (r-l+1) < result ? (r-l+1) : result ; + } + } + if(result==nums.length+1){ + return 0; + } + return result; + } +} + +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0215-Kth-Largest-Element-in-an-Array/Animation/1.mp4 b/0215-Kth-Largest-Element-in-an-Array/Animation/1.mp4 new file mode 100644 index 00000000..67a1b0ef Binary files /dev/null and b/0215-Kth-Largest-Element-in-an-Array/Animation/1.mp4 differ diff --git a/0215-Kth-Largest-Element-in-an-Array/Animation/Animation.gif b/0215-Kth-Largest-Element-in-an-Array/Animation/Animation.gif new file mode 100644 index 00000000..790141c9 Binary files /dev/null and b/0215-Kth-Largest-Element-in-an-Array/Animation/Animation.gif differ diff --git a/0215-Kth-Largest-Element-in-an-Array/Article/0215-Kth-Largest-Element-in-an-Array b/0215-Kth-Largest-Element-in-an-Array/Article/0215-Kth-Largest-Element-in-an-Array new file mode 100644 index 00000000..c7ea5bdd --- /dev/null +++ b/0215-Kth-Largest-Element-in-an-Array/Article/0215-Kth-Largest-Element-in-an-Array @@ -0,0 +1,88 @@ +# LeetCode 第 215 号问题:数组中的第K个最大元素 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 215 号问题:数组中的第K个最大元素。题目难度为 Medium,目前通过率为 62.0% 。 + +#### 题目描述 + +> 在未排序的数组中找到第 **k** 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 +> + +```java +示例1: +输入: [3,2,1,5,6,4] 和 k = 2 +输出: 5 +示例2: +输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 +输出: 4 +``` + +#### 题目解析 + +维护一个K大小的最小堆,堆中元素个数小于K时,新元素直接进入堆中;否则,当堆顶小于新元素时,弹出堆顶,将新元素加入堆。 + +由于堆是最小堆,堆顶是堆中的最小元素,新元素都会保证比堆顶小(否则新元素替换堆顶),故堆中K个元素是已扫描元素里最大的K个;堆顶元素即为第K大数。 + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 代码实现 + +Java语言 + +```java +class Solution { + public int findKthLargest(int[] nums, int k) { + // // 创建一个小顶堆(优先队列模拟) + PriorityQueue heap = + new PriorityQueue(); + + // 在堆中维护当前最大k个元素 + for (int i = 0; i < nums.length; i++){ + if(heap.size() < k){ + heap.add(nums[i]); + }else if (heap.element() < nums[i]){ + heap.poll(); + heap.add(nums[i]); + } + } + return heap.poll(); + } +} +``` + +C++语言实现 + +```c++ +#include + +class Solution { +public: + int findKthLargest(vector& nums, int k) { + priority_queue, greater > Q; + for(int i = 0; i < nums.size(); i++){ + if(Q.size() < k){ + Q.push(nums[i]); + } + else if(Q.top() < nums[i]){ + Q.pop(); + Q.push(nums[i]); + } + } + + return Q.top(); + } +}; +``` + +#### 复杂度分析 + ++ 时间复杂度:向大小为 k 的堆中添加元素的时间复杂度为O(logK),重复该操作 N 次,故总时间复杂度为 O(NlogK) ++ 空间复杂度:O(K) + + + diff --git a/0215-Kth-Largest-Element-in-an-Array/Code/1.cpp b/0215-Kth-Largest-Element-in-an-Array/Code/1.cpp new file mode 100644 index 00000000..716dae09 --- /dev/null +++ b/0215-Kth-Largest-Element-in-an-Array/Code/1.cpp @@ -0,0 +1,19 @@ +#include + +class Solution { +public: + int findKthLargest(vector& nums, int k) { + priority_queue, greater > Q; + for(int i = 0; i < nums.size(); i++){ + if(Q.size() < k){ + Q.push(nums[i]); + } + else if(Q.top() < nums[i]){ + Q.pop(); + Q.push(nums[i]); + } + } + + return Q.top(); + } +}; \ No newline at end of file diff --git a/0215-Kth-Largest-Element-in-an-Array/Code/1.java b/0215-Kth-Largest-Element-in-an-Array/Code/1.java new file mode 100644 index 00000000..0d447733 --- /dev/null +++ b/0215-Kth-Largest-Element-in-an-Array/Code/1.java @@ -0,0 +1,18 @@ +class Solution { + public int findKthLargest(int[] nums, int k) { + // // һСѣȶģ⣩ + PriorityQueue heap = + new PriorityQueue(); + + // ڶάǰkԪ + for (int i = 0; i < nums.length; i++){ + if(heap.size() < k){ + heap.add(nums[i]); + }else if (heap.element() < nums[i]){ + heap.poll(); + heap.add(nums[i]); + } + } + return heap.poll(); + } +} \ No newline at end of file diff --git a/0219-Contains-Duplicate-II/Animation/Animation.gif b/0219-Contains-Duplicate-II/Animation/Animation.gif new file mode 100644 index 00000000..828ac4ac Binary files /dev/null and b/0219-Contains-Duplicate-II/Animation/Animation.gif differ diff --git a/0219-Contains-Duplicate-II/Article/0219-Contains-Duplicate-II.md b/0219-Contains-Duplicate-II/Article/0219-Contains-Duplicate-II.md new file mode 100644 index 00000000..7170f6af --- /dev/null +++ b/0219-Contains-Duplicate-II/Article/0219-Contains-Duplicate-II.md @@ -0,0 +1,85 @@ +# LeetCode 第 219 号问题:存在重复元素 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 219 号问题:存在重复元素 II。题目难度为 Easy,目前通过率为 34.8% 。 + +### 题目描述 + +给定一个整数数组和一个整数 *k*,判断数组中是否存在两个不同的索引 *i* 和 *j*,使得 **nums [i] = nums [j]**,并且 *i* 和 *j* 的差的绝对值最大为 *k*。 + +**示例 1:** + +``` +输入: nums = [1,2,3,1], k = 3 +输出: true +``` + +**示例 2:** + +``` +输入: nums = [1,0,1,1], k = 1 +输出: true +``` + +**示例 3:** + +``` +输入: nums = [1,2,3,1,2,3], k = 2 +输出: false +``` + +### 题目解析 + +考虑用滑动窗口与查找表来解决。 + +* 设置查找表`record`,用来保存每次遍历时插入的元素,`record `的最大长度为`k ` +* 遍历数组`nums`,每次遍历的时候在`record `查找是否存在相同的元素,如果存在则返回`true`,遍历结束 +* 如果此次遍历在`record `未查找到,则将该元素插入到`record `中,而后查看`record `的长度是否为`k + 1` +* 如果此时`record `的长度是否为`k + 1`,则删减`record`的元素,该元素的值为`nums[i - k]` +* 如果遍历完整个数组`nums`未查找到则返回`false` + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +// 219. Contains Duplicate II +// https://leetcode.com/problems/contains-duplicate-ii/description/ +// 时间复杂度: O(n) +// 空间复杂度: O(k) +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + + if(nums.size() <= 1) return false; + + if(k <= 0) return false; + + + unordered_set record; + for(int i = 0 ; i < nums.size() ; i ++){ + + if(record.find(nums[i]) != record.end()){ + return true; + } + + record.insert(nums[i]); + + // 保持record中最多有k个元素 + // 因为在下一次循环中会添加一个新元素,使得总共考虑k+1个元素 + if(record.size() == k + 1){ + record.erase(nums[i - k]); + } + } + + return false; + } +}; +``` + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0231-Power-Of-Two/Article/0231-Power-Of-Two.md b/0231-Power-Of-Two/Article/0231-Power-Of-Two.md new file mode 100644 index 00000000..55810539 --- /dev/null +++ b/0231-Power-Of-Two/Article/0231-Power-Of-Two.md @@ -0,0 +1,81 @@ +# LeetCode 第 231 号问题:2 的幂 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 231 号问题:2 的幂。题目难度为 Easy,目前通过率为 45.6% 。 + +### 题目描述 + +给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + +**示例 1:** + +``` +输入: 1 +输出: true +解释: 2^0 = 1 +``` + +**示例 2:** + +``` +输入: 16 +输出: true +解释: 2^4 = 16 +``` + +**示例 3:** + +``` +输入: 218 +输出: false +``` + +### 题目解析 + +首先,先来分析一下 2 的次方数的二进制写法: + +![表格](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/3wdpd.jpg) + +仔细观察,可以看出 2 的次方数都只有一个 1 ,剩下的都是 0 。根据这个特点,只需要每次判断最低位是否为 1 ,然后向右移位,最后统计 1 的个数即可判断是否是 2 的次方数。 + +代码很简单: + +```c++ +class Solution { +public: + bool isPowerOfTwo(int n) { + int cnt = 0; + while (n > 0) { + cnt += (n & 1); + n >>= 1; + } + return cnt == 1; + } +}; +``` + +该题还有一种巧妙的解法。再观察上面的表格,如果一个数是 2 的次方数的话,那么它的二进数必然是最高位为1,其它都为 0 ,那么如果此时我们减 1 的话,则最高位会降一位,其余为 0 的位现在都为变为 1,那么我们把两数相与,就会得到 0。 + +比如 2 的 3 次方为 8,二进制位 1000 ,那么 ` 8 - 1 = 7`,其中 7 的二进制位 0111。 + +### 图片描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/1w9lq.jpg) + +### 代码实现 + +利用这个性质,只需一行代码就可以搞定。 + +```c++ +class Solution { +public: + bool isPowerOfTwo(int n) { + return (n > 0) && (!(n & (n - 1))); + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0234-isPalindrome/Animation/solved01.gif b/0234-isPalindrome/Animation/solved01.gif new file mode 100644 index 00000000..b68a9262 Binary files /dev/null and b/0234-isPalindrome/Animation/solved01.gif differ diff --git a/0234-isPalindrome/Animation/solved02.gif b/0234-isPalindrome/Animation/solved02.gif new file mode 100644 index 00000000..ddaabdd9 Binary files /dev/null and b/0234-isPalindrome/Animation/solved02.gif differ diff --git a/0234-isPalindrome/Article/0234-isPalindrome.md b/0234-isPalindrome/Article/0234-isPalindrome.md new file mode 100644 index 00000000..57172d9e --- /dev/null +++ b/0234-isPalindrome/Article/0234-isPalindrome.md @@ -0,0 +1,111 @@ +## LeetCode第234号问题:回文链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 个人博客:www.zhangxiaoshuai.fun + +**本题选择leetcode第234题,easy难度,目前通过率41.5%** + +```txt +题目描述: +请判断一个链表是否为回文链表。 +示例 1: + 输入: 1->2 + 输出: false + +示例 2: + 输入: 1->2->2->1 + 输出: true +``` + +***这道题还有进阶版本,我们先实现这个普通版本再看。*** + +### 题目分析: + +``` +首先,我们先遍历一遍链表,将链表中的每个值存入数组当中,然后我们判断数组中的元素是否满足回文数条件即可。 +这里因为我们不知道链表的长度,我们先使用动态数组将值存起来,然后再存到固定大小的数组中。 +``` + +### 解法一gif动画演示: + +![01](../Animation/solved01.gif) + +### 代码: + +```java +public boolean isPalindrome(ListNode head) { + List list = new ArrayList<>(); + while (head != null) { + list.add(head.val); + head = head.next; + } + int[] arr = new int[list.toArray().length]; + int temp = 0; + for (int a : list) { + arr[temp++] = a; + } + temp = 0; + for (int i = 0;i < arr.length/2;i++) { + if (arr[i] == arr[arr.length-i-1]) { + temp++; + } + } + if(temp == arr.length/2) return true; + return false; +} +``` + +**时间复杂度:O(n) 空间复杂度:O(n)** + +### 进阶: + +**你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?** + +**思路分析:**我们先找到链表的中间结点,然后将中间结点后面的链表进行反转,反转之后再和前半部分链表进行比较,如果相同则表示该链表属于回文链表,返回true;否则,否则返回false + +### 解法二gif动画演示: + +![02](../Animation/solved02.gif) + +### 代码: + +```java +public boolean isPalindrome(ListNode head) { + if(head == null || head.next == null) return true; + ListNode p = new ListNode(-1); + ListNode low = p; + ListNode fast = p; + p.next = head; + //使用快慢指针来确定中间结点 + while(fast != null && fast.next != null){ + low = low.next; + fast = fast.next.next; + } + ListNode cur = low.next; + ListNode pre = null; + low.next = null; + low = p.next; + + //反转后半部分链表 + while(cur != null){ + ListNode tmp = cur.next; + cur.next = pre; + pre = cur; + cur = tmp; + } + //将前半部分链表和后半部分进行比较 + while(pre != null){ + if(low.val != pre.val){ + return false; + } + low = low.next; + pre = pre.next; + } + return true; +} +``` + +**时间复杂度:O(n) 空间复杂度:O(1)** + +**没错,可以看到上面的代码是完全能可以通过的,虽然我们完成了题目,但是我们改变了链表的结构,也就是说它现在不是它了;出题人应该是不希望我们破坏链表的,所以在我们完成判断之后,需要将链表恢复原样,也就是将后半部分链表反转之后接到前半部分链表的末尾。** \ No newline at end of file diff --git a/0237-Delete-Node-in-a-Linked-List/Animation/Animation.gif b/0237-Delete-Node-in-a-Linked-List/Animation/Animation.gif new file mode 100644 index 00000000..9178ec3e Binary files /dev/null and b/0237-Delete-Node-in-a-Linked-List/Animation/Animation.gif differ diff --git a/0237-Delete-Node-in-a-Linked-List/Article/0237-Delete-Node-in-a-Linked-List.md b/0237-Delete-Node-in-a-Linked-List/Article/0237-Delete-Node-in-a-Linked-List.md new file mode 100644 index 00000000..3b53aa50 --- /dev/null +++ b/0237-Delete-Node-in-a-Linked-List/Article/0237-Delete-Node-in-a-Linked-List.md @@ -0,0 +1,77 @@ +# LeetCode 第 237 号问题:删除链表中的节点 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 237 号问题:删除链表中的节点。题目难度为 Easy,目前通过率为 72.6% 。 + +### 题目描述 + +请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。 + +现有一个链表 -- head = [4,5,1,9],它可以表示为: + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/uv8bw.png) + + + +**示例 1:** + +``` +输入: head = [4,5,1,9], node = 5 +输出: [4,1,9] +解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. +``` + +**示例 2:** + +``` +输入: head = [4,5,1,9], node = 1 +输出: [4,5,9] +解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. +``` + + + +**说明:** + +- 链表至少包含两个节点。 +- 链表中所有节点的值都是唯一的。 +- 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 +- 不要从你的函数中返回任何结果。 + +### 题目解析 + +此题注意的点是没有给我们链表的起点,只给我们了一个要删的节点,与以往处理的情况稍许不同。 + +**这道题的处理方法是先把当前节点的值用下一个节点的值覆盖,然后我们删除下一个节点即可** + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { +public: + void deleteNode(ListNode* node) { + if (node == NULL) return; + if (node->next == NULL) { + delete node; + node = NULL; + return; + } + node->val = node->next->val; + ListNode *delNode = node->next; + node->next = delNode->next; + + delete delNode; + } +}; +``` + + + +![](../../Pictures/qrcode.jpg) diff --git a/0239-Sliding-Window-Maximum/Animation/Animation.gif b/0239-Sliding-Window-Maximum/Animation/Animation.gif new file mode 100644 index 00000000..e0535296 Binary files /dev/null and b/0239-Sliding-Window-Maximum/Animation/Animation.gif differ diff --git a/0239-Sliding-Window-Maximum/Article/0239-Sliding-Window-Maximum.md b/0239-Sliding-Window-Maximum/Article/0239-Sliding-Window-Maximum.md new file mode 100644 index 00000000..5d02531c --- /dev/null +++ b/0239-Sliding-Window-Maximum/Article/0239-Sliding-Window-Maximum.md @@ -0,0 +1,86 @@ +# LeetCode 第 239 号问题:滑动窗口最大值 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 239 号问题:滑动窗口最大值。题目难度为 Hard,目前通过率为 40.5% 。 + +### 题目描述 + +给定一个数组 *nums*,有一个大小为 *k* 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 *k* 内的数字。滑动窗口每次只向右移动一位。 + +返回滑动窗口最大值。 + +**示例:** + +``` +输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 +输出: [3,3,5,5,6,7] +解释: + + 滑动窗口的位置 最大值 +--------------- ----- +[1 3 -1] -3 5 3 6 7 3 + 1 [3 -1 -3] 5 3 6 7 3 + 1 3 [-1 -3 5] 3 6 7 5 + 1 3 -1 [-3 5 3] 6 7 5 + 1 3 -1 -3 [5 3 6] 7 6 + 1 3 -1 -3 5 [3 6 7] 7 +``` + +**注意:** + +你可以假设 *k* 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。 + +**进阶:** + +你能在线性时间复杂度内解决此题吗? + +### 题目解析 + +利用一个 **双端队列**,在队列中存储元素在数组中的位置, 并且维持队列的严格递减,,也就说维持队首元素是 **最大的 **,当遍历到一个新元素时, 如果队列里有比当前元素小的,就将其移除队列,以保证队列的递减。当队列元素位置之差大于 k,就将队首元素移除。 + +### 补充:什么是双端队列(Dqueue) + +Deque 的含义是 “double ended queue”,即双端队列,它具有队列和栈的性质的数据结构。顾名思义,它是一种前端与后端都支持插入和删除操作的队列。 + +Deque 继承自 Queue(队列),它的直接实现有 ArrayDeque、LinkedList 等。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + //有点坑,题目里都说了数组不为空,且 k > 0。但是看了一下,测试用例里面还是有nums = [], k = 0,所以只好加上这个判断 + if (nums == null || nums.length < k || k == 0) return new int[0]; + int[] res = new int[nums.length - k + 1]; + //双端队列 + Deque deque = new LinkedList<>(); + for (int i = 0; i < nums.length; i++) { + //在尾部添加元素,并保证左边元素都比尾部大 + while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) { + deque.removeLast(); + } + deque.addLast(i); + //在头部移除元素 + if (deque.getFirst() == i - k) { + deque.removeFirst(); + } + //输出结果 + if (i >= k - 1) { + res[i - k + 1] = nums[deque.getFirst()]; + } + } + return res; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0242-Valid-Anagram/Animation/242.gif b/0242-Valid-Anagram/Animation/242.gif new file mode 100644 index 00000000..e788356d Binary files /dev/null and b/0242-Valid-Anagram/Animation/242.gif differ diff --git a/0242-Valid-Anagram/Animation/242.m4v b/0242-Valid-Anagram/Animation/242.m4v new file mode 100644 index 00000000..c45aab7e Binary files /dev/null and b/0242-Valid-Anagram/Animation/242.m4v differ diff --git a/0242-Valid-Anagram/Article/0242-Valid-Anagram.md b/0242-Valid-Anagram/Article/0242-Valid-Anagram.md new file mode 100644 index 00000000..b1f17c41 --- /dev/null +++ b/0242-Valid-Anagram/Article/0242-Valid-Anagram.md @@ -0,0 +1,90 @@ +# LeetCode 第 242 号问题:有效的字母异位词 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 242 号问题:有效的字母异位词。题目难度为 Easy,目前通过率为 60.5% 。 + +### 题目描述 + +给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + +**示例 1:** + +``` +输入: s = "anagram", t = "nagaram" +输出: true +``` + +**示例 2:** + +``` +输入: s = "rat", t = "car" +输出: false +``` + +**说明:** + +你可以假设字符串只包含小写字母。 + +**进阶:** + +如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? + +### 题目解析 + +字母异位词的意思是,如果两个字符串互为字母异位词,那么两个字符串里的字符数量和种类都一样,不一样的是每个字符出现的位置,以及先后顺序。最简单的方法是直接将字符串按一定的规则排序,然后遍历对比即可。这种方法省空间,但是因为涉及到排序,时间复杂度就是 `O(nlgn)`。 + +还有一个类似计数排序的方法,就是统计一个字符串里面所有字符对应的个数,然后再拿另外一个字符串做对比,这么做可以把时间复杂度降到 `O(n)`,如果这道题目中的字符串仅仅包含小写字母的话,我们可以开辟一个长度是 26 的数组,这样就不需要额外的空间,但如果说输入的字符串包含 unicode 字符,由于 unicode 字符集过于庞大,常量级别的数组变得不那么可取,我们可以考虑使用散列表这样的结构进行存储,逻辑是和之前一样的,但是这里的空间复杂度就不再是 `O(1)`,而是 `O(n)` + +
+ +### 代码实现(排序) + +```java +public boolean isAnagram(String s, String t) { + if ((s == null) || (t == null) || (t.length() != s.length())) { + return false; + } + char[] sArr1 = s.toCharArray(); + char[] sArr2 = t.toCharArray(); + Arrays.sort(sArr1); + Arrays.sort(sArr2); + return Arrays.equals(sArr1, sArr2); +} +``` + +### 代码实现(哈希) + +```java +public boolean isAnagram(String s, String t) { + if ((s == null) || (t == null) || (t.length() != s.length())) { + return false; + } + + int n = s.length(); + + Map counts = new HashMap<>(); + + for (int i = 0; i < n; ++i) { + counts.put(s.charAt(i), counts.getOrDefault(s.charAt(i), 0) + 1); + } + + for (int i = 0; i < n; ++i) { + counts.put(t.charAt(i), counts.getOrDefault(t.charAt(i), 0) - 1); + if (counts.getOrDefault(t.charAt(i), -1) < 0) { + return false; + } + } + + return true; +} +``` + +### 动画描述 + +![](../Animation/242.gif) + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0260-Single-Number-III/Animation/260.gif b/0260-Single-Number-III/Animation/260.gif new file mode 100644 index 00000000..eef4297c Binary files /dev/null and b/0260-Single-Number-III/Animation/260.gif differ diff --git a/0260-Single-Number-III/Animation/260.m4v b/0260-Single-Number-III/Animation/260.m4v new file mode 100644 index 00000000..8d5eaea1 Binary files /dev/null and b/0260-Single-Number-III/Animation/260.m4v differ diff --git a/0260-Single-Number-III/Article/0260-Single-Number-III.md b/0260-Single-Number-III/Article/0260-Single-Number-III.md new file mode 100644 index 00000000..981fc14a --- /dev/null +++ b/0260-Single-Number-III/Article/0260-Single-Number-III.md @@ -0,0 +1,71 @@ +# LeetCode 第 260 号问题:只出现一次的数字 III + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 260 号问题:只出现一次的数字 III。题目难度为 Medium,目前通过率为 72.1% 。 + +## 题目描述 + +给定一个整数数组 `nums`,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。 + +**示例:** + +``` +输入: [1,2,1,3,2,5] +输出: [3,5] +``` + +**注意:** + +1. 结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。 +2. 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现? + +
+ +## 题目解析 + +第三道题目,和第一道题目只变化了一点,就是输入数组中除了 **两** 个元素出现了一次,其余的都出现了两次。我们依然可以从第一道题目的解法去思考这道题,如果我们还是按照第一题的做法,最后我们得到的答案将会是 `ele1 ^ ele2` 的结果,我们需要思考如何从这个结果出发,得到 `ele1` 和 `ele2`。首先思考一个问题 `ele1 ^ ele2` 的结果具体是什么,或者说里面有什么样的信息,异或操作是将相同的 bit 位置 0,相异的置 1,也就是说 **`ele1` 和 `ele2` 异或的结果中为 1 的 bit 位是两个元素相异的 bit 位,再进一步讲,我们可以用这个 bit 位来区分两个元素**。于是在第一题的基础之上,用一个 bit 位作为判断条件,来决定当前遍历到的元素和那个值进行异或,因为这时我们要求的值有两个。 + +从上面这些题目中你可以看到位运算的强大,三道系列题目的时间复杂度均为 `O(n)`,但是位运算是更加底层的运算,实际时间消耗会比正常操作要更快一些。在理解位运算的时候,**试着把 bit 作为最小单位去思考,或许会有不一样的发现**。 + +
+ +### 代码实现 + +```java +public int[] singleNumber(int[] nums) { + if (nums == null || nums.length == 0) { + return new int[2]; + } + + int different = 0; + for (int i : nums) { + different ^= i; + } + + // 这个操作是取 different 从左往右最后一个为 1 的 bit 位 + different &= -different; + int[] ans = {0, 0}; + for (int i : nums) { + if ((different & i) == 0) { + ans[0] ^= i; + } else { + ans[1] ^= i; + } + } + + return ans; +} +``` + +
+ +### 动画演示 +![](../Animation/260.gif) + + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0268-Missing-Number/Animation/Animation.gif b/0268-Missing-Number/Animation/Animation.gif new file mode 100644 index 00000000..67319487 Binary files /dev/null and b/0268-Missing-Number/Animation/Animation.gif differ diff --git a/0268-Missing-Number/Article/0268-Missing-Number.md b/0268-Missing-Number/Article/0268-Missing-Number.md new file mode 100644 index 00000000..14933d01 --- /dev/null +++ b/0268-Missing-Number/Article/0268-Missing-Number.md @@ -0,0 +1,126 @@ +# LeetCode 第 268 号问题:缺失数字 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享一道很简单的算法题。 + +题目来源于 LeetCode 上第 268 号问题:缺失数字。题目难度为 Easy,目前通过率为 50.2% 。 + +## 题目描述 + +给定一个包含 `0, 1, 2, ..., n` 中 *n* 个数的序列,找出 0 .. *n* 中没有出现在序列中的那个数。 + +**说明:** + +你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/i47fw.png) + +## 题目解析 + +这道题目有三种解法。 + +### 解法一:异或法 + +和之前那道 **只出现一次的数字** 很类似: + +> 只出现一次的数字: 给定一个**非空**整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 + +如果我们补充一个完整的数组和原数组进行组合,那所求解的问题就变成了 **只出现一次的数字**。 + +将少了一个数的数组与 0 到 n 之间完整的那个数组进行异或处理,因为相同的数字异或会变为了 0 ,那么全部数字异或后,剩下的就是少了的那个数字。 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/el8zt.png) + +#### 代码实现1 + +```java +class Solution { + public int missingNumber(int[] nums) { + int res = 0; + int i = 0; + //注意数组越界情况 + for (; i < nums.length;i++){ + // i 表示完整数组中的数字,与原数组中的数字 nums[i] 进行异或,再与保存的结果异或 + res = res^i^nums[i]; + } + //最后需要与循环中无法使用到的那个最大的数异或 + return res^i; + } +} +``` + +#### 代码实现2 + +```java +class Solution { + public int missingNumber(int[] nums) { + int res = nums.length; + for (int i = 0; i < nums.length; ++i){ + res ^= nums[i]; + res ^= i; + } + return res; + } +} +``` + + + +### 解法二:求和法 + +- 求出 0 到 n 之间所有的数字之和 +- 遍历数组计算出原始数组中数字的累积和 +- 两和相减,差值就是丢失的那个数字 + +![](../Animation/Animation.gif) + +```java +//小吴之前担心会数据溢出,不过估计这题考察的不是这个,所以测试用例没写这种吧,还是能 AC 的 +class Solution { + public int missingNumber(int[] nums) { + int n = nums.length; + int sum = (n+0)*(n+1)/2; + for (int i=0; i 注:由于一开始进行了排序操作,因此使用二分法的性能是不如上面两种方法。 + +```java +public class Solution { + public int missingNumber(int[] nums) { + Arrays.sort(nums); + int left = 0; + int right = nums.length; + while (left < right){ + int mid = (left + right) / 2; + if (nums[mid] > mid){ + right = mid; + }else{ + left = mid + 1; + } + } + return left; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0279-Perfect-Squares/Animation/Animation.gif b/0279-Perfect-Squares/Animation/Animation.gif new file mode 100644 index 00000000..9a7eccac Binary files /dev/null and b/0279-Perfect-Squares/Animation/Animation.gif differ diff --git a/0279-Perfect-Squares/Article/0279-Perfect-Squares.md b/0279-Perfect-Squares/Article/0279-Perfect-Squares.md new file mode 100644 index 00000000..ceb9ffd5 --- /dev/null +++ b/0279-Perfect-Squares/Article/0279-Perfect-Squares.md @@ -0,0 +1,118 @@ +# LeetCode 第 279 号问题:完全平方数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 279 号问题:完全平方数。题目难度为 Medium,目前通过率为 49.1% 。 + +### 题目描述 + +给定正整数 *n*,找到若干个完全平方数(比如 `1, 4, 9, 16, ...`)使得它们的和等于 *n*。你需要让组成和的完全平方数的个数最少。 + +**示例 1:** + +``` +输入: n = 12 +输出: 3 +解释: 12 = 4 + 4 + 4. +``` + +**示例 2:** + +``` +输入: n = 13 +输出: 2 +解释: 13 = 4 + 9. +``` + +### 题目解析 + +这道题目很有意思。 + +大部分文章给出的答案都是依托于一个定理:**四平方定理**。 + +四平方定理讲的就是任何一个正整数都可以表示成不超过四个整数的平方之和。也就是说,这道题的答案只有 1,2 ,3,4 这四种可能。 + +同时,还有一个非常重要的推论满足四数平方和定理的数n(这里要满足由四个数构成,小于四个不行),必定满足 n = 4a * (8b + 7)。 + +根据这个重要的推论来解决此题,首先将输入的`n`迅速缩小。然后再判断,这个缩小后的数是否可以通过`两个平方数的和或一个平方数`组成,不能的话我们返回`3`,能的话我们返回`平方数的个数`。 + +所以代码很简洁,如下: + +```java +public int numSquares(int n) { + while (n % 4 == 0){ + n /= 4; + } + if ( n % 8 == 7){ + return 4; + } + int a = 0; + while ( (a * a) <= n){ + int b = (int)Math.pow((n - a * a),0.5); + if(a * a + b * b == n) { + //如果可以 在这里返回 + if(a != 0 && b != 0) { + return 2; + } else{ + return 1; + } + } + a++; + } + return 3; +} +``` + + + +但因为本章是「广度优先遍历」的专栏,因此再补充一个图的广度优先遍历的答案: + +使用广度优先搜索方法,将 n 依次减去比 n 小的所有平方数,直至 n = 0 ,此时的层数即为最后的结果。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +import java.util.LinkedList; +import javafx.util.Pair; +class Solution { + public int numSquares(int n) { + if(n == 0) + return 0; + + LinkedList> queue = new LinkedList>(); + queue.addLast(new Pair(n, 0)); + + boolean[] visited = new boolean[n+1]; + visited[n] = true; + + while(!queue.isEmpty()){ + Pair front = queue.removeFirst(); + int num = front.getKey(); + int step = front.getValue(); + + if(num == 0) + return step; + + for(int i = 1 ; num - i*i >= 0 ; i ++){ + int a = num - i*i; + if(!visited[a]){ + if(a == 0) return step + 1; + queue.addLast(new Pair(num - i * i, step + 1)); + visited[num - i * i] = true; + } + } + } + return 0; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) diff --git a/0283-Move-Zeroes/Animation/Animation.gif b/0283-Move-Zeroes/Animation/Animation.gif new file mode 100644 index 00000000..8b44b9a4 Binary files /dev/null and b/0283-Move-Zeroes/Animation/Animation.gif differ diff --git a/0283-Move-Zeroes/Article/0283-Move-Zeroes.md b/0283-Move-Zeroes/Article/0283-Move-Zeroes.md new file mode 100644 index 00000000..10192bc3 --- /dev/null +++ b/0283-Move-Zeroes/Article/0283-Move-Zeroes.md @@ -0,0 +1,135 @@ +# LeetCode 第 283 号问题:移动零 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 283 号问题:移动零。题目难度为 Easy,目前通过率为 53.8% 。 + +### 题目描述 + +给定一个数组 `nums`,编写一个函数将所有 `0` 移动到数组的末尾,同时保持非零元素的相对顺序。 + +**示例:** + +``` +输入: [0,1,0,3,12] +输出: [1,3,12,0,0] +``` + +**说明**: + +1. 必须在原数组上操作,不能拷贝额外的数组。 +2. 尽量减少操作次数。 + +### 题目解析 + +设定一个临时变量 k = 0,遍历数组 nums ,将非零元素移动到 nums[k]位 置,同时 k++,而后将【k,….nums.size()】中的元素置零。 + +### 解法一 + +创建一个临时数组 nonZeroElements ,遍历 nums ,将 nums 中非 0 元素赋值到 nonZeroElements中,而后按顺序将 nonZeroElements 赋值到 nums 上,未遍历的元素置 0 ; + +动画如下: + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0eeix.gif) + +代码如下: + +``` +// 时间复杂度: O(n) +// 空间复杂度: O(n) +class Solution { +public: + void moveZeroes(vector& nums) { + + vector nonZeroElements; + + // 将vec中所有非0元素放入nonZeroElements中 + for(int i = 0 ; i < nums.size() ; i ++) + if(nums[i]) + nonZeroElements.push_back(nums[i]); + + // 将nonZeroElements中的所有元素依次放入到nums开始的位置 + for(int i = 0 ; i < nonZeroElements.size() ; i ++) + nums[i] = nonZeroElements[i]; + + // 将nums剩余的位置放置为0 + for(int i = nonZeroElements.size() ; i < nums.size() ; i ++) + nums[i] = 0; + } +}; + +``` + +### 解法二 + +设定一个临时变量 k = 0,遍历数组 nums ,将非零元素移动到 nums[k] 位置,同时 k++,而后将【k,….nums.size()】中的元素置零。 + +动画如下: + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/jodp0.gif) + +代码如下: + +``` +// 原地(in place)解决该问题 +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { +public: + void moveZeroes(vector& nums) { + + int k = 0; // nums中, [0...k)的元素均为非0元素 + + // 遍历到第i个元素后,保证[0...i]中所有非0元素 + // 都按照顺序排列在[0...k)中 + for(int i = 0 ; i < nums.size() ; i ++) + if(nums[i]) + nums[k++] = nums[i]; + + // 将nums剩余的位置放置为0 + for(int i = k ; i < nums.size() ; i ++) + nums[i] = 0; + } +}; +``` + +### 解法三 + +思路:设定一个临时变量 k = 0,遍历数组 nums,将非零元素与之前的零元素进行交换,维护变量k的值。 + +动画如下: + +![](../Animation/Animation.gif) +代码如下: + +``` +// 原地(in place)解决该问题 +// 时间复杂度: O(n) +// 空间复杂度: O(1) +class Solution { +public: + void moveZeroes(vector& nums) { + + int k = 0; // nums中, [0...k)的元素均为非0元素 + + // 遍历到第i个元素后,保证[0...i]中所有非0元素 + // 都按照顺序排列在[0...k)中 + // 同时, [k...i] 为 0 + for(int i = 0 ; i < nums.size() ; i ++) + if(nums[i]){ + if(k != i){ + swap(nums[k++] , nums[i]); + }else{ + k ++; + } + } + } +}; + +``` + + + +![](../../Pictures/qrcode.jpg) diff --git a/0290-Word-Pattern/Animation/1.mp4 b/0290-Word-Pattern/Animation/1.mp4 new file mode 100644 index 00000000..fb1f281f Binary files /dev/null and b/0290-Word-Pattern/Animation/1.mp4 differ diff --git a/0290-Word-Pattern/Animation/Animation.gif b/0290-Word-Pattern/Animation/Animation.gif new file mode 100644 index 00000000..ec08b4e1 Binary files /dev/null and b/0290-Word-Pattern/Animation/Animation.gif differ diff --git a/0290-Word-Pattern/Article/0290-Word-Pattern b/0290-Word-Pattern/Article/0290-Word-Pattern new file mode 100644 index 00000000..c2ec6948 --- /dev/null +++ b/0290-Word-Pattern/Article/0290-Word-Pattern @@ -0,0 +1,87 @@ +# LeetCode 第 290 号问题:单词规律 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 290 号问题:单词规律。题目难度为 Easy,目前通过率为 42.4% 。 + +#### 题目描述 + +> 给定一种规律 `pattern` 和一个字符串 `str` ,判断 `str` 是否遵循相同的规律。 +> +> 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。 +> +> 说明:你可以假设 `pattern` 只包含小写字母, `str` 包含了由单个空格分隔的小写字母。 + +```java +示例1: +输入: pattern = "abba", str = "dog cat cat dog" +输出: true +示例2: +输入:pattern = "abba", str = "dog cat cat fish" +输出: false +示例3: +输入: pattern = "aaaa", str = "dog cat cat dog" +输出: false +示例4: +输入: pattern = "abba", str = "dog dog dog dog" +输出: false +``` + +#### 题目解析 + +这道题目主要考察哈希表和字符串的内容。可以将题目拆解为下面三步: + +1. 设置`pattern`字符到单词(字符串 `str`)的**映射(哈希)**,使用`HashMap()`存储;使用`HashSet()` 记录被使用过的单词 。 +2. 若单词个数和`pattern`字符个数不匹配,返回false; +3. 遍历`pattern`,同时**对应的**向前移动 `str` 中单词的指针,每次拆分出`pattern`中的一个字符, **判断:** + + 如果该字符**从未出现**在哈希表中: + + 如果该字符对应的单词已被使用过 ,即`HashSet()`中包含该字符对应的单词,则返回false; + + 将该字符与其对应的单词**做映射**,加入哈希表中;**标记**该字符指向的单词**为已使用**,并加入`HashSet()`; + + 如果该字符在哈希表的**映射单词**与当前指向的单词不同,则返回false; + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 代码实现 + +Java语言 + +```java +class Solution { + public boolean wordPattern(String pattern, String str) { + HashMap map = new HashMap<>(); + HashSet set = new HashSet<>(); + String[] array = str.split(" "); + + if (pattern.length() != array.length) { + return false; + } + for (int i = 0; i < pattern.length(); i++) { + char key = pattern.charAt(i); + if (!map.containsKey(key)) { + if (set.contains(array[i])) { + return false; + } + map.put(key, array[i]); + set.add(array[i]); + } else { + if (!map.get(key).equals(array[i])) { + return false; + } + } + } + return true; + } +} +``` + +#### 复杂度分析 + ++ 时间复杂度:遍历`pattren` 一遍,时间复杂度O(n) ++ 空间复杂度:需要申请`HashMap()` 与 `HashSet()` ,还需要将字符串 `str` 分割后存储起来,空间复杂度O(n) + + + diff --git a/0290-Word-Pattern/Code/1.java b/0290-Word-Pattern/Code/1.java new file mode 100644 index 00000000..ee4780f1 --- /dev/null +++ b/0290-Word-Pattern/Code/1.java @@ -0,0 +1,26 @@ +class Solution { + public boolean wordPattern(String pattern, String str) { + HashMap map = new HashMap<>(); + HashSet set = new HashSet<>(); + String[] array = str.split(" "); + + if (pattern.length() != array.length) { + return false; + } + for (int i = 0; i < pattern.length(); i++) { + char key = pattern.charAt(i); + if (!map.containsKey(key)) { + if (set.contains(array[i])) { + return false; + } + map.put(key, array[i]); + set.add(array[i]); + } else { + if (!map.get(key).equals(array[i])) { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/0295-Find-Median-from-Data-Stream/Animation/Animation.gif b/0295-Find-Median-from-Data-Stream/Animation/Animation.gif new file mode 100644 index 00000000..24dbe5f0 Binary files /dev/null and b/0295-Find-Median-from-Data-Stream/Animation/Animation.gif differ diff --git a/0295-Find-Median-from-Data-Stream/Article/0295-Find-Median-from-Data-Stream.md b/0295-Find-Median-from-Data-Stream/Article/0295-Find-Median-from-Data-Stream.md new file mode 100644 index 00000000..141eaacf --- /dev/null +++ b/0295-Find-Median-from-Data-Stream/Article/0295-Find-Median-from-Data-Stream.md @@ -0,0 +1,90 @@ +# LeetCode 第 295 号问题:数据流的中位数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 295 号问题:数据流的中位数。难度级别为 Hard,目前通过率为 33.5% 。 + +### 题目描述 + +中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。 + +例如, + +[2,3,4] 的中位数是 3 + +[2,3] 的中位数是 (2 + 3) / 2 = 2.5 + +设计一个支持以下两种操作的数据结构: + +- void addNum(int num) - 从数据流中添加一个整数到数据结构中。 +- double findMedian() - 返回目前所有元素的中位数。 + +**示例:** + +```java +addNum(1) +addNum(2) +findMedian() -> 1.5 +addNum(3) +findMedian() -> 2 +``` + + + +### 题目解析 + +这道题给我们一个数据流,让我们找出中位数。对于数据流这种动态(流动)的数据,如果使用数组存储,那么每次新进来一个数据都进行排序的话,效率很低。 + +处理动态数据来说一般使用的数据结构是栈、队列、二叉树、堆。 + +本题中,我们使用 **堆** 这种数据结构。 + +首先将数据分为两部分,位于 **上边最大堆** 的数据要比 **下边最小堆** 的数据都要小。 + +为了保证将数据平均分配到两个堆中,在动态的操作的过程中两个堆中数据的数目之差不能超过 1。 + +为了保证 **最大堆中的所有数据都小于最小堆中的数据**,在操作过程中,新添加进去的数据需要先和最大堆的最大值或者最小堆中的最小值进行比较。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + + + +```java +class MedianFinder { + public PriorityQueue minheap, maxheap; + public MedianFinder() { + //维护较大的元素的最小堆 + maxheap = new PriorityQueue(Collections.reverseOrder()); + //维护较小元素的最大堆 + minheap = new PriorityQueue(); + } + + // Adds a number into the data structure. + public void addNum(int num) { + maxheap.add(num); + minheap.add(maxheap.poll()); + if (maxheap.size() < minheap.size()) { + maxheap.add(minheap.poll()); + } + } + + // Returns the median of current data stream + public double findMedian() { + if (maxheap.size() == minheap.size()) { + return (maxheap.peek() + minheap.peek()) * 0.5; + } else { + return maxheap.peek(); + } + } +}; +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0328-Odd-Even-Linked-List/Animation/Animation.gif b/0328-Odd-Even-Linked-List/Animation/Animation.gif new file mode 100644 index 00000000..1eaf1b9a Binary files /dev/null and b/0328-Odd-Even-Linked-List/Animation/Animation.gif differ diff --git a/0328-Odd-Even-Linked-List/Article/0328-Odd-Even-Linked-List.md b/0328-Odd-Even-Linked-List/Article/0328-Odd-Even-Linked-List.md new file mode 100644 index 00000000..2acbf0cd --- /dev/null +++ b/0328-Odd-Even-Linked-List/Article/0328-Odd-Even-Linked-List.md @@ -0,0 +1,85 @@ +# LeetCode 第 328 号问题:奇偶链表 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 328 号问题:奇偶链表。题目难度为 Medium,目前通过率为 52.0% 。 + +### 题目描述 + +给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。 + +请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。 + +**示例 1:** + +``` +输入: 1->2->3->4->5->NULL +输出: 1->3->5->2->4->NULL +``` + +**示例 2:** + +``` +输入: 2->1->3->5->6->4->7->NULL +输出: 2->3->6->7->1->5->4->NULL +``` + +**说明:** + +- 应当保持奇数节点和偶数节点的相对顺序。 +- 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。 + +### 题目解析 + +这道题给了我们一个链表,让我们分开奇偶节点,所有奇节点在前,偶节点在后。 + +* 设定两个虚拟节点,`dummyHead1 `用来保存奇节点,`dummyHead2 `来保存偶节点; +* 遍历整个原始链表,将奇节点放于`dummyHead1 `中,其余的放置在`dummyHead2 `中 +* 遍历结束后,将`dummyHead2 `插入到`dummyHead1 `后面 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { +public: + ListNode* oddEvenList(ListNode* head) { + + if(head == NULL || head->next == NULL || head->next->next == NULL) + return head; + + ListNode* dummyHead1 = new ListNode(-1); + ListNode* dummyHead2 = new ListNode(-1); + ListNode* p1 = dummyHead1; + ListNode* p2 = dummyHead2; + ListNode* p = head; + for(int i = 0; p; i ++) + if(i % 2 == 0){ + p1->next = p; + p = p->next; + p1 = p1->next; + p1->next = NULL; + } + else{ + p2->next = p; + p = p->next; + p2 = p2->next; + p2->next = NULL; + } + + p1->next = dummyHead2->next; + ListNode* ret = dummyHead1->next; + + delete dummyHead1; + delete dummyHead2; + return ret; + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0342-Power-Of-Four/Article/0342-Power-Of-Four.md b/0342-Power-Of-Four/Article/0342-Power-Of-Four.md new file mode 100644 index 00000000..8a821023 --- /dev/null +++ b/0342-Power-Of-Four/Article/0342-Power-Of-Four.md @@ -0,0 +1,112 @@ +# LeetCode 第 342 号问题:4 的幂 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 342 号问题:4 的幂。题目难度为 Easy,目前通过率为 45.3% 。 + +### 题目描述 + +给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。 + +**示例 1:** + +``` +输入: 16 +输出: true +``` + +**示例 2:** + +``` +输入: 5 +输出: false +``` + +**进阶:** +你能不使用循环或者递归来完成本题吗? + +### 题目解析 + +这道题最直接的方法就是不停的去除以 4 ,看最终结果是否为 1 ,参见代码如下: + +```java +class Solution { + public boolean isPowerOfFour(int num) { + while ( (num != 0) && (num % 4 == 0)) { + num /= 4; + } + return num == 1; + } +} +``` + +不过这段代码使用了 **循环** ,逼格不够高。 + +对于一个整数而言,如果这个数是 4 的幂次方,那它必定也是 2 的幂次方。 + +我们先将 2 的幂次方列出来找一下其中哪些数是 4 的幂次方。 + +| 十进制 | 二进制 | +| ------ | ------------------------------- | +| 2 | 10 | +| 4 | **100** (1 在第 3 位) | +| 8 | 1000 | +| 16 | **10000**(1 在第 5 位) | +| 32 | 100000 | +| 64 | **1000000**(1 在第 7 位) | +| 128 | 10000000 | +| 256 | **100000000**(1 在第 9 位) | +| 512 | 1000000000 | +| 1024 | **10000000000**(1 在第 11 位) | + +找一下规律: 4 的幂次方的数的二进制表示 1 的位置都是在**奇数位**。 + +之前在小吴的文章中判断一个是是否是 2 的幂次方数使用的是位运算 `n & ( n - 1 )`。同样的,这里依旧可以使用位运算:将这个数与特殊的数做位运算。 + +这个特殊的数有如下特点: + +* 足够大,但不能超过 32 位,即最大为 1111111111111111111111111111111( 31 个 1) + +* 它的二进制表示中奇数位为 1 ,偶数位为 0 + + 符合这两个条件的二进制数是: + +```java +1010101010101010101010101010101 +``` + +**如果用一个 4 的幂次方数和它做与运算,得到的还是 4 的幂次方数**。 + +将这个二进制数转换成 16 进制表示:0x55555555 。有没有感觉逼格更高点。。。 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/c0s9n.png) + + + +### 图片描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ux5pa.jpg) + +### 代码实现 + +```java +class Solution { + public boolean isPowerOfFour(int num) { + if (num <= 0) + return false; + //先判断是否是 2 的幂 + if ((num & num - 1) != 0) + return false; + //如果与运算之后是本身则是 4 的幂 + if ((num & 0x55555555) == num) + return true; + return false; + } +} +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0344-Reverse-String/Animation/Animation.gif b/0344-Reverse-String/Animation/Animation.gif new file mode 100644 index 00000000..9fe3af34 Binary files /dev/null and b/0344-Reverse-String/Animation/Animation.gif differ diff --git a/0344-Reverse-String/Article/0344-Reverse-String.md b/0344-Reverse-String/Article/0344-Reverse-String.md new file mode 100644 index 00000000..52139894 --- /dev/null +++ b/0344-Reverse-String/Article/0344-Reverse-String.md @@ -0,0 +1,56 @@ +# LeetCode 第 344 号问题:反转字符串 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 第 344 号问题:反转字符串。面试官最喜欢让你手写的一道算法题! + +### 题目描述 + +编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 `char[]` 的形式给出。 + +不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。 + +你可以假设数组中的所有字符都是 [ASCII](https://baike.baidu.com/item/ASCII) 码表中的可打印字符。 + +**示例 1:** + +``` +输入:["h","e","l","l","o"] +输出:["o","l","l","e","h"] +``` + +**示例 2:** + +``` +输入:["H","a","n","n","a","h"] +输出:["h","a","n","n","a","H"] +``` + +### 题目解析 + +这道题没什么难度,直接从两头往中间走,同时交换两边的字符。注意需要白板编程写出来即可,也注意千万别回答一句使用 reverse() 这种高级函数来解决。。。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { +public: + string reverseString(string s) { + int i = 0, j = s.size() - 1; + while (i < j){ + swap(s[i],s[j]); + i++; + j--; + } + return s; + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0347-Top-K-Frequent-Elements/Animation/Animation.gif b/0347-Top-K-Frequent-Elements/Animation/Animation.gif new file mode 100644 index 00000000..436c60ff Binary files /dev/null and b/0347-Top-K-Frequent-Elements/Animation/Animation.gif differ diff --git a/0347-Top-K-Frequent-Elements/Article/0347-Top-K-Frequent-Elements.md b/0347-Top-K-Frequent-Elements/Article/0347-Top-K-Frequent-Elements.md new file mode 100644 index 00000000..e304472b --- /dev/null +++ b/0347-Top-K-Frequent-Elements/Article/0347-Top-K-Frequent-Elements.md @@ -0,0 +1,169 @@ +# LeetCode 第 347 号问题:前 K 个高频元素 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +今天分享的题目来源于 LeetCode 上第 347 号问题:前 K 个高频元素。题目难度为 Medium,目前通过率为 56.9% 。 + +## 题目描述 + +给定一个非空的整数数组,**返回其中出现频率前 k 高**的元素。 + +**示例 1:** + +``` +输入: nums = [1,1,1,2,2,3], k = 2 +输出: [1,2] +``` + +**示例 2:** + +``` +输入: nums = [1], k = 1 +输出: [1] +``` + +**说明:** + +- 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。 +- 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。 + +### 题目解析 + +### 解法一:粗暴排序法 + +最简单粗暴的思路就是 **使用排序算法对元素按照频率由高到低进行排序**,然后再取前 k 个元素。 + +以下十种排序算法,任你挑选! + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/qya5e.png) + +可以发现,使用常规的诸如 冒泡、选择、甚至快速排序都是不满足题目要求,它们的时间复杂度都是大于或者等于 O(n log⁡n) ,而题目要求算法的时间复杂度必须优于 O(n log n) 。 + +#### 复杂度分析 + +- **时间复杂度**:O(nlogn),n 表示数组长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n);接着,排序算法时间复杂度为O(nlogn) ;因此整体时间复杂度为 O(nlogn) 。 +- **空间复杂度**:O(n),最极端的情况下(每个元素都不同),用于存储元素及其频率的 Map 需要存储 n 个键值对。 + +### 解法二:最小堆 + +题目最终需要返回的是前 k 个频率最大的元素,可以想到借助堆这种数据结构,对于 k 频率之后的元素不用再去处理,进一步优化时间复杂度。 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/nloow.jpg) + +具体操作为: + +- 借助 **哈希表** 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率 +- 维护一个元素数目为 k 的最小堆 +- 每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较 +- 如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中 +- 最终,堆中的 k 个元素即为前 k 个高频元素 + + + +### 动画理解 + +![](../Animation/Animation.gif) + +### 参考代码 + +```java +class Solution { + public List topKFrequent(int[] nums, int k) { + // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值 + HashMap map = new HashMap(); + for(int num : nums){ + if (map.containsKey(num)) { + map.put(num, map.get(num) + 1); + } else { + map.put(num, 1); + } + } + // 遍历map,用最小堆保存频率最大的k个元素 + PriorityQueue pq = new PriorityQueue<>(new Comparator() { + @Override + public int compare(Integer a, Integer b) { + return map.get(a) - map.get(b); + } + }); + for (Integer key : map.keySet()) { + if (pq.size() < k) { + pq.add(key); + } else if (map.get(key) > map.get(pq.peek())) { + pq.remove(); + pq.add(key); + } + } + // 取出最小堆中的元素 + List res = new ArrayList<>(); + while (!pq.isEmpty()) { + res.add(pq.remove()); + } + return res; + } +} + +``` + +#### 复杂度分析 + +- **时间复杂度**:O(nlogk), n 表示数组的长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n);接着,遍历用于存储元素频率的 map,如果元素的频率大于最小堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,**这里维护堆的数目是 k **,所以这一系列操作的时间复杂度是 O(nlogk)的;因此,总的时间复杂度是 O(nlog⁡k) 。 +- **空间复杂度**:O(n),最坏情况下(每个元素都不同),map 需要存储 n 个键值对,优先队列需要存储 k个元素,因此,空间复杂度是 O(n)。 + + + +### 解法三:桶排序法 + +首先依旧使用哈希表统计频率,统计完成后,创建一个数组,将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标即可。 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/6tge2.jpg) + +代码实现如下: + +```java +//基于桶排序求解「前 K 个高频元素」 +class Solution { + public List topKFrequent(int[] nums, int k) { + List res = new ArrayList(); + // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值 + HashMap map = new HashMap(); + for(int num : nums){ + if (map.containsKey(num)) { + map.put(num, map.get(num) + 1); + } else { + map.put(num, 1); + } + } + + //桶排序 + //将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标 + List[] list = new List[nums.length+1]; + for(int key : map.keySet()){ + // 获取出现的次数作为下标 + int i = map.get(key); + if(list[i] == null){ + list[i] = new ArrayList(); + } + list[i].add(key); + } + + // 倒序遍历数组获取出现顺序从大到小的排列 + for(int i = list.length - 1;i >= 0 && res.size() < k;i--){ + if(list[i] == null) continue; + res.addAll(list[i]); + } + return res; + } +} +``` + +#### 复杂度分析 + +- **时间复杂度**:O(n), n 表示数组的长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n);桶的数量为 n + 1,所以桶排序的时间复杂度为 O(n);因此,总的时间复杂度是 O(n)。 +- **空间复杂度**:很明显为 O(n) + + + +![](../../Pictures/qrcode.jpg) + diff --git a/0349-Intersection-of-Two-Arrays/Animation/Animation.gif b/0349-Intersection-of-Two-Arrays/Animation/Animation.gif new file mode 100644 index 00000000..1567ca80 Binary files /dev/null and b/0349-Intersection-of-Two-Arrays/Animation/Animation.gif differ diff --git a/0349-Intersection-of-Two-Arrays/Article/0349-Intersection-of-Two-Arrays.md b/0349-Intersection-of-Two-Arrays/Article/0349-Intersection-of-Two-Arrays.md new file mode 100644 index 00000000..faf83f4f --- /dev/null +++ b/0349-Intersection-of-Two-Arrays/Article/0349-Intersection-of-Two-Arrays.md @@ -0,0 +1,71 @@ +# LeetCode 第 349 号问题:两个数组的交集 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 349 号问题:两个数组的交集。题目难度为 Easy,目前通过率为 62.3% 。 + +### 题目描述 + +给定两个数组,编写一个函数来计算它们的交集。 + +**示例 1:** + +``` +输入: nums1 = [1,2,2,1], nums2 = [2,2] +输出: [2] +``` + +**示例 2:** + +``` +输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4] +输出: [9,4] +``` + +**说明:** + +- 输出结果中的每个元素一定是唯一的。 +- 我们可以不考虑输出结果的顺序。 + +### 题目解析 + +容器类 [set](https://zh.cppreference.com/w/cpp/container/set) 的使用。 + +- 遍历 num1,通过 set 容器 record 存储 num1 的元素 +- 遍历 num2 ,在 record 中查找是否有相同的元素,如果有,用 set 容器 resultSet 进行存储 +- 将 resultSet 转换为 vector 类型 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + set record; + for(int i = 0; i < nums1.size(); i ++){ + record.insert(nums1[i]); + } + + set resultSet; + for(int i = 0; i < nums2.size();i++){ + if(record.find(nums2[i]) != record.end()){ + resultSet.insert(nums2[i]); + } + } + + vector resultVector; + for(set::iterator iter=resultSet.begin(); iter != resultSet.end();iter++){ + resultVector.push_back(*iter); + } + return resultVector; + } +}; +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0350-Intersection-of-Two-Arrays-II/Animation/Animation.gif b/0350-Intersection-of-Two-Arrays-II/Animation/Animation.gif new file mode 100644 index 00000000..291398bb Binary files /dev/null and b/0350-Intersection-of-Two-Arrays-II/Animation/Animation.gif differ diff --git a/0350-Intersection-of-Two-Arrays-II/Article/0350-Intersection-of-Two-Arrays-II.md b/0350-Intersection-of-Two-Arrays-II/Article/0350-Intersection-of-Two-Arrays-II.md new file mode 100644 index 00000000..f1b8b6b9 --- /dev/null +++ b/0350-Intersection-of-Two-Arrays-II/Article/0350-Intersection-of-Two-Arrays-II.md @@ -0,0 +1,83 @@ +# LeetCode 第 350 号问题:两个数组的交集 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 350 号问题:两个数组的交集 II。题目难度为 Easy,目前通过率为 41.8% 。 + +### 题目描述 + +给定两个数组,编写一个函数来计算它们的交集。 + +**示例 1:** + +``` +输入: nums1 = [1,2,2,1], nums2 = [2,2] +输出: [2,2] +``` + +**示例 2:** + +``` +输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4] +输出: [4,9] +``` + +**说明:** + +- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。 +- 我们可以不考虑输出结果的顺序。 + +**进阶:** + +- 如果给定的数组已经排好序呢?你将如何优化你的算法? +- 如果 *nums1* 的大小比 *nums2* 小很多,哪种方法更优? +- 如果 *nums2* 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办? + +### 题目解析 + +容器类 [map](https://zh.cppreference.com/w/cpp/container/map) 的使用。 + +- 遍历 num1,通过map容器 record 存储 num1 的元素与频率 +- 遍历 num2 ,在 record 中查找是否有相同的元素(该元素的存储频率大于0),如果有,用map容器resultVector 进行存储,同时该元素的频率减一 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +// 350. Intersection of Two Arrays II +// https://leetcode.com/problems/intersection-of-two-arrays-ii/description/ +// 时间复杂度: O(nlogn) +// 空间复杂度: O(n) +class Solution { +public: + vector intersect(vector& nums1, vector& nums2) { + + map record; + for(int i = 0 ; i < nums1.size() ; i ++){ + record[nums1[i]] += 1; + } + + vector resultVector; + for(int i = 0 ; i < nums2.size() ; i ++){ + if(record[nums2[i]] > 0){ + resultVector.push_back(nums2[i]); + record[nums2[i]] --; + } + } + + return resultVector; + } +}; +``` + + + +![](../../Pictures/qrcode.jpg) + + + diff --git a/0387-First-Unique-Character-In-String/Animation/387.mp4 b/0387-First-Unique-Character-In-String/Animation/387.mp4 new file mode 100644 index 00000000..1ccfd95a Binary files /dev/null and b/0387-First-Unique-Character-In-String/Animation/387.mp4 differ diff --git a/0387-First-Unique-Character-In-String/Article/0387-First-Unique-Character-In-String.md b/0387-First-Unique-Character-In-String/Article/0387-First-Unique-Character-In-String.md new file mode 100644 index 00000000..8fb1cb6b --- /dev/null +++ b/0387-First-Unique-Character-In-String/Article/0387-First-Unique-Character-In-String.md @@ -0,0 +1,72 @@ +# 387. 字符串中的第一个唯一字符 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 387题,主要涉及哈希表。 + +## 题目 + +给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 + +案例: + +``` +s = "leetcode" +返回 0. + +s = "loveleetcode", +返回 2. +``` + +注意事项:您可以假定该字符串只包含小写字母。 + +## 题目解析 + +这道题不管怎么样都是要遍历一遍字符串才能保证字符是唯一,所以我们的算法如下 + +1. 遍历的时候把每个字符出现的次数用Map记录一下,如果这个字符是第一次出现,那么赋值为[i],如果它已经在Map里有了,那么我们给这个字符的值赋为false。 +2. 再次遍历Map,找到值不为false的第一个字符,然后将它的值输出来 +3. 如果值全部为false,然后返回-1 + + +## 动画理解 + + + + +## 参考代码 + + +```javaScript +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function(s) { + let map = new Map() + for (let i = 0; i < s.length;i++) { + if(map.has(s[i])) { + map.set(s[i], false) + }else { + map.set(s[i], [i]) + } + } + for(let item of map){ + if (item[1]) { + return item[1][0] + } +   } + return -1 +}; +``` + +## 复杂度分析 + +哈希表的时间复杂度是O(n) + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0394-Decode-String/Animation/1.mp4 b/0394-Decode-String/Animation/1.mp4 new file mode 100644 index 00000000..d0d8022c Binary files /dev/null and b/0394-Decode-String/Animation/1.mp4 differ diff --git a/0394-Decode-String/Animation/Animation.gif b/0394-Decode-String/Animation/Animation.gif new file mode 100644 index 00000000..0437424a Binary files /dev/null and b/0394-Decode-String/Animation/Animation.gif differ diff --git a/0394-Decode-String/Article/0394-Decode-String b/0394-Decode-String/Article/0394-Decode-String new file mode 100644 index 00000000..b4b0be00 --- /dev/null +++ b/0394-Decode-String/Article/0394-Decode-String @@ -0,0 +1,80 @@ +# LeetCode 第 394 号问题:字符串解码 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 394 号问题:字符串解码。题目难度为 Medium,目前通过率为 49.7% 。 + +#### 题目描述 + +> 给定一个经过编码的字符串,返回它解码后的字符串。 +> +> 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 +> +> 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。 +> +> 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。 +> + +```java +示例: + s = "3[a]2[bc]", 返回 "aaabcbc". + s = "3[a2[c]]", 返回 "accaccacc". + s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef". +``` + +#### 题目解析 + +由于中括号[]存在嵌套,因此需要**从内向外**生成并拼接字符串,这与栈的**先进后出**的特性相一致。我们可以增加一个辅助栈 **stack\_multi** 用于存储紧挨着左方括号[ 的倍数,每当遇到左方括号时,**res** 与 **multi** 的值进行入栈操作;当遇到右方括号时,进行出栈操作;当遇到数字时,更新 **multi** 的值;当遇到除方括号和数字以外的其他字符时,更新 **res** 的值。需要注意更新 **multi** 的值要考虑连续多位数字的情况。 + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 代码实现 + +```java +class Solution { + public String decodeString(String s) { + StringBuilder res = new StringBuilder(); + int multi = 0; + + Stack stack_multi = new Stack(); + Stack stack_res = new Stack(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ('[' == c){ + stack_multi.push(multi); + stack_res.push(res.toString()); + multi = 0; + res = new StringBuilder(); + } + else if (']' == c) { + StringBuilder tmp = new StringBuilder(); + int cur_multi = stack_multi.pop(); + for (int j = 0; j < cur_multi; j++){ + tmp.append(res); + } + res = new StringBuilder(stack_res.pop() + tmp); + } + else if(c >= '0' && c <= '9'){ + multi = multi * 10 + (c - '0'); + } + else{ + res.append(c); + } + } + return res.toString(); + } +} +``` + +#### 复杂度分析 + ++ 时间复杂度 *O*(*N*),一次遍历字符串`s`; ++ 空间复杂度 *O(N)*,辅助栈在极端情况下需要线性空间,例如 `4[3[2[LeetCodeAnimation]]]`。 + + + diff --git a/0394-Decode-String/Code/1.java b/0394-Decode-String/Code/1.java new file mode 100644 index 00000000..c5b26e1e --- /dev/null +++ b/0394-Decode-String/Code/1.java @@ -0,0 +1,34 @@ +class Solution { + public String decodeString(String s) { + StringBuilder res = new StringBuilder(); + int multi = 0; + + Stack stack_multi = new Stack(); + Stack stack_res = new Stack(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ('[' == c){ + stack_multi.push(multi); + stack_res.push(res.toString()); + multi = 0; + res = new StringBuilder(); + } + else if (']' == c) { + StringBuilder tmp = new StringBuilder(); + int cur_multi = stack_multi.pop(); + for (int j = 0; j < cur_multi; j++){ + tmp.append(res); + } + res = new StringBuilder(stack_res.pop() + tmp); + } + else if(c >= '0' && c <= '9'){ + multi = multi * 10 + (c - '0'); + } + else{ + res.append(c); + } + } + return res.toString(); + } +} \ No newline at end of file diff --git a/0407-Trapping-Rain-Water-II/Animation/407.gif b/0407-Trapping-Rain-Water-II/Animation/407.gif new file mode 100644 index 00000000..7508c11f Binary files /dev/null and b/0407-Trapping-Rain-Water-II/Animation/407.gif differ diff --git a/0407-Trapping-Rain-Water-II/Animation/407.m4v b/0407-Trapping-Rain-Water-II/Animation/407.m4v new file mode 100644 index 00000000..f260d312 Binary files /dev/null and b/0407-Trapping-Rain-Water-II/Animation/407.m4v differ diff --git a/0407-Trapping-Rain-Water-II/Animation/example.png b/0407-Trapping-Rain-Water-II/Animation/example.png new file mode 100644 index 00000000..0ee7a725 Binary files /dev/null and b/0407-Trapping-Rain-Water-II/Animation/example.png differ diff --git a/0407-Trapping-Rain-Water-II/Article/0407-Trapping-Rain-Water-II.md b/0407-Trapping-Rain-Water-II/Article/0407-Trapping-Rain-Water-II.md new file mode 100644 index 00000000..93b92fdd --- /dev/null +++ b/0407-Trapping-Rain-Water-II/Article/0407-Trapping-Rain-Water-II.md @@ -0,0 +1,127 @@ +# LeetCode 第 407 号问题:接雨水 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 407 号问题:接雨水 II。题目难度为 Hard,目前通过率为 38% 。 + +### 题目描述 + +给你一个 m x n 的矩阵,其中的值均为正整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。 + +**示例:** + +``` +给出如下 3x6 的高度图: +[ + [1,4,3,1,3,2], + [3,2,1,3,2,4], + [2,3,3,2,3,1] +] + +返回 4 。 +``` + +![](../Animation/example.png) + +### 题目解析 + +在 1 个 2 维的矩阵中,每个格子都有其高度,问这个 2 维矩阵能够盛多少的水。首先我们分析,格子能够盛水的必要条件是其周围存在格子比当前格子高,这样水才能够被框得住,但是仔细一想,最外围的格子怎么办?它们是存不了水的,可以把最外围的格子想象成围栏,它们的作用就是保证里面格子的水不会流出来,所以我们就得先考虑这些格子,它们的高度直接决定了内部格子的蓄水量,但是这些格子也有局部性,一个格子的长短并不会影响矩阵当中所有的格子,但是它会影响与其相邻的格子,那么我们就需要有一个考虑的顺序,那就是优先考虑最外层最短的格子,由于每个格子都会影响到其周围的格子,内部格子也需要列入考虑范围,每次我们都考虑最短的格子,然后看其周围有没有没考虑过的比它还短的格子,于是就有了考虑的先后顺序: + +1. 考虑最外层格子 +2. 选出最外层最短的格子 +3. 考虑该格子与其相邻的内部格子是否能盛水,并把这个内部格子也纳入考虑范围 +4. 在考虑范围内的所有格子中选出最短的格子,重复步骤 3 + +这里需要注意的是,每次纳入考虑范围的格子是加了水之后的高度,而不是之前的高度,原因想一下应该不难理解。另外就是可以使用了 “堆” 这个数据结构来帮助实现寻找 “当前考虑范围内最短的格子” 这个操作步骤。 + +### 动画描述 + +![](../Animation/407.gif) + +### 代码实现 + +```java +private class Pair { + int x, y, h; + Pair(int x, int y, int h) { + this.x = x; + this.y = y; + this.h = h; + } +} + +private int[] dirX = {0, 0, -1, 1}; +private int[] dirY = {-1, 1, 0, 0}; + +public int trapRainWater(int[][] heightMap) { + if (heightMap.length == 0 || heightMap[0].length == 0) { + return 0; + } + + int m = heightMap.length; + int n = heightMap[0].length; + + PriorityQueue pq = new PriorityQueue<>(new Comparator() { + @Override + public int compare(Pair a, Pair b) { + return a.h - b.h; + } + }); + + boolean[][] visited = new boolean[m][n]; + + // 优先将外围的元素加入队列中 + for (int i = 0; i < n; ++i) { + pq.offer(new Pair(0, i, heightMap[0][i])); + pq.offer(new Pair(m - 1, i, heightMap[m - 1][i])); + + visited[0][i] = true; + visited[m - 1][i] = true; + } + + for (int i = 1; i < m - 1; ++i) { + pq.offer(new Pair(i, 0, heightMap[i][0])); + pq.offer(new Pair(i, n - 1, heightMap[i][n - 1])); + + visited[i][0] = true; + visited[i][n - 1] = true; + } + + int result = 0; + while (!pq.isEmpty()) { + Pair cur = pq.poll(); + + // 遍历当前位置上下左右四个方向 + for (int k = 0; k < 4; ++k) { + int curX = cur.x + dirX[k]; + int curY = cur.y + dirY[k]; + + if (curX < 0 || curY < 0 || curX >= m || curY >= n || visited[curX][curY]) { + continue; + } + + if (heightMap[curX][curY] < cur.h) { + result += cur.h - heightMap[curX][curY]; + } + + pq.offer(new Pair(curX, curY, + Math.max(heightMap[curX][curY], cur.h))); + visited[curX][curY] = true; + } + } + + return result; +} +``` + +
+ +### 复杂度分析 + +因为使用了优先队列这个数据结构,每次元素出入队列的时间复杂度是 O(logn),于是我们可以得出整体时间复杂度是 `O(m*n*logm*n)`,当然,需要说明的是,这是最差时间复杂度,由于并不是所有的元素都一次性加入队列,平均时间复杂度要比这个来的低,具体是什么就得看输入数据了。空间复杂度是 `O(m*n)`,这里也不难理解。通过这道题,堆的用法又被很好地展现了出来。 + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0445-Add-Two-Numbers-II/Animation/Animation.gif b/0445-Add-Two-Numbers-II/Animation/Animation.gif new file mode 100644 index 00000000..cbdfd03d Binary files /dev/null and b/0445-Add-Two-Numbers-II/Animation/Animation.gif differ diff --git a/0445-Add-Two-Numbers-II/Animation/Animation01.gif b/0445-Add-Two-Numbers-II/Animation/Animation01.gif new file mode 100644 index 00000000..e363c321 Binary files /dev/null and b/0445-Add-Two-Numbers-II/Animation/Animation01.gif differ diff --git a/0445-Add-Two-Numbers-II/Animation/Animation02.gif b/0445-Add-Two-Numbers-II/Animation/Animation02.gif new file mode 100644 index 00000000..eabad440 Binary files /dev/null and b/0445-Add-Two-Numbers-II/Animation/Animation02.gif differ diff --git a/0445-Add-Two-Numbers-II/Article/0445-Add-Two-Numbers-II-02.md b/0445-Add-Two-Numbers-II/Article/0445-Add-Two-Numbers-II-02.md new file mode 100644 index 00000000..39dacf86 --- /dev/null +++ b/0445-Add-Two-Numbers-II/Article/0445-Add-Two-Numbers-II-02.md @@ -0,0 +1,128 @@ +# LeetCode 第 445 号问题:两数相加 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步个人博客:www.zhangxiaoshuai.fun + + +```txt +难度:medium 目前通过率:57.2% +题目描述: + 给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。 + 它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 + 你可以假设除了数字0之外,这两个数字都不会以零开头。 + +示例: + 输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) + 输出: 7 -> 8 -> 0 -> 7 +``` + + + +显然,给定的两个“数字”都是**从左到右按照高位到低位的顺序**排列的,按照习惯我们需要将链表反转之后然后相加,再将结果链表反转之后返回。 + +那么顺序的问题解决了,还存在一个问题: + +**两个一位数相加是有可能产生一个两位数的,而我们的节点中是不能存储一个两位数字的,如果产生进位那我们需要将进位保存下来,然后将进位加到后面的两个数字相加的结果中。** + +**下面是GIF图解:** + +![解法1](../Animation/Animation01.gif) + +**那么我们先来试试写出这个版本的代码:** + +因为在整个计算中,链表反转的这个功能用到了三次,那我们可以将这个功能单独的抽取出来写一个方法。(方法比较简单,直接附上代码,不再赘述) + +```java +//链表反转 +private ListNode reverseListNode(ListNode head){ + if(head == null) return null; + ListNode prev = null; + while (head != null) { + ListNode next = head.next; + head.next = prev; + prev = head; + head = next; + } + return prev; +} +``` + +要返回两条链表相加的结果,我们需要新的链表来存储这个结果,dummy节点的值随意,因为我们最终返回的是连接在它后面的聊链表。 + +我们需要初始化**addOne**(代表进位)为0 + +```java +//两条链表中的各个节点数字相加 +private ListNode add(ListNode l1, ListNode l2){ + if (l1 == null && l2 == null) return null; + ListNode dummy = new ListNode(0); + ListNode temp = dummy; + int addOne = 0; + while (l1 != null || l2 != null || addOne != 0) { + int val1 = l1 == null ? 0 : l1.val; + int val2 = l2 == null ? 0 : l2.val; + int sum = val1 + val2 + addOne; + temp.next = new ListNode(sum%10); + temp = temp.next; + addOne = sum / 10; + if (l1 != null) l1 = l1.next; + if (l2 != null) l2 = l2.next; + } + return dummy.next; +} +``` + +ok,那我们现在只需要在指定的方法中调用这两个方法并传递相应的参数即可得到我们想要的。 + +```java +public static ListNode addTwoNumbers(ListNode l1, ListNode l2){ + ListNode node1 = reverseListNode(l1); + ListNode node2 = reverseListNode(l2); + return reverseListNode(add(node1,node2)); +} +``` + +如果你觉得上面的解法太过简单,那一起来看看进阶版本吧: + +**如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。** +那我们上面的使用的“反转链表”的方法就无法再次使用了。 +首先我们应该明确“反转链表”方法的出现是为了解决链表中“数字”的顺序问题,那么如果要得到正确的数字顺序只有这一中方法吗? +我们还有可以依靠的数据结构:**栈**。栈的特点就是“**先进后出**” + +**算法思路:**先将两条链表的各个数字分别压入两个栈中,然后每次弹出两个栈顶的数字进行相加,第一次相加的结点指向null结点,每次更新头节点,后面的结点依次指向新链表的头节点,那么最终我们可以得到与上面解法相同的结果。 + +**解法2图解:** + +![解法2](../Animation/Animation02.gif) + +**代码:** + +```java +public static ListNode addTwoNumbers(ListNode l1, ListNode l2){ + Stack s1 = new Stack<>(); + Stack s2 = new Stack<>(); + while (l1 != null) { + s1.push(l1.val); + l1 = l1.next; + } + while (l2 != null) { + s2.push(l2.val); + l2 = l2.next; + } + int addOne = 0;//进位 + ListNode head = null; + while (!s1.isEmpty() || !s2.isEmpty() || addOne != 0) { + int sum = addOne; + sum += s1.isEmpty() ? 0 : s1.pop(); + sum += s2.isEmpty() ? 0 : s2.pop(); + ListNode temp = new ListNode(sum%10); + temp.next = head; + head = temp; + addOne = sum/10; + } + return head; + } +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0445-Add-Two-Numbers-II/Article/0445-Add-Two-Numbers-II.md b/0445-Add-Two-Numbers-II/Article/0445-Add-Two-Numbers-II.md new file mode 100644 index 00000000..9b8fe73d --- /dev/null +++ b/0445-Add-Two-Numbers-II/Article/0445-Add-Two-Numbers-II.md @@ -0,0 +1,69 @@ +# LeetCode 第 445 号问题:两数相加 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 445 号问题:两数相加 II。题目难度为 Medium,目前通过率为 48.8% 。 + +### 题目描述 + +给定两个**非空**链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。 + + + +你可以假设除了数字 0 之外,这两个数字都不会以零开头。 + +**进阶:** + +如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。 + +**示例:** + +``` +输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) +输出: 7 -> 8 -> 0 -> 7 +``` + +### 题目解析 + +由于计算时要保证最右边的数对齐,那么很自然的想到先用**栈**存放链表中的每个值,然后依次计算。由于相加时可能产生进位,所以使用一个flag表示是否有进位。 + +提示:若栈中元素相加结束之后仍有进位,则需要新加入一个头结点。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +```python +class Solution: + def addTwoNumbers(self, l1, l2): + # 分别入栈 + stack1 = [] + stack2 = [] + while l1: + stack1.append(l1.val) + l1 = l1.next + while l2: + stack2.append(l2.val) + l2 = l2.next + + flag = 0 + head = None + while stack1 or stack2 or flag != 0: + if stack1: + flag += stack1.pop() + if stack2: + flag += stack2.pop() + node = ListNode(flag % 10) + node.next = head + head = node + flag = flag // 10 + return head +``` + + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0447-Number-of-Boomerangs/Animation/Animation.gif b/0447-Number-of-Boomerangs/Animation/Animation.gif new file mode 100644 index 00000000..772b1bc5 Binary files /dev/null and b/0447-Number-of-Boomerangs/Animation/Animation.gif differ diff --git a/0447-Number-of-Boomerangs/Article/0447-Number-of-Boomerangs.md b/0447-Number-of-Boomerangs/Article/0447-Number-of-Boomerangs.md new file mode 100644 index 00000000..b1f34a44 --- /dev/null +++ b/0447-Number-of-Boomerangs/Article/0447-Number-of-Boomerangs.md @@ -0,0 +1,97 @@ +# LeetCode第447号问题:回旋镖的数量 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 447 号问题:回旋镖的数量。题目难度为 Easy,目前通过率为 45.8% 。 + +### 题目描述 + +给定平面上 *n* 对不同的点,“回旋镖” 是由点表示的元组 `(i, j, k)` ,其中 `i` 和 `j` 之间的距离和 `i` 和 `k` 之间的距离相等(**需要考虑元组的顺序**)。 + +找到所有回旋镖的数量。你可以假设 *n* 最大为 **500**,所有点的坐标在闭区间 **[-10000, 10000]** 中。 + +**示例:** + +``` +输入: +[[0,0],[1,0],[2,0]] + +输出: +2 + +解释: +两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]] +``` + +### 题目解析 + +n 最大为 500,可以使用时间复杂度为 O(n^2)的算法。 + +- 遍历所有的点,让每个点作为一个锚点 +- 然后再遍历其他的点,统计和锚点距离相等的点有多少个 +- 然后分别带入 n(n-1) 计算结果并累加到res中 + +##### Tips: + +###### Tip1 + +- 如果有一个点 a,还有两个点 b 和 c ,如果 ab 和 ac 之间的距离相等,那么就有两种排列方法 abc 和 acb; +- 如果有三个点 b,c,d都分别和a之间的距离相等,那么有六种排列方法,abc, acb, acd, adc, abd, adb; +- 如果有 n 个点和点 a 距离相等,那么排列方式为 n(n-1)。 + +###### Tip2 + +- 计算距离时不进行开根运算, 以保证精度; +- 只有当n大于等于2时,res值才会真正增加,因为当n=1时,增加量为`1*(1-1)=0`。 + + + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +// 447. Number of Boomerangs +// https://leetcode.com/problems/number-of-boomerangs/description/ +// 时间复杂度: O(n^2) +// 空间复杂度: O(n) +class Solution { +public: + int numberOfBoomerangs(vector>& points) { + + int res = 0; + for( int i = 0 ; i < points.size() ; i ++ ){ + + // record中存储 点i 到所有其他点的距离出现的频次 + unordered_map record; + for(int j = 0 ; j < points.size() ; j ++){ + if(j != i){ + // 计算距离时不进行开根运算, 以保证精度 + record[dis(points[i], points[j])] += 1; + } + } + + for(unordered_map::iterator iter = record.begin() ; iter != record.end() ; iter ++){ + res += (iter->second) * (iter->second - 1); + } + } + return res; + } + +private: + int dis(const pair &pa, const pair &pb){ + return (pa.first - pb.first) * (pa.first - pb.first) + + (pa.second - pb.second) * (pa.second - pb.second); + } +}; + + +``` + + + +![](../../Pictures/qrcode.jpg) diff --git a/0454-4Sum-II/Animation/Animation.gif b/0454-4Sum-II/Animation/Animation.gif new file mode 100644 index 00000000..b4784ad8 Binary files /dev/null and b/0454-4Sum-II/Animation/Animation.gif differ diff --git a/0454-4Sum-II/Article/0454-4Sum-II.md b/0454-4Sum-II/Article/0454-4Sum-II.md new file mode 100644 index 00000000..b743b41e --- /dev/null +++ b/0454-4Sum-II/Article/0454-4Sum-II.md @@ -0,0 +1,79 @@ +# LeetCode 第 454 号问题:四数相加 II + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 454 号问题:四数相加 II。题目难度为 Medium,目前通过率为 50.8% 。 + +### 题目描述 + +给定四个包含整数的数组列表 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 +2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0 +``` + +### 题目解析 + +与[Two Sum](https://xiaozhuanlan.com/topic/7923618450)类似,需要用哈希表来解决问题。 + +- 把 A 和 B 的两两之和都求出来,在哈希表中建立两数之和与其出现次数之间的映射 +- 遍历 C 和 D 中任意两个数之和,只要看哈希表存不存在这两数之和的相反数就行了 + + + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +``` +// 454. 4Sum II +// https://leetcode.com/problems/4sum-ii/description/ +// 时间复杂度: O(n^2) +// 空间复杂度: O(n^2) +class Solution { +public: + int fourSumCount(vector& A, vector& B, vector& C, vector& D) { + + unordered_map hashtable; + for(int i = 0 ; i < A.size() ; i ++){ + for(int j = 0 ; j < B.size() ; j ++){ + hashtable[A[i]+B[j]] += 1; + } + } + + int res = 0; + for(int i = 0 ; i < C.size() ; i ++){ + for(int j = 0 ; j < D.size() ; j ++){ + if(hashtable.find(-C[i]-D[j]) != hashtable.end()){ + res += hashtable[-C[i]-D[j]]; + } + } + } + + return res; + } +}; + +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0461-hamming-distance/Animation/Animation.mp4 b/0461-hamming-distance/Animation/Animation.mp4 new file mode 100644 index 00000000..7812753a Binary files /dev/null and b/0461-hamming-distance/Animation/Animation.mp4 differ diff --git a/0461-hamming-distance/Article/0461-hamming-distance.md b/0461-hamming-distance/Article/0461-hamming-distance.md new file mode 100644 index 00000000..f64ffca4 --- /dev/null +++ b/0461-hamming-distance/Article/0461-hamming-distance.md @@ -0,0 +1,172 @@ +### 题目描述 + +两个整数之间的[汉明距离](https://baike.baidu.com/item/汉明距离)指的是这两个数字对应二进制位不同的位置的数目。 + +给出两个整数 `x` 和 `y`,计算它们之间的汉明距离。 + +示例 : + +``` +输入: x = 1, y = 4 + +输出: 2 + +解释: +1 (0 0 0 1) +4 (0 1 0 0) + ↑ ↑ +``` + +### 题目解析 + +首先通过 异或 操作找出两个数字对应位不同的位置,然后统计这些位置的个数。 + +统计解法借鉴Java中Integer.bitCount()方法源码来进行讲解,通过固定步数得到异或后1的个数。 + +第一步:将奇数位与偶数位相加,可以得出每两位1的个数,并将个数记录在这两位空间中 + +> i = i - (( i >>> 1 ) & 0x55555555 ) +> +> ``` +> 0x55555555 => 01 01 01 01 ... 01 01 +> i & 0x55555555 取出奇数位的1 +> (i >>> 1) & 0x55555555 取出偶数位的1 +> 比如,两位的情况下总共就四种情况:00 11 01 10 +> 假设 i = 00 11 01 10 +> i & 0x55555555 = 00 11 01 10 +> 01 01 01 01 +> ----------- +> 00 01 01 00 +> (i >>> 1) & 0x55555555 = 00 01 10 11 +> 01 01 01 01 +> ----------- +> 00 01 00 01 +> 将奇数位1的个数与偶数位的1求和: +> 00 01 01 00 +> 00 01 00 01 +> ----------- +> 00 10 01 01 +> 结合原数字可以看出,00(00:没有1) 11(10:两个1) 01(01:1个1) 10(01:1个1) +> +> 每两位在通过加法统计时,总共如下四种情况[i & 01 + (i>>>1) & 01]: +> 11: 01 + 01 = 10 = 2, 10: 00 + 01 = 01 = 1, 01: 01 + 00 = 01 = 1, 00: 00 + 00 = 00 = 0 +> 每两位在通过减法统计时,总共如下四种情况[i - (i>>>1) & 01]: +> 11: 11 - 01 = 10 = 2, 10: 10 - 01 = 01 = 1, 01: 01 - 00 = 01 = 1, 00: 00 + 00 = 00 = 0 +> 可以发现结果是一样的,但是少了一次位运算! +> +> 在将每两位1的个数统计完之后,就可以开始两位两位、四位四位...相加求出1的总数 +> ``` + +第二步:通过相邻两位1的个数相加,求出每四位包含1的个数,并将结果存储在所在的四位中 + +> i = ( i & 0x33333333 ) + (( i >>> 2 ) & 0x33333333 ) +> +> ``` +> 0x55555555 => 0011 0011 0011 ... 0011 0011 +> 继续上一步的结果向下进行:00 10 01 01 +> i & 0x33333333 = 0010 0101 +> 0011 0011 +> --------- +> 0010 0001 +> (i >>> 2) & 0x33333333 = 0000 1001 +> 0011 0011 +> --------- +> 0000 0001 +> +> 就和得出每四位所包含1的个数 +> 0010 0001 +> 0000 0001 +> --------- +> 0010 0010 +> 结合原数字可以看出,0011(0010:有两个1) 0110(0010:有两个1) +> ``` + +第三步:通过相邻四位1的个数相加,求出每八位包含1的个数,并将结果存储在所在的八位中 + +>i = ( i + ( i >>> 4 )) & 0x0f0f0f0f; +> +>``` +>0x0f0f0f0f => 00001111 ... 00001111‬ +>继续上一步的结果向下进行:0010 0010 +>i & 0x0f0f0f0f = 00100010 +> 00001111 +> -------- +> 00000010 +>(i >>> 4) & 0x0f0f0f0f = 00000010 +> 00001111 +> -------- +> 00000010 +>就和得出每八位所包含1的个数 +>00000010 +>00000010 +>-------- +>00000100 +>结合原数字可以看出,00110110(00000100:有四个1) +> +>源码中直接先将相邻四位进行相加,然后做了一次无用位清除 +>``` + +第四步:通过相邻八位1的个数相加,求出每十六位包含1的个数,并将结果存储在所在的十六位中 + +> i = i + ( i >>> 8 ); +> +> ``` +> 可以理解为( i & 0x0f0f0f0f ) + (( i >>> 8 ) & 0x0f0f0f0f ); +> +> 0x0f0f0f0f => 00000000111111110000000011111111 +> ``` + +第五步:通过将int类型前十六位1的个数与后16位1的个数相加,求出int中所有1的个数 + +> i = i + ( i >>> 16 ); +> +> ``` +> 可以理解为( i & 0x0000ffff ) + (( i >>> 8 ) & 0x0000ffff ); +> +> 0x0000ffff => 00000000000000001111111111111111‬ +> ``` + +第六步:去除无用的位 + +> return i & 0x3f; +> +> ``` +> int类型32位,即最多0x100000个1,除此之外左边的位都是无用的。 +> 0x3f => 00111111‬ +> ``` + +### 动画理解 + +![](../Animation/Animation.mp4) + + +‎⁨ + +### 参考代码 + +```java +class Solution { + public int hammingDistance(int x, int y) { + return Integer.bitCount(x ^ y); + } +} +``` + +bitCount源码: + +```java +public static int bitCount(int i) { + i = i - ((i >>> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); + i = (i + (i >>> 4)) & 0x0f0f0f0f; + i = i + (i >>> 8); + i = i + (i >>> 16); + return i & 0x3f; +} +``` + +### 复杂度分析 + +时间复杂度:O(1) + +空间复杂度:O(1) \ No newline at end of file diff --git a/0477-total-hamming-distance/Animation/Animation.mp4 b/0477-total-hamming-distance/Animation/Animation.mp4 new file mode 100755 index 00000000..cc44c406 Binary files /dev/null and b/0477-total-hamming-distance/Animation/Animation.mp4 differ diff --git a/0477-total-hamming-distance/Article/0477-total-hamming-distance.md b/0477-total-hamming-distance/Article/0477-total-hamming-distance.md new file mode 100755 index 00000000..c7f80c68 --- /dev/null +++ b/0477-total-hamming-distance/Article/0477-total-hamming-distance.md @@ -0,0 +1,102 @@ +### 题目描述 + +两个整数的 [汉明距离](https://baike.baidu.com/item/汉明距离/475174?fr=aladdin) 指的是这两个数字的二进制数对应位不同的数量。 + +计算一个数组中,任意两个数之间汉明距离的总和。 + +示例 : + +``` +输入: 4, 14, 2 + +输出: 6 + +解释: 在二进制表示中,4表示为0100,14表示为1110,2表示为0010。(这样表示是为了体现后四位之间关系) +所以答案为: +HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. +``` + +**注意:** + +1. 数组中元素的范围为从 `0`到 `10^9`。 +2. 数组的长度不超过 `10^4`。 + +### 题目解析 + +已示例为例,两两暴力计算的时间复杂度为o(n^2),实现上肯定是没有问题,但是当数据量大的时候性能堪忧。 + +我们先将数组与结果的数字二进制写出来 + +``` + 4 0 1 0 0 +14 1 1 1 0 + 2 0 0 1 0 +HammingDistance(4, 14) = 1 0 1 0 +HammingDistance(4, 2) = 0 1 1 0 +HammingDistance(14, 2) = 1 1 0 0 +``` + +结合结果,从左往右按列观察这三个数字的二进制与运算结果的二进制可以发现一种关系: + +数字个数 Count = 3 + +第一列: 0 1 0 ==> 1 * (3 -1) = 2 = 1 0 1 + +> 本列只有1个1,说明在所有数字的第一位中,有(Count - 1)个数字的第一位与 **本数字** 不同,也就是求距离的时候结果为1, 即这一位产生1的个数为1 * (3 -1) + +第二列: 1 1 0 ==> 2 * (3 -2) = 2 = 0 1 1 + +> 本列有2个1,说明在所有数字的第二位中,有(Count - 2)个数字的第二位与这 **两个数字** 不同,即这一位产生1的个数为(Count - 2)+ (Count - 2)= 2 *(3 - 2) + +第三列同第二列 + +第四列: 0 0 0 ==> 0 * (3 -0) = 0 = 0 0 0 + +> 本列所有数字相同,求距离时也就不会产生1, 结果为0 +> +> 如果是 1 1 1也一样,3 * (3 - 3), 结果依旧为0 + +总结 :每一列求距离产生1的个数 = 本列 1 的个数 * (数字个数 – 本列1的个数)= 本列 1 的个数 * 本列 0 的个数 + +### 动画理解 + +![](../Animation/Animation.mp4) + + + + +‎ + +### 参考代码 + +```java +class Solution { + public int totalHammingDistance(int[] nums) { + int len=nums.length; + int[] bitCount = new int[32]; + if(len <= 1){ + return 0; + } + for(int numIndex = 0; numIndex < len; numIndex++){ + for(int bitIndex = 0; bitIndex < 32; bitIndex++){ + bitCount[bitIndex] += nums[numIndex] & 1; + nums[numIndex] = nums[numIndex] >> 1; + if(nums[numIndex] == 0){ + break; + } + } + } + int oneCount = 0; + for(int bitIndex = 0; bitIndex < 32; bitIndex++){ + oneCount += bitCount[bitIndex] * (len - bitCount[bitIndex]); + } + return oneCount; + } +} +``` + +### 复杂度分析 + +时间复杂度:时间复杂度:O(N log ⁡C) 其中 C是常数,表示数组中数可能的最大值。 + +空间复杂度:O(log C) \ No newline at end of file diff --git a/0530-minimum-absolute-difference-in-bst/Animation/0530.m4v b/0530-minimum-absolute-difference-in-bst/Animation/0530.m4v new file mode 100644 index 00000000..e3405417 Binary files /dev/null and b/0530-minimum-absolute-difference-in-bst/Animation/0530.m4v differ diff --git a/0530-minimum-absolute-difference-in-bst/Article/0530-minimum-absolute-difference-in-bst b/0530-minimum-absolute-difference-in-bst/Article/0530-minimum-absolute-difference-in-bst new file mode 100644 index 00000000..ddaea72a --- /dev/null +++ b/0530-minimum-absolute-difference-in-bst/Article/0530-minimum-absolute-difference-in-bst @@ -0,0 +1,95 @@ +# 530. 二叉搜索树的最小绝对差 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 530. 二叉搜索树的最小绝对差. 是关于树的一道题。 + +## 题目 + +给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。 +  +示例: + +``` +输入: + + 1 + \ + 3 + / + 2 + +输出: +1 + +解释: +最小绝对差为 1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。 +``` + +提示: + +树中至少有 2 个节点。 +本题与 783 https://leetcode-cn.com/problems/minimum-distance-between-bst-nodes/ 相同 + + + +## 题目解析 + +计算树中任意两节点的差的绝对值的最小值,那么肯定是要遍历树,然后相邻节点求差对比是不是最小的。 +二叉树的遍历有三种,前序遍历,中序遍历,后序遍历。 + +题目中给的是二叉搜索树,二叉搜索树有一个特色,就是中序遍历出来的结果,值是按照从小到大排列的。 + +所以我们只要中序遍历,保存上一个节点,然后遍历的时候取得当前节点和上一个节点的值的绝对值,如果比当前最小差还要小,那么更新最小差。 + +中序遍历是遍历左子树,然后根节点,最后是右子树,我们用递归去实现。 + +## 动画理解 + + + + +## 参考代码 + + +```javaScript +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var getMinimumDifference = function(root) { + let min = Number.MAX_VALUE + let preNode = null + var travelTree = function (node) { + if (node) { + travelTree(node.left) + if(preNode) { + min = Math.min(min, Math.abs(preNode.val - node.val)) + } + preNode = node + travelTree(node.right) + } + } + travelTree(root) + return min +}; +``` + +## 复杂度分析 + +时间复杂度:O(N),N为树中节点个数。 + +空间复杂度:O(log(N))。 + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/0540-Single-Element-in-a-Sorted-Array/Animation/1.m4v b/0540-Single-Element-in-a-Sorted-Array/Animation/1.m4v new file mode 100644 index 00000000..6efeb4d1 Binary files /dev/null and b/0540-Single-Element-in-a-Sorted-Array/Animation/1.m4v differ diff --git a/0540-Single-Element-in-a-Sorted-Array/Animation/2.gif b/0540-Single-Element-in-a-Sorted-Array/Animation/2.gif new file mode 100644 index 00000000..abfbed2b Binary files /dev/null and b/0540-Single-Element-in-a-Sorted-Array/Animation/2.gif differ diff --git a/0540-Single-Element-in-a-Sorted-Array/Animation/2.m4v b/0540-Single-Element-in-a-Sorted-Array/Animation/2.m4v new file mode 100644 index 00000000..c0cc627a Binary files /dev/null and b/0540-Single-Element-in-a-Sorted-Array/Animation/2.m4v differ diff --git a/0540-Single-Element-in-a-Sorted-Array/Animation/Animation.gif b/0540-Single-Element-in-a-Sorted-Array/Animation/Animation.gif new file mode 100644 index 00000000..fde75cbe Binary files /dev/null and b/0540-Single-Element-in-a-Sorted-Array/Animation/Animation.gif differ diff --git a/0540-Single-Element-in-a-Sorted-Array/Article/0540-Single-Element-in-a-Sorted-Array.md b/0540-Single-Element-in-a-Sorted-Array/Article/0540-Single-Element-in-a-Sorted-Array.md new file mode 100644 index 00000000..505a2ddb --- /dev/null +++ b/0540-Single-Element-in-a-Sorted-Array/Article/0540-Single-Element-in-a-Sorted-Array.md @@ -0,0 +1,93 @@ +题目来源于LeetCode上第540号问题:有序数组中的单一元素。题目难度为中等,目前通过率60.2%。 +##题目描述 +给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。 + +``` +示例 1: + +输入: [1,1,2,3,3,4,4,8,8] +输出: 2 +示例 2: + +输入: [3,3,7,7,10,11,11] +输出: 10 +注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。 +``` +##题目解析 +我们先读题,找出题中关键词“包含整数的有序数组”、‘’元素出现两次“、”只有一个数出现一次“,这里我们可以知道:只出现一次的那个元素所在的有序数组元素个数必为奇数。这个是解题的关键。因为题目要求我们的时间复杂度为O(log n),因此我们可以用二分搜索法。 + +## 解法一:二分搜索 + +首先将lo和hi分别指向数组首尾元素,mid指向中间元素,这时我们会发现中间元素与其左右两边元素分别有以下三种情况,如:(1)3、3、4,(2)3、4、3,(3)4、3、3。对于第二种情况,我们立马就找到了只出现一次的那个元素。那么对于第一种情况mid=mid-1,那么以(3、3)为界将数组一分为二,判断两边的元素个数,因为我们知道只出现一次的那个元素所在的有序数组元素个数必为奇数,如果(3、3)左边的元素个数为奇数,那么只出现一次的那个数载左边,则将hi移到mid-2位置,即(3、3)的左边,如果(3、3)右边的元素个数为奇数,那么只出现一次的那个数在右边,则将lo移到mid+1位置,即(3、3)的右边。第三种情况与第二种情况类似分析,如果(3、3)左边为奇数,则hi移到mid-1位置,如果(3、3)右边为奇数,则lo移到mid+2位置。以此类推,直到lo=hi,搜索结束。 + +##动画理解 + +![](../Animation/Animation.gif) + +##代码实现 +``` +class Solution { + public int singleNonDuplicate(int[] nums) { + int lo = 0; + int hi = nums.length - 1; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + boolean halvesAreEven = (hi - mid) % 2 == 0; + if (nums[mid + 1] == nums[mid]) { + if (halvesAreEven) { + lo = mid + 2; + } else { + hi = mid - 1; + } + } else if (nums[mid - 1] == nums[mid]) { + if (halvesAreEven) { + hi = mid - 2; + } else { + lo = mid + 1; + } + } else { + return nums[mid]; + } + } + return nums[lo]; + } +} +``` +##复杂度分析 + +- 时间复杂度:O(log n),在每次循环迭代中,我们将搜索空间减少了一半。 +- 空间复杂度:O(1),仅用了常数空间 + +##解法二:仅对偶数索引进行二分搜索 + +我们发现当mid索引为偶数时,mid两边的数组元素个数是偶数,如果mid索引为奇数时,mid两边的数组元素个数为奇数。当mid索引为偶数时,如果mid=mid+1,即解法一的第三种情况,因为mid右边个数为偶数,所以mid+2到hi个数为奇数,那么只出现一次的那个元素肯定在mid的右边,将lo移动到mid+2位置。如果mid!= mid+1,那么只出现一次的那个元素肯定在mid的左边或者就是mid。如果mid 索引为奇数,那么我们为了保证mid索引为偶数,将mid移到mid-1位置,这样mid索引就变成偶数了,重复上述操作,直到hi=lo,搜索结束。 + +##动画理解 + +![](../Animation/2.gif) + +##代码实现 + +``` +class Solution { + public int singleNonDuplicate(int[] nums) { + int lo = 0; + int hi = nums.length - 1; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + if (mid % 2 == 1) mid--; + if (nums[mid] == nums[mid + 1]) { + lo = mid + 2; + } else { + hi = mid; + } + } + return nums[lo]; + } +} +``` + +##复杂度分析 + +- 时间复杂度:O(log2 / n)=O(log n),我们仅对元素的一半进行二分搜索。 +- 空间复杂度:O(1),仅用了常数空间 diff --git a/0642-Design-Search-Autocomplete-System/Animation/Animation.gif b/0642-Design-Search-Autocomplete-System/Animation/Animation.gif new file mode 100644 index 00000000..d18a3dca Binary files /dev/null and b/0642-Design-Search-Autocomplete-System/Animation/Animation.gif differ diff --git a/0642-Design-Search-Autocomplete-System/Article/0642-Design-Search-Autocomplete-System.md b/0642-Design-Search-Autocomplete-System/Article/0642-Design-Search-Autocomplete-System.md new file mode 100644 index 00000000..85c659b1 --- /dev/null +++ b/0642-Design-Search-Autocomplete-System/Article/0642-Design-Search-Autocomplete-System.md @@ -0,0 +1,201 @@ +# LeetCode 第 642 号问题:设计一个搜索自动完成系统 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 642 号问题:设计一个搜索自动完成系统。题目难度为 Hard,目前通过率为 37.8% 。 + +### 题目描述 + +为搜索引擎设计一个搜索自动完成系统。用户可以输入一个句子(至少一个单词,并以一个特殊的字符'#'结尾)。对于除'#'之外的每个字符,您需要返回与已输入的句子部分前缀相同的前3个历史热门句子。具体规则如下: + +一个句子的热度定义为用户输入完全相同句子的次数。 +返回的前3个热门句子应该按照热门程度排序(第一个是最热的)。如果几个句子的热度相同,则需要使用ascii代码顺序(先显示较小的一个)。 +如果少于3个热门句子,那么就尽可能多地返回。 +当输入是一个特殊字符时,它意味着句子结束,在这种情况下,您需要返回一个空列表。 +您的工作是实现以下功能: + +构造函数: + +AutocompleteSystem(String[] sentence, int[] times):这是构造函数。输入是历史数据。句子是由之前输入的句子组成的字符串数组。Times是输入一个句子的相应次数。您的系统应该记录这些历史数据。 + +现在,用户想要输入一个新句子。下面的函数将提供用户类型的下一个字符: + +List input(char c):输入c是用户输入的下一个字符。字符只能是小写字母(“a”到“z”)、空格(“”)或特殊字符(“#”)。另外,前面输入的句子应该记录在系统中。输出将是前3个历史热门句子,它们的前缀与已经输入的句子部分相同。 + +例子: +操作:AutocompleteSystem(["i love you", "island","ironman", "i love leetcode"], [5,3,2,2]) +系统已经追踪到以下句子及其对应的时间: + +"i love you" : 5 times +"island" : 3 times +"ironman" : 2 times +"i love leetcode" : 2 times + +现在,用户开始另一个搜索: + +操作:输入(“i”) +输出:["i love you", "island","i love leetcode"] +解释: +有四个句子有前缀“i”。其中,《ironman》和《i love leetcode》有着相同的热度。既然“ ” ASCII码为32,“r”ASCII码为114,那么“i love leetcode”应该在“ironman”前面。此外,我们只需要输出前3个热门句子,所以“ironman”将被忽略。 + +操作:输入(' ') +输出:[“i love you”,“i love leetcode”] +解释: +只有两个句子有前缀“i”。 + +操作:输入(' a ') +输出:[] +解释: +没有以“i a”为前缀的句子。 + +操作:输入(“#”) +输出:[] +解释: +用户完成输入后,在系统中将句子“i a”保存为历史句。下面的输入将被计算为新的搜索。 + +注意: + +输入的句子总是以字母开头,以“#”结尾,两个单词之间只有一个空格。 +要搜索的完整句子不会超过100个。包括历史数据在内的每句话的长度不会超过100句。 +在编写测试用例时,即使是字符输入,也请使用双引号而不是单引号。 +请记住重置在AutocompleteSystem类中声明的类变量,因为静态/类变量是跨多个测试用例持久化的。详情请点击这里。 + +### 题目解析 + +设计一个搜索自动补全系统,它需要包含如下两个方法: + +#### 构造方法: + +AutocompleteSystem(String[] sentences, int[] times): 输入句子sentences,及其出现次数times + +#### 输入方法: + +List input(char c): 输入字符c可以是26个小写英文字母,也可以是空格,以'#'结尾。返回输入字符前缀对应频率最高的至多3个句子,频率相等时按字典序排列。 + +### 思路解析: + +核心点:Trie(字典树) + +利用字典树记录所有出现过的句子集合,利用字典保存每个句子出现的次数。 + +#### 解题思路 + +题目的要求是补全的句子是按之前出现的频率排列的,高频率的出现在最上面,如果频率相同,就按字母顺序来显示。 + +频率 这种要求很容易想到 堆、优先队列、树、Map等知识点,这里涉及到 字典 与 树,那肯定使用 字典树 能解决。 + +所以首先构造 Trie 的 trieNode 结构以及 insert 方法,构造完 trieNode 类后,再构造一个树的根节点。 + +由于每次都要输入一个字符,我们可以用一个私有的 Node:curNode 来追踪当前的节点。 + +curNode 初始化为 root ,在每次输入完一个句子时,即输入的字符为‘#’时,我们需要将其置为root。 + +同时还需要一个 string 类型 stn 来表示当前的搜索的句子。 + +每输入一个字符,首先检查是不是结尾标识“#”,如果是的话,将当前句子加入trie树,重置相关变量,返回空数组。 + +* 如不是,检查当前 TrieNode 对应的 child 是否含有 c 的对应节点。如果没有,将 curNode 置为 NULL 并且返回空数组。 + +* 若存在,将curNode 更新为c对应的节点,并且对curNode进行dfs。 + +dfs 时,我们首先检查当前是不是一个完整的句子,如果是,将句子与其次数同时加入 priority_queue 中,然后对其 child 中可能存在的子节点进行 dfs 。 + +进行完 dfs 后,只需要取出前三个,需要注意的是,可能可选择的结果不满3个,所以要在 while 中多加入检测 q 为空的条件语句。 + +最后要将 q 中的所有元素都弹出。 + +### 动画描述 + +![](../Animation/Animation.gif) + +### 代码实现 + +#### C++ +``` +class TrieNode{ + public: + string str; + int cnt; + unordered_map child; + TrieNode(): str(""), cnt(0){}; +}; + +struct cmp{ + bool operator() (const pair &p1, const pair &p2){ + return p1.second < p2.second || (p1.second == p2.second && p1.first > p2.first); + } +}; + +class AutocompleteSystem { +public: + AutocompleteSystem(vector sentences, vector times) { + root = new TrieNode(); + for(int i = 0; i < sentences.size(); i++){ + insert(sentences[i], times[i]); + } + curNode = root; + stn = ""; + } + + vector input(char c) { + if(c == '#'){ + insert(stn, 1); + stn.clear(); + curNode = root; + return {}; + } + stn.push_back(c); + if(curNode && curNode->child.count(c)){ + curNode = curNode->child[c]; + }else{ + curNode = NULL; + return {}; + } + + dfs(curNode); + + vector ret; + int n = 3; + while(n > 0 && !q.empty()){ + ret.push_back(q.top().first); + q.pop(); + n--; + } + while(!q.empty()) q.pop(); + + return ret; + } + + void dfs(TrieNode* n){ + if(n->str != ""){ + q.push({n->str, n->cnt}); + } + for(auto p : n->child){ + dfs(p.second); + } + } + + void insert(string s, int cnt){ + TrieNode* cur = root; + for(auto c : s){ + if(cur->child.count(c) == 0){ + cur->child[c] = new TrieNode(); + } + cur = cur->child[c]; + } + cur->str = s; + cur->cnt += cnt; + } + +private: + TrieNode *root, *curNode; + string stn; + priority_queue, vector>, cmp > q; + +}; + +``` + +![](../../Pictures/qrcode.jpg) diff --git a/0695-Max-Area-of-Island/Animation/Animation.gif b/0695-Max-Area-of-Island/Animation/Animation.gif new file mode 100644 index 00000000..536516cc Binary files /dev/null and b/0695-Max-Area-of-Island/Animation/Animation.gif differ diff --git a/0695-Max-Area-of-Island/Animation/Animation.m4v b/0695-Max-Area-of-Island/Animation/Animation.m4v new file mode 100644 index 00000000..6e9e37ac Binary files /dev/null and b/0695-Max-Area-of-Island/Animation/Animation.m4v differ diff --git a/0695-Max-Area-of-Island/Article/0695-Max-Area-of-Island.md b/0695-Max-Area-of-Island/Article/0695-Max-Area-of-Island.md new file mode 100644 index 00000000..3a5cbfa0 --- /dev/null +++ b/0695-Max-Area-of-Island/Article/0695-Max-Area-of-Island.md @@ -0,0 +1,173 @@ +# LeetCode 图解 | + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +本题解作者:nettee + +## 题目描述 + +给定一个包含了一些 `0` 和 `1` 的非空二维数组 `grid`。 + +一个**岛屿**是由一些相邻的 `1` (代表土地) 构成的组合,这里的「相邻」要求两个 `1` 必须在水平或者竖直方向上相邻。你可以假设 `grid` 的四个边缘都被 `0`(代表水)包围着。 + +找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 `0`。) + +**示例 1:** + +``` +[[0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] +``` + + +对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 `1` 。 + +**示例 2:** + +``` +[[0,0,0,0,0,0,0,0]] +``` + + +对于上面这个给定的矩阵, 返回 0。 + +注意: 给定的矩阵 `grid` 的长度和宽度都不超过 50。 + +## 题目解析 + +这道题的主要思路是深度优先搜索。每次走到一个是 1 的格子,就搜索整个岛屿,并计算当前岛屿的面积。最后返回岛屿面积的最大值。 + +网格可以看成是一个无向图的结构,每个格子和它上下左右的四个格子相邻。如果四个相邻的格子坐标合法,且是陆地,就可以继续搜索。 + +在深度优先搜索的时候要注意避免重复遍历。我们可以把已经遍历过的陆地改成 2,这样遇到 2 我们就知道已经遍历过这个格子了,不进行重复遍历。 + +## 动画理解 + +![](../Animation/Animation.gif) + +## 参考代码 + +C++ 代码: + +```C++ +class Solution { +public: + int maxAreaOfIsland(vector>& grid) { + int res = 0; + for (int r = 0; r < grid.size(); r++) { + for (int c = 0; c < grid[0].size(); c++) { + if (grid[r][c] == 1) { + int a = area(grid, r, c); + res = max(res, a); + } + } + } + return res; + } + + int area(vector>& grid, int r, int c) { + if (!(inArea(grid, r, c))) { + return 0; + } + if (grid[r][c] != 1) { + return 0; + } + grid[r][c] = 2; + + return 1 + + area(grid, r - 1, c) + + area(grid, r + 1, c) + + area(grid, r, c - 1) + + area(grid, r, c + 1); + } + + bool inArea(vector>& grid, int r, int c) { + return 0 <= r && r < grid.size() + && 0 <= c && c < grid[0].size(); + } +}; +``` + +Java 代码: + +```Java +class Solution { + public int maxAreaOfIsland(int[][] grid) { + int res = 0; + for (int r = 0; r < grid.length; r++) { + for (int c = 0; c < grid[0].length; c++) { + if (grid[r][c] == 1) { + int a = area(grid, r, c); + res = Math.max(res, a); + } + } + } + return res; + } + + int area(int[][] grid, int r, int c) { + if (!inArea(grid, r, c)) { + return 0; + } + if (grid[r][c] != 1) { + return 0; + } + grid[r][c] = 2; + + return 1 + + area(grid, r - 1, c) + + area(grid, r + 1, c) + + area(grid, r, c - 1) + + area(grid, r, c + 1); + } + + boolean inArea(int[][] grid, int r, int c) { + return 0 <= r && r < grid.length + && 0 <= c && c < grid[0].length; + } +} +``` + +Python 代码: + +```Python +class Solution: + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + res = 0 + for r in range(len(grid)): + for c in range(len(grid[0])): + if grid[r][c] == 1: + a = self.area(grid, r, c) + res = max(res, a) + return res + + def area(self, grid: List[List[int]], r: int, c: int) -> int: + if not self.inArea(grid, r, c): + return 0 + if grid[r][c] != 1: + return 0 + grid[r][c] = 2 + + return 1 \ + + self.area(grid, r - 1, c) \ + + self.area(grid, r + 1, c) \ + + self.area(grid, r, c - 1) \ + + self.area(grid, r, c + 1) + + def inArea(self, grid: List[List[int]], r: int, c: int) -> bool: + return 0 <= r < len(grid) and 0 <= c < len(grid[0]) +``` + + + +## 复杂度分析 + +设网格的边长为 n,则时间复杂度为 O(n²)。 \ No newline at end of file diff --git a/0771-Jewels-Stones/Animation/1.mp4 b/0771-Jewels-Stones/Animation/1.mp4 new file mode 100644 index 00000000..7e1b1199 Binary files /dev/null and b/0771-Jewels-Stones/Animation/1.mp4 differ diff --git a/0771-Jewels-Stones/Animation/Animation.gif b/0771-Jewels-Stones/Animation/Animation.gif new file mode 100644 index 00000000..aeb499f2 Binary files /dev/null and b/0771-Jewels-Stones/Animation/Animation.gif differ diff --git a/0771-Jewels-Stones/Article/0771-Jewels-Stones b/0771-Jewels-Stones/Article/0771-Jewels-Stones new file mode 100644 index 00000000..53451615 --- /dev/null +++ b/0771-Jewels-Stones/Article/0771-Jewels-Stones @@ -0,0 +1,62 @@ +# LeetCode 第 771 号问题:宝石与石头 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于LeetCode上第771号问题:宝石与石头。题目难度为Easy,目前通过率82.3%。 + +#### 题目描述 + +> 给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。 +> + +```java +示例 1: + +输入: J = "aA", S = "aAAbbbb" +输出: 3 +示例 2: + +输入: J = "z", S = "ZZ" +输出: 0 +注意: +S 和 J 最多含有50个字母。 + J 中的字符不重复。 +``` + +#### 题目解析 + +这道题目中有宝石,石头,看起来高大上似的。其实是比较简单的,大致意思就是给定一串字符J和另一串字符S,求J中每个字符出现在S字符串中的次数。比较好的解法是先将J字符串中字符放进哈希集合中,然后把S中每个字符依次去判断是否包含在哈希集合中。我刷了不少LeetCode题,总结一点就是:当你看懂题目基本上就成功了一半。 + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 代码实现 + +Java语言 + +```java +class Solution { + public int numJewelsInStones(String J, String S) { + Set Jset = new HashSet(); + for (char j: J.toCharArray()) + Jset.add(j); + + int ans = 0; + for (char s: S.toCharArray()) + if (Jset.contains(s)) + ans++; + return ans; + } +} +``` + +#### 复杂度分析 + ++ 时间复杂度:O(J.length+S.length),O(J.length) 这部分来自于创建J,O(S.length) 这部分来自于搜索 S。 ++ 空间复杂度:O(J.length) + + + diff --git a/0771-Jewels-Stones/Code/1.java b/0771-Jewels-Stones/Code/1.java new file mode 100644 index 00000000..b6894927 --- /dev/null +++ b/0771-Jewels-Stones/Code/1.java @@ -0,0 +1,14 @@ + +class Solution { + public int numJewelsInStones(String J, String S) { + Set Jset = new HashSet(); + for (char j: J.toCharArray()) + Jset.add(j); + + int ans = 0; + for (char s: S.toCharArray()) + if (Jset.contains(s)) + ans++; + return ans; + } +} \ No newline at end of file diff --git a/0942-di-String-Match/Animation/0942-di-String-Match.mp4 b/0942-di-String-Match/Animation/0942-di-String-Match.mp4 new file mode 100644 index 00000000..420d7fbb Binary files /dev/null and b/0942-di-String-Match/Animation/0942-di-String-Match.mp4 differ diff --git a/0942-di-String-Match/Animation/0942-di-String-Match01.gif b/0942-di-String-Match/Animation/0942-di-String-Match01.gif new file mode 100644 index 00000000..f5888b82 Binary files /dev/null and b/0942-di-String-Match/Animation/0942-di-String-Match01.gif differ diff --git a/0942-di-String-Match/Animation/0942-di-String-Match02.gif b/0942-di-String-Match/Animation/0942-di-String-Match02.gif new file mode 100644 index 00000000..6dae7bb7 Binary files /dev/null and b/0942-di-String-Match/Animation/0942-di-String-Match02.gif differ diff --git a/0942-di-String-Match/Article/0942-di-String-Match.md b/0942-di-String-Match/Article/0942-di-String-Match.md new file mode 100644 index 00000000..16f156b0 --- /dev/null +++ b/0942-di-String-Match/Article/0942-di-String-Match.md @@ -0,0 +1,121 @@ +## LeetCode第942号问题:增减字符串匹配 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步个人博客:www.zhangxiaoshuai.fun + +本题在leetcode中题目序号942,属于easy级别,目前通过率为71.4% + +### 题目描述: + +``` +给定只含 "I"(增大)或 "D"(减小)的字符串 S ,令 N = S.length。 +返回 [0, 1, ..., N] 的任意排列 A 使得对于所有 i = 0, ..., N-1,都有: + 如果 S[i] == "I",那么 A[i] < A[i+1] + 如果 S[i] == "D",那么 A[i] > A[i+1] + +示例 1: +输出:"IDID" +输出:[0,4,1,3,2] + +示例 2: +输出:"III" +输出:[0,1,2,3] + +示例 3: +输出:"DDI" +输出:[3,2,0,1] + +提示: + 1 <= S.length <= 10000 + S 只包含字符 "I" 或 "D" +``` + +**题目分析:** + +``` +题目中的意思很明确,我们只要满足给出的两个条件即可。 + +1.假如字符串的长度为N,那么目标数组的长度就为N+1; + +2.数组中的数字都是从0~N,且没有重复; + +3.遇见‘I’,要增加;遇见‘D’要减少; +``` + +### GIF动画演示: + +![](../Animation/0942-di-String-Match01.gif) + +### 代码: + +```java +//这里搬运下官方的解法 +public int[] diStringMatch(String S) { + int N = S.length(); + int lo = 0, hi = N; + int[] ans = new int[N + 1]; + for (int i = 0; i < N; ++i) { + if (S.charAt(i) == 'I') + ans[i] = lo++; + else + ans[i] = hi--; + } + ans[N] = lo; + return ans; +} +``` + +**虽然上述代码很简洁,好像已经不需要我们去实现什么;但是满足条件的序列并不止一种,官方的好像只能通过一种,下面的代码虽然有些冗余,但是得出的序列是满足题意要求的,但是并不能AC;** + +### 思路: + +``` +(1)如果遇见的是‘I’,那么对应数组当前位置的数字要小于它右边的第一个数字 +(2)如果遇见的是‘D’,那么对应数组当前位置的数字要大于它右边的第一个数字 + +首先对目标数组进行初始化,赋值0~N +我们开始遍历字符串,如果遇见‘I’就判断对应数组该位置上的数是否满足(1)号条件 +如果满足,跳过本次循环;如果不满足,交换两个数字的位置; +对于‘D’,也是同样的思路; +``` + +### GIF动画演示: + +![](../Animation/0942-di-String-Match02.gif) + +### 代码: + +```java +public int[] diStringMatch(String S) { + int[] res = new int[S.length()+1]; + String[] s = S.split(""); + for (int i = 0; i < res.length; i++) { + res[i] = i; + } + for (int i = 0; i < s.length; i++) { + if (s[i].equals("I")) { + //判断指定位置的数字是否符合条件 + if (res[i] < res[i + 1]) { + continue; + } else { + //交换两个数字的位置 + res[i] = res[i] ^ res[i+1]; + res[i+1] = res[i] ^ res[i+1]; + res[i] = res[i] ^ res[i+1]; + } + } else { + if (res[i] > res[i + 1]) { + continue; + } else { + res[i] = res[i] ^ res[i+1]; + res[i+1] = res[i] ^ res[i+1]; + res[i] = res[i] ^ res[i+1]; + } + } + } + return res; +} +``` + +**以上内容如有错误、不当之处,欢迎批评指正。** \ No newline at end of file diff --git a/0946--validate-stack-sequences/Animation/1.mp4 b/0946--validate-stack-sequences/Animation/1.mp4 new file mode 100644 index 00000000..e2ea0e2d Binary files /dev/null and b/0946--validate-stack-sequences/Animation/1.mp4 differ diff --git a/0946--validate-stack-sequences/Animation/Animation.gif b/0946--validate-stack-sequences/Animation/Animation.gif new file mode 100644 index 00000000..56008446 Binary files /dev/null and b/0946--validate-stack-sequences/Animation/Animation.gif differ diff --git a/0946--validate-stack-sequences/Article/0946-validate-stack-sequences b/0946--validate-stack-sequences/Article/0946-validate-stack-sequences new file mode 100644 index 00000000..ec29889a --- /dev/null +++ b/0946--validate-stack-sequences/Article/0946-validate-stack-sequences @@ -0,0 +1,61 @@ +# LeetCode 第 946 号问题:验证栈序列 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上第 946 号问题:验证栈序列。题目难度为 Medium,目前通过率为 56.5% 。 + +#### 题目描述 + +> 给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false +> + +```java +示例: +输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1] +输出:true +解释:我们可以按以下顺序执行: +push(1), push(2), push(3), push(4), pop() -> 4, +push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1 +``` + +#### 题目解析 + +出栈结果存储在队列中,按元素顺序push进入栈,每push一个元素,即检查是否与队列首部元素相同,若相同则弹出队列首元素,弹出栈顶元素,直到两个元素不同结束,若最终栈为空,说明序列合法,否则不合法。 + +#### 动画理解 + +![](../Animation/Animation.gif) + +#### 代码实现 + +```java +class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + + int N = pushed.length; + Stack stack = new Stack(); + + int j = 0; + for (int x: pushed) { + stack.push(x); + while (!stack.isEmpty() && j < N && stack.peek() == popped[j]) { + //队头元素出队,栈顶元素出栈 + stack.pop(); + j++; + } + } + if (!stack.isEmpty()){ + return false; + } + return true; + } +} +``` + +#### 复杂度分析 + ++ 时间复杂度:O(n)。 ++ 空间复杂度:O(n)。 + diff --git a/0946--validate-stack-sequences/Code/1.java b/0946--validate-stack-sequences/Code/1.java new file mode 100644 index 00000000..9d2026cf --- /dev/null +++ b/0946--validate-stack-sequences/Code/1.java @@ -0,0 +1,21 @@ +class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + + int N = pushed.length; + Stack stack = new Stack(); + + int j = 0; + for (int x: pushed) { + stack.push(x); + while (!stack.isEmpty() && j < N && stack.peek() == popped[j]) { + //ͷԪسӣջԪسջ + stack.pop(); + j++; + } + } + if (!stack.isEmpty()){ + return false; + } + return true; + } +} \ No newline at end of file diff --git a/0994-orangesRotting/Animation/example01.png b/0994-orangesRotting/Animation/example01.png new file mode 100644 index 00000000..5174a846 Binary files /dev/null and b/0994-orangesRotting/Animation/example01.png differ diff --git "a/0994-orangesRotting/Animation/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\22001.gif" "b/0994-orangesRotting/Animation/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\22001.gif" new file mode 100644 index 00000000..82b37ac2 Binary files /dev/null and "b/0994-orangesRotting/Animation/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\22001.gif" differ diff --git a/0994-orangesRotting/Article/0994-orangesRotting.md b/0994-orangesRotting/Article/0994-orangesRotting.md new file mode 100644 index 00000000..c1243487 --- /dev/null +++ b/0994-orangesRotting/Article/0994-orangesRotting.md @@ -0,0 +1,112 @@ +## LeetCode第994号问题:腐烂的橘子 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步个人博客:www.zhangxiaoshuai.fun + +本题在leetcode中题目序号994,属于medium级别,目前通过率为50.7% + +**题目描述:** + +``` +在给定的网格中,每个单元格可以有以下三个值之一: + 值0代表空单元格; + 值1代表新鲜橘子; + 值2代表腐烂的橘子。 +每分钟,任何与腐烂的橘子(在4个正方向上)相邻的新鲜橘子都会腐烂。返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回-1。 + +示例1: + 输入:[[2,1,1],[1,1,0],[0,1,1]] + 输出:4 + +示例2: + 输入:[[2,1,1],[0,1,1],[1,0,1]] + 输出:-1 + 解释:左下角的橘子(第2行,第0列)永远不会腐烂,因为腐烂只会发生在4个正向上。 + +示例3: + 输入:[[0,2]] + 输出:0 + 解释:因为0分钟时已经没有新鲜橘子了,所以答案就是0。 + +提示: + 1<=grid.length<=10 + 1<=grid[0].length<=10 + grid[i][j]仅为0、1或2 +``` + +**由题意:只有腐烂的橘子才可以去污染它周围四个方向上存在的新鲜橘子,且它每一分钟只能污染一次,下一次,被它腐蚀的橘子再去腐蚀自己周边的新鲜橘子,每次只有被新腐蚀的橘子才能继续向外腐蚀(因为旧的腐烂的橘子已经被“包围”了)** + +这就很像一个人得了传染病,只要他遇见人就会将病传染给那个人,而被传染的又会去感染别的人(不同的是,这里的橘子的位置是固定的,无法移动) + +思路是非常简单的,我们通过动态图直观理解下: + +![腐烂的橘子gif演示](../Animation/腐烂的橘子01.gif) + +既然理清了思路,那么我们来试试代码: +首先,我们需要知道初始状态下的单元格中有多少腐烂的橘子,并且要将它们的位置信息保存下来,我们可以用一个队列(**先入先出**)将(x,y)保存下来;然后我们开始遍历整个队列,每次弹出一个保存的位置信息,将这个位置周围的新鲜橘子全部腐蚀,并且将被腐蚀的橘子的位置信息存入队列中,在下次循环中从它们的位置上再“**向外延伸**”(注意:为了模拟同步,我们需要将每次存入队列中的所有位置都要在下一次全部取出来);直到队列为空,循环结束,这个时候并不能说明整个单元格中已经不存在新鲜的橘子,因为可能存在下面这种情况: + +![](../Animation/example01.png) + +很明显,标红的区域(新鲜橘子)永远不能被腐蚀,因为它周围唯一的两个单元格是空的。 + +那么针对这种情况,我们在前面遍历统计腐烂橘子的时候可以顺便统计一下新鲜橘子的数量count,后面我们每腐蚀一个橘子就从count中减去1。最终循环结束的时候,我们只需要判断count是否大于0,若是,返回-1,否则返回轮数res。 + +------ + +**代码:** + +```java +public static int orangesRotting02(int[][] grid){ + int row = grid.length,col = grid[0].length; + Queue queue = new ArrayDeque(); + int count = 0;//统计新鲜橘子的数量 + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (grid[i][j] == 2) { + queue.add(new int[]{i,j}); + } + if (grid[i][j] == 1) { + count++; + } + } + } + int res = 0; + while (count > 0 && !queue.isEmpty()) { + res++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + int[] temp = queue.poll(); + int r = temp[0],c = temp[1];//(x,y) + //上 + if (r > 0 && grid[r-1][c] == 1) { + grid[r-1][c] = 2; + count--; + queue.add(new int[]{r-1,c}); + } + //下 + if (r < grid.length-1 && grid[r+1][c] == 1) { + grid[r+1][c] = 2; + count--; + queue.add(new int[]{r+1,c}); + } + //左 + if (c > 0 && grid[r][c-1] == 1) { + grid[r][c-1] = 2; + count--; + queue.add(new int[]{r,c-1}); + } + //右 + if (c < grid[0].length-1 && grid[r][c+1] == 1) { + grid[r][c+1] = 2; + count--; + queue.add(new int[]{r,c+1}); + } + } + } + if (count > 0) { + return -1; + } + return res; +} +``` \ No newline at end of file diff --git a/1054-rearrangeBarcodes/Animation/1054-rearrangeBarcodes.gif b/1054-rearrangeBarcodes/Animation/1054-rearrangeBarcodes.gif new file mode 100644 index 00000000..e0c529e6 Binary files /dev/null and b/1054-rearrangeBarcodes/Animation/1054-rearrangeBarcodes.gif differ diff --git a/1054-rearrangeBarcodes/Animation/1054-rearrangeBarcodes.mp4 b/1054-rearrangeBarcodes/Animation/1054-rearrangeBarcodes.mp4 new file mode 100644 index 00000000..0b3ab696 Binary files /dev/null and b/1054-rearrangeBarcodes/Animation/1054-rearrangeBarcodes.mp4 differ diff --git a/1054-rearrangeBarcodes/Article/1054-rearrangeBarcodes.md b/1054-rearrangeBarcodes/Article/1054-rearrangeBarcodes.md new file mode 100644 index 00000000..dd859b5d --- /dev/null +++ b/1054-rearrangeBarcodes/Article/1054-rearrangeBarcodes.md @@ -0,0 +1,68 @@ +## LeetCode第1054号问题:距离相等的条形码 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步个人博客:www.zhangxiaoshuai.fun + +**本题选自leetcode第1054号问题,medium级别,目前通过率33.3%** + +**题目描述:** + + 在一个仓库里,有一排条形码,其中第 i 个条形码为 barcodes[i]。 + 请你重新排列这些条形码,使其中两个相邻的条形码不能相等。 + 你可以返回任何满足该要求的答案,此题保证存在答案。 + 示例 1: + 输入:[1,1,1,2,2,2] + 输出:[2,1,2,1,2,1] + + 示例 2: + 输入:[1,1,1,1,2,2,3,3] + 输出:[1,3,1,3,2,1,2,1] + + 提示: + 1 <= barcodes.length <= 10000 + 1 <= barcodes[i] <= 10000 +### 题目分析: + 1.首先我们需要将每个条形码和出现的次数作一记录,为了存取方便,这里使用数组(题目中已经给出了数组的最大和最小长度)进行操作; + 2.找出其中出现最多次数的条形码,拿到该barcode和count; + 3.先将出现次数最多的条形码存入目标数组中(偶数位或者奇数位),并对记录数组作一更新; + 4.随后将剩余的barcode填充进目标数组中。 + +### GIF动画展示: + +![](../Animation/1054-rearrangeBarcodes.gif) + +### 代码: + +```java +public static int[] rearrangeBarcodes(int[] barcodes){ + int[] address = new int[10001]; + for (int barcode : barcodes) + address[barcode]++; + // 找到出现次数最多的barcode + int maxCode = 0, maxCount = 0; + for (int i = 0; i < address.length; i++) { + if (maxCount < address[i]) { + maxCode = i; + maxCount = address[i]; + } + } + int index = 0; + // 先填充最大的那一位barcode + for (; address[maxCode] > 0; index += 2) { + barcodes[index] = maxCode; + address[maxCode]--; + } + // 继续填充剩余的条形码 + for (int i = 1; i < address.length; i++) { + while (address[i] > 0) { + //偶数位填充完毕 + if (index >= barcodes.length) index = 1; + barcodes[index] = i; + address[i]--; + index += 2; + } + } + return barcodes; +} +``` \ No newline at end of file diff --git a/1137-Tribonacci/Animation/1137-Tribonacci.gif b/1137-Tribonacci/Animation/1137-Tribonacci.gif new file mode 100644 index 00000000..b9aeb520 Binary files /dev/null and b/1137-Tribonacci/Animation/1137-Tribonacci.gif differ diff --git a/1137-Tribonacci/Animation/1137-Tribonacci.mp4 b/1137-Tribonacci/Animation/1137-Tribonacci.mp4 new file mode 100644 index 00000000..99ba8046 Binary files /dev/null and b/1137-Tribonacci/Animation/1137-Tribonacci.mp4 differ diff --git a/1137-Tribonacci/Article/1137-Tribonacci.md b/1137-Tribonacci/Article/1137-Tribonacci.md new file mode 100644 index 00000000..b7feb502 --- /dev/null +++ b/1137-Tribonacci/Article/1137-Tribonacci.md @@ -0,0 +1,89 @@ +## LeetCode第1137号问题:第N个泰波那契数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 个人博客:www.zhangxiaoshuai.fun + +**本题选自leetcode中第1137题,easy级别,目前通过率52.4%** + +### 题目描述: + +```txt +泰波那契序列 Tn 定义如下: +T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2 +给你整数 n,请返回第 n 个泰波那契数 Tn 的值。 + +示例 1: +输入:n = 4 +输出:4 +解释: +T_3 = 0 + 1 + 1 = 2 +T_4 = 1 + 1 + 2 = 4 + +示例 2: +输入:n = 25 +输出:1389537 + +提示: + 0 <= n <= 37 + 答案保证是一个 32 位整数,即 answer <= 2^31 - 1。 +``` + +### 题目分析: +要是之前有接触过斐波那契数列的话,这道题是非常容易有解决思路的。我们有以下三种方法(正经方法两种,哈哈哈)来解决该问题: + +``` +1.递归(但是leetcode中是无法AC的,超出时间限制,但是还是会将代码展示出来) +2.动态规划(这种题都是已知前面的来求得未知的,使用dp再合适不过) +3.暴力(抖机灵,看一乐就可以啦) +``` + +### GIF动画演示: + +![](../Animation/1137-Tribonacci.gif) + +## 代码: + +### 递归版本: + +```java +public int tribonacci(int n) { + if (n == 0) { + return 0; + } + if (n == 1 || n == 2) { + return 1; + } + return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n -3); +} +``` + +### 动态规划 + +```java +int[] dp = new int[38]; +public int tribonacci(int n) { + if (dp[n] != 0) { + return dp[n]; + } + if (n == 0) { + return 0; + } else if (n == 1 || n == 2) { + return 1; + } else { + int res = tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + dp[n] = res; + return res; + } +} +``` + +### 暴力法(十分暴力,哈哈哈哈……) + +```java +public int tribonacci(int n) { + int[] Ts = {0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, 927, 1705, 3136, 5768, 10609, 19513, 35890, 66012, 121415, 223317, 410744, 755476, 1389537, 2555757, 4700770, 8646064, 15902591, 29249425, 53798080, 98950096, 181997601, 334745777, 615693474, 1132436852, 2082876103}; + return Ts[n]; +} +``` + diff --git a/1281- subtract-the-product-and-sum-of-digits-of-an-integer/Animation/Animation.mp4 b/1281- subtract-the-product-and-sum-of-digits-of-an-integer/Animation/Animation.mp4 new file mode 100644 index 00000000..9508a51d Binary files /dev/null and b/1281- subtract-the-product-and-sum-of-digits-of-an-integer/Animation/Animation.mp4 differ diff --git a/1281- subtract-the-product-and-sum-of-digits-of-an-integer/Article/1281- subtract-the-product-and-sum-of-digits-of-an-integer.md b/1281- subtract-the-product-and-sum-of-digits-of-an-integer/Article/1281- subtract-the-product-and-sum-of-digits-of-an-integer.md new file mode 100644 index 00000000..6a3ca2b6 --- /dev/null +++ b/1281- subtract-the-product-and-sum-of-digits-of-an-integer/Article/1281- subtract-the-product-and-sum-of-digits-of-an-integer.md @@ -0,0 +1,68 @@ +### 题目描述 + +给你一个整数 `n`,请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。 + +示例 1: + +``` +输入:n = 234 +输出:15 +解释: +各位数之积 = 2 * 3 * 4 = 24 +各位数之和 = 2 + 3 + 4 = 9 +结果 = 24 - 9 = 15 +``` + +示例 2: + +``` +输入:n = 4421 +输出:21 +解释: +各位数之积 = 4 * 4 * 2 * 1 = 32 +各位数之和 = 4 + 4 + 2 + 1 = 11 +结果 = 32 - 11 = 21 +``` + +**提示:** + +``` +1 <= n <= 10^5 +``` + +### 题目解析 + +1、通过取模运算遍历数字每一位 + +2、通过两个变量在遍历过程中分别记录求和与求积 + +### 动画理解 + +![](../Animation/Animation.mp4) + +‎⁨ + +### 参考代码 + +```java +class Solution { + public int subtractProductAndSum(int n) { + int addResult = 0, mulResult = 1; + while (n > 0) { + int num = n % 10; + n /= 10; + addResult += num; + mulResult *= num; + } + return mulResult - addResult; + } +} +``` + + + +### 复杂度分析 + +时间复杂度:O(logN) + +空间复杂度:O(1) \ No newline at end of file diff --git a/1351-count-negative-numbers-in-a-sorted-matrix/Animation/1351.mp4 b/1351-count-negative-numbers-in-a-sorted-matrix/Animation/1351.mp4 new file mode 100644 index 00000000..97d22713 Binary files /dev/null and b/1351-count-negative-numbers-in-a-sorted-matrix/Animation/1351.mp4 differ diff --git a/1351-count-negative-numbers-in-a-sorted-matrix/Article/1351-count-negative-numbers-in-a-sorted-matrix b/1351-count-negative-numbers-in-a-sorted-matrix/Article/1351-count-negative-numbers-in-a-sorted-matrix new file mode 100644 index 00000000..d7ad3987 --- /dev/null +++ b/1351-count-negative-numbers-in-a-sorted-matrix/Article/1351-count-negative-numbers-in-a-sorted-matrix @@ -0,0 +1,178 @@ +# 1351. 统计有序矩阵中的负数 + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 1531 题。 + +## 题目 + +给你一个 m * n 的矩阵 grid,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。  + +请你统计并返回 grid 中 负数 的数目。 + + +示例 1: + + +``` +输入:grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]] +输出:8 +解释:矩阵中共有 8 个负数。 + +``` + +示例 2: + +``` +输入:grid = [[3,2],[1,0]] +输出:0 +``` + +示例 3: + +``` +输入:grid = [[1,-1],[-1,-1]] +输出:3 +``` + +示例 4: + + +``` +输入:grid = [[-1]] +输出:1 + +``` + + +提示: + + +m == grid.length + +n == grid[i].length + +1 <= m, n <= 100 + +-100 <= grid[i][j] <= 100 + + +## 题目解析 + +首先审题,在一个矩阵中找出所有负数,这个很好理解,小白一开始做,可以直接暴力遍历,然后依次判断当前值是不是小于0,小于0计数加一。如果是面试的话,面试官一般都会问还没有其他办法,这时候你肯定要说怎么没有,所以我们做一道题不能仅仅限于做出了,要从多方面下手,使得复杂度越小越好。 + +所以我们要仔细审题,我们看到题目中有这样一句描述**矩阵中的元素无论是按行还是按列,都以非递增顺序排列**,这句话信息量很大,你品,你仔细品。 + +首先我们可以理解到的是:一个 m * n 的矩阵 grid,grid[i][j]<0的话,i那一行第j个到数组最后一个都是小于0,第i行开始,j到后面的列都是小于0 + +我们举个例子 + +``` +[ + [4, 3, -1, -1], + [3, 2, -1, -1], + [1, 1, -1, -2], + [-1, -1, -2, -3] +] + +``` + +可以看到第一行第三列的值小于0,那么第一行及后面几行的第三列和后面的列的值都小于0。 + +所以我们可以进行倒序遍历,找到负数的左边界,也就是左边第一个为负数的值。 + +i为行遍历的指针,j为列遍历指针,count为负数的个数,len1为**当前行**的个数,len2为**当前列**的个数 + +``` +初始值 +let count = 0; +let len1 = grid.length +let len2 = grid[0].length +let i = 0; +let j = len2 - 1; +``` + +然后我们倒序遍历列,行还是正序遍历,以上面的例子来说,我们找到的第一个负数是grid[0][2],然后我们能确定的负数就有如下,所以我们count就可以加上(len1 - i -1) * (len2 - j ) + +``` +[ + [ -1, -1], + [ -1, -1], + [ -1, -2], + [ -2, -3] +] + +``` + +然后我们遍历的矩阵就变成如下,然后重复上面操作。 + + +``` +[ + [3, 2], + [1, 1], + [-1, -1] +] + +``` + +所以我们每次遍历后要更新len1和len2,具体看代码和动画。 + + +## 动画理解 + + + + +## 参考代码 + +```JavaScript +/** + * @param {number[][]} arr + * @return {number} + */ +var countNegatives = function(arr) { + if (arr.length <= 0 ) {return 0} + let count = 0; + let len1 = arr.length; + let len2 = arr[0].length; + let i = 0; + let j = len2 - 1; + while(i < len1) { + while(j >= 0) { + if (arr[i][j] < 0) { + if (j==0){ + count += (len2 * (len1 - i)) + len2 = 0 + break + } + j-- + }else { + if (len2 == j + 1) { + break + } + count += ((len2 - j - 1) * (len1 - i)) + len2 = j + 1 + break + } + } + i++ + j= len2 - 1 + } + return count +}; +``` + +## 复杂度分析 + +时间复杂度: O(mn)。 因为如果矩阵中所有的数都为正数,那么要遍历整个矩阵,所以时间复杂度是O(mn)。 + + +空间复杂度:O(1)。 + + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/Interview053-I- Find-Number-In-Sort-Array-I/Animation/Interview053-I- Find-Number-In-Sort-Array-I.mp4 b/Interview053-I- Find-Number-In-Sort-Array-I/Animation/Interview053-I- Find-Number-In-Sort-Array-I.mp4 new file mode 100644 index 00000000..7199c27e Binary files /dev/null and b/Interview053-I- Find-Number-In-Sort-Array-I/Animation/Interview053-I- Find-Number-In-Sort-Array-I.mp4 differ diff --git a/Interview053-I- Find-Number-In-Sort-Array-I/Article/Interview053-I- Find-Number-In-Sort-Array-I b/Interview053-I- Find-Number-In-Sort-Array-I/Article/Interview053-I- Find-Number-In-Sort-Array-I new file mode 100644 index 00000000..81cf5f7e --- /dev/null +++ b/Interview053-I- Find-Number-In-Sort-Array-I/Article/Interview053-I- Find-Number-In-Sort-Array-I @@ -0,0 +1,106 @@ +# 面试题53 - I. 在排序数组中查找数字 I + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +题目来源于 LeetCode 上 面试题53 - I. 在排序数组中查找数字 I. 是算法入门的一道题。 + +## 题目 + +统计一个数字在排序数组中出现的次数。 +  + +示例 1: + +``` +输入: nums = [5,7,7,8,8,10], target = 8 +输出: 2 +``` + +示例 2: + + +``` +输入: nums = [5,7,7,8,8,10], target = 6 +输出: 0 +``` +  + +限制: + +``` +0 <= 数组长度 <= 50000 +``` +## 题目解析 + +题目很好理解,就是要找到给定的target在排序数组中出现的次数,刚接触到算法的萌新,不仔细审题的话,会忽略排序这个重要的点。然后直接暴力循环数组,定义一个计数变量count,每次出现目标值,count加一,遍历结束,return count。 + +老司机一看到这个题,“排序”,因为数组是排序的,所以所以目标都会连在一起,我们只要找到目标值左右边界,然后相减加一就可以得到出现的次数。 + +要找到左右边界,其实可以理解为在数组中找到某个值,那么就会想到最常见的一个算法,二分法。 + + 1. 定义两个指针:start,end分别指向数组的头和尾部 + 2. 定义mid等于Math.ceil((start+end)/2) + 3. 判断mid指向的数组的元素和目标值target的大小 + 4. mid大,那么移动end,否则移动start + 5. 重复以上的操作两遍,分别得到左边界left,右边界right + 6. 最后得到目标值出现次数 right - left + 1 + + +## 动画理解 + + + + +## 参考代码 + + +```javaScript +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + let start = 0; + let mid = 0; + let end = nums.length - 1; + let left = 0; + let right = 0; + // 查找右边界 + while(start <= end) { + mid = Math.ceil((start + end) / 2) + if (nums[mid] <= target) { + start = mid + 1 + } else { + end = mid -1 + } + } + right = start - 1; // 右边界 + // 查找左边界 + start = 0; + mid = 0; + end = nums.length - 1; + while(start <= end) { + mid = Math.ceil((start + end) / 2) + if (nums[mid] < target) { + start = mid + 1 + } else { + end = mid -1 + } + } + left = end + 1 + return right - left + 1 +}; +``` + +## 复杂度分析 + +二分查找的时间复杂度计算如下:假设一个数组长度为n,每次查找后数据长度减半,第一次查找后数据长度为n/2,第二次查找后数据长度为n/(2的2次方),第k次查找后数据长度为n/(2的k次方),最坏情况下数数据长度为1时找到该数,即n/(2的k次方)=1, 解得k=log2(N). + +时间复杂度为:O(log2n)。 + +![](../../Pictures/qrcode.jpg) \ No newline at end of file diff --git a/Pictures/qrcode.jpg b/Pictures/qrcode.jpg new file mode 100644 index 00000000..3c299f73 Binary files /dev/null and b/Pictures/qrcode.jpg differ diff --git a/Readme.md b/Readme.md index e51b64b4..15cc1ea8 100755 --- a/Readme.md +++ b/Readme.md @@ -8,116 +8,109 @@ 我会尽力将 LeetCode 上所有的题目都用动画的形式演示出来,计划用 3 到 4 年时间去完成它,期待与你见证这一天! -文章最新首发于微信公众号 **五分钟学算法** ,您可以关注获取最新的文章。 - -## 汇总 - -| 序号 | 题目&题解 | -| ---- | ------------------------------------------------------------ | -| 0 | [十大经典排序算法动画与解析,看我就够了!(配代码完全版)](https://mp.weixin.qq.com/s/vn3KiV-ez79FmbZ36SX9lg) | -| 1 | [两数之和](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第1号问题:两数之和.md) | -| 2 | [两数相加](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第2号问题:两数相加.md) | -| 3 | [无重复字符的最长子串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第3号问题:无重复字符的最长子串.md) | -| 9 | [回文数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第9号问题:回文数.md) | -| 15 | [三数之和](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第15号问题:三数之和.md) | -| 19 | [删除链表的倒数第 N 个节点](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第19号问题:删除链表的倒数第N个节点.md) | -| 20 | [有效的括号](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第20号问题:有效的括号.md) | -| 21 | [合并两个有序链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第21号问题:合并两个有序链表.md) | -| 23 | [合并 K 个排序链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第23号问题:合并K个排序链表.md) | -| 24 | [两两交换链表中的节点](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第24号问题:两两交换链表中的节点.md) | -| 26 | [删除排序数组中的重复项](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第26号问题:删除排序数组中的重复项.md) | -| 66 | [加一](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第66号问题:加一.md) | -| 75 | [颜色分类](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第75号问题:颜色分类.md) | -| 86 | [分割链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第86号问题:分割链表.md) | -| 92 | [反转链表 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第92号问题:反转链表II.md) | -| 94 | [二叉树的中序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第94号问题:二叉树的中序遍历.md) | -| 101 | [对称二叉树](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第101号问题:对称二叉树.md) | -| 102 | [二叉树的层序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第102号问题:二叉树的层序遍历.md) | -| 103 | [二叉树的锯齿形层次遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第103号问题:二叉树的锯齿形层次遍历.md) | -| 107 | [二叉树的层次遍历 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第107号问题:二叉树的层次遍历II.md) | -| 118 | [杨辉三角](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第118号问题:杨辉三角.md) | -| 119 | [杨辉三角II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第119号问题:杨辉三角II.md) | -| 110 | [平衡二叉树](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第110号问题:平衡二叉树.md) | -| 121 | [买卖股票的最佳时机](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第121号问题:买卖股票的最佳时机.md) | -| 122 | [买卖股票的最佳时机II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第122号问题:买卖股票的最佳时机II.md) | -| 123 | [买卖股票的最佳时机III](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第123号问题:买卖股票的最佳时机III.md) | -| 125 | [验证回文串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第125号问题:验证回文串.md) | -| 131 | [分割回文串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第131号问题:分割回文串.md) | -| 136 | [只出现一次的数字](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第136号问题:只出现一次的数字.md) | -| 138 | [复制带随机指针](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第138号问题:复制带随机指针.md) | -| 139 | [单词拆分](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第139号问题:单词拆分.md) | -| 141 | [环形链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第141号问题:环形链表.md) | -| 144 | [二叉树的前序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第144号问题:二叉树的前序遍历.md) | -| 145 | [二叉树的后序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第145号问题:二叉树的后序遍历.md) | -| 146 | [LRU缓存机制](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第146号问题:LRU缓存机制.md) | -| 150 | [逆波兰表达式求值](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第150号问题:逆波兰表达式求值.md) | -| 167 | [两数之和 II - 输入有序数组](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第167号问题:两数之和II-输入有序数组.md) | -| 172 | [阶乘后的零](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第172号问题:阶乘后的零.md) | -| 187 | [重复的 DNA 序列](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第187号问题:重复的DNA序列.md) | -| 191 | [位1的个数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第191号问题:位1的个数.md) | -| 199 | [二叉树的右视图](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第199号问题:二叉树的右视图.md) | -| 201 | [数字范围按位与](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第201号问题:数字范围按位与.md) | -| 203 | [移除链表元素](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第203号问题:移除链表元素.md) | -| 206 | [反转链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第206号问题:反转链表.md) | -| 209 | [长度最小的子数组](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第209号问题:长度最小的子数组.md) | -| 219 | [存在重复元素 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第219号问题:存在重复元素II.md) | -| 231 | [2的幂](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第231号问题:2的幂.md) | -| 237 | [删除链表中的节点](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第237号问题:删除链表中的节点.md) | -| 239 | [滑动窗口最大值](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第239号问题:滑动窗口最大值.md) | -| 268 | [缺失数字](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第268号问题:缺失数字.md) | -| 279 | [完全平方数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第279号问题:完全平方数.md) | -| 283 | [移动零](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第283号问题:移动零.md) | -| 295 | [数据流的中位数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第295号问题:数据流的中位数.md) | -| 301 | [删除无效的括号](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第301号问题:删除无效的括号.md) | -| 326 | [3 的幂](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第326号问题:3的幂.md) | -| 328 | [奇偶链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第328号问题:奇偶链表.md) | -| 342 | [4的幂](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第342号问题:4的幂.md) | -| 344 | [反转字符串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第344号问题:反转字符串.md) | -| 347 | [前K个高频元素](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第347号问题:前K个高频元素.md) | -| 349 | [两个数组的交集](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第349号问题:两个数组的交集.md) | -| 350 | [两个数组的交集 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第350号问题:两个数组的交集II.md) | -| 445 | [两数相加 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第445号问题:两数相加II.md) | -| 447 | [回旋镖的数量](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第447号问题:回旋镖的数量.md) | -| 454 | [四数相加 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第454号问题:四数相加II.md) | -| 642 | [设计一个搜索自动完成系统](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第642号问题:设计一个搜索自动完成系统.md) | -| 690 | [员工的重要性](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第690号问题:员工的重要性.md) | -| 877 | [石子游戏](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第877号问题:石子游戏.md) | - - - -## 补充 -**该仓库保持随时更新**。 - -2018-12-29 说明: - -[《2019年LeetCodeAnimationd的更新计划》](https://mp.weixin.qq.com/s?__biz=MzUyNjQxNjYyMg==&mid=2247484375&idx=1&sn=5a5482d9863342650d8b43bb59171f7c&chksm=fa0e6c56cd79e540115e52500b80c8e72001c87ddceb7c0ae1de166fd283d632b960cde41aca&token=578760218&lang=zh_CN#rd) - -2018-12-07 说明: - -为了更好的做好 LeetCode 动画,笔者正在努力的学习更多的数据结构与算法。 - -笔者目前正在写数据结构的文章与动画,将《动画图解数据结构》系列文章写完后将继续更新此仓库。 - -邮箱:misterbigbooo@gmail.com - -喜欢就 star❤️ 一下吧! - -## 和我交流 - - - -| 二维码 | 说明 | -| --- | --- | -|![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) | 欢迎前来和程序员小吴一起学算法 | -|![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190625171010.jpeg) | 扫描小程序阅读最新算法文章 | - - - +文章最新首发于微信公众号 **吴师兄学算法**,您可以关注获取最新的文章。 +为了帮助大家更好的入门学习算法,经过半年的积累,我给大家整理了《剑指 Offer》系列的四十道题目,都是算法面试的高频题目,每一道题目我都提供详细的分析、精美的配图、易于理解的动画视频,适合那些第一次刷题的同学,当然,也适合重复刷题的老手再次学习巩固基础。 +![](https://weixin-1257126549.cos.ap-guangzhou.myqcloud.com/blog/qebp5.png) +文章同步博客地址:https://blog.algomooc.com/ +## 汇总 +| 序号 | 题目&题解 | 动画 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 0 | [十大经典排序算法动画与解析,看我就够了!(配代码完全版)](https://mp.weixin.qq.com/s/vn3KiV-ez79FmbZ36SX9lg) | | +| 1 | [两数之和](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第1号问题:两数之和.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/av47v.gif) | +| 2 | [两数相加](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第2号问题:两数相加.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz933.gif) | +| 3 | [无重复字符的最长子串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第3号问题:无重复字符的最长子串.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/vxa7f.gif) | +| 4 | [寻找两个有序数组的中位数](https://mp.weixin.qq.com/s/FBlH7o-ssj_iMEPLcvsY2w) | | +| 9 | [回文数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第9号问题:回文数.md) | | +| 10 | [正则表达式匹配](https://mp.weixin.qq.com/s/ZoytuPt5dfP5pMODbuKnCQ) | | +| 11 | [盛最多水的容器](https://mp.weixin.qq.com/s/0PCW-7JzU8rfrLA5GQmFiQ) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/en8u4.gif) | +| 15 | [三数之和](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第15号问题:三数之和.md) | | +| 19 | [删除链表的倒数第 N 个节点](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第19号问题:删除链表的倒数第N个节点.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/metqn.gif) | +| 20 | [有效的括号](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第20号问题:有效的括号.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ey3lr.gif) | +| 21 | [合并两个有序链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第21号问题:合并两个有序链表.md) | | +| 23 | [合并 K 个排序链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第23号问题:合并K个排序链表.md) | | +| 24 | [两两交换链表中的节点](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第24号问题:两两交换链表中的节点.md) | | +| 25 | [K 个一组翻转链表](https://mp.weixin.qq.com/s/YOz66mJchVIEQjA7TBV2cg) | | +| 26 | [删除排序数组中的重复项](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第26号问题:删除排序数组中的重复项.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/4tk72.gif) | +| 32 | [最长有效括号](https://mp.weixin.qq.com/s/CPIDKHsg3ROT10rVFDyDJQ) | | +| 38 | [报数](https://mp.weixin.qq.com/s/DKXJR8pNX3fKGvtSn0TEjw) | | +| 41 | [缺失的第一个正数]() | | +| 66 | [加一](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第66号问题:加一.md) | | +| 75 | [颜色分类](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第75号问题:颜色分类.md) | ![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ehgxu.gif) | +| 86 | [分割链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第86号问题:分割链表.md) | | +| 92 | [反转链表 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第92号问题:反转链表II.md) | | +| 94 | [二叉树的中序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第94号问题:二叉树的中序遍历.md) | | +| 101 | [对称二叉树](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第101号问题:对称二叉树.md) | | +| 102 | [二叉树的层序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第102号问题:二叉树的层序遍历.md) | | +| 103 | [二叉树的锯齿形层次遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第103号问题:二叉树的锯齿形层次遍历.md) | | +| 107 | [二叉树的层次遍历 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第107号问题:二叉树的层次遍历II.md) | | +| 118 | [杨辉三角](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第118号问题:杨辉三角.md) | | +| 119 | [杨辉三角II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第119号问题:杨辉三角II.md) | | +| 110 | [平衡二叉树](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第110号问题:平衡二叉树.md) | | +| 121 | [买卖股票的最佳时机](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第121号问题:买卖股票的最佳时机.md) | | +| 122 | [买卖股票的最佳时机II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第122号问题:买卖股票的最佳时机II.md) | | +| 123 | [买卖股票的最佳时机III](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第123号问题:买卖股票的最佳时机III.md) | | +| 125 | [验证回文串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第125号问题:验证回文串.md) | | +| 131 | [分割回文串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第131号问题:分割回文串.md) | | +| 136 | [只出现一次的数字](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第136号问题:只出现一次的数字.md) | | +| 138 | [复制带随机指针](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第138号问题:复制带随机指针.md) | | +| 139 | [单词拆分](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第139号问题:单词拆分.md) | | +| 141 | [环形链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第141号问题:环形链表.md) | | +| 144 | [二叉树的前序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第144号问题:二叉树的前序遍历.md) | | +| 145 | [二叉树的后序遍历](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第145号问题:二叉树的后序遍历.md) | | +| 146 | [LRU缓存机制](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第146号问题:LRU缓存机制.md) | | +| 150 | [逆波兰表达式求值](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第150号问题:逆波兰表达式求值.md) | | +| 153 | [寻找旋转排序数组中的最小值]() | | +| 164 | [最大间距](https://mp.weixin.qq.com/s/xHxjCDdFZyCW2pnY6Cz8SQ) | | +| 167 | [两数之和 II - 输入有序数组](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第167号问题:两数之和II-输入有序数组.md) | | +| 169 | [求众数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第169号问题:求众数.md) | | +| 172 | [阶乘后的零](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第172号问题:阶乘后的零.md) | | +| 187 | [重复的 DNA 序列](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第187号问题:重复的DNA序列.md) | | +| 191 | [位1的个数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第191号问题:位1的个数.md) | | +| 199 | [二叉树的右视图](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第199号问题:二叉树的右视图.md) | | +| 201 | [数字范围按位与](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第201号问题:数字范围按位与.md) | | +| 203 | [移除链表元素](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第203号问题:移除链表元素.md) | | +| 206 | [反转链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第206号问题:反转链表.md) | | +| 209 | [长度最小的子数组](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第209号问题:长度最小的子数组.md) | | +| 219 | [存在重复元素 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第219号问题:存在重复元素II.md) | | +| 229 | [求众数II](https://mp.weixin.qq.com/s/ObO4eQbjp1s1g_WXPkjixQ) | | +| 231 | [2的幂](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第231号问题:2的幂.md) | | +| 232 | [使用栈实现队列](https://mp.weixin.qq.com/s/j6w94_PjvsL9Dip_xBcqcg) | | +| 237 | [删除链表中的节点](https://mp.weixin.qq.com/s/2XdUeDNblryFpXpTUgsaMQ) | | +| 239 | [滑动窗口最大值](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第239号问题:滑动窗口最大值.md) | | +| 242 | [有效的字母异位词](https://mp.weixin.qq.com/s/o5HTxmOgpftSaQdebS9zyQ) | | +| 268 | [缺失数字](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第268号问题:缺失数字.md) | | +| 279 | [完全平方数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第279号问题:完全平方数.md) | | +| 283 | [移动零](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第283号问题:移动零.md) | | +| 295 | [数据流的中位数](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第295号问题:数据流的中位数.md) | | +| 301 | [删除无效的括号](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第301号问题:删除无效的括号.md) | | +| 319 | [灯泡开关](https://mp.weixin.qq.com/s/u35RGvT5Bc2o7jM-Uu_ZYA) | | +| 326 | [3 的幂](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第326号问题:3的幂.md) | | +| 328 | [奇偶链表](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第328号问题:奇偶链表.md) | | +| 342 | [4的幂](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第342号问题:4的幂.md) | | +| 344 | [反转字符串](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第344号问题:反转字符串.md) | | +| 347 | [前K个高频元素](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第347号问题:前K个高频元素.md) | | +| 349 | [两个数组的交集](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第349号问题:两个数组的交集.md) | | +| 350 | [两个数组的交集 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第350号问题:两个数组的交集II.md) | | +| 445 | [两数相加 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第445号问题:两数相加II.md) | | +| 447 | [回旋镖的数量](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第447号问题:回旋镖的数量.md) | | +| 454 | [四数相加 II](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第454号问题:四数相加II.md) | | +| 642 | [设计一个搜索自动完成系统](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第642号问题:设计一个搜索自动完成系统.md) | | +| 690 | [员工的重要性](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第690号问题:员工的重要性.md) | | +| 739 | [每日温度](https://mp.weixin.qq.com/s/3kDSOHyd-qOw7apzj0Z9YQ) | | +| 877 | [石子游戏](https://github.com/MisterBooo/LeetCodeAnimation/tree/master/notes/LeetCode第877号问题:石子游戏.md) | | +| 1025 | [除数博弈](https://mp.weixin.qq.com/s/0u6z02QYj1OpAwf54k8-Dw) | | +| 1099 | [小于 K 的两数之和](https://mp.weixin.qq.com/s/S6BbLeP_th_9JheNX7NN-w) | | + + + + + +![](Pictures/qrcode.jpg) diff --git a/anima.py b/anima.py new file mode 100755 index 00000000..a500e642 --- /dev/null +++ b/anima.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +import fire + +import anima + +if __name__ == '__main__': + fire.Fire(anima.Anima()) diff --git a/anima/__init__.py b/anima/__init__.py new file mode 100644 index 00000000..108d14e2 --- /dev/null +++ b/anima/__init__.py @@ -0,0 +1,10 @@ +from anima.create import create_solution + + +class Anima: + """ + LeetCode Animation Manager + """ + + def new(self, id: str, title: str): + create_solution(id, title) diff --git a/anima/base.py b/anima/base.py new file mode 100644 index 00000000..7f5ad75d --- /dev/null +++ b/anima/base.py @@ -0,0 +1,12 @@ +import os +from pathlib import Path + + +def get_project_path() -> Path: + script_path = os.path.realpath(__file__) + project_path = Path(script_path).parent.parent + return project_path + + +def get_md_template_path() -> Path: + return get_project_path() / 'template' / 'template.md' diff --git a/anima/create.py b/anima/create.py new file mode 100644 index 00000000..e82ab9bf --- /dev/null +++ b/anima/create.py @@ -0,0 +1,22 @@ +import shutil + +from anima.base import get_project_path, get_md_template_path +from anima.model import ProblemInfo, Solution + + +def create_solution(problem_id: int, problem_title: str) -> None: + problem = ProblemInfo(problem_id, problem_title) + solution_dir = get_project_path() / problem.title_slug() + + if solution_dir.exists(): + print(f'创建失败,文件夹 {solution_dir} 已存在') + exit(1) + solution_dir.mkdir() + + solution = Solution.create(problem, solution_dir) + + template = get_md_template_path() + shutil.copy(template, solution.doc_path()) + + print(f'题解框架创建完毕,位于文件夹 {solution.path}') + diff --git a/anima/model.py b/anima/model.py new file mode 100644 index 00000000..9f386b6a --- /dev/null +++ b/anima/model.py @@ -0,0 +1,47 @@ +import re +from dataclasses import dataclass +from pathlib import Path + + +@dataclass +class ProblemInfo: + id: int + title: str + + def title_slug(self): + title_parts = re.split(r'\s+', self.title) + return f'{self.id:04d}-' + '-'.join(title_parts) + + +@dataclass +class Solution: + problem: ProblemInfo + path: Path + + @classmethod + def create(cls, problem: ProblemInfo, path: Path): + solution = Solution(problem, path) + solution._create_dirs() + return solution + + def _create_dirs(self): + self.animation_path().mkdir() + self.article_path().mkdir() + self.code_path().mkdir() + (self.animation_path() / 'Animation.m4v').touch() + (self.animation_path() / 'Animation.gif').touch() + + def _path_to(self, s: str) -> Path: + return self.path / s + + def animation_path(self) -> Path: + return self.path / 'Animation' + + def article_path(self) -> Path: + return self.path / 'Article' + + def doc_path(self) -> Path: + return self.article_path() / (self.problem.title_slug() + '.md') + + def code_path(self) -> Path: + return self.path / 'Code' diff --git a/contributing.md b/contributing.md new file mode 100644 index 00000000..3634c6a3 --- /dev/null +++ b/contributing.md @@ -0,0 +1,18 @@ +## LeetCode Animation 管理脚本 anima 使用说明 + +需要使用 Python 3.6+ 版本 + +安装依赖: + +``` +pip install -r requirements.txt +``` + +### 创建新题解 + +生成题解目录框架,以及 Markdown 文件模板。 + +``` +python anima.py new 1 'Two Sum' +``` + diff --git "a/notes/LeetCode\347\254\254101\345\217\267\351\227\256\351\242\230\357\274\232\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.md" "b/notes/LeetCode\347\254\254101\345\217\267\351\227\256\351\242\230\357\274\232\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.md" index 46b6ac1f..7b4310c9 100644 --- "a/notes/LeetCode\347\254\254101\345\217\267\351\227\256\351\242\230\357\274\232\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.md" +++ "b/notes/LeetCode\347\254\254101\345\217\267\351\227\256\351\242\230\357\274\232\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.md" @@ -45,4 +45,4 @@ class Solution { } ``` -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/kbvfp.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254102\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" "b/notes/LeetCode\347\254\254102\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" index 8b2a793d..dca36de2 100644 --- "a/notes/LeetCode\347\254\254102\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" +++ "b/notes/LeetCode\347\254\254102\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" @@ -48,7 +48,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181112084159.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/2elr5.gif) @@ -96,4 +96,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/96yrg.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254103\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.md" "b/notes/LeetCode\347\254\254103\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.md" index 9b900c01..53111ad4 100644 --- "a/notes/LeetCode\347\254\254103\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.md" +++ "b/notes/LeetCode\347\254\254103\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.md" @@ -45,12 +45,12 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502103236.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xuoqo.gif) ### 代码实现 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502103307.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/7mnmj.png) -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/q9yt7.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254107\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206II.md" "b/notes/LeetCode\347\254\254107\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206II.md" index 2e8e5f52..7fe597f1 100644 --- "a/notes/LeetCode\347\254\254107\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206II.md" +++ "b/notes/LeetCode\347\254\254107\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206II.md" @@ -46,16 +46,16 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181112203355.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/varp8.gif) ### 代码实现 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181112203645.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/9iccc.png) -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/tdqxb.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254110\345\217\267\351\227\256\351\242\230\357\274\232\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" "b/notes/LeetCode\347\254\254110\345\217\267\351\227\256\351\242\230\357\274\232\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" index c9b6e73c..128cf7fe 100644 --- "a/notes/LeetCode\347\254\254110\345\217\267\351\227\256\351\242\230\357\274\232\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" +++ "b/notes/LeetCode\347\254\254110\345\217\267\351\227\256\351\242\230\357\274\232\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" @@ -44,4 +44,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/z7w57.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254118\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222.md" "b/notes/LeetCode\347\254\254118\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222.md" index 235355f5..bdcc1688 100644 --- "a/notes/LeetCode\347\254\254118\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222.md" +++ "b/notes/LeetCode\347\254\254118\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222.md" @@ -2,7 +2,7 @@ # 杨辉三角 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190507201419.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/yf0dp.png) > 本文首发于公众号「五分钟学算法」,是[图解 LeetCode](https://github.com/MisterBooo/LeetCodeAnimation) 系列文章之一。 > @@ -29,7 +29,7 @@ 给定一个非负整数 *numRows,*生成杨辉三角的前 *numRows* 行。 -![img](https://upload.wikimedia.org/wikipedia/commons/0/0d/PascalTriangleAnimated2.gif) +![img](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ehk16.gif) 在杨辉三角中,每个数是它左上方和右上方的数的和。 @@ -89,7 +89,7 @@ class Solution { 给定一个非负索引 *k*,其中 *k* ≤ 33,返回杨辉三角的第 *k* 行。 -![img](https://upload.wikimedia.org/wikipedia/commons/0/0d/PascalTriangleAnimated2.gif) +![img](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ehk16.gif) 在杨辉三角中,每个数是它左上方和右上方的数的和。 @@ -140,7 +140,7 @@ class Solution { ## 一个有趣的结论 感兴趣小伙伴的可以搜索一下李永乐讲得抽奖概率相关的视频,里面提及到了很多杨辉三角的神奇特点。 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190509165331.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ggto5.gif) -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/bhn6z.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254119\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222II.md" "b/notes/LeetCode\347\254\254119\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222II.md" index 235355f5..d9c45ed6 100644 --- "a/notes/LeetCode\347\254\254119\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222II.md" +++ "b/notes/LeetCode\347\254\254119\345\217\267\351\227\256\351\242\230\357\274\232\346\235\250\350\276\211\344\270\211\350\247\222II.md" @@ -2,7 +2,7 @@ # 杨辉三角 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190507201419.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/inihp.png) > 本文首发于公众号「五分钟学算法」,是[图解 LeetCode](https://github.com/MisterBooo/LeetCodeAnimation) 系列文章之一。 > @@ -29,7 +29,7 @@ 给定一个非负整数 *numRows,*生成杨辉三角的前 *numRows* 行。 -![img](https://upload.wikimedia.org/wikipedia/commons/0/0d/PascalTriangleAnimated2.gif) +![img](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ks594.gif) 在杨辉三角中,每个数是它左上方和右上方的数的和。 @@ -89,7 +89,7 @@ class Solution { 给定一个非负索引 *k*,其中 *k* ≤ 33,返回杨辉三角的第 *k* 行。 -![img](https://upload.wikimedia.org/wikipedia/commons/0/0d/PascalTriangleAnimated2.gif) +![img](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ks594.gif) 在杨辉三角中,每个数是它左上方和右上方的数的和。 @@ -140,7 +140,7 @@ class Solution { ## 一个有趣的结论 感兴趣小伙伴的可以搜索一下李永乐讲得抽奖概率相关的视频,里面提及到了很多杨辉三角的神奇特点。 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190509165331.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0b495.gif) -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/besbk.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254121\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" "b/notes/LeetCode\347\254\254121\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" index 31163bd2..7c93a2c6 100644 --- "a/notes/LeetCode\347\254\254121\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" +++ "b/notes/LeetCode\347\254\254121\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" @@ -290,4 +290,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/w3oig.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254122\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" "b/notes/LeetCode\347\254\254122\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" index c0c0405d..c5afb2a2 100644 --- "a/notes/LeetCode\347\254\254122\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" +++ "b/notes/LeetCode\347\254\254122\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" @@ -69,7 +69,7 @@ ### 图片描述 -![买卖股票的最佳时机 II](https://upload-images.jianshu.io/upload_images/1940317-07904ca85dc535a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![买卖股票的最佳时机 II](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/nkvdj.png) ### 代码实现 @@ -93,7 +93,7 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/4s1oh.png) diff --git "a/notes/LeetCode\347\254\254123\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.md" "b/notes/LeetCode\347\254\254123\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.md" index 949aad8d..19490160 100644 --- "a/notes/LeetCode\347\254\254123\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.md" +++ "b/notes/LeetCode\347\254\254123\345\217\267\351\227\256\351\242\230\357\274\232\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.md" @@ -289,7 +289,7 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/pr1o6.png) diff --git "a/notes/LeetCode\347\254\254125\345\217\267\351\227\256\351\242\230\357\274\232\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.md" "b/notes/LeetCode\347\254\254125\345\217\267\351\227\256\351\242\230\357\274\232\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.md" index f3ff2d81..24752799 100644 --- "a/notes/LeetCode\347\254\254125\345\217\267\351\227\256\351\242\230\357\274\232\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.md" +++ "b/notes/LeetCode\347\254\254125\345\217\267\351\227\256\351\242\230\357\274\232\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.md" @@ -75,4 +75,4 @@ class Solution { ``` -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/k5tcp.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254131\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.md" "b/notes/LeetCode\347\254\254131\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.md" index 23be125c..cbef3ea8 100644 --- "a/notes/LeetCode\347\254\254131\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.md" +++ "b/notes/LeetCode\347\254\254131\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.md" @@ -90,4 +90,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/buypf.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254136\345\217\267\351\227\256\351\242\230\357\274\232\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" "b/notes/LeetCode\347\254\254136\345\217\267\351\227\256\351\242\230\357\274\232\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" index dc27cc6d..35993de3 100644 --- "a/notes/LeetCode\347\254\254136\345\217\267\351\227\256\351\242\230\357\274\232\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" +++ "b/notes/LeetCode\347\254\254136\345\217\267\351\227\256\351\242\230\357\274\232\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" @@ -49,7 +49,7 @@ ### 动画演示 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190115181920.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8720h.gif) @@ -79,10 +79,10 @@ ### 动画再演示 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190115190719.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/5uz1n.gif) -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/2hfta.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254138\345\217\267\351\227\256\351\242\230\357\274\232\345\244\215\345\210\266\345\270\246\351\232\217\346\234\272\346\214\207\351\222\210.md" "b/notes/LeetCode\347\254\254138\345\217\267\351\227\256\351\242\230\357\274\232\345\244\215\345\210\266\345\270\246\351\232\217\346\234\272\346\214\207\351\222\210.md" index a86c37c2..17d8348d 100644 --- "a/notes/LeetCode\347\254\254138\345\217\267\351\227\256\351\242\230\357\274\232\345\244\215\345\210\266\345\270\246\351\232\217\346\234\272\346\214\207\351\222\210.md" +++ "b/notes/LeetCode\347\254\254138\345\217\267\351\227\256\351\242\230\357\274\232\345\244\215\345\210\266\345\270\246\351\232\217\346\234\272\346\214\207\351\222\210.md" @@ -37,7 +37,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502092750.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/whvl5.gif) ### 代码实现 @@ -77,5 +77,5 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ijlxu.png) diff --git "a/notes/LeetCode\347\254\254139\345\217\267\351\227\256\351\242\230\357\274\232\345\215\225\350\257\215\346\213\206\345\210\206.md" "b/notes/LeetCode\347\254\254139\345\217\267\351\227\256\351\242\230\357\274\232\345\215\225\350\257\215\346\213\206\345\210\206.md" index 31631073..e1ce04e7 100644 --- "a/notes/LeetCode\347\254\254139\345\217\267\351\227\256\351\242\230\357\274\232\345\215\225\350\257\215\346\213\206\345\210\206.md" +++ "b/notes/LeetCode\347\254\254139\345\217\267\351\227\256\351\242\230\357\274\232\345\215\225\350\257\215\346\213\206\345\210\206.md" @@ -67,4 +67,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8s46l.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254141\345\217\267\351\227\256\351\242\230\357\274\232\347\216\257\345\275\242\351\223\276\350\241\250.md" "b/notes/LeetCode\347\254\254141\345\217\267\351\227\256\351\242\230\357\274\232\347\216\257\345\275\242\351\223\276\350\241\250.md" index 3ba37185..58c0b71f 100644 --- "a/notes/LeetCode\347\254\254141\345\217\267\351\227\256\351\242\230\357\274\232\347\216\257\345\275\242\351\223\276\350\241\250.md" +++ "b/notes/LeetCode\347\254\254141\345\217\267\351\227\256\351\242\230\357\274\232\347\216\257\345\275\242\351\223\276\350\241\250.md" @@ -22,7 +22,7 @@ 解释:链表中有一个环,其尾部连接到第二个节点。 ``` -![](https://assets.leetcode.com/uploads/2018/12/07/circularlinkedlist.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/vweoq.png) **示例 2:** @@ -32,7 +32,7 @@ 解释:链表中有一个环,其尾部连接到第一个节点。 ``` -![](https://assets.leetcode.com/uploads/2018/12/07/circularlinkedlist_test2.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/kxbrz.png) **示例 3:** @@ -42,7 +42,7 @@ 解释:链表中没有环。 ``` -![](https://assets.leetcode.com/uploads/2018/12/07/circularlinkedlist_test3.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/w3vsg.png) **进阶:** @@ -59,7 +59,7 @@ ### 动画描述 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190627145511.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/mj4qo.gif) ### 代码实现 diff --git "a/notes/LeetCode\347\254\254144\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.md" "b/notes/LeetCode\347\254\254144\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.md" index 1095a88d..d9ec4ef7 100644 --- "a/notes/LeetCode\347\254\254144\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.md" +++ "b/notes/LeetCode\347\254\254144\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.md" @@ -40,7 +40,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502105303.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/uu82j.gif) ### 代码实现 @@ -76,4 +76,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/obddd.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254145\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.md" "b/notes/LeetCode\347\254\254145\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.md" index 913d48d5..1ccd9840 100644 --- "a/notes/LeetCode\347\254\254145\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.md" +++ "b/notes/LeetCode\347\254\254145\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.md" @@ -42,7 +42,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181110154019.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/y7nxo.gif) ### 代码实现 @@ -69,4 +69,4 @@ public class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8yuu3.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254146\345\217\267\351\227\256\351\242\230\357\274\232LRU\347\274\223\345\255\230\346\234\272\345\210\266.md" "b/notes/LeetCode\347\254\254146\345\217\267\351\227\256\351\242\230\357\274\232LRU\347\274\223\345\255\230\346\234\272\345\210\266.md" index 4a09df57..2d39bd24 100644 --- "a/notes/LeetCode\347\254\254146\345\217\267\351\227\256\351\242\230\357\274\232LRU\347\274\223\345\255\230\346\234\272\345\210\266.md" +++ "b/notes/LeetCode\347\254\254146\345\217\267\351\227\256\351\242\230\357\274\232LRU\347\274\223\345\255\230\346\234\272\345\210\266.md" @@ -55,7 +55,7 @@ cache.get(4); // 返回 4 ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502105833.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/90896.gif) ### 代码实现 @@ -96,4 +96,4 @@ private: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/inind.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254150\345\217\267\351\227\256\351\242\230\357\274\232\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.md" "b/notes/LeetCode\347\254\254150\345\217\267\351\227\256\351\242\230\357\274\232\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.md" index ab3122dc..635d293b 100644 --- "a/notes/LeetCode\347\254\254150\345\217\267\351\227\256\351\242\230\357\274\232\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.md" +++ "b/notes/LeetCode\347\254\254150\345\217\267\351\227\256\351\242\230\357\274\232\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.md" @@ -59,7 +59,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181108162351.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/4ieg7.gif) ### 代码实现 @@ -98,4 +98,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fhe4g.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25415\345\217\267\351\227\256\351\242\230\357\274\232\344\270\211\346\225\260\344\271\213\345\222\214.md" "b/notes/LeetCode\347\254\25415\345\217\267\351\227\256\351\242\230\357\274\232\344\270\211\346\225\260\344\271\213\345\222\214.md" index 7b3cc4ee..9bdedee8 100644 --- "a/notes/LeetCode\347\254\25415\345\217\267\351\227\256\351\242\230\357\274\232\344\270\211\346\225\260\344\271\213\345\222\214.md" +++ "b/notes/LeetCode\347\254\25415\345\217\267\351\227\256\351\242\230\357\274\232\344\270\211\346\225\260\344\271\213\345\222\214.md" @@ -55,4 +55,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/m1phx.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254167\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214II-\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.md" "b/notes/LeetCode\347\254\254167\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214II-\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.md" index 4201b5aa..b3b7d569 100644 --- "a/notes/LeetCode\347\254\254167\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214II-\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.md" +++ "b/notes/LeetCode\347\254\254167\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214II-\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.md" @@ -37,7 +37,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502110607.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/59rnm.gif) ### 代码实现 @@ -68,4 +68,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/9lro6.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254169\345\217\267\351\227\256\351\242\230\357\274\232\346\261\202\344\274\227\346\225\260.md" "b/notes/LeetCode\347\254\254169\345\217\267\351\227\256\351\242\230\357\274\232\346\261\202\344\274\227\346\225\260.md" new file mode 100644 index 00000000..6babf26f --- /dev/null +++ "b/notes/LeetCode\347\254\254169\345\217\267\351\227\256\351\242\230\357\274\232\346\261\202\344\274\227\346\225\260.md" @@ -0,0 +1,163 @@ +# 【数组中超过一半的数字】三种解法,最后一个解法太牛逼了! + +今天分享的题目来源于 LeetCode 上第 169 号问题:求众数(求数组中超过一半的数字)。题目难度为 Easy,目前通过率为 45.8% 。 + +最后一种解法 **Cool** !!! + +# 题目描述 + +给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 + +你可以假设数组是非空的,并且给定的数组总是存在众数。 + +**示例 1:** + +``` +输入: [3,2,3] +输出: 3 +``` + +**示例 2:** + +``` +输入: [2,2,1,1,1,2,2] +输出: 2 +``` + +# 题目解析 + +题目意思很好理解:给你一个数组,里面有一个数字出现的次数超过了一半,你要找到这个数字并返回。 + +## 解法一:暴力解法 + +遍历整个数组,同时统计每个数字出现的次数。 + +最后将出现次数大于一半的元素返回即可。 + +### 动画描述 + +![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190626114223.gif) + +### **代码实现** + +```java +class Solution { + public int majorityElement(int[] nums) { + int majorityCount = nums.length/2; + + for (int num : nums) { + int count = 0; + for (int elem : nums) { + if (elem == num) { + count += 1; + } + } + if (count > majorityCount) { + return num; + } + + } + } +} +``` + +### 复杂度分析 + +**时间复杂度**:O(n2) + +暴力解法包含两重嵌套的 for 循环,每一层 n 次迭代,因此时间复杂度为 O(n2) 。 + +**空间复杂度**:O(1) + +暴力解法没有分配任何与输入规模成比例的额外的空间,因此空间复杂度为 O(1)。 + +## 解法二:哈希表法 + +这个问题可以视为查找问题,对于查找问题往往可以使用时间复杂度为 O(1) 的 **哈希表**,通过以空间换时间的方式进行优化。 + +直接遍历整个 **数组** ,将每一个数字(num)与它出现的次数(count)存放在 **哈希表** 中,同时判断该数字出现次数是否是最大的,动态更新 maxCount,最后输出 maxNum。 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/bbjtv.gif) + +### 代码实现 + +```java +class Solution { + public int majorityElement(int[] nums) { + Map map = new HashMap<>(); + // maxNum 表示元素,maxCount 表示元素出现的次数 + int maxNum = 0, maxCount = 0; + for (int num: nums) { + int count = map.getOrDefault(num, 0) + 1; + map.put(num, count); + if (count > maxCount) { + maxCount = count; + maxNum = num; + } + } + return maxNum; + } +} +``` + +### 复杂度分析 + +**时间复杂度**:O(n) + +总共有一个循环,里面哈希表的插入是常数时间的,因此时间复杂度为 O(n)。 + +**空间复杂度**:O(n) + +哈希表占用了额外的空间 O(n),因此空间复杂度为 O(n)。 + +## 解法三:摩尔投票法 + +再来回顾一下题目:寻找数组中超过一半的数字,这意味着数组中**其他数字出现次数的总和都是比不上这个数字出现的次数** 。 + +即如果把 该众数记为 `+1` ,把其他数记为 `−1` ,将它们全部加起来,和是大于 0 的。 + +所以可以这样操作: + +* 设置两个变量 candidate 和 count,**candidate** 用来保存数组中遍历到的某个数字,**count** 表示当前数字的出现次数,一开始 **candidate** 保存为数组中的第一个数字,**count** 为 1 +* 遍历整个数组 +* 如果数字与之前 **candidate** 保存的数字相同,则 **count** 加 1 +* 如果数字与之前 **candidate** 保存的数字不同,则 **count** 减 1 +* 如果出现次数 **count** 变为 0 ,**candidate** 进行变化,保存为当前遍历的那个数字,并且同时把 **count** 重置为 1 +* 遍历完数组中的所有数字即可得到结果 + +### 动画描述 + +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8wyb2.gif) + +### 代码实现 + +```java +class Solution { + public int majorityElement(int[] nums) { + int candidate = nums[0], count = 1; + for (int i = 1; i < nums.length; ++i) { + if (count == 0) { + candidate = nums[i]; + count = 1; + } else if (nums[i] == candidate) { + count++; + } else{ + count--; + } + } + return candidate; + } +} +``` + +### 复杂度分析 + +**时间复杂度**:O(n) + +总共只有一个循环,因此时间复杂度为 O(n)。 + +**空间复杂度**:O(1) + +只需要常数级别的额外空间,因此空间复杂度为 O(1)。 \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254172\345\217\267\351\227\256\351\242\230\357\274\232\351\230\266\344\271\230\345\220\216\347\232\204\351\233\266.md" "b/notes/LeetCode\347\254\254172\345\217\267\351\227\256\351\242\230\357\274\232\351\230\266\344\271\230\345\220\216\347\232\204\351\233\266.md" index 545ff2aa..86ec316a 100644 --- "a/notes/LeetCode\347\254\254172\345\217\267\351\227\256\351\242\230\357\274\232\351\230\266\344\271\230\345\220\216\347\232\204\351\233\266.md" +++ "b/notes/LeetCode\347\254\254172\345\217\267\351\227\256\351\242\230\357\274\232\351\230\266\344\271\230\345\220\216\347\232\204\351\233\266.md" @@ -68,7 +68,7 @@ public class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/tvt94.png) diff --git "a/notes/LeetCode\347\254\254187\345\217\267\351\227\256\351\242\230\357\274\232\351\207\215\345\244\215\347\232\204DNA\345\272\217\345\210\227.md" "b/notes/LeetCode\347\254\254187\345\217\267\351\227\256\351\242\230\357\274\232\351\207\215\345\244\215\347\232\204DNA\345\272\217\345\210\227.md" index 41eb8c7e..1a4aeedc 100644 --- "a/notes/LeetCode\347\254\254187\345\217\267\351\227\256\351\242\230\357\274\232\351\207\215\345\244\215\347\232\204DNA\345\272\217\345\210\227.md" +++ "b/notes/LeetCode\347\254\254187\345\217\267\351\227\256\351\242\230\357\274\232\351\207\215\345\244\215\347\232\204DNA\345\272\217\345\210\227.md" @@ -65,4 +65,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xzbvx.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254191\345\217\267\351\227\256\351\242\230\357\274\232\344\275\2151\347\232\204\344\270\252\346\225\260.md" "b/notes/LeetCode\347\254\254191\345\217\267\351\227\256\351\242\230\357\274\232\344\275\2151\347\232\204\344\270\252\346\225\260.md" index 42c28287..7179ee2c 100644 --- "a/notes/LeetCode\347\254\254191\345\217\267\351\227\256\351\242\230\357\274\232\344\275\2151\347\232\204\344\270\252\346\225\260.md" +++ "b/notes/LeetCode\347\254\254191\345\217\267\351\227\256\351\242\230\357\274\232\344\275\2151\347\232\204\344\270\252\346\225\260.md" @@ -88,4 +88,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/se6v6.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254199\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" "b/notes/LeetCode\347\254\254199\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" index fcf229fc..bd3d000a 100644 --- "a/notes/LeetCode\347\254\254199\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" +++ "b/notes/LeetCode\347\254\254199\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" @@ -36,7 +36,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181115113908.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/i2nzo.gif) ### 代码实现 @@ -67,4 +67,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/afv89.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25419\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\350\212\202\347\202\271.md" "b/notes/LeetCode\347\254\25419\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\350\212\202\347\202\271.md" index 140dfd8f..2c437b3c 100644 --- "a/notes/LeetCode\347\254\25419\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\350\212\202\347\202\271.md" +++ "b/notes/LeetCode\347\254\25419\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\350\212\202\347\202\271.md" @@ -40,7 +40,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181106162853.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/r04hv.gif) ### 代码实现 @@ -75,4 +75,4 @@ public: }; ``` -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xbyq0.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\2541\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214.md" "b/notes/LeetCode\347\254\2541\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214.md" index 0daeb6c9..99469b0b 100644 --- "a/notes/LeetCode\347\254\2541\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214.md" +++ "b/notes/LeetCode\347\254\2541\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\344\271\213\345\222\214.md" @@ -35,7 +35,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181028221055.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/mol6g.gif) ### 代码实现 @@ -58,6 +58,8 @@ public: record[nums[i]] = i; } + + return {}; } }; @@ -65,4 +67,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/wavdg.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254201\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\345\255\227\350\214\203\345\233\264\346\214\211\344\275\215\344\270\216.md" "b/notes/LeetCode\347\254\254201\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\345\255\227\350\214\203\345\233\264\346\214\211\344\275\215\344\270\216.md" index 9e7a70ec..9997d556 100644 --- "a/notes/LeetCode\347\254\254201\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\345\255\227\350\214\203\345\233\264\346\214\211\344\275\215\344\270\216.md" +++ "b/notes/LeetCode\347\254\254201\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\345\255\227\350\214\203\345\233\264\346\214\211\344\275\215\344\270\216.md" @@ -59,4 +59,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/bjgx9.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254203\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.md" "b/notes/LeetCode\347\254\254203\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.md" index 4dc56521..5d842706 100644 --- "a/notes/LeetCode\347\254\254203\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.md" +++ "b/notes/LeetCode\347\254\254203\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.md" @@ -25,7 +25,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181102163006.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/tuy84.gif) ### 代码实现 @@ -86,4 +86,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/sbo5i.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254206\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250.md" "b/notes/LeetCode\347\254\254206\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250.md" index e2e2ba42..b9aea663 100644 --- "a/notes/LeetCode\347\254\254206\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250.md" +++ "b/notes/LeetCode\347\254\254206\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250.md" @@ -31,7 +31,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181101175158.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/voxlq.gif) ### 代码实现 @@ -57,4 +57,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/2z3t0.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254209\345\217\267\351\227\256\351\242\230\357\274\232\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.md" "b/notes/LeetCode\347\254\254209\345\217\267\351\227\256\351\242\230\357\274\232\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.md" index 0b9dfaa1..ff2de23d 100644 --- "a/notes/LeetCode\347\254\254209\345\217\267\351\227\256\351\242\230\357\274\232\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.md" +++ "b/notes/LeetCode\347\254\254209\345\217\267\351\227\256\351\242\230\357\274\232\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.md" @@ -37,25 +37,25 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181027160331.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0ga4f.gif) 设置滑动窗口的长度为 0 ,位于数轴的最左端。 ##### 1 .滑动窗口右端 R 开始移动,直到区间满足给定的条件,也就是和大于 7 ,此时停止于第三个元素 2,当前的最优长度为 4 -![图 1](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8rr0w.jpg) +![图 1](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/lo41y.jpg) ##### 2. 滑动窗口左端 L 开始移动,缩小滑动窗口的大小,停止于第一个元素 3,此时区间和为 6,使得区间和不满足给定的条件(此时不大于 7) -![图片 2](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/77oa4.jpg) +![图片 2](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/j7qnc.jpg) #### 3. 滑动窗口右端 R 继续移动,停止于第四个元素 4,此时和位 10 ,但最优长度仍然为 4 -![图片 3](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8ksiz.jpg) +![图片 3](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/q8dxy.jpg) @@ -95,4 +95,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0fotr.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25420\345\217\267\351\227\256\351\242\230\357\274\232\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.md" "b/notes/LeetCode\347\254\25420\345\217\267\351\227\256\351\242\230\357\274\232\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.md" index c8c25c9b..712e0347 100644 --- "a/notes/LeetCode\347\254\25420\345\217\267\351\227\256\351\242\230\357\274\232\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.md" +++ "b/notes/LeetCode\347\254\25420\345\217\267\351\227\256\351\242\230\357\274\232\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.md" @@ -67,11 +67,12 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181108111124.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xu55u.gif) ### 代码实现 -``` +#### C++ +```c++ class Solution { public: bool isValid(string s) { @@ -109,7 +110,52 @@ public: } }; ``` +#### Java +```java +class Solution { + public boolean isValid(String s) { + //String open="({["; + //String close="]})"; + + Stack stack = new Stack(); + + if(s.length() % 2 != 0) + return false; + + + for (char c : s.toCharArray()) + { + if (c == '(') stack.push(')'); + else if (c == '{') stack.push('}'); + else if (c == '[') stack.push(']'); + else if (stack.isEmpty() || stack.pop() != c) return false; + } + + return stack.isEmpty(); + } + +} +``` +#### Python +```python +class Solution(object): + def isValid(self, s): + open_list = ["[", "{", "("] + close_list = ["]", "}", ")"] + stack = [] + + for i in s: + if i in open_list: + stack.append(i) + elif i in close_list: + pos=close_list.index(i) + if len(stack)>0 and (open_list[pos] == stack[len(stack)-1]): + stack.pop() + else: + return False + if len(stack) == 0: + return True +``` - -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/gkcza.png) diff --git "a/notes/LeetCode\347\254\254219\345\217\267\351\227\256\351\242\230\357\274\232\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240II.md" "b/notes/LeetCode\347\254\254219\345\217\267\351\227\256\351\242\230\357\274\232\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240II.md" index f14ed2cd..fa381ff3 100644 --- "a/notes/LeetCode\347\254\254219\345\217\267\351\227\256\351\242\230\357\274\232\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240II.md" +++ "b/notes/LeetCode\347\254\254219\345\217\267\351\227\256\351\242\230\357\274\232\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240II.md" @@ -43,7 +43,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181031104805.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/gjz5m.gif) ### 代码实现 @@ -84,4 +84,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/msz27.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25421\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.md" "b/notes/LeetCode\347\254\25421\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.md" index 35131f7b..03489cce 100644 --- "a/notes/LeetCode\347\254\25421\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.md" +++ "b/notes/LeetCode\347\254\25421\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.md" @@ -104,4 +104,4 @@ ListNode* mergeTwoOrderedLists(ListNode* pHead1, ListNode* pHead2){ -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/1ubug.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254231\345\217\267\351\227\256\351\242\230\357\274\2322\347\232\204\345\271\202.md" "b/notes/LeetCode\347\254\254231\345\217\267\351\227\256\351\242\230\357\274\2322\347\232\204\345\271\202.md" index 32dd3db4..c9d507a8 100644 --- "a/notes/LeetCode\347\254\254231\345\217\267\351\227\256\351\242\230\357\274\2322\347\232\204\345\271\202.md" +++ "b/notes/LeetCode\347\254\254231\345\217\267\351\227\256\351\242\230\357\274\2322\347\232\204\345\271\202.md" @@ -37,7 +37,7 @@ 首先,先来分析一下 2 的次方数的二进制写法: -![表格](https://user-gold-cdn.xitu.io/2019/2/21/1690d8f7ad5bc000?w=1630&h=190&f=jpeg&s=18479) +![表格](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/3wdpd.jpg) 仔细观察,可以看出 2 的次方数都只有一个 1 ,剩下的都是 0 。根据这个特点,只需要每次判断最低位是否为 1 ,然后向右移位,最后统计 1 的个数即可判断是否是 2 的次方数。 @@ -63,7 +63,7 @@ public: ### 图片描述 -![](https://user-gold-cdn.xitu.io/2019/2/21/1690d8f7ad92ad5e?w=356&h=466&f=jpeg&s=15576) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/1w9lq.jpg) ### 代码实现 @@ -80,4 +80,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/guem9.png) diff --git "a/notes/LeetCode\347\254\254237\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" "b/notes/LeetCode\347\254\254237\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" index e4198d77..b842012f 100644 --- "a/notes/LeetCode\347\254\254237\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" +++ "b/notes/LeetCode\347\254\254237\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" @@ -12,7 +12,7 @@ 现有一个链表 -- head = [4,5,1,9],它可以表示为: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502113234.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/uv8bw.png) @@ -49,7 +49,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181105171450.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/1navy.gif) ### 代码实现 @@ -76,4 +76,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/f5g8p.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254239\345\217\267\351\227\256\351\242\230\357\274\232\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.md" "b/notes/LeetCode\347\254\254239\345\217\267\351\227\256\351\242\230\357\274\232\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.md" index 1b80f714..57a8cd52 100644 --- "a/notes/LeetCode\347\254\254239\345\217\267\351\227\256\351\242\230\357\274\232\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.md" +++ "b/notes/LeetCode\347\254\254239\345\217\267\351\227\256\351\242\230\357\274\232\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.md" @@ -53,7 +53,7 @@ Deque 继承自 Queue(队列),它的直接实现有 ArrayDeque、LinkedLis ### 动画描述 -![动画描述 Made by Jun Chen](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/20whr.gif) +![动画描述 Made by Jun Chen](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/8ggd3.gif) ### 代码实现 @@ -89,4 +89,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/jwm9y.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25423\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.md" "b/notes/LeetCode\347\254\25423\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.md" index b49a6513..b0aa05a1 100644 --- "a/notes/LeetCode\347\254\25423\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.md" +++ "b/notes/LeetCode\347\254\25423\345\217\267\351\227\256\351\242\230\357\274\232\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.md" @@ -24,11 +24,11 @@ **输入** -![图一](https://user-gold-cdn.xitu.io/2019/4/9/169ff8cbaf694440?w=2360&h=614&f=jpeg&s=63179) +![图一](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/u2jnp.jpg) **输出** -![图二](https://user-gold-cdn.xitu.io/2019/4/9/169ff8cbb372e71f?w=2518&h=572&f=jpeg&s=62543) +![图二](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/yc4ac.jpg) ### 题目解析 @@ -44,7 +44,7 @@ ### 动画演示 -![动画演示](https://user-gold-cdn.xitu.io/2019/4/9/169ff8cbb36cb6f7?w=939&h=507&f=gif&s=6542126) +![动画演示](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/iuxmh.gif) ### 代码实现 @@ -136,7 +136,7 @@ class Solution { 这里可以参考 **归并排序 **的分治思想,将这 K 个链表先划分为两个 K/2 个链表,处理它们的合并,然后不停的往下划分,直到划分成只有一个或两个链表的任务,开始合并。 -![归并-分治](https://user-gold-cdn.xitu.io/2019/4/9/169ff8cbb2c891fe?w=953&h=531&f=gif&s=164652) +![归并-分治](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/74ush.gif) ### 代码实现 @@ -186,4 +186,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/jhykq.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25424\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" "b/notes/LeetCode\347\254\25424\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" index bba7a6f1..3861ccef 100644 --- "a/notes/LeetCode\347\254\25424\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" +++ "b/notes/LeetCode\347\254\25424\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271.md" @@ -36,7 +36,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181103145019.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/6kpyu.gif) ### 代码实现 @@ -76,4 +76,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/k8lty.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254268\345\217\267\351\227\256\351\242\230\357\274\232\347\274\272\345\244\261\346\225\260\345\255\227.md" "b/notes/LeetCode\347\254\254268\345\217\267\351\227\256\351\242\230\357\274\232\347\274\272\345\244\261\346\225\260\345\255\227.md" index 4edd3faa..d83e6edc 100644 --- "a/notes/LeetCode\347\254\254268\345\217\267\351\227\256\351\242\230\357\274\232\347\274\272\345\244\261\346\225\260\345\255\227.md" +++ "b/notes/LeetCode\347\254\254268\345\217\267\351\227\256\351\242\230\357\274\232\347\274\272\345\244\261\346\225\260\345\255\227.md" @@ -16,7 +16,7 @@ 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190516113448.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/i47fw.png) ## 题目解析 @@ -32,7 +32,7 @@ 将少了一个数的数组与 0 到 n 之间完整的那个数组进行异或处理,因为相同的数字异或会变为了 0 ,那么全部数字异或后,剩下的就是少了的那个数字。 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190516143539.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/el8zt.png) #### 代码实现1 @@ -75,7 +75,7 @@ class Solution { - 遍历数组计算出原始数组中数字的累积和 - 两和相减,差值就是丢失的那个数字 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190516151203.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fn1ys.gif) ```java //小吴之前担心会数据溢出,不过估计这题考察的不是这个,所以测试用例没写这种吧,还是能 AC 的 @@ -123,4 +123,4 @@ public class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/bfmeg.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25426\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.md" "b/notes/LeetCode\347\254\25426\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.md" index 8222ab6b..d3744057 100644 --- "a/notes/LeetCode\347\254\25426\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.md" +++ "b/notes/LeetCode\347\254\25426\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.md" @@ -62,7 +62,7 @@ for (int i = 0; i < len; i++) { ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181116115601.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/4y1ec.gif) @@ -90,4 +90,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/j3v4r.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254279\345\217\267\351\227\256\351\242\230\357\274\232\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.md" "b/notes/LeetCode\347\254\254279\345\217\267\351\227\256\351\242\230\357\274\232\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.md" index 6a547d72..80883302 100644 --- "a/notes/LeetCode\347\254\254279\345\217\267\351\227\256\351\242\230\357\274\232\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.md" +++ "b/notes/LeetCode\347\254\254279\345\217\267\351\227\256\351\242\230\357\274\232\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.md" @@ -73,7 +73,7 @@ public int numSquares(int n) { ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502113958.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/pf34s.gif) ### 代码实现 @@ -117,4 +117,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/dell9.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254283\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\345\212\250\351\233\266.md" "b/notes/LeetCode\347\254\254283\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\345\212\250\351\233\266.md" index 98bdc9d7..352a9a29 100644 --- "a/notes/LeetCode\347\254\254283\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\345\212\250\351\233\266.md" +++ "b/notes/LeetCode\347\254\254283\345\217\267\351\227\256\351\242\230\357\274\232\347\247\273\345\212\250\351\233\266.md" @@ -32,7 +32,7 @@ 动画如下: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181027160100.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0eeix.gif) 代码如下: @@ -68,7 +68,7 @@ public: 动画如下: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181027160135.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/jodp0.gif) 代码如下: @@ -101,10 +101,11 @@ public: 动画如下: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181030085904.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/gcetr.gif) 代码如下: +C++ Code: -``` +```c++ // 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) @@ -130,8 +131,45 @@ public: ``` +Java Code: + +```java +class Solution { + public void moveZeroes(int[] nums) { + // 双指针 + int i = 0; + for(int j=0; j None: + """ + Do not return anything, modify nums in-place instead. + """ + # 双指针 + i = 0 + for j in range(len(nums)): + # 不为0,前移 + if nums[j] != 0: + nums[i], nums[j] = nums[j], nums[i] + i+=1 +``` -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/o6der.png) diff --git "a/notes/LeetCode\347\254\254295\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\346\215\256\346\265\201\347\232\204\344\270\255\344\275\215\346\225\260.md" "b/notes/LeetCode\347\254\254295\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\346\215\256\346\265\201\347\232\204\344\270\255\344\275\215\346\225\260.md" index 70daee50..c5eca3a4 100644 --- "a/notes/LeetCode\347\254\254295\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\346\215\256\346\265\201\347\232\204\344\270\255\344\275\215\346\225\260.md" +++ "b/notes/LeetCode\347\254\254295\345\217\267\351\227\256\351\242\230\357\274\232\346\225\260\346\215\256\346\265\201\347\232\204\344\270\255\344\275\215\346\225\260.md" @@ -49,7 +49,7 @@ findMedian() -> 2 ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502114925.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/slcao.gif) ### 代码实现 @@ -91,4 +91,4 @@ class MedianFinder { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/k2ihh.gif) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\2542\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240.md" "b/notes/LeetCode\347\254\2542\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240.md" index 8b9ae50a..f0824dce 100644 --- "a/notes/LeetCode\347\254\2542\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240.md" +++ "b/notes/LeetCode\347\254\2542\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240.md" @@ -28,7 +28,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181117122234.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/lchmg.gif) ### 代码实现 diff --git "a/notes/LeetCode\347\254\254301\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\227\240\346\225\210\347\232\204\346\213\254\345\217\267.md" "b/notes/LeetCode\347\254\254301\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\227\240\346\225\210\347\232\204\346\213\254\345\217\267.md" index 57485d34..a0d41a0e 100644 --- "a/notes/LeetCode\347\254\254301\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\227\240\346\225\210\347\232\204\346\213\254\345\217\267.md" +++ "b/notes/LeetCode\347\254\254301\345\217\267\351\227\256\351\242\230\357\274\232\345\210\240\351\231\244\346\227\240\346\225\210\347\232\204\346\213\254\345\217\267.md" @@ -160,4 +160,4 @@ public class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/syhz6.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254326\345\217\267\351\227\256\351\242\230\357\274\2323\347\232\204\345\271\202.md" "b/notes/LeetCode\347\254\254326\345\217\267\351\227\256\351\242\230\357\274\2323\347\232\204\345\271\202.md" index da9887f9..e31cb832 100644 --- "a/notes/LeetCode\347\254\254326\345\217\267\351\227\256\351\242\230\357\274\2323\347\232\204\345\271\202.md" +++ "b/notes/LeetCode\347\254\254326\345\217\267\351\227\256\351\242\230\357\274\2323\347\232\204\345\271\202.md" @@ -55,4 +55,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fzqbe.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254328\345\217\267\351\227\256\351\242\230\357\274\232\345\245\207\345\201\266\351\223\276\350\241\250.md" "b/notes/LeetCode\347\254\254328\345\217\267\351\227\256\351\242\230\357\274\232\345\245\207\345\201\266\351\223\276\350\241\250.md" index 8f2e7222..a50deb19 100644 --- "a/notes/LeetCode\347\254\254328\345\217\267\351\227\256\351\242\230\357\274\232\345\245\207\345\201\266\351\223\276\350\241\250.md" +++ "b/notes/LeetCode\347\254\254328\345\217\267\351\227\256\351\242\230\357\274\232\345\245\207\345\201\266\351\223\276\350\241\250.md" @@ -41,7 +41,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181104142817.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xh0aj.gif) ### 代码实现 @@ -86,4 +86,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ro88e.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254342\345\217\267\351\227\256\351\242\230\357\274\2324\347\232\204\345\271\202.md" "b/notes/LeetCode\347\254\254342\345\217\267\351\227\256\351\242\230\357\274\2324\347\232\204\345\271\202.md" index 4de507d8..151af415 100644 --- "a/notes/LeetCode\347\254\254342\345\217\267\351\227\256\351\242\230\357\274\2324\347\232\204\345\271\202.md" +++ "b/notes/LeetCode\347\254\254342\345\217\267\351\227\256\351\242\230\357\274\2324\347\232\204\345\271\202.md" @@ -77,13 +77,13 @@ class Solution { 将这个二进制数转换成 16 进制表示:0x55555555 。有没有感觉逼格更高点。。。 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190509194742.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/c0s9n.png) ### 图片描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190510090919.jpeg) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ux5pa.jpg) diff --git "a/notes/LeetCode\347\254\254344\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.md" "b/notes/LeetCode\347\254\254344\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.md" index a5a14f5a..76511b4b 100644 --- "a/notes/LeetCode\347\254\254344\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.md" +++ "b/notes/LeetCode\347\254\254344\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.md" @@ -59,4 +59,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/bksj7.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254347\345\217\267\351\227\256\351\242\230\357\274\232\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.md" "b/notes/LeetCode\347\254\254347\345\217\267\351\227\256\351\242\230\357\274\232\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.md" index aa5cfaf1..65ca3387 100644 --- "a/notes/LeetCode\347\254\254347\345\217\267\351\227\256\351\242\230\357\274\232\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.md" +++ "b/notes/LeetCode\347\254\254347\345\217\267\351\227\256\351\242\230\357\274\232\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.md" @@ -37,7 +37,7 @@ 以下十种排序算法,任你挑选! -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190624173156) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/qya5e.png) 可以发现,使用常规的诸如 冒泡、选择、甚至快速排序都是不满足题目要求,它们的时间复杂度都是大于或者等于 O(n log⁡n) ,而题目要求算法的时间复杂度必须优于 O(n log n) 。 @@ -50,7 +50,7 @@ 题目最终需要返回的是前 k 个频率最大的元素,可以想到借助堆这种数据结构,对于 k 频率之后的元素不用再去处理,进一步优化时间复杂度。 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190624213721.jpeg) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/nloow.jpg) 具体操作为: @@ -62,7 +62,7 @@ -![堆中的元素就是前 k 个频率最大的元素](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190624231240.gif) +![堆中的元素就是前 k 个频率最大的元素](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xged1.gif) 代码如下: @@ -115,7 +115,7 @@ class Solution { 首先依旧使用哈希表统计频率,统计完成后,创建一个数组,将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标即可。 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190625100134.jpeg) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/6tge2.jpg) 代码实现如下: diff --git "a/notes/LeetCode\347\254\254349\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206.md" "b/notes/LeetCode\347\254\254349\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206.md" index 0b6c7661..cabf8a20 100644 --- "a/notes/LeetCode\347\254\254349\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206.md" +++ "b/notes/LeetCode\347\254\254349\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206.md" @@ -39,7 +39,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502123122.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xfx1k.gif) ### 代码实现 @@ -72,4 +72,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/y7jcl.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254350\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.md" "b/notes/LeetCode\347\254\254350\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.md" index d67d69a4..63a556a9 100644 --- "a/notes/LeetCode\347\254\254350\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.md" +++ "b/notes/LeetCode\347\254\254350\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.md" @@ -44,7 +44,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181027160512.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/3kc4w.gif) ### 代码实现 @@ -77,8 +77,8 @@ public: #### 执行结果 -![img](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181029083150.png) +![img](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/xdsii.png) -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/3zqhi.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\2543\345\217\267\351\227\256\351\242\230\357\274\232\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.md" "b/notes/LeetCode\347\254\2543\345\217\267\351\227\256\351\242\230\357\274\232\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.md" index d6d62806..98a85f47 100644 --- "a/notes/LeetCode\347\254\2543\345\217\267\351\227\256\351\242\230\357\274\232\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.md" +++ "b/notes/LeetCode\347\254\2543\345\217\267\351\227\256\351\242\230\357\274\232\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.md" @@ -31,7 +31,7 @@ ### 动画描述 -![动画描述](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/o2acw.gif) +![动画描述](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/20ahe.gif) ### 代码实现 diff --git "a/notes/LeetCode\347\254\254445\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240II.md" "b/notes/LeetCode\347\254\254445\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240II.md" index 002ab1ed..337dfa5a 100644 --- "a/notes/LeetCode\347\254\254445\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240II.md" +++ "b/notes/LeetCode\347\254\254445\345\217\267\351\227\256\351\242\230\357\274\232\344\270\244\346\225\260\347\233\270\345\212\240II.md" @@ -66,4 +66,4 @@ class Solution: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fdvu1.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254447\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217.md" "b/notes/LeetCode\347\254\254447\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217.md" index d8faff9f..1439251e 100644 --- "a/notes/LeetCode\347\254\254447\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217.md" +++ "b/notes/LeetCode\347\254\254447\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217.md" @@ -50,7 +50,7 @@ n 最大为 500,可以使用时间复杂度为 O(n^2)的算法。 ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181030112917.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0ys9o.gif) ### 代码实现 @@ -96,4 +96,4 @@ private: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/s95kv.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254454\345\217\267\351\227\256\351\242\230\357\274\232\345\233\233\346\225\260\347\233\270\345\212\240II.md" "b/notes/LeetCode\347\254\254454\345\217\267\351\227\256\351\242\230\357\274\232\345\233\233\346\225\260\347\233\270\345\212\240II.md" index 6fffd209..d43e7fea 100644 --- "a/notes/LeetCode\347\254\254454\345\217\267\351\227\256\351\242\230\357\274\232\345\233\233\346\225\260\347\233\270\345\212\240II.md" +++ "b/notes/LeetCode\347\254\254454\345\217\267\351\227\256\351\242\230\357\274\232\345\233\233\346\225\260\347\233\270\345\212\240II.md" @@ -41,7 +41,7 @@ D = [ 0, 2] ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181029154232.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/dgth9.gif) ### 代码实现 @@ -82,4 +82,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/sx6gy.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254642\345\217\267\351\227\256\351\242\230\357\274\232\350\256\276\350\256\241\344\270\200\344\270\252\346\220\234\347\264\242\350\207\252\345\212\250\345\256\214\346\210\220\347\263\273\347\273\237.md" "b/notes/LeetCode\347\254\254642\345\217\267\351\227\256\351\242\230\357\274\232\350\256\276\350\256\241\344\270\200\344\270\252\346\220\234\347\264\242\350\207\252\345\212\250\345\256\214\346\210\220\347\263\273\347\273\237.md" index d5189f7c..4ea7caf5 100644 --- "a/notes/LeetCode\347\254\254642\345\217\267\351\227\256\351\242\230\357\274\232\350\256\276\350\256\241\344\270\200\344\270\252\346\220\234\347\264\242\350\207\252\345\212\250\345\256\214\346\210\220\347\263\273\347\273\237.md" +++ "b/notes/LeetCode\347\254\254642\345\217\267\351\227\256\351\242\230\357\274\232\350\256\276\350\256\241\344\270\200\344\270\252\346\220\234\347\264\242\350\207\252\345\212\250\345\256\214\346\210\220\347\263\273\347\273\237.md" @@ -204,4 +204,4 @@ private: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/gxmux.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25466\345\217\267\351\227\256\351\242\230\357\274\232\345\212\240\344\270\200.md" "b/notes/LeetCode\347\254\25466\345\217\267\351\227\256\351\242\230\357\274\232\345\212\240\344\270\200.md" index f73ee1b2..c20ae20c 100644 --- "a/notes/LeetCode\347\254\25466\345\217\267\351\227\256\351\242\230\357\274\232\345\212\240\344\270\200.md" +++ "b/notes/LeetCode\347\254\25466\345\217\267\351\227\256\351\242\230\357\274\232\345\212\240\344\270\200.md" @@ -56,9 +56,9 @@ ### 动画描述 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190606112155.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/iejo0.gif) -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190606112128.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/15na7.gif) @@ -86,5 +86,5 @@ public class Solution { } ``` -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/r5cpx.png) diff --git "a/notes/LeetCode\347\254\254690\345\217\267\351\227\256\351\242\230\357\274\232\345\221\230\345\267\245\347\232\204\351\207\215\350\246\201\346\200\247.md" "b/notes/LeetCode\347\254\254690\345\217\267\351\227\256\351\242\230\357\274\232\345\221\230\345\267\245\347\232\204\351\207\215\350\246\201\346\200\247.md" index 55723cf2..6ebbaa32 100644 --- "a/notes/LeetCode\347\254\254690\345\217\267\351\227\256\351\242\230\357\274\232\345\221\230\345\267\245\347\232\204\351\207\215\350\246\201\346\200\247.md" +++ "b/notes/LeetCode\347\254\254690\345\217\267\351\227\256\351\242\230\357\274\232\345\221\230\345\267\245\347\232\204\351\207\215\350\246\201\346\200\247.md" @@ -67,4 +67,4 @@ public int getImportance(List employees, int id) { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/wvk3e.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25475\345\217\267\351\227\256\351\242\230\357\274\232\351\242\234\350\211\262\345\210\206\347\261\273.md" "b/notes/LeetCode\347\254\25475\345\217\267\351\227\256\351\242\230\357\274\232\351\242\234\350\211\262\345\210\206\347\261\273.md" index 2562520b..acf4571d 100644 --- "a/notes/LeetCode\347\254\25475\345\217\267\351\227\256\351\242\230\357\274\232\351\242\234\350\211\262\345\210\206\347\261\273.md" +++ "b/notes/LeetCode\347\254\25475\345\217\267\351\227\256\351\242\230\357\274\232\351\242\234\350\211\262\345\210\206\347\261\273.md" @@ -40,7 +40,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502094455.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/6g5tm.gif) ### 代码实现 @@ -74,4 +74,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/r5e2r.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25486\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\351\223\276\350\241\250.md" "b/notes/LeetCode\347\254\25486\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\351\223\276\350\241\250.md" index 3ad772a2..ecfdb2c6 100644 --- "a/notes/LeetCode\347\254\25486\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\351\223\276\350\241\250.md" +++ "b/notes/LeetCode\347\254\25486\345\217\267\351\227\256\351\242\230\357\274\232\345\210\206\345\211\262\351\223\276\350\241\250.md" @@ -29,7 +29,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181104095701.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/t96zg.gif) ### 代码实现 @@ -72,4 +72,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/5a3tl.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\254877\345\217\267\351\227\256\351\242\230\357\274\232\347\237\263\345\255\220\346\270\270\346\210\217.md" "b/notes/LeetCode\347\254\254877\345\217\267\351\227\256\351\242\230\357\274\232\347\237\263\345\255\220\346\270\270\346\210\217.md" index c1867537..0ef15302 100644 --- "a/notes/LeetCode\347\254\254877\345\217\267\351\227\256\351\242\230\357\274\232\347\237\263\345\255\220\346\270\270\346\210\217.md" +++ "b/notes/LeetCode\347\254\254877\345\217\267\351\227\256\351\242\230\357\274\232\347\237\263\345\255\220\346\270\270\346\210\217.md" @@ -84,7 +84,7 @@ 代码如下: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502124645.jpg) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/af7fm.jpg) @@ -131,4 +131,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/lnwx8.png) diff --git "a/notes/LeetCode\347\254\25492\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250II.md" "b/notes/LeetCode\347\254\25492\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250II.md" index 498f6630..81c10173 100644 --- "a/notes/LeetCode\347\254\25492\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250II.md" +++ "b/notes/LeetCode\347\254\25492\345\217\267\351\227\256\351\242\230\357\274\232\345\217\215\350\275\254\351\223\276\350\241\250II.md" @@ -40,7 +40,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20181103160226.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/rjjr0.gif) ### 代码实现 @@ -73,4 +73,4 @@ public: -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/l2f8n.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\25494\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.md" "b/notes/LeetCode\347\254\25494\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.md" index 4ce0a1c3..b1c9560e 100644 --- "a/notes/LeetCode\347\254\25494\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.md" +++ "b/notes/LeetCode\347\254\25494\345\217\267\351\227\256\351\242\230\357\274\232\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.md" @@ -39,7 +39,7 @@ ### 动画描述 -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502102629.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/v17b8.gif) ### 代码实现 @@ -66,4 +66,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/4beoi.png) \ No newline at end of file diff --git "a/notes/LeetCode\347\254\2549\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\226\207\346\225\260.md" "b/notes/LeetCode\347\254\2549\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\226\207\346\225\260.md" index ca79fd07..37a2dca2 100644 --- "a/notes/LeetCode\347\254\2549\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\226\207\346\225\260.md" +++ "b/notes/LeetCode\347\254\2549\345\217\267\351\227\256\351\242\230\357\274\232\345\233\236\346\226\207\346\225\260.md" @@ -49,7 +49,7 @@ #### 动画描述 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190525181152.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/ods8b.gif) #### 代码实现 @@ -78,7 +78,7 @@ class Solution { #### 动画描述 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190525181202.gif) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/v3tkl.gif) #### 代码实现 @@ -122,7 +122,7 @@ class Solution { #### 动画描述 -![](https://raw.githubusercontent.com/MisterBooo/myBlogPic/master/20190525181211.png) +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/0siv7.png) #### 代码实现 @@ -143,4 +143,4 @@ class Solution { -![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png) \ No newline at end of file +![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/kcet5.png) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..69b21656 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +fire \ No newline at end of file diff --git a/template/template.md b/template/template.md new file mode 100644 index 00000000..63c5f993 --- /dev/null +++ b/template/template.md @@ -0,0 +1,26 @@ +# LeetCode 图解 | + +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode]() 系列文章之一。 +> +> 同步博客:https://www.algomooc.com + +本题解作者: + +## 题目描述 + + + +## 题目解析 + + + +## 动画理解 + +![](../Animation/Animation.gif) + +## 参考代码 + + + +## 复杂度分析 +