007 — Best Time to Buy and Sell Stock¶
Source¶
- Original source: Classic running-min / one-pass exercise
- Platform: LeetCode — https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
- Topic: array
- Difficulty: Easy
- Pattern: running min (Kadane-flavored one pass)
- Basic idea inherited from: prefix-min thinking, math level 5 — "এ পর্যন্ত সবচেয়ে ছোট কত দেখেছি" মনে রাখা
1. Problem in simple words¶
একটা array prices দেওয়া, যেখানে prices[i] হলো i-তম দিনে একটা share-এর দাম। তুমি একবার কিনে পরে একবার বেচতে পারো (আগে কিনতে হবে, পরে বেচতে)। সবচেয়ে বেশি কত profit করা সম্ভব? কোনো profit সম্ভব না হলে 0।
2. Which basic idea does this inherit from?¶
ভিত math level 5-এর prefix-min thinking: আজকের দিনে বেচলে সেরা profit = আজকের দাম − আগের দিনগুলোর সবচেয়ে কম দাম। তাই বাঁ থেকে হাঁটতে হাঁটতে "এ পর্যন্ত দেখা minimum" মনে রাখলেই হয়। গভীর derivation: ../../01-math-based-programming-fundamentals/05-prefix-difference-contribution/।
3. What is the hidden math or logic?¶
লুকানো logic: max profit = max over j ( prices[j] - min over i<j prices[i] )। অর্থাৎ প্রতিটা "বেচার দিন" j-এর জন্য সেরা "কেনার দিন" হলো তার আগের সবচেয়ে সস্তা দিন। এই দুটো (running min আর running best) একসাথে এক pass-এ track করা যায় — এটাই Kadane-এর চিন্তার ছোট ভাই।
4. Which data structure is needed?¶
কোনো data structure লাগে না — শুধু দুটো scalar variable: min_price (এ পর্যন্ত সবচেয়ে কম দাম) আর best (এ পর্যন্ত সেরা profit)। O(1) extra space।
5. Why this data structure fits¶
পুরো prefix-min array জমিয়ে রাখার দরকার নেই — যেকোনো দিনে শুধু এই মুহূর্ত পর্যন্ত minimum-টুকুই দরকার, যা একটা variable-এ ধরে রাখা যায়। array-র O(1) sequential read + দুটো variable update = সবচেয়ে হালকা সমাধান।
6. Real-life analogy¶
তুমি প্রতিদিন একটা জিনিসের দাম খাতায় টুকছ। আজ যদি বেচতে, তোমার সেরা লাভ = আজকের দাম − এ যাবৎ দেখা সবচেয়ে সস্তা দিন। তাই শুধু দুটো জিনিস মনে রাখো: "এখন পর্যন্ত সবচেয়ে সস্তা কত" আর "এখন পর্যন্ত সেরা লাভ কত"।
7. Visual explanation¶
prices = [7, 1, 5, 3, 6, 4]:
min_price শুরুতে +inf, best=0
p=7: 7<inf -> min=7
p=1: 1<7 -> min=1
p=5: 5-1=4 > 0 -> best=4
p=3: 3-1=2 (best 4 অপরিবর্তিত)
p=6: 6-1=5 > 4 -> best=5
p=4: 4-1=3 (best 5 অপরিবর্তিত)
answer: best = 5
8. Example input and output¶
Input : [7, 1, 5, 3, 6, 4] -> 5 (1-এ কেনো, 6-এ বেচো)
Input : [7, 6, 4, 3, 1] -> 0 (দাম শুধু কমেছে, লাভ নেই)
Edge case 1 (খালি): [] -> 0
Edge case 2 (V-shape): [2,4,1] -> 2 (2-এ কেনো, 4-এ বেচো)
9. Brute force thinking¶
প্রথম সরল চিন্তা: প্রতিটা জোড়া দিন (i, j) যেখানে i < j, profit prices[j] - prices[i] হিসাব করে সবচেয়ে বড়টা রাখি — দুটো nested loop।
10. Why brute force is slow¶
দুটো nested loop মানে O(n^2) জোড়া। বারবার একই কাজ: প্রতিটা বেচার দিনের জন্য আগের সব দিন আবার scan করে minimum খুঁজছি — অথচ সেই minimum এক pass-এ চলতে চলতেই track করা যায়।
11. Key observation¶
মূল observation: একটা বেচার দিন j fix করলে, সেরা কেনার দিন সবসময় তার আগের সবচেয়ে সস্তা দিন — আর কিছু না। তাই সব জোড়া না দেখে শুধু running minimum রাখলেই প্রতিটা দিনের সেরা profit পাওয়া যায়।
12. Optimized intuition¶
বাঁ থেকে ডানে একবার হাঁটো। প্রতিটা দাম p-এ: যদি p এ-যাবৎ minimum-এর চেয়ে কম, min_price update করো (নতুন সস্তা কেনার দিন)। নাহলে p - min_price দিয়ে best update করার চেষ্টা করো (আজ বেচলে কত লাভ)। এক pass, O(n)।
13. Thinking tweak¶
মোচড়: "সব (কেনা, বেচা) জোড়া try করব" (O(n^2)) ভুলে গিয়ে ভাবো "আজ বেচলে সেরা লাভ = আজকের দাম − এ যাবৎ সবচেয়ে সস্তা।" একটাই running minimum পুরো ভেতরের loop-টা মুছে দেয়।
14. Step-by-step dry run¶
Input [2, 4, 1]:
- শুরু:
min_price=+inf,best=0। p=2:2 < inf→min_price=2।p=4:4 < 2মিথ্যা →4 - 2 = 2 > best→best=2।p=1:1 < 2সত্য →min_price=1(best 2 অপরিবর্তিত)।- ফল:
best = 2।
15. Python solution¶
def max_profit(prices):
min_price = float("inf") # এ যাবৎ সবচেয়ে কম দাম
best = 0 # এ যাবৎ সেরা profit
for p in prices:
if p < min_price:
min_price = p # নতুন সস্তা কেনার দিন
elif p - min_price > best:
best = p - min_price # আজ বেচলে আরো ভালো লাভ
return best
# ---- tests ----
assert max_profit([7, 1, 5, 3, 6, 4]) == 5
assert max_profit([7, 6, 4, 3, 1]) == 0 # লাভ নেই
assert max_profit([]) == 0 # খালি
assert max_profit([2, 4, 1]) == 2
print("সব test pass!")
16. Line-by-line code explanation¶
min_price = float("inf")— শুরুতে কোনো দাম দেখিনি, তাই "অসীম বড়" ধরি যাতে প্রথম দামই minimum হয়।best = 0— কোনো লাভ না হলে উত্তর 0 (কিছু না কেনা)।if p < min_price— আজকের দাম এ যাবৎ সবচেয়ে সস্তা হলে কেনার দিন update।elif p - min_price > best— নাহলে আজ বেচলে লাভ আগের সেরা ছাড়াবে কিনা দেখি।return best— পুরো pass শেষে সেরা সম্ভব profit।
17. Output walkthrough¶
[7,1,5,3,6,4]: min নামতে নামতে 1, তারপর 6-এর দিন 6-1=5 সেরা → 5, assert pass। শুধু কমতে থাকা array-তে best 0-ই থাকে; খালি array-তে loop চলে না, 0; [2,4,1] দেয় 2। শেষে print: সব test pass!।
18. Time complexity¶
O(n) — array-তে একবার হাঁটা; প্রতিটা step-এ O(1) compare/update।
19. Space complexity¶
O(1) — শুধু দুটো scalar (min_price, best)।
20. Common mistakes¶
best-কে negative হতে দেওয়া — কোনো লাভ না হলে উত্তর 0 (কিছু না করা), negative নয়।- আগে বেচে পরে কেনার ভুল — running minimum সবসময় current দিনের আগের দাম থেকে আসে বলে এই code-এ এটা আপনাআপনি ঠিক থাকে।
- প্রতিটা দিনে
min_priceআরbestদুটোই একসাথে update করার চেষ্টা — যেদিন নতুন min, সেদিন বেচলে লাভ ≤ 0, তাইelifযথেষ্ট।
21. Which basic problem this inherits from¶
ভিত্তি: prefix-min / running aggregate (math level 5)। এটা Kadane (maximum subarray, #11)-এর জমজ — "best ending here" বনাম "best so far"। chapter-এর ../patterns.md-এর "Pattern 7 — Kadane's Algorithm" দেখো।
22. Similar harder problems¶
- Best Time to Buy and Sell Stock II (যত খুশি transaction) — https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/
- Maximum Subarray (Kadane) — https://leetcode.com/problems/maximum-subarray/ (#11)
- Best Time to Buy and Sell Stock with Cooldown — https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
23. Pattern learned¶
Running min + running best (one pass): prefix-aggregate মনে রেখে প্রতিটা index-এ local সেরা decision — O(n) time, O(1) space, Kadane-ঘরানার।
24. Final summary¶
ভবিষ্যতের আমাকে: "max profit = আজকের দাম − এ যাবৎ minimum; min আর best দুটোই এক pass-এ রাখো।" "best buy/sell" বা "max difference (later − earlier)" দেখলেই running-min মনে করবে।
25. JavaScript solution (with test cases)¶
running min + best — এক pass-এ "এ যাবৎ সবচেয়ে সস্তা" আর "এ যাবৎ সেরা profit" track করি।
// throwing assert — fail করলে থেমে error দেবে
const assert = (cond, msg = "") => { if (!cond) throw new Error("FAIL " + msg); };
function maxProfit(prices) {
let minPrice = Infinity; // এ যাবৎ সবচেয়ে কম দাম
let best = 0; // এ যাবৎ সেরা profit (লাভ না হলে 0)
for (const p of prices) {
if (p < minPrice) minPrice = p; // নতুন সস্তা কেনার দিন
else if (p - minPrice > best) best = p - minPrice; // আজ বেচলে ভালো লাভ?
}
return best;
}
// ---- test cases (file-এর example + edge case) ----
assert(maxProfit([7, 1, 5, 3, 6, 4]) === 5, "1-এ কেনো, 6-এ বেচো");
assert(maxProfit([7, 6, 4, 3, 1]) === 0, "শুধু কমেছে → 0");
assert(maxProfit([]) === 0, "খালি");
assert(maxProfit([2, 4, 1]) === 2, "V-shape");
assert(maxProfit([1]) === 0, "এক দিন → লাভ নেই");
assert(maxProfit([3, 3, 3]) === 0, "সমান দাম → 0");
console.log("সব JS test pass!");
JS টীকা: "অসীম বড়" শুরু হিসেবে
Infinity(Pythonfloat("inf")-এর বদলি), যাতে প্রথম দামই minPrice হয়।best0 থেকে শুরু — লাভ না হলে উত্তর 0 (কিছু না কেনা), কখনো negative নয়।else ifযথেষ্ট: যেদিন নতুন min, সেদিন বেচলে লাভ ≤ 0।
26. TypeScript solution (with test cases)¶
function maxProfit(prices: number[]): number {
let minPrice = Infinity;
let best = 0;
for (const p of prices) {
if (p < minPrice) minPrice = p;
else if (p - minPrice > best) best = p - minPrice;
}
return best;
}
function expect(actual: number, expected: number, msg = ""): void {
if (actual !== expected) throw new Error(`FAIL ${msg}: ${actual}`);
}
const cases: ReadonlyArray<readonly [number[], number]> = [
[[7, 1, 5, 3, 6, 4], 5], [[7, 6, 4, 3, 1], 0], [[], 0],
[[2, 4, 1], 2], [[1], 0], [[3, 3, 3], 0],
];
for (const [arr, want] of cases) expect(maxProfit(arr), want, `[${arr}]`);
console.log("সব TS test pass!");
TS টীকা:
prices: number[]ও returnnumberdeclare করায় ভুল ধরনের input বাদ, আর profit সবসময় number — empty array-তেও 0 ফেরে, type ভাঙে না।
27. কোথায় কাজে লাগে (real-world use)¶
- Best buy/sell / max gain: কোনো time-series-এ "পরে − আগের সবচেয়ে ছোট" সর্বোচ্চ পার্থক্য — দাম, metric, score।
- Running min/max tracking: dashboard-এ চলমান lowest/peak, drawdown হিসাব (finance, monitoring)।
- One-pass aggregate: পুরো history না রেখে শুধু দরকারি running state — stream/online algorithm।
- Kadane-গোত্রের decision: "best ending here vs best so far" — maximum subarray, max profit with cooldown-এর ভিত্তি। মূল pattern — "prefix-min মনে রেখে প্রতিটা index-এ local সেরা সিদ্ধান্ত (one pass, O(1) space)" — running aggregate, Kadane, sliding optimum জুড়ে বড় ছবিতে ফেরে।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" দাবি নেই — এটা একটা common interview pattern।