C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でソート済み連結リストIIから重複要素を削除する方法

ソート済みの連結リストが与えられたとき、2回以上出現した要素をすべて取り除き、1度だけ現れる要素(ユニークな要素)だけを残すことを考えます。

例えば、リストが [1,1,1,2,2,3,5,6,6,7,8] の場合、出力は [3,5,7,8] となります。これは、1・2・6 がそれぞれ複数回出現している一方で、3・5・7・8 は1度しか現れていないためです。

アルゴリズムの手順

この問題は、ダミーノード(番兵ノード)を活用することで効率的に解くことができます。手順は以下の通りです。

  • 値 -1 を持つダミーノードを作成し、prev := NULLdummyPtr := dummy と初期化します。
  • head が NULL でない限り、以下を繰り返します。
    • 次ノードが存在しない、または現在の値と次ノードの値が異なる場合(ユニークな要素の場合):
      • dummyPtr->nexthead を接続します。
      • temphead->next を保存し、head->next を NULL にして切り離します。
      • head := temp として処理を進め、dummyPtr を1つ進めます。
    • 重複が見つかった場合
      • prev := headhead := head->next とします。
      • head が NULL でなく、かつ head の値が prev の値と等しい間、両方のポインタを進め続け、重複ブロック全体をスキップします。
  • 最後に、ダミーノードの next を結果として返します。

C++での実装例

以下のコードを実行すると、実際の動作をより深く理解できます。

#include <bits/stdc++.h>
using namespace std;
class ListNode{
    public:
        int val;
        ListNode *next;
        ListNode(int data){
            val = data;
            next = NULL;
        }
    };
    ListNode *make_list(vector<int> v){
        ListNode *head = new ListNode(v[0]);
        for(int i = 1; i<v.size(); i++){
            ListNode *ptr = head;
            while(ptr->next != NULL){
                ptr = ptr->next;
            }
            ptr->next = new ListNode(v[i]);
        }
        return head;
    }
    void print_list(ListNode *head){
        ListNode *ptr = head;
        cout << "[";
        while(ptr){
        cout << ptr->val << ", ";
        ptr = ptr->next;
    }
    cout << "]" << endl;
}
class Solution {
    public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* dummy = new ListNode(-1);
        ListNode* prev = NULL;
        ListNode* dummyPtr = dummy;
        ListNode* nextNode;
        while(head){
            if(!head->next || head->val != head->next->val){
                dummyPtr->next = head;
                ListNode* temp = head->next;
                head->next = NULL;
                head = temp;
                dummyPtr = dummyPtr->next;
            } else {
                prev = head;
                head = head->next;
                while(head && head->val == prev->val){
                    prev = head;
                    head = head->next;
                }
            }
        }
    return dummy->next;
    }
};
main(){
    Solution ob;
    vector<int> v = {1,1,1,2,2,3,5,6,6,7,8};
    ListNode *head = make_list(v);
    print_list(ob.deleteDuplicates(head));
}

入力

[1,1,1,2,2,3,5,6,6,7,8]

出力

[3, 5, 7, 8]

このアルゴリズムの計算量は、リストを一度走査するだけでよいため O(n) となり、追加の領域もポインタ数個分のみで済むため、非常に効率的な手法といえます。

  1. 【Android】ソート済みリンクリストから重複を削除する方法をわかりやすく解説

    はじめに この記事では、Androidアプリ開発において、ソート済みのリンクリスト(LinkedList)から重複する要素を削除する方法を解説します。Java標準ライブラリのLinkedHashSetを活用すれば、要素の順序を保ったまま重複だけを簡単に除去できます。 実装手順 ステップ1:新規プロジェクトの作成 まず、Android Studioで新しいプロジェクトを作成します。メニューから「File」⇒「New Project」を選択し、必要な項目を入力してプロジェクトを生成してください。 ステップ2:レイアウトファイル(activity_main.xml)の編集 次に、res/layout

  2. C++でソート・回転済み連結リストの回転数を求める方法

    問題概要ある連結リストが与えられます。このリストは、最初に昇順にソートされ、その後 K 個のノード分だけ回転(ローテーション)されたものです。この記事の目的は、元のリストに対する回転数 K を求めることです。たとえば、以下のような連結リストが入力として与えられたとします。5 → 7 → 9 → 1 → 3このリストは、元のソート済みリスト1 → 3 → 5 → 7 → 9を 2 ノード分だけ回転したものになっています。つまり、この場合の K は 2 です。具体例で理解する例 1入力: リスト: 5 → 7 → 9 → 1 → 3出力:連結リストの要素: 5 7 9 1 3ソート・回転済み連結リ