力扣算法题(三)双指针

283-Move Zeroes

给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。
必须在原数组上操作,不能拷贝额外的数组

使用两个指针,i是遍历指针,指向遍历的元素,j指向下一个要覆盖元素的位置。
非0就赋给nums[j],J++;是0的话就j不动。

创建两个指针i和j,第一次遍历的时候指针j用来记录当前有多少非0元素。
遍历的时候每遇到一个非0元素就将其往数组左边挪,第一次遍历完后,
j指针的下标就指向了最后一个非0元素下标

public void moveZeros(int[] nums) {
        	if(nums==null) {
        		return;
        	}
        	//第一次遍历的时候,j指针记录非0的个数,只要是非0的统统赋给nums[j]
        	int j=0;
        	for(int i=0;i<nums.length;++i) {
        		if(nums[i]!=0) {//不是0,的话赋给j所在且j++,是0的话j不移动
        			nums[j]=nums[i];j++;
        		}
        	}
        	//非0元素统计完了,剩下的都是0了
        	//所以第二次遍历把末尾的元素都赋为0即可
        	for(int i=j;i<nums.length;++i) {
        		nums[i]=0;
        	}
        }
    一次遍历,参考了快速排序的思想,快速排序首先确定一个待分割的元素做中间点x
    然后把所有小于等于x的元素放在左边,大于x的元素放右边
    这里我们用0当作这个中间点,把不等于0的放到中间点的左边
    注意题目没说没有负数
 public void moveZeroes01(int[] nums) {
        	if(nums==null) {
        		return;
        	}
        	//两个指针i和j
        	int j=0;
        	for(int i=0;i<nums.length;i++) {
        		//当前元素!=0,就把其交换到左边
        		if(nums[i]!=0) {
        			int tmp=nums[i];
        			nums[i]=nums[j];
        			nums[j++]=tmp;
        		}
        	}
        }
 public void moveZeroes(int[] nums) {
        	int sums[]=null,start=0,end=nums.length;
        	for(int i=0;i<nums.length;i++) {
        		if(nums[i]==0) {
        			sums[end]=nums[i];
        			end--;
        		}else {
        			sums[start]=nums[i];
        			start++;
        		}
        	}
        }

7-Remove Element (283与27类型相似,都为在数组中移除给定的值)

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

保留两个指针,j是遍历指针,i指针指向下一个需要被移除的位置 当nums[j]与给定的值相等,递增j以跳过该元素,i此时不动留在需要被覆盖元素位置
当nums[j]不等于val,nums[j]不用移除,我们就用nums[j]覆盖nums[i],且i++继续指向下一个需要被覆盖的元素

public int removeElement(int[] nums,int val) {
        	int i=0;
        	for(int j=0;j<nums.length;j++) {
        		if(nums[j]!=val) {//nums[j]不需要覆盖,放在i位置
        			nums[i]=nums[j];
        			i++;//不等于的时候不用覆盖所以向前移动
        			//等于的时候就不移动等待被覆盖
        			//i继续向后指向下一个需要被覆盖的元素
        		}
        	}
        	return i;
        }

双指针,当要删除的元素很少时 当我们遇到nums[i]=val时,我们可以将当前元素与最后一个元素交换,并释放最后一个元素
这实际上使数组的大小减少了1 被交换的最后一个元素可能是需要移除的值,但是下一次迭代中我们还会检查这个元素

 public int removeElement01(int[] nums,int val) {
        	
         int i=0;
         int n=nums.length-1;
         while(i<n) {
        	 if(nums[i]==val) {
        		 nums[i]=nums[n];
        		 n--;//如果换了之后还是val,会有下次迭代,因为这里i没有右移
        	 }else {
        		 i++;
        	 }
         }
         return n;
    }

26-Remove Duplicates from Sorted Array

给定一个排序数组,需要在原地删除重复出现的元素,返回移除后数组的新长度,不能改顺序

    //错误没有检查出来
    public int removeDuplicates(int[] nums) {
    	int j=0;
    	//i是快指针,j是慢指针
    	for(int i=0;i<nums.length-1;i++) {
    		if(nums[i]==nums[j]) {
    			
    		}else {
    			nums[j+1]=nums[i];//不相等j++
    			j++;i++;
    		}
    	}
    	return nums.length-j;
    }

双指针法
i指针指向需要覆盖的元素,j指针是遍历指针。
只要nums[i]=nums[j],我们就增加j以跳过重复项
当nums[i]!=nums[j]时,把nums[j]的值复制到nums[i+1],递增i
//再次重复相同的过程直到j到达数组的末尾
//当没有重复元素时,资源会浪费,我们可以增加判断,差值>1时,才进行复制
//代码错误!!!!!!!!!!!!!
public int removeDuplicates01(int[] nums) {
int i=0;
//i是慢指针,j是快指针
for(int j=0;j<nums.length;j++) {
if(nums[i]==nums[j]) {
j++;//增加j跳过重复项,j移动的话说明有需要覆盖的
}else {
nums[i+1]=nums[j];
}
}
return nums.length;
}

    //正确代码!!!!没看懂
    public int removeDuplicates02(int[] nums) {
    	if(nums.length==0) return 0;
    	int i=0;//i是慢指针,j是快指针
    	for(int j=1;j<nums.length;j++) {
    		if(nums[j]!=nums[i]) {
    			i++;
    			nums[i]=nums[j];//i是被替代的,不一样的就放在i
    		}
    	}
    	return i+1;
    }

