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

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

作者推荐

【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数

本文涉及的基础知识点

二分查找算法合集

自写二分函数 的封装

我暂时只发现两种:

一,在左闭右开的区间寻找最后一个符合条件的元素,我封装成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);
}


相关下载

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

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

测试环境

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

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

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

相关文章
|
设计模式 开发框架 监控
精准解读桥接模式-用桥接模式构建可扩展的软件系统
桥接模式是一种设计模式,旨在将抽象和实现部分分离,使它们可以独立地变化。这种模式的目的是提高系统的灵活性和可扩展性。桥接模式的主要思想是将抽象和实现通过一个桥接类连接起来,从而实现它们的解耦。在这种模式中,抽象部分可以根据需要进行扩展,而实现部分可以自由地变化,而不影响彼此。桥接模式在处理多个独立变化的维度、解耦继承关系、处理平台差异和扩展现有系统等方面具有广泛的应用领域。通过使用桥接模式,可以提高系统的可维护性和可扩展性,使系统更加灵活和适应变化。通过桥接模式,将系统中的抽象部分与实现部分解耦,从而...
432 0
精准解读桥接模式-用桥接模式构建可扩展的软件系统
|
关系型数据库 MySQL Shell
4.3 使用sqlmap直连MySQL获取webshell
4.3 使用sqlmap直连MySQL获取webshell
815 0
|
JavaScript API 开发者
Vue 3 为什么同时需要 Ref 和 Reactive?
Vue 3 为什么同时需要 Ref 和 Reactive?
|
Java 监控 安全
Java一分钟之-JMX:Java管理扩展
【6月更文挑战第3天】Java Management Extensions (JMX) 允许创建、注册和管理MBeans以监控和控制Java应用。本文关注JMX的基本概念、常见问题和易错点。关键点包括:正确实现MBean和使用`StandardMBean`,确保MBean注册时名称唯一,引用平台MBean Server,配置安全管理,以及处理MBean操作异常。理解这些概念和最佳实践对于有效利用JMX至关重要。记得在实际应用中测试管理接口并加强生产环境的安全性。
370 8
|
消息中间件 存储 监控
|
消息中间件 Serverless Go
Serverless 应用引擎操作报错合集之通过自定义域名配置jwt认证,始终报错:"Code": "JWTTokenIsInvalid",是什么导致的
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
430 2
|
存储 JSON 前端开发
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
453 0
|
安全 算法 数据安全/隐私保护
密码学系列之八:密码协议
密码学系列之八:密码协议
|
API 开发工具 开发者
淘宝店铺所有商品数据接口(Taobao.item_search_shop)
淘宝店铺所有商品数据接口(Taobao.item_search_shop)
|
供应链 安全 数据管理
中国新闻周刊报道|不流通无价值,阿里瓴羊港打造共享“数据流通港”
中国新闻周刊报道|不流通无价值,阿里瓴羊港打造共享“数据流通港”
392 0