83.remove duplicates from sorted list#3
Conversation
| 予めソートされてることに気づかずにやり始めてしまった。 | ||
| つまり[1,1,2,1]とかが出るのかと勘違いした。 | ||
| この場合は二つのポインタとハッシュセットが必要だと思った。 | ||
| 前に出るものと後ろに出るもの。それでハッシュセットを前の値で検索してあったら後ろから書き換える。 |
There was a problem hiding this comment.
2つのポインタはどんなイメージでしょうか。私は、一つでいいかなーと思いました。
・リストノードを前から順にみる。
・HashSetをみて、今のノードの値が見たことないものだったとき、そのノードの値をHashSetに入れる。その値は消さずに次に行く。
・HashSetをみて、今のノードが見たことあったとき、そのノードを消す
追記: ↑こちら、読んでいったら「ポインタは2ついらない」と言及されていましたね。
There was a problem hiding this comment.
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;
}
};ちょっと書いてみました。
There was a problem hiding this comment.
話は逸れますが、
もしソートされていなければ、ソートしてからこの問題の解法を適用すると良さそうですね。
There was a problem hiding this comment.
わざわざコメントだけでなくコードまで書いてくださりありがとうございます。
すぐ答えを見てしまい検討しなかったのですごく参考になります。
イメージの問題として、続きの82番と同様の動作を想定しての間違ったファーストインプレッションです。
82: https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/description/
->nextと本体のポインタを動かすことの区別がついていなかったのでこのような書き方になりました。
| ```cpp | ||
| private: | ||
| ListNode* deleteloop(ListNode* head) { | ||
| ListNode* temp = head; |
| } | ||
| ``` | ||
| 89行目のreturnとelse以下の代入がわからなかったからgemini使用した。 | ||
| で、returnは同じ場所で二度以上deleteする機能であり、else以下の代入はもしtemp->nextが存在しなくなってた場合に繋げる機能がある。 |
There was a problem hiding this comment.
ちょっと読み取れませんでした。
このあたり、もう少し説明いただけるとありがたいです!
There was a problem hiding this comment.
まず、returnは87行目ですね。書き間違いです。
h-masderさんが以下で書かれてるコードのcontinueに当たる部分を再帰呼び出しにしており、else以下では続きの問題82を想定して1,2,2,3が1,3になったときにtemp->nextに当たる2がないからこれを挿入しないと困るよといったgeminiの説明を写した形ですね。
参考:hajimeito1108/arai60#6 (comment)
There was a problem hiding this comment.
ちょっといいお返事が思いつかないのですが、書きます。
h-masderさんが以下で書かれてるコードのcontinueに当たる部分を再帰呼び出し
↑こちらは、これでいいと思うのですが、
else以下では続きの問題82を想定して1,2,2,3が1,3になったときにtemp->nextに当たる2がないからこれを挿入しないと困る
↑こちらは、変な感じがします。
There was a problem hiding this comment.
(これ以降から推測が入ってくるのでいろいろと間違っているかもしれません)
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になります。
There was a problem hiding this comment.
再帰には、呼び出し元の関数(A)と呼び出された関数(B)があると思います。
return deleteloop(temp);という書き方は、Bが、Aに何を返すのか
temp->next = deleteloop(temp->next);という書き方は、Aが、Bから何を受け取るか
と考えることができます。
AがBから返り値を受け取らないと、ポインタがリストノードを走査したっきり帰ってこないです。
temp->nextに当たる2がないからこれを挿入しないと困るというより、ポインタをheadまで戻したいのでそう書きます。
There was a problem hiding this comment.
返信ありがとうございます。
そうですね、temp->next=deleteloop(temp->next);の下にreturn temp;を置いとくべきですね。
コメント頂いた後、82でもこの再帰関数の再利用ができるか試したのですが、やはりそのような場合にはreturn temp;に当たるものが必要でした。
There was a problem hiding this comment.
ありがとうございます。理解が深まります。
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;
}There was a problem hiding this comment.
上記のコードは、
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);
}
}
};There was a problem hiding this comment.
82でもこの再帰関数の再利用ができるか試したのですが、やはりそのような場合にはreturn temp;に当たるものが必要でした。
たくさん試して慣れていただくのが良いかと思います。
There was a problem hiding this comment.
ありがとうございます。
ポインタを返すのではなく最後の地点の返り値から遡及的にheadまで巻き戻るような形ですね。
今後の参考にさせていただきます。
| こういう基本仕様を141やったときに理解しておくような勉強をする必要がある。 | ||
|
|
||
| ## step2 | ||
| 他の方の回答みた。普通の解き方は以下のようになる。 |
There was a problem hiding this comment.
考え方はいくつかあると思います。
素直に考えてコードにできれば一番いいですね。
私は他の方に以下のようなコメントをしました。
hajimeito1108/arai60#6 (comment)
This problem: https://leetcode.com/problems/remove-duplicates-from-sorted-list/
Next problem: https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/description/