算法通关村第二关——双指针思想及应用

算法通关村第二关——双指针思想及应用

双指针思想

思路:指在使用两个变量来进行相互配合,相互操作

例子:删除重复元素[1,2,2,2,3,3,3,5,5,7,8],重复元素只保留一个

图示:

删除元素专题

原地移除所有数值等于val的元素

题目:LeetCode:27

思路:可以使用三种双指针的思路:

  1. 快慢双指针,slow在相等val处等待,等待fast遍历到非val值,然后进行替换

    public 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. 对撞双指针:思路为从右侧找到不是val的值来替换左侧是val的值

        public int removeElement(int[] nums, int val) {
            //1.对撞双指针
            int left = 0;
            int right = nums.length-1;
            for (; left <= right;) {
                if (nums[left]==val&&nums[right]!=val){
                    int tmp = nums[right];
                    nums[right]= nums[left];
                    nums[left]= tmp;
                }
                if (nums[left]!=val){
                    left++;
                }
                if (nums[right]==val){
                    right--;
                }
            }
            return left;
        }
    
  3. 两种结合,在第二种的基础上不做交换,而是直接做覆盖,所以不必判断右边是否为val,因为会慢慢替换

        public int removeElement(int[] nums, int val) {
            //1.对撞双指针+覆盖法
            int left = 0;
            int right = nums.length-1;
            for (; left <= right;) {
                if (nums[left]==val){
                    nums[left]= nums[right];
                    right--;
                }
                if (nums[left]!=val){
                    left++;
                }
            }
            return left;
        }
    
删除有序数组中的重复项

题目:leetCode 26

思路:双指针,一个指针负责数组遍历,一个指向有效数组的最后一个位置,注意:可以另slow=1,以减少不必要的操作,并且比较对象变为nums[slow-1]

