1. [1,1,2,3,3] -> [1,2,3]
  2. [1,1,1] -> [1]
  3. [1,2,3] -> [1,2,3]
  4. [1] -> [1]
  5. [1,1,2,2,3,3,4,4] -> [1,2,3,4]
コース一覧
コンピューターサイエンス上級:アルゴリズムとデータ構造
ソート済みリストの重複削除

コンピューターサイエンス上級:アルゴリズムとデータ構造

探索、ソート、木、グラフ、動的計画法、貪欲法など、競技プログラミングや技術面接で問われる高度なアルゴリズムとデータ構造を学べるコースです。基本的な CS の知識を持ち、アルゴリズム力を伸ばしたい学習者や、外資・大手の技術面接対策をしたい方を対象としています。約 13 時間 (1 日 30 分 × 26 日) で 50 レッスンを修了でき、修了後はコーディング面接の実装問題を構造的に解けるようになります。

1
連結リスト
01. リンクリスト構築と長さの計算5分
02. リンクリストの反転5分
03. リンクリストのサイクル検出5分
04. ソート済みリンクリストの merge5分
05. リンクリストの中央ノード取得5分
06. ソート済みリストの重複削除5分
07. 第1章まとめクイズ5分
2
二分木
01. 二分木の in-order 走査5分
02. 二分木の pre-order 走査5分
03. 二分木の post-order 走査5分
04. 二分木の幅優先走査 (BFS)5分
05. 二分木の高さ5分
06. 二分木の平衡判定5分
07. 第2章まとめクイズ — 二分木5分
3
探索木 (BST)
01. BST に値を挿入する5分
02. BST から値を検索する5分
03. BST の最小値と最大値5分
04. BST 妥当性チェック5分
05. BST で k 番目に小さい値5分
06. BST から値を削除する5分
07. 第 3 章 まとめクイズ5分
4
ハッシュとセット
01. hashmap で頻度集計5分
02. キーでグループ化5分
03. two sum (hash で O(n))5分
04. 部分配列の和 = k の個数5分
05. 最長連続部分列5分
06. 集合の積 (intersection)5分
07. 第4章まとめクイズ5分
5
グラフ
01. グラフ BFS で連結成分サイズを求める5分
02. グラフ DFS で連結成分の数を数える5分
03. グラフのパス存在判定5分
04. BFS で最短経路の長さを求める5分
05. トポロジカルソート5分
06. 2 部グラフ判定5分
07. ダイクストラ法 — 重み付きグラフの最短経路15分
08. 第5章まとめクイズ — グラフ5分
6
動的計画法 (上級)
01. 編集距離 (レーベンシュタイン距離)5分
02. 最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法5分
03. 最大部分配列和 (Kadane)5分
04. 隣り合わない最大値 (House Robber)5分
05. グリッド経路数 (Unique Paths)5分
06. 単語分割可能か (Word Break)5分
07. 第6章まとめクイズ — 動的計画法 (上級)5分
7
総合データ構造と関数型
01. map と filter を組み合わせる5分
02. reduce で積を計算5分
03. 関数合成5分
04. カリー化5分
05. trie の単純検索 (prefix マッチ)5分
06. Union-Find (連結成分数)5分
07. 最終総まとめクイズ5分

ソート済みリストの重複削除

同じ値が続けて並んでいる列から、重複を 1 つにまとめます。すでに並べ替えてあるなら、同じ値は必ず隣どうしにいます。この前提が効いてきます。

set に入れると、並び順が消える

重複を消すだけなら、集合に入れて取り出すのが早そうに見えます。ところが集合は中身の順序を約束しません。せっかく並べ替えてあった情報が、そこで捨てられます。時刻順のログや、順位のついた一覧では使えない手です。

言語によっては、たまたま入れた順で出てくることもあります。それがいちばん厄介です。手元では通り、本番で並びが崩れます。順序に意味があるなら、順序を保証する書き方を選んでください。

並んでいるという前提を活かせば、集合はそもそも要りません。同じ値が離れて現れることは無いので、隣とだけ比べれば足ります。

隣が同じなら、鎖から外す

札でつないだリストなら、新しい入れ物を作らずにその場で外せます。自分の次が自分と同じ値なら、その次のさらに次へ札を張り替えます。

Python

cur = head while cur is not None and cur.next is not None: if cur.value == cur.next.value: cur.next = cur.next.next # 1 つ飛ばす else: cur = cur.next

JavaScript

let cur = head; while (cur !== null && cur.next !== null) { if (cur.value === cur.next.value) { cur.next = cur.next.next; } else { cur = cur.next; } }

外されたノードは、どこからも指されなくなって消えます。書き換えたのは札 1 枚だけで、他のノードは 1 つも動いていません。

外したときは、進まない

上のコードで、飛ばした側に cur = cur.next が無いのは書き忘れではありません。1, 1, 1 のように 3 つ続く場合を考えてください。

1 つ飛ばした直後、cur はまだ 1 つ目の 1 にいて、その次が 3 つ目の 1 になっています。ここで進んでしまうと、3 つ目が生き残ります。飛ばしたときは同じ場所に留まってもう一度隣を見る、違う値に出会ったときだけ進む。この使い分けが要です。

条件を cur.next まで確かめているのも同じ理由です。次が無い場所で cur.next.value を読むと落ちます。末尾の 1 つ手前で止まる書き方になっているか、確かめてください。

実務で出てくるのは、時系列に並べたイベントから、同じ状態が続く区間を 1 件にまとめる場面です。センサーの値でも画面の操作履歴でも、変化した瞬間だけ残したいことが多い。並んでいるなら、隣を見るだけで切れ目が分かります。

新しい列に詰め直す書き方もあります。どちらで書くにせよ、1, 1, 1 と 1, 2, 3 の 2 つで試すと、進める判断を間違えていないかがすぐ分かります。

要件

  1. 入力は昇順ソート済みの配列であると仮定して良い
  2. 連続する重複を除去し、順序を保った配列を返す
  3. set() などで順序を崩さず、1 パスで処理すること

入出力例

removeDuplicates([1,1,2,3,3]) → [1,2,3] removeDuplicates([1,1,1]) → [1] removeDuplicates([1,2,3]) → [1,2,3] removeDuplicates([1]) → [1] removeDuplicates([1,1,2,2,3,3,4,4]) → [1,2,3,4]

ヒント

結果配列の末尾と現在の要素を比較し、違うときだけ追加する

最初の要素は無条件に result に入れる

set(arr) は重複を除去できるが順序が保証されない。ソート済みなら隣り合うものを比較するのが定石

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

関連レッスン

  • 第1章まとめクイズ

    連結リストの基本操作・サイクル検出・2 ポインタ法の理解度を確認するまとめクイズ。

  • 二分木の in-order 走査

    二分木を配列表現で受け取り、in-order (左 → 根 → 右) で訪問した値の配列を返す関数を実装する。

  • BST に値を挿入する

    二分探索木 (BST) に新しい値を挿入し、挿入後の in-order 走査結果を配列で返す関数を実装する。

  • hashmap で頻度集計

    配列の各要素が何回現れたかを `hashmap` で集計し、最も多く出た値を返す関数を実装する。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • リスト順序付きで複数の値を扱うデータ構造
  • イベントユーザーの操作(クリック、入力、スクロール等)を検知して処理を実行する仕組み。
  • 判断YES/NO 分岐を表す菱形
  • 配列サイズ固定の同型データの集まり
  • 処理計算や代入を表す長方形
main.py
学習モード
エディタを読み込んでいます

メモ

ソート済みリストの重複削除

⌘S で保存