双指针:使用两个变量对一个数据结构进行处理。
原地删除所有数值等于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();
}