二分查找实现及其应用

来源:互联网 发布:linux 统计登录次数 编辑:程序博客网 时间:2024/05/01 07:45

1、二分查找实现

迭代:

template<typename T>int BinarySearch(T *array,T low,T high,T value){    while(low<=high)    {        int mid=(low+high)/2;        if(array[mid]==value)            return mid;        else if(array[mid]>value)            high=mid-1;        else            low=mid+1;    }    return -1;}

递归:

template<typename T>int BinarySearch(T *array,int low,int high,T value){    if(low<=high)     {       int mid=(low+high)/2;       if(array[mid]==value)          return mid;       else if(array[mid]>value)       {          return BinarySearch(array,low,mid-1,value);       }       else       {          return BinarySearch(array,mid+1,right,value);       }    }    return -1;}

2、二分查找应用

1、求=target的first位置

int search(int A[], int n, int target)  {      int low = 0, high = n-1pos=-1;      while(low <= high)                       {          // 注意:若使用(low+high)/2求中间位置容易溢出          int mid = low+((high-low)>>1);           if(A[mid] == target)          {              pos=mid;            if(mid > 0 && A[mid-1] == target)                  high = mid-1;                     }          else if(A[mid] < target)              low = mid+1;                       else // A[mid] > target              high = mid-1;                 }      return pos;    }  

2、求=target的last位置

int search(int A[], int n, int target)  {      int low = 0, high = n-1pos=-1;      while(low <= high)                       {          // 注意:若使用(low+high)/2求中间位置容易溢出          int mid = low+((high-low)>>1);           if(A[mid] == target)          {              pos=mid;            if(mid <n-1 && A[mid+1] == target)                  low= mid+1;                     }          else if(A[mid] < target)              low = mid+1;                       else // A[mid] > target              high = mid-1;                 }      return pos;    }  

有了上面这两个方法,求有序数组中数字出现的次数,以及求小于target最大的一个(最小target位置减一),以及大于target最小的一个(最大target位置加一)就迎刃而解了!

3、计算有序数组中数字出现的次数

在给定的一个已经排好序的数组中,找出指定数字出现的次数。例如数组[1,2,3,3,3,4,5]中3出现的次数为3次。

此问题可以在二分法的基础上进行改进。假设数组array为递增的数列,需要查找的数字为num,可以分别查找num在数组array中出现的起始位置和最后一次的位置,通过二者的差计算出数字num在数组array中出现的次数。

template<typename T>int BinarySearch(T *array,T size,T value,bool isLeft){    int low=0;    int high=size-1;    int pos=-1;    while(low<=high)    {        int mid=(low+high)/2;        if(array[mid]==value)        {              pos=mid;            if(isLeft) //查找这个数最左的位置            {               if(mid>0&&array[mid-1]==target)                  high=mid-1;            }            else  //查找这个数最右的位置            {                if(mid<n-1&& array[mid+1]==target)                   low=mid+1;            }                       }        else if(value<array[mid])            high=mid-1;        else            low=mid=1;    }    return pos;}//使用left = FindCntofNum(a,10,4,true);right = FindCntofNum(a,10,4,false);int num=right-left+1;

4、二分查找排序

直接插入排序的思路是对于无序序列的第一个元素,从后至前进行顺序查找 扫描有序序列寻找合适的插入点。改进后的二分插入排序算法使用二分查找在有序序列中查找插入点,将插入排序的比较次数降为O(log2n)。这个思路的实现代码为:

/*二分查找函数,返回插入下标*/template <typename T>int BinarySearch(T *array, int start, int end, T k){    while (start <= end)    {        int middle = (start + end) / 2;        int middleData = array[middle];        if (middleData > k)        {            end = middle - 1;        }        else            start = middle + 1;    }    return start;}//二叉查找插入排序template <typename T>void InsertSort(T *array, int length){    if (array == NULL|| length < 0)        return;    int i, j;    for (i = 1; i < length; i++)    {        if (array[i]<array[i - 1])        {            int temp = array[i];            int insertIndex = BinarySearch(array, 0, i, array[i]);//使用二分查找在有序序列中进行查找,获取插入下标            for (j = i - 1; j>=insertIndex; j--) //移动元素            {                array[j + 1] = array[j];              }                  array[insertIndex] = temp;    //插入元素        }    }}

5、旋转数组中的最小数字

问题描述:把一个有序数组最开始的若干个元素搬到数组的末尾,我们称为数组的旋转,输入一个递增排序数组的一个旋转,输出旋转数组的最小元素。例如数组{4,5,6,1,2,3}为数组 {1,2,3,4,5,6}的一个旋转,最小旋转元素为1.

解决思路:
寻找数组中最小的元素,我们可以遍历数组,时间复杂度为O(n)。但是借助二分查找的思想,我们能够在O(logn)的时间复杂度内找到最小的元素。旋转数组的特点是数组中两个部分都分别是有序的,以{4,5,6,1,2,3}为例,{4,5,6}是非递减的,{1,2,3}也是非递减的:
这里写图片描述
我们可以定义两个索引:start指向第一部分的起始位置;end指向第二部分的最后一个元素,如图所示。由于数组在旋转前整体有序,故array[start]>=array[end],而中间值array[middle]满足:

  • 如果array[middle]大于array[start] ,则array[middle]属于第一部分。此时令start= middle ;
  • 如果array[middle]小于array[start] ,则array[middle]属于第二部分。此时令end = middle ;

end所指元素便是最小元素。
这里写图片描述
这个寻址的过程有一个隐喻的要求:中间这个元素必须能够判断它是属于第一部分还是第二部分。在有些输入下,这个要求不能满足,例如数组:{0,1,1,1,1},它的两个选择数组为:
{1,0,1,1,1}
{1,1,1,0,1}
此时因array[middle] == array[start] == array[end] 而无法判断array[middle]属于哪一部分,我们只能进行顺序查找找出最小元素。

此时无法确定array[middle]是属于第一部分还是第二部分,这种情况下我们需要进行顺序查找。
因此,我们的代码实现为:

//顺序查找函数int Min(int array[], int length){    int result = array[0];    for (int i = 1; i < length; i++)    {        if (array[i] < result)            result = array[i];    }    return result;}int MinInRotation(int array[],int length){    int result = -999;    if (array == nullptr || length < 0)        return result;    int start = 0;    int end = length - 1;    int middle = start;    while (start < end)    {        if (start + 1 == end)        {            result = array[end];            break;        }        middle = (start + end)/2;        //如果遇上特殊情况,则需要进行顺序查找        if (array[middle] == array[start] && array[middle] == array[end])            return Min(array,length); //调用顺序查找函数        //否则;中间元素属于第一部分        if (array[middle]>=array[start])        {            start = middle;        }//中间元素属于第二部分        else if (array[middle] <= array[start])        {            end = middle;        }    }    return result;}

参考资料:

数据结构图文解析之:二分查找及与其相关的几个问题解析
二分法计算有序数组中数字出现的次数
二分查找问题全集OK

0 0
原创粉丝点击