Skip to content

83.remove duplicates from sorted list#3

Open
nicah4o wants to merge 3 commits into
mainfrom
83.remove-duplicates-from-sorted-list
Open

83.remove duplicates from sorted list#3
nicah4o wants to merge 3 commits into
mainfrom
83.remove-duplicates-from-sorted-list

Conversation

@nicah4o
Copy link
Copy Markdown
Owner

@nicah4o nicah4o commented May 3, 2026

予めソートされてることに気づかずにやり始めてしまった。
つまり[1,1,2,1]とかが出るのかと勘違いした。
この場合は二つのポインタとハッシュセットが必要だと思った。
前に出るものと後ろに出るもの。それでハッシュセットを前の値で検索してあったら後ろから書き換える。
Copy link
Copy Markdown

@h-masder h-masder May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2つのポインタはどんなイメージでしょうか。私は、一つでいいかなーと思いました。

・リストノードを前から順にみる。
・HashSetをみて、今のノードの値が見たことないものだったとき、そのノードの値をHashSetに入れる。その値は消さずに次に行く。
・HashSetをみて、今のノードが見たことあったとき、そのノードを消す

追記: ↑こちら、読んでいったら「ポインタは2ついらない」と言及されていましたね。

Copy link
Copy Markdown

@h-masder h-masder May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (head == nullptr){
            return head;
        }
        unordered_set<int> seen;
        ListNode *node = head;
        seen.insert(node->val);
        while (node->next != nullptr) {
            if (seen.find(node->next->val) != seen.end()) {
                node->next = node->next->next;
            }
            else {
                seen.insert(node->next->val);
                node = node->next;
            }
        }
        return head;
    }
};

ちょっと書いてみました。

Copy link
Copy Markdown

@h-masder h-masder May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

話は逸れますが、
もしソートされていなければ、ソートしてからこの問題の解法を適用すると良さそうですね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

わざわざコメントだけでなくコードまで書いてくださりありがとうございます。
すぐ答えを見てしまい検討しなかったのですごく参考になります。

イメージの問題として、続きの82番と同様の動作を想定しての間違ったファーストインプレッションです。
82: https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/description/
->nextと本体のポインタを動かすことの区別がついていなかったのでこのような書き方になりました。

```cpp
private:
ListNode* deleteloop(ListNode* head) {
ListNode* temp = head;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変数名は意図が明確になるといいと思います。
nodeなどはいかがでしょうか

}
```
89行目のreturnとelse以下の代入がわからなかったからgemini使用した。
で、returnは同じ場所で二度以上deleteする機能であり、else以下の代入はもしtemp->nextが存在しなくなってた場合に繋げる機能がある。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっと読み取れませんでした。
このあたり、もう少し説明いただけるとありがたいです!

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まず、returnは87行目ですね。書き間違いです。
h-masderさんが以下で書かれてるコードのcontinueに当たる部分を再帰呼び出しにしており、else以下では続きの問題82を想定して1,2,2,3が1,3になったときにtemp->nextに当たる2がないからこれを挿入しないと困るよといったgeminiの説明を写した形ですね。
参考:hajimeito1108/arai60#6 (comment)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっといいお返事が思いつかないのですが、書きます。

h-masderさんが以下で書かれてるコードのcontinueに当たる部分を再帰呼び出し

↑こちらは、これでいいと思うのですが、

else以下では続きの問題82を想定して1,2,2,3が1,3になったときにtemp->nextに当たる2がないからこれを挿入しないと困る

↑こちらは、変な感じがします。

Copy link
Copy Markdown

@h-masder h-masder May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(これ以降から推測が入ってくるのでいろいろと間違っているかもしれません)

else以下では続きの問題82を想定して1,2,2,3が1,3になったとき

をみて 90行のtemp->next = deleteloop(temp->next);return deleteloop(temp->next);のとき、1,2,2,3が1,3になってしまうことを想定していると解釈しました。

もしそうだとすると、1, 2, 2, 3のときの結果は1, 3ではなく、3になります。もうすこしちゃんというと、1, 2, 3のように重複を消せているのですが、returnされるポインタが3になります。

Copy link
Copy Markdown

@h-masder h-masder May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再帰には、呼び出し元の関数(A)と呼び出された関数(B)があると思います。
return deleteloop(temp);という書き方は、Bが、Aに何を返すのか
temp->next = deleteloop(temp->next);という書き方は、Aが、Bから何を受け取るか
と考えることができます。

AがBから返り値を受け取らないと、ポインタがリストノードを走査したっきり帰ってこないです。

temp->nextに当たる2がないからこれを挿入しないと困るというより、ポインタをheadまで戻したいのでそう書きます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

返信ありがとうございます。
そうですね、temp->next=deleteloop(temp->next);の下にreturn temp;を置いとくべきですね。
コメント頂いた後、82でもこの再帰関数の再利用ができるか試したのですが、やはりそのような場合にはreturn temp;に当たるものが必要でした。

Copy link
Copy Markdown
Owner Author

@nicah4o nicah4o May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。理解が深まります。

private:
    ListNode* recursion(ListNode* head) {
        if (head && head->next) {
            if (head->val == head->next->val) {
                while (head->next && head->val == head->next->val) {
                    head->next = head->next->next;
                }
                return recursion(head->next);
            } else {
                head->next = recursion(head->next);
                return head;
            }
        }
        return head;
    }

Copy link
Copy Markdown

@h-masder h-masder May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

上記のコードは、
return recursion(head->next)retrun recursion(head)ですかね。

temp->next=deleteloop(temp->next);の下にreturn temp;を置いとくべきですね。

私のイメージと少し違ったのでコードを載せておきます。
「ポインタがリストノードを走査したっきり帰ってこないです。」に対する対処法の一つは、ポインタをリストの末尾から先頭まで返すイメージです。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (head && head->next) {
            if (head->val == head->next->val) {
                while (head->next && head->val == head->next->val) {
                    head->next = head->next->next;
                }
                head =  deleteDuplicates(head);
            } else {
                head->next = deleteDuplicates(head->next);
            }
        }
        return head;
    }
};

もしくは、helperのようなものを挟むかですね。これならポインタを返す必要はないです。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        helper(head);
        return head;
    }
private:
    void helper(ListNode* node) {
        if (node == nullptr || node->next == nullptr){
            return;
        } 
        if (node->val == node->next->val) {
            node->next = node->next->next;
            helper(node);
        } else {
            helper(node->next);
        }
    }
};

Copy link
Copy Markdown

@h-masder h-masder May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

82でもこの再帰関数の再利用ができるか試したのですが、やはりそのような場合にはreturn temp;に当たるものが必要でした。

たくさん試して慣れていただくのが良いかと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
ポインタを返すのではなく最後の地点の返り値から遡及的にheadまで巻き戻るような形ですね。
今後の参考にさせていただきます。

こういう基本仕様を141やったときに理解しておくような勉強をする必要がある。

## step2
他の方の回答みた。普通の解き方は以下のようになる。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考え方はいくつかあると思います。
素直に考えてコードにできれば一番いいですね。
私は他の方に以下のようなコメントをしました。
hajimeito1108/arai60#6 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants