在一个int数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数

题目的意思很简单,比如说有数组[1,6,9,2,1,5,15,20],那么满足上述条件的元素为1,15,20。


分析1.

暴力的方法就是从左到右进行遍历,到达某个数字后,在从0开始遍历到当前下标,找出最大的,再从当前下标遍历到尾,找出最小的,然后根据题意比较一下,这个算法为O(n^2)。

比较简单的算法就是利用一下额外的空间,保存一下,每个元素右侧的最小值,或者左侧的最大值,在反向遍历一次。举个例子。

就如[1,6,9,2,1,5,15,20],如果我们要找到该数组右侧的最小值,则从后向前遍历,20开始,20的右侧的最小值为20,之后15,15右侧的最小值为15,之后5,最小值为5,之后1,最小值为1,那么新的数组就是[1,1,1,1,1,5,15,20]。

之后我们再从左向右遍历数组,并保存当前的最大值,到1,最大值为1,看新的数组,右侧的最小值为1,那么1>=1,1<=1,满足,输出,对于6,更新最大值为6,看新数组6右边的最小值为1,6>=6,但是>1,也就是比右侧最小的值大,不满足条件,不输出。同理遍历一遍就好。

代码如下:

#include <iostream>
#include <assert.h>
using namespace std;


void FindValue(int a[], int size)
{    
    assert(a!=NULL);
    assert(size!=0);


    // 定义一个辅助数组
    int* b = new int[size];
    int min=a[size-1];
    for(int i=size-1; i>=0 ; i--)
    {
        if(a[i]<=min)
        {
            min = a[i];
        }
        b[i] = min;
    }
    
    int max = a[0];
    for(int i=0; i<size; i++)
    {
        if(max <= a[i])
            max = a[i];
        if(a[i]>=max && a[i]<= b[i])
            cout<<a[i]<<endl;
    }


    
    delete[] b;


}


int main()
{
    int a[]={1,6,9,2,1,5,15,20};
    int size = sizeof(a)/sizeof(a[0]);
    for (int i=0; i<size; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<"\n满足要求的元素为:\n";
    FindValue(a,size);
}

分析2.

如果我们在从左到右扫描数组的时候,能够维护一个candidate的数组, 该数组的元素满足: 到目前为止,这些元素都大于等于它前面的元素,而小于等于到目前为止扫描到的它右边的所有元素。 容易证明, candidate数组中的元素是按照非递减顺序排列的,即对任意的i<j有cand[i] <= cand[j].
当我们扫描到一个小的元素的时候,需要从右到左(从大到小)的判断cand数组中的元素是否还满足条件,知道找到第一个不大于当前扫面元素的cand.

下面是算法的实现:

如果我们在从左到右扫描数组的时候,能够维护一个candidate的数组, 该数组的元素满足: 到目前为止,这些元素都大于等于它前面的元素,而小于等于到目前为止扫描到的它右边的所有元素。 容易证明, candidate数组中的元素是按照非递减顺序排列的,即对任意的i<j有cand[i] <= cand[j].
当我们扫描到一个小的元素的时候,需要从右到左(从大到小)的判断cand数组中的元素是否还满足条件,知道找到第一个不大于当前扫面元素的cand.

下面是算法的实现:

  1. int findNum(int *arr, int n)  
  2. {  
  3.     if(arr == NULL) return 0;  
  4.     int *cand = new int[n];  
  5.     int idx = 0; //记录当前cand数组中的元素个数。  
  6.     cand[idx++] = arr[0];  
  7.     int max = arr[0], i;  
  8.     for(i=1; i<n; i++) {  
  9.         if( arr[i] >= max) {  
  10.             cand[idx++] = arr[i];  
  11.             max = arr[i];  
  12.         } else {  
  13.             while(idx > 0 && cand[idx-1] > arr[i]) idx--;  
  14.         }  
  15.     }  
  16.     printf(" num: ");  
  17.     for(i=0; i<idx; i++) printf("%d ", cand[i]);  
  18.     printf("\n");  
  19.     return idx;  
  20. }  
  21. int main()  
  22. {  
  23.     int arr[20] = {3, 1, 6, 4, 5, 7, 9, 8, 10, 14, 12};  
  24.     //int arr[20] = {1, 2, 4, 5, 6, 7, 8, 9, 10, 3, 11};  
  25.     int count = 0;  
  26.     count = findNum(arr, 11);  
  27. }  
整个算法只需要对原来的数组扫描一次, 并且每个元素进入或者退出candidate数组最多一次。 算法在最坏情况下最多进行比较2*n次,时间和空间复杂度都是O(n).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值