解法:

  1. 双指针

            /**
            * 利用双指针解决,注意点:可以令slow=1,并且比较的对象换做nums[slow-1],这样来比较换玩完之后位置的元素
            * @param nums
            * @return
            */
            public int removeDuplicates(int[] nums) {
                if (nums.length==0){
                    return 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;
            }
    
  2. 扩展(保留2个有效字符)

        /**
         * 利用双指针解决,注意点:可以令slow=2,并且比较的对象换做nums[slow-2],这样来比较换完之后位置的元素
         * @param nums
         * @return
         */
        public int removeDuplicates(int[] nums) {
            if (nums.length<=2){
                return nums.length;
            }
            //开始循环便利
            int slow = 2;
            for (int fast = 2; fast < nums.length; fast++) {
                if (nums[fast]!=nums[slow-2]){
                    nums[slow]=nums[fast];
                    slow++;
                }
            }
            return slow;
        }
    

元素奇偶移动专题

题目:LeetCode 905

思路:最简单的思路是 搞一个临时数组,然后利用双指针我们可以搞一个对撞双指针,通过比较是否奇偶来进行对换

解法:

  1. 对撞双指针

        /**
         * 利用对撞双指针实现,两天碰到奇偶的时候进行交换
         * @param nums
         * @return
         */
        public int[] sortArrayByParity(int[] nums) {
            if (nums.length<=1){
                return nums;
            }
            int left = 0;
            int right = nums.length-1;
            while (left<=right){
                while (left<nums.length&&nums[left]%2==0){
                    left++;
                }
                while (right>=0&&nums[right]%2==1){
                    right--;
                }
                if (left>=right){
                    return nums;
                }
                int tmp = nums[right];
                nums[right] = nums[left];
                nums[left] = tmp;
    
            }
            return nums;
        }
    

    优化版本

        /**
         * 利用对撞双指针实现,两天碰到奇偶的时候进行交换()
         * @param nums
         * @return
         */
        public int[] sortArrayByParity(int[] nums) {
            if (nums.length<=1){
                return nums;
            }
            int left = 0;
            int right = nums.length-1;
            while (left<=right){
                if (nums[left]%2>nums[right]%2){
                int tmp = nums[right];
                nums[right] = nums[left];
                nums[left] = tmp;
                }
              if(nums[left]%2==0)left++;
              if(nums[right]%2==1)right--;
            }
            return nums;
        }
    

数组轮换问题

题目:leetCode:189

思路:思路跟链表的轮换问题一致,先整个进行反转,然后在反转0-k-1部分和k-length部分,注意K要是除于length的余,

解法:

  1. 翻转数组

        /**
         * 数组轮转,先整体翻转,在根据k处分隔成左右两个部分分别翻转
         * @param nums
         * @param k
         */
        public void rotate(int[] nums, int k) {
            k %= nums.length;
            //1. 先进行整体翻转
            revers(nums,0,nums.length-1);
            //2.分成两个部分进行分别反转
            revers(nums,0,k-1);
            revers(nums,k,nums.length-1);
        }
    
        public void revers(int[]nums,int start,int end){
            while (start<end){
                int tmp = nums[start];
                nums[start] = nums[end];
                nums[end] = tmp;
                start++;
                end--;
            }
        }
    

数组区间问题

题目:leetCode:228

思路 :通过快慢指针来实现,慢指针值区间 头,快指针遍历,遇上不连续的区间则输出

解法:

  1. 快慢指针

        /**
         * 使用快慢指针来进行解决
         * @param nums
         * @return
         */
        public List<String> summaryRanges(int[] nums) {
            ArrayList<String> list = new ArrayList<>();
            int fast = 0;
            int slow = 0;
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < nums.length; i++) {
                if (nums[slow] == nums[fast]) {
                    //相等则为刚开始区间
                    stringBuilder.append(nums[slow]);
                }
                if (fast+1>=nums.length){
                    //说明已经便利到尽头
                    if (slow!=fast){
                        stringBuilder.append("->" + nums[fast]);
                    }
                    list.add(stringBuilder.toString());
                    break;
                }
                //不相等则继续递增
                if (nums[fast + 1] != nums[fast] + 1) {
                    //不递增,结束
                    if (slow != fast) {
                        stringBuilder.append("->" + nums[fast]);
                    }
                    list.add(stringBuilder.toString());
                    stringBuilder = new StringBuilder();
                    //递增
                    fast++;
                    slow = fast;
                }else {
                    fast++;
                }
            }
            return list;
        }
    
  2. 优化版本

        /**
         * 使用快慢指针来进行解决
         * @param nums
         * @return
         */
        public List<String> summaryRanges(int[] nums) {
            ArrayList<String> list = new ArrayList<>();
            int slow = 0;
            for (int fast = 0; fast < nums.length; fast++) {
                //fast向后遍历,直到不满足连续递增(即 nums[fast + 1] != nums[fast] + 1)
                //或者fast 达到数组边界,则当前连续递增区间[slow,fast]遍历完毕,将其写入结果列表
                if (fast + 1 == nums.length || nums[fast + 1] != nums[fast] + 1) {
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.append(nums[slow]);
                    if (slow != fast) {
                        stringBuilder.append("->" + nums[fast]);
                    }
                    list.add(stringBuilder.toString());
                    //将 slow 指向更新为fast+1,作为下一个区间的起始位置
                    slow = fast + 1;
                }
            }
            return list;
        }
    

字符串替换空格问题

题目:剑指offer

思路:1. 利用char数组管理,直接把原来的原始数组的中的空格位置进行替换,如果不能申请额外的空间,则通过双指针进行移动和替换

解法 :

  1. 利用char数组(可变数组)

        /**
         * 方法1:创建新的字符串
         *
         * @param str
         * @return
         */
        public static String replaceSpace1(StringBuffer str) {
            String res = "";
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                if (c == ' ')
                    res += "%20";
                else
                    res += c;
            }
            return res;
        }
    
  2. 利用 快慢双指针(固定数组)

        /**
         * 方法2:在原数组基础上改
         *
         * @param str
         * @return
         */
        public static String replaceSpace2(StringBuffer str) {
            if (str == null)
                return null;
            int numOfblank = 0;//空格数量
            int len = str.length();
            for (int i = 0; i < len; i++) {  //计算空格数量
                if (str.charAt(i) == ' ')
                    numOfblank++;
            }
            str.setLength(len + 2 * numOfblank); //设置长度
            int fast = len - 1;  //两个指针
            int slow = (len + 2 * numOfblank) - 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);
                    fast--;
                    slow--;
                }
            }
            return str.toString();
        }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值