From 2dee14d97801f4a4a2017e9b638270ef206f06b1 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Tue, 14 May 2019 18:03:11 +0800 Subject: [PATCH] auto commit --- docs/notes/Leetcode 题解 - 双指针.md | 40 ++++++++++++++------------ docs/notes/Leetcode 题解 - 排序.md | 43 +++++++++++++++------------- notes/Leetcode 题解 - 双指针.md | 40 ++++++++++++++------------ notes/Leetcode 题解 - 排序.md | 43 +++++++++++++++------------- 4 files changed, 88 insertions(+), 78 deletions(-) diff --git a/docs/notes/Leetcode 题解 - 双指针.md b/docs/notes/Leetcode 题解 - 双指针.md index d8942ab2..527445ce 100644 --- a/docs/notes/Leetcode 题解 - 双指针.md +++ b/docs/notes/Leetcode 题解 - 双指针.md @@ -1,17 +1,17 @@ -* [有序数组的 Two Sum](#有序数组的-two-sum) -* [两数平方和](#两数平方和) -* [反转字符串中的元音字符](#反转字符串中的元音字符) -* [回文字符串](#回文字符串) -* [归并两个有序数组](#归并两个有序数组) -* [判断链表是否存在环](#判断链表是否存在环) -* [最长子序列](#最长子序列) +* [1. 有序数组的 Two Sum](#1-有序数组的-two-sum) +* [2. 两数平方和](#2-两数平方和) +* [3. 反转字符串中的元音字符](#3-反转字符串中的元音字符) +* [4. 回文字符串](#4-回文字符串) +* [5. 归并两个有序数组](#5-归并两个有序数组) +* [6. 判断链表是否存在环](#6-判断链表是否存在环) +* [7. 最长子序列](#7-最长子序列) 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 -# 有序数组的 Two Sum +# 1. 有序数组的 Two Sum [Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) @@ -45,7 +45,7 @@ public int[] twoSum(int[] numbers, int target) { } ``` -# 两数平方和 +# 2. 两数平方和 [633. Sum of Square Numbers (Easy)](https://leetcode.com/problems/sum-of-square-numbers/description/) @@ -74,7 +74,7 @@ public boolean judgeSquareSum(int c) { } ``` -# 反转字符串中的元音字符 +# 3. 反转字符串中的元音字符 [345. Reverse Vowels of a String (Easy)](https://leetcode.com/problems/reverse-vowels-of-a-string/description/) @@ -85,7 +85,8 @@ Given s = "leetcode", return "leotcede". 使用双指针指向待反转的两个元音字符,一个指针从头向尾遍历,一个指针从尾到头遍历。 ```java -private final static HashSet vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); +private final static HashSet vowels = new HashSet<>( + Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); public String reverseVowels(String s) { int i = 0, j = s.length() - 1; @@ -106,7 +107,7 @@ public String reverseVowels(String s) { } ``` -# 回文字符串 +# 4. 回文字符串 [680. Valid Palindrome II (Easy)](https://leetcode.com/problems/valid-palindrome-ii/description/) @@ -120,8 +121,7 @@ Explanation: You could delete the character 'c'. ```java public boolean validPalindrome(String s) { - int i = -1, j = s.length(); - while (++i < --j) { + for (int i = 0, j = s.length() - 1; i < j; i++, j--) { if (s.charAt(i) != s.charAt(j)) { return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j); } @@ -139,7 +139,7 @@ private boolean isPalindrome(String s, int i, int j) { } ``` -# 归并两个有序数组 +# 5. 归并两个有序数组 [88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/) @@ -173,7 +173,7 @@ public void merge(int[] nums1, int m, int[] nums2, int n) { } ``` -# 判断链表是否存在环 +# 6. 判断链表是否存在环 [141. Linked List Cycle (Easy)](https://leetcode.com/problems/linked-list-cycle/description/) @@ -196,7 +196,7 @@ public boolean hasCycle(ListNode head) { } ``` -# 最长子序列 +# 7. 最长子序列 [524. Longest Word in Dictionary through Deleting (Medium)](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/) @@ -210,6 +210,8 @@ Output: 题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。 +通过删除字符串 s 中的一个字符能得到字符串 t,可以认为 t 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。 + ```java public String findLongestWord(String s, List d) { String longestWord = ""; @@ -218,14 +220,14 @@ public String findLongestWord(String s, List d) { if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) { continue; } - if (isValid(s, target)) { + if (isSubstr(s, target)) { longestWord = target; } } return longestWord; } -private boolean isValid(String s, String target) { +private boolean isSubstr(String s, String target) { int i = 0, j = 0; while (i < s.length() && j < target.length()) { if (s.charAt(i) == target.charAt(j)) { diff --git a/docs/notes/Leetcode 题解 - 排序.md b/docs/notes/Leetcode 题解 - 排序.md index 2b0adb78..49e7300e 100644 --- a/docs/notes/Leetcode 题解 - 排序.md +++ b/docs/notes/Leetcode 题解 - 排序.md @@ -1,36 +1,41 @@ * [快速选择](#快速选择) -* [堆排序](#堆排序) - * [Kth Element](#kth-element) +* [堆](#堆) + * [1. Kth Element](#1-kth-element) * [桶排序](#桶排序) - * [出现频率最多的 k 个数](#出现频率最多的-k-个数) - * [按照字符出现次数对字符串排序](#按照字符出现次数对字符串排序) + * [1. 出现频率最多的 k 个元素](#1-出现频率最多的-k-个元素) + * [2. 按照字符出现次数对字符串排序](#2-按照字符出现次数对字符串排序) * [荷兰国旗问题](#荷兰国旗问题) - * [按颜色进行排序](#按颜色进行排序) + * [1. 按颜色进行排序](#1-按颜色进行排序) # 快速选择 -用于求解 **Kth Element** 问题,使用快速排序的 partition() 进行实现。 +用于求解 **Kth Element** 问题,也就是第 K 个元素的问题。 -需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 +可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 -# 堆排序 +# 堆 -用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。 +用于求解 **TopK Elements** 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。 -堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。 +堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。 快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。 可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 -## Kth Element +## 1. Kth Element [215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) -题目描述:找到第 k 大的元素。 +```text +Input: [3,2,1,5,6,4] and k = 2 +Output: 5 +``` + +题目描述:找到倒数第 k 个的元素。 **排序** :时间复杂度 O(NlogN),空间复杂度 O(1) @@ -41,7 +46,7 @@ public int findKthLargest(int[] nums, int k) { } ``` -**堆排序** :时间复杂度 O(NlogK),空间复杂度 O(K)。 +**堆** :时间复杂度 O(NlogK),空间复杂度 O(K)。 ```java public int findKthLargest(int[] nums, int k) { @@ -97,7 +102,7 @@ private void swap(int[] a, int i, int j) { # 桶排序 -## 出现频率最多的 k 个数 +## 1. 出现频率最多的 k 个元素 [347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/) @@ -105,7 +110,7 @@ private void swap(int[] a, int i, int j) { Given [1,1,1,2,2,3] and k = 2, return [1,2]. ``` -设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 +设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。 @@ -138,7 +143,7 @@ public List topKFrequent(int[] nums, int k) { } ``` -## 按照字符出现次数对字符串排序 +## 2. 按照字符出现次数对字符串排序 [451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/) @@ -187,14 +192,12 @@ public String frequencySort(String s) { 荷兰国旗包含三种颜色:红、白、蓝。 -有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。 - -它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。 +有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。

-## 按颜色进行排序 +## 1. 按颜色进行排序 [75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/) diff --git a/notes/Leetcode 题解 - 双指针.md b/notes/Leetcode 题解 - 双指针.md index d8942ab2..527445ce 100644 --- a/notes/Leetcode 题解 - 双指针.md +++ b/notes/Leetcode 题解 - 双指针.md @@ -1,17 +1,17 @@ -* [有序数组的 Two Sum](#有序数组的-two-sum) -* [两数平方和](#两数平方和) -* [反转字符串中的元音字符](#反转字符串中的元音字符) -* [回文字符串](#回文字符串) -* [归并两个有序数组](#归并两个有序数组) -* [判断链表是否存在环](#判断链表是否存在环) -* [最长子序列](#最长子序列) +* [1. 有序数组的 Two Sum](#1-有序数组的-two-sum) +* [2. 两数平方和](#2-两数平方和) +* [3. 反转字符串中的元音字符](#3-反转字符串中的元音字符) +* [4. 回文字符串](#4-回文字符串) +* [5. 归并两个有序数组](#5-归并两个有序数组) +* [6. 判断链表是否存在环](#6-判断链表是否存在环) +* [7. 最长子序列](#7-最长子序列) 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 -# 有序数组的 Two Sum +# 1. 有序数组的 Two Sum [Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) @@ -45,7 +45,7 @@ public int[] twoSum(int[] numbers, int target) { } ``` -# 两数平方和 +# 2. 两数平方和 [633. Sum of Square Numbers (Easy)](https://leetcode.com/problems/sum-of-square-numbers/description/) @@ -74,7 +74,7 @@ public boolean judgeSquareSum(int c) { } ``` -# 反转字符串中的元音字符 +# 3. 反转字符串中的元音字符 [345. Reverse Vowels of a String (Easy)](https://leetcode.com/problems/reverse-vowels-of-a-string/description/) @@ -85,7 +85,8 @@ Given s = "leetcode", return "leotcede". 使用双指针指向待反转的两个元音字符,一个指针从头向尾遍历,一个指针从尾到头遍历。 ```java -private final static HashSet vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); +private final static HashSet vowels = new HashSet<>( + Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); public String reverseVowels(String s) { int i = 0, j = s.length() - 1; @@ -106,7 +107,7 @@ public String reverseVowels(String s) { } ``` -# 回文字符串 +# 4. 回文字符串 [680. Valid Palindrome II (Easy)](https://leetcode.com/problems/valid-palindrome-ii/description/) @@ -120,8 +121,7 @@ Explanation: You could delete the character 'c'. ```java public boolean validPalindrome(String s) { - int i = -1, j = s.length(); - while (++i < --j) { + for (int i = 0, j = s.length() - 1; i < j; i++, j--) { if (s.charAt(i) != s.charAt(j)) { return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j); } @@ -139,7 +139,7 @@ private boolean isPalindrome(String s, int i, int j) { } ``` -# 归并两个有序数组 +# 5. 归并两个有序数组 [88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/) @@ -173,7 +173,7 @@ public void merge(int[] nums1, int m, int[] nums2, int n) { } ``` -# 判断链表是否存在环 +# 6. 判断链表是否存在环 [141. Linked List Cycle (Easy)](https://leetcode.com/problems/linked-list-cycle/description/) @@ -196,7 +196,7 @@ public boolean hasCycle(ListNode head) { } ``` -# 最长子序列 +# 7. 最长子序列 [524. Longest Word in Dictionary through Deleting (Medium)](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/) @@ -210,6 +210,8 @@ Output: 题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。 +通过删除字符串 s 中的一个字符能得到字符串 t,可以认为 t 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。 + ```java public String findLongestWord(String s, List d) { String longestWord = ""; @@ -218,14 +220,14 @@ public String findLongestWord(String s, List d) { if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) { continue; } - if (isValid(s, target)) { + if (isSubstr(s, target)) { longestWord = target; } } return longestWord; } -private boolean isValid(String s, String target) { +private boolean isSubstr(String s, String target) { int i = 0, j = 0; while (i < s.length() && j < target.length()) { if (s.charAt(i) == target.charAt(j)) { diff --git a/notes/Leetcode 题解 - 排序.md b/notes/Leetcode 题解 - 排序.md index d0178684..40849882 100644 --- a/notes/Leetcode 题解 - 排序.md +++ b/notes/Leetcode 题解 - 排序.md @@ -1,36 +1,41 @@ * [快速选择](#快速选择) -* [堆排序](#堆排序) - * [Kth Element](#kth-element) +* [堆](#堆) + * [1. Kth Element](#1-kth-element) * [桶排序](#桶排序) - * [出现频率最多的 k 个数](#出现频率最多的-k-个数) - * [按照字符出现次数对字符串排序](#按照字符出现次数对字符串排序) + * [1. 出现频率最多的 k 个元素](#1-出现频率最多的-k-个元素) + * [2. 按照字符出现次数对字符串排序](#2-按照字符出现次数对字符串排序) * [荷兰国旗问题](#荷兰国旗问题) - * [按颜色进行排序](#按颜色进行排序) + * [1. 按颜色进行排序](#1-按颜色进行排序) # 快速选择 -用于求解 **Kth Element** 问题,使用快速排序的 partition() 进行实现。 +用于求解 **Kth Element** 问题,也就是第 K 个元素的问题。 -需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 +可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 -# 堆排序 +# 堆 -用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。 +用于求解 **TopK Elements** 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。 -堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。 +堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。 快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。 可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 -## Kth Element +## 1. Kth Element [215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) -题目描述:找到第 k 大的元素。 +```text +Input: [3,2,1,5,6,4] and k = 2 +Output: 5 +``` + +题目描述:找到倒数第 k 个的元素。 **排序** :时间复杂度 O(NlogN),空间复杂度 O(1) @@ -41,7 +46,7 @@ public int findKthLargest(int[] nums, int k) { } ``` -**堆排序** :时间复杂度 O(NlogK),空间复杂度 O(K)。 +**堆** :时间复杂度 O(NlogK),空间复杂度 O(K)。 ```java public int findKthLargest(int[] nums, int k) { @@ -97,7 +102,7 @@ private void swap(int[] a, int i, int j) { # 桶排序 -## 出现频率最多的 k 个数 +## 1. 出现频率最多的 k 个元素 [347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/) @@ -105,7 +110,7 @@ private void swap(int[] a, int i, int j) { Given [1,1,1,2,2,3] and k = 2, return [1,2]. ``` -设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 +设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。 @@ -138,7 +143,7 @@ public List topKFrequent(int[] nums, int k) { } ``` -## 按照字符出现次数对字符串排序 +## 2. 按照字符出现次数对字符串排序 [451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/) @@ -187,14 +192,12 @@ public String frequencySort(String s) { 荷兰国旗包含三种颜色:红、白、蓝。 -有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。 - -它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。 +有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。

-## 按颜色进行排序 +## 1. 按颜色进行排序 [75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/)