【问题标题】:Algorithm to find minimum in array of point在点数组中找到最小值的算法
【发布时间】:2021-11-21 17:16:55
【问题描述】:

我有一个具有最小值的值向量,但在它之后不减少,之前不增加。示例如下:

std::vector<int> arr = {90, 80, 70, 60, 55, 62, 71, 89, 104}

在示例中,我想找到 55。 我希望能够有效地找到它的最小值。 O(n) 复杂度是不够的。据说可以对二分搜索进行某种修改,但我想知道是否有现成的解决方案。

【问题讨论】:

  • 好吧。在这个元素之前不增加,之后不减少
  • 如果数组是排序好的,可以使用二分查找或者斐波那契查找。但是,对于小型数组,任何比线性搜索更复杂的事情都不容忽视。
  • 我愿意,我明确地说“它之后不减少,之前不增加”
  • 对不起,我没看到那部分。
  • 没有内置任何东西,但你说得对,修改后的二进制搜索在一般情况下就足够了。

标签: c++ algorithm search


【解决方案1】:

您可以实现 O(lg n) 复杂度如下:

int findTurningPoint(const vector<int>& v, int begin, int end) {
    int mid = (begin + end) / 2;
    if (v[mid - 1] > v[mid] && v[mid + 1] > v[mid]) {
        return mid;
    } else if (v[mid - 1] > v[mid]) {
        return findTurningPoint(v, mid + 1, end);
    } else if (v[mid - 1] < v[mid]) {
        return findTurningPoint(v, begin, mid - 1);
    }
}

vector<int> arr = {90, 80, 70, 60, 55, 62, 71, 89, 104};
cout << findTurningPoint(arr, 0, arr.size() - 1); // 4

方法说明:

选择向量中间的元素。通过与邻居比较来检查它是否形成最小值。如果不是,则查找该元素及其相邻元素是否形成递增序列。如果是这样,请对中间元素左侧的所有元素重复上述过程。否则,对中间元素右侧的所有元素重复该过程。

【讨论】:

  • 如果你在走运的情况下放弃提前退出,正好降落在最小值上,每一步都可以只做两次比较(begin &lt; endv[mid] &lt; v[mid+1])。您当前的实现可能会读取数组的两端,这也是一个问题。
  • 另外这个mid = (begin - end) / 2; 是完全错误的(而且总是否定的,哎哟!)。你的意思可能是mid = begin + (end - begin) / 2;
  • "如果你在走运的情况下放弃提前退出,正好降落在最小值,每一步都可以只做两次比较(begin " 我不明白。如果“幸运情况”被消除,那么递归就永远不会停止。
  • 我的建议是,如果每次迭代/递归的成本可以大大降低,最好让搜索运行到begin&gt;=end(更多迭代)。现在,每次迭代/递归您有多达 6 次比较,并且您仍然没有处理基本情况(begin==end 不能被忽略,因为从第一次调用就可以做到)所以至少7. 将其减少到 2 或 3 可能是净赢,即使在没有提前退出的情况下平均迭代/递归计数会增加。
  • @WaisKamal 供将来参考,平均而言,二分搜索中的提前退出仅节省了一次循环。例如,假设对 127 个元素的数组进行二分搜索。第一次尝试只能找到 127 个元素中的 1 个。 127 中的 2 将在第二次尝试中找到。 3 次尝试 4 次,4 次尝试 8 次,5 次尝试 16 次,6 次尝试 32 次,7 次尝试 64 次。平均尝试次数刚刚超过 6 次。因此,提前退出平均可以节省一次循环。
猜你喜欢
  • 2011-03-30
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
  • 2013-04-08
  • 2012-03-16
  • 2018-07-04
  • 2014-10-23
相关资源
最近更新 更多