双指针在算法中无论是数组还是链表类题目中都是重要且常见的“套路”之一。
- 两数之和
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] ret = new int[2];
//首尾各一个指针,根据有序性移动指针
int index1=0,index2 = nums.length-1;
while(index1<index2&&nums[index1]+nums[index2]!=target){
if(nums[index1]+nums[index2]<target){
index1++;
}else{
index2--;
}
}
ret[0] = nums[index1];
ret[1] = nums[index2];
return ret;
}
}
-
最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
先固定一个数,然后前后各一个指针,进行遍历找最接近!
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int curMinSum=nums[0]+nums[1]+nums[2];
for(int i=0;i<nums.length;i++){//先固定一个指针,再找两数之和
int l = i+1;
int r = nums.length-1;
while(l<r){
int threeSum = nums[i]+nums[l]+nums[r];
if(Math.abs(threeSum-target)<Math.abs(curMinSum-target)){
curMinSum = threeSum;
}
if(threeSum<target){
l++;
}else if(threeSum>target){
r--;
}else{
return target;
}
}
}
return curMinSum;
}
} -
-
四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ret = new ArrayList();
Arrays.sort(nums);
int l=0,r=0;
for(int i=0;i<nums.length-3;i++){
if(i>=1&&nums[i]==nums[i-1]) continue;
int first = nums[i];//先固定两个位置,再找两数之和!
for(int j=i+1;j<nums.length-2;j++){
if(j-1>=i+1&&nums[j]==nums[j-1]) continue;
int second = nums[j];
int rest = target-first-second;
l=j+1;
r = nums.length-1;
while(l<r){
if(rest>nums[l]+nums[r]){
l++;
}else if(rest<nums[l]+nums[r]){
r--;
}else{
List<Integer> list = new ArrayList();
list.add(first);
list.add(second);
list.add(nums[l]);
list.add(nums[r]);
ret.add(list);
l++;
r--;
while(l<r&&nums[l]==nums[l-1]&&nums[r]==nums[r+1]){
l++;
r--;
}
}
}
}
}
return ret;
}
}-
链表中倒数第K个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。(考虑到如果这个链表长度不到k呢?)
-
- class Solution {//快慢指针,先让快指针先走K步,再同时让两个指针走,当快指针到达链表尾部时,慢指针自然在倒数第K个了
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former = head,later =head;
while(k>1&&former!=null){
former = former.next;
k--;
}
while(former.next!=null){
later = later.next;
former = former.next;
}
return later;
}
}-
合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null&&l2==null){
return null;
}else if(l1==null&&l2!=null){
return l2;
}else if(l1!=null&&l2==null){
return l1;
}
ListNode dummy = new ListNode(0);
ListNode r = dummy;//设定哑结点,便于处理边界条件!
ListNode p = l1;//双指针分别遍历两个链表
ListNode q = l2;
while(p!=null&&q!=null){
if(p.val<=q.val){
r.next = p;
p = p.next;
}else{
r.next = q;
q = q.next;
}
r = r.next;
r.next = null;
}
if(p==null){
r.next = q;
}
if(q==null){
r.next = p;
}
return dummy.next;
}
}-
找到链表环中的第一个节点
注:先判断是否有环,然后计算出环的数量,快慢指针找出首节点
给定一个有环链表,实现一个算法返回环路的开头节点。 有环链表的定义:在链表中某个节点的next元素指向在它前面出现过的节点,则表明该链表存在环路。
-
-
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null||head.next==null){
return null;
}
ListNode slow = head;//快慢指针判断是否有环
ListNode fast = head.next.next;
while(fast!=null&&fast.next!=null){
if(fast==slow){
break;
}
slow = slow.next;
fast = fast.next.next;
}
if(fast==null||fast.next==null){
return null;
}
ListNode p = slow;//单指针统计环的数量
int count=1;
while(p.next!=fast){
p = p.next;
count++;
}
slow = head;
fast = head;//快慢指针找出链表环的第一个节点
while(count>0){
fast = fast.next;
count--;
}
while(slow!=fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}