换汤不换药--双指针

题目1: 删除有序数组中的重复项
力扣https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/

题目描述:给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

 输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeDuplicates(int[] nums) {
        /**
        * 先判断边界条件
        **/
        if(nums==null || nums.length == 0){
            return 0;
        }
        if(nums.length == 1){
            return 1;
        }
        
        //定义两个指针
        int j = 0; //快指针,往后找到和i值不一样的地方
        int i = 0; //慢指针,等待互换的地方
        while(j<nums.length){
            if(nums[j] != nums[i]){
                nums[i+1] = nums[j];
                i++;
            }
            j++; // 如果nums[i]==nums[j], j就往后走,直到nums[i]!=nums[j],把j换到i+1的位置
        }
        return i+1; //最后数组的length比i大1
    }
}

题目2: 最长连续递增序列

力扣https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/

题目描述:给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。 

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int len = nums.length;
        int res = 0; //目前,最长连续递增序列的长度
        int i = 0; //慢指针, 指向递增序列的头
        int j = 0;//快指针, 指向递增序列的尾

        while(j<len){
            //因为是递增序列,所以异常条件就是nums[前一位] >= nums[后一位]
            if(j>0 && nums[j-1] >= nums[j]){
                i = j;
            }
            
            // 把异常条件排除后, 剩下的就是可以正常比较的了
            j++;
            res = Math.max(res, j-i);

        }
        return res;
    }

}

题目3: 移除元素

力扣https://leetcode-cn.com/problems/remove-element/ 题目描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums == null || nums.length==0){
            return 0;
        }
        int len = nums.length ; //指向数组尾部的指针
        int i = 0; //从前往后循环的指针
        while(i<len){
            if(nums[i] == val){ // 因为不考虑数组的顺序, 所以当有val的时候,从数组末尾拿个数过来补位就可以
                nums[i] = nums[len-1];
                len--;
            }else{
                i++;
            }
        }
        return len;
    }
}

题目4: 删除排序数组中的重复项 II

力扣https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/题目描述:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

输入:nums = [0,0,1,1,1,1,2,3,3]
输出:7, nums = [0,0,1,1,2,3,3]
解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。 不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeDuplicates(int[] nums) {

        if(nums == null || nums.length==0){
            return 0;
        }
        if(nums.length <=2){
            return nums.length;
        }

        int slow = 2;
        int fast = 2;
        while(fast<nums.length){

            //因为有序数组,所以fast只需要和slow的前两位比较即可
            if(nums[slow-2] != nums[fast]){
                nums[slow] = nums[fast];
                slow ++;
            }
            fast++;
        }
        return slow;


    }
}

题目3和题目4 的区别: 

题目三为无序数组,可以使用头指针和尾指针, 相互交换

题目四为有序数组,需要快慢指针,进行比较和交换

题目5: 移动零

力扣https://leetcode-cn.com/problems/move-zeroes/题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。

输入: nums = [0,1,0,3,12]

输出: [1,3,12,0,0]

    /**第一版本,很乱
    **/
    public void moveZeroesFirst(int[] nums) {

        int i = 0;
        int j = 1;
        while (j < nums.length && i < nums.length) {
            if (nums[i] == 0) {
                while (nums[j] == 0 && j + 1 < nums.length) {
                    j++;
                }
                int temp = nums[j];
                nums[j] = nums[i];
                nums[i] = temp;
                i++;
                j++;
            } else {
                i++;
                j++;
            }
        }
        
    }

    /**第二版本
    * if和else中相同的内容可以拿出来
    **/
    public void moveZeroesSecond(int[] nums) {

        int i = 0;
        int j = 1;
        while (j < nums.length && i < nums.length) {
            if (nums[i] == 0) {
                while (nums[j] == 0 && j + 1 < nums.length) {
                    j++;
                }
                int temp = nums[j];
                nums[j] = nums[i];
                nums[i] = temp;
            } 
                i++;
                j++;
            
        }
    }

    /**第三版本
    * 换了个判断条件,就清楚多了
    **/
    public void moveZeroesThird(int[] nums) {
        int i = 0;
        int j = 0;
        while (j < nums.length) {
            if (nums[j] != 0) {
                int temp = nums[j];
                nums[j] = nums[i];
                nums[i] = temp;
                i++;
            }
            j++;
        }

    }

题目6: 盛水最多的容器盛最多水的容器icon-default.png?t=M3K6https://leetcode-cn.com/problems/container-with-most-water/

题目描述: 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。  找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。 说明:你不能倾斜容器。

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

public int maxArea(int[] height) {
        if(height.length < 2){
            return 0;
        }

        /**
         * 面积=长*宽, 所以要保证左右两边尽可能长, 最小值尽可能大
         */
        int left = 0; //最左开始
        int right = height.length-1; //最右开始
        int area = 0;
        while (left< right){
            area = Math.max(area,Math.min(height[left],height[right]) * (right-left));

            if(height[left]<=height[right]){ // 短的一个边往前移,试图找到长一点的边
                left++;
            }else{
                right--;
            }

        }
        return area;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值