zoukankan      html  css  js  c++  java
  • 二分查找&&二分中位数

    普通二分查找

     1 int bs(int L,int R,int x)
     2 {//在l到r区间上查找x,找不到就返回-1
     3     int l=L,r=R;
     4     while(l<=r){
     5         int m=l+r>>1;
     6         if(a[m]==x){
     7             return m;
     8         }
     9         else if(a[m]>x){
    10             r=m-1;
    11         }
    12         else{
    13             l=m+1;
    14         }
    15     }
    16     return -1;
    17 }

    普通版很简单就不详细总结了

    二分查找中位数

    题意:给定长度为n的两个有序序列,求两个序列合并后的中位数

    直接归并法,找到第(2n+1)/2数时停止并输出答案,复杂度为O(n)

    但题目要求logn,我们就得换个思路了;

    题目给的是两个有序序列,所以两个序列的中位数都可以O(1)求得,那么最终答案的中位数和这两个序列的中位数有什么关系呢?假设第一个序列叫a,中位数为ma,第二个是b,中位数为mb,他们的并集是c,最终答案为ans。

    如果ma==mb,也就是说,ma的两端和mb的两端分别在ans的两端,ans也就是ma+mb>>1,也就是随便一个了

    如果ma<mb,也就是说,a的中位数比b的要小,所以a的左边和b的右边可以不用考虑了,ans一定在ma与mb中间

    如果ma>mb,情况同上,把b视作a,a视作b即可

    那么每一次都可以删掉c的一半,直到达到终止条件(假装知到),复杂度就是O(logn)了!

     1 #include<iostream>
     2 using namespace std;
     3 int a[100010],b[100010];
     4 int n;
     5 int bs(int la,int ra,int lb,int rb){
     6     int ma=la+ra>>1;
     7     int mb=lb+rb>>1;
     8     if(a[ma]==b[mb]){
     9         return a[ma];
    10     }
    11     if(a[ma]<b[mb]){
    12         bs(la,ra,lb,rb);
    13     }
    14     else if(a[ma]>b[mb]){
    15         bs(la,ra,lb,rb);
    16     }
    17 }
    18 int main()
    19 {
    20     cin>>n;
    21     for(int i=1;i<=n;i++)
    22         cin>>a[i];
    23     for(int i=1;i<=n;i++)
    24         cin>>b[i];
    25     cout<<bs(1,n,1,n);
    26 }
    View Code

    至此我们可以写出这样的代码,还缺了点边界情况和终止条件。

    那么极端情况是什么呢,ab序列都只有一个的时候,最终答案自然是小的那个,

    那么跑一遍样例,看看有没有错,发现了死循环:

    当a序列为3 5 ,b序列为4 时,ma=3,mb=4,ma的左边和mb的右边删无可删了,所以这算一种边界情况。

    推广,当总序列只有三个元素时,算一种边界情况,那么最终答案肯定是三个数中间那个数,我的做法:出现两序列不等长的时候,让小的那个序列左指针右移一就行,然后就会回到第一种终止条件!

    代码:

     1 #include<iostream>
     2 using namespace std;
     3 int a[100010],b[100010];
     4 int n;
     5 int bs(int la,int ra,int lb,int rb){
     6     int ma=la+ra>>1;
     7     int mb=lb+rb>>1;
     8 
     9     if(la==ra){
    10         return a[ma]<b[mb]?a[ma]:b[mb];
    11     }
    12     if(a[ma]==b[mb]){
    13         return a[ma];
    14     }
    15     if(a[ma]<b[mb]){
    16         la=ma;
    17         rb=mb;
    18         if(ra-la!=rb-lb)
    19         ra-la > rb-lb ? la++ :lb++;
    20         bs(la,ra,lb,rb);
    21     }
    22     else if(a[ma]>b[mb]){
    23         lb=mb;
    24         ra=ma;
    25         if(ra-la!=rb-lb)
    26         ra-la > rb-lb ? la++ :lb++;
    27         bs(la,ra,lb,rb);
    28     }
    29 }
    30 int main()
    31 {
    32     cin>>n;
    33     for(int i=1;i<=n;i++)
    34         cin>>a[i];
    35     for(int i=1;i<=n;i++)
    36         cin>>b[i];
    37     cout<<bs(1,n,1,n);
    38     return 0;
    39 }
    View Code

     输入的复杂度为O(n),查找的复杂的为O(logn),总复杂度为O(logn)  (手动滑稽)。

        

  • 相关阅读:
    Python安装(小白教程)中文版Pycharm
    二叉树遍历1
    node* p 和 node *p 和 node * p 的区别
    WinForm中的ListBox组件编程
    C# winform listBox中的项上下移动(转)
    C# ListBox 左移、右移、上移、下移
    C#上移,下移TreeView中的树节点顺序
    C#遍历DataSet与DataSet元素实现代码
    C# 手动编写 DataSet,DataTable 及遍历DataSet中的数据
    【.NET】C#中遍历各类数据集合的方法
  • 原文地址:https://www.cnblogs.com/qq2210446939/p/11571934.html
Copyright © 2011-2022 走看看