80-Remove Duplicates from Sorted ArrayII

(26与80类型相似,都为在排序数组中允许元素至多重复k次,同类型解题模板在这里)
递增排列数组nums,原地删除重复出现的元素,
使得每个元素最多!出现两次,!返回移除后数组的新长度
我们需要在遍历数组元素的同时删除多余的重复项,
删除多余重复项的同时更新数组的索引,
否则将访问到无效的元素或跳过需要访问的元素

使用两个变量,i遍历数组的指针,count是记录当前数字出现的次数。count最小是1,
我们从索引1开始一次处理一个数组元素。若当前元素与前一个元素相同,即nums[i]==nums[i-1],即count++,若count>2说明遇到了多余的重复元素,删除,由于记录了索引,可以使用del,pop或remove。由于删除了一个元素所以索引-1。若当前元素与前一个不相同,nums[i]!=nums[i-1],说明遇到了新元素,更新count=1。由于我们从原始数组中删除所有的多于项,所以最终在原数组保留了有效元素,返回数组长度。

  public int[] remElement(int[] arr,int index) {
        	for(int i=index+1;i<arr.length;i++) {
        		arr[i-1]=arr[i];//删除index下标的元素
        	}
        	return arr;
        }
        public int removeDuplicates(int[] nums) {
        	int i=1,count=1,length=nums.length;
        	while (i<length) {
        		if(nums[i]==nums[i-1]) {
        			count++;
        			if(count>2) {
        				this.remElement(nums, i);
        				i--;
        				length--;
        				//删除了一个元素,索引也要减1
        				//数组长度也要减少
        			}
        		}else {
        			count=1;
        		}
        		i++;//移动到下一个元素
        	}
        	return length;
        }
        //覆盖多于项

使用两个指针,i是遍历指针,指向遍历的元素,j指向下一个要覆盖的元素的位置
count记录当前数字出现的次数,最小为1
从索引1开始 处理,一次处理一个数组元素
若当前元素与前一个元素相同,即nums[i]==nums[i-1],则count++.
若count>2,则说明遇到了多余的重复项。向前移动i,而j不动指向重复项
若count<=2,则我们将i所指向的元素移动到j位置,并同时增加i和j。
数组遍历完成,返回j

public int removeDuplicates03(int[] nums) {
    	
    	int j=1,count=1;
    	for(int i=1;i<nums.length;i++) {
    		if(nums[i]==nums[i-1]) {
    			count++;
    		}else {
    			count=1;
    		}
    		if(count<=2) {
    			nums[j++]=nums[i];
    		}
    	}
		return j;
    }

986-Interval List Intersections

给定两个由一些闭区间组成的列表,firstList和secondList,
其中firstList[i]=[starti,endi]而secondList[j]=[startj,endj]
每个区间列表都是成对不相交的,并且已经排序。
返回这两个区间列表的交集

两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。
例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。

 public int[][] intervalIntersection(int[][] A,int[][] B){
        	List<int[]> ans=new ArrayList();
        	int i=0,j=0;
        	while(i<A.length&&j<B.length) {
        		int lo=Math.max(A[i][0], B[j][0]);//第一段,左端点最大值
        		int hi=Math.min(A[i][1], B[j][1]);//第二段,右端点最小值
        		if(lo<=hi) {
        			ans.add(new int[] {lo,hi});
        		}
        		if(A[i][1]<B[j][1])
        			i++;//A第一段右端小于B第二段右端,那么就选A的第二段
        		else
        			j++;//A第一段右端大于B第二段右端,那么就选B的第二段
        	}
        	
			return B;
        	
        }

849-Maximize Distance to Closest Person

数组seats表示一排座位,其中seats[i]=1代表有人坐在第i个座位上,
seats[i]=0代表座位i上是空的(下标从0开始)
至少有一个空座位,至少有一个人已经坐在座位上。
tom希望坐在离他最近的人的距离最大化的位置
返回离他最近的人的最大距离

