二叉搜索

本文探讨了二分搜索的基本实现及其多种变化形式,包括如何减少比较次数、寻找给定值的下界、查找旋转排序数组中的最小值及在旋转排序数组中进行搜索等问题。

knuth 在TAOCP有过这么一句话: 虽然第一篇二分搜索在1946年就发表了, 但是第一个没有错误的二分搜索程序直到1962年才出现。 所以可以说是是最简单的难题了。

问题: 给定排好序的N个distinct elements 的array, 使用最少的比较次数确定某一个值是否存在。

分析: 线性查找的时间复杂度是O(n), 但是考虑到数组是sorted, 所以我们一定要利用好这个信息。 使用二分搜索更好, 理论上, 最坏情况下, 比较次数需要log(N) + 1。

#include <iostream>
using namespace std;
int Binary_Search(int A[], int l, int r, int x) {
    int m = 0;
    while(l <= r) {
        m = l + (r - l)/2;
        if(A[m] == x)
            return m;
        if(A[m] < x)
            l = m + 1;
        else
            r = m - 1;
    }
    return -1;
}

int main() {
    int A[6] = {1, 2, 3, 4, 7, 8};
    int p = 0;
    p = Binary_Search(A, 0, 5, 8);
    cout << p << endl;

    p = Binary_Search(A, 0, 5, 2);
    cout << p << endl;

    p = Binary_Search(A, 0, 5, 6);
    cout << p << endl;
}


运行结果:

 

不难看出, 上面的程序, while语句在每一次迭代的时候, 如果第一个比较不成立, 即没找到, 那么就会还会再次执行一次比较。 如此, 一次不成功的迭代比较了两次, 能不能做改进, 从而减少比较的次数。 答案是可以的。 改进如下:

Binary_Search2(int A[], int l, int r, int x) {
    int m = 0;
    while(r - l > 1) { // 退出的时候, 表明r - l == 1
        m = l + (r - l)/2;
        // 即A[l] <= x
        if(A[m] <= x) {
            l = m;
        }
        // 即A[r] > x
        else {
            r = m;
        }
        // 退出循环后, 表明r - l == 1, 只需要判断A[l]和X 是否相等即可
        if(A[l] == x) {
            return l;
        }
        else {
            return -1;
        }
    }
}


问题二:给定一个具有N个distinct integers的array, 找出给定的x的floor。 例如A = {-1, 2, 3, 5, 6, 8, 9, 10},给定x = 7, 应该返回的值是6。

程序如下:

int Floor(int A[], int l, int r, int x) {
    int m = 0;
    while(r - l > 1) {
        m = l + (r - l)/2;
        if(A[m] <= x)
            l = m;
        else
            r = m;
    }
    return A[l];
}
// 调用, 包含错误检测
int Floor(int A[], int n, int x) {
    if(x < A[0])
        return -1;
    return Floor(A, 0, n, x);
}


问题: 给定一个sorted array of distinct elememts, 这个sorted array rotated to an unknow position, 现在我们的任务就是找到这个rotated sorted array的最小值(找到最小值的位置, 我们当然就知道其rotated的情况)。

 

 

程序:

int RotatedSortedArrayMin(int A[], int l, int r) {
    
    int m = 0;

    // 前提条件: A[l] > A[r]
    if( A[l] <= A[r] )
        return l;

    while( l <= r ) // 退出循环条件时l > r
    {
        // 终止条件, l最终落在r上,r指向的是最小值的位置索引
        if( l == r )
            return l;

        m = l + (r-l)/2; // 'm' can fall in first pulse,
                        // second pulse or exactly in the middle

        if( A[m] < A[r] )
            // 此时最小值不可能在区间
            // (m < i <= r)上, we can exclude A[m+1 ... r]
            r = m;
        else // A[m] >= r
            // min must be in the range (m < i <= r),
            // we must search in A[m+1 ... r]
            l = m+1;
    }

    return -1;
}

int RotatedSortedArrayMin(int A[], int n)
{
    return RotatedSortedArrayMin(A, 0, n-1);
}


问题: Searhing an element in a rotated sorted array:

int rotated_binary_search(int A[], int N, int key) {
  int L = 0;
  int R = N - 1;
 
  while (L <= R) {
    // 使用下面的式子而不是 M=(L+R)/2的好处的避免溢出
    int M = L + ((R - L) / 2);
    // 返回条件
    if (A[M] == key) return M;
 
    // 下半部分A[L..M]是排好序的
    if (A[L] <= A[M]) {
    // 确定key所在的区间: A[L] <= key <= A[M]
      if (A[L] <= key && key < A[M])
        R = M - 1;
      else
        L = M + 1;
    }
    // 上半部分(条件A[L] > A[M]) A[M...R]是排好序的
    else {
      if (A[M] < key && key <= A[R])
        L = M + 1;
      else 
        R = M - 1;
    }
  }
  return -1;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值