学習コンテンツテック企業求人ブログ面接対策サポート

Coding InterviewCat

トップ

01 Coding InterviewCat

はじめに

02 イントロダクション03 Coding InterviewCat対象読者

コーディング面接対策とロードマップ

04 企業ごとの対策のレベル感05 コーディング面接に対する心構え06 コーディング面接対策ロードマップ

Python基礎と計算量

07 コーディング面接で必要なPythonの学習08 計算量とBig O

Discordサポートについて

09 Discordサポート(購入者特典)

本書掲載のLeetCode問題集

10 本書に掲載されているLeetCode問題集

配列 / 文字列

11 配列と文字列(導入)12 ハッシュテーブル(導入)13 ソート(導入)14 スタック(導入)15 配列 / 文字列(基礎)二重ループ16 配列 / 文字列(基礎)ハッシュテーブル17 配列 / 文字列(基礎)ソート, カスタムソート, バケットソート18 配列 / 文字列(基礎)行列 2D Matrix19 配列 / 文字列(基礎)スタック20 配列 / 文字列(応用)累積和(Prefix Sum)21 配列 / 文字列(応用)Two Pointers22 配列 / 文字列(応用)Sliding Window23 配列 / 文字列(応用)In-place Counting, Negative Marking24 配列 / 文字列(応用)Quickselect

ヒープ / 優先度付きキュー

25 ヒープ / 優先度付きキュー(導入)26 ヒープ / 優先度付きキュー(基礎) heapify, heappush, heappop27 ヒープ / 優先度付きキュー(基礎)ヒープソート

再帰呼び出し / バックトラック法

28 再帰呼び出し / バックトラック法(導入)29 再帰呼び出し / バックトラック法(基礎)再帰30 再帰呼び出し / バックトラック法(応用)バックトラック

連結リスト

31 連結リスト(導入)32 連結リスト(基礎)リスト走査33 連結リスト(基礎)ノード削除34 連結リスト(基礎)リスト反転35 連結リスト(基礎) 複数のリスト走査36 連結リスト(応用) Two Pointers, Slow/Fast Pointers37 連結リスト(応用) 双方向リスト38 キュー(導入)

二分探索

39 二分探索(基礎)値の探索, 境界の探索40 二分探索(基礎)下界, 上界41 二分探索(応用)答えの決めうち二分探索, 最長部分増加列42 二分探索(発展)2D 最長部分増加列

二分木

43 二分木(導入)44 二分木(基礎)BFS, DFS45 二分木(基礎)巡回, 二分探索木46 二分木(応用)二分木の再構築, 二分木のシリアライズ

グラフ

47 グラフ(導入)48 グラフ(基礎)BFS, DFS49 グラフ(基礎)二次元配列50 グラフ(基礎)ダイクストラ51 グラフ(基礎)トポロジカルソート52 グラフ(応用)木の直径, 強連結成分, 関節点 & 橋53 グラフ(応用)Unionfind, 最小全域木54 グラフ(応用)Warshall-Floyd, 0-1 BFS55 グラフ(発展)グラフDP

動的計画法

56 動的計画法(導入)57 動的計画法(基礎)貰うDP, 配るDP58 動的計画法(基礎)”まで”を状態として扱う, 状態の拡張59 動的計画法(基礎)二次元状態DP60 動的計画法(応用)グラフDP, メモ化再帰DP61 動的計画法(応用)辞書で状態を管理, bitで状態を管理62 動的計画法(応用)2つのDP, 絶対値DP, ゲームDP63 動的計画法(発展)スタックとDP, 累積和とDP
64 [Coming Soon] Bit Manipulation65 [Coming Soon] 貪欲法66 [Coming Soon] トライ木、サフィックス木67 [Coming Soon] Intervals68 [Coming Soon] 数学
© 2026 InterviewCat. All rights reserved.
プライバシーポリシー利用規約特定商取引法に基づく表記運営お問い合わせフォーム
🧑‍💻
Coding InterviewCat
/
📖
ソート(導入)
📖

ソート(導入)

ソートはコーディング面接において頻繁に使われる概念です。某外資戦略系コンサルファームの面接にてソート関数をそのまま実装しろという問題を私は過去に一度だけ経験した事はあります。正直にいってそのまま丸暗記する事にあまり意味はありません。しかし、ソートアルゴリズムの特性や仕組みを知っておく事で、コーディング面接でのフォローアップでの会話についていく事ができます。
なぜクイックソートがの時間計算量でソートできるのか?なぜ最悪の場合にの時間計算量になるのか?マージソートは何故安定ソートなのか?メモリをほぼ限界まで使用している巨大な配列に対してソートする場合にはどうすべきか?など普通に面接中に聞かれてもおかしくはありません。この章でしっかりと仕組みを理解し、そのまま丸暗記より導出できるようになっておきましょう。