//求两端的距离,求中间段的1/2
        public int maxDistToCloset(int[] seats) {
        	int N=seats.length;
        	int K=0;//当前最长的空位子
        	int ans=0;
        	
        	for(int i=0;i<N;++i) {
        		if(seats[i]==1) {
        			K=0;
        		}else {
        			K++;
        			ans=Math.max(ans,(K+1)/2);//从左到右记录最大值
        		}
        	}
        	for(int i=0;i<N;++i) if(seats[i]==1){
        		ans=Math.max(ans, i);
        		break;//最左端的空位
        	}
        	for(int i=N-1;i>=0;--i)if(seats[i]==1) {
        		ans=Math.max(ans, N-1-i);
        		break;//最右端空位
        	}
        	return ans;
        }
    //令left[i]为座位i到坐在i左边的人的最近距离。
    //同理right[i]为座位i到坐在i右边的人的最近距离。
    //该座位到最近的人的距离为min(left[i],right[i])
    //如果i左边的位置是空的,那么left[i]=left[i-1]+1;否则left[i]=0.
    public int maxDistToClosest(int[] seats) {
    	int N=seats.length;
    	int[] left=new int[N], right=new int[N];
    	Arrays.fill(left, N);//初始化为最大值,因为端点有一边是最大值
    	Arrays.fill(right,N);
    	
    	//计算空座位的最小的最大距离值
    	for(int i=0;i<N;++i) {//从左到右算座位i到坐在i左边的人的最近距离
    		if(seats[i]==1)left[i]=0;
    		else if(i>0)left[i]=left[i-1]+1;
    	}
    	for(int i=N-1;i>=0;--i) {
    		if(seats[i]==1)right[i]=0;
    		else if(i<N-1)right[i]=right[i+1]+1;
    	}
    	int ans=0;
    	for(int i=0;i<N;++i) 
    		if(seats[i]==0) //座位为空计算最大的最小距离
    			ans=Math.max(ans,Math.min(left[i], right[i]));
    	return ans;
    }
    //遍历所有座位,找出每个空座位左边最近的人和右边最近的人,更新当前空位到最近的人的距离。
    //使用prev记录i最左边第一个 有人的位置,future记录i最右边第一个人的位置
    //座位i到最近的人的距离为min(i-prev,future-i).
    //另外有一种特殊情况,如果i左边没人,则设为无限大
    
    public int maxDisToCloset(int[] seats) {
    	int N=seats.length;
    	int prev=-1,future=0;
    	int ans=0;
    	
    	for(int i=0;i<N;++i) {
    		if(seats[i]==1) {
    			prev=i;
    		}else {
    			while(future<N&&seats[future]==0||future<i) 
    				future++;
    			
    			int left=prev==-1?N:i-prev;
    			int right=future==N?N:future-i;
    			ans=Math.max(ans, Math.min(left, right));
    		}
    	}
    	
		return ans;
    }
    
    //855-Exam Room (849题目的升级)
    //在考场里,一排有N个座位,分别编号0,1,2, N-1
    /* 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上。
     * 如果有多个这样的座位,他会坐在编号最小的座位上。(另外,如果考场里没有人,
     * 那么学生就坐在 0 号座位上。)返回 ExamRoom(int N) 类,它有两个公开的函数:
     * 其中,函数 ExamRoom.seat() 会返回一个 int (整型数据),代表学生坐的位置;
     * 函数 ExamRoom.leave(int p) 代表坐在座位 p 上的学生现在离开了考场。
     * 每次调用 ExamRoom.leave(p) 时都保证有学生坐在座位 p 上。*/
    
    //不太懂这道题!!!!!!!!!!!!!!1
    //维护有序的座位编号。使用有序集合(Java中的Treeset)存储目前有学生的座位编号。
    //当我们要调用leave(p)函数时,我们只需要把有序集合中的p移除即可。
    //当我们要调用seat()函数时,我们遍历这个有序集合,对于相邻的两个座位i和j
    //如果在两个座位之间入座最近的距离d为(j-i)/2,选择座位为i+d。
    //还需考虑最左侧0和最右侧N-1的情况
    class ExamRoom{
    	int N;
    	TreeSet<Integer> students;
    	
    	public ExamRoom(int N) {
        	this.N=N;
        	students=new TreeSet();
        }
        public int seat() {//返回一个int,代表学生座位位置
        	int student=0;
        	if(students.size()>0) {
        		//dist是距离最近学生的距离
        		//先考虑最左边的座位
        		int dist=students.first();
        		Integer prev=null;
        		for(Integer s:students) {
        			if(prev!=null) {
        				//对于没对相邻的学生,d是距离附近学生的最短距离,在prev
        				int d=(s-prev)/2;
        				if(d>dist) {
        					dist=d;
        					student=prev+d;
        				}
        			}
        			prev=s;
        		}
        		//再考虑右端学生
        		if(N-1-students.last()>dist)
        			student=N-1;
        	}
        	//把学生加到已排列的treeset里
        	students.add(student);
        	return student;
        }
        public void leave(int p) {//代表学生离开了p位置,调用需保证有学生在p位置上
        	students.remove(p);
        }
    }
    
    
   

public static void main(String[] args) {
	int[] sums= {1,2,2,3,4,5,6,7,8};
	List<List<Integer>> list=fourSum(sums,8);
	System.out.println("123456789");
	System.out.println(list);
}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值