链表中的双指针问题:
// 初始化快慢指针
ListNode slow = head;
ListNode fast = head;
/**
* 对于每个问题具体改变
* 注意避免空指针异常
**/
while (slow != null && fast != null && fast.next != null) {
slow = slow.next; // 每次移动慢指针一步
fast = fast.next.next; // 每次移动快指针两步
if (slow == fast) { // 具体问题具体修改
return true;
}
}
return false;
需要把双指针技巧分为两类,一类是快慢指针,一类是左右指针。前者主要解决链表中的问题,比如典型的判别链表中是否包含环;后者主要解决数组(或者字符串)中的问题,比如二分查找。
一、快慢指针的常见算法
快慢指针一般都初始化指向链表的头结点head,前进时快指针fast在前,慢指针在后。
1.判断链表中是否有环
单链表的特点是每个节点只知道下一个节点,所以一个指针的话无法判断链表是否含有环。如果链表中有环,那么这个指针最终会遇到null.
boolean hasCycle(ListNode head){
while(head != null){
head = head.next;
}
return false;
}
如果有环,那么这个指针就会陷入死循环。
用快慢指针,如果不含有环,跑得快的那个指针最终会遇到null,说明链表中不含有环;如果含有环,快指针最终会与慢指针相遇,说明有环。类似小学数学中环形操场赛跑的追击问题。
boolean hasCycle(ListNode head){
ListNode fast,slow;
fast = slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
return true;
}
return false;
}
2.已知链表中含有环,返回这个换的起始位置
3.寻找链表的中点
让快指针一次前进2步,慢指针一次前进1步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。
while(null != fast && null != fast.next){
fast = fast.next.next;
slow = slow.next;
}
// slow 就在中间位置
return slow;
如果链表长度是奇数,slow在中点位置;如果是偶数,slow在中间偏右;
4.寻找链表的倒数第K个元素【删除链表的第k个元素】
先让快指针走k步,然后快慢指针开始同速度前进。这样当快指针走到链表末尾null时,慢指针所在的位置就是倒数第k个链表节点【但是要注意特殊情况--只有一个节点、删除的是头结点】
ListNode slow, fast;
slow = fast = head;
while(k-- > 0){ // 让快指针先移动k步
fast = fast.next;
}
while(fast != null){ // 此时两者的间隔刚刚是K,当fast到达结尾时,slow刚刚是倒数第k个
slow = slow.next;
fast = fast.next;
}
return slow;
二、左右指针的常用算法
左右指针在数组中实际是两个索引值,一般初始化为left = 0,right=nums.length-1.
1.二分查找
int binarySearch(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
while(left <= right){
if (nums[mid] == target){
return mid;
} else if (nums[mid] < target){
left = mid + 1;
}else if (nums[mid] > target){
right = mid - 1;
}
}
return -1;
}
2.两数之和【leetcode第一道题】
只要数组有序,就应该想到双指针技巧。
int[] twoSum(int[] nums, int target){
int left = 0, right = nums.length - 1;
while(left < right){
int sum = nums[left] + nums[right];
if (sum == target){
return new int[]{left + 1, right + 1};
} else if (sum < target){
left++;
} else if (sum > target){
right--;
}
}
return new int[]{-1, -1};
}
3.反转数组
void reverse(int[] nums){
int left = 0;
int right = nums.length - 1;
while(left < right){
// swap(a,b);
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++; right--;
}
}