バブルソート

バブルソートはシンプルなソートアルゴリズムです。隣接する要素を比較し、大きい(or 小さな)値が右側に来るように交換することで、リスト内の要素をソートします。
配列のサイズをnをし、昇順のソートの場合を考えます。ステップとしては
  1. 最初のイテレーションでは、j=0からj=n-1まで隣接する要素a[j] > a[j+1]を比較して、条件が成立する場合要素を入れ替えることで、最大の要素が配列の最後に移動します。
  1. 次のイテレーションでは、j=0からj=n-2まで同様のプロセスが繰り返され、次に大きい要素が配列の末尾から2番目に移動します。
  1. このイテレーションを、全ての要素はソートされるまで繰り返します。
notion image
from typing import List def bubble_sort(nums: List[int]) -> List[int]: n = len(nums) for i in range(n): for j in range(n-i-1): if nums[j] > nums[j+1]: nums[j], nums[j+1] = nums[j+1], nums[j] return nums print(bubble_sort([6, 15, 4, 2, 8])) # Output: [2, 4, 6, 8, 15]

ソートの安定性

安定ソートとはキーが同等な複数のデータのソート前の前後関係がソート後も保存されるものの事を言います。バブルソートは等しい値の場合に要素が交換されないので安定ソートとなります。

時間計算量

上記のコードのバブルソートの最良、最悪、平均時間計算量はです。これは、ネストされたfor文を使用して、配列の各要素が他のすべての要素と比較されるためです。
しかし、一度でも要素の交換が行われたのかを内側のループでフラグで管理する事で要素の入れ替えが行われなかった際にはその配列は既にソートされている事と見なしループをbreakでいち早く抜ける事ができるので、最良時間計算量をにまで減らす事ができます。これはほぼソート済の配列に対して有効です。

空間計算量

バブルソートはin-placeアルゴリズムなので最良、最悪、平均空間計算量はです。in-placeアルゴリズムとはデータ構造の変換を行うにあたって、追加のメモリ領域をほとんど使わずに行うアルゴリズムの事です。バブルソートはソートを行う際に元の配列に対して要素の交換を行います。

マージソート

マージソートは効率的なソートアルゴリズムです。分割統治法に基づいており、以下のステップで動作します。マージソートは再帰呼出しの知識を要します。前提知識のない方はRecursion / Backtracking(再帰呼び出し / バックトラック法)の序盤の説明を読む事をオススメします。
  1. 分割: リストを同じサイズの再帰的に2つのサブリストに分割していきます。
  1. ソート: サブリストをソートします
  1. 結合: ソートされたサブリストをマージして、元のリストを再構築します
詳細を見ていきましょう。2つのソート済み配列を「マージ(併合)」して1つのソート済み配列を作る merge() 関数を活用します。配列のleft番目からright番目までをソートする merge_sort(nums, left, right)のアルゴリズムは次です。
  1. mid = (left + right) / 2 でサブリストに分割するポイントを決める
  1. merge_sort(nums, left, mid)でleft番目からmid番目のサブリストをソート
  1. merge_sort(nums, mid+1, right)でmid+1番目からright番目のサブリストをソート
  1. merge(nums, left, mid, right) で整列済のソートした配列をマージ
notion image
from typing import List def merge(nums: List[int], left: int, mid: int, right: int): # 並べ替えられた左右の半分を一時配列にコピー left_nums = nums[left:mid+1] right_nums = nums[mid+1:right+1] i, j, k = 0, 0, left # left_nums、right_nums、numsのインデックス # 二つの並べ替えられた半分を元の配列に統合 while i < len(left_nums) and j < len(right_nums): if left_nums[i] <= right_nums[j]: nums[k] = left_nums[i] i += 1 else: nums[k] = right_nums[j] j += 1 k += 1 # left_numsとright_numsの残りの要素をコピー(あれば) while i < len(left_nums): nums[k] = left_nums[i] i += 1 k += 1 while j < len(right_nums): nums[k] = right_nums[j] j += 1 k += 1 def merge_sort(nums: List[int], left: int, right: int) -> List[int]: # これ以上分割できない場合 if left >= right: return nums # 配列の中間インデックス mid = (left + right) // 2 # 左右の半分を再帰的に並べ替え merge_sort(nums, left, mid) merge_sort(nums, mid+1, right) # 並べ替えられた半分を統合 merge(nums, left, mid, right) return nums print(merge_sort([5, 2, 3, 2, 1], 0, 4))

