LeetCode笔记之双指针(一)

只愿长相守 提交于 2020-03-01 22:08:28

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

  • 两数之和

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!