【二分查找】自写二分函数的总结

简介: 【二分查找】自写二分函数的总结

自写二分函数 的封装

我暂时只发现两种:

一,在左闭右开的区间寻找最后一个符合条件的元素,我封装成FindEnd函数。

二,在左开右闭的区间寻找第一个符合条件的元素,我封装成FindFirst函数。

namespace NBinarySearch
{
  template<class INDEX_TYPE,class _Pr>
  INDEX_TYPE FindFrist(INDEX_TYPE left, INDEX_TYPE rightInclue, _Pr pr)
  {
    while (rightInclue - left > 1)
    {
      const auto mid = left + (rightInclue - left) / 2;
      if (pr(mid))
      {
        rightInclue = mid;
      }
      else
      {
        left = mid;
      }
    }
    return rightInclue;
  }
  template<class INDEX_TYPE, class _Pr>
  INDEX_TYPE FindEnd(INDEX_TYPE leftInclude, INDEX_TYPE right, _Pr pr)
  {
    while (right - leftInclude > 1)
    {
      const auto mid = leftInclude + (right - leftInclude) / 2;
      if (pr(mid))
      {
        leftInclude = mid;
      }
      else
      {
        right = mid;
      }
    }
    return leftInclude;
  }
}

左闭右开 左开右闭的例子

LeetCode33: C++算法:二分查找旋转数组

class Solution {
public:
  int search(vector<int>& nums, int target) {
    int rBegin = NBinarySearch::FindFrist(-1, (int)nums.size() - 1, [&](const int mid) {return nums[mid] <= nums.back(); });
    if (target <= nums.back())
    {
      return Find(nums, rBegin, nums.size(), target);
    }
    return Find(nums, 0, rBegin, target);
  }
  int Find(const vector<int>& nums, int left, int r, int target)
  {
    int iRet = NBinarySearch::FindEnd(left,r, [&](const int mid) {return nums[mid] <= target; });
    return (target == nums[iRet]) ? iRet : -1;
  }
};
int main()
{
  vector<int> vNums = { 1,2,3,4,6 };
  auto res = Solution().search(vNums, 4);
  std::cout << "index:" << res;
  if (-1 != res)
  {
    std::cout << " value:" << vNums[res];
  }
}

左闭右开的例子

Leetcode2790:C++二分查找算法的应用:长度递增组的最大数目

namespace NBinarySearch
{
  template<class INDEX_TYPE,class _Pr>
  INDEX_TYPE FindFrist(INDEX_TYPE left, INDEX_TYPE rightInclue, _Pr pr)
  {
    while (rightInclue - left > 1)
    {
      const auto mid = left + (rightInclue - left) / 2;
      if (pr(mid))
      {
        rightInclue = mid;
      }
      else
      {
        left = mid;
      }
    }
    return rightInclue;
  }
  template<class INDEX_TYPE, class _Pr>
  INDEX_TYPE FindEnd(INDEX_TYPE leftInclude, INDEX_TYPE right, _Pr pr)
  {
    while (right - leftInclude > 1)
    {
      const auto mid = leftInclude + (right - leftInclude) / 2;
      if (pr(mid))
      {
        leftInclude = mid;
      }
      else
      {
        right = mid;
      }
    }
    return leftInclude;
  }
}
class Solution {
public:
  int maxIncreasingGroups(vector<int>& usageLimits) {
    m_c = usageLimits.size();
    m_v = usageLimits;
    sort(m_v.begin(), m_v.end());
    auto Can = [&](int len)
    {
      int i = m_c - 1;
      long long llNeed = 0;
      for (; len > 0; len--, i--)
      {
        llNeed -= (m_v[i] - len);
        if (m_v[i] >= len)
        {
          llNeed = max(0LL, llNeed);
        }
      }
      for (; i >= 0; i--)
      {
        llNeed -= m_v[i];
      }
      return llNeed <= 0;
    };
    return NBinarySearch::FindEnd(1, m_c + 1, Can);
  }
  int m_c;
  vector<int> m_v;
};
template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  Solution slu;
  vector<int> usageLimits;
  int res = 0;
  usageLimits = { 2,2,2 };
  res = slu.maxIncreasingGroups(usageLimits);
  Assert(res, 3);
  usageLimits = { 1,2,5 };
  res = slu.maxIncreasingGroups(usageLimits);
  Assert(res, 3);
  usageLimits = { 2,1,2 };
  res = slu.maxIncreasingGroups(usageLimits);
  Assert(res, 2);
  usageLimits = { 1,1 };
  res = slu.maxIncreasingGroups(usageLimits);
  Assert(res, 1);
  //CConsole::Out(res);
}

左闭右开的应用

C++二分查找算法的应用:最小好进制

Is

计算是否存在m位 k进制的1为目标数。m位iRet 进制1大于等于目标数,可能有多个符合的iRet,取最小值。非最小值一定不是。

