局部最小值问题

问题描述:有一个无序数组,且相邻两个元素间不相等,现在让我们找出该数组中的一个局部最小值,并返回对应的下标,要求程序的时间复杂度不能大于O(n)。

局部最小值:

在一个长度为n+1的数组中,局部最小值能有多个。如:

a[0]<a[1],则a[0]就是一个局部最小值。

a[n]<a[n-1],则a[n]就是一个局部最小值。

a[i]<a[i+1]&&a[i]<a[i-1],(0<i<n-1),则a[i]就是一个局部最小值

由于题目要求编写的程序时间复杂度不能大于O(n),所以我们想到使用二分查找的思想(二分查找时间复杂度为O(log2N)),但是一个无序的数组我们该怎么去查找它的局部最小值呢?

1.我们假设情况一:a[0]>a[1],a[n]>a[n-1],从图中可以看出开始a[0]到a[1]是一个数在减小往下走的趋势,而结束a[n-1]到a[n]是一个数在增大往上走的趋势,所以我们可以确定在a[0]到a[n]之间一定存在一个低谷值使得这个趋势的方向发生了改变,那这个低谷值就我们要找的一个局部最小值。

我们就可以使用二分查找将需要查找的区间减半,此时我们需要判断的就是就是a[mid]与a[mid+1]、a[mid-1]所表示的趋势,如以左边为例,如下图,若此时a[mid-1]到a[mid]是一个上升的趋势(其余情况同理)。所以在a[0]到a[mid]之间一定有一个低谷;不断减小范围,当找到一个低谷值,也就是局部最小值就返回。

注意:若此时a[mid]到a[mid+1]为下降趋势时,以mid为基准的两边我们就选择一边就行。

2.情况二:a[0]>a[1]&&a[n]<a[n-1]或a[0]<a[1]&&a[n]>a[n-1],此时a[n]或a[0]就是一个局部最小值。

3.情况三:a[0]<a[1]&&a[n]<a[n-1],则此时a[0]、a[n]都是一个局部最小值,选择一边返回下标即可。

具体代码如下:

#include <stdio.h>
#include <malloc.h>

int* creatArray(int* p,int length);
int binarySearchMin(int* p,int length);
int compare(int* p,int i);
int main()
{
  int length = 0;
  scanf_s("%d",&length);

  int* p = (int*)malloc(length * sizeof(int));//定义长度为length的
  if (p == NULL)
  {  
  printf("内存分配失败!");
  return 0;
  }

  p = creatArray(p,length);
  int index = binarySearchMin(p,length);

  printf("%d\n",index);

  free(p);
  p = NULL;
  return 0;
}
int* creatArray(int* p,int length)
{
  for (int i = 0; i < length; i++)
  {
    scanf_s("%d", &p[i]);
  }
  return p;
}
//查找
int binarySearchMin(int* p,int length)
{
  int left = 0,right = length-1;
  int mid = 0;
  if (compare(p,left)==1&&compare(p,right-1)==-1)//头大尾大
  {
    while (left <= right)
    {
      mid = left + ((right - left) >> 1);
      if (compare(p, mid) == -1 && compare(p, mid - 1) == 1)      //p[mid]<p[mid+1]&&p[mid]<p[mid-1]
      {
        return mid;
      }
      else if (compare(p, mid) == 1)//左右只看一边
      {
        left = mid + 1;
      }
      else if (compare(p, mid - 1) == -1)
      {
        right = mid - 1;
      }
    }
  }
  else if (compare(p, left) == -1&&compare(p,right-1)==-1)//头小尾大
  {
    return left;
  }
  else if (compare(p,left)==1 && compare(p, right - 1) == 1)//头大尾小
  {
    return right;
  }
  else if (compare(p, left) == -1 && compare(p, right - 1) == 1)//头小尾小,(可以任选头尾返回)
  {
    return left;
  }
    return 0;
}
int compare(int* p, int i)
{
return p[i] > p[i + 1] ? 1 : -1;
}

总结:这个题其实就是用二分查找的思想减小时间复杂度。然后在使用二分查找时,并不是只有有序的数组才能使用,无序的也可以。同时这段代码其实还可以在使用递归的思想再去改良一下。

 

posted @   明明就不喜欢  阅读(15)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示