双指针总结

盛最多的水

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。
在这里插入图片描述
思路:两个指针,l,r 两边往中间遍历,
height[i]<height[j],i++;
height[i]>height[j],j–;

class Solution {
    public int maxArea(int[] height) {
        int maxArea=0;//最大面积
        int tmp=0;//记录临时面积
        int n=height.length;
        int i=0;
        int j=n-1;
        //双指针
        while(i<j){
            if(height[i]<height[j]){
                tmp=(j-i)*height[i];
                i++;
            }else{
                tmp=(j-i)*height[j];
                j--;
            }
            maxArea=Math.max(maxArea,tmp);
        }
        return maxArea;
    }
}

三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。
思路:先排序,三个指针k,i,j ,固定k,然后i,j往中间遍历,找到目标值,就i++,j–,同时要注意不能有重复的三元组

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //排序
        Arrays.sort(nums);
        int n=nums.length;
        int k=0,i=0,j=0;
        List<List<Integer>> res=new ArrayList<>();
        if(n<3||nums[0]>0){
            return res;
        }
        while(k<n){
            if(nums[k]>0){
                break;
            }
            i=k+1;
            j=n-1;
            while(i<j){
                if(nums[k]+nums[i]+nums[j]==0){
                    List<Integer> list=new ArrayList<>();
                    list.add(nums[k]);
                    list.add(nums[i]);
                    list.add(nums[j]);
                    res.add(list);
                    //避免相同的nums[i]和nums[j]再次出现
                    while(i<j&&nums[i]==nums[i+1]){
                        i++;
                    }
                    i++;
                    while(i<j&&nums[j]==nums[j-1]){
                        j--;
                    }
                    j--;
                }else if(nums[k]+nums[i]+nums[j]<0){
                   i++;
                }else{
                    j--;
                }
            }
            //避免相同的nums[k]的出现
            while(k<n-1&&nums[k]==nums[k+1]){
                k++;
            }
            k++;
        }
        return res;
    }
}

数对和

设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。

示例 1:

输入: nums = [5,6,5], target = 11
输出: [[5,6]]
思路:先排序,两个指针l,r首尾开始往中间遍历

class Solution {
    public List<List<Integer>> pairSums(int[] nums, int target) {
        Arrays.sort(nums);
        int n=nums.length;
        int i=0,j=n-1;
        List<List<Integer>> res=new ArrayList<>();
        while(i<j){
            if(nums[i]+nums[j]<target){
                i++;
            }else if(nums[i]+nums[j]>target){
                j--;
            }else{
                List<Integer> list=new ArrayList<>();
                list.add(nums[i]);
                list.add(nums[j]);
                res.add(list);
                i++;
                j--;
            }
        }
        return res;
    }
}

面试题 16.06. 最小差

给定两个整数数组a和b,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差

示例:

输入:{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8}
输出:3,即数值对(11, 8)