namespace NBinarySearch
{
  template<class INDEX_TYPE,class _Pr>
  INDEX_TYPE FindFrist(INDEX_TYPE left, INDEX_TYPE rightInclue, _Pr pr)
  {
    while (rightInclue - left > 1)
    {
      const auto mid = left + (rightInclue - left) / 2;
      if (pr(mid))
      {
        rightInclue = mid;
      }
      else
      {
        left = mid;
      }
    }
    return rightInclue;
  }
  template<class INDEX_TYPE, class _Pr>
  INDEX_TYPE FindEnd(INDEX_TYPE leftInclude, INDEX_TYPE right, _Pr pr)
  {
    while (right - leftInclude > 1)
    {
      const auto mid = leftInclude + (right - leftInclude) / 2;
      if (pr(mid))
      {
        leftInclude = mid;
      }
      else
      {
        right = mid;
      }
    }
    return leftInclude;
  }
}
class Solution {
public:
  string smallestGoodBase(string n) {
    long long llN = 0;
    for (const auto& ch : n)
    {
      llN = (llN * 10 + ch - '0');
    }
    for (int i = 70; i > 2; i--)
    {
      long long llRet = Is(i, llN);
      if (llRet > 0)
      {
        return std::to_string(llRet);
      }
    }
    return std::to_string(llN - 1);
  }
  long long Is(int m, long long n)
  {
    int iRet = NBinarySearch::FindEnd(2LL, n + 1LL, [&](const auto mid) {return Cmp(mid, m, n) >= 0; });
    return (0 == Cmp(iRet,m,n))? iRet : 0;
  }
  //k进制的m个1和n的大小比较,n大返回正数,相等返回0,n小返回负数
  long long Cmp(long long k, int m, long long n)
  {
    long long llHas = 1;
    for (; m > 0; m--)
    {
      if (n < llHas)
      {
        return -1;
      }
      n -= llHas;
      if (m > 1)
      {// 最后一次llHas并不使用,所以越界不影响
        if (LLONG_MAX / k < llHas)
        {
          return -1;
        }
        llHas *= k;
      }
    }
    return n;
  }
};
template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  Solution slu;
  string res;
  res = slu.smallestGoodBase("470988884881403701");
  Assert(res, std::string("686286299"));
  res = slu.smallestGoodBase("2251799813685247");
  Assert(res, std::string("2"));
  res = slu.smallestGoodBase("13");
  Assert(res, std::string("3"));
  res = slu.smallestGoodBase("4681");
  Assert(res, std::string("8"));
  res = slu.smallestGoodBase("1000000000000000000");
  Assert(res, std::string("999999999999999999"));
  res = slu.smallestGoodBase("1333");
  Assert(res, std::string("36"));
  res = slu.smallestGoodBase("463381");
  Assert(res, std::string("463380"));
  //CConsole::Out(res);
}


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。



相关文章
|
5月前
|
算法
数据结构和算法学习记录——时间复杂度的计算(嵌套循环、大O的渐进表示法、双重循环、常数循环、strchr、冒泡排序、二分查找、斐波那契数列递归)
数据结构和算法学习记录——时间复杂度的计算(嵌套循环、大O的渐进表示法、双重循环、常数循环、strchr、冒泡排序、二分查找、斐波那契数列递归)
427 1
|
5月前
|
存储 机器学习/深度学习 算法
数据结构和算法学习记录——空间复杂度的计算(冒泡排序、阶乘递归、斐波那契数列递归、常见复杂度对比、栈帧、栈溢出)
数据结构和算法学习记录——空间复杂度的计算(冒泡排序、阶乘递归、斐波那契数列递归、常见复杂度对比、栈帧、栈溢出)
46 0
|
5月前
|
人工智能 算法 C语言
数据结构与算法——简单排序-冒泡排序、插入排序,时间复杂度下界(图示、代码、时间复杂度、定理)
数据结构与算法——简单排序-冒泡排序、插入排序,时间复杂度下界(图示、代码、时间复杂度、定理)
35 0
|
6月前
|
搜索推荐 算法 索引
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
158 4
|
11月前
|
搜索推荐 算法 C语言
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)
|
6月前
|
算法 测试技术 C++
【二分查找】自写二分函数的总结
【二分查找】自写二分函数的总结
|
11月前
|
搜索推荐 算法
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(下)
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(下)
|
搜索推荐 算法 索引
快排图文详解:快速排序算法的实现 - 【双边循环法与单边循环法 & 递归与非递归(栈的方式)的实现】(一)
快排图文详解:快速排序算法的实现 - 【双边循环法与单边循环法 & 递归与非递归(栈的方式)的实现】
210 0
|
存储 搜索推荐 索引
快排图文详解:快速排序算法的实现 - 【双边循环法与单边循环法 & 递归与非递归(栈的方式)的实现】(二)
快排图文详解:快速排序算法的实现 - 【双边循环法与单边循环法 & 递归与非递归(栈的方式)的实现】
176 0
|
存储
二分法查找解题原理与运用方式
二分法查找解题原理与运用方式
248 0
二分法查找解题原理与运用方式