အခန်း ၉ - Binary Search
Binary Search (တစ်ဝက်စီ ခွဲခြမ်းရှာဖွေခြင်း) ဆိုတာ Sorted (စီထားပြီးသား) ဖြစ်နေတဲ့ linear data structure (ဥပမာ - Array) တစ်ခုထဲကနေ ကိုယ်လိုချင်တဲ့ တန်ဖိုး (Target) ကို အလွန် လျင်မြန်စွာ ရှာဖွေနိုင်တဲ့ နည်းလမ်း တစ်ခု ဖြစ်ပါတယ်။
ပုစ္ဆာတွေမှာ အချိန်ကြာမြင့်မှု (Time Complexity) ကို အထိ လျှော့ချချင်တဲ့အခါ Binary Search ကို မဖြစ်မနေ သုံးကြရပါတယ်။
Binary Search ၏ အဓိက အယူအဆ
Binary Search ရဲ့ အလုပ်လုပ်ပုံကို နားလည်ဖို့ စီထားပြီးသား Array တစ်ခုထဲမှာ target ရှာတာ ကို တိုက်ရိုက်ကြည့်ရအောင်။
ဥပမာ nums = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] ဖြစ်ပြီး target = 23 ကို ရှာမယ်ဆိုပါစို့။
Binary Search မှာ left နဲ့ right က လက်ရှိရှာနေတဲ့ နယ်ပယ်ကို သတ်မှတ်ပါတယ်။ mid က အဲ့ဒီနယ်ပယ်ရဲ့ အလယ်နေရာပါ။ nums[mid] ကို target နဲ့ နှိုင်းယှဉ်ပြီး target မရှိနိုင်တဲ့ တစ်ဝက်ကို တစ်ခါတည်း ဖယ်ထုတ်သွားပါတယ်။