ソートの安定性

マージソートは要素が等しい場合にそれらの元の順序を保持するため安定ソートです。このアルゴリズムは、分割されたサブリストをマージする際に、等しい要素が見つかった場合、常に先に分割された左側のサブリストの要素を選択します。これにより、等しい要素間の相対的な順序が変わらないため、マージソートは元の位置関係を保持する安定なソート方法となります。

時間計算量

マージソートの最良、最悪、平均時間計算量はすべてです。これは、どのような状況でもリストが毎回半分に分割され、各レベルで全要素がマージされるためです。

空間計算量

マージソートの最良、最悪、平均空間計算量はすべてです。これは、ソート中に配列を分割し、その分割された部分をマージするために最終的に入力される配列と同じサイズの配列を確保するためです。

クイックソート

クイックソートはマージソート同様に分割統治法に基づくアルゴリズムで、効率的なソートアルゴリズムです。以下の手順で動作します。
  1. Pivotの選択: 配列内から一つの要素(Pivot)を選びます。このPivotの選び方には様々な方法がありますが、ランダムに選ぶ方法や配列の中央値を選ぶ方法などが一般的です。以下のコードではランダムに一つ選んでいます。
  1. パーティション(分割): Pivotより小さい要素を配列の左側に、Pivot以上の要素を配列の右側に移動させます。
  1. 再帰的にソート: パーティションされたサブリストに対して、上記の手順を再帰的に適用します。
下の図では配列をPivotごとにパーティションに分割する手法を図で表したものです。最後の値の3がPivotだった場合を想定しています。
今回はより簡単なLomutoのパーティションをベースに解説しています。他にもHoareのパーティションなどもあります。比較はこちらをご覧ください。
notion image
このパーティションによりパーティションの左側にはPivotよりの要素が並び、右側にPivot以上の要素が並びます。これはnums[j] < pivot_valueの時に左側にnums[j]の要素を移動させているからです。
notion image
ソートの全体図を書くと
notion image
from typing import List import random def partition(nums: List[int], left: int, right: int): # Pivotをランダムに選択する pivot_index = random.randint(left, right) pivot_value = nums[pivot_index] # Pivotをリストの最後に移動する nums[pivot_index], nums[right] = nums[right], nums[pivot_index] i = left # リストをパーティションに分ける for j in range(left, right): if nums[j] < pivot_value: nums[i], nums[j] = nums[j], nums[i] i += 1 # Pivotを正しい位置に移動する nums[i], nums[right] = nums[right], nums[i] return i # Pivotの位置を返す def quick_sort(nums: List[int], left: int, right: int): if left >= right: return mid = partition(nums, left, right) quick_sort(nums, left, mid-1) quick_sort(nums, mid+1, right) nums = [5, 2, 1, 4, 3] quick_sort(nums, 0, len(nums)-1) print(nums) # Output: [1, 2, 3, 4, 5]

ソートの安定性

クイックソートは不安定なソートです。これは、Pivotを基準に要素を分割する過程で、等しい値の要素の相対的な順序が変わる可能性があるためです。例えば、配列内に等しい値の要素が複数ある場合、これらの要素がソート後に元の順序を維持する保証はありません。

時間計算量

クイックソートの時間計算量は、最悪の場合にはですが、最良・平均時間計算量ではです。
最悪のケースは、Pivotが毎回最小値または最大値になる場合に発生します。例えば以下のケースで考えてみましょう。毎回最大値をPivotとして選択するとN回の分割が行われ、N + (N - 1) + (N -2) + 2 + 1の比較が行われるので時間計算量はとなります。導出過程は1からNまでの和の公式を調べてみてください。
notion image
しかし、一般的にはPivotの選択が適切であれば、パーティションが均等に分割されるため、平均的なケースの時間計算量がになります。

空間計算量

クイックソートはin-placeアルゴリズムなので最良、最悪、平均空間計算量はすべてです。なお、再帰におけるコールスタックを考えると最悪となります(コールスタックの深さが愛作Nになるので)。
📖
ハッシュテーブル(導入)
📖
スタック(導入)