快慢指针
主要解决链表中的一些问题,比如判断链表中是否右环,判断两个链表的相较节点
判断链表中是否有环
// https://leetcode-cn.com/problems/linked-list-cycle/
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null) return false;
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) return true;
}
return false;
}
}
返回环入口位置
// https://leetcode-cn.com/problems/linked-list-cycle-ii/
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) return null;
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}
简单证明:
设从
head到环入口距离为a,环入口到fast和slow相交点距离为b,b到环入口为c,则: \(\begin{align} fast &= a + n * (b + c) + b \\ slow &= a + m * (b + c) + b \\ \\ fast - slow &= (n - m) * (b + c) \\ &= slow = a + m * (b + c) + b \\ \\ a &= (n - 2m) * (b + c) - b \\ &= (n - 2m - 1) * (b + c) + c \end{align}\) 可以看出,a的距离等于多圈环距离加上c的距离,故可以分别让两个节点从头节点和fast和slow相交点开始走,相交处就是环入口
寻找链表的中点
ListNode middleNode(ListNode head) {
ListNode fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
// slow 就在中间位置
return slow;
}
需要注意的是,当链表的长度为奇数,slow恰好停在中间位置;而当链表的长度为偶数,slow的位置会在中间偏右
删除链表的倒数第 N 个结点
// https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode slow = dummy;
ListNode fast = head;
for (int i = 0; i < n; i ++) {
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
}
左右指针
主要解决数组(字符串)中的问题,比如二分查找
两数之和 II - 输入有序数组
// https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0;
int right = numbers.length - 1;
while (left < right) {
if (numbers[left] + numbers[right] == target) {
return new int[] {left + 1, right + 1};
} else if (numbers[left] + numbers[right] < target) {
left += 1;
} else if (numbers[left] + numbers[right] > target) {
right -= 1;
}
}
return new int[] {};
}
}
三数之和
// https://leetcode-cn.com/problems/3sum/
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
List<List<Integer>> list = new ArrayList<>();
if (len < 3) return list;
Arrays.sort(nums);
for (int i = 0; i < len - 2; i ++) {
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
int target = -nums[i];
int start = i + 1;
int end = len - 1;
while (start < end) {
if (nums[start] + nums[end] == target) {
list.add(new ArrayList<>(Arrays.asList(nums[i], nums[start], nums[end])));
start ++;
end --;
while (start < end && nums[start] == nums[start - 1]) start ++;
while (start < end && nums[end] == nums[end + 1]) end --;
} else if (nums[start] + nums[end] > target) {
end --;
} else {
start ++;
}
}
}
return list;
}
}
四数之和
// https://leetcode-cn.com/problems/4sum/
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
if (nums.length < 4 || nums == null) return res;
Arrays.sort(nums);
for (int i = 0; i < nums.length - 3; i ++) {
if (nums[i] > target && nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
for (int j = i + 1; j < nums.length - 2; j ++) {
// 此处注意后面的值
if (nums[j] > target - nums[i] && nums[i] > 0) break;
// 此处 j 需要大于 i + 1
if (j > i + 1 && nums[j] == nums[j - 1]) continue;
int tmpTarget = target - nums[i] - nums[j];
int left = j + 1;
int right = nums.length - 1;
while (left < right) {
if (nums[left] + nums[right] == tmpTarget) {
res.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[left], nums[right])));
left ++;
right --;
while (left < right && nums[left] == nums[left - 1]) left ++;
while (left < right && nums[right] == nums[right + 1]) right --;
} else if (nums[left] + nums[right] > tmpTarget) {
right --;
} else {
left ++;
}
}
}
}
return res;
}
}
反转字符串
// https://leetcode-cn.com/problems/reverse-string/)
class Solution {
public void reverseString(char[] s) {
int n = s.length;
for (int i = 0; i < n / 2; i ++) {
char tmp = s[i];
s[i] = s[n - 1 - i];
s[n - 1 - i] = tmp;
}
}
}