双指针问题

单纯的双指针问题很简单,只需要排序后一前一后互相靠拢

对于双指针的"变种",三个或更多,只需要用一个原则:定一议二

三指针

例如三数之和,只需"定"一个指针,另外两个相互靠拢,即真正的"双指针"是另外两个

public List<List<Integer>> threeSum(int[] nums) {
    //对给定数组排序
    Arrays.sort(nums);
    LinkedList<List<Integer>> list = new LinkedList<>();
    //i递增遍历数组,j,k作为双指针,一前一后
    int i = 0, j, k;
    while(i <= nums.length-3){
        //i!=0:避免数组下标越界
        //如果nums[i] == nums[i-1],直接跳过,是为了去重,前提是数组已经排好序
        if(i != 0 && nums[i] == nums[i-1]){
            i++;
            continue;
        }else{
            j = i+1;
            k = nums.length-1;
            while(j < k){
                //同理,为了去重
                if (j != i+1 && nums[j] == nums[j-1]){
                    j++;
                    continue;
                //同理,为了去重
                }else if (k != nums.length-1 && nums[k] == nums[k+1]){
                    k--;
                    continue;
                }else {
                    if(nums[i] + nums[j] + nums[k] == 0){
                        list.add(new LinkedList<Integer>());
                        list.getLast().add(nums[i]);
                        list.getLast().add(nums[j]);
                        list.getLast().add(nums[k]);
                        j++;k--;
                    }else if(nums[i] + nums[j] + nums[k] < 0){
                        j++;
                    }else{
                        k--;
                    }
                }
            }
            i++;
        }
    }
    return list;
}

四指针

四数之和,"定"一个指针后再"定"一个指针,另外两个指针相互靠拢

public List<List<Integer>> fourSum(int[] nums,int target){
    //定义一个返回值
    List<List<Integer>> result=new ArrayList<>();
    //当数组为null或元素小于4个时,直接返回
    if(nums==null||nums.length<4){
        return result;
    }
    //对数组进行从小到大排序
    Arrays.sort(nums);
    //数组长度
    int length=nums.length;
    //定义4个指针k,i,j,h  k从0开始遍历,i从k+1开始遍历,留下j和h,j指向i+1,h指向数组最大值
    for(int k=0;k<length-3;k++){
        //当k的值与前面的值相等时跳过
        if(k>0&&nums[k]==nums[k-1]){
            continue;
        }
        //获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏
        int min1=nums[k]+nums[k+1]+nums[k+2]+nums[k+3];
        if(min1>target){
            break;
        }
        //获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略
        int max1=nums[k]+nums[length-1]+nums[length-2]+nums[length-3];
        if(max1<target){
            continue;
        }
        //第二层循环i,初始值指向k+1
        for(int i=k+1;i<length-2;i++){
            //当i的值与前面的值相等时跳过
            if(i>k+1&&nums[i]==nums[i-1]){
                continue;
            }
            //定义指针j指向i+1
            int j=i+1;
            //定义指针h指向数组末尾
            int h=length-1;
            //获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏,忽略
            int min=nums[k]+nums[i]+nums[j]+nums[j+1];
            if(min>target){
                break;
            }
            //获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略
            int max=nums[k]+nums[i]+nums[h]+nums[h-1];
            if(max<target){
                continue;
            }
            //开始j指针和h指针的表演,计算当前和,如果等于目标值,j++并去重,h--并去重,当当前和大于目标值时h--,当当前和小于目标值时j++
            while (j<h){
                int curr=nums[k]+nums[i]+nums[j]+nums[h];
                if(curr==target){
                    result.add(Arrays.asList(nums[k],nums[i],nums[j],nums[h]));
                    j++;
                    while(j<h&&nums[j]==nums[j-1]){
                        j++;
                    }
                    h--;
                    while(j<h&&i<h&&nums[h]==nums[h+1]){
                        h--;
                    }
                }else if(curr>target){
                    h--;
                }else {
                    j++;
                }
            }
        }
    }
    return result;
}

时间复杂度

"n指针"问题需要n-2个作为"定针",另外两个相互靠拢,所以"n指针"的时间复杂度只能降到(n-1)次方

posted @ 2020-02-28 20:17  tianqibucuo  阅读(130)  评论(0)    收藏  举报