blog

ピークを見つける

トピックでは、ピーク要素は要素の隣接要素よりも大きく、nums[-1] = nums[n] = -∞と言ったので、nums[0]がnums[1]より大きく、さらにnums[-1]が負の無限大であれば、...

Mar 26, 2020 · 2 min. read
Share this

問題ではピーク要素とは左右の隣接要素より大きいもので、nums[-1]=nums[n]=-∞とも書いてあるので、nums[0]がnums[1]より大きいかどうかのハントで、nums[-1]が負の無限大ならnums[0]がピーク要素になるのでは?

nums[0] が nums[1] よりも大きくない場合は、そのまま nums[1] と nums[2] を比較し、nums[1] の方が大きければ、nums[1] がピーク要素であると仮定することもできます。nums[1] が nums[2] よりも大きくない場合は、後ろを振り返り続けます。

この方法の時間的複雑さはO(n)です。

class Solution {
public:
 int findPeakElement(vector<int>& nums) {
 for(int i = 0; i < nums.size() - 1; ++i) {
 if(nums[i] > nums[i + 1]) {
 return i;
 }
 }
 return 0;
 }
};

この問題では、O(logn)の時間複雑度を持つ解が求められています。それなら、バイセクションを使うしかありません。

一般に、二等分法は順序配列の要素を見つけるために使われますが、ここでは順序配列ではありません。

この問題では、nums[-1] = nums[n] = -∞ となっており、さらにこの問題の要素はすべて同じではないので、隣接するすべての要素よりも大きい要素があるはずです。配列の要素が増加する場合でも、最後の要素がピーク要素であり、減少することは同じです。ですから、この問題には解があることが保証されています。

もし nums[mid] < nums[mid + 1]であれば、右半分にピークがあるはずです。nums[mid + 1]はmidより大きく、nums[n]は負の無限大ですから、右半分は上昇と下降を繰り返しているはずです。右半分が上昇し、右半分が下降しているように見えるはずなので、left = mid + 1 とします(最小のピークも nums[mid + 1] です)。

同様に、nums[mid] > nums[mid + 1]の場合、nums[mid]はnums[mid + 1]より大きく、nums[0]は負の無限大なので、区間の左半分にピークがあるはずです。

ルックアップを毎回半分に折りたたむと、ピークエレメントが出来上がります。

コードは以下の通り:

class Solution {
public:
 int findPeakElement(vector<int>& nums) {
 int left = 0, right = nums.size() - 1;
 while(left < right) {
 int mid = (left + right) >> 1;
 if(nums[mid] < nums[mid + 1]) {
 left = mid + 1;
 } else {
 right = mid;
 }
 }
 return left;
 }
};
Read next

CSSポジショニング

i. display 1. display 1. インライン要素はFlex 2として指定されます。 コンテナはFlexとして指定されます。 row:主軸は水平、始点は左。 row-reverse:主軸は水平、始点は右。 列:主軸は垂直、始点は上端。 column-reverse: 主軸は垂直、始点は上端。

Mar 26, 2020 · 4 min read