思路:
* 先对两个数组进行排序,
* 然后两个指针分别指向两个数组的初始位置 i=0,j=0计算两个数的差值的绝对值abs(a[i]-b[j]
* min=Math.min(min,abs(a[i]-b[j])
* 然后把两数中的较小的数的指针右移,如此循环,如果min=0,结束return
注:两个数的 差值可能超出int的范围,所以计算的时候用long型,返回的时候转成int

import static java.lang.Math.abs;

class Solution {
    public int smallestDifference(int[] a, int[] b) {
        //排序
        Arrays.sort(a);
        Arrays.sort(b);
        long min=Integer.MAX_VALUE;
        int i=0,j=0;

        while(i<a.length&&j<b.length){
            long tmp=abs((long)a[i]-(long)b[j]);//取两数差的绝对值
            if(tmp<=min){//如果小于最小值,更新最小值
                min=tmp;
            }
            //如果两数差的绝对值最小值已经是0,就结束循环
            if(min==0){
                break;
            }
            if(a[i]<b[j]){
                i++;
            }else{
                j++;
            }
        }
        //返回结果,转成int型
        return (int)min;
    }
}

面试题 16.16. 部分排序

给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。

示例:

输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]
思路:
双指针,start,end
end:从前往后找,同时保持数组递增,如果出现导致数组不递增的元素,就用end标记,遍历一遍数组之后,end的标记位置,就是需要排序的区间的右区间
start:同理,从后往前,遍历一遍数组,保持递减,start标记的最终位置就是需要排序的左区间
例如:
1 2 3 5 4 5 6 1 7 8 9
从前往后:1 2 3 5 4 5 6 1 7 8 9
end标记的元素有:4,1,但最终右区间end=元素1 的下标
从后往前:1 2 3 5 4 5 6 1 7 8 9
start标记的元素有:6,5,4,5,3,2,但最终左区间start=元素2的下标

class Solution {
    public int[] subSort(int[] array) {
        int n=array.length;
        int start=-1,end=-1;
        int max=Integer.MIN_VALUE,min=Integer.MAX_VALUE;
        int[] res=new int[2];
        //从前往后找,保持数组的递增,找到最后一个导致数组不递增的那个点,就是无序的end
        for(int i=0;i<n;i++){
            if(array[i]>=max){
                max=array[i];
            }else{
                end=i;
            }
        }
        //如果end==-1,那么说明该数组是有序递增的,不需要排序
        if(end==-1){
            res[0]=-1;
            res[1]=-1;
            return res;
        }
        //从后往前,保持数组的递减,找到最后一个导致数组不递减的那个点,就是无序的start
        for(int i=n-1;i>=0;i--){
            if(array[i]<=min){
                min=array[i];
            }else{
                start=i;
            }
        }

        res[0]=start;
        res[1]=end;
        return res;
    }
}

寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
思路:
快慢指针,low,fast,
1.low=nums[low],fast=nums[nums[fast]]两个指针第一次相与是确定有重复的数,
2.恢复快指针的速度,low=nums[low],fast=nums[fast],第二次相遇是确定重复的数

class Solution {
    public int findDuplicate(int[] nums) {
        //快慢指针,第一次相与判定有环,把快指针重置开始的位置,速度恢复慢指针相同的速度,
        //第二次相与就是开始的入口
        int low=0,fast=0;
        do{
            fast=nums[nums[fast]];
            low=nums[low];
        }while(fast!=low);
        fast=0;
        while(fast!=low){
            fast=nums[fast];
            low=nums[low];
        }
        return fast;
    }
}

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作
思路:
两个指针l,r,刚开始都指向索引0位置,有指针找到不为零的数和左指针的0互换,这样就把0换到了后面

class Solution {
    public void moveZeroes(int[] nums) {
        //两个指针,l,r,右指针遍历数组,遇到非零的数就和左指针的0互换
        int l=0,r=0;
        while(r<nums.length){
            if(nums[r]!=0){
                swap(nums,l,r);
                l++;
            }
            r++;
        }
    }

    public void swap(int[] nums,int l,int r){
        int tmp=nums[l];
        nums[l]=nums[r];
        nums[r]=tmp;
    }
}

环形链表

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。


public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null){
            return null;
        }
        ListNode low=head,fast=head;
        while(fast!=null){
            low=low.next;
            if(fast.next!=null){
                fast=fast.next.next;
            }else{
                return null;
            }
            //如果两个指针相遇,说明链表存在环
            if(low==fast){
                fast=head;//把fast重新指向head
                while(fast!=low){
                    fast=fast.next;
                    low=low.next;
                }
                return low;
            }
        }
        
        return null;
    }
}

相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。
在这里插入图片描述
思路:
我把你走过的路走一遍,你把我走过的路走一遍,如果有缘,总有一天我们会相遇

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null){
            return null;
        }
        ListNode p=headA,q=headB;
        while(p!=q){
            p=p==null?headB:p.next;
            q=q==null?headA:q.next;
        }

        return p;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值