双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务
1 两数之和 II - 输入有序数组
167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)
双指针:一个指向最小的元素,一个指向最大的元素
sum 等于两个指针对应的值
当 sum < target 时,需要调整 sum 变大,于是左指针右移
当 sum > target 时,需要调整 sum 变小,于是右指针左移
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0;
int right = numbers.length - 1;
while (numbers[left] + numbers[right] != target) {
int sum = numbers[left] + numbers[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
}
}
return new int[] { left + 1, right + 1 };
}
}
2 平方数之和
和 1 类似,但是注意要声明为long,防止 int 溢出
class Solution {
public boolean judgeSquareSum(int c) {
long left = 0;
long right = (int) Math.sqrt(c);
long sum = -1;
while (left <= right) {
sum = left * left + right * right;
if (sum == c) {
return true;
} else if (sum > c) {
right--;
} else {
left++;
}
}
return sum == c;
}
}
3 反转字符串中的元音字母
345. 反转字符串中的元音字母 - 力扣(LeetCode)
左指针和右指针分别是头尾,如果两个字母都是元音(vowel),就交换他们的位置,这里用 StringBuilder 的 setCharAt
注意的是,都是元音的话,左右指针都要进一步(左指针右移,右指针左移)
class Solution {
public String reverseVowels(String s) {
int left = 0;
int right = s.length() - 1;
StringBuilder sb = new StringBuilder(s);
List<Character> vowel = new ArrayList<>();
vowel.add('A');
vowel.add('a');
vowel.add('E');
vowel.add('e');
vowel.add('I');
vowel.add('i');
vowel.add('O');
vowel.add('o');
vowel.add('U');
vowel.add('u');
while (left <= right) {
if (vowel.contains(sb.charAt(left)) && vowel.contains(sb.charAt(right))) {
char temp = sb.charAt(left);
sb.setCharAt(left, sb.charAt(right));
sb.setCharAt(right, temp);
left++;
right--;
} else if (vowel.contains(sb.charAt(left)) && !vowel.contains(sb.charAt(right))) {
right--;
} else if (!vowel.contains(sb.charAt(left)) && vowel.contains(sb.charAt(right))) {
left++;
} else {
left++;
right--;
}
}
return sb.toString();
}
}
4 验证回文串 II
使用双指针可以很容易判断一个字符串是否是回文(palindrome)字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串
class Solution {
public boolean validPalindrome(String s) {
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
return isPalindrome(s, i + 1, j) || isPalindrome(s, i, j - 1);
}
}
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;
}
}
5 合并两个有序数组
双指针,从头开始,一个数组一个指针,小的就存入res数组,但是题要求存入nums1,需要对其覆盖
同时,对于两个有序数组,有序的性质没有用上
class Solution {
public static void merge(int[] nums1, int m, int[] nums2, int n) {
int left = 0;
int right = 0;
int[] res = new int[m + n];
int index = 0;
while (left < m && right < n) {
if (nums1[left] < nums2[right]) {
res[index++] = nums1[left++];
} else if (nums1[left] > nums2[right]) {
res[index++] = nums2[right++];
} else {
res[index++] = nums1[left++];
res[index++] = nums2[right++];
}
}
if (left == m && right != n) {
while (right < n) {
res[index++] = nums2[right++];
}
} else {
while (left < m) {
res[index++] = nums1[left++];
}
}
for (int i = 0; i < nums1.length; i++) {
nums1[i] = res[i];
}
}
}
改进:
6 环形链表
快慢指针
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
var slow = head;
var fast = head.next;
while (slow != null && fast != null && fast.next != null) {
if (slow == fast) {
return true;
}
slow = slow.next;
fast = fast.next.next;
}
return false;
}
}
7 通过删除字母匹配到字典里最长单词
524. 通过删除字母匹配到字典里最长单词 - 力扣(LeetCode)
对 dictionary 先排序,这里排序使用 List 的 sort 方法,传入一个比较器,这里使用的是 方法引用 ,先对长度排序,然后按字典序排序
对于判断子串,使用 双指针 ,如果相等,第二个指针后移
class Solution {
public String findLongestWord(String s, List<String> dictionary) {
dictionary.sort(((o1, o2) -> {
if (o1.length() != o2.length()) {
return o2.length() - o1.length();
} else {
return o1.compareTo(o2);
}
}));
for (String stringDic : dictionary) {
if (isSubStr(s, stringDic)) {
return stringDic;
}
}
return "";
}
private boolean isSubStr(String s1, String s2) {
// 判断s2是否为s1的子串
int i = 0, j = 0;
while (i < s1.length() && j < s2.length()) {
if (s1.charAt(i) == s2.charAt(j)) {
j++;
}
i++;
}
return j == s2.length();
}
}