二分法总结

二分查找本身并不难, 难的是二分查找的扩展问题

1. 假如 target 不在数组中, 输入其应该在的位置

2. 找到大于 target 的最小值的位置或小于 target 的最大值的位置

3. target 在数组中连续出现多次, 分别找到 target 的最左位置和最右位置

 

int _bsearch(int A[], int m, int n, int target) {

  if(m > n)                                              ----------------------------- 变量 x

    return m;                                       ----------------------------- 变量 y

  int mid = (m+n) >> 1;                   

  if(A[mid] == target) {        

   code here                                        ----------------------------- 变量 z

  }

}

 

在二分查找的框架内, 大致有 3 个可变的位置, 分别是 x, y, z 处

具体来说

1. x 可以填写 m >=n 或 m > n

2. y 可以填写 return m, return n, return min(n,m)...

3. z 可以填写 b_search(A, mid+1, n, target) or b_search(A, m, mid-1, target)

 

一般来讲, 

x 处都要填 m > n, 因为当 m==n 时, 代码还需要向下走进行验证

y 处是最难把握的了, 需要具体情况具体分析

z 的话需要考虑 当 a[mid] == target 时, 我们有怎样的需求, 再做选择

 

例题

1. Leetcode Search in Rotated Sorted Array

class Solution {
public:
    int search(int A[], int n, int target) {
    	if(n < 1)
    		return -1;

		int pivot = A[0];        
		int pivot_pos = find_pos(A, 0, n-1, pivot);

		//cout << "pivot at " << pivot_pos << endl;

		int got;
		if(pivot == target) {
			return 0;
		}else if(pivot < target) {
			got = b_search(A, 0, pivot_pos-1, target);
			if(got < 0 || got >= pivot_pos)
				return -1;
			return got;
		}else{
			got = b_search(A, pivot_pos, n-1, target);
			if(got < pivot_pos || got >= n)
				return -1;
			return got;
		}


    }

    int find_pos(int A[], int m, int n, int pivot) {
    	if(m > n)
    		return m;

    	int mid = (m+n)>>1;
    	//cout << "mid " << mid << endl;
    	if(A[mid] >= pivot) {
    		return find_pos(A, mid+1, n, pivot);
    	}else {
    		return find_pos(A, m, mid-1, pivot);
    	}
    }

    int b_search(int A[], int m, int n, int pivot) {
    	if(m > n) {
    		return -1;
    	}

    	int mid = (m+n)>>1;
    	if(A[mid] == pivot) {
    		return mid;
    	}else if(A[mid] < pivot) {
    		return b_search(A, mid+1, n, pivot);
    	}else{
    		return b_search(A, m, mid-1, pivot);
    	}

    }
};

  

 

2. Leetcode  Search Insert Position

y 处. 当 m > n 时, 说明有两种情况发生了, 第一, 上一步是 b_search(A, mid+1, n, target) 这说明, 待求位置在 mid "右边"(A[mid] < target), 所以返回 m (mid+1);  第二种, 上一步是 b_search(A, m, mid-1, target), 说明 (A[mid] > target) 且这是最后一步递归(以发生越界 m>n), 所以我们要插入的位置在mid-1右边, 仍然填 m

 

#include <iostream>
using namespace std;

class Solution {
public:
    int searchInsert(int A[], int n, int target) {
        int pos = b_search(A, 0, n-1, target);
        return pos;
    }

    int b_search(int A[], int m, int n, int target) {
    	if(m > n)
    		return m;

    	int mid = (m+n)>>1;
    	//cout << mid << endl;
    	if(A[mid] == target) {
    		return mid;
    	}else if(A[mid] > target) {
    		return b_search(A, m, mid-1, target);
    	}else{
    		return b_search(A, mid+1, n, target);
    	}
    }
};

  

3. Leetcode Search for a Range

x 处. m==n 时, 仍需要判断, 所以向下走

z 处. 求 left_position 时, 当 A[mid] == target 时, 我们希望看看 mid 左边是否还有更合适的解, 所以应该 b_search(A, m, mid-1, target) 强制向左, 求 right_position 同理

y 处. 求 left_position 时, 我们总是强制向左, 所以最终会越界, left_position 应该是越界的右端, 返回 m

class Solution {
public:
    vector<int> searchRange(int A[], int n, int target) {
        int left = left_pos(A, 0, n-1, target);
        int right = right_pos(A, 0, n-1, target);
        
        if(left < 0 || left >= n || A[left] != target) {
        	left = right = -1;
        }

        vector<int> res;
        res.push_back(left);
        res.push_back(right);

        return res;
    }
    
    int left_pos(int A[], int m, int n, int target) {
    	if(m > n)
    		return m;

    	int mid = (m+n)>>1;

    	// when a[mid] == target, prefer left one
    	if(A[mid] >= target) {
    		return left_pos(A, m, mid-1, target);
    	}else{
    		return left_pos(A, mid+1, n, target);
    	}
    }

    int right_pos(int A[], int m, int n, int target) {
    	if(m > n)
    		return n;

    	int mid = (m+n)>>1;
    	//cout  << mid << endl;
    	// when a[mid] == target, prefer right one
    	if(A[mid] > target) {
    		return right_pos(A, m, mid-1, target);
    	}else{
    		return right_pos(A, mid+1, n, target);
    	}
    }
};

  

posted @ 2013-12-11 22:11  SangS  阅读(412)  评论(0编辑  收藏  举报