二分查找的巧妙运用(C++)

本文详细探讨了二分查找算法在C++编程中的应用,通过实例解析其巧妙之处,帮助读者深入理解如何高效地利用二分查找解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、二分查找
1、查找无序数组中的任意一个局部最小值
局部最小值: arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0]<arr[1],那么arr[0]是局部最小;如果arr[N-1]<arr[N-2],那么arr[N-1]是局部最小;如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那么arr[i]是局部最小。
解法:二分查找。先查看左右两边是不是局部最小。都不是则局部最小一定在中间,于是查看mid处是不是,若不是则局部最小要么在其左边要么在其右边,判断出在左边还是在右边,继续二分查找即可。
class  Solution {
public :
     int getLessIndex(vector< int > arr) {
         int length = arr.size();
         if (length ==  0 )
             return - 1 ;
         if (length ==  1 )
             return 0 ;
         if (arr[ 0 ] < arr[ 1 ])
             return 0 ;
         if (arr[length- 1 ] < arr[length- 2 ])
             return length- 1 ;
         int start =  0 ,end = length- 1 ;
         int mid = (start+end)/ 2 ;
         while ( 1 ) {
             if (arr[mid] < arr[mid+ 1 ] && arr[mid] < arr[mid- 1 ])
                 return mid;
             else if (arr[mid] > arr[mid- 1 ]) {
                 end = mid;
                 mid = (start+end)/ 2 ;
             }
             else {
                 start = mid;
                 mid = (start+end)/ 2 ;
             }            
         }
     }
};

2、对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置
解法:二分查找。要注意当arr[mid]大于或等于num时都要转向左半部查找。查找停止条件很重要。
class LeftMostAppearance {
public :
     int findPos(vector< int > arr,  int n,  int num) {
         int res = - 1 ;
         int start =  0 ,end = n- 1 ;
         int mid = (start+end)/ 2 ;
         
         while (start <= end) {
             if (arr[mid] < num) {
                 start = mid+ 1 ;               
             }
             else if (arr[mid] == num) {
                 res = mid;
                 end = mid- 1 ;
             }
             else {
                 end = mid- 1 ;
             }
             mid = (start+end)/ 2 ;
         }
         return res;
     }
};

3、有序循环数组arr,返回arr中的最小值
有序循环数组:1,2,3,4,5      2,3,4,5,1      3,4,5,1,2     4,5,1,2,3      5,1,2,3,4
解法:①判断最左和最右的关系。确定数组是有序的还是循环过的。
           ②判断最左和中间的关系。确定最小值位于左半部还是右半部。
           ③最左大于中间,在左半部,end = mid;最左小于或等于中间,在右半部,但是start的更新不同,小于时        start=mid,等于时start=mid+1.

class  MinValue {
public :
     int  getMin(vector< int > arr,  int  n) {
         if  (n ==  0 ) {
             return  - 1 ;
        }

         int  start =  0 ,end = n- 1 ;
         int  mid = start + (end-start)/ 2 ;

         if  (arr[start] < arr[end]) {
             return  arr[start];
        }        
         else if  (arr[start] > arr[end]) {
                 while  (start != end) {
                     if  (arr[start] > arr[mid]) {
                        end = mid;
                }
                 else if (arr[start] < arr[mid]){
                    start = mid;
                }
                 else  {
                    start = mid+1;
                }
                mid = start + (end-start)/ 2 ;
            }
                 return  arr[start];            
        }
         else  {
             int  min = arr[start];           
             for  ( int  i =  0 ;i < n;i++) {
                 if  (arr[i] < min)
                    min = arr[i];
            }
             return  min;
        }        
    }
};

4、返回最左原位。即对于有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置
解法:直接判断mid是否满足,arr[mid] >= mid都说明只有左半部有可能出现满足条件的元素,arr[mid] < mid说明只有右半部有可能。
class Find {
public :
     int findPos(vector< int > arr,  int n) {
         int res = - 1 ;
         if (n ==  0 )
             return res;
         int start =  0 ,end = n- 1 ;
         int mid = -1 ;
         if (arr[start] == start)
             return start;
         while (start <= end) {
            mid = start + (end-start)/2
             if (arr[mid] == mid) {
                 res = mid;
                 end = mid-1;
             }
             else if (arr[mid] > mid) 
                 end = mid-1;
             else 
                 start = mid +  1 ;
         }
         return res;
     }
};
5、统计完全二叉树的结点总数
要求:时间复杂度低于O(N)。
解法:二分查找。
①先遍历到二叉树左子树最左边的结点和右子树最左边的结点,统计左右子树的深度。
②若左右子树深度相同,说明左子树是满二叉树,若左子树深度大于右子树深度,说明右子树是满二叉树。
③递归地进行上述过程。
class CountNodes {
public :
     int count(TreeNode* root) {
         if (!root)
             return 0 ;
         int leftDepth =  0 ,rightDepth =  0 ;
         TreeNode *leftL = root,*rightL = root;
         while (leftL || rightL) {           
             if (leftL) {
                 leftDepth++;
                 leftL = leftL->left;
             }              
             if (rightL) {
                 rightDepth++;
                 if (rightL == root)
                     rightL = rightL->right;
                 else
                     rightL = rightL->left;
             }              
         }
         if (leftDepth == rightDepth) {
             return count(root->right) + pow( 2 ,leftDepth- 1 );
         }
         else
             return count(root->left) + pow( 2 ,rightDepth- 1 );
     }
};

6、 快速幂算法
要求:求 整数k的N次方的过程请实现时间复杂度为O(logN)的方法。
解法: N的二进制表示完美地划分了求幂的过程 ,二进制有多少位基数base就计算多少次,每一次求base都等于上一次的base的平方,将二进制中为1的位对应的基数base乘起来就是最后的结果。
class QuickPower {
public :
     int  getPower( int  k,  int  N) {
         long  res = 1 ;
         long   base=k;
         while  (N){
             if  (N& 1 )
                 res=res*base% 1000000007 ;
             base=base*base% 1000000007 ;
             N>>= 1 ;
         }
         return  res;
     }
};

7、矩阵的快速幂
应用:计算斐波那契数列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值