CS-Notes/notes/Leetcode 题解 - 双指针.md

290 lines
9.9 KiB
Java
Raw Normal View History

2019-04-25 18:24:51 +08:00
<!-- GFM-TOC -->
2019-05-14 18:03:11 +08:00
* [1. 有序数组的 Two Sum](#1-有序数组的-two-sum)
* [2. 两数平方和](#2-两数平方和)
* [3. 反转字符串中的元音字符](#3-反转字符串中的元音字符)
* [4. 回文字符串](#4-回文字符串)
* [5. 归并两个有序数组](#5-归并两个有序数组)
* [6. 判断链表是否存在环](#6-判断链表是否存在环)
* [7. 最长子序列](#7-最长子序列)
2019-04-25 18:24:51 +08:00
<!-- GFM-TOC -->
双指针主要用于遍历数组两个指针指向不同的元素从而协同完成任务
2019-05-14 18:03:11 +08:00
# 1. 有序数组的 Two Sum
2019-04-25 18:24:51 +08:00
2019-10-27 00:40:05 +08:00
167\. Two Sum II - Input array is sorted (Easy)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) / [力扣](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/description/)
2019-04-25 18:24:51 +08:00
```html
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2
```
题目描述在有序数组中找出两个数使它们的和为 target
使用双指针一个指针指向值较小的元素一个指针指向值较大的元素指向较小元素的指针从头向尾遍历指向较大元素的指针从尾向头遍历
- 如果两个指针指向元素的和 sum == target那么得到要求的结果
- 如果 sum > target移动较大的元素使 sum 变小一些
- 如果 sum < target移动较小的元素使 sum 变大一些
2019-10-27 16:24:45 +08:00
数组中的元素最多遍历一次时间复杂度为 O(N)只使用了两个额外变量空间复杂度为 O(1)
2019-10-27 15:51:57 +08:00
<div align="center"> <img src="pics/437cb54c-5970-4ba9-b2ef-2541f7d6c81e.gif" width="200px"> </div><br>
2019-04-25 18:24:51 +08:00
```java
public int[] twoSum(int[] numbers, int target) {
2019-10-27 15:51:57 +08:00
if (numbers == null) return null;
2019-04-25 18:24:51 +08:00
int i = 0, j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if (sum == target) {
return new int[]{i + 1, j + 1};
} else if (sum < target) {
i++;
} else {
j--;
}
}
return null;
}
```
2019-05-14 18:03:11 +08:00
# 2. 两数平方和
2019-04-25 18:24:51 +08:00
2019-10-27 15:51:57 +08:00
633\. Sum of Square Numbers (Easy)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/sum-of-square-numbers/description/) / [力扣](https://leetcode-cn.com/problems/sum-of-square-numbers/description/)
2019-04-25 18:24:51 +08:00
```html
Input: 5
Output: True
Explanation: 1 * 1 + 2 * 2 = 5
```
2019-10-27 16:24:45 +08:00
题目描述判断一个非负整数是否为两个整数的平方和
2019-10-27 16:42:12 +08:00
可以看成是在元素为 0\~target 的有序数组中查找两个数使得这两个数的平方和为 target如果能找到则返回 true表示 target 是两个整数的平方和
2019-10-27 16:24:45 +08:00
2019-10-27 16:42:12 +08:00
本题和 167\. Two Sum II - Input array is sorted 类似只有一个明显区别一个是和为 target一个是平方和为 target本题同样可以使用双指针得到两个数使其平方和为 target
2019-10-27 16:24:45 +08:00
2019-10-27 16:42:12 +08:00
本题的关键是右指针的初始化实现剪枝从而降低时间复杂度设右指针为 x左指针固定为 0为了使 0<sup>2</sup> + x<sup>2</sup> 的值尽可能接近 target我们可以将 x 取为 sqrt(target)
2019-10-27 16:24:45 +08:00
2019-10-27 16:42:12 +08:00
因为最多只需要遍历一次 0\~sqrt(target)所以时间复杂度为 O(log<sub>2</sub>N)又因为只使用了两个额外的变量因此空间复杂度为 O(1)
2019-04-25 18:24:51 +08:00
```java
2019-10-27 16:24:45 +08:00
public boolean judgeSquareSum(int target) {
2019-10-27 16:42:12 +08:00
if (target < 0) return false;
2019-10-27 16:24:45 +08:00
int i = 0, j = (int) Math.sqrt(target);
while (i <= j) {
int powSum = i * i + j * j;
if (powSum == target) {
return true;
} else if (powSum > target) {
j--;
} else {
i++;
}
}
return false;
}
2019-04-25 18:24:51 +08:00
```
2019-05-14 18:03:11 +08:00
# 3. 反转字符串中的元音字符
2019-04-25 18:24:51 +08:00
2019-10-27 15:51:57 +08:00
345\. Reverse Vowels of a String (Easy)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/reverse-vowels-of-a-string/description/) / [力扣](https://leetcode-cn.com/problems/reverse-vowels-of-a-string/description/)
2019-04-25 18:24:51 +08:00
```html
Given s = "leetcode", return "leotcede".
```
2019-10-27 17:57:35 +08:00
<div align="center"> <img src="pics/a7cb8423-895d-4975-8ef8-662a0029c772.png" width="400px"> </div><br>
使用双指针一个指针从头向尾遍历一个指针从尾到头遍历当两个指针都遍历到元音字符时交换这两个元音字符
为了快速判断一个字符是不是元音字符我们将全部元音字符添加到集合 HashSet 从而以 O(1) 的时间复杂度进行该操作
- 时间复杂度为 O(N)只需要遍历所有元素一次
- 空间复杂度 O(1)只需要使用两个额外变量
2019-10-27 18:01:01 +08:00
<div align="center"> <img src="pics/ef25ff7c-0f63-420d-8b30-eafbeea35d11.gif" width="400px"> </div><br>
2019-04-25 18:24:51 +08:00
```java
2019-05-14 18:03:11 +08:00
private final static HashSet<Character> vowels = new HashSet<>(
Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
2019-04-25 18:24:51 +08:00
public String reverseVowels(String s) {
2019-10-27 17:57:35 +08:00
if (s == null) return null;
2019-04-25 18:24:51 +08:00
int i = 0, j = s.length() - 1;
char[] result = new char[s.length()];
while (i <= j) {
char ci = s.charAt(i);
char cj = s.charAt(j);
if (!vowels.contains(ci)) {
result[i++] = ci;
} else if (!vowels.contains(cj)) {
result[j--] = cj;
} else {
result[i++] = cj;
result[j--] = ci;
}
}
return new String(result);
}
```
2019-05-14 18:03:11 +08:00
# 4. 回文字符串
2019-04-25 18:24:51 +08:00
2019-10-27 15:51:57 +08:00
680\. Valid Palindrome II (Easy)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/valid-palindrome-ii/description/) / [力扣](https://leetcode-cn.com/problems/valid-palindrome-ii/description/)
2019-04-25 18:24:51 +08:00
```html
Input: "abca"
Output: True
Explanation: You could delete the character 'c'.
```
题目描述可以删除一个字符判断是否能构成回文字符串
```java
public boolean validPalindrome(String s) {
2019-05-14 18:03:11 +08:00
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
2019-04-25 18:24:51 +08:00
if (s.charAt(i) != s.charAt(j)) {
return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
}
}
return true;
}
private boolean isPalindrome(String s, int i, int j) {
while (i < j) {
if (s.charAt(i++) != s.charAt(j--)) {
return false;
}
}
return true;
}
```
2019-05-14 18:03:11 +08:00
# 5. 归并两个有序数组
2019-04-25 18:24:51 +08:00
2019-10-27 15:51:57 +08:00
88\. Merge Sorted Array (Easy)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/merge-sorted-array/description/) / [力扣](https://leetcode-cn.com/problems/merge-sorted-array/description/)
2019-04-25 18:24:51 +08:00
```html
Input:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
Output: [1,2,2,3,5,6]
```
题目描述把归并结果存到第一个数组上
需要从尾开始遍历否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值
```java
public void merge(int[] nums1, int m, int[] nums2, int n) {
int index1 = m - 1, index2 = n - 1;
int indexMerge = m + n - 1;
while (index1 >= 0 || index2 >= 0) {
if (index1 < 0) {
nums1[indexMerge--] = nums2[index2--];
} else if (index2 < 0) {
nums1[indexMerge--] = nums1[index1--];
} else if (nums1[index1] > nums2[index2]) {
nums1[indexMerge--] = nums1[index1--];
} else {
nums1[indexMerge--] = nums2[index2--];
}
}
}
```
2019-05-14 18:03:11 +08:00
# 6. 判断链表是否存在环
2019-04-25 18:24:51 +08:00
2019-10-27 15:51:57 +08:00
141\. Linked List Cycle (Easy)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/linked-list-cycle/description/) / [力扣](https://leetcode-cn.com/problems/linked-list-cycle/description/)
2019-04-25 18:24:51 +08:00
使用双指针一个指针每次移动一个节点一个指针每次移动两个节点如果存在环那么这两个指针一定会相遇
```java
public boolean hasCycle(ListNode head) {
if (head == null) {
return false;
}
ListNode l1 = head, l2 = head.next;
while (l1 != null && l2 != null && l2.next != null) {
if (l1 == l2) {
return true;
}
l1 = l1.next;
l2 = l2.next.next;
}
return false;
}
```
2019-05-14 18:03:11 +08:00
# 7. 最长子序列
2019-04-25 18:24:51 +08:00
2019-10-27 15:51:57 +08:00
524\. Longest Word in Dictionary through Deleting (Medium)
2019-10-27 00:30:59 +08:00
2019-10-27 00:42:02 +08:00
[Leetcode](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/) / [力扣](https://leetcode-cn.com/problems/longest-word-in-dictionary-through-deleting/description/)
2019-04-25 18:24:51 +08:00
```
Input:
s = "abpcplea", d = ["ale","apple","monkey","plea"]
Output:
"apple"
```
题目描述删除 s 中的一些字符使得它构成字符串列表 d 中的一个字符串找出能构成的最长字符串如果有多个相同长度的结果返回字典序的最小字符串
2019-05-14 18:03:11 +08:00
通过删除字符串 s 中的一个字符能得到字符串 t可以认为 t s 的子序列我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列
2019-04-25 18:24:51 +08:00
```java
public String findLongestWord(String s, List<String> d) {
String longestWord = "";
for (String target : d) {
int l1 = longestWord.length(), l2 = target.length();
if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) {
continue;
}
2019-05-14 18:03:11 +08:00
if (isSubstr(s, target)) {
2019-04-25 18:24:51 +08:00
longestWord = target;
}
}
return longestWord;
}
2019-05-14 18:03:11 +08:00
private boolean isSubstr(String s, String target) {
2019-04-25 18:24:51 +08:00
int i = 0, j = 0;
while (i < s.length() && j < target.length()) {
if (s.charAt(i) == target.charAt(j)) {
j++;
}
i++;
}
return j == target.length();
}
```
2019-06-13 13:31:54 +08:00
# 微信公众号
2019-06-10 11:23:18 +08:00
2019-06-18 00:57:23 +08:00
更多精彩内容将发布在微信公众号 CyC2018 你也可以在公众号后台和我交流学习和求职相关的问题另外公众号提供了该项目的 PDF 等离线阅读版本后台回复 "下载" 即可领取公众号也提供了一份技术面试复习大纲不仅系统整理了面试知识点而且标注了各个知识点的重要程度从而帮你理清多而杂的面试知识点后台回复 "大纲" 即可领取我基本是按照这个大纲来进行复习的对我拿到了 BAT 头条等 Offer 起到很大的帮助你们完全可以和我一样根据大纲上列的知识点来进行复习就不用看很多不重要的内容也可以知道哪些内容很重要从而多安排一些复习时间
2019-06-10 11:23:18 +08:00
2019-10-24 02:09:52 +08:00
<div align="center"><img width="350px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码.png"></img></div>