-
Notifications
You must be signed in to change notification settings - Fork 0
Code1.two sum #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| URL:https://leetcode.com/problems/two-sum/ | ||
|
|
||
| Arai 1 / 60問目 | ||
|
|
||
| # Step1 | ||
|
|
||
| - 使用言語:c++(普段はC言語などが主なためほぼC++初心者です) | ||
| - 所要時間:骨格部分は2~5分(STLの使い方を調べるのにはもっとかかりました) | ||
|
|
||
| no.1 | ||
| ```c++ | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| std::vector<int> twoSum(std::vector<int>& nums, int target) { | ||
| for (int i = 0; i < nums.size(); i++) { | ||
| for (int j = i + 1; j < nums.size(); j++) { | ||
| if (nums[i] + nums[j] == target) { | ||
| return {i, j}; | ||
| } | ||
| } | ||
| } | ||
| return {}; | ||
| } | ||
| }; | ||
| ``` | ||
| no.2 | ||
| ```c++ | ||
| #include <vector> | ||
| #include <map> | ||
|
|
||
| class Solution { | ||
| public: | ||
| std::vector<int> twoSum(std::vector<int>& nums, int target) { | ||
| std::map<int, int> map_to_complement_index; | ||
|
|
||
| for (int i = 0; i < nums.size(); ++i) { | ||
| int complement = target - nums[i]; | ||
| if (map_to_complement_index.find(complement) != map_to_complement_index.end()) { | ||
| return {map_to_complement_index[complement], i}; | ||
| } | ||
| map_to_complement_index[nums[i]] = i; | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
| }; | ||
| ``` | ||
| no.3 | ||
| ```c++ | ||
| #include <vector> | ||
| #include <set> | ||
|
|
||
| class Solution { | ||
| public: | ||
| std::vector<int> twoSum(std::vector<int>& nums, int target) { | ||
| std::set<int> numSet; | ||
|
|
||
| for (int i = 0; i < nums.size(); ++i) { | ||
| int complement = target - nums[i]; | ||
| if (numSet.find(complement) != numSet.end()) { | ||
| int complementIndex = std::distance(nums.begin(), std::find(nums.begin(), nums.end(), complement)); | ||
| return {complementIndex, i}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 変数名は lower_snake で書くのをよく見かけます。チームの平均的な書き方に合わせることをお勧めいたします。 参考までに Google C++ Style Guide へのリンクを貼ります。
ただし、上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 変数名のスタイルはいくつかありますが、統一してなかったのが痛手ですね。 |
||
| } | ||
| numSet.insert(nums[i]); | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
|
|
||
| ## 思考過程 | ||
| - 問題だけ読んだ時は、愚直な解答(no.1)はすぐ書けると考えました。 | ||
|
|
||
| - 何回かこの問題のレビューをみていたので、mapを使う方法もあると朧げながら覚えていた。unordered_map, mapなどの使い方をAIとリファレンスを言ったり来たりしてno.2は書きました。 | ||
| - 途中でmapの特徴などを見ていたら、set(集合)的なやつでも書けるのでは?と思いsetのリファレンスも見に行きました。 | ||
| →しかしsetは`set<T>`であるため値を放り込んでおくことしかできないと気づく。そうなるとcomplementは見つけられてもそこからインデックスを抽出するのにそのcomplementを探す線形探索が必要になる。これはAIに書いてもらった(no.3) | ||
|
|
||
| - ここら辺で問題を再整理。ほしいのは足すとtargetになるインデックスのペアなので、値をkey、インデックスをvalueにする形で調べた値は保管しておいて、ペアになる値があるか探すno.2はかなり自然に感じる。 | ||
| - setの回答は途中までいい感じで進めてるけど、いざペアが見つかった!となったあとにこの値って何番目だっけ?とインデックスを探しに行っている感じがちょっと素っ頓狂に見える。 | ||
|
|
||
| - あとはこのtwoSum関数がどこまで使われて、かつ速さが求められるか、チームのメンバのC++の理解などでどの方式にするかは違うかも。特に自分はまだC++に慣れてないため、no.2が理解したら自然に感じられるとはいえど、no.1の方が理解しやすい。ただ、最近C言語で配列でのループをみる時も、2重ループ程度でもえーっとこのi,jは初期値が何でどうゆう条件でループするんだっけと意外とワーキングメモリ使っていて読むの大変だなと思うときがある。多分慣れるとno.2の方がわかりやすいんだろうなと思う。 | ||
|
|
||
| # step2 | ||
|
|
||
| https://github.com/Miyamoto-tryk/leetcode-arai60/pull/1/files | ||
| https://github.com/Shinkomori19/arai60/pull/2/files | ||
| https://github.com/azriel1rf/leetcode-prep/pull/3 | ||
| - 他の方の実装を読む。見つからなかったときにどういう風に返すのが自然か。実際Step1を実際に手元で確かめることをしてた時、これreturn {};が返されたときってどう検知しようかと一瞬迷ったので自然な返り値は大事。 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 他の人のコードを読む時、細かい実装を把握するのもそうですが、パッとみてコードブロックがどれくらいの長さかも把握すると良いです。 練習をしていく中で、この問題/計算/主張なら、これくらいの分量であるという感覚が掴めると良いかと思います。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、大まかな枠の把握みたいなことですね?たしかに大まかな方針がたっても意外と細かい条件への対処で見通しとずれることはあるので意識したいと思います! |
||
| ```c++ | ||
| std::vector<int> result = solution.twoSum(nums, target); | ||
| if (result.size() != 2) { | ||
| std::cout << "No solution found." << std::endl; | ||
| return 1; | ||
| } | ||
| ``` | ||
| - pythonなどだとNoneを返す手がある。AIに聞いたらC++17から使えるようになったstd::optionalを使うとちょっと返り値の部分をいじる必要はあるもののstd::nulloptというnullっぽい値を返すことができるようだ。これはC言語でNULLを返しがちな自分としては好み。 | ||
| - {-1, -1}というようにこの問題ではあり得ない値のペアを返す案も見かけたが、今後データがマイナスまで伸びるかもしれないとか考えると使いづらいと思いました。 | ||
| - exceptionをthrowするのは、ちょっとこの関数を使うためだけに呼び出し元でtry catch書かせるのは違うかなという感覚があります。 | ||
|
|
||
|
|
||
| - C++だとunordered_mapかmapかという話がよく出てくる。自分はリファレンスを調べたときにunordered_mapがハッシュテーブル、mapが赤黒木という実装だと読んだ。自分の中でハッシュテーブルは(たとえばmodをとる場合などに)同じリストに配属されてしまうみたいなことがなければ、かなり早いものの、データによっては(最悪の場合)線形探索と同等になってしまうイメージ。要求とデータが上手い具合に重なれば選択肢となりそう。 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
C++ の規格では、時間計算量だけが規定されており、データ構造については言及がありません。 https://timsong-cpp.github.io/cppwp/n4950/associative.reqmts#general-65
https://timsong-cpp.github.io/cppwp/n4950/unord.req#general-97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (実際の実装は確かにハッシュテーブルと赤黒木であることが多いです。)
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. おそらくdiscordなどの情報とごっちゃになっていて日本語リファレンスを読んだときにそう読み取ってしまったようです。しっかり規格なども裏どりしていきたいです! 後からきてこの辺が気になった方へ(英語版のmapのリファレンスにはred-black treeへの言及があります。「一般的には赤黒木で実装されることが多い」) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. map と unordered_map のどちらを使うべきかについては、過去に何度かコメントが付けられています。探されることをお勧めいたします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます!探してみます! |
||
| - 一方で赤黒木はあまり詳しい実装方法は知らないが、平衡二分木の一つという話は知ってて、平衡二分木はきれいな二分木にするために毎回深さや層を調整するtreeだと理解していました。こちらのほうが平均的にそちらを選びました。 | ||
|
|
||
| https://github.com/irohafternoon/LeetCode/pull/13/files | ||
| - map_to_complement_indexという変数名は、to_complement_indexの部分はなるべく分かりやすいようにしたつもりだったがmap型の変数にmapとつけるのは冗長だと反省。num_to_index, num_to_indexes, num_to_indicesなどが多い。indexを複数形にするかは悩みどころ。 | ||
| - 一般的に複数形の方が有標なので、複数形にしていると何か意図があるのかもと考えてしまうかも。今回のようにすぐに中身がわかる(スコープがそんなに広くない)変数であれば、まあどっちでもいいかもですがnum_to_indexesなどだと一瞬1対多なのかと思ったり。 | ||
|
|
||
| ```c++ | ||
| #include <vector> | ||
| #include <map> | ||
|
|
||
| class solution { | ||
| public: | ||
| std::vector<int> twoSum(std::vector<int>& nums, int target) { | ||
| std::map<int, int> num_to_index; | ||
|
|
||
| for (int i = 0; i < nums.size(); i++) { | ||
| int complement = target - nums[i]; | ||
| if (num_to_index.find(complement) != num_to_index.end()) { | ||
| return {num_to_index[complement], i}; | ||
| } | ||
| num_to_index[nums[i]] = i; | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| step3 | ||
| - 3回書くと流石にすらすらかけるようになった。 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. どんどん先に進みましょう。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます!合間を見つけてどんどんやっていきたいです! |
||
|
|
||
| ```c++ | ||
| #include <vector> | ||
| #include <map> | ||
|
Comment on lines
+138
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここはアルファベティカルに並べることもありますね。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. あ、なるほど。アルファべティカルの方が検索性がよいですね(特にincludeが増えた場合) |
||
|
|
||
| class solution { | ||
| public: | ||
| std:vector<int> twoSum(std::vector<int>& nums, int target) { | ||
| std::map<int, int> num_to_index; | ||
|
|
||
| for (int i = 0; i < nums.size(); i++) { | ||
| int complement = target - nums[i]; | ||
|
|
||
| if (num_to_index.find(complement) != num_to_index.end()) { | ||
| return {num_to_index[complement], i}; | ||
| } | ||
| num_to_index[nums[i]] = i; | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (map_to_complement_index.containts(complement)) {としたほうがシンプルだと思います。あるいは、 2 度検索が走らないよう、
とするのもよいと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
containsは自分のすぐあとに解いた方が書いていて、見逃してた!となりました。
auto it = map_to_complement_index.find(complement); it != map_to_complement_index.end()もfindで見つけてきたものをうまく使っていてこれならfind使った意図がわかる感じですね
コメントありがとうございます!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hurukawa2121/leetcode#6 (comment)
調べなおしているときに出てきた話。findがありゃ十分だろというC++03以前の考えもわかるけど今回みたいなあるか否かで条件式に入れたい場合はcontainsやっぱり便利なのでC++20以降なら使っていきたい。