အဆင့်လိုက် ကြည့်ပါ။
| အဆင့် | ရှာနေသည့်နယ်ပယ် | mid တန်ဖိုး |
နှိုင်းယှဉ်ချက် | ဆက်လုပ်မည့်အရာ |
|---|---|---|---|---|
| ၁ | [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] |
16 |
16 < 23 |
23 က ပိုကြီးလို့ ဘယ်ဘက်တစ်ဝက် [2, 5, 8, 12, 16] ကို ဖယ် |
| ၂ | [23, 38, 56, 72, 91] |
56 |
56 > 23 |
23 က ပိုငယ်လို့ ညာဘက်တစ်ဝက် [56, 72, 91] ကို ဖယ် |
| ၃ | [23, 38] |
23 |
23 == 23 |
တွေ့ပြီ၊ index 5 ကို return ပြန် |
ဒီ table မှာ အဓိက သတိထားရမယ့်အချက်က တစ်ကြိမ်စစ်ပြီးတိုင်း ရှာစရာနယ်ပယ်က တစ်ဝက်လျော့သွားတာ ပါ။
ပထမအဆင့်မှာ 16 ကို စစ်ပါတယ်။ target = 23 က 16 ထက် ပိုကြီးတဲ့အတွက် 16 အပါအဝင် ဘယ်ဘက်ခြမ်းမှာ target မရှိနိုင်တော့ပါဘူး။ ဒါကြောင့် ဘယ်ဘက်တစ်ဝက်ကို တစ်ခါတည်း ဖယ်ပါတယ်။
ဒုတိယအဆင့်မှာ 56 ကို စစ်ပါတယ်။ target = 23 က 56 ထက် ပိုငယ်တဲ့အတွက် 56 အပါအဝင် ညာဘက်ခြမ်းမှာ target မရှိနိုင်တော့ပါဘူး။ ဒါကြောင့် ညာဘက်တစ်ဝက်ကို ထပ်ဖယ်ပါတယ်။
နောက်ဆုံး ကျန်တဲ့နယ်ပယ် [23, 38] ထဲမှာ 23 ကို စစ်လိုက်တဲ့အခါ target ကို တွေ့သွားပါတယ်။
အခြေခံဆုံး စည်းမျဉ်းကို ဒီလို မှတ်နိုင်ပါတယ်။
| အခြေအနေ | ဘာလုပ်မလဲ |
|---|---|
nums[mid] == target |
အဖြေတွေ့ပြီ၊ mid ကို return ပြန် |
nums[mid] < target |
target က ညာဘက်မှာပဲ ရှိနိုင်လို့ left = mid + 1 |
nums[mid] > target |
target က ဘယ်ဘက်မှာပဲ ရှိနိုင်လို့ right = mid - 1 |
၏ အစွမ်းထက်ပုံ
အချက်အလက် အရေအတွက်ဟာ မည်မျှပင် များပြားပါစေ၊ Binary Search ဟာ တစ်ကြိမ်လျှင် ရှာဖွေရမည့် နယ်ပယ်ကို ထက်ဝက်တိတိ လျှော့ချသွားနိုင်တဲ့အတွက် အလွန် အစွမ်းထက်ပါတယ်။
- အချက်အလက် အရေအတွက် ၁,၀၀၀ (တစ်ထောင်) ရှိလျှင် အဆိုးဆုံး ၁၀ ကြိမ်သာ ရှာရန် လိုအပ်သည်။
- အချက်အလက် အရေအတွက် ၁,၀၀၀,၀၀၀ (တစ်သန်း) ရှိလျှင် အဆိုးဆုံး ၂၀ ကြိမ်သာ ရှာရန် လိုအပ်သည်။
- အချက်အလက် အရေအတွက် ၄,၀၀၀,၀၀၀,၀၀၀ (လေးဘီလီယံ) ရှိလျှင် အဆိုးဆုံး ၃၂ ကြိမ်သာ ရှာရန် လိုအပ်သည်။
Binary Search တွင် သိထားရမည့် အခြေခံစည်းမျဉ်းများ
Binary Search ကို မှန်ကန်စွာ ရေးသားနိုင်ဖို့ အဓိက အချက် ၄ ချက်ကို နားလည်ထားရပါမယ်။
၁။ Pointers သုံးခု သတ်မှတ်ခြင်း
ရှာဖွေရမည့် ဘောင်ကို သတ်မှတ်ရန် left (အစ) နှင့် right (အဆုံး) pointer နှစ်ခု လိုအပ်ပြီး၊ နှိုင်းယှဉ်ရန် mid (အလယ်) pointer တစ်ခု လိုအပ်ပါသည်။
left=right=
၂။ Integer Overflow ကို ရှောင်လွှဲခြင်း (Critical Tip)
အလယ်ကိန်း mid ကို ရှာတဲ့အခါ ပုံမှန်အားဖြင့် အောက်ပါအတိုင်း တွက်လေ့ရှိကြပါတယ် —
int mid = (left + right) / 2;
ဒီနည်းလမ်းက သာမန်အချိန်မှာ အလုပ်လုပ်ပေမယ့် left ရော right ပါ အလွန်ကြီးမားတဲ့ ကိန်းဂဏန်းတွေ ဖြစ်နေရင် (ဥပမာ - ဝန်းကျင်)၊ နှစ်ခုပေါင်းလိုက်တဲ့အခါ int ရဲ့ အမြင့်ဆုံး သတ်မှတ်ချက်ထက် ကျော်လွန်ပြီး Integer Overflow ဖြစ်သွားစေနိုင်ပါတယ်။ ဒါကြောင့် mid ကို ရှာသည့် အခါမှာ အောက်ပါအတိုင်း ရေးသားကြပါတယ်။
int mid = left + (right - left) / 2;
၃။ Pointer များကို ရွေ့လျားပုံ (Boundary Update)
mid မှာ ရှိတဲ့ တန်ဖိုးနဲ့ target ကို နှိုင်းယှဉ်ပြီး ဘောင်တွေကို ကျဉ်းသွားစေရပါမယ်။
nums[mid] == targetဖြစ်လျှင် အဖြေတွေ့ပြီဖြစ်၍midကို return ပြန်သည်။nums[mid] < targetဖြစ်လျှင် target သည် ညာဘက်ခြမ်းတွင်သာ ရှိနိုင်၍ ဘယ်ဘက်ဘောင်ကို ရွှေ့သည်:left = mid + 1nums[mid] > targetဖြစ်လျှင် target သည် ဘယ်ဘက်ခြမ်းတွင်သာ ရှိနိုင်၍ ညာဘက်ဘောင်ကို ရွှေ့သည်:right = mid - 1
၄။ Infinite Loop မဖြစ်စေရန် (Common Pitfall)
Binary Search ရေးတဲ့အခါ အများဆုံး မှားတတ်တာက loop က ဘယ်တော့မှ မရပ်ဘဲ infinite loop ဖြစ်သွားတာ ပါ။ မှတ်ထားရမယ့် အချက် ၂ ချက် —
left <= right(target ရှာတဲ့ template): boundary ကိုmid + 1/mid - 1နဲ့ အမြဲ ရွှေ့ ပေးရမယ်။left = midဒါမှမဟုတ်right = midလို့ ရေးမိရင်midက နေရာ မပြောင်းတော့ဘဲ loop ထဲ ထိုင်ကျန်သွားပါတယ်။left < right(boundary/lower bound ရှာတဲ့ template):right = midသုံးလို့ ရပေမယ့်၊left = midတော့ မသုံးရပါဘူး။ ဘာကြောင့်လဲဆိုတော့ element ၂ ခု ကျန်တဲ့အခါmidကleftဖြစ်နေပြီးleft = midက နေရာ မပြောင်းလို့ infinite loop ဖြစ်စေပါတယ်။ ဒီအခါmidကိုleft + (right - left + 1) / 2(အပေါ်ကို round) လုပ်ပြီး ဖြေရှင်းရပါတယ်။
Lower Bound နှင့် Upper Bound
တန်ဖိုးတစ်ခုကို ရှာဖို့ထက် "ဘယ်နေရာမှာ ထည့်သင့်သလဲ" ဆိုတဲ့ boundary ကို ရှာရတာ ပိုအသုံးဝင်ပါတယ်။ ဒါက Binary Search ရဲ့ အစွမ်းအထက်ဆုံး အသုံးချမှုပါ။
- Lower Bound — target ထက် ငယ်ခြင်းမရှိ (>= target) တဲ့ ပထမဆုံး နေရာ (index)။
- Upper Bound — target ထက် ကြီးတဲ့ (> target) ပထမဆုံး နေရာ (index)။
ဥပမာ nums = [1, 2, 2, 2, 5], target = 2 ဆိုရင် —
- Lower bound = index
1(ပထမဆုံး2) - Upper bound = index
4(ပထမဆုံး2ထက်ကြီးတဲ့5)
target ရှာတဲ့ template နဲ့ မတူတာက — တွေ့တာနဲ့ ချက်ချင်း မရပ်ဘဲ၊ ဖြစ်နိုင်ချေ အဖြေကို မှတ်ထားပြီး ဆက် သွားတာပါ။
// Lower bound: nums[i] >= target ဖြစ်တဲ့ ပထမဆုံး index
int lowerBound(int[] nums, int target) {
int left = 0, right = nums.length; // right က length (out of range) က စသည်
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1; // mid က သေးနေသေးလို့ ညာဘက်
} else {
right = mid; // mid ကိုယ်တိုင် အဖြေ ဖြစ်နိုင်လို့ ဖယ်မပစ်
}
}
return left;
}
Upper bound အတွက်ဆို nums[mid] < target ကို nums[mid] <= target လို့ ပြောင်းရုံပါပဲ။ ဒီ pattern နှစ်ခုက အောက်က Search Insert Position နဲ့ First and Last Position ပုစ္ဆာတွေရဲ့ အခြေခံပါ။
Real-world မှာ ဘယ်လိုသုံးလဲ
Binary Search က "sorted ဖြစ်နေတဲ့ အရာ" ဒါမှမဟုတ် "monotonic (တစ်လမ်းသွား တိုး/လျော့) ဖြစ်တဲ့ အဖြေနယ်ပယ်" ရှိတိုင်း သုံးနိုင်ပါတယ်။
- Sorted records ရှာခြင်း — Database index (B-Tree) တွေဟာ sorted key ပေါ်မှာ binary search သဘောတရားနဲ့ အလုပ်လုပ်တယ်။
git bisectက bug ဝင်တဲ့ commit ကို binary search နဲ့ ရှာတယ်။ - First available version / capacity — "condition မှန်တဲ့ အငယ်ဆုံးတန်ဖိုး" ရှာတာ — server အနည်းဆုံး ဘယ်နှလုံး လိုသလဲ၊ package version ဘယ်ကစပြီး bug ဝင်သလဲ (lower bound pattern)။
- Price range / log timestamp — sorted price list ဒါမှမဟုတ် time-series log ထဲက အပိုင်း (range) ကို lower/upper bound နဲ့ ဆွဲထုတ်တာ။
- Minimum capacity / rate (Binary Search on Answer) — အောက်က Koko Eating Bananas လို "အနည်းဆုံး k ဘယ်လောက် လိုသလဲ" ပုစ္ဆာမျိုး။
Questions
Binary Search ကို ကောင်းစွာ အသုံးချတတ်စေဖို့ အမေးအများဆုံး မေးခွန်းပုံစံတွေကို လွယ်ရာက ခက်ရာ တစ်ဆင့်ချင်း လေ့လာကြည့်ရအောင်။
၁။ Binary Search (Basic Template)
Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1.
You must write an algorithm with runtime complexity.
Example 1:
Input: nums = [-1,0,3,5,9,12], target = 9
Output: 4
Explanation: 9 exists in nums and its index is 4
Example 2:
Input: nums = [-1,0,3,5,9,12], target = 2
Output: -1
Explanation: 2 does not exist in nums so return -1
ရှင်းလင်းချက်
ဒါကတော့ Binary Search ရဲ့ အခြေခံအကျဆုံး Template ပုစ္ဆာ ဖြစ်ပါတယ်။ ပေးထားတဲ့ Array က အငယ်ကနေ အကြီး အစဉ်လိုက် စီပြီးသား ဖြစ်တဲ့အတွက် left နဲ့ right pointer တွေကို သုံးပြီး target ကို ရှာဖွေသွားရမှာ ဖြစ်ပါတယ်။ left <= right ဖြစ်နေသရွေ့ ပတ်ပါမယ်။
Time Complexity: - အဆင့်တိုင်းမှာ search space က ထက်ဝက် လျော့သွားလို့ ဖြစ်ပါတယ်။
Space Complexity: - memory ပို ထပ်မယူလို့ ဖြစ်ပါတယ်။
Java Solution
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
// Integer Overflow မဖြစ်စေရန် တွက်နည်း
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid; // အဖြေရှာတွေ့ပါက index ကို ပြန်သည်
} else if (nums[mid] < target) {
// target က ပိုကြီးနေလျှင် ညာဘက်ခြမ်းတွင် ဆက်ရှာရန်
left = mid + 1;
} else {
// target က ပိုငယ်နေလျှင် ဘယ်ဘက်ခြမ်းတွင် ဆက်ရှာရန်
right = mid - 1;
}
}
return -1; // ရှာမတွေ့ပါက -1 ပြန်သည်
}
}
၂။ Search Insert Position
Sorted array nums (distinct values) နဲ့ target ပေးထားတယ်။ target ရှိရင် သူ့ index ကို ပြန်ပါ။ မရှိရင် target ကို စီထားတဲ့ အစဉ်အတိုင်း ထည့်သင့်တဲ့ index ကို ပြန်ပါ။
Example:
Input: nums = [1,3,5,6], target = 5 → Output: 2
Input: nums = [1,3,5,6], target = 2 → Output: 1
Input: nums = [1,3,5,6], target = 7 → Output: 4
ရှင်းလင်းချက်
ဒါက အပေါ်က Lower Bound ကို တိုက်ရိုက် အသုံးချတာပါ။ nums[i] >= target ဖြစ်တဲ့ ပထမဆုံး index ဟာ — target ရှိရင် သူ့နေရာ၊ မရှိရင် ထည့်သင့်တဲ့နေရာ — အတိအကျ ဖြစ်နေပါတယ်။
Time Complexity: Space Complexity:
Java Solution
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length; // out-of-range index ကစသည် (ထည့်စရာ နေရာ ဖြစ်နိုင်လို့)
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid; // mid ကိုယ်တိုင် အဖြေ ဖြစ်နိုင်လို့ ဖယ်မပစ်
}
}
return left;
}
}
၃။ Find First and Last Position of Element
Sorted array nums ထဲမှာ target ရဲ့ ပထမဆုံး (first) နဲ့ နောက်ဆုံး (last) index ကို ရှာပါ။ မရှိရင် [-1, -1]။ ဖြစ်ရမယ်။
Example:
Input: nums = [5,7,7,8,8,10], target = 8 → Output: [3,4]
Input: nums = [5,7,7,8,8,10], target = 6 → Output: [-1,-1]
ရှင်းလင်းချက်
target ထပ်နေနိုင်တဲ့အတွက် binary search ၂ ခါ လုပ်ပါမယ် —
- First position =
targetရဲ့ lower bound (>= targetပထမဆုံး index)။ - Last position =
targetရဲ့ upper bound ထက် တစ်လုံး နောက်ပြန် (> targetပထမဆုံး index ရဲ့ ရှေ့)။
Time Complexity: Space Complexity:
Java Solution
class Solution {
public int[] searchRange(int[] nums, int target) {
int first = lowerBound(nums, target);
// target မရှိလျှင်
if (first == nums.length || nums[first] != target) {
return new int[] { -1, -1 };
}
int last = lowerBound(nums, target + 1) - 1; // target+1 ၏ lower bound ၏ ရှေ့
return new int[] { first, last };
}
// nums[i] >= key ဖြစ်တဲ့ ပထမဆုံး index
private int lowerBound(int[] nums, int key) {
int left = 0, right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < key) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
}
၄။ Search a 2D Matrix
You are given an integer matrix matrix with the following two properties:
- Each row is sorted in non-decreasing order.
- The first integer of each row is greater than the last integer of the previous row.
Given an integer target, return true if target is in matrix or false otherwise.
You must write a solution in time complexity.
Example 1:
Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
Output: true
ရှင်းလင်းချက်
ဒီပုစ္ဆာမှာ Matrix (2D Array) တစ်ခု ပေးထားပေမယ့် ထူးခြားချက်က Row တစ်ခုချင်းစီဟာ စီထားပြီးသား ဖြစ်ရုံသာမက၊ နောက် Row ရဲ့ ပထမဆုံးဂဏန်းက အရှေ့ Row ရဲ့ နောက်ဆုံးဂဏန်းထက် အမြဲ ပိုကြီးနေပါတယ်။ ဒါဟာ တကယ်တော့ 2D Matrix သာ ဖြစ်နေပေမယ့် ဂဏန်းအားလုံးကို တန်းစီချလိုက်ရင် 1D Sorted Array တစ်ခုတည်း အတိုင်းပါပဲ။
ဒါကြောင့် 2D Matrix ကို 1D Array တစ်ခုလို စိတ်ကူးပုံဖော်ပြီး Binary Search သုံးနိုင်ပါတယ်။
- 1D Array ရဲ့ အလျား (Length) = ဖြစ်ပါမယ်။
- Virtual 1D index
midကို 2D Coordinates (Row, Col) အဖြစ် ပြန်ပြောင်းနည်း-row = mid / n(ညာဘက် Column အရေအတွက်နဲ့ စားသည်)col = mid % n(ညာဘက် Column အရေအတွက်နဲ့ စားကြွင်းရှာသည်)
Time Complexity: - 2D matrix တစ်ခုလုံးကို 1D array လို တစ်ကြိမ်တည်းနဲ့ ရှာဖွေနိုင်သောကြောင့် ဖြစ်သည်။
Space Complexity:
Java Solution
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int rows = matrix.length;
int cols = matrix[0].length;
// 1D Array သဘောမျိုး အစ နှင့် အဆုံး သတ်မှတ်သည်
int left = 0;
int right = (rows * cols) - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
// Virtual 1D index 'mid' မှ 2D Row, Col သို့ ပြောင်းလဲခြင်း
int midVal = matrix[mid / cols][mid % cols];
if (midVal == target) {
return true;
} else if (midVal < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}
၅။ Koko Eating Bananas
Koko loves to eat bananas. There are n piles of bananas, the pile has piles[i] bananas. The guards have gone and will come back in h hours.
Koko can decide her bananas-per-hour eating speed of k. Each hour, she chooses some pile of bananas and eats k bananas from that pile. If the pile has less than k bananas, she eats all of them instead and will not eat any more bananas during this hour.
Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.
Return the minimum integer k such that she can eat all the bananas within h hours.
Example 1:
Input: piles = [3,6,7,11], h = 8
Output: 4
ရှင်းလင်းချက်
ဒီပုစ္ဆာက "Binary Search on Answer / Solution Space" လို့ ခေါ်တဲ့ အဆင့်မြင့် ပုံစံတစ်မျိုး ဖြစ်ပါတယ်။ ကျွန်တော်တို့က Array ထဲမှာ တန်ဖိုးတစ်ခုကို လိုက်ရှာတာ မဟုတ်ပါဘူး။ Koko ငှက်ပျောသီး စားရမယ့် အနည်းဆုံးနှုန်း k (တစ်နာရီ စားမယ့် အရေအတွက်) ကို စဉ်းစားရမှာ ဖြစ်ပါတယ်။
- တစ်နာရီ စားနိုင်မယ့် အနည်းဆုံးနှုန်း
kက ၁ ဖြစ်ပါတယ်။ - တစ်နာရီ စားနိုင်မယ့် အများဆုံးနှုန်း
kကတော့ ပေးထားတဲ့ စုပုံတွေထဲက အများဆုံး အရေအတွက်max(piles)ဖြစ်ပါတယ် (ဘာဖြစ်လို့လဲဆိုတော့ တစ်နာရီကို စုပုံတစ်ပုံထက် ပိုမစားနိုင်လို့ အကြီးဆုံးပုံထက် ပိုမြန်အောင် စားဖို့ မလိုပါဘူး)။ - ဒါကြောင့် ကျွန်တော်တို့ရဲ့ ရှာဖွေရမယ့် ဘောင်က
[1, max(piles)]ဖြစ်သွားပါပြီ။ - အလယ်ကိန်း
midကို ယူပြီး တစ်နာရီmidနှုန်းနဲ့ စားရင် စုစုပေါင်း ဘယ်နှနာရီ ကြာမလဲလို့ တွက်ကြည့်ပါမယ်။- ကြာမယ့်အချိန်က
hနာရီထက် ငယ်ရင် သို့မဟုတ် ညီရင် (သတ်မှတ်ချိန်အတွင်း ပြီးရင်) - ဒီထက်ပိုနှေးပြီး စားလို့ ရမရ ဆက်စစ်ဖို့ ညာဘက်ဘောင်ကို ကျဉ်းပါမယ်:right = mid - 1(အဖြေကိုmidအဖြစ် မှတ်ထားပါမယ်)။ - ကြာမယ့်အချိန်က
hထက် ကျော်သွားရင် (သတ်မှတ်ချိန်အတွင်း မပြီးရင်) - ပိုမြန်မြန် စားဖို့ လိုတဲ့အတွက် ဘယ်ဘက်ဘောင်ကို တိုးပါမယ်:left = mid + 1။
- ကြာမယ့်အချိန်က
တစ်ပုံချင်းစီ စားတဲ့အခါ ကြာမယ့်အချိန်ကို
Math.ceil((double)pile / k)သို့မဟုတ် Integer arithmetic သုံးပြီး(pile + k - 1) / kဖြင့် ရှာနိုင်ပါတယ်။
Time Complexity: - binary search ဘောင်
[1, max(piles)]ကို အကြိမ် ပတ်ပြီး၊ အဆင့်တိုင်းမှာ အချိန်ယူ၍ piles အားလုံး စားချိန်ကို တွက်သောကြောင့် ဖြစ်သည်။
Space Complexity:
Java Solution
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int left = 1;
int right = 0;
// အများဆုံး စားနှုန်းကို ရှာရန် Piles ထဲမှ အကြီးဆုံးတန်ဖိုးကို ရှာသည်
for (int pile : piles) {
right = Math.max(right, pile);
}
int result = right; // အဆိုးဆုံးနှုန်းဖြင့် စတင်ထားမည်
while (left <= right) {
int mid = left + (right - left) / 2;
// တစ်နာရီ 'mid' နှုန်းဖြင့် စားပါက စုစုပေါင်း ကြာမည့်နာရီ
long totalHours = 0;
for (int pile : piles) {
// (pile + mid - 1) / mid သည် Math.ceil((double)pile / mid) နှင့် အတူတူပင်ဖြစ်သည်
totalHours += (pile + mid - 1) / mid;
}
if (totalHours <= h) {
// သတ်မှတ်ချိန်မီပါက ပိုနှေးသော စားနှုန်းကို ဆက်ရှာရန်
result = mid;
right = mid - 1;
} else {
// သတ်မှတ်ချိန်မမီပါက ပိုမြန်မြန် စားရန်
left = mid + 1;
}
}
return result;
}
}
၆။ Find Minimum in Rotated Sorted Array
Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:
[4,5,6,7,0,1,2]if it was rotated4times.[0,1,2,4,5,6,7]if it was rotated7times.
Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].
Given the sorted rotated array nums of unique elements, return the minimum element of this array.
You must write an algorithm that runs in time.
Example 1:
Input: nums = [3,4,5,1,2]
Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.
ရှင်းလင်းချက်
Sorted ဖြစ်နေတဲ့ Array တစ်ခုကို တစ်နေရာရာကနေ ဖြတ်ပြီး ခေါက်လိုက်တဲ့အခါ (Rotated sorted array)၊ Array ရဲ့ ပုံစံဟာ အပိုင်းနှစ်ပိုင်း ကွဲသွားပါတယ်။
အဓိက အယူအဆကတော့ — အပိုင်းတစ်ခုခုသည် အမြဲတမ်း စနစ်တကျ စီထားလျက် ရှိနေမည် ဖြစ်ပြီး၊ အခြားအပိုင်းတွင်မူ လှည့်ခေါက်ထားသော Pivot Point (အနည်းဆုံးတန်ဖိုး) ရှိနေပါလိမ့်မယ်။
nums[mid]တန်ဖိုးကို ညာဘက်ဆုံးတန်ဖိုးnums[right]နဲ့ တိုက်စစ်ပါမယ်။nums[mid] > nums[right]ဖြစ်နေလျှင် - ဆိုလိုတာက ဘယ်ဘက်ခြမ်းကနေ အလယ်အထိဟာ ပုံမှန်အတိုင်း တိုးလာနေပေမယ့် ညာဘက်အဆုံးထက် ကြီးနေတာမို့လို့ အလှည့် Pivot Point (သို့) အငယ်ဆုံးကိန်းဟာmidရဲ့ ညာဘက်ခြမ်းမှာပဲ ရှိနိုင်ပါတော့တယ်။ ဒါကြောင့်left = mid + 1။nums[mid] <= nums[right]ဖြစ်နေလျှင် - ဆိုလိုတာကmidကနေ ညာဘက်ခြမ်းတစ်ခုလုံးက ပုံမှန်အတိုင်း စီပြီးသား ဖြစ်နေလို့ အငယ်ဆုံးကိန်းကmidကိုယ်တိုင် သို့မဟုတ်midရဲ့ ဘယ်ဘက်ခြမ်းမှာပဲ ရှိပါလိမ့်မယ်။ ဒါကြောင့်right = mid။
ဤနေရာတွင်
left < rightဟု သုံးရပါမည်။ ဘာကြောင့်လဲဆိုတော့right = midဟု ပြောင်းလဲချိန်တွင်midသည် အဖြေဖြစ်နိုင်သောကြောင့် loop မပတ်မိစေရန် ဖြစ်သည်။
Time Complexity:
Space Complexity:
Java Solution
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
// အလယ်ကိန်းသည် ညာဘက်အစွန်းကိန်းထက် ကြီးနေလျှင် Pivot သည် ညာဘက်တွင်ရှိသည်
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
// အလယ်ကိန်းသည် ညာဘက်အစွန်းထက် ငယ်/ညီ နေပါက Pivot သည် ဘယ်ဘက်တွင် (သို့) mid ကိုယ်တိုင်ဖြစ်သည်
right = mid;
}
}
// left နှင့် right ဆုံသည့် နေရာသည် အနည်းဆုံးတန်ဖိုးဖြစ်သည်
return nums[left];
}
}
၇။ Search in Rotated Sorted Array
There is an integer array nums sorted in ascending order (with distinct values).
Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2].
Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums.
You must write an algorithm with runtime complexity.
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
ရှင်းလင်းချက်
ဒီပုစ္ဆာက "Find Minimum in Rotated Sorted Array" အပေါ် ထပ်ဆင့်ပြီး target ကို ရှာခိုင်းတာ ဖြစ်ပါတယ်။ တစ်ဝက်ခွဲလိုက်တဲ့အခါ ဘယ်ဘက်ခြမ်း သို့မဟုတ် ညာဘက်ခြမ်း တစ်ဖက်ဖက်ဟာ အမြဲတမ်း ပုံမှန် Sorted ဖြစ်နေဆဲ ဆိုတဲ့ အချက်ကို အခြေခံပြီး ဖြေရှင်းရပါမယ်။
၁။ အရင်ဆုံး ဘယ်ဘက်ခြမ်း [left, mid] က ပုံမှန် Sorted ဖြစ်နေသလား သိဖို့ nums[left] <= nums[mid] ကို စစ်ဆေးပါတယ်။
- Sorted ဖြစ်နေလျှင် - target သည် အဲ့ဒီဘယ်ဘက်ခြမ်း ဘောင်
[nums[left], nums[mid])ထဲမှာ ရှိမရှိ စစ်သည်။ ရှိလျှင်right = mid - 1၊ မရှိလျှင် ညာဘက်ခြမ်းမှာ ဆက်ရှာရန်left = mid + 1။
၂။ အကယ်၍ ဘယ်ဘက်ခြမ်းက Sorted မဟုတ်ပါက၊ ညာဘက်ခြမ်း (mid, right] က မလွဲမသွေ ပုံမှန် Sorted ဖြစ်နေပါလိမ့်မယ်။
- target သည် ညာဘက်ခြမ်း ဘောင်
(nums[mid], nums[right]]ထဲမှာ ရှိမရှိ စစ်သည်။ ရှိလျှင်left = mid + 1၊ မရှိလျှင် ဘယ်ဘက်ခြမ်းမှာ ဆက်ရှာရန်right = mid - 1။
Time Complexity:
Space Complexity:
Java Solution
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
// ဘယ်ဘက်ခြမ်းသည် ပုံမှန် Sorted ဖြစ်နေသလား စစ်ဆေးသည်
if (nums[left] <= nums[mid]) {
// target သည် ဘယ်ဘက်ခြမ်း၏ ဘောင်ထဲတွင် ရှိနေသလား
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1; // ဘယ်ဘက်သို့ ရွေ့သည်
} else {
left = mid + 1; // ညာဘက်သို့ ရွေ့သည်
}
}
// မဟုတ်ပါက ညာဘက်ခြမ်းသည် ပုံမှန် Sorted ဖြစ်နေရမည်
else {
// target သည် ညာဘက်ခြမ်း၏ ဘောင်ထဲတွင် ရှိနေသလား
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1; // ညာဘက်သို့ ရွေ့သည်
} else {
right = mid - 1; // ဘယ်ဘက်သို့ ရွေ့သည်
}
}
}
return -1;
}
}
၈။ Time Based Key-Value Store
Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp.
Implement the TimeMap class:
TimeMap()Initializes the object of the data structure.void set(String key, String value, int timestamp)Stores the keykeywith the valuevalueat the given timetimestamp.String get(String key, int timestamp)Returns a value such thatsetwas called previously, withtimestamp_prev <= timestamp. If there are multiple such values, it returns the value associated with the largesttimestamp_prev. If there are no values, it returns"".
Example 1:
Input:
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
Output:
[null, null, "bar", "bar", null, "bar2", "bar2"]
ရှင်းလင်းချက်
ဒီပုစ္ဆာကတော့ Database တွေမှာ Version ထိန်းချုပ်မှု (Timestamp tracking) အတွက် သုံးတဲ့ ပုံစံမျိုး ဖြစ်ပါတယ်။
setလုပ်တဲ့အခါkeyတစ်ခုတည်းအတွက်valueတွေနဲ့ ဝင်လာတဲ့timestampတွေကို တွဲပြီး သိမ်းသွားရပါမယ်။ (ဥပမာ-Map<String, List<Pair>>)။getလုပ်တဲ့အခါtimestampပေးထားပြီး ထို timestamp ထက် ငယ်သော သို့မဟုတ် ညီသော အကြီးဆုံး timestamp_prev (Floor value) ရဲ့ တန်ဖိုးကို ရှာပေးရပါမယ်။- ပေးထားချက်အရ
setရဲ့ timestamp တွေဟာ အမြဲတမ်း တိုးလာနေတဲ့အတွက် (Strictly increasing)၊ သက်ဆိုင်ရာkeyရဲ့ List ထဲမှာ Binary Search သုံးပြီး အလွယ်တကူ ရှာဖွေနိုင်ပါတယ်။
Time Complexity:
set:get: (ပေးထားသော Key အတွက် list အရွယ်အစား ပေါ်မူတည်၍)Space Complexity: - entries အားလုံးကို map ထဲတွင် သိမ်းဆည်းသောကြောင့် ဖြစ်သည်။
Java Solution
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class TimeMap {
// Value နှင့် Timestamp ကို တွဲသိမ်းရန် Class တည်ဆောက်သည်
private static class Node {
String value;
int timestamp;
Node(String value, int timestamp) {
this.value = value;
this.timestamp = timestamp;
}
}
private Map<String, List<Node>> map;
public TimeMap() {
map = new HashMap<>();
}
public void set(String key, String value, int timestamp) {
map.putIfAbsent(key, new ArrayList<>());
map.get(key).add(new Node(value, timestamp));
}
public String get(String key, int timestamp) {
if (!map.containsKey(key)) {
return "";
}
List<Node> list = map.get(key);
int left = 0;
int right = list.size() - 1;
String result = "";
// Floor value ရှာရန် Binary Search
while (left <= right) {
int mid = left + (right - left) / 2;
if (list.get(mid).timestamp <= timestamp) {
// လက်ရှိ timestamp ထက် ငယ်/ညီပါက ဖြစ်နိုင်ချေရှိသောအဖြေအဖြစ် မှတ်ပြီး
// ပိုကြီးသည့်အဖြေရှိနိုင်သေးသဖြင့် ညာဘက်သို့ ဆက်ရှာသည်
result = list.get(mid).value;
left = mid + 1;
} else {
// timestamp ကြီးနေပါက ဘယ်ဘက်သို့ ရွေ့သည်
right = mid - 1;
}
}
return result;
}
}
၉။ Median of Two Sorted Arrays
Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.
The overall run time complexity should be .
Example 1:
Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.
Example 2:
Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.
ရှင်းလင်းချက်
ဒါကတော့ Binary Search အခန်းရဲ့ အခက်ခဲဆုံးနဲ့ အဆင့်အမြင့်ဆုံး Hard အဆင့်ရှိ မေးခွန်း ဖြစ်ပါတယ်။ ပုံမှန်အားဖြင့် Arrays နှစ်ခုကို ပေါင်းပြီး Sort လုပ်ရင် ကြာပေမယ့်၊ ဒီပုစ္ဆာက သာ ကြာအောင် စွမ်းဆောင်ရမှာ ဖြစ်ပါတယ်။
ဒါကို ဖြေရှင်းဖို့အတွက် "Partition (ပိုင်းခြားခြင်း) နည်းလမ်း" ကို သုံးရပါမယ်။
ကျွန်တော်တို့ လိုချင်တဲ့ အလယ်တန်ဖိုး (Median) ဆိုတာ စုစုပေါင်း Array ၂ ခုကို ဘယ်ခြမ်း (Left Partition) နဲ့ ညာခြမ်း (Right Partition) လို့ ညီတူညီမျှ ခွဲလိုက်တဲ့အခါ -
- Left Partition မှာ ရှိသမျှ ဂဏန်းတွေအားလုံးက Right Partition က ဂဏန်းတွေအားလုံးထက် ငယ် သို့မဟုတ် ညီ နေရပါမယ်။
- Left partition ၏ အရွယ်အစားသည်
(m + n + 1) / 2ဖြစ်ရပါမယ်။
Array A (nums1): [ ... A[i-1] | A[i] ... ]
Array B (nums2): [ ... B[j-1] | B[j] ... ]
<-- Left --> | <-- Right -->
ကျွန်တော်တို့က အရွယ်အစား ပိုငယ်တဲ့ Array (ဥပမာ A) ပေါ်မှာ Binary Search စလုပ်ပါမယ်။
- အကယ်၍ A ထဲက Partition index
iကို ရွေးလိုက်ရင်၊ B ထဲက Partition indexjက အလိုအလျောက် သတ်မှတ်ပြီးသား ဖြစ်သွားပါတယ်:j = (m + n + 1) / 2 - i။ - ပြီးရင် ဖြတ်တောက်လိုက်တဲ့ နယ်နိမိတ် ၄ ခုဖြစ်တဲ့
A[i-1],A[i],B[j-1],B[j]တို့ကို စစ်ဆေးပါမယ်။- မှန်ကန်သော အခြေအနေ:
A[i-1] <= B[j]နှင့်B[j-1] <= A[i]ဖြစ်ပါက ခွဲခြားမှု မှန်ကန်သွားပါပြီ။- စုစုပေါင်းအရေအတွက် မကိန်း ဖြစ်ပါက
max(A[i-1], B[j-1])သည် Median ဖြစ်သည်။ - စုံကိန်း ဖြစ်ပါက
(max(A[i-1], B[j-1]) + min(A[i], B[j])) / 2.0သည် Median ဖြစ်သည်။
- စုစုပေါင်းအရေအတွက် မကိန်း ဖြစ်ပါက
A[i-1] > B[j]ဖြစ်နေပါက - A ဘက်မှ partition က ကြီးလွန်းနေ၍ ဘယ်ဘက်သို့ ရွှေ့ရန်right = i - 1လုပ်သည်။B[j-1] > A[i]ဖြစ်နေပါက - A ဘက်မှ partition က သေးလွန်းနေ၍ ညာဘက်သို့ ရွှေ့ရန်left = i + 1လုပ်သည်။
- မှန်ကန်သော အခြေအနေ:
Time Complexity: - ပိုမိုတိုတောင်းသော array ပေါ်တွင်သာ binary search ပြုလုပ်သောကြောင့် အလွန်လျင်မြန်သည်။
Space Complexity:
Java Solution
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// nums1 က အမြဲတမ်း အရွယ်အစား ပိုငယ်စေရန် ပြောင်းလဲသည် (Binary Search ပိုမြန်စေရန်)
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int m = nums1.length;
int n = nums2.length;
int left = 0;
int right = m;
int halfLen = (m + n + 1) / 2;
while (left <= right) {
int i = left + (right - left) / 2; // nums1 ၏ partition
int j = halfLen - i; // nums2 ၏ partition
// နယ်နိမိတ်တန်ဖိုးများ သတ်မှတ်ခြင်း (Out of bounds ဖြစ်ပါက Infinity ယူသည်)
int maxLeft1 = (i == 0) ? Integer.MIN_VALUE : nums1[i - 1];
int minRight1 = (i == m) ? Integer.MAX_VALUE : nums1[i];
int maxLeft2 = (j == 0) ? Integer.MIN_VALUE : nums2[j - 1];
int minRight2 = (j == n) ? Integer.MAX_VALUE : nums2[j];
// ခွဲခြားမှု မှန်ကန်မှု ရှိမရှိ စစ်ဆေးသည်
if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
// စုစုပေါင်း အရေအတွက် မကိန်းဖြစ်လျှင်
if ((m + n) % 2 != 0) {
return Math.max(maxLeft1, maxLeft2);
}
// စုစုပေါင်း အရေအတွက် စုံကိန်းဖြစ်လျှင်
return ((long)Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0;
}
// nums1 ၏ ဘယ်ဘက်ခြမ်းသည် ကြီးလွန်းနေပါက ၎င်းအား လျှော့ရန်
else if (maxLeft1 > minRight2) {
right = i - 1;
}
// nums1 ၏ ဘယ်ဘက်ခြမ်းသည် ငယ်လွန်းနေပါက တိုးရန်
else {
left = i + 1;
}
}
return 0.0;
}
}