二分法——【Search Insert Position】【Search for a Range】【数字在排序数组中出现的次数】

【Search Insert Position】 

https://leetcode.com/problems/search-insert-position/description/

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

Here are few examples.
[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0

思路:二分查找算法总是先拿数组中间的数字和k作比较,比k大,则在数组前半段找,比k小,则在数组后半段找,如果相等,则直接返回;通过下面的迭代方式,当循环结束时,如果没有找到目标元素,那么l一定停在恰好比目标大的index上,r一定停在恰好比目标小的index上,也就是元素应该插入的位置

 * @function 给定一个有序数组和目标元素,如果目标元素在有序数组中,则返回其位置;如果不在,则返回插入位置
 */
public class Search_Insert_Position {

	public static void main(String[] args) {
		int[] nums={1,3};
		System.out.println(searchInsert(nums,0));
	}
	
	public static int searchInsert(int[] nums, int target) {
        if(nums==null||nums.length==0)
        	return -1;
        int l=0;
        int r=nums.length-1;
        while(l<=r){               //必须要这样等号,不然测试用例[1],2输出结果是错误的
        	int mid=(l+r)/2;
        	if(nums[mid]==target)
        		return mid;
        	if(nums[mid]<target)
        		l=mid+1;
        	else
        		r=mid-1;
        }
        return l;
    }	
}

变式一:【Search for a Range】

https://leetcode.com/problems/search-for-a-range/description/

Given an array of integers sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

思路:先拿数组中间的数字和k作比较,比k大,则在数组前半段找,比k小,则在数组后半段找。但是如果相等的话,就不能直接返回了,因为我们这里要找的是范围,那么我们考虑找到数组中的第一个和最后一个k,那么范围也就知道了。我们先判断这个数字是不是第一个k。如果位于中间数字的前面一个数字不是k,此时中间的数字刚好就是第一个k。如果位于中间数字的前面一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮我们仍然需要在数组的前半段找。同理,也可以在排序数组中找到最后一个k。即如果中间数字等于k的话,我们看它的下一个数是不是也是k,如果是的话,那么下一轮就在数组的后半段中去查找最后一个k。(代码中的target就是k)


 * @function 返回目标函数在有序数组中出现的起始位置和结束位置
 */
public class Search_for_a_Range {

	public static void main(String[] args) {
		
		int[] array={1,2,3,3,3,3,4,5};
		int k=3;
		int[] res=searchRange_first(array,k);
		System.out.print(res[0]+" "+res[1]);
		
	}
	
	public static int[] searchRange_first(int[] nums, int target) {
		int[] res=new int[2];
		res[0]=-1;
		res[1]=-1;
		if(nums==null||nums.length==0)
			return res;
		res[0]=GetFirstK(nums , target, 0, nums.length-1);
		res[1]=GetLastK(nums , target, 0, nums.length-1);
		return res;
		
	}
	
	public static int GetFirstK(int [] array , int k, int start, int end) {
		
		while(start<=end){
			int mid=(start+end)/2;
			if(array[mid]<k){
				start=mid+1;
			}
			else if(array[mid]>k){
				end=mid-1;
			}
			else{
				if(mid-1>=0 && array[mid-1]==k){ //中间位置的前一个数也为k的话,则仍在前半部分找第一个k
					end=mid-1;
				}
				else{
					return mid;
				}
			}
		}
		return -1;
	}
	
	public static int GetLastK(int [] array , int k, int start, int end) {
		
		while(start<=end){
			int mid=(start+end)/2;
			if(array[mid]<k){
				start=mid+1;
			}
			else if(array[mid]>k){
				end=mid-1;
			}
			else{
				if(mid+1<array.length && array[mid+1]==k){ //中间位置的后一个数也为k的话,则仍在后半部分找最后一个k
					start=mid+1;
				}
				else{
					return mid;
				}
			}
		}
		return -1;
	}
}

变式二:剑指offer 面试题38【数字在排序数组中出现的次数】 

思路:就是变式一的思路,变式一求的是范围,这个求的是个数,范围知道了,个数也就知道了

 * @function 数字在排序数组中出现的次数:循环写法,下一个版本是递归写法
 */
public class GetNumberOfK {

	public static void main(String[] args) {
		
		int[] array={1,2,3,3,3,3,4,5};
		int k=3;
		System.out.println(getNumberOfK(array,k));
	}
	
	public static int getNumberOfK(int [] array , int k) {
		if(array==null||array.length==0)
			return 0;
		int firstK=GetFirstK(array , k, 0, array.length-1);
		int lastK=GetLastK(array , k, 0, array.length-1);
		if(firstK !=-1 && lastK !=-1){  //这里必须限定,不然该数不存在的情况下也会输出1
			return lastK-firstK+1;
		}
		return 0;
	}
	
	public static int GetFirstK(int [] array , int k, int start, int end) {
		
		while(start<=end){
			int mid=(start+end)/2;
			if(array[mid]<k){
				start=mid+1;
			}
			else if(array[mid]>k){
				end=mid-1;
			}
			else{
				if(mid-1>=0 && array[mid-1]==k){ //中间位置的前一个数也为k的话,则仍在前半部分找第一个k
					end=mid-1;
				}
				else{
					return mid;
				}
			}
		}
		return -1;
	}
	
	public static int GetLastK(int [] array , int k, int start, int end) {
		
		while(start<=end){
			int mid=(start+end)/2;
			if(array[mid]<k){
				start=mid+1;
			}
			else if(array[mid]>k){
				end=mid-1;
			}
			else{
				if(mid+1<array.length && array[mid+1]==k){ //中间位置的后一个数也为k的话,则仍在后半部分找最后一个k
					start=mid+1;
				}
				else{
					return mid;
				}
			}
		}
		return -1;
	}
}

 * @function 数字在排序数组中出现的次数:递归版本
 */
public class GetNumberOfK_2 {
	public int GetNumberOfK(int [] array , int k) {
	       int number=0;
			if(array.length==0||array==null)
				return 0;
			int first=GetFirstK(array,k,0,array.length-1);
			int last=GetLastK(array,k,0,array.length-1);
			if(first>-1&&last>-1){      //这里必须限定,不然该数不存在的情况下也会输出1
				number=last-first+1;	
			}
			return number;
		}
		public int GetFirstK(int[] array,int k,int start,int end){
			if(start>end)
				return -1;
			int mid=(start+end)/2;
			if(k<array[mid]){
				end=mid-1;
			}
			else if(k>array[mid]){
				start=mid+1;
			}
			else if(k==array[mid]){
				if((mid-1>=0 && k!=array[mid-1] ) || mid==0){  //这里需要对边界进行处理,不然会抛出空指针异常
					return mid;		
				}
				else 
					end=mid-1;
			}
			return GetFirstK(array,k,start,end);
		}
	        
	    public int GetLastK(int[] array,int k,int start,int end){
	    	if(start>end)
				return -1;
			int mid=(start+end)/2;
			if(k<array[mid]){
				end=mid-1;
				mid=(start+end)/2;
			}
			else if(k>array[mid]){
				start=mid+1;
				mid=(start+end)/2;
			}
			else if(k==array[mid]){
				if((mid<end && k!=array[mid+1] )||mid==end){  //这里需要对边界进行处理,不然会抛出空指针异常
					return mid;	
				}
				else
					start=mid+1;
			}
			return GetLastK(array,k,start,end);
		}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值