From 8fb665f9a84b25cc4c3a59dfabab8355c4b49295 Mon Sep 17 00:00:00 2001 From: potrue <126231160+potrue@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:25:17 +0900 Subject: [PATCH] 153. Find Minimum in Rotated Sorted Array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 問題文: https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/ --- 153/153.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 153/153.md diff --git a/153/153.md b/153/153.md new file mode 100644 index 0000000..58c2c0e --- /dev/null +++ b/153/153.md @@ -0,0 +1,102 @@ +## 何も見ずに解いてみる + +```cpp +class Solution { +public: + int findMin(vector& nums) { + int left = 0; + int right = nums.size() - 1; + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] > nums[right]) left = mid + 1; + if (nums[mid] < nums[right]) right = mid; + } + return nums[left]; + } +}; +``` + +## 他の人のコードを見てみる + +https://github.com/tokuhirat/LeetCode/pull/42/files +https://github.com/ryosuketc/leetcode_arai60/pull/31/files +https://github.com/takuya576/leetcode/pull/2/files +https://github.com/Ryotaro25/leetcode_first60/pull/46/files +https://github.com/goto-untrapped/Arai60/pull/24/files + +numsの最初や最後の要素をnums[mid]との比較対象に選んでいるコードもあったが、個人的には[left, right]に入っていない要素はループ中考えなくてよい感じにしたいと思いました。再帰関数みたいなイメージです。 +(そういえばこれ書いているときは思いつきませんでしたが再帰関数でも書けますね) + +ライブラリ関数を使って書けないかなと考えていたら、[std::partition_point](https://cpprefjp.github.io/reference/algorithm/partition_point.html) というのを見つけました。 +これを使うと「nums[0]未満の最初の要素を見つける、なかったらnums[0]を返す」というアプローチで書けそう。 + +```cpp +class Solution { +public: + int findMin(vector& nums) { + auto it = std::partition_point(nums.begin(), nums.end(), [&](int x) { + return x >= nums[0]; + }); + if (it != nums.end()) { + return *it; + } else { + return nums[0]; + } + } +}; +``` + +値がユニークでなかったとき(nums[mid]とnums[right]が同じだったとき)はどうするべきだろう? +[std::invalid_argument](https://learn.microsoft.com/ja-jp/cpp/standard-library/invalid-argument-class?view=msvc-170) をthrowしてみる? +geminiに相談してみたらrightを1個下げることで正しい答えが求まるようになると言われました。なるほど。 + +ロバストにするならこんな感じでしょうか? + +```cpp +class Solution { +public: + int findMin(std::vector& nums) { + if (nums.empty()) throw std::invalid_argument("nums are empty"); + int left = 0; + int right = nums.size() - 1; + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] > nums[right]) { + left = mid + 1; + } else if (nums[mid] < nums[right]) { + right = mid; + } else { // if (nums[mid] == nums[right]) + --right; + } + } + return nums[left]; + } +}; +``` + +ただこれでも(uniqueでないとしても)ソートされてることは仮定しないといけないので、実際にはemptyかどうかのチェックぐらいでいいかな? +emptyのとき、indexを返すだけだったら-1でも返しておけばいいかなと思ったが、中身を返すとなるとエラーをthrowするぐらいしかないかもしれない。intの上限を返すのもちょっと変な気がする。 + + +## 最終コード + +条件分岐って全部単体のifで回すよりelseとかcontinueとかつけておいた方が余計な条件分岐の判定が少なくなってちょっと早くなるとかあるんでしょうか? +コンパイラが勝手に最適化してくれたりすることもあるんですかね。 +もしかしたらこのelseは少し見づらいかも・・・ + +```cpp +class Solution { +public: + int findMin(std::vector& nums) { + if (nums.empty()) throw std::invalid_argument("nums is empty"); + int left = 0; + int right = nums.size() - 1; + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] > nums[right]) left = mid + 1; + else right = mid; + } + return nums[left]; + } +}; +```