int数组,找小于右边所有数,大于左边所有数的数
一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。能否只用一个额外数组和少量其它空间实现。
老早就想把这道题目写了,一直没写;N天不碰算法,思维完全钝了。
网络上的解法思路(见附),都是一直的。那如果再说一模一样的就没太大意思,除了记录题目和增加网络冗余度之外;
其实这里说的和网络大部分也是一样的,但是,解释稍微转变,希望让你这透彻的看懂它。
经一番琢磨,题目很网上的解释包含了一个很简单的思想,“单调栈”,哈哈,是不是很熟悉呢?
如果想不起来,就回去看看其他博文中的题目吧:单调栈:柱形统计图中最大面积(POJ 2559) 谁看得最大 (使用了单调栈的思想)
好了这里,当你理解了单调栈了,可以开始解题了;
7, 10, 2, 6, 19, 22, 32
如果只遍历一遍,我们很容易可以知道它是否比它前面的所有数要大(记录其前面遍历的最大元素),例如,这时遍历到19了,之前先记录前面最大的(这是可以做到的),为10。因为19>10 所有19符合其中大于等于其前面所有元素的条件。这时同时也记录最大元素为19,接着检测下一个元素。
好,这里我们完成了检测“左边的数都小于等于它”的条件,麻烦就在于“右边的数都大于等于它”;这时候“单调栈”的思想就可以很好的解决这个问题。因为它保证了每次压进栈里的元素是单调递增的。如果不是递增,把之前栈内的元素出栈(这些元素已经不符合“右边的数都大于等于它”了),保持单调栈的特性。
所有把符合上述的元素放在单调栈中就OK了。
OK,简单吧。自认为比一些网络上了各种抄的复杂解释来得简单,当然也有人会认为其他的要简单,whatever,反正我们都理解了。
1 void func37(int a[], int n) 2 { 3 assert(a && n>0); 4 5 int pre_max = -INT_MAX; //可使用栈的特性剔除这个变量 6 int i; 7 stack<int> st; 8 9 for (i=0; i<n; i++) 10 { 11 if (a[i] >= pre_max) 12 { 13 pre_max = a[i]; 14 15 while(!st.empty() && a[st.top()] > a[i]) //保存栈的特性 16 { 17 st.pop(); 18 } 19 20 st.push(i); 21 } 22 else 23 { 24 while(!st.empty() && a[st.top()] > a[i]) //保存栈的特性 25 { 26 st.pop(); 27 } 28 continue; 29 } 30 } 31 32 if (st.empty()) 33 { 34 cout<<"no one is appropriated"<<endl; 35 return; 36 } 37 while(!st.empty()) 38 { 39 cout<<a[st.top()]<<" "; 40 st.pop(); 41 } 42 cout<<endl; 43 }
附:
最原始的方法是检查每一个数 array[i] ,看是否左边的数都小于等于它,右边的数都大于等于它。这样做的话,要找出所有这样的数,时间复杂度为O(N^2)。
其实可以有更简单的方法,我们使用额外数组,比如rightMin[],来帮我们记录原始数组array[i]右边(包括自己)的最小值。假如原始数组为: array[] = {7, 10, 2, 6, 19, 22, 32}, 那么rightMin[] = {2, 2, 2, 6, 19, 22, 32}. 也就是说,7右边的最小值为2, 2右边的最小值也是2。
有了这样一个额外数组,当我们从头开始遍历原始数组时,我们保存一个当前最大值 max, 如果当前最大值刚好等于rightMin[i], 那么这个最大值一定满足条件。还是刚才的例子。
第一个值是7,最大值也是7,因为7 不等于 2, 继续,
第二个值是10,最大值变成了10,但是10也不等于2,继续,
第三个值是2,最大值是10,但是10也不等于2,继续,
第四个值是6,最大值是10,但是10不等于6,继续,
第五个值是19,最大值变成了19,而且19也等于当前rightMin[4] = 19, 所以,满足条件。
如此继续下去,后面的几个都满足。
毕