算法通关村——数组双指针

双指针:使用两个变量对一个数据结构进行处理。

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

可使用3种双指针

第一种:快慢双指针

slow前为数组有效部分,fast表示当前访问的元素,均从数组首部开始移动。

如果nums[fast]!=val,令nums[slow]=nums[fast],fast与slow向后移动;

如果nums[fast]==val,fast++。

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

第二种:对撞双指针

left前为数组有效部分,right后为无效部分,left从数组首部开始移动,right从数组尾部开始移动。

如果nums[left]!=val,令left++;

如果nums[fast]==val,fast--;

如果nums[left]==val&&nums[right]!=val,nums[left]=nums[right]。

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

可进行优化

public static int removeElement(int[] nums,int val){
	int left = 0;
	int right = nums.length - 1;
	while(left<=right){
		if(nums[left]==val){
			nums[left]= nums[right];
			right--;
		}else{
			left++;
		}	
	}
	return left;
}

删除有序数组中的重复项

删除有序数组nums中的重复元素,使每个元素仅出现一次,返回删除后数组的新长度。

快慢双指针:

public static int removeDuplicates(int[] nums){
	int slow = 1;//slow为可放入新元素的位置
	int fast = 0;
	for(;fast<nums.length;fast++){
		if(nums[fast]!=nums[slow-1]){
			nums[slow] = nums[fast];
			slow++;
		}
	}
	return slow;
}

拓展:删除重复元素最多保存k个

    public static int removeDuplicates(int[] nums,int k){
        int slow = 1;//slow为可放入新元素的位置
        int fast = 0;
        int count = 0;//记录重复元素数目
        for(;fast<nums.length;fast++){
            if (nums[fast]!=nums[slow-1]){//不相同
                nums[slow]=nums[fast];
                slow++;
            }else {
                while (fast<nums.length&&nums[fast]==nums[slow-1]){//出现重复元素
                    fast++;
                    count++;
                }
                if (count>k){//重复元素数目大于k,则移动fast,使下一次判断的count==k,转到count<=k的情况
                    fast = fast - k;
                    count = 0;
                }
                if (count<=k){//slow的位置一般在第二个重复元素的位置,所以移动count-1次即可
                    for(;count-1>0;count--){
                        slow++;
                    }
                    fast--;
                }
            }
        }
        return slow;
    }

按奇偶排序数组

对撞双指针

left:寻找偶数

right:寻找奇数

left==偶数&&right==奇数,交换num[left]和num[right],left++和right++。

public static int[] sortArrayByParity(int[] nums){
	int left = 0;
	int right = nums.length - 1;
	while(left<=right){
		if(nums[left]%2==0&&nums[right]%2!=0){
			int temp = nums[right];
			nums[right] = nums[left];
			nums[left] = temp;
		}
		if(nums[left]%2!=0){
			left++;
		}
		if(nums[right]%2==0){
			right--;
		}
	}
	return nums;
}

数据轮转问题

将数组中的元素向右轮转k个位置,其中k为非负数。

使用两轮翻转:

1、将整个数组进行翻转,[1,2,3,4,5,6,7]反转为[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,6,7,1,2,3,4]。

   public static void rotate(int[] nums, int k){
        k%=nums.length;
        reverse(nums,0,nums.length-1);
        reverse(nums,0,k-1);
        reverse(nums,k,nums.length-1);
    }
    
    public static void reverse(int[] nums,int start,int end){
        while (start<end){
            int temp = nums[start];
            nums[start]=nums[end];
            nums[end]=temp;
            start++;
            end--;
        }
    }

数组的区间问题

给定一个无重复元素的有序整数数组nums。返回数组中所有数字的最小有序区间范围列表。

输入:[0,1,2,4,5,7]

输出:0->2,4->5,7

使用list来存储字符串,使用StringBuilder来组合字符串。

使用双指针,slow为区间的首,fast为区间的尾。

使用nums[fast]+1!=nums[fast+1]来判断是否在一个区间内。

    public static List<String> summaryRanges(int[] nums){
        List<String> res = new ArrayList<>();
        int slow = 0;
        for (int fast = 0; fast < nums.length;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("->").append(nums[fast]);
                }
                res.add(stringBuilder.toString());
                slow = fast + 1;
            }
        }
        return res;
    }

拓展:寻找缺失的区间

输入:[0,1,3,50,75],lower = 0,upper = 99

输出:2,4->49,51->74,76->99

缺失1个时,数组相邻元素差值为2;缺失两个及以上时,数组相邻元素差值大于等于3。

令slow为当前元素的前一个元素,fast为当前元素。

nums[fast]-slow==1;slow=nums[fast];fast++;

nums[fast]-slow==2;缺失的一个元素为slow+1或nums[fast]-1;slow=nums[fast];fast++;

nums[fast]-slow>=3;区间为[slow+1,nums[fast]-1];slow=nums[fast];fast++;

    public static List<String> lostRanges(int[] nums,int lower,int upper){
        List<String> res = new ArrayList<>();
        int slow = lower-1;
        int fast = 0;
        for (;fast<nums.length;fast++){
            if (nums[fast]-slow==2){
                res.add(String.valueOf(slow+1));
            }else if (nums[fast]-slow>=3){
                res.add((slow+1)+"->"+(nums[fast]-1));
            }
            slow = nums[fast];
        }
        if (upper-slow==1){
            res.add(String.valueOf(upper));
        }else if (upper-slow>=2){
            res.add((slow+1)+"->"+upper);
        }
        return res;
    }

具体可看leetcode-163

字符串替换空格问题

将几个字符串中的每个空格替换成“%20”,例如We Are Happy替换后为We%20Are%20Happy。

“ ”占一个字符,“%20”占3个字符

新串的长度=原来串的长度+2*空格数目

slow指向新串的末尾,fast指向原来串的末尾。

fast向前读取,读到的不是空格,则复制到slow位置,fast和slow向前一步;

读到空格,fast向前一步,slow向前填0、2、%,slow向前一步;

fast与slow相遇,说明替换完成。

  public static String replaceSpace(StringBuilder 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==' '){
                str.setCharAt(slow--,'0');
                str.setCharAt(slow--,'2');
                str.setCharAt(slow--,'%');
                fast--;
            }else {
                str.setCharAt(slow--,c);
                fast--;
            }
        }
        return str.toString();
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值