Pythonでリストが交互に増加・減少しているかどうかを判定するプログラム
問題の概要
数値のリスト nums が与えられ、それが「厳密に増加 → 厳密に減少 → 厳密に増加 → …」というように、増加と減少が交互に入れ替わっているかどうかを判定します。ただし、リスト全体がずっと厳密に増加し続けている場合も条件を満たすものとみなします。
たとえば入力が [2, 4, 8, 7, 5, 1, 5, 7, 2, 1] の場合、出力は True になります。[2, 4, 8] が増加し、続く [7, 5, 1] が減少、その後の [5, 7] が再び増加、最後の [2, 1] が減少しており、増加と減少がきちんと交互に現れているためです。
解法の考え方
一見すると、増加区間と減少区間を切り分けながら複雑に走査する必要があるように思えますが、実際はもっとシンプルに判定できます。確認すべきポイントは次の2つだけです。
- 最初は必ず増加で始まること:
nums[1] <= nums[0]であれば、その時点で条件違反となるためFalseを返します。 - 隣接する要素が等しくてはならないこと:「厳密な」増加・減少が求められているため、
nums[i] == nums[i - 1]となる箇所がひとつでもあればFalseを返します。
この2つのチェックを通過したリストは、必ず条件を満たします。隣接要素がすべて異なる数列は「最大の単調区間」(増加のみ、または減少のみが続く区間)に分解できますが、隣り合う単調区間は必ず方向が入れ替わるため、増加と減少は自然に交互に現れるからです。
アルゴリズムの手順
nums[1] <= nums[0]ならばFalseを返す。- リストを先頭から順に走査し、隣接する2要素
nums[i]とnums[i - 1]が等しければFalseを返す。 - 最後まで問題がなければ
Trueを返す。
実装例
以下がPythonでの実装例です。
def solve(nums):
if nums[1] <= nums[0]:
return False
for i in range(len(nums)):
if i - 1 >= 0:
if nums[i] == nums[i - 1]:
return False
return True
nums = [2, 4, 8, 7, 5, 1, 5, 7, 2, 1]
print(solve(nums))
入力
[2, 4, 8, 7, 5, 1, 5, 7, 2, 1]
出力
True
計算量
このアルゴリズムはリストを一度だけ走査するため、時間計算量は O(n)、追加のメモリ使用量は O(1) と非常に効率的です。
注意点
上記の実装は nums[1] に直接アクセスするため、要素数が1以下のリストを渡すと IndexError が発生します。実務では冒頭に if len(nums) < 2: return True のようなガードを追加しておくと安全です(要素が0個または1個のリストは自明に条件を満たすものとして扱います)。
-
Pythonでブロックの高さリストが直線y=xに対して対称かどうかを判定するプログラム
数値のリスト nums があるとします。これは正方形のブロックを横一列に並べたときの、各列の高さを表しています。ここで、このブロック形状が直線 y = x に対して対称であるかどうかを判定する必要があります。 たとえば、入力が nums = [7, 5, 3, 2, 2, 1, 1] の場合、出力は True になります。 解き方のアプローチ この問題は、リストの両端から同時に走査していくことで効率的に判定できます。手順は次のとおりです。 i を 0、j を「リストの長さ - 1」で初期化します。 i <= j である間、次の処理を繰り返します。 h := nums[j](右側の
-
Pythonで与えられたグラフが2部グラフかどうかを判定するプログラム
2部グラフとは無向グラフが与えられたとき、そのグラフが2部グラフ(バイパータイトグラフ)であるかどうかを判定する方法を解説します。2部グラフとは、グラフのすべての頂点を2つの集合 A と B に分割でき、グラフ内のすべての辺 {u, v} が必ず一方の端点 u が集合 A、もう一方の端点 v が集合 B に属するようなグラフのことです。つまり、同じ集合内の頂点同士を結ぶ辺(A-A や B-B)が一切存在しないグラフです。例として、次のようなグラフを考えてみましょう。この場合、頂点 [0, 4] を集合 A に、[1, 2, 3] を集合 B に分類できます。すべての辺は A から B、または