算法通关村第三关 | 数组双指针

1. 双指针思想

        在数组中,所谓双指针就是两个变量,不一定真的是指针,双指针在处理数组,字符串场景下很常见,例子:从下面序列中删除重复元素,[1,2,2,2,3,3,3,3,5,5,7,8],重复元素只保留一个,删除后结果:[1,2,3,5,7,8],如图:

 上面的一个在前一个在后的方式称为快慢指针,有些场景从两端向中间走,这种称为对撞型指针,还有背向型,从中间往两端走。

2.删除元素

LeetCode27.给你一个数组和一个值val。你需要原地移除所有的数值等于val的元素,并返回移除后数组的新长度,要求:不要使用额外的数组空间,你必须仅使用O(1),额外空间并原地修改输入数组,元素的顺序可以改变,你不需要考虑数组中超出新长度后面的数组。

 2.1 第一种:快慢指针,

定义两个指针slow和fast,初始值都是0,slow之前是有效部分,fast表示当前要访问的元素,

1. 如果nums[fast] != val时候,将其移动到nums[slow++]处,slow同步移动

2. 如果nums[fast] = val时候,slow不动,fast向后移动图示:

 代码:

    public static int removeElement(int[] nums,int val){
        int slow = 0;
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != val ){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }

2.2 对撞双指针

创建左右指针,分别在头尾处,

1. 当左边不等于val时候继续向右移动,如果等于val,和右指针不等于val时候,则替换,让右指针位置元素替换左指针,

2. 右指针不等于val时候停止移动,等待替换,等于val时候向前移动

3.直到左右碰撞的时候两指针停止。

以nums= [0,1,2,2,3,0,4,2] ,val = 2为例。

 代码:

    public static int remove2(int[] nums,int val){
        int right = nums.length-1;
        int left = 0;
        for(left = 0; left <= right;){
            if (nums[left] == val && nums[right] != val){
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = nums[temp];
            }
        }
        while (nums[left] != val) left++;
        while (nums[right] == val) right--;
        return left;
    }

2.2删除有序数组的重复项

 不考虑数组中超出新长度后面的元素,

1. 每个元素只出现一次,不考虑只有一个元素的移动情况

2. slow指针指向第二个元素,让nums[fast]和nums[slow -1] 比较,相等则fast移动,不相等则将nums[fast]填充到nums[slow]处,slow++;fast++;

3.因为slow最后指向位置的元素是不需要的,所以数组长度就是slow

代码:

    public static int remove(int[] nums){
        //slow表示可以放入新元素的位置,索引为0的元素可不用管,
        int slow = 1;
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != nums[slow -1]){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }

接下来由于作者精力有限只为大家提供题目和思路,如果需要进一步学习,可在评论区留言或自信我。后续有空会为大家补上。

3.元素奇偶移动专题

 这个题目和2.1中的对撞双指针解题思路类似

1. 定义左右指针,在头部和尾部,左指针为偶数则向右移动,不为偶数和右指针为偶数同时满足时候,交换位置,

2.交换完继续,左指针为偶数向右移动,右指针为奇数向左移动,

3.返回值,最后交换完毕返回的数组,不用考虑边界问题

4.数组轮转问题

解题思路:

1.  首先对整个数组反转,结果[7,6,5,4,3,2,1]

2. 在K处将数组分为两组,[7,6,5],  [4,3,2,1]

3. 再将两个数组都反转得到[5,6,7] 和[1,2,3,4]

5. 数组的区间专题

 解题思路:用快慢指针解题

1. 慢指针在每个区间的起始位置,快指针从慢指针处开始向后遍历,出现nums[fast]+1 !=  nums[fast+1]的时候达到边界此时[slow,fast]数组为一个区间

2.之后让slow=fast +1,重新开始遍历循环。

代码:

    public static List<String> ranges(int[] numns){
        List<String> res = new ArrayList<>();
        //slow指向第一个区间的起始位置
        int slow = 0;
        //fast向后遍历直到不满足连续递增为止numns[fast] +1 != numns[fast +1]
        //或者fast达到边界,则当前连续区间[slow,fast]遍历完毕,将其写入结果咧
        for (int fast = 0; fast < numns.length; fast++) {
            if (fast + 1 == numns.length && numns[fast] +1 != numns[fast +1]){
                //将当前区间[slow,fast]写入结果列表
                StringBuilder sb = new StringBuilder();
                sb.append(numns[slow]);
                if (slow != fast){
                    sb.append("->").append(numns[fast]);
                }
                res.add(sb.toString());
                //将slow更新到下个区间起始位置
                slow = fast +1;
            }
        }
        return res;
    }

6.字符串替换空格问题

解题思路:

第一种:当空间足够大的时候,将原始数组出现空格的位置直接替换成20%,

第二种:

1.先遍历一次数组,计算出字符串中的空格数,由此计算出替换后之后的字符串长度,每一个空格长度增加2,

2.从尾部开始复制和替换,用fast和slow指针分别指向原始字符串和新字符串的末尾,然后slow不动,fast移动,

3.若指向的不是空格,则复制到slow位置,然后两者同时移动一步,

4.若指向的是空格,在slow中插入20%,每插入一次移动一步,fast只移动一步,

代码:

    public String replase(StringBuffer str){
        if (str == null){
            return null;
        }
        //空格数量
        int numblank = 0;
        int len = str.length();
        for (int i = 0; i < len; i++) {
            if (str.charAt(i) == ' '){
                numblank++;
            }
        }
        //计算总长度并设置长度
        str.setLength(len + 2 * numblank);
        //两个指针‘
        int fast = len -1;
        int slow = len + 2 * numblank -1;
        //第二个条件
        while (fast >= 0 && slow > fast){
            char c = str.charAt(fast);
            if (c == ' '){
                fast--;
                str.setCharAt(slow--,'0');
                str.setCharAt(slow--,'2');
                str.setCharAt(slow--,'%');
            }else {
                str.setCharAt(slow,c);
                slow--;
                fast--;
            }
        }
        return str.toString();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值