你真的会写二分检索吗?

转自
https://mp.weixin.qq.com/s?__biz=MzUxOTc4NjEyMw==&mid=2247484132&idx=1&sn=d24991da2d56b6e483fa78925ec15479&chksm=f9f51900ce829016addd383a820fd4659e5236f306169d11ed611b16587527d87ad7cf0ce622&mpshare=1&scene=1&srcid=0424hAKnlEuautqzTqrZVVXT&key=0373dd6dcb08f5afb2ee689db0c8c8b247e8be6f6bc46606737e20ac6de025b1d086afbad4b3e30a9d945403ebb26049d8baef1fa6ef9eea62e7b1581834349bd87f5fef74847dec981ad05a8d22d1d9&ascene=1&uin=MjUwMTE2MjgzOQ%3D%3D&devicetype=Windows+10&version=62060739&lang=zh_CN&pass_ticket=HcHE5tMqEREmOhjRoj2wFO6AGoU01wykPrg8E%2F9Ta5PrHwus4KINn3B4efHZtJf8

二分法真的不那么简单,尤其是二分法的各个变种。最最简单的二分法,就是从一个排好序的数组之查找一个key值。如下面的程序:

 int search(int *arr, int n, int key)
 {
     int left = 0, right = n-1;
     while(left<=right) {
         int mid = left + ((right - left) << 1);
         if (arr[mid] == key) return mid;
         else if(arr[mid] > key) right = mid - 1;
         else left = mid + 1;
     }
     return -1;
 }

这个程序,相信只要是一个合格的程序员应该都会写。稍微注意一点, 每次移动left和right指针的时候,需要在mid的基础上+1或者-1, 防止出现死循环, 程序也就能够正确的运行。

但如果条件稍微变化一下, 你还会写吗?如,数组之中的数据可能可以重复,要求返回匹配的数据的最小(或最大)的下标更近一步, 需要找出数组中第一个大于key的元素(也就是最小的大于key的元素的)下标,等等。这些,虽然只有一点点的变化,实现的时候确实要更加的细心。下面列出了这些二分检索变种的实现。

1、找出第一个与key相等的元素

 int searchFirstEqual(int *arr, int n, int key)
 {
     int left = 0, right = n-1;
     while(left<=right) {
         int mid = (left+right)/2;
         if(arr[mid] >= key) right = mid - 1;
         else if(arr[mid] < key) left = mid + 1;
     }
     if( left < n && arr[left] == key) return left;
     return -1;
 }

2、找出最后一个与key相等的元素

int searchLastEqual(int *arr, int n, int key)
{
     int left = 0, right = n-1;
     while(left<=right) {
         int mid = (left+right)/2;
         if(arr[mid] > key) right = mid - 1;
         else if(arr[mid] <= key) left = mid + 1;
     }
     if( right>=0 && arr[right] == key) return right;
     return -1;
 }

3、查找第一个等于或者大于Key的元素

 int searchFirstEqualOrLarger(int *arr, int n, int key)
 {
     int left=0, right=n-1;
     while(left<=right) {
         int mid = (left+right)/2;
         if(arr[mid] >= key) right = mid-1;
         else if (arr[mid] < key) left = mid+1;
     }
     return left;
}

4、查找第一个大于key的元素

int searchFirstLarger(int *arr, int n, int key)
{
     int left=0, right=n-1;
     while(left<=right) {
         int mid = (left+right)/2;
         if(arr[mid] > key) right = mid-1;
         else if (arr[mid] <= key) left = mid+1;
     }
     return left;
}

5、查找最后一个等于或者小于key的元素

 int searchLastEqualOrSmaller(int *arr, int n, int key)
 {
     int left=0, right=n-1;
     while(left<=right) {
         int m = (left+right)/2;
         if(arr[m] > key) right = m-1;
        else if (arr[m] <= key) left = m+1;
    }
    return right;
 }

6、查找最后一个小于key的元素

 int searchLastSmaller(int *arr, int n, int key)
 {
     int left=0, right=n-1;
     while(left<=right) {
         int mid = (left+right)/2;
         if(arr[mid] >= key) right = mid-1;
         else if (arr[mid] < key) left = mid+1;
     }
    return right;
}

下面是一个测试的例子:

 int main(void)
 {
     int arr[17] = {1,
                   2, 2, 5, 5, 5,
                   5, 5, 5, 5, 5,
                   5, 5, 6, 6, 7};
     printf("First Equal           : %2d \n", searchFirstEqual(arr, 16, 5));
     printf("Last Equal            : %2d \n", searchLastEqual(arr, 16, 5));
     printf("First Equal or Larger : %2d \n", searchFirstEqualOrLarger(arr, 16, 5));
     printf("First Larger          : %2d \n", searchFirstLarger(arr, 16, 5));
     printf("Last Equal or Smaller : %2d \n", searchLastEqualOrSmaller(arr, 16, 5));
     printf("Last Smaller          : %2d \n", searchLastSmaller(arr, 16, 5));
     system("pause");
    return 0;
}

最后输出结果是:

1. First Equal           :  3
2. Last Equal            : 12
3. First Equal or Larger :  3
4. First Larger          : 13
5. Last Equal or Smaller : 12
6. Last Smaller          :  2

很多的时候,应用二分检索的地方都不是直接的查找和key相等的元素,而是使用上面提到的二分检索的各个变种,熟练掌握了这些变种,当你再次使用二分检索的检索的时候就会感觉的更加的得心应手了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二分搜索树(Binary Search Tree)是一种基于二分查找的数据结构,它的左子树中的所有节点都小于根节点,右子树中的所有节点都大于根节点。最优二分搜索树(Optimal Binary Search Tree)也称为哈夫曼树(Huffman Tree),是一种带权路径最短的二叉树,它的带权路径长度最小,也就是说,它的各个叶子节点的深度加上其权值的乘积之和最小。 以下是用Java代码实现最优二分搜索树的示例: ```java public class OptimalBST { private static final int INF = Integer.MAX_VALUE; private static int n; private static int[] p; private static int[] q; private static int[][] w; private static int[][] e; private static int[][] root; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入节点数:"); n = scanner.nextInt(); p = new int[n + 1]; q = new int[n + 1]; System.out.println("请输入节点的概率:"); for (int i = 1; i <= n; i++) { p[i] = scanner.nextInt(); } System.out.println("请输入虚拟节点的概率:"); for (int i = 0; i <= n; i++) { q[i] = scanner.nextInt(); } w = new int[n + 2][n + 2]; e = new int[n + 2][n + 2]; root = new int[n + 1][n + 1]; optimalBST(); System.out.println("最优二叉搜索树的带权路径长度为:" + e[1][n]); } private static void optimalBST() { // 初始化 w、e、root 数组 for (int i = 1; i <= n + 1; i++) { w[i][i - 1] = q[i - 1]; e[i][i - 1] = q[i - 1]; } for (int len = 1; len <= n; len++) { for (int i = 1; i <= n - len + 1; i++) { int j = i + len - 1; e[i][j] = INF; w[i][j] = w[i][j - 1] + p[j] + q[j]; for (int k = i; k <= j; k++) { int t = e[i][k - 1] + e[k + 1][j] + w[i][j]; if (t < e[i][j]) { e[i][j] = t; root[i][j] = k; } } } } } } ``` 该代码中,使用了动态规划的思想,通过计算每个子树的带权路径长度,逐步求得最优二叉搜索树的带权路径长度。在计算过程中,使用了一个 root 数组,记录了每个子树的根节点。最后,根据 root 数组和节点的概率,可以构建出最优二叉搜索树。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值