LeetCode笔记之双指针(一)

双指针在算法中无论是数组还是链表类题目中都是重要且常见的“套路”之一。

  • 两数之和

输入一个递增排序的数组和一个数字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;
        }
    }

猜你喜欢

转载自www.cnblogs.com/elegentProgramer/p/12392290.html