Create 20251118.ts#23
Conversation
There was a problem hiding this comment.
コーディング面接対策:解答評価レポート
総合評価:3点 / 10点
良い点
-
基本的な構造は理解している
- ループで配列を走査する基本的なアプローチは正しい
- 変数の初期化が適切に行われている
-
コードの可読性
- 変数名が分かりやすい(maxProfit, minPrice, totalProfit)
- シンプルな構造でコードが読みやすい
改善が必要な点
1. アルゴリズムの根本的な誤り
あなたのコードには致命的な論理エラーがあります。このコードは正しい結果を返しません。
問題点:
- maxProfitを毎回リセットしていないため、利益の計算が正しく行われない
- 例:prices = [7,1,5,3,6,4]の場合
- i=1: maxProfit=0, minPrice=1, totalProfit=0
- i=2: maxProfit=4, minPrice=1, totalProfit=4
- i=3: maxProfit=4, minPrice=1, totalProfit=8 ← 誤り(正解は4)
- i=4: maxProfit=5, minPrice=1, totalProfit=13 ← 誤り(正解は7)
2. 問題の本質を理解していない
この問題の鍵は「複数回の売買が可能」という点です。最適戦略は:
- 価格が上昇するたびに、その差分を全て利益として加算する
- つまり、prices[i] > prices[i-1]のとき、差分prices[i] - prices[i-1]を加算
最適解との比較
最適解(Greedy Algorithm)
function maxProfit(prices: number[]): number {
let totalProfit = 0;
for (let i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
totalProfit += prices[i] - prices[i - 1];
}
}
return totalProfit;
}なぜこれが最適か:
- 価格が上昇する部分は全て利益として取得できる
- 下降する部分は取引しなければよい
- 「前日に買って今日売る」を繰り返すことで、全ての上昇トレンドを捉える
計算量の比較
| 項目 | あなたのコード | 最適解 |
|---|---|---|
| 時間計算量 | O(n) | O(n) |
| 空間計算量 | O(1) | O(1) |
| 正確性 | ❌ 不正確 | ✅ 正確 |
時間計算量は同じですが、あなたのコードは正しい結果を返さないため、実質的に使用できません。
あなたのコードの具体的な改善
改善版コード
function maxProfit(prices: number[]): number {
let totalProfit = 0;
// 隣接する日の価格差を見て、上昇分を全て加算
for (let i = 1; i < prices.length; i++) {
// 今日の価格が昨日より高ければ、その差分が利益
if (prices[i] > prices[i - 1]) {
totalProfit += prices[i] - prices[i - 1];
}
}
return totalProfit;
}変更点の説明
-
maxProfitとminPrice変数を削除
- これらは「1回だけ売買する」問題(Best Time to Buy and Sell Stock I)で使う変数
- 今回は複数回売買できるため不要
-
隣接要素の比較に変更
- prices[i] > prices[i-1]のときだけ差分を加算
- これで全ての上昇トレンドを捉えられる
-
条件分岐を簡略化
- 正の差分のみ加算することで、自動的に利益のある取引だけを行う
学習ポイント
1. 問題の制約を正確に理解する
- 「複数回売買可能」という条件をアルゴリズムに反映させる
- 異なる問題(Stock I vs Stock II)で異なるアプローチが必要
2. Greedy Algorithmの適用
- この問題はGreedy(貪欲法)で解ける典型例
- 「各ステップで最良の選択をする」→ 上昇する度に利益を取る
3. 具体例でのシミュレーション
prices = [7, 1, 5, 3, 6, 4]
日々の差分:
1-7 = -6 (無視)
5-1 = +4 (加算) → totalProfit = 4
3-5 = -2 (無視)
6-3 = +3 (加算) → totalProfit = 7
4-6 = -2 (無視)
最終利益:7
4. 関連問題との違い
- Stock I(1回のみ売買):最小値と最大値の差を求める
- Stock II(複数回売買):全ての上昇分を加算
- Stock III(最大2回):Dynamic Programming
- Stock IV(最大k回):Dynamic Programming
まとめ
あなたのコードはアルゴリズムの根本的な誤りがあり、正しい結果を返しません。問題の本質である「複数回売買可能」という条件を正しく理解し、Greedy Algorithmで解くべきです。
今後の学習ステップ:
- LeetCodeの「Best Time to Buy and Sell Stock」シリーズ全体を解く
- Greedy Algorithmの基本概念を学習する
- 具体的な例でコードをトレースし、期待値と実際の出力を比較する習慣をつける
- 問題の制約条件(1回のみ vs 複数回)がアルゴリズムにどう影響するか意識する
頑張ってください!この問題を理解できれば、Greedy Algorithmの重要な基礎が身につきます。
隣接する日だけの売買を考えれば良い理由
素晴らしい質問です!これは多くの人が疑問に思う重要なポイントです。
なぜ隣接する日だけで考えればいいのか?
数学的な証明
例を使って説明します。
例:prices = [1, 3, 5]
パターンA:1日目に買って3日目に売る
- 利益 = 5 - 1 = 4
パターンB:隣接する日で取引
- 1日目に買って2日目に売る:3 - 1 = 2
- 2日目に買って3日目に売る:5 - 3 = 2
- 合計利益 = 2 + 2 = 4
結果は同じ!
一般的な証明
離れた日付での取引:
買値:prices[i]
売値:prices[j] (j > i)
利益:prices[j] - prices[i]
これを隣接する日の取引に分解すると:
prices[j] - prices[i]
= (prices[i+1] - prices[i]) + (prices[i+2] - prices[i+1]) + ... + (prices[j] - prices[j-1])
つまり、どんな取引も隣接する日の取引の合計に分解できるのです!
具体例で確認
prices = [7, 1, 5, 3, 6, 4]
問題の例の解き方:
- 2日目(1)に買って3日目(5)に売る:5 - 1 = 4
- 4日目(3)に買って5日目(6)に売る:6 - 3 = 3
- 合計:7
隣接する日で考える:
- 1→5の取引を分解:
- 1→5 = (5-1) = (5-3) + (3-1) = 2 + 2 = 4
- でも、3は下がっているので実際は:(5-1) = 4だけ
- もっとシンプルに:
- 2日目→3日目:5 - 1 = 4(上昇)
- 3日目→4日目:3 - 5 = -2(下降、取引しない)
- 4日目→5日目:6 - 3 = 3(上昇)
- 5日目→6日目:4 - 6 = -2(下降、取引しない)
- 合計:4 + 3 = 7
なぜこのアプローチが最適なのか?
-
上昇部分は全て利益になる
- 隣接する日で上昇していれば、その差分は必ず利益になる
- 下降部分は取引しなければ損失を避けられる
-
取引回数に制限がない
- 同じ日に売買できるので、連続した上昇を細かく分割しても問題ない
- 1→5の取引は、1→3→5と分割しても同じ利益
-
手数料がない
- 実際の株式市場では手数料があるので離れた日の方が有利だが、この問題では手数料がない
- だから細かく分割しても不利にならない
視覚的な理解
価格: [1, 3, 2, 5, 4, 6]
6 ─────────────────●
5 ──────────●
4 ────────────────×─●
3 ───●
2 ──────×
1 ●
上昇部分(利益):
1→3: +2
2→5: +3
4→6: +2
合計: 7
これは以下と同じ:
1で買って6で売る: 6-1=5
でも途中で下がっているので、実際は:
(3-1) + (5-2) + (6-4) = 2+3+2 = 7
まとめ
離れた日の取引 = 隣接する日の取引の合計
なので、アルゴリズムとしては隣接する日だけチェックすれば十分です。これが Greedy Algorithm の美しさです!
あなたの疑問は非常に本質的で、この理解があれば問題を完全にマスターしたと言えます。素晴らしい質問でした!
Status
Wrong Answer
Time Taken
15 m 43 s