Bài tập lập trình - Luyện thuật toán
Trang này tổng hợp 200 bài tập luyện thuật toán, được biên soạn theo phong cách các bài trên LeetCode, HackerRank và Codewars — mỗi bài có phần mô tả đề bài, các ví dụ minh họa (test case) với Input/Output/Giải thích, phần ràng buộc (constraints), và nhãn độ khó. Đây là bước tiếp theo hợp lý nếu bạn đã hoàn thành Bài tập lập trình - Cơ bản và Nâng cao — ở đây trọng tâm là tư duy thuật toán và cấu trúc dữ liệu, không phải cú pháp của một ngôn ngữ cụ thể. Mỗi bài đều có đáp án minh họa bằng nhiều ngôn ngữ lập trình khác nhau.
200 bài được chia thành 60 bài Dễ, 100 bài Trung bình, 40 bài Khó, sắp xếp theo chủ đề từ cơ bản (mảng, chuỗi) đến nâng cao (quy hoạch động, đồ thị, backtracking). Mỗi bài đều có phần đáp án gợi ý ở dưới, mặc định ẩn đi — bạn nên tự làm trước (đọc kỹ các ví dụ và ràng buộc), sau đó bấm vào “Xem đáp án” để đối chiếu. Đáp án chỉ là một cách giải, không phải cách duy nhất và không phải lúc nào cũng tối ưu nhất.
(Đề bài được tham khảo, chuyển ngữ và điều chỉnh từ các bài toán phổ biến trên LeetCode, HackerRank và Codewars.)
Nhóm 1: Mảng & Số học cơ bản
Phần tiêu đề “Nhóm 1: Mảng & Số học cơ bản”1. Tìm các số bị thiếu trong mảng (Find All Numbers Disappeared in an Array)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng nums gồm n số nguyên trong khoảng [1, n], mỗi số có thể xuất hiện 1 hoặc 2 lần. Trả về danh sách (tăng dần) tất cả các số trong [1, n] không xuất hiện trong nums.
Ví dụ 1:
Input: nums = [4, 3, 2, 7, 8, 2, 3, 1]Output: [5, 6]Ví dụ 2:
Input: nums = [1, 1]Output: [2]Ràng buộc:
n == len(nums)1 <= n <= 10^51 <= nums[i] <= n
Xem đáp án
def find_disappeared_numbers(nums): present = set(nums) return [i for i in range(1, len(nums) + 1) if i not in present]
print(find_disappeared_numbers([4, 3, 2, 7, 8, 2, 3, 1])) # [5, 6]#include <iostream>#include <vector>#include <unordered_set>using namespace std;
vector<int> findDisappearedNumbers(vector<int>& nums) { unordered_set<int> present(nums.begin(), nums.end()); vector<int> result; for (int i = 1; i <= (int)nums.size(); i++) { if (present.find(i) == present.end()) result.push_back(i); } return result;}
int main() { vector<int> nums = {4, 3, 2, 7, 8, 2, 3, 1}; for (int x : findDisappearedNumbers(nums)) cout << x << " "; cout << endl; // 5 6 return 0;}import java.util.*;
public class Main { static List<Integer> findDisappearedNumbers(int[] nums) { Set<Integer> present = new HashSet<>(); for (int n : nums) present.add(n); List<Integer> result = new ArrayList<>(); for (int i = 1; i <= nums.length; i++) { if (!present.contains(i)) result.add(i); } return result; }
public static void main(String[] args) { int[] nums = {4, 3, 2, 7, 8, 2, 3, 1}; System.out.println(findDisappearedNumbers(nums)); // [5, 6] }}fun findDisappearedNumbers(nums: List<Int>): List<Int> { val present = nums.toHashSet() return (1..nums.size).filter { it !in present }}
fun main() { val nums = listOf(4, 3, 2, 7, 8, 2, 3, 1) println(findDisappearedNumbers(nums)) // [5, 6]}List<int> findDisappearedNumbers(List<int> nums) { final present = nums.toSet(); return [for (var i = 1; i <= nums.length; i++) if (!present.contains(i)) i];}
void main() { final nums = [4, 3, 2, 7, 8, 2, 3, 1]; print(findDisappearedNumbers(nums)); // [5, 6]}2. Thời điểm mua bán cổ phiếu tốt nhất (Best Time to Buy and Sell Stock)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng prices, trong đó prices[i] là giá cổ phiếu ở ngày thứ i. Bạn chỉ được mua 1 lần và bán 1 lần sau đó. Tìm lợi nhuận lớn nhất có thể đạt được, nếu không thể có lãi thì trả về 0.
Ví dụ 1:
Input: prices = [7, 1, 5, 3, 6, 4]Output: 5Giải thích: Mua ngày giá 1, bán ngày giá 6, lãi 5.Ví dụ 2:
Input: prices = [7, 6, 4, 3, 1]Output: 0Giải thích: Giá luôn giảm nên không có lãi, không giao dịch.Ràng buộc:
1 <= len(prices) <= 10^50 <= prices[i] <= 10^4
Xem đáp án
def max_profit(prices): min_price = float("inf") best = 0 for p in prices: min_price = min(min_price, p) best = max(best, p - min_price) return best
print(max_profit([7, 1, 5, 3, 6, 4])) # 5#include <iostream>#include <vector>#include <climits>using namespace std;
int maxProfit(vector<int>& prices) { int minPrice = INT_MAX; int best = 0; for (int p : prices) { minPrice = min(minPrice, p); best = max(best, p - minPrice); } return best;}
int main() { vector<int> prices = {7, 1, 5, 3, 6, 4}; cout << maxProfit(prices) << endl; // 5 return 0;}public class Main { static int maxProfit(int[] prices) { int minPrice = Integer.MAX_VALUE; int best = 0; for (int p : prices) { minPrice = Math.min(minPrice, p); best = Math.max(best, p - minPrice); } return best; }
public static void main(String[] args) { int[] prices = {7, 1, 5, 3, 6, 4}; System.out.println(maxProfit(prices)); // 5 }}fun maxProfit(prices: List<Int>): Int { var minPrice = Int.MAX_VALUE var best = 0 for (p in prices) { minPrice = minOf(minPrice, p) best = maxOf(best, p - minPrice) } return best}
fun main() { val prices = listOf(7, 1, 5, 3, 6, 4) println(maxProfit(prices)) // 5}int maxProfit(List<int> prices) { int minPrice = 1 << 30; int best = 0; for (var p in prices) { minPrice = p < minPrice ? p : minPrice; final profit = p - minPrice; best = profit > best ? profit : best; } return best;}
void main() { final prices = [7, 1, 5, 3, 6, 4]; print(maxProfit(prices)); // 5}3. Kiểm tra phần tử trùng lặp (Contains Duplicate)
Độ khó: Dễ · Chủ đề: Mảng, Hash Set
Cho một mảng số nguyên, trả về True nếu có bất kỳ giá trị nào xuất hiện ít nhất 2 lần, ngược lại trả về False.
Ví dụ 1:
Input: nums = [1, 2, 3, 1]Output: TrueVí dụ 2:
Input: nums = [1, 2, 3, 4]Output: FalseRàng buộc:
1 <= len(nums) <= 10^5
Xem đáp án
def contains_duplicate(nums): return len(set(nums)) != len(nums)
print(contains_duplicate([1, 2, 3, 1])) # True#include <iostream>#include <vector>#include <unordered_set>using namespace std;
bool containsDuplicate(vector<int>& nums) { unordered_set<int> seen(nums.begin(), nums.end()); return seen.size() != nums.size();}
int main() { vector<int> nums = {1, 2, 3, 1}; cout << boolalpha << containsDuplicate(nums) << endl; // true return 0;}import java.util.*;
public class Main { static boolean containsDuplicate(int[] nums) { Set<Integer> seen = new HashSet<>(); for (int n : nums) seen.add(n); return seen.size() != nums.length; }
public static void main(String[] args) { int[] nums = {1, 2, 3, 1}; System.out.println(containsDuplicate(nums)); // true }}fun containsDuplicate(nums: List<Int>): Boolean { return nums.toHashSet().size != nums.size}
fun main() { val nums = listOf(1, 2, 3, 1) println(containsDuplicate(nums)) // true}bool containsDuplicate(List<int> nums) { return nums.toSet().length != nums.length;}
void main() { final nums = [1, 2, 3, 1]; print(containsDuplicate(nums)); // true}4. Dãy con liên tiếp có tổng lớn nhất (Maximum Subarray)
Độ khó: Dễ · Chủ đề: Mảng, Quy hoạch động
Cho một mảng số nguyên nums, tìm dãy con liên tiếp (chứa ít nhất 1 phần tử) có tổng lớn nhất, và trả về tổng đó (thuật toán Kadane).
Ví dụ 1:
Input: nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]Output: 6Giải thích: Dãy con [4, -1, 2, 1] có tổng lớn nhất = 6.Ví dụ 2:
Input: nums = [-1]Output: -1Ràng buộc:
1 <= len(nums) <= 10^5
Xem đáp án
def max_subarray(nums): best = cur = nums[0] for n in nums[1:]: cur = max(n, cur + n) best = max(best, cur) return best
print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])) # 6#include <iostream>#include <vector>using namespace std;
int maxSubarray(vector<int>& nums) { int best = nums[0], cur = nums[0]; for (size_t i = 1; i < nums.size(); i++) { cur = max(nums[i], cur + nums[i]); best = max(best, cur); } return best;}
int main() { vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; cout << maxSubarray(nums) << endl; // 6 return 0;}public class Main { static int maxSubarray(int[] nums) { int best = nums[0], cur = nums[0]; for (int i = 1; i < nums.length; i++) { cur = Math.max(nums[i], cur + nums[i]); best = Math.max(best, cur); } return best; }
public static void main(String[] args) { int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; System.out.println(maxSubarray(nums)); // 6 }}fun maxSubarray(nums: List<Int>): Int { var best = nums[0] var cur = nums[0] for (i in 1 until nums.size) { cur = maxOf(nums[i], cur + nums[i]) best = maxOf(best, cur) } return best}
fun main() { val nums = listOf(-2, 1, -3, 4, -1, 2, 1, -5, 4) println(maxSubarray(nums)) // 6}int maxSubarray(List<int> nums) { int best = nums[0]; int cur = nums[0]; for (int i = 1; i < nums.length; i++) { cur = nums[i] > cur + nums[i] ? nums[i] : cur + nums[i]; best = cur > best ? cur : best; } return best;}
void main() { final nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]; print(maxSubarray(nums)); // 6}5. Đưa các số 0 về cuối mảng (Move Zeroes)
Độ khó: Dễ · Chủ đề: Mảng, Two Pointers
Cho mảng số nguyên nums, di chuyển tất cả các số 0 về cuối mảng, giữ nguyên thứ tự tương đối của các phần tử khác 0. Phải thực hiện tại chỗ (in-place), không tạo mảng mới.
Ví dụ 1:
Input: nums = [0, 1, 0, 3, 12]Output: [1, 3, 12, 0, 0]Ví dụ 2:
Input: nums = [0, 0, 1]Output: [1, 0, 0]Ràng buộc:
1 <= len(nums) <= 10^4
Xem đáp án
def move_zeroes(nums): insert_pos = 0 for n in nums: if n != 0: nums[insert_pos] = n insert_pos += 1 for i in range(insert_pos, len(nums)): nums[i] = 0 return nums
print(move_zeroes([0, 1, 0, 3, 12])) # [1, 3, 12, 0, 0]#include <iostream>#include <vector>using namespace std;
vector<int> moveZeroes(vector<int>& nums) { int insertPos = 0; for (int n : nums) { if (n != 0) nums[insertPos++] = n; } for (int i = insertPos; i < (int)nums.size(); i++) nums[i] = 0; return nums;}
int main() { vector<int> nums = {0, 1, 0, 3, 12}; moveZeroes(nums); for (int x : nums) cout << x << " "; cout << endl; // 1 3 12 0 0 return 0;}import java.util.Arrays;
public class Main { static int[] moveZeroes(int[] nums) { int insertPos = 0; for (int n : nums) { if (n != 0) nums[insertPos++] = n; } for (int i = insertPos; i < nums.length; i++) nums[i] = 0; return nums; }
public static void main(String[] args) { int[] nums = {0, 1, 0, 3, 12}; System.out.println(Arrays.toString(moveZeroes(nums))); // [1, 3, 12, 0, 0] }}fun moveZeroes(nums: MutableList<Int>): MutableList<Int> { var insertPos = 0 for (n in nums.toList()) { if (n != 0) { nums[insertPos] = n insertPos++ } } for (i in insertPos until nums.size) nums[i] = 0 return nums}
fun main() { val nums = mutableListOf(0, 1, 0, 3, 12) println(moveZeroes(nums)) // [1, 3, 12, 0, 0]}List<int> moveZeroes(List<int> nums) { int insertPos = 0; for (var n in List.of(nums)) { if (n != 0) { nums[insertPos] = n; insertPos++; } } for (int i = insertPos; i < nums.length; i++) nums[i] = 0; return nums;}
void main() { final nums = [0, 1, 0, 3, 12]; print(moveZeroes(nums)); // [1, 3, 12, 0, 0]}6. Xóa phần tử theo giá trị (Remove Element)
Độ khó: Dễ · Chủ đề: Mảng, Two Pointers
Cho mảng nums và một giá trị val. Xóa tại chỗ (in-place) tất cả các phần tử bằng val, trả về độ dài mới k. k phần tử đầu của nums sau biến đổi chứa các phần tử khác val, thứ tự không quan trọng.
Ví dụ 1:
Input: nums = [3, 2, 2, 3], val = 3Output: k = 2, nums = [2, 2, ...]Ví dụ 2:
Input: nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2Output: k = 5, nums chứa [0, 1, 4, 0, 3] theo thứ tự bất kỳRàng buộc:
0 <= len(nums) <= 100
Xem đáp án
def remove_element(nums, val): k = 0 for n in nums: if n != val: nums[k] = n k += 1 return k, nums[:k]
print(remove_element([3, 2, 2, 3], 3)) # (2, [2, 2])#include <iostream>#include <vector>using namespace std;
int removeElement(vector<int>& nums, int val) { int k = 0; for (int n : nums) { if (n != val) nums[k++] = n; } return k;}
int main() { vector<int> nums = {3, 2, 2, 3}; int k = removeElement(nums, 3); cout << "k = " << k << ", nums = ["; for (int i = 0; i < k; i++) cout << nums[i] << (i + 1 < k ? ", " : ""); cout << "]" << endl; // k = 2, nums = [2, 2] return 0;}import java.util.Arrays;
public class Main { static int removeElement(int[] nums, int val) { int k = 0; for (int n : nums) { if (n != val) nums[k++] = n; } return k; }
public static void main(String[] args) { int[] nums = {3, 2, 2, 3}; int k = removeElement(nums, 3); System.out.println("k = " + k + ", nums = " + Arrays.toString(Arrays.copyOfRange(nums, 0, k))); // k = 2, nums = [2, 2] }}fun removeElement(nums: MutableList<Int>, `val`: Int): Int { var k = 0 for (n in nums.toList()) { if (n != `val`) { nums[k] = n k++ } } return k}
fun main() { val nums = mutableListOf(3, 2, 2, 3) val k = removeElement(nums, 3) println("k = $k, nums = ${nums.subList(0, k)}") // k = 2, nums = [2, 2]}int removeElement(List<int> nums, int val) { int k = 0; for (var n in List.of(nums)) { if (n != val) { nums[k] = n; k++; } } return k;}
void main() { final nums = [3, 2, 2, 3]; final k = removeElement(nums, 3); print("k = $k, nums = ${nums.sublist(0, k)}"); // k = 2, nums = [2, 2]}7. Cộng thêm 1 vào số biểu diễn dạng mảng (Plus One)
Độ khó: Dễ · Chủ đề: Mảng
Cho một mảng số nguyên digits biểu diễn các chữ số của một số nguyên không âm (chữ số đầu là chữ số hàng cao nhất). Cộng thêm 1 vào số đó và trả về mảng chữ số kết quả.
Ví dụ 1:
Input: digits = [1, 2, 3]Output: [1, 2, 4]Ví dụ 2:
Input: digits = [9, 9, 9]Output: [1, 0, 0, 0]Ràng buộc:
1 <= len(digits) <= 1000 <= digits[i] <= 9
Xem đáp án
def plus_one(digits): n = int("".join(map(str, digits))) + 1 return [int(c) for c in str(n)]
print(plus_one([9, 9, 9])) # [1, 0, 0, 0]#include <iostream>#include <vector>using namespace std;
vector<int> plusOne(vector<int>& digits) { for (int i = digits.size() - 1; i >= 0; i--) { if (digits[i] < 9) { digits[i]++; return digits; } digits[i] = 0; } digits.insert(digits.begin(), 1); return digits;}
int main() { vector<int> digits = {9, 9, 9}; vector<int> result = plusOne(digits); cout << "["; for (size_t i = 0; i < result.size(); i++) cout << result[i] << (i + 1 < result.size() ? ", " : ""); cout << "]" << endl; // [1, 0, 0, 0] return 0;}import java.util.Arrays;
public class Main { static int[] plusOne(int[] digits) { for (int i = digits.length - 1; i >= 0; i--) { if (digits[i] < 9) { digits[i]++; return digits; } digits[i] = 0; } int[] result = new int[digits.length + 1]; result[0] = 1; return result; }
public static void main(String[] args) { int[] digits = {9, 9, 9}; System.out.println(Arrays.toString(plusOne(digits))); // [1, 0, 0, 0] }}fun plusOne(digits: MutableList<Int>): List<Int> { for (i in digits.indices.reversed()) { if (digits[i] < 9) { digits[i]++ return digits } digits[i] = 0 } return listOf(1) + digits}
fun main() { val digits = mutableListOf(9, 9, 9) println(plusOne(digits)) // [1, 0, 0, 0]}List<int> plusOne(List<int> digits) { for (int i = digits.length - 1; i >= 0; i--) { if (digits[i] < 9) { digits[i]++; return digits; } digits[i] = 0; } return [1, ...digits];}
void main() { final digits = [9, 9, 9]; print(plusOne(digits)); // [1, 0, 0, 0]}8. Xóa phần tử trùng khỏi mảng đã sắp xếp (Remove Duplicates from Sorted Array)
Độ khó: Dễ · Chủ đề: Mảng, Two Pointers
Cho mảng nums đã sắp xếp tăng dần, xóa các phần tử trùng lặp tại chỗ sao cho mỗi giá trị chỉ xuất hiện 1 lần, trả về độ dài mới k. k phần tử đầu của nums sau khi biến đổi phải chứa các giá trị duy nhất theo đúng thứ tự ban đầu.
Ví dụ 1:
Input: nums = [1, 1, 2]Output: k = 2, nums = [1, 2, ...]Ví dụ 2:
Input: nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]Output: k = 5, nums = [0, 1, 2, 3, 4, ...]Ràng buộc:
1 <= len(nums) <= 3 * 10^4numsđã được sắp xếp tăng dần.
Xem đáp án
def remove_duplicates(nums): k = 1 for i in range(1, len(nums)): if nums[i] != nums[k - 1]: nums[k] = nums[i] k += 1 return k, nums[:k]
print(remove_duplicates([0, 0, 1, 1, 1, 2, 2, 3, 3, 4])) # (5, [0, 1, 2, 3, 4])#include <iostream>#include <vector>using namespace std;
int removeDuplicates(vector<int>& nums) { int k = 1; for (size_t i = 1; i < nums.size(); i++) { if (nums[i] != nums[k - 1]) nums[k++] = nums[i]; } return k;}
int main() { vector<int> nums = {0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; int k = removeDuplicates(nums); cout << "k = " << k << ", nums = ["; for (int i = 0; i < k; i++) cout << nums[i] << (i + 1 < k ? ", " : ""); cout << "]" << endl; // k = 5, nums = [0, 1, 2, 3, 4] return 0;}import java.util.Arrays;
public class Main { static int removeDuplicates(int[] nums) { int k = 1; for (int i = 1; i < nums.length; i++) { if (nums[i] != nums[k - 1]) nums[k++] = nums[i]; } return k; }
public static void main(String[] args) { int[] nums = {0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; int k = removeDuplicates(nums); System.out.println("k = " + k + ", nums = " + Arrays.toString(Arrays.copyOfRange(nums, 0, k))); // k = 5, nums = [0, 1, 2, 3, 4] }}fun removeDuplicates(nums: MutableList<Int>): Int { var k = 1 for (i in 1 until nums.size) { if (nums[i] != nums[k - 1]) { nums[k] = nums[i] k++ } } return k}
fun main() { val nums = mutableListOf(0, 0, 1, 1, 1, 2, 2, 3, 3, 4) val k = removeDuplicates(nums) println("k = $k, nums = ${nums.subList(0, k)}") // k = 5, nums = [0, 1, 2, 3, 4]}int removeDuplicates(List<int> nums) { int k = 1; for (int i = 1; i < nums.length; i++) { if (nums[i] != nums[k - 1]) { nums[k] = nums[i]; k++; } } return k;}
void main() { final nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]; final k = removeDuplicates(nums); print("k = $k, nums = ${nums.sublist(0, k)}"); // k = 5, nums = [0, 1, 2, 3, 4]}9. Số xuất hiện đúng 1 lần (Single Number)
Độ khó: Dễ · Chủ đề: Mảng, Bit Manipulation
Cho một mảng số nguyên, mỗi phần tử xuất hiện đúng 2 lần, ngoại trừ 1 phần tử chỉ xuất hiện đúng 1 lần. Tìm phần tử đó, yêu cầu độ phức tạp O(n) và không dùng thêm bộ nhớ phụ (không dùng set/dict).
Ví dụ 1:
Input: nums = [2, 2, 1]Output: 1Ví dụ 2:
Input: nums = [4, 1, 2, 1, 2]Output: 4Ràng buộc:
1 <= len(nums) <= 3 * 10^4
Xem đáp án
def single_number(nums): result = 0 for n in nums: result ^= n # a XOR a = 0, nên các cặp trùng sẽ tự triệt tiêu return result
print(single_number([4, 1, 2, 1, 2])) # 4#include <iostream>#include <vector>using namespace std;
int singleNumber(vector<int>& nums) { int result = 0; for (int n : nums) result ^= n; // a XOR a = 0, cac cap trung se tu triet tieu return result;}
int main() { vector<int> nums = {4, 1, 2, 1, 2}; cout << singleNumber(nums) << endl; // 4 return 0;}public class Main { static int singleNumber(int[] nums) { int result = 0; for (int n : nums) result ^= n; return result; }
public static void main(String[] args) { int[] nums = {4, 1, 2, 1, 2}; System.out.println(singleNumber(nums)); // 4 }}fun singleNumber(nums: List<Int>): Int { var result = 0 for (n in nums) result = result xor n return result}
fun main() { val nums = listOf(4, 1, 2, 1, 2) println(singleNumber(nums)) // 4}int singleNumber(List<int> nums) { int result = 0; for (var n in nums) result ^= n; return result;}
void main() { final nums = [4, 1, 2, 1, 2]; print(singleNumber(nums)); // 4}10. Phần tử xuất hiện nhiều hơn nửa mảng (Majority Element)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng nums kích thước n, tìm phần tử xuất hiện nhiều hơn n // 2 lần. Đề bài đảm bảo luôn tồn tại phần tử như vậy. Thử giải với độ phức tạp O(n) và O(1) bộ nhớ phụ (thuật toán Boyer-Moore Voting).
Ví dụ 1:
Input: nums = [3, 2, 3]Output: 3Ví dụ 2:
Input: nums = [2, 2, 1, 1, 1, 2, 2]Output: 2Ràng buộc:
1 <= len(nums) <= 5 * 10^4
Xem đáp án
def majority_element(nums): candidate, count = None, 0 for n in nums: if count == 0: candidate = n count += 1 if n == candidate else -1 return candidate
print(majority_element([2, 2, 1, 1, 1, 2, 2])) # 2#include <iostream>#include <vector>using namespace std;
int majorityElement(vector<int>& nums) { int candidate = 0, count = 0; for (int n : nums) { if (count == 0) candidate = n; count += (n == candidate) ? 1 : -1; } return candidate;}
int main() { vector<int> nums = {2, 2, 1, 1, 1, 2, 2}; cout << majorityElement(nums) << endl; // 2 return 0;}public class Main { static int majorityElement(int[] nums) { int candidate = 0, count = 0; for (int n : nums) { if (count == 0) candidate = n; count += (n == candidate) ? 1 : -1; } return candidate; }
public static void main(String[] args) { int[] nums = {2, 2, 1, 1, 1, 2, 2}; System.out.println(majorityElement(nums)); // 2 }}fun majorityElement(nums: List<Int>): Int { var candidate = 0 var count = 0 for (n in nums) { if (count == 0) candidate = n count += if (n == candidate) 1 else -1 } return candidate}
fun main() { val nums = listOf(2, 2, 1, 1, 1, 2, 2) println(majorityElement(nums)) // 2}int majorityElement(List<int> nums) { int candidate = 0; int count = 0; for (var n in nums) { if (count == 0) candidate = n; count += (n == candidate) ? 1 : -1; } return candidate;}
void main() { final nums = [2, 2, 1, 1, 1, 2, 2]; print(majorityElement(nums)); // 2}11. Tích lớn nhất của hai phần tử (Maximum Product of Two Elements in an Array)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng số nguyên dương nums, chọn 2 chỉ số phân biệt i, j sao cho (nums[i] - 1) * (nums[j] - 1) đạt giá trị lớn nhất. Trả về giá trị lớn nhất đó.
Ví dụ 1:
Input: nums = [3, 4, 5, 2]Output: 12Giải thích: Chọn 2 phần tử lớn nhất là 5 và 4: (5-1) * (4-1) = 12.Ví dụ 2:
Input: nums = [1, 5, 4, 5]Output: 16Ràng buộc:
2 <= len(nums) <= 5001 <= nums[i] <= 10^3
Xem đáp án
def max_product(nums): a, b = sorted(nums)[-2:] return (a - 1) * (b - 1)
print(max_product([3, 4, 5, 2])) # 12#include <iostream>#include <vector>#include <algorithm>using namespace std;
int maxProduct(vector<int>& nums) { sort(nums.begin(), nums.end()); int n = nums.size(); return (nums[n - 1] - 1) * (nums[n - 2] - 1);}
int main() { vector<int> nums = {3, 4, 5, 2}; cout << maxProduct(nums) << endl; // 12 return 0;}import java.util.Arrays;
public class Main { static int maxProduct(int[] nums) { int[] sorted = nums.clone(); Arrays.sort(sorted); int n = sorted.length; return (sorted[n - 1] - 1) * (sorted[n - 2] - 1); }
public static void main(String[] args) { int[] nums = {3, 4, 5, 2}; System.out.println(maxProduct(nums)); // 12 }}fun maxProduct(nums: List<Int>): Int { val sorted = nums.sorted() val n = sorted.size return (sorted[n - 1] - 1) * (sorted[n - 2] - 1)}
fun main() { val nums = listOf(3, 4, 5, 2) println(maxProduct(nums)) // 12}int maxProduct(List<int> nums) { final sorted = List.of(nums)..sort(); final n = sorted.length; return (sorted[n - 1] - 1) * (sorted[n - 2] - 1);}
void main() { final nums = [3, 4, 5, 2]; print(maxProduct(nums)); // 12}12. Tìm phần khác biệt của hai mảng (Find the Difference of Two Arrays)
Độ khó: Dễ · Chủ đề: Mảng, Hash Set
Cho 2 mảng số nguyên nums1, nums2. Trả về một danh sách gồm 2 danh sách: danh sách các phần tử phân biệt chỉ có trong nums1 (không có trong nums2), và danh sách các phần tử phân biệt chỉ có trong nums2.
Ví dụ 1:
Input: nums1 = [1, 2, 3], nums2 = [2, 4, 6]Output: [[1, 3], [4, 6]]Ví dụ 2:
Input: nums1 = [1, 2, 3, 3], nums2 = [1, 1, 2, 2]Output: [[3], []]Ràng buộc:
1 <= len(nums1), len(nums2) <= 1000
Xem đáp án
def find_difference(nums1, nums2): set1, set2 = set(nums1), set(nums2) return [list(set1 - set2), list(set2 - set1)]
print(find_difference([1, 2, 3], [2, 4, 6])) # [[1, 3], [4, 6]]#include <iostream>#include <vector>#include <unordered_set>using namespace std;
vector<vector<int>> findDifference(vector<int>& nums1, vector<int>& nums2) { unordered_set<int> set1(nums1.begin(), nums1.end()); unordered_set<int> set2(nums2.begin(), nums2.end()); vector<int> onlyIn1, onlyIn2; for (int x : set1) if (!set2.count(x)) onlyIn1.push_back(x); for (int x : set2) if (!set1.count(x)) onlyIn2.push_back(x); return {onlyIn1, onlyIn2};}
int main() { vector<int> nums1 = {1, 2, 3}, nums2 = {2, 4, 6}; auto result = findDifference(nums1, nums2); for (auto& part : result) { cout << "["; for (size_t i = 0; i < part.size(); i++) cout << part[i] << (i + 1 < part.size() ? ", " : ""); cout << "] "; } cout << endl; // [1, 3] [4, 6] return 0;}import java.util.*;
public class Main { static List<List<Integer>> findDifference(int[] nums1, int[] nums2) { Set<Integer> set1 = new HashSet<>(), set2 = new HashSet<>(); for (int n : nums1) set1.add(n); for (int n : nums2) set2.add(n); List<Integer> onlyIn1 = new ArrayList<>(set1); onlyIn1.removeAll(set2); List<Integer> onlyIn2 = new ArrayList<>(set2); onlyIn2.removeAll(set1); return Arrays.asList(onlyIn1, onlyIn2); }
public static void main(String[] args) { int[] nums1 = {1, 2, 3}, nums2 = {2, 4, 6}; System.out.println(findDifference(nums1, nums2)); // [[1, 3], [4, 6]] }}fun findDifference(nums1: List<Int>, nums2: List<Int>): List<List<Int>> { val set1 = nums1.toHashSet() val set2 = nums2.toHashSet() return listOf((set1 - set2).toList(), (set2 - set1).toList())}
fun main() { val nums1 = listOf(1, 2, 3) val nums2 = listOf(2, 4, 6) println(findDifference(nums1, nums2)) // [[1, 3], [4, 6]]}List<List<int>> findDifference(List<int> nums1, List<int> nums2) { final set1 = nums1.toSet(); final set2 = nums2.toSet(); return [set1.difference(set2).toList(), set2.difference(set1).toList()];}
void main() { final nums1 = [1, 2, 3]; final nums2 = [2, 4, 6]; print(findDifference(nums1, nums2)); // [[1, 3], [4, 6]]}13. Xoay mảng sang phải k vị trí (Rotate Array)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng nums, xoay mảng sang phải k bước (k có thể lớn hơn độ dài mảng).
Ví dụ 1:
Input: nums = [1, 2, 3, 4, 5, 6, 7], k = 3Output: [5, 6, 7, 1, 2, 3, 4]Ví dụ 2:
Input: nums = [-1, -100, 3, 99], k = 2Output: [3, 99, -1, -100]Ràng buộc:
1 <= len(nums) <= 10^50 <= k <= 10^5
Xem đáp án
def rotate(nums, k): n = len(nums) k %= n nums[:] = nums[-k:] + nums[:-k] if k else nums return nums
print(rotate([1, 2, 3, 4, 5, 6, 7], 3)) # [5, 6, 7, 1, 2, 3, 4]#include <iostream>#include <vector>#include <algorithm>using namespace std;
vector<int> rotate(vector<int>& nums, int k) { int n = nums.size(); k %= n; reverse(nums.begin(), nums.end()); reverse(nums.begin(), nums.begin() + k); reverse(nums.begin() + k, nums.end()); return nums;}
int main() { vector<int> nums = {1, 2, 3, 4, 5, 6, 7}; rotate(nums, 3); for (int x : nums) cout << x << " "; cout << endl; // 5 6 7 1 2 3 4 return 0;}import java.util.Arrays;import java.util.Collections;
public class Main { static int[] rotate(int[] nums, int k) { int n = nums.length; k %= n; reverse(nums, 0, n - 1); reverse(nums, 0, k - 1); reverse(nums, k, n - 1); return nums; }
static void reverse(int[] arr, int lo, int hi) { while (lo < hi) { int tmp = arr[lo]; arr[lo] = arr[hi]; arr[hi] = tmp; lo++; hi--; } }
public static void main(String[] args) { int[] nums = {1, 2, 3, 4, 5, 6, 7}; System.out.println(Arrays.toString(rotate(nums, 3))); // [5, 6, 7, 1, 2, 3, 4] }}fun rotate(nums: MutableList<Int>, k: Int): List<Int> { val n = nums.size val kk = k % n val rotated = nums.subList(n - kk, n) + nums.subList(0, n - kk) nums.clear() nums.addAll(rotated) return nums}
fun main() { val nums = mutableListOf(1, 2, 3, 4, 5, 6, 7) println(rotate(nums, 3)) // [5, 6, 7, 1, 2, 3, 4]}List<int> rotate(List<int> nums, int k) { final n = nums.length; final kk = k % n; final rotated = [...nums.sublist(n - kk), ...nums.sublist(0, n - kk)]; nums.setAll(0, rotated); return nums;}
void main() { final nums = [1, 2, 3, 4, 5, 6, 7]; print(rotate(nums, 3)); // [5, 6, 7, 1, 2, 3, 4]}14. Xáo trộn mảng (Shuffle the Array)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng nums gồm 2n phần tử theo dạng [x1, x2, ..., xn, y1, y2, ..., yn]. Trả về mảng theo thứ tự xen kẽ [x1, y1, x2, y2, ..., xn, yn].
Ví dụ 1:
Input: nums = [2, 5, 1, 3, 4, 7], n = 3Output: [2, 3, 5, 4, 1, 7]Giải thích: x = [2,5,1], y = [3,4,7] -> xen kẽ thành [2,3,5,4,1,7]Ví dụ 2:
Input: nums = [1, 2, 3, 4], n = 2Output: [1, 3, 2, 4]Ràng buộc:
1 <= n <= 500len(nums) == 2 * n
Xem đáp án
def shuffle(nums, n): result = [] for i in range(n): result.append(nums[i]) result.append(nums[i + n]) return result
print(shuffle([2, 5, 1, 3, 4, 7], 3)) # [2, 3, 5, 4, 1, 7]#include <iostream>#include <vector>using namespace std;
vector<int> shuffle(vector<int>& nums, int n) { vector<int> result; for (int i = 0; i < n; i++) { result.push_back(nums[i]); result.push_back(nums[i + n]); } return result;}
int main() { vector<int> nums = {2, 5, 1, 3, 4, 7}; for (int x : shuffle(nums, 3)) cout << x << " "; cout << endl; // 2 3 5 4 1 7 return 0;}import java.util.Arrays;
public class Main { static int[] shuffle(int[] nums, int n) { int[] result = new int[2 * n]; for (int i = 0; i < n; i++) { result[2 * i] = nums[i]; result[2 * i + 1] = nums[i + n]; } return result; }
public static void main(String[] args) { int[] nums = {2, 5, 1, 3, 4, 7}; System.out.println(Arrays.toString(shuffle(nums, 3))); // [2, 3, 5, 4, 1, 7] }}fun shuffle(nums: List<Int>, n: Int): List<Int> { val result = mutableListOf<Int>() for (i in 0 until n) { result.add(nums[i]) result.add(nums[i + n]) } return result}
fun main() { val nums = listOf(2, 5, 1, 3, 4, 7) println(shuffle(nums, 3)) // [2, 3, 5, 4, 1, 7]}List<int> shuffle(List<int> nums, int n) { final result = <int>[]; for (int i = 0; i < n; i++) { result.add(nums[i]); result.add(nums[i + n]); } return result;}
void main() { final nums = [2, 5, 1, 3, 4, 7]; print(shuffle(nums, 3)); // [2, 3, 5, 4, 1, 7]}15. Tam giác Pascal (Pascal’s Triangle)
Độ khó: Dễ · Chủ đề: Mảng, Toán học
Cho số nguyên numRows, sinh ra numRows dòng đầu tiên của tam giác Pascal, mỗi số bằng tổng 2 số ngay phía trên nó ở dòng trước.
Ví dụ 1:
Input: numRows = 5Output: [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]]Ví dụ 2:
Input: numRows = 1Output: [[1]]Ràng buộc:
1 <= numRows <= 30
Xem đáp án
def pascal_triangle(num_rows): triangle = [] for i in range(num_rows): row = [1] * (i + 1) for j in range(1, i): row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j] triangle.append(row) return triangle
print(pascal_triangle(5)) # [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]]#include <iostream>#include <vector>using namespace std;
vector<vector<int>> pascalTriangle(int numRows) { vector<vector<int>> triangle; for (int i = 0; i < numRows; i++) { vector<int> row(i + 1, 1); for (int j = 1; j < i; j++) { row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]; } triangle.push_back(row); } return triangle;}
int main() { for (auto& row : pascalTriangle(5)) { cout << "["; for (size_t j = 0; j < row.size(); j++) cout << row[j] << (j + 1 < row.size() ? "," : ""); cout << "] "; } cout << endl; // [1] [1,1] [1,2,1] [1,3,3,1] [1,4,6,4,1] return 0;}import java.util.*;
public class Main { static List<List<Integer>> pascalTriangle(int numRows) { List<List<Integer>> triangle = new ArrayList<>(); for (int i = 0; i < numRows; i++) { List<Integer> row = new ArrayList<>(Collections.nCopies(i + 1, 1)); for (int j = 1; j < i; j++) { row.set(j, triangle.get(i - 1).get(j - 1) + triangle.get(i - 1).get(j)); } triangle.add(row); } return triangle; }
public static void main(String[] args) { System.out.println(pascalTriangle(5)); // [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]] }}fun pascalTriangle(numRows: Int): List<List<Int>> { val triangle = mutableListOf<List<Int>>() for (i in 0 until numRows) { val row = MutableList(i + 1) { 1 } for (j in 1 until i) { row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j] } triangle.add(row) } return triangle}
fun main() { println(pascalTriangle(5)) // [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]}List<List<int>> pascalTriangle(int numRows) { final triangle = <List<int>>[]; for (int i = 0; i < numRows; i++) { final row = List<int>.filled(i + 1, 1); for (int j = 1; j < i; j++) { row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]; } triangle.add(row); } return triangle;}
void main() { print(pascalTriangle(5)); // [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]}16. Số lớn thứ ba khác nhau (Third Maximum Number)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng số nguyên nums, trả về số lớn thứ ba trong số các giá trị khác nhau của mảng. Nếu không tồn tại (có ít hơn 3 giá trị khác nhau), trả về số lớn nhất.
Ví dụ 1:
Input: nums = [3, 2, 1]Output: 1Ví dụ 2:
Input: nums = [1, 2]Output: 2Giải thích: Không có số lớn thứ 3 (chỉ có 2 giá trị khác nhau), trả về số lớn nhất.Ràng buộc:
1 <= len(nums) <= 10^4
Xem đáp án
def third_max(nums): distinct = sorted(set(nums), reverse=True) if len(distinct) >= 3: return distinct[2] return distinct[0]
print(third_max([2, 2, 3, 1])) # 1#include <iostream>#include <vector>#include <set>using namespace std;
int thirdMax(vector<int>& nums) { set<int, greater<int>> distinct(nums.begin(), nums.end()); if (distinct.size() >= 3) { auto it = distinct.begin(); advance(it, 2); return *it; } return *distinct.begin();}
int main() { vector<int> nums = {2, 2, 3, 1}; cout << thirdMax(nums) << endl; // 1 return 0;}import java.util.*;
public class Main { static int thirdMax(int[] nums) { TreeSet<Integer> distinct = new TreeSet<>(Collections.reverseOrder()); for (int n : nums) distinct.add(n); if (distinct.size() >= 3) { Iterator<Integer> it = distinct.iterator(); it.next(); it.next(); return it.next(); } return distinct.first(); }
public static void main(String[] args) { int[] nums = {2, 2, 3, 1}; System.out.println(thirdMax(nums)); // 1 }}fun thirdMax(nums: List<Int>): Int { val distinct = nums.toSortedSet(compareByDescending { it }).toList() return if (distinct.size >= 3) distinct[2] else distinct[0]}
fun main() { val nums = listOf(2, 2, 3, 1) println(thirdMax(nums)) // 1}int thirdMax(List<int> nums) { final distinct = nums.toSet().toList()..sort((a, b) => b.compareTo(a)); return distinct.length >= 3 ? distinct[2] : distinct[0];}
void main() { final nums = [2, 2, 3, 1]; print(thirdMax(nums)); // 1}17. Kiểm tra tồn tại số gấp đôi (Check If N and Its Double Exist)
Độ khó: Dễ · Chủ đề: Mảng, Hash Set
Cho mảng số nguyên arr, kiểm tra có tồn tại 2 chỉ số i != j sao cho arr[i] == 2 * arr[j] hay không.
Ví dụ 1:
Input: arr = [10, 2, 5, 3]Output: TrueGiải thích: 10 = 2 * 5Ví dụ 2:
Input: arr = [3, 1, 7, 11]Output: FalseRàng buộc:
2 <= len(arr) <= 500-10^3 <= arr[i] <= 10^3
Xem đáp án
def check_if_exist(arr): seen = set() for n in arr: if n * 2 in seen or (n % 2 == 0 and n // 2 in seen): return True seen.add(n) return False
print(check_if_exist([10, 2, 5, 3])) # True#include <iostream>#include <vector>#include <unordered_set>using namespace std;
bool checkIfExist(vector<int>& arr) { unordered_set<int> seen; for (int n : arr) { if (seen.count(n * 2) || (n % 2 == 0 && seen.count(n / 2))) return true; seen.insert(n); } return false;}
int main() { vector<int> arr = {10, 2, 5, 3}; cout << boolalpha << checkIfExist(arr) << endl; // true return 0;}import java.util.*;
public class Main { static boolean checkIfExist(int[] arr) { Set<Integer> seen = new HashSet<>(); for (int n : arr) { if (seen.contains(n * 2) || (n % 2 == 0 && seen.contains(n / 2))) return true; seen.add(n); } return false; }
public static void main(String[] args) { int[] arr = {10, 2, 5, 3}; System.out.println(checkIfExist(arr)); // true }}fun checkIfExist(arr: List<Int>): Boolean { val seen = mutableSetOf<Int>() for (n in arr) { if (n * 2 in seen || (n % 2 == 0 && n / 2 in seen)) return true seen.add(n) } return false}
fun main() { val arr = listOf(10, 2, 5, 3) println(checkIfExist(arr)) // true}bool checkIfExist(List<int> arr) { final seen = <int>{}; for (var n in arr) { if (seen.contains(n * 2) || (n % 2 == 0 && seen.contains(n ~/ 2))) return true; seen.add(n); } return false;}
void main() { final arr = [10, 2, 5, 3]; print(checkIfExist(arr)); // true}18. Chỉ số trung tâm của mảng (Find Pivot Index)
Độ khó: Dễ · Chủ đề: Mảng, Prefix Sum
Cho mảng nums, tìm chỉ số “trung tâm” (pivot) sao cho tổng các phần tử bên trái bằng tổng các phần tử bên phải chỉ số đó. Nếu không tồn tại, trả về -1. Nếu có nhiều đáp án, trả về chỉ số nhỏ nhất.
Ví dụ 1:
Input: nums = [1, 7, 3, 6, 5, 6]Output: 3Giải thích: Tổng bên trái index 3 = 1+7+3 = 11, tổng bên phải = 5+6 = 11.Ví dụ 2:
Input: nums = [1, 2, 3]Output: -1Ràng buộc:
1 <= len(nums) <= 10^4
Xem đáp án
def pivot_index(nums): total = sum(nums) left_sum = 0 for i, n in enumerate(nums): if left_sum == total - left_sum - n: return i left_sum += n return -1
print(pivot_index([1, 7, 3, 6, 5, 6])) # 3#include <iostream>#include <vector>#include <numeric>using namespace std;
int pivotIndex(vector<int>& nums) { int total = accumulate(nums.begin(), nums.end(), 0); int leftSum = 0; for (size_t i = 0; i < nums.size(); i++) { if (leftSum == total - leftSum - nums[i]) return i; leftSum += nums[i]; } return -1;}
int main() { vector<int> nums = {1, 7, 3, 6, 5, 6}; cout << pivotIndex(nums) << endl; // 3 return 0;}public class Main { static int pivotIndex(int[] nums) { int total = 0; for (int n : nums) total += n; int leftSum = 0; for (int i = 0; i < nums.length; i++) { if (leftSum == total - leftSum - nums[i]) return i; leftSum += nums[i]; } return -1; }
public static void main(String[] args) { int[] nums = {1, 7, 3, 6, 5, 6}; System.out.println(pivotIndex(nums)); // 3 }}fun pivotIndex(nums: List<Int>): Int { val total = nums.sum() var leftSum = 0 for (i in nums.indices) { if (leftSum == total - leftSum - nums[i]) return i leftSum += nums[i] } return -1}
fun main() { val nums = listOf(1, 7, 3, 6, 5, 6) println(pivotIndex(nums)) // 3}int pivotIndex(List<int> nums) { final total = nums.fold(0, (a, b) => a + b); int leftSum = 0; for (int i = 0; i < nums.length; i++) { if (leftSum == total - leftSum - nums[i]) return i; leftSum += nums[i]; } return -1;}
void main() { final nums = [1, 7, 3, 6, 5, 6]; print(pivotIndex(nums)); // 3}19. Tổng dồn của mảng (Running Sum of 1d Array)
Độ khó: Dễ · Chủ đề: Mảng, Prefix Sum
Cho mảng nums, trả về mảng result sao cho result[i] là tổng của nums[0] + nums[1] + ... + nums[i].
Ví dụ 1:
Input: nums = [1, 2, 3, 4]Output: [1, 3, 6, 10]Ví dụ 2:
Input: nums = [3, 1, 2, 10, 1]Output: [3, 4, 6, 16, 17]Ràng buộc:
1 <= len(nums) <= 10^3
Xem đáp án
def running_sum(nums): result = [] total = 0 for n in nums: total += n result.append(total) return result
print(running_sum([3, 1, 2, 10, 1])) # [3, 4, 6, 16, 17]#include <iostream>#include <vector>using namespace std;
vector<int> runningSum(vector<int>& nums) { vector<int> result; int total = 0; for (int n : nums) { total += n; result.push_back(total); } return result;}
int main() { vector<int> nums = {3, 1, 2, 10, 1}; for (int x : runningSum(nums)) cout << x << " "; cout << endl; // 3 4 6 16 17 return 0;}import java.util.Arrays;
public class Main { static int[] runningSum(int[] nums) { int[] result = new int[nums.length]; int total = 0; for (int i = 0; i < nums.length; i++) { total += nums[i]; result[i] = total; } return result; }
public static void main(String[] args) { int[] nums = {3, 1, 2, 10, 1}; System.out.println(Arrays.toString(runningSum(nums))); // [3, 4, 6, 16, 17] }}fun runningSum(nums: List<Int>): List<Int> { val result = mutableListOf<Int>() var total = 0 for (n in nums) { total += n result.add(total) } return result}
fun main() { val nums = listOf(3, 1, 2, 10, 1) println(runningSum(nums)) // [3, 4, 6, 16, 17]}List<int> runningSum(List<int> nums) { final result = <int>[]; int total = 0; for (var n in nums) { total += n; result.add(total); } return result;}
void main() { final nums = [3, 1, 2, 10, 1]; print(runningSum(nums)); // [3, 4, 6, 16, 17]}20. Kiểm tra mảng đơn điệu (Monotonic Array)
Độ khó: Dễ · Chủ đề: Mảng
Cho mảng số nguyên nums, kiểm tra mảng có đơn điệu hay không — nghĩa là mảng chỉ tăng dần (không giảm) hoặc chỉ giảm dần (không tăng) trên toàn bộ mảng.
Ví dụ 1:
Input: nums = [1, 2, 2, 3]Output: TrueVí dụ 2:
Input: nums = [1, 3, 2]Output: FalseRàng buộc:
1 <= len(nums) <= 10^5
Xem đáp án
def is_monotonic(nums): increasing = all(nums[i] <= nums[i + 1] for i in range(len(nums) - 1)) decreasing = all(nums[i] >= nums[i + 1] for i in range(len(nums) - 1)) return increasing or decreasing
print(is_monotonic([1, 3, 2])) # False#include <iostream>#include <vector>using namespace std;
bool isMonotonic(vector<int>& nums) { bool increasing = true, decreasing = true; for (size_t i = 0; i + 1 < nums.size(); i++) { if (nums[i] > nums[i + 1]) increasing = false; if (nums[i] < nums[i + 1]) decreasing = false; } return increasing || decreasing;}
int main() { vector<int> nums = {1, 3, 2}; cout << boolalpha << isMonotonic(nums) << endl; // false return 0;}public class Main { static boolean isMonotonic(int[] nums) { boolean increasing = true, decreasing = true; for (int i = 0; i + 1 < nums.length; i++) { if (nums[i] > nums[i + 1]) increasing = false; if (nums[i] < nums[i + 1]) decreasing = false; } return increasing || decreasing; }
public static void main(String[] args) { int[] nums = {1, 3, 2}; System.out.println(isMonotonic(nums)); // false }}fun isMonotonic(nums: List<Int>): Boolean { var increasing = true var decreasing = true for (i in 0 until nums.size - 1) { if (nums[i] > nums[i + 1]) increasing = false if (nums[i] < nums[i + 1]) decreasing = false } return increasing || decreasing}
fun main() { val nums = listOf(1, 3, 2) println(isMonotonic(nums)) // false}bool isMonotonic(List<int> nums) { bool increasing = true; bool decreasing = true; for (int i = 0; i < nums.length - 1; i++) { if (nums[i] > nums[i + 1]) increasing = false; if (nums[i] < nums[i + 1]) decreasing = false; } return increasing || decreasing;}
void main() { final nums = [1, 3, 2]; print(isMonotonic(nums)); // false}Nhóm 2: Chuỗi (String)
Phần tiêu đề “Nhóm 2: Chuỗi (String)”21. Ký tự không lặp đầu tiên (First Unique Character)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một chuỗi s, tìm chỉ số (index) của ký tự đầu tiên không lặp lại trong chuỗi. Nếu không có ký tự nào như vậy, trả về -1.
Ví dụ 1:
Input: s = "leetcode"Output: 0Giải thích: Ký tự 'l' ở vị trí 0 chỉ xuất hiện đúng 1 lần và là ký tự không lặp đầu tiên.Ví dụ 2:
Input: s = "aabb"Output: -1Giải thích: Mọi ký tự đều xuất hiện từ 2 lần trở lên.Ràng buộc:
1 <= len(s) <= 10^5schỉ gồm chữ cái thường tiếng Anh.
Xem đáp án
def first_unique_char(s): count = {} for c in s: count[c] = count.get(c, 0) + 1
for i, c in enumerate(s): if count[c] == 1: return i return -1
print(first_unique_char("leetcode")) # 0print(first_unique_char("aabb")) # -1#include <iostream>#include <string>#include <unordered_map>using namespace std;
int firstUniqueChar(const string& s) { unordered_map<char, int> count; for (char c : s) count[c]++;
for (int i = 0; i < (int)s.size(); i++) { if (count[s[i]] == 1) return i; } return -1;}
int main() { cout << firstUniqueChar("leetcode") << endl; // 0 cout << firstUniqueChar("aabb") << endl; // -1 return 0;}import java.util.*;
public class Main { static int firstUniqueChar(String s) { Map<Character, Integer> count = new HashMap<>(); for (char c : s.toCharArray()) { count.put(c, count.getOrDefault(c, 0) + 1); }
for (int i = 0; i < s.length(); i++) { if (count.get(s.charAt(i)) == 1) return i; } return -1; }
public static void main(String[] args) { System.out.println(firstUniqueChar("leetcode")); // 0 System.out.println(firstUniqueChar("aabb")); // -1 }}fun firstUniqueChar(s: String): Int { val count = HashMap<Char, Int>() for (c in s) count[c] = (count[c] ?: 0) + 1
for (i in s.indices) { if (count[s[i]] == 1) return i } return -1}
fun main() { println(firstUniqueChar("leetcode")) // 0 println(firstUniqueChar("aabb")) // -1}int firstUniqueChar(String s) { final count = <String, int>{}; for (var c in s.split('')) { count[c] = (count[c] ?? 0) + 1; }
for (var i = 0; i < s.length; i++) { if (count[s[i]] == 1) return i; } return -1;}
void main() { print(firstUniqueChar("leetcode")); // 0 print(firstUniqueChar("aabb")); // -1}22. Tiền tố chung dài nhất (Longest Common Prefix)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một danh sách các chuỗi, tìm tiền tố chung dài nhất của tất cả chuỗi trong danh sách. Nếu không có tiền tố chung, trả về chuỗi rỗng "".
Ví dụ 1:
Input: strs = ["flower", "flow", "flight"]Output: "fl"Ví dụ 2:
Input: strs = ["dog", "racecar", "car"]Output: ""Giải thích: Không có tiền tố chung giữa các chuỗi.Ràng buộc:
1 <= len(strs) <= 2000 <= len(strs[i]) <= 200
Xem đáp án
def longest_common_prefix(strs): if not strs: return ""
prefix = strs[0] for s in strs[1:]: while not s.startswith(prefix): prefix = prefix[:-1] if not prefix: return "" return prefix
print(longest_common_prefix(["flower", "flow", "flight"])) # flprint(longest_common_prefix(["dog", "racecar", "car"])) # (rỗng)#include <iostream>#include <vector>#include <string>using namespace std;
string longestCommonPrefix(vector<string>& strs) { if (strs.empty()) return "";
string prefix = strs[0]; for (size_t i = 1; i < strs.size(); i++) { while (strs[i].find(prefix) != 0) { prefix = prefix.substr(0, prefix.size() - 1); if (prefix.empty()) return ""; } } return prefix;}
int main() { vector<string> a = {"flower", "flow", "flight"}; vector<string> b = {"dog", "racecar", "car"}; cout << longestCommonPrefix(a) << endl; // fl cout << longestCommonPrefix(b) << endl; // (rong) return 0;}public class Main { static String longestCommonPrefix(String[] strs) { if (strs.length == 0) return "";
String prefix = strs[0]; for (int i = 1; i < strs.length; i++) { while (!strs[i].startsWith(prefix)) { prefix = prefix.substring(0, prefix.length() - 1); if (prefix.isEmpty()) return ""; } } return prefix; }
public static void main(String[] args) { System.out.println(longestCommonPrefix(new String[]{"flower", "flow", "flight"})); // fl System.out.println(longestCommonPrefix(new String[]{"dog", "racecar", "car"})); // (rong) }}fun longestCommonPrefix(strs: List<String>): String { if (strs.isEmpty()) return ""
var prefix = strs[0] for (s in strs.drop(1)) { while (!s.startsWith(prefix)) { prefix = prefix.dropLast(1) if (prefix.isEmpty()) return "" } } return prefix}
fun main() { println(longestCommonPrefix(listOf("flower", "flow", "flight"))) // fl println(longestCommonPrefix(listOf("dog", "racecar", "car"))) // (rong)}String longestCommonPrefix(List<String> strs) { if (strs.isEmpty) return "";
var prefix = strs[0]; for (var s in strs.skip(1)) { while (!s.startsWith(prefix)) { prefix = prefix.substring(0, prefix.length - 1); if (prefix.isEmpty) return ""; } } return prefix;}
void main() { print(longestCommonPrefix(["flower", "flow", "flight"])); // fl print(longestCommonPrefix(["dog", "racecar", "car"])); // (rong)}23. Palindrome chỉ tính chữ và số (Valid Palindrome)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một chuỗi s, kiểm tra xem chuỗi đó có phải là palindrome hay không, chỉ xét các ký tự chữ cái và chữ số (bỏ qua dấu câu, khoảng trắng), không phân biệt hoa thường.
Ví dụ 1:
Input: s = "A man, a plan, a canal: Panama"Output: TrueVí dụ 2:
Input: s = "race a car"Output: FalseRàng buộc:
1 <= len(s) <= 2 * 10^5sgồm ký tự ASCII in được.
Xem đáp án
def is_palindrome(s): cleaned = [c.lower() for c in s if c.isalnum()] return cleaned == cleaned[::-1]
print(is_palindrome("A man, a plan, a canal: Panama")) # Trueprint(is_palindrome("race a car")) # False#include <iostream>#include <string>#include <cctype>using namespace std;
bool isPalindrome(const string& s) { string cleaned; for (char c : s) { if (isalnum((unsigned char)c)) cleaned += tolower((unsigned char)c); } string reversed(cleaned.rbegin(), cleaned.rend()); return cleaned == reversed;}
int main() { cout << boolalpha; cout << isPalindrome("A man, a plan, a canal: Panama") << endl; // true cout << isPalindrome("race a car") << endl; // false return 0;}public class Main { static boolean isPalindrome(String s) { StringBuilder cleaned = new StringBuilder(); for (char c : s.toCharArray()) { if (Character.isLetterOrDigit(c)) cleaned.append(Character.toLowerCase(c)); } String forward = cleaned.toString(); String backward = cleaned.reverse().toString(); return forward.equals(backward); }
public static void main(String[] args) { System.out.println(isPalindrome("A man, a plan, a canal: Panama")); // true System.out.println(isPalindrome("race a car")); // false }}fun isPalindrome(s: String): Boolean { val cleaned = s.filter { it.isLetterOrDigit() }.lowercase() return cleaned == cleaned.reversed()}
fun main() { println(isPalindrome("A man, a plan, a canal: Panama")) // true println(isPalindrome("race a car")) // false}bool isPalindrome(String s) { final cleaned = s .split('') .where((c) => RegExp(r'[a-zA-Z0-9]').hasMatch(c)) .map((c) => c.toLowerCase()) .join(); return cleaned == cleaned.split('').reversed.join();}
void main() { print(isPalindrome("A man, a plan, a canal: Panama")); // true print(isPalindrome("race a car")); // false}24. Đảo thứ tự các từ (Reverse Words in a String)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một câu s có thể chứa nhiều khoảng trắng liên tiếp ở đầu, cuối hoặc giữa các từ. In ra câu đó với thứ tự các từ bị đảo ngược, các từ cách nhau đúng 1 khoảng trắng, không có khoảng trắng thừa ở đầu/cuối.
Ví dụ 1:
Input: s = "the sky is blue"Output: "blue is sky the"Ví dụ 2:
Input: s = " hello world "Output: "world hello"Ràng buộc:
1 <= len(s) <= 10^4schứa chữ cái tiếng Anh và khoảng trắng.
Xem đáp án
def reverse_words(s): words = s.split() return " ".join(reversed(words))
print(reverse_words("the sky is blue")) # blue is sky theprint(reverse_words(" hello world ")) # world hello#include <iostream>#include <sstream>#include <vector>#include <algorithm>using namespace std;
string reverseWords(const string& s) { istringstream iss(s); vector<string> words; string w; while (iss >> w) words.push_back(w); reverse(words.begin(), words.end());
string result; for (size_t i = 0; i < words.size(); i++) { if (i > 0) result += " "; result += words[i]; } return result;}
int main() { cout << reverseWords("the sky is blue") << endl; // blue is sky the cout << reverseWords(" hello world ") << endl; // world hello return 0;}import java.util.*;
public class Main { static String reverseWords(String s) { String[] words = s.trim().split("\\s+"); Collections.reverse(Arrays.asList(words)); return String.join(" ", words); }
public static void main(String[] args) { System.out.println(reverseWords("the sky is blue")); // blue is sky the System.out.println(reverseWords(" hello world ")); // world hello }}fun reverseWords(s: String): String { return s.trim().split(Regex("\\s+")).reversed().joinToString(" ")}
fun main() { println(reverseWords("the sky is blue")) // blue is sky the println(reverseWords(" hello world ")) // world hello}String reverseWords(String s) { final words = s.trim().split(RegExp(r'\s+')); return words.reversed.join(" ");}
void main() { print(reverseWords("the sky is blue")); // blue is sky the print(reverseWords(" hello world ")); // world hello}25. Chuỗi đồng dạng (Isomorphic Strings)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho 2 chuỗi s và t cùng độ dài, kiểm tra chúng có “đồng dạng” hay không: mỗi ký tự trong s có thể được thay thế để tạo thành t, với điều kiện ánh xạ 1-1 (2 ký tự khác nhau trong s không được ánh xạ tới cùng 1 ký tự trong t).
Ví dụ 1:
Input: s = "egg", t = "add"Output: TrueGiải thích: e->a, g->d.Ví dụ 2:
Input: s = "foo", t = "bar"Output: FalseGiải thích: 'o' phải ánh xạ tới cả 'a' và 'r', vi phạm ánh xạ 1-1.Ràng buộc:
1 <= len(s) == len(t) <= 5 * 10^4
Xem đáp án
def is_isomorphic(s, t): map_st = {} map_ts = {}
for a, b in zip(s, t): if a in map_st and map_st[a] != b: return False if b in map_ts and map_ts[b] != a: return False map_st[a] = b map_ts[b] = a return True
print(is_isomorphic("egg", "add")) # Trueprint(is_isomorphic("foo", "bar")) # False#include <iostream>#include <string>#include <unordered_map>using namespace std;
bool isIsomorphic(const string& s, const string& t) { unordered_map<char, char> mapST, mapTS;
for (size_t i = 0; i < s.size(); i++) { char a = s[i], b = t[i]; if (mapST.count(a) && mapST[a] != b) return false; if (mapTS.count(b) && mapTS[b] != a) return false; mapST[a] = b; mapTS[b] = a; } return true;}
int main() { cout << boolalpha; cout << isIsomorphic("egg", "add") << endl; // true cout << isIsomorphic("foo", "bar") << endl; // false return 0;}import java.util.*;
public class Main { static boolean isIsomorphic(String s, String t) { Map<Character, Character> mapST = new HashMap<>(); Map<Character, Character> mapTS = new HashMap<>();
for (int i = 0; i < s.length(); i++) { char a = s.charAt(i), b = t.charAt(i); if (mapST.containsKey(a) && mapST.get(a) != b) return false; if (mapTS.containsKey(b) && mapTS.get(b) != a) return false; mapST.put(a, b); mapTS.put(b, a); } return true; }
public static void main(String[] args) { System.out.println(isIsomorphic("egg", "add")); // true System.out.println(isIsomorphic("foo", "bar")); // false }}fun isIsomorphic(s: String, t: String): Boolean { val mapST = HashMap<Char, Char>() val mapTS = HashMap<Char, Char>()
for (i in s.indices) { val a = s[i] val b = t[i] if (mapST.containsKey(a) && mapST[a] != b) return false if (mapTS.containsKey(b) && mapTS[b] != a) return false mapST[a] = b mapTS[b] = a } return true}
fun main() { println(isIsomorphic("egg", "add")) // true println(isIsomorphic("foo", "bar")) // false}bool isIsomorphic(String s, String t) { final mapST = <String, String>{}; final mapTS = <String, String>{};
for (var i = 0; i < s.length; i++) { final a = s[i], b = t[i]; if (mapST.containsKey(a) && mapST[a] != b) return false; if (mapTS.containsKey(b) && mapTS[b] != a) return false; mapST[a] = b; mapTS[b] = a; } return true;}
void main() { print(isIsomorphic("egg", "add")); // true print(isIsomorphic("foo", "bar")); // false}26. Thư đe dọa (Ransom Note)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho 2 chuỗi ransom_note và magazine. Kiểm tra ransom_note có thể được “cắt ghép” hoàn toàn từ các ký tự có trong magazine hay không (mỗi ký tự trong magazine chỉ dùng được 1 lần).
Ví dụ 1:
Input: ransom_note = "aa", magazine = "aab"Output: TrueVí dụ 2:
Input: ransom_note = "aa", magazine = "ab"Output: FalseGiải thích: magazine chỉ có 1 chữ 'a' trong khi ransom_note cần 2 chữ 'a'.Ràng buộc:
1 <= len(ransom_note), len(magazine) <= 4.5 * 10^4- Chỉ gồm chữ cái thường tiếng Anh.
Xem đáp án
from collections import Counter
def can_construct(ransom_note, magazine): need = Counter(ransom_note) have = Counter(magazine) for c, cnt in need.items(): if have[c] < cnt: return False return True
print(can_construct("aa", "aab")) # Trueprint(can_construct("aa", "ab")) # False#include <iostream>#include <string>#include <unordered_map>using namespace std;
bool canConstruct(const string& ransomNote, const string& magazine) { unordered_map<char, int> have; for (char c : magazine) have[c]++;
for (char c : ransomNote) { if (have[c] <= 0) return false; have[c]--; } return true;}
int main() { cout << boolalpha; cout << canConstruct("aa", "aab") << endl; // true cout << canConstruct("aa", "ab") << endl; // false return 0;}import java.util.*;
public class Main { static boolean canConstruct(String ransomNote, String magazine) { Map<Character, Integer> have = new HashMap<>(); for (char c : magazine.toCharArray()) { have.put(c, have.getOrDefault(c, 0) + 1); }
for (char c : ransomNote.toCharArray()) { int cnt = have.getOrDefault(c, 0); if (cnt <= 0) return false; have.put(c, cnt - 1); } return true; }
public static void main(String[] args) { System.out.println(canConstruct("aa", "aab")); // true System.out.println(canConstruct("aa", "ab")); // false }}fun canConstruct(ransomNote: String, magazine: String): Boolean { val have = HashMap<Char, Int>() for (c in magazine) have[c] = (have[c] ?: 0) + 1
for (c in ransomNote) { val cnt = have[c] ?: 0 if (cnt <= 0) return false have[c] = cnt - 1 } return true}
fun main() { println(canConstruct("aa", "aab")) // true println(canConstruct("aa", "ab")) // false}bool canConstruct(String ransomNote, String magazine) { final have = <String, int>{}; for (var c in magazine.split('')) { have[c] = (have[c] ?? 0) + 1; }
for (var c in ransomNote.split('')) { final cnt = have[c] ?? 0; if (cnt <= 0) return false; have[c] = cnt - 1; } return true;}
void main() { print(canConstruct("aa", "aab")); // true print(canConstruct("aa", "ab")); // false}27. Số La Mã sang số nguyên (Roman to Integer)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một chuỗi số La Mã hợp lệ (gồm các ký tự I, V, X, L, C, D, M), chuyển nó thành số nguyên tương ứng. Lưu ý các trường hợp trừ như “IV” = 4, “IX” = 9, “XL” = 40.
Ví dụ 1:
Input: s = "III"Output: 3Ví dụ 2:
Input: s = "LVIII"Output: 58Giải thích: L = 50, V = 5, III = 3.Ràng buộc:
1 <= len(s) <= 15slà số La Mã hợp lệ trong khoảng [1, 3999].
Xem đáp án
def roman_to_int(s): value = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} total = 0
for i in range(len(s)): current = value[s[i]] if i + 1 < len(s) and current < value[s[i + 1]]: total -= current else: total += current return total
print(roman_to_int("III")) # 3print(roman_to_int("LVIII")) # 58print(roman_to_int("MCMXCIV"))# 1994#include <iostream>#include <string>#include <unordered_map>using namespace std;
int romanToInt(const string& s) { unordered_map<char, int> value = { {'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000} }; int total = 0;
for (size_t i = 0; i < s.size(); i++) { int current = value[s[i]]; if (i + 1 < s.size() && current < value[s[i + 1]]) { total -= current; } else { total += current; } } return total;}
int main() { cout << romanToInt("III") << endl; // 3 cout << romanToInt("LVIII") << endl; // 58 cout << romanToInt("MCMXCIV") << endl; // 1994 return 0;}import java.util.*;
public class Main { static int romanToInt(String s) { Map<Character, Integer> value = new HashMap<>(); value.put('I', 1); value.put('V', 5); value.put('X', 10); value.put('L', 50); value.put('C', 100); value.put('D', 500); value.put('M', 1000);
int total = 0; for (int i = 0; i < s.length(); i++) { int current = value.get(s.charAt(i)); if (i + 1 < s.length() && current < value.get(s.charAt(i + 1))) { total -= current; } else { total += current; } } return total; }
public static void main(String[] args) { System.out.println(romanToInt("III")); // 3 System.out.println(romanToInt("LVIII")); // 58 System.out.println(romanToInt("MCMXCIV")); // 1994 }}fun romanToInt(s: String): Int { val value = mapOf('I' to 1, 'V' to 5, 'X' to 10, 'L' to 50, 'C' to 100, 'D' to 500, 'M' to 1000) var total = 0
for (i in s.indices) { val current = value[s[i]]!! total += if (i + 1 < s.length && current < value[s[i + 1]]!!) -current else current } return total}
fun main() { println(romanToInt("III")) // 3 println(romanToInt("LVIII")) // 58 println(romanToInt("MCMXCIV")) // 1994}int romanToInt(String s) { final value = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}; int total = 0;
for (var i = 0; i < s.length; i++) { final current = value[s[i]]!; if (i + 1 < s.length && current < value[s[i + 1]]!) { total -= current; } else { total += current; } } return total;}
void main() { print(romanToInt("III")); // 3 print(romanToInt("LVIII")); // 58 print(romanToInt("MCMXCIV")); // 1994}28. Cộng hai số nhị phân (Add Binary)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho 2 chuỗi nhị phân a và b, trả về tổng của chúng, cũng dưới dạng một chuỗi nhị phân (không dùng int(x, 2) hoặc bin()).
Ví dụ 1:
Input: a = "11", b = "1"Output: "100"Ví dụ 2:
Input: a = "1010", b = "1011"Output: "10101"Ràng buộc:
1 <= len(a), len(b) <= 10^4a,bchỉ gồm ký tự ‘0’ hoặc ‘1’, không có số 0 thừa ở đầu (trừ khi bản thân số đó là “0”).
Xem đáp án
def add_binary(a, b): i, j = len(a) - 1, len(b) - 1 carry = 0 result = []
while i >= 0 or j >= 0 or carry: total = carry if i >= 0: total += int(a[i]) i -= 1 if j >= 0: total += int(b[j]) j -= 1 result.append(str(total % 2)) carry = total // 2
return "".join(reversed(result))
print(add_binary("11", "1")) # 100print(add_binary("1010", "1011"))# 10101#include <iostream>#include <string>#include <algorithm>using namespace std;
string addBinary(const string& a, const string& b) { int i = a.size() - 1, j = b.size() - 1; int carry = 0; string result;
while (i >= 0 || j >= 0 || carry) { int total = carry; if (i >= 0) total += a[i--] - '0'; if (j >= 0) total += b[j--] - '0'; result += char('0' + total % 2); carry = total / 2; }
reverse(result.begin(), result.end()); return result;}
int main() { cout << addBinary("11", "1") << endl; // 100 cout << addBinary("1010", "1011") << endl; // 10101 return 0;}public class Main { static String addBinary(String a, String b) { int i = a.length() - 1, j = b.length() - 1; int carry = 0; StringBuilder result = new StringBuilder();
while (i >= 0 || j >= 0 || carry != 0) { int total = carry; if (i >= 0) total += a.charAt(i--) - '0'; if (j >= 0) total += b.charAt(j--) - '0'; result.append(total % 2); carry = total / 2; }
return result.reverse().toString(); }
public static void main(String[] args) { System.out.println(addBinary("11", "1")); // 100 System.out.println(addBinary("1010", "1011")); // 10101 }}fun addBinary(a: String, b: String): String { var i = a.length - 1 var j = b.length - 1 var carry = 0 val result = StringBuilder()
while (i >= 0 || j >= 0 || carry != 0) { var total = carry if (i >= 0) { total += a[i] - '0'; i-- } if (j >= 0) { total += b[j] - '0'; j-- } result.append(total % 2) carry = total / 2 }
return result.reverse().toString()}
fun main() { println(addBinary("11", "1")) // 100 println(addBinary("1010", "1011")) // 10101}String addBinary(String a, String b) { int i = a.length - 1, j = b.length - 1; int carry = 0; final result = StringBuffer();
while (i >= 0 || j >= 0 || carry != 0) { int total = carry; if (i >= 0) total += a.codeUnitAt(i--) - '0'.codeUnitAt(0); if (j >= 0) total += b.codeUnitAt(j--) - '0'.codeUnitAt(0); result.write(total % 2); carry = total ~/ 2; }
return result.toString().split('').reversed.join();}
void main() { print(addBinary("11", "1")); // 100 print(addBinary("1010", "1011")); // 10101}29. Nén chuỗi (String Compression)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một chuỗi chỉ gồm chữ cái thường, nén chuỗi bằng cách thay các nhóm ký tự lặp liên tiếp thành <ký tự><số lần> (nếu số lần là 1 thì không ghi số). Ví dụ "aaabbc" thành "a3b2c".
Ví dụ 1:
Input: s = "aaabbc"Output: "a3b2c"Ví dụ 2:
Input: s = "abcd"Output: "abcd"Giải thích: Không có ký tự nào lặp lại nên chuỗi nén dài hơn hoặc bằng chuỗi gốc, giữ nguyên các nhóm độ dài 1.Ràng buộc:
1 <= len(s) <= 2 * 10^4schỉ gồm chữ cái thường.
Xem đáp án
def compress(s): result = [] i = 0 while i < len(s): j = i while j < len(s) and s[j] == s[i]: j += 1 count = j - i result.append(s[i] + (str(count) if count > 1 else "")) i = j return "".join(result)
print(compress("aaabbc")) # a3b2cprint(compress("abcd")) # abcd#include <iostream>#include <string>using namespace std;
string compress(const string& s) { string result; size_t i = 0; while (i < s.size()) { size_t j = i; while (j < s.size() && s[j] == s[i]) j++; int count = j - i; result += s[i]; if (count > 1) result += to_string(count); i = j; } return result;}
int main() { cout << compress("aaabbc") << endl; // a3b2c cout << compress("abcd") << endl; // abcd return 0;}public class Main { static String compress(String s) { StringBuilder result = new StringBuilder(); int i = 0; while (i < s.length()) { int j = i; while (j < s.length() && s.charAt(j) == s.charAt(i)) j++; int count = j - i; result.append(s.charAt(i)); if (count > 1) result.append(count); i = j; } return result.toString(); }
public static void main(String[] args) { System.out.println(compress("aaabbc")); // a3b2c System.out.println(compress("abcd")); // abcd }}fun compress(s: String): String { val result = StringBuilder() var i = 0 while (i < s.length) { var j = i while (j < s.length && s[j] == s[i]) j++ val count = j - i result.append(s[i]) if (count > 1) result.append(count) i = j } return result.toString()}
fun main() { println(compress("aaabbc")) // a3b2c println(compress("abcd")) // abcd}String compress(String s) { final result = StringBuffer(); int i = 0; while (i < s.length) { int j = i; while (j < s.length && s[j] == s[i]) j++; final count = j - i; result.write(s[i]); if (count > 1) result.write(count); i = j; } return result.toString();}
void main() { print(compress("aaabbc")); // a3b2c print(compress("abcd")); // abcd}30. Từ dài nhất bao gồm từ nhỏ hơn (Longest Word Built From Others)
Độ khó: Dễ · Chủ đề: Chuỗi
Cho một danh sách từ words, tìm từ dài nhất trong danh sách sao cho từ đó có thể được xây dựng dần dần bằng cách thêm từng ký tự một, mà tại mỗi bước tiền tố đó cũng phải có mặt trong danh sách. Nếu có nhiều đáp án cùng độ dài, trả về từ nhỏ nhất theo thứ tự từ điển.
Ví dụ 1:
Input: words = ["w", "wo", "wor", "worl", "world"]Output: "world"Giải thích: "world" có thể xây dần từ "w" -> "wo" -> "wor" -> "worl" -> "world", mỗi bước đều có trong danh sách.Ví dụ 2:
Input: words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]Output: "apple"Ràng buộc:
1 <= len(words) <= 10001 <= len(words[i]) <= 30
Xem đáp án
def longest_word(words): word_set = set(words) best = ""
for w in words: if all(w[:k] in word_set for k in range(1, len(w) + 1)): if len(w) > len(best) or (len(w) == len(best) and w < best): best = w return best
print(longest_word(["w", "wo", "wor", "worl", "world"]))print(longest_word(["a", "banana", "app", "appl", "ap", "apply", "apple"]))#include <iostream>#include <vector>#include <string>#include <unordered_set>using namespace std;
string longestWord(vector<string>& words) { unordered_set<string> wordSet(words.begin(), words.end()); string best = "";
for (const string& w : words) { bool ok = true; for (size_t k = 1; k <= w.size(); k++) { if (wordSet.find(w.substr(0, k)) == wordSet.end()) { ok = false; break; } } if (ok) { if (w.size() > best.size() || (w.size() == best.size() && w < best)) { best = w; } } } return best;}
int main() { vector<string> a = {"w", "wo", "wor", "worl", "world"}; vector<string> b = {"a", "banana", "app", "appl", "ap", "apply", "apple"}; cout << longestWord(a) << endl; cout << longestWord(b) << endl; return 0;}import java.util.*;
public class Main { static String longestWord(String[] words) { Set<String> wordSet = new HashSet<>(Arrays.asList(words)); String best = "";
for (String w : words) { boolean ok = true; for (int k = 1; k <= w.length(); k++) { if (!wordSet.contains(w.substring(0, k))) { ok = false; break; } } if (ok) { if (w.length() > best.length() || (w.length() == best.length() && w.compareTo(best) < 0)) { best = w; } } } return best; }
public static void main(String[] args) { System.out.println(longestWord(new String[]{"w", "wo", "wor", "worl", "world"})); System.out.println(longestWord(new String[]{"a", "banana", "app", "appl", "ap", "apply", "apple"})); }}fun longestWord(words: List<String>): String { val wordSet = words.toHashSet() var best = ""
for (w in words) { val ok = (1..w.length).all { k -> w.substring(0, k) in wordSet } if (ok) { if (w.length > best.length || (w.length == best.length && w < best)) { best = w } } } return best}
fun main() { println(longestWord(listOf("w", "wo", "wor", "worl", "world"))) println(longestWord(listOf("a", "banana", "app", "appl", "ap", "apply", "apple")))}String longestWord(List<String> words) { final wordSet = words.toSet(); String best = "";
for (var w in words) { bool ok = true; for (var k = 1; k <= w.length; k++) { if (!wordSet.contains(w.substring(0, k))) { ok = false; break; } } if (ok) { if (w.length > best.length || (w.length == best.length && w.compareTo(best) < 0)) { best = w; } } } return best;}
void main() { print(longestWord(["w", "wo", "wor", "worl", "world"])); print(longestWord(["a", "banana", "app", "appl", "ap", "apply", "apple"]));}31. Chuỗi con không lặp ký tự dài nhất (Longest Substring Without Repeating Characters)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho một chuỗi s, tìm độ dài của chuỗi con liên tiếp dài nhất mà không có ký tự nào lặp lại.
Ví dụ 1:
Input: s = "abcabcbb"Output: 3Giải thích: Chuỗi con dài nhất không lặp là "abc", độ dài 3.Ví dụ 2:
Input: s = "bbbbb"Output: 1Ví dụ 3:
Input: s = "pwwkew"Output: 3Giải thích: "wke" độ dài 3. Lưu ý "pwke" không phải chuỗi con liên tiếp.Ràng buộc:
0 <= len(s) <= 5 * 10^4
Xem đáp án
def length_of_longest_substring(s): last_seen = {} start = 0 best = 0
for i, c in enumerate(s): if c in last_seen and last_seen[c] >= start: start = last_seen[c] + 1 last_seen[c] = i best = max(best, i - start + 1)
return best
print(length_of_longest_substring("abcabcbb")) # 3print(length_of_longest_substring("bbbbb")) # 1print(length_of_longest_substring("pwwkew")) # 3#include <iostream>#include <string>#include <unordered_map>#include <algorithm>using namespace std;
int lengthOfLongestSubstring(const string& s) { unordered_map<char, int> lastSeen; int start = 0, best = 0;
for (int i = 0; i < (int)s.size(); i++) { char c = s[i]; if (lastSeen.count(c) && lastSeen[c] >= start) { start = lastSeen[c] + 1; } lastSeen[c] = i; best = max(best, i - start + 1); } return best;}
int main() { cout << lengthOfLongestSubstring("abcabcbb") << endl; // 3 cout << lengthOfLongestSubstring("bbbbb") << endl; // 1 cout << lengthOfLongestSubstring("pwwkew") << endl; // 3 return 0;}import java.util.*;
public class Main { static int lengthOfLongestSubstring(String s) { Map<Character, Integer> lastSeen = new HashMap<>(); int start = 0, best = 0;
for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (lastSeen.containsKey(c) && lastSeen.get(c) >= start) { start = lastSeen.get(c) + 1; } lastSeen.put(c, i); best = Math.max(best, i - start + 1); } return best; }
public static void main(String[] args) { System.out.println(lengthOfLongestSubstring("abcabcbb")); // 3 System.out.println(lengthOfLongestSubstring("bbbbb")); // 1 System.out.println(lengthOfLongestSubstring("pwwkew")); // 3 }}fun lengthOfLongestSubstring(s: String): Int { val lastSeen = HashMap<Char, Int>() var start = 0 var best = 0
for (i in s.indices) { val c = s[i] if (lastSeen.containsKey(c) && lastSeen[c]!! >= start) { start = lastSeen[c]!! + 1 } lastSeen[c] = i best = maxOf(best, i - start + 1) } return best}
fun main() { println(lengthOfLongestSubstring("abcabcbb")) // 3 println(lengthOfLongestSubstring("bbbbb")) // 1 println(lengthOfLongestSubstring("pwwkew")) // 3}int lengthOfLongestSubstring(String s) { final lastSeen = <String, int>{}; int start = 0, best = 0;
for (var i = 0; i < s.length; i++) { final c = s[i]; if (lastSeen.containsKey(c) && lastSeen[c]! >= start) { start = lastSeen[c]! + 1; } lastSeen[c] = i; best = best > (i - start + 1) ? best : (i - start + 1); } return best;}
void main() { print(lengthOfLongestSubstring("abcabcbb")); // 3 print(lengthOfLongestSubstring("bbbbb")); // 1 print(lengthOfLongestSubstring("pwwkew")); // 3}32. Nhóm các từ đồng dạng (Group Anagrams)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho một danh sách chuỗi, nhóm các chuỗi là anagram của nhau vào cùng một nhóm. Thứ tự các nhóm và thứ tự trong từng nhóm không quan trọng.
Ví dụ 1:
Input: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]Output: [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]]Ví dụ 2:
Input: strs = [""]Output: [[""]]Ràng buộc:
1 <= len(strs) <= 10^40 <= len(strs[i]) <= 100strs[i]chỉ gồm chữ cái thường.
Xem đáp án
from collections import defaultdict
def group_anagrams(strs): groups = defaultdict(list) for s in strs: key = "".join(sorted(s)) groups[key].append(s) return list(groups.values())
print(group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))print(group_anagrams([""]))#include <iostream>#include <vector>#include <string>#include <unordered_map>#include <algorithm>using namespace std;
vector<vector<string>> groupAnagrams(vector<string>& strs) { unordered_map<string, vector<string>> groups; for (const string& s : strs) { string key = s; sort(key.begin(), key.end()); groups[key].push_back(s); }
vector<vector<string>> result; for (auto& [key, group] : groups) result.push_back(group); return result;}
int main() { vector<string> strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; auto groups = groupAnagrams(strs); for (auto& g : groups) { cout << "["; for (auto& s : g) cout << s << " "; cout << "] "; } cout << endl; return 0;}import java.util.*;
public class Main { static List<List<String>> groupAnagrams(String[] strs) { Map<String, List<String>> groups = new HashMap<>(); for (String s : strs) { char[] chars = s.toCharArray(); Arrays.sort(chars); String key = new String(chars); groups.computeIfAbsent(key, k -> new ArrayList<>()).add(s); } return new ArrayList<>(groups.values()); }
public static void main(String[] args) { String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; System.out.println(groupAnagrams(strs)); }}fun groupAnagrams(strs: List<String>): List<List<String>> { val groups = HashMap<String, MutableList<String>>() for (s in strs) { val key = s.toCharArray().sorted().joinToString("") groups.getOrPut(key) { mutableListOf() }.add(s) } return groups.values.toList()}
fun main() { val strs = listOf("eat", "tea", "tan", "ate", "nat", "bat") println(groupAnagrams(strs))}List<List<String>> groupAnagrams(List<String> strs) { final groups = <String, List<String>>{}; for (var s in strs) { final chars = s.split('')..sort(); final key = chars.join(); groups.putIfAbsent(key, () => []).add(s); } return groups.values.toList();}
void main() { final strs = ["eat", "tea", "tan", "ate", "nat", "bat"]; print(groupAnagrams(strs));}33. Viết theo hình Zigzag (Zigzag Conversion)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho một chuỗi s và số hàng num_rows, sắp xếp các ký tự theo hình zigzag trên num_rows hàng (đi xuống rồi đi chéo lên, lặp lại), sau đó đọc lần lượt theo từng hàng để tạo thành chuỗi kết quả.
Ví dụ 1:
Input: s = "PAYPALISHIRING", num_rows = 3Output: "PAHNAPLSIIGYIR"Giải thích:P A H NA P L S I I GY I RVí dụ 2:
Input: s = "AB", num_rows = 1Output: "AB"Giải thích: Với 1 hàng, chuỗi không đổi.Ràng buộc:
1 <= len(s) <= 10001 <= num_rows <= 1000
Xem đáp án
def convert(s, num_rows): if num_rows == 1 or num_rows >= len(s): return s
rows = [""] * num_rows current_row = 0 going_down = False
for c in s: rows[current_row] += c if current_row == 0 or current_row == num_rows - 1: going_down = not going_down current_row += 1 if going_down else -1
return "".join(rows)
print(convert("PAYPALISHIRING", 3)) # PAHNAPLSIIGYIRprint(convert("AB", 1)) # AB#include <iostream>#include <vector>#include <string>using namespace std;
string convert(const string& s, int numRows) { if (numRows == 1 || numRows >= (int)s.size()) return s;
vector<string> rows(numRows); int currentRow = 0; bool goingDown = false;
for (char c : s) { rows[currentRow] += c; if (currentRow == 0 || currentRow == numRows - 1) goingDown = !goingDown; currentRow += goingDown ? 1 : -1; }
string result; for (auto& row : rows) result += row; return result;}
int main() { cout << convert("PAYPALISHIRING", 3) << endl; // PAHNAPLSIIGYIR cout << convert("AB", 1) << endl; // AB return 0;}public class Main { static String convert(String s, int numRows) { if (numRows == 1 || numRows >= s.length()) return s;
StringBuilder[] rows = new StringBuilder[numRows]; for (int i = 0; i < numRows; i++) rows[i] = new StringBuilder();
int currentRow = 0; boolean goingDown = false;
for (char c : s.toCharArray()) { rows[currentRow].append(c); if (currentRow == 0 || currentRow == numRows - 1) goingDown = !goingDown; currentRow += goingDown ? 1 : -1; }
StringBuilder result = new StringBuilder(); for (StringBuilder row : rows) result.append(row); return result.toString(); }
public static void main(String[] args) { System.out.println(convert("PAYPALISHIRING", 3)); // PAHNAPLSIIGYIR System.out.println(convert("AB", 1)); // AB }}fun convert(s: String, numRows: Int): String { if (numRows == 1 || numRows >= s.length) return s
val rows = Array(numRows) { StringBuilder() } var currentRow = 0 var goingDown = false
for (c in s) { rows[currentRow].append(c) if (currentRow == 0 || currentRow == numRows - 1) goingDown = !goingDown currentRow += if (goingDown) 1 else -1 }
return rows.joinToString("") { it.toString() }}
fun main() { println(convert("PAYPALISHIRING", 3)) // PAHNAPLSIIGYIR println(convert("AB", 1)) // AB}String convert(String s, int numRows) { if (numRows == 1 || numRows >= s.length) return s;
final rows = List.generate(numRows, (_) => StringBuffer()); int currentRow = 0; bool goingDown = false;
for (var c in s.split('')) { rows[currentRow].write(c); if (currentRow == 0 || currentRow == numRows - 1) goingDown = !goingDown; currentRow += goingDown ? 1 : -1; }
return rows.map((r) => r.toString()).join();}
void main() { print(convert("PAYPALISHIRING", 3)); // PAHNAPLSIIGYIR print(convert("AB", 1)); // AB}34. Chuỗi con đối xứng dài nhất (Longest Palindromic Substring)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho một chuỗi s, tìm chuỗi con liên tiếp dài nhất là palindrome (đối xứng). Nếu có nhiều đáp án cùng độ dài, trả về đáp án bất kỳ.
Ví dụ 1:
Input: s = "babad"Output: "bab"Giải thích: "aba" cũng là đáp án hợp lệ.Ví dụ 2:
Input: s = "cbbd"Output: "bb"Ràng buộc:
1 <= len(s) <= 1000
Xem đáp án
def longest_palindrome(s): if not s: return ""
def expand(left, right): while left >= 0 and right < len(s) and s[left] == s[right]: left -= 1 right += 1 return s[left + 1:right]
best = "" for i in range(len(s)): odd = expand(i, i) even = expand(i, i + 1) for candidate in (odd, even): if len(candidate) > len(best): best = candidate return best
print(longest_palindrome("babad")) # bab (hoặc aba)print(longest_palindrome("cbbd")) # bb#include <iostream>#include <string>using namespace std;
string expand(const string& s, int left, int right) { while (left >= 0 && right < (int)s.size() && s[left] == s[right]) { left--; right++; } return s.substr(left + 1, right - left - 1);}
string longestPalindrome(const string& s) { if (s.empty()) return "";
string best = ""; for (int i = 0; i < (int)s.size(); i++) { string odd = expand(s, i, i); string even = expand(s, i, i + 1); if (odd.size() > best.size()) best = odd; if (even.size() > best.size()) best = even; } return best;}
int main() { cout << longestPalindrome("babad") << endl; // bab (hoac aba) cout << longestPalindrome("cbbd") << endl; // bb return 0;}public class Main { static String expand(String s, int left, int right) { while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) { left--; right++; } return s.substring(left + 1, right); }
static String longestPalindrome(String s) { if (s.isEmpty()) return "";
String best = ""; for (int i = 0; i < s.length(); i++) { String odd = expand(s, i, i); String even = expand(s, i, i + 1); if (odd.length() > best.length()) best = odd; if (even.length() > best.length()) best = even; } return best; }
public static void main(String[] args) { System.out.println(longestPalindrome("babad")); // bab (hoac aba) System.out.println(longestPalindrome("cbbd")); // bb }}fun expand(s: String, l: Int, r: Int): String { var left = l var right = r while (left >= 0 && right < s.length && s[left] == s[right]) { left-- right++ } return s.substring(left + 1, right)}
fun longestPalindrome(s: String): String { if (s.isEmpty()) return ""
var best = "" for (i in s.indices) { val odd = expand(s, i, i) val even = expand(s, i, i + 1) if (odd.length > best.length) best = odd if (even.length > best.length) best = even } return best}
fun main() { println(longestPalindrome("babad")) // bab (hoac aba) println(longestPalindrome("cbbd")) // bb}String expand(String s, int l, int r) { int left = l, right = r; while (left >= 0 && right < s.length && s[left] == s[right]) { left--; right++; } return s.substring(left + 1, right);}
String longestPalindrome(String s) { if (s.isEmpty) return "";
String best = ""; for (var i = 0; i < s.length; i++) { final odd = expand(s, i, i); final even = expand(s, i, i + 1); if (odd.length > best.length) best = odd; if (even.length > best.length) best = even; } return best;}
void main() { print(longestPalindrome("babad")); // bab (hoac aba) print(longestPalindrome("cbbd")); // bb}35. Nhân hai số dạng chuỗi (Multiply Strings)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho 2 chuỗi số num1 và num2 biểu diễn 2 số nguyên không âm, trả về tích của chúng dưới dạng chuỗi, không dùng int() để chuyển toàn bộ chuỗi thành số hoặc phép nhân lớn có sẵn.
Ví dụ 1:
Input: num1 = "2", num2 = "3"Output: "6"Ví dụ 2:
Input: num1 = "123", num2 = "456"Output: "56088"Ràng buộc:
1 <= len(num1), len(num2) <= 200num1,num2chỉ gồm chữ số, không có số 0 thừa ở đầu (trừ khi bản thân số là “0”).
Xem đáp án
def multiply(num1, num2): if num1 == "0" or num2 == "0": return "0"
n1, n2 = len(num1), len(num2) result = [0] * (n1 + n2)
for i in range(n1 - 1, -1, -1): for j in range(n2 - 1, -1, -1): mul = (ord(num1[i]) - ord('0')) * (ord(num2[j]) - ord('0')) p1, p2 = i + j, i + j + 1 total = mul + result[p2]
result[p2] = total % 10 result[p1] += total // 10
result_str = "".join(map(str, result)).lstrip("0") return result_str if result_str else "0"
print(multiply("2", "3")) # 6print(multiply("123", "456")) # 56088#include <iostream>#include <string>#include <vector>using namespace std;
string multiply(const string& num1, const string& num2) { if (num1 == "0" || num2 == "0") return "0";
int n1 = num1.size(), n2 = num2.size(); vector<int> result(n1 + n2, 0);
for (int i = n1 - 1; i >= 0; i--) { for (int j = n2 - 1; j >= 0; j--) { int mul = (num1[i] - '0') * (num2[j] - '0'); int p1 = i + j, p2 = i + j + 1; int total = mul + result[p2];
result[p2] = total % 10; result[p1] += total / 10; } }
string resultStr; for (int d : result) resultStr += char('0' + d); size_t firstNonZero = resultStr.find_first_not_of('0'); if (firstNonZero == string::npos) return "0"; return resultStr.substr(firstNonZero);}
int main() { cout << multiply("2", "3") << endl; // 6 cout << multiply("123", "456") << endl; // 56088 return 0;}public class Main { static String multiply(String num1, String num2) { if (num1.equals("0") || num2.equals("0")) return "0";
int n1 = num1.length(), n2 = num2.length(); int[] result = new int[n1 + n2];
for (int i = n1 - 1; i >= 0; i--) { for (int j = n2 - 1; j >= 0; j--) { int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0'); int p1 = i + j, p2 = i + j + 1; int total = mul + result[p2];
result[p2] = total % 10; result[p1] += total / 10; } }
StringBuilder sb = new StringBuilder(); for (int d : result) sb.append(d); int i = 0; while (i < sb.length() - 1 && sb.charAt(i) == '0') i++; return sb.substring(i); }
public static void main(String[] args) { System.out.println(multiply("2", "3")); // 6 System.out.println(multiply("123", "456")); // 56088 }}fun multiply(num1: String, num2: String): String { if (num1 == "0" || num2 == "0") return "0"
val n1 = num1.length val n2 = num2.length val result = IntArray(n1 + n2)
for (i in n1 - 1 downTo 0) { for (j in n2 - 1 downTo 0) { val mul = (num1[i] - '0') * (num2[j] - '0') val p1 = i + j val p2 = i + j + 1 val total = mul + result[p2]
result[p2] = total % 10 result[p1] += total / 10 } }
val resultStr = result.joinToString("").trimStart('0') return resultStr.ifEmpty { "0" }}
fun main() { println(multiply("2", "3")) // 6 println(multiply("123", "456")) // 56088}String multiply(String num1, String num2) { if (num1 == "0" || num2 == "0") return "0";
final n1 = num1.length, n2 = num2.length; final result = List<int>.filled(n1 + n2, 0);
for (var i = n1 - 1; i >= 0; i--) { for (var j = n2 - 1; j >= 0; j--) { final mul = (num1.codeUnitAt(i) - 48) * (num2.codeUnitAt(j) - 48); final p1 = i + j, p2 = i + j + 1; final total = mul + result[p2];
result[p2] = total % 10; result[p1] += total ~/ 10; } }
var resultStr = result.join(); resultStr = resultStr.replaceFirst(RegExp(r'^0+(?=.)'), ''); return resultStr;}
void main() { print(multiply("2", "3")); // 6 print(multiply("123", "456")); // 56088}36. Cách giải mã (Decode Ways)
Độ khó: Trung bình · Chủ đề: Chuỗi
Một chuỗi số được mã hóa từ chữ cái theo quy tắc 'A' -> "1", 'B' -> "2", …, 'Z' -> "26". Cho một chuỗi số s, đếm xem có bao nhiêu cách giải mã được chuỗi đó thành chữ cái.
Ví dụ 1:
Input: s = "12"Output: 2Giải thích: Có thể giải mã thành "AB" (1 2) hoặc "L" (12).Ví dụ 2:
Input: s = "226"Output: 3Giải thích: "BZ" (2 26), "VF" (22 6), "BBF" (2 2 6).Ví dụ 3:
Input: s = "06"Output: 0Giải thích: "06" không hợp lệ vì không có ký tự nào tương ứng số bắt đầu bằng 0.Ràng buộc:
1 <= len(s) <= 100schỉ gồm chữ số.
Xem đáp án
def num_decodings(s): if not s or s[0] == '0': return 0
n = len(s) dp = [0] * (n + 1) dp[0] = 1 dp[1] = 1
for i in range(2, n + 1): one_digit = int(s[i - 1:i]) two_digit = int(s[i - 2:i])
if one_digit >= 1: dp[i] += dp[i - 1] if 10 <= two_digit <= 26: dp[i] += dp[i - 2]
return dp[n]
print(num_decodings("12")) # 2print(num_decodings("226")) # 3print(num_decodings("06")) # 0#include <iostream>#include <string>#include <vector>using namespace std;
int numDecodings(const string& s) { if (s.empty() || s[0] == '0') return 0;
int n = s.size(); vector<int> dp(n + 1, 0); dp[0] = 1; dp[1] = 1;
for (int i = 2; i <= n; i++) { int oneDigit = s[i - 1] - '0'; int twoDigit = (s[i - 2] - '0') * 10 + (s[i - 1] - '0');
if (oneDigit >= 1) dp[i] += dp[i - 1]; if (twoDigit >= 10 && twoDigit <= 26) dp[i] += dp[i - 2]; }
return dp[n];}
int main() { cout << numDecodings("12") << endl; // 2 cout << numDecodings("226") << endl; // 3 cout << numDecodings("06") << endl; // 0 return 0;}public class Main { static int numDecodings(String s) { if (s.isEmpty() || s.charAt(0) == '0') return 0;
int n = s.length(); int[] dp = new int[n + 1]; dp[0] = 1; dp[1] = 1;
for (int i = 2; i <= n; i++) { int oneDigit = s.charAt(i - 1) - '0'; int twoDigit = (s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0');
if (oneDigit >= 1) dp[i] += dp[i - 1]; if (twoDigit >= 10 && twoDigit <= 26) dp[i] += dp[i - 2]; }
return dp[n]; }
public static void main(String[] args) { System.out.println(numDecodings("12")); // 2 System.out.println(numDecodings("226")); // 3 System.out.println(numDecodings("06")); // 0 }}fun numDecodings(s: String): Int { if (s.isEmpty() || s[0] == '0') return 0
val n = s.length val dp = IntArray(n + 1) dp[0] = 1 dp[1] = 1
for (i in 2..n) { val oneDigit = s[i - 1] - '0' val twoDigit = (s[i - 2] - '0') * 10 + (s[i - 1] - '0')
if (oneDigit >= 1) dp[i] += dp[i - 1] if (twoDigit in 10..26) dp[i] += dp[i - 2] }
return dp[n]}
fun main() { println(numDecodings("12")) // 2 println(numDecodings("226")) // 3 println(numDecodings("06")) // 0}int numDecodings(String s) { if (s.isEmpty || s[0] == '0') return 0;
final n = s.length; final dp = List<int>.filled(n + 1, 0); dp[0] = 1; dp[1] = 1;
for (var i = 2; i <= n; i++) { final oneDigit = s.codeUnitAt(i - 1) - 48; final twoDigit = (s.codeUnitAt(i - 2) - 48) * 10 + (s.codeUnitAt(i - 1) - 48);
if (oneDigit >= 1) dp[i] += dp[i - 1]; if (twoDigit >= 10 && twoDigit <= 26) dp[i] += dp[i - 2]; }
return dp[n];}
void main() { print(numDecodings("12")); // 2 print(numDecodings("226")); // 3 print(numDecodings("06")); // 0}37. Ngắt từ (Word Break)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho một chuỗi s và một danh sách từ điển word_dict, kiểm tra xem s có thể được tách thành một dãy các từ liên tiếp, mỗi từ đều thuộc word_dict hay không (mỗi từ trong từ điển có thể dùng lại nhiều lần).
Ví dụ 1:
Input: s = "leetcode", word_dict = ["leet", "code"]Output: TrueGiải thích: "leetcode" tách được thành "leet code".Ví dụ 2:
Input: s = "catsandog", word_dict = ["cats", "dog", "sand", "and", "cat"]Output: FalseRàng buộc:
1 <= len(s) <= 3001 <= len(word_dict) <= 1000
Xem đáp án
def word_break(s, word_dict): words = set(word_dict) n = len(s) dp = [False] * (n + 1) dp[0] = True
for i in range(1, n + 1): for j in range(i): if dp[j] and s[j:i] in words: dp[i] = True break
return dp[n]
print(word_break("leetcode", ["leet", "code"])) # Trueprint(word_break("catsandog", ["cats", "dog", "sand", "and", "cat"])) # False#include <iostream>#include <vector>#include <string>#include <unordered_set>using namespace std;
bool wordBreak(const string& s, vector<string>& wordDict) { unordered_set<string> words(wordDict.begin(), wordDict.end()); int n = s.size(); vector<bool> dp(n + 1, false); dp[0] = true;
for (int i = 1; i <= n; i++) { for (int j = 0; j < i; j++) { if (dp[j] && words.count(s.substr(j, i - j))) { dp[i] = true; break; } } }
return dp[n];}
int main() { vector<string> d1 = {"leet", "code"}; vector<string> d2 = {"cats", "dog", "sand", "and", "cat"}; cout << boolalpha; cout << wordBreak("leetcode", d1) << endl; // true cout << wordBreak("catsandog", d2) << endl; // false return 0;}import java.util.*;
public class Main { static boolean wordBreak(String s, List<String> wordDict) { Set<String> words = new HashSet<>(wordDict); int n = s.length(); boolean[] dp = new boolean[n + 1]; dp[0] = true;
for (int i = 1; i <= n; i++) { for (int j = 0; j < i; j++) { if (dp[j] && words.contains(s.substring(j, i))) { dp[i] = true; break; } } }
return dp[n]; }
public static void main(String[] args) { System.out.println(wordBreak("leetcode", Arrays.asList("leet", "code"))); // true System.out.println(wordBreak("catsandog", Arrays.asList("cats", "dog", "sand", "and", "cat"))); // false }}fun wordBreak(s: String, wordDict: List<String>): Boolean { val words = wordDict.toHashSet() val n = s.length val dp = BooleanArray(n + 1) dp[0] = true
for (i in 1..n) { for (j in 0 until i) { if (dp[j] && s.substring(j, i) in words) { dp[i] = true break } } }
return dp[n]}
fun main() { println(wordBreak("leetcode", listOf("leet", "code"))) // true println(wordBreak("catsandog", listOf("cats", "dog", "sand", "and", "cat"))) // false}bool wordBreak(String s, List<String> wordDict) { final words = wordDict.toSet(); final n = s.length; final dp = List<bool>.filled(n + 1, false); dp[0] = true;
for (var i = 1; i <= n; i++) { for (var j = 0; j < i; j++) { if (dp[j] && words.contains(s.substring(j, i))) { dp[i] = true; break; } } }
return dp[n];}
void main() { print(wordBreak("leetcode", ["leet", "code"])); // true print(wordBreak("catsandog", ["cats", "dog", "sand", "and", "cat"])); // false}38. Máy tính biểu thức cơ bản (Basic Calculator)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho chuỗi s biểu diễn một biểu thức toán học gồm số nguyên không âm, dấu +, - và dấu ngoặc đơn (, ) (không có *, /). Tính và trả về kết quả của biểu thức.
Ví dụ 1:
Input: s = "1 + 1"Output: 2Ví dụ 2:
Input: s = "(1+(4+5+2)-3)+(6+8)"Output: 23Ràng buộc:
1 <= len(s) <= 3 * 10^5
Xem đáp án
def calculate(s): # Duy trì kết quả tích lũy, dấu hiện tại, và ngăn xếp (result, sign) mỗi khi gặp "(" stack = [] result = 0 number = 0 sign = 1
for c in s: if c.isdigit(): number = number * 10 + int(c) elif c in "+-": result += sign * number number = 0 sign = 1 if c == "+" else -1 elif c == "(": stack.append(result) stack.append(sign) result = 0 sign = 1 elif c == ")": result += sign * number number = 0 result *= stack.pop() # dấu trước dấu ngoặc result += stack.pop() # kết quả tích lũy trước dấu ngoặc
return result + sign * number
print(calculate("1 + 1")) # 2print(calculate("(1+(4+5+2)-3)+(6+8)")) # 23#include <iostream>#include <string>#include <stack>using namespace std;
int calculate(const string& s) { stack<int> st; int result = 0, number = 0, sign = 1;
for (char c : s) { if (isdigit(c)) { number = number * 10 + (c - '0'); } else if (c == '+' || c == '-') { result += sign * number; number = 0; sign = (c == '+') ? 1 : -1; } else if (c == '(') { st.push(result); st.push(sign); result = 0; sign = 1; } else if (c == ')') { result += sign * number; number = 0; int prevSign = st.top(); st.pop(); int prevResult = st.top(); st.pop(); result = prevResult + prevSign * result; } }
return result + sign * number;}
int main() { cout << calculate("1 + 1") << endl; // 2 cout << calculate("(1+(4+5+2)-3)+(6+8)") << endl; // 23 return 0;}import java.util.*;
public class Main { static int calculate(String s) { Deque<Integer> stack = new ArrayDeque<>(); int result = 0, number = 0, sign = 1;
for (char c : s.toCharArray()) { if (Character.isDigit(c)) { number = number * 10 + (c - '0'); } else if (c == '+' || c == '-') { result += sign * number; number = 0; sign = (c == '+') ? 1 : -1; } else if (c == '(') { stack.push(result); stack.push(sign); result = 0; sign = 1; } else if (c == ')') { result += sign * number; number = 0; int prevSign = stack.pop(); int prevResult = stack.pop(); result = prevResult + prevSign * result; } }
return result + sign * number; }
public static void main(String[] args) { System.out.println(calculate("1 + 1")); // 2 System.out.println(calculate("(1+(4+5+2)-3)+(6+8)")); // 23 }}fun calculate(s: String): Int { val stack = ArrayDeque<Int>() var result = 0 var number = 0 var sign = 1
for (c in s) { when { c.isDigit() -> number = number * 10 + (c - '0') c == '+' || c == '-' -> { result += sign * number number = 0 sign = if (c == '+') 1 else -1 } c == '(' -> { stack.addLast(result) stack.addLast(sign) result = 0 sign = 1 } c == ')' -> { result += sign * number number = 0 val prevSign = stack.removeLast() val prevResult = stack.removeLast() result = prevResult + prevSign * result } } }
return result + sign * number}
fun main() { println(calculate("1 + 1")) // 2 println(calculate("(1+(4+5+2)-3)+(6+8)")) // 23}int calculate(String s) { final stack = <int>[]; int result = 0, number = 0, sign = 1;
for (var c in s.split('')) { if (RegExp(r'\d').hasMatch(c)) { number = number * 10 + int.parse(c); } else if (c == '+' || c == '-') { result += sign * number; number = 0; sign = (c == '+') ? 1 : -1; } else if (c == '(') { stack.add(result); stack.add(sign); result = 0; sign = 1; } else if (c == ')') { result += sign * number; number = 0; final prevSign = stack.removeLast(); final prevResult = stack.removeLast(); result = prevResult + prevSign * result; } }
return result + sign * number;}
void main() { print(calculate("1 + 1")); // 2 print(calculate("(1+(4+5+2)-3)+(6+8)")); // 23}39. Chuỗi ngoặc hợp lệ có ký tự đại diện (Valid Parenthesis String)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho một chuỗi s chỉ gồm 3 loại ký tự '(', ')' và '*' (ký tự '*' có thể coi là '(', ')', hoặc chuỗi rỗng). Kiểm tra xem s có thể hợp lệ (mọi dấu ngoặc đều được đóng đúng cách) hay không.
Ví dụ 1:
Input: s = "()"Output: TrueVí dụ 2:
Input: s = "(*)"Output: TrueGiải thích: '*' đóng vai trò chuỗi rỗng.Ví dụ 3:
Input: s = "(*))"Output: TrueRàng buộc:
1 <= len(s) <= 100
Xem đáp án
def check_valid_string(s): low = high = 0 # low: số dấu '(' tối thiểu chưa đóng, high: tối đa
for c in s: if c == '(': low += 1 high += 1 elif c == ')': low -= 1 high -= 1 else: # '*' low -= 1 high += 1
if high < 0: return False low = max(low, 0)
return low == 0
print(check_valid_string("()")) # Trueprint(check_valid_string("(*)")) # Trueprint(check_valid_string("(*))")) # True#include <iostream>#include <string>#include <algorithm>using namespace std;
bool checkValidString(const string& s) { int low = 0, high = 0;
for (char c : s) { if (c == '(') { low++; high++; } else if (c == ')') { low--; high--; } else { low--; high++; }
if (high < 0) return false; low = max(low, 0); }
return low == 0;}
int main() { cout << boolalpha; cout << checkValidString("()") << endl; // true cout << checkValidString("(*)") << endl; // true cout << checkValidString("(*))") << endl; // true return 0;}public class Main { static boolean checkValidString(String s) { int low = 0, high = 0;
for (char c : s.toCharArray()) { if (c == '(') { low++; high++; } else if (c == ')') { low--; high--; } else { low--; high++; }
if (high < 0) return false; low = Math.max(low, 0); }
return low == 0; }
public static void main(String[] args) { System.out.println(checkValidString("()")); // true System.out.println(checkValidString("(*)")); // true System.out.println(checkValidString("(*))")); // true }}fun checkValidString(s: String): Boolean { var low = 0 var high = 0
for (c in s) { when (c) { '(' -> { low++; high++ } ')' -> { low--; high-- } else -> { low--; high++ } }
if (high < 0) return false low = maxOf(low, 0) }
return low == 0}
fun main() { println(checkValidString("()")) // true println(checkValidString("(*)")) // true println(checkValidString("(*))")) // true}bool checkValidString(String s) { int low = 0, high = 0;
for (var c in s.split('')) { if (c == '(') { low++; high++; } else if (c == ')') { low--; high--; } else { low--; high++; }
if (high < 0) return false; low = low < 0 ? 0 : low; }
return low == 0;}
void main() { print(checkValidString("()")); // true print(checkValidString("(*)")); // true print(checkValidString("(*))")); // true}40. So khớp mẫu ký tự đại diện (Wildcard Matching)
Độ khó: Trung bình · Chủ đề: Chuỗi
Cho chuỗi s và mẫu p chứa các ký tự thường, '?' (khớp đúng 1 ký tự bất kỳ) và '*' (khớp một dãy ký tự bất kỳ, kể cả chuỗi rỗng). Kiểm tra p có khớp toàn bộ s hay không.
Ví dụ 1:
Input: s = "aa", p = "a"Output: FalseGiải thích: "a" không khớp toàn bộ "aa".Ví dụ 2:
Input: s = "cb", p = "?a"Output: FalseVí dụ 3:
Input: s = "adceb", p = "*a*b"Output: TrueGiải thích: '*' khớp chuỗi rỗng, 'a' khớp 'a', '*' khớp "dce", 'b' khớp 'b'.Ràng buộc:
0 <= len(s), len(p) <= 2000
Xem đáp án
def is_match(s, p): m, n = len(s), len(p) dp = [[False] * (n + 1) for _ in range(m + 1)] dp[0][0] = True
for j in range(1, n + 1): if p[j - 1] == '*': dp[0][j] = dp[0][j - 1]
for i in range(1, m + 1): for j in range(1, n + 1): if p[j - 1] == '*': dp[i][j] = dp[i - 1][j] or dp[i][j - 1] elif p[j - 1] == '?' or p[j - 1] == s[i - 1]: dp[i][j] = dp[i - 1][j - 1]
return dp[m][n]
print(is_match("aa", "a")) # Falseprint(is_match("cb", "?a")) # Falseprint(is_match("adceb", "*a*b")) # True#include <iostream>#include <vector>#include <string>using namespace std;
bool isMatch(const string& s, const string& p) { int m = s.size(), n = p.size(); vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false)); dp[0][0] = true;
for (int j = 1; j <= n; j++) { if (p[j - 1] == '*') dp[0][j] = dp[0][j - 1]; }
for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (p[j - 1] == '*') { dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; } else if (p[j - 1] == '?' || p[j - 1] == s[i - 1]) { dp[i][j] = dp[i - 1][j - 1]; } } }
return dp[m][n];}
int main() { cout << boolalpha; cout << isMatch("aa", "a") << endl; // false cout << isMatch("cb", "?a") << endl; // false cout << isMatch("adceb", "*a*b") << endl; // true return 0;}public class Main { static boolean isMatch(String s, String p) { int m = s.length(), n = p.length(); boolean[][] dp = new boolean[m + 1][n + 1]; dp[0][0] = true;
for (int j = 1; j <= n; j++) { if (p.charAt(j - 1) == '*') dp[0][j] = dp[0][j - 1]; }
for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { char pc = p.charAt(j - 1); if (pc == '*') { dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; } else if (pc == '?' || pc == s.charAt(i - 1)) { dp[i][j] = dp[i - 1][j - 1]; } } }
return dp[m][n]; }
public static void main(String[] args) { System.out.println(isMatch("aa", "a")); // false System.out.println(isMatch("cb", "?a")); // false System.out.println(isMatch("adceb", "*a*b")); // true }}fun isMatch(s: String, p: String): Boolean { val m = s.length val n = p.length val dp = Array(m + 1) { BooleanArray(n + 1) } dp[0][0] = true
for (j in 1..n) { if (p[j - 1] == '*') dp[0][j] = dp[0][j - 1] }
for (i in 1..m) { for (j in 1..n) { val pc = p[j - 1] dp[i][j] = if (pc == '*') { dp[i - 1][j] || dp[i][j - 1] } else if (pc == '?' || pc == s[i - 1]) { dp[i - 1][j - 1] } else { false } } }
return dp[m][n]}
fun main() { println(isMatch("aa", "a")) // false println(isMatch("cb", "?a")) // false println(isMatch("adceb", "*a*b")) // true}bool isMatch(String s, String p) { final m = s.length, n = p.length; final dp = List.generate(m + 1, (_) => List<bool>.filled(n + 1, false)); dp[0][0] = true;
for (var j = 1; j <= n; j++) { if (p[j - 1] == '*') dp[0][j] = dp[0][j - 1]; }
for (var i = 1; i <= m; i++) { for (var j = 1; j <= n; j++) { final pc = p[j - 1]; if (pc == '*') { dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; } else if (pc == '?' || pc == s[i - 1]) { dp[i][j] = dp[i - 1][j - 1]; } } }
return dp[m][n];}
void main() { print(isMatch("aa", "a")); // false print(isMatch("cb", "?a")); // false print(isMatch("adceb", "*a*b")); // true}Nhóm 3: Hash Map & Two Pointers
Phần tiêu đề “Nhóm 3: Hash Map & Two Pointers”41. Tổng hai số (Two Sum)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho một mảng số nguyên nums và một số nguyên target, tìm chỉ số (index) của hai phần tử trong mảng sao cho tổng của chúng bằng target. Giả sử mỗi input có đúng một đáp án, và bạn không được dùng cùng một phần tử hai lần. Trả về hai chỉ số theo thứ tự bất kỳ.
Ví dụ 1:
Input: nums = [2, 7, 11, 15], target = 9Output: [0, 1]Giải thích: nums[0] + nums[1] = 2 + 7 = 9Ví dụ 2:
Input: nums = [3, 2, 4], target = 6Output: [1, 2]Giải thích: nums[1] + nums[2] = 2 + 4 = 6 (không được dùng lại nums[0] = 3)Ví dụ 3:
Input: nums = [3, 3], target = 6Output: [0, 1]Ràng buộc:
2 <= len(nums) <= 10^4-10^9 <= nums[i] <= 10^9- Luôn tồn tại đúng một cặp thỏa mãn.
Xem đáp án
def two_sum(nums, target): # O(n) thời gian: duyệt 1 lần, tra cứu phần bù trong hash map O(1) seen = {} # value -> index for i, num in enumerate(nums): complement = target - num if complement in seen: return [seen[complement], i] seen[num] = i return []
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]print(two_sum([3, 2, 4], 6)) # [1, 2]print(two_sum([3, 3], 6)) # [0, 1]#include <iostream>#include <vector>#include <unordered_map>using namespace std;
vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> seen; for (int i = 0; i < (int)nums.size(); i++) { int complement = target - nums[i]; if (seen.count(complement)) return {seen[complement], i}; seen[nums[i]] = i; } return {};}
int main() { vector<int> a = {2, 7, 11, 15}; vector<int> b = {3, 2, 4}; vector<int> c = {3, 3}; for (int x : twoSum(a, 9)) cout << x << " "; cout << endl; // 0 1 for (int x : twoSum(b, 6)) cout << x << " "; cout << endl; // 1 2 for (int x : twoSum(c, 6)) cout << x << " "; cout << endl; // 0 1 return 0;}import java.util.*;
public class Main { static int[] twoSum(int[] nums, int target) { Map<Integer, Integer> seen = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (seen.containsKey(complement)) return new int[]{seen.get(complement), i}; seen.put(nums[i], i); } return new int[]{}; }
public static void main(String[] args) { System.out.println(Arrays.toString(twoSum(new int[]{2, 7, 11, 15}, 9))); // [0, 1] System.out.println(Arrays.toString(twoSum(new int[]{3, 2, 4}, 6))); // [1, 2] System.out.println(Arrays.toString(twoSum(new int[]{3, 3}, 6))); // [0, 1] }}fun twoSum(nums: IntArray, target: Int): IntArray { val seen = HashMap<Int, Int>() for (i in nums.indices) { val complement = target - nums[i] if (seen.containsKey(complement)) return intArrayOf(seen[complement]!!, i) seen[nums[i]] = i } return intArrayOf()}
fun main() { println(twoSum(intArrayOf(2, 7, 11, 15), 9).toList()) // [0, 1] println(twoSum(intArrayOf(3, 2, 4), 6).toList()) // [1, 2] println(twoSum(intArrayOf(3, 3), 6).toList()) // [0, 1]}List<int> twoSum(List<int> nums, int target) { final seen = <int, int>{}; for (var i = 0; i < nums.length; i++) { final complement = target - nums[i]; if (seen.containsKey(complement)) return [seen[complement]!, i]; seen[nums[i]] = i; } return [];}
void main() { print(twoSum([2, 7, 11, 15], 9)); // [0, 1] print(twoSum([3, 2, 4], 6)); // [1, 2] print(twoSum([3, 3], 6)); // [0, 1]}42. Tổng hai số trên mảng đã sắp xếp (Two Sum II)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho một mảng số nguyên numbers đã sắp xếp tăng dần và một số target. Tìm hai chỉ số (bắt đầu từ 1) sao cho tổng hai phần tử bằng target, dùng kỹ thuật hai con trỏ (two pointers) với độ phức tạp O(n) và O(1) bộ nhớ phụ (không dùng hash map).
Ví dụ 1:
Input: numbers = [2, 7, 11, 15], target = 9Output: [1, 2]Giải thích: numbers[1] + numbers[2] = 2 + 7 = 9 (đánh số từ 1)Ví dụ 2:
Input: numbers = [2, 3, 4], target = 6Output: [1, 3]Ràng buộc:
2 <= len(numbers) <= 3 * 10^4numbersđã sắp xếp tăng dần.- Luôn tồn tại đúng một cặp thỏa mãn.
Xem đáp án
def two_sum_sorted(numbers, target): # O(n) thời gian, O(1) bộ nhớ: hai con trỏ đầu-cuối, thu hẹp dần left, right = 0, len(numbers) - 1 while left < right: total = numbers[left] + numbers[right] if total == target: return [left + 1, right + 1] elif total < target: left += 1 else: right -= 1 return []
print(two_sum_sorted([2, 7, 11, 15], 9)) # [1, 2]print(two_sum_sorted([2, 3, 4], 6)) # [1, 3]#include <iostream>#include <vector>using namespace std;
vector<int> twoSumSorted(vector<int>& numbers, int target) { int left = 0, right = numbers.size() - 1; while (left < right) { int total = numbers[left] + numbers[right]; if (total == target) return {left + 1, right + 1}; else if (total < target) left++; else right--; } return {};}
int main() { vector<int> a = {2, 7, 11, 15}; vector<int> b = {2, 3, 4}; for (int x : twoSumSorted(a, 9)) cout << x << " "; cout << endl; // 1 2 for (int x : twoSumSorted(b, 6)) cout << x << " "; cout << endl; // 1 3 return 0;}import java.util.*;
public class Main { static int[] twoSumSorted(int[] numbers, int target) { int left = 0, right = numbers.length - 1; while (left < right) { int total = numbers[left] + numbers[right]; if (total == target) return new int[]{left + 1, right + 1}; else if (total < target) left++; else right--; } return new int[]{}; }
public static void main(String[] args) { System.out.println(Arrays.toString(twoSumSorted(new int[]{2, 7, 11, 15}, 9))); // [1, 2] System.out.println(Arrays.toString(twoSumSorted(new int[]{2, 3, 4}, 6))); // [1, 3] }}fun twoSumSorted(numbers: IntArray, target: Int): IntArray { var left = 0 var right = numbers.size - 1 while (left < right) { val total = numbers[left] + numbers[right] when { total == target -> return intArrayOf(left + 1, right + 1) total < target -> left++ else -> right-- } } return intArrayOf()}
fun main() { println(twoSumSorted(intArrayOf(2, 7, 11, 15), 9).toList()) // [1, 2] println(twoSumSorted(intArrayOf(2, 3, 4), 6).toList()) // [1, 3]}List<int> twoSumSorted(List<int> numbers, int target) { int left = 0, right = numbers.length - 1; while (left < right) { final total = numbers[left] + numbers[right]; if (total == target) return [left + 1, right + 1]; else if (total < target) left++; else right--; } return [];}
void main() { print(twoSumSorted([2, 7, 11, 15], 9)); // [1, 2] print(twoSumSorted([2, 3, 4], 6)); // [1, 3]}43. Tổng ba số bằng 0 (3Sum)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho một mảng số nguyên nums, tìm tất cả các bộ ba (a, b, c) phân biệt theo chỉ số sao cho a + b + c = 0. Kết quả không được chứa bộ ba trùng lặp (theo giá trị).
Ví dụ 1:
Input: nums = [-1, 0, 1, 2, -1, -4]Output: [[-1, -1, 2], [-1, 0, 1]]Giải thích: Hai bộ ba trên có tổng bằng 0, không tính trùng.Ví dụ 2:
Input: nums = [0, 1, 1]Output: []Ví dụ 3:
Input: nums = [0, 0, 0]Output: [[0, 0, 0]]Ràng buộc:
3 <= len(nums) <= 3000-10^5 <= nums[i] <= 10^5
Xem đáp án
def three_sum(nums): # O(n^2): sắp xếp trước, cố định 1 phần tử, dùng 2 con trỏ cho phần còn lại nums.sort() result = [] n = len(nums)
for i in range(n - 2): if i > 0 and nums[i] == nums[i - 1]: continue # bỏ qua để tránh bộ ba trùng lặp left, right = i + 1, n - 1 while left < right: total = nums[i] + nums[left] + nums[right] if total == 0: result.append([nums[i], nums[left], nums[right]]) while left < right and nums[left] == nums[left + 1]: left += 1 while left < right and nums[right] == nums[right - 1]: right -= 1 left += 1 right -= 1 elif total < 0: left += 1 else: right -= 1
return result
print(three_sum([-1, 0, 1, 2, -1, -4])) # [[-1, -1, 2], [-1, 0, 1]]print(three_sum([0, 1, 1])) # []print(three_sum([0, 0, 0])) # [[0, 0, 0]]#include <iostream>#include <vector>#include <algorithm>using namespace std;
vector<vector<int>> threeSum(vector<int>& nums) { sort(nums.begin(), nums.end()); vector<vector<int>> result; int n = nums.size();
for (int i = 0; i < n - 2; i++) { if (i > 0 && nums[i] == nums[i - 1]) continue; int left = i + 1, right = n - 1; while (left < right) { int total = nums[i] + nums[left] + nums[right]; if (total == 0) { result.push_back({nums[i], nums[left], nums[right]}); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (total < 0) { left++; } else { right--; } } }
return result;}
int main() { vector<int> nums = {-1, 0, 1, 2, -1, -4}; for (auto& t : threeSum(nums)) { cout << "[" << t[0] << "," << t[1] << "," << t[2] << "] "; } cout << endl; // [-1,-1,2] [-1,0,1] return 0;}import java.util.*;
public class Main { static List<List<Integer>> threeSum(int[] nums) { Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); int n = nums.length;
for (int i = 0; i < n - 2; i++) { if (i > 0 && nums[i] == nums[i - 1]) continue; int left = i + 1, right = n - 1; while (left < right) { int total = nums[i] + nums[left] + nums[right]; if (total == 0) { result.add(Arrays.asList(nums[i], nums[left], nums[right])); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (total < 0) { left++; } else { right--; } } }
return result; }
public static void main(String[] args) { System.out.println(threeSum(new int[]{-1, 0, 1, 2, -1, -4})); // [[-1, -1, 2], [-1, 0, 1]] }}fun threeSum(nums: IntArray): List<List<Int>> { nums.sort() val result = mutableListOf<List<Int>>() val n = nums.size
for (i in 0 until n - 2) { if (i > 0 && nums[i] == nums[i - 1]) continue var left = i + 1 var right = n - 1 while (left < right) { val total = nums[i] + nums[left] + nums[right] when { total == 0 -> { result.add(listOf(nums[i], nums[left], nums[right])) while (left < right && nums[left] == nums[left + 1]) left++ while (left < right && nums[right] == nums[right - 1]) right-- left++ right-- } total < 0 -> left++ else -> right-- } } }
return result}
fun main() { println(threeSum(intArrayOf(-1, 0, 1, 2, -1, -4))) // [[-1, -1, 2], [-1, 0, 1]]}List<List<int>> threeSum(List<int> nums) { nums.sort(); final result = <List<int>>[]; final n = nums.length;
for (var i = 0; i < n - 2; i++) { if (i > 0 && nums[i] == nums[i - 1]) continue; var left = i + 1, right = n - 1; while (left < right) { final total = nums[i] + nums[left] + nums[right]; if (total == 0) { result.add([nums[i], nums[left], nums[right]]); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (total < 0) { left++; } else { right--; } } }
return result;}
void main() { print(threeSum([-1, 0, 1, 2, -1, -4])); // [[-1, -1, 2], [-1, 0, 1]]}44. Bộ ba gần target nhất (3Sum Closest)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng số nguyên nums và số target, tìm tổng của 3 phần tử trong nums sao cho tổng đó gần target nhất. Trả về tổng đó.
Ví dụ 1:
Input: nums = [-1, 2, 1, -4], target = 1Output: 2Giải thích: Tổng gần 1 nhất là -1 + 2 + 1 = 2Ví dụ 2:
Input: nums = [0, 0, 0], target = 1Output: 0Ràng buộc:
3 <= len(nums) <= 500-1000 <= nums[i] <= 1000
Xem đáp án
def three_sum_closest(nums, target): nums.sort() n = len(nums) closest = nums[0] + nums[1] + nums[2]
for i in range(n - 2): left, right = i + 1, n - 1 while left < right: total = nums[i] + nums[left] + nums[right] if abs(total - target) < abs(closest - target): closest = total if total < target: left += 1 elif total > target: right -= 1 else: return total # bằng target luôn là gần nhất có thể
return closest
print(three_sum_closest([-1, 2, 1, -4], 1)) # 2print(three_sum_closest([0, 0, 0], 1)) # 0#include <iostream>#include <vector>#include <algorithm>#include <cmath>using namespace std;
int threeSumClosest(vector<int>& nums, int target) { sort(nums.begin(), nums.end()); int n = nums.size(); int closest = nums[0] + nums[1] + nums[2];
for (int i = 0; i < n - 2; i++) { int left = i + 1, right = n - 1; while (left < right) { int total = nums[i] + nums[left] + nums[right]; if (abs(total - target) < abs(closest - target)) closest = total; if (total < target) left++; else if (total > target) right--; else return total; } }
return closest;}
int main() { vector<int> a = {-1, 2, 1, -4}; vector<int> b = {0, 0, 0}; cout << threeSumClosest(a, 1) << endl; // 2 cout << threeSumClosest(b, 1) << endl; // 0 return 0;}import java.util.*;
public class Main { static int threeSumClosest(int[] nums, int target) { Arrays.sort(nums); int n = nums.length; int closest = nums[0] + nums[1] + nums[2];
for (int i = 0; i < n - 2; i++) { int left = i + 1, right = n - 1; while (left < right) { int total = nums[i] + nums[left] + nums[right]; if (Math.abs(total - target) < Math.abs(closest - target)) closest = total; if (total < target) left++; else if (total > target) right--; else return total; } }
return closest; }
public static void main(String[] args) { System.out.println(threeSumClosest(new int[]{-1, 2, 1, -4}, 1)); // 2 System.out.println(threeSumClosest(new int[]{0, 0, 0}, 1)); // 0 }}import kotlin.math.abs
fun threeSumClosest(nums: IntArray, target: Int): Int { nums.sort() val n = nums.size var closest = nums[0] + nums[1] + nums[2]
for (i in 0 until n - 2) { var left = i + 1 var right = n - 1 while (left < right) { val total = nums[i] + nums[left] + nums[right] if (abs(total - target) < abs(closest - target)) closest = total when { total < target -> left++ total > target -> right-- else -> return total } } }
return closest}
fun main() { println(threeSumClosest(intArrayOf(-1, 2, 1, -4), 1)) // 2 println(threeSumClosest(intArrayOf(0, 0, 0), 1)) // 0}int threeSumClosest(List<int> nums, int target) { nums.sort(); final n = nums.length; int closest = nums[0] + nums[1] + nums[2];
for (var i = 0; i < n - 2; i++) { var left = i + 1, right = n - 1; while (left < right) { final total = nums[i] + nums[left] + nums[right]; if ((total - target).abs() < (closest - target).abs()) closest = total; if (total < target) { left++; } else if (total > target) { right--; } else { return total; } } }
return closest;}
void main() { print(threeSumClosest([-1, 2, 1, -4], 1)); // 2 print(threeSumClosest([0, 0, 0], 1)); // 0}45. Tổng bốn số (4Sum)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng số nguyên nums và số target, tìm tất cả các bộ bốn số phân biệt theo chỉ số có tổng bằng target, không trùng lặp bộ giá trị.
Ví dụ 1:
Input: nums = [1, 0, -1, 0, -2, 2], target = 0Output: [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]Ví dụ 2:
Input: nums = [2, 2, 2, 2, 2], target = 8Output: [[2, 2, 2, 2]]Ràng buộc:
1 <= len(nums) <= 200-10^9 <= nums[i], target <= 10^9
Xem đáp án
def four_sum(nums, target): nums.sort() n = len(nums) result = []
for i in range(n - 3): if i > 0 and nums[i] == nums[i - 1]: continue for j in range(i + 1, n - 2): if j > i + 1 and nums[j] == nums[j - 1]: continue left, right = j + 1, n - 1 while left < right: total = nums[i] + nums[j] + nums[left] + nums[right] if total == target: result.append([nums[i], nums[j], nums[left], nums[right]]) while left < right and nums[left] == nums[left + 1]: left += 1 while left < right and nums[right] == nums[right - 1]: right -= 1 left += 1 right -= 1 elif total < target: left += 1 else: right -= 1
return result
print(four_sum([1, 0, -1, 0, -2, 2], 0))print(four_sum([2, 2, 2, 2, 2], 8))#include <iostream>#include <vector>#include <algorithm>using namespace std;
vector<vector<long long>> fourSum(vector<long long>& nums, long long target) { sort(nums.begin(), nums.end()); int n = nums.size(); vector<vector<long long>> result;
for (int i = 0; i < n - 3; i++) { if (i > 0 && nums[i] == nums[i - 1]) continue; for (int j = i + 1; j < n - 2; j++) { if (j > i + 1 && nums[j] == nums[j - 1]) continue; int left = j + 1, right = n - 1; while (left < right) { long long total = nums[i] + nums[j] + nums[left] + nums[right]; if (total == target) { result.push_back({nums[i], nums[j], nums[left], nums[right]}); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (total < target) { left++; } else { right--; } } } }
return result;}
int main() { vector<long long> nums = {1, 0, -1, 0, -2, 2}; for (auto& q : fourSum(nums, 0)) { cout << "[" << q[0] << "," << q[1] << "," << q[2] << "," << q[3] << "] "; } cout << endl; return 0;}import java.util.*;
public class Main { static List<List<Long>> fourSum(long[] nums, long target) { Arrays.sort(nums); int n = nums.length; List<List<Long>> result = new ArrayList<>();
for (int i = 0; i < n - 3; i++) { if (i > 0 && nums[i] == nums[i - 1]) continue; for (int j = i + 1; j < n - 2; j++) { if (j > i + 1 && nums[j] == nums[j - 1]) continue; int left = j + 1, right = n - 1; while (left < right) { long total = nums[i] + nums[j] + nums[left] + nums[right]; if (total == target) { result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (total < target) { left++; } else { right--; } } } }
return result; }
public static void main(String[] args) { System.out.println(fourSum(new long[]{1, 0, -1, 0, -2, 2}, 0)); }}fun fourSum(nums: LongArray, target: Long): List<List<Long>> { nums.sort() val n = nums.size val result = mutableListOf<List<Long>>()
for (i in 0 until n - 3) { if (i > 0 && nums[i] == nums[i - 1]) continue for (j in i + 1 until n - 2) { if (j > i + 1 && nums[j] == nums[j - 1]) continue var left = j + 1 var right = n - 1 while (left < right) { val total = nums[i] + nums[j] + nums[left] + nums[right] when { total == target -> { result.add(listOf(nums[i], nums[j], nums[left], nums[right])) while (left < right && nums[left] == nums[left + 1]) left++ while (left < right && nums[right] == nums[right - 1]) right-- left++ right-- } total < target -> left++ else -> right-- } } } }
return result}
fun main() { println(fourSum(longArrayOf(1, 0, -1, 0, -2, 2), 0))}List<List<int>> fourSum(List<int> nums, int target) { nums.sort(); final n = nums.length; final result = <List<int>>[];
for (var i = 0; i < n - 3; i++) { if (i > 0 && nums[i] == nums[i - 1]) continue; for (var j = i + 1; j < n - 2; j++) { if (j > i + 1 && nums[j] == nums[j - 1]) continue; var left = j + 1, right = n - 1; while (left < right) { final total = nums[i] + nums[j] + nums[left] + nums[right]; if (total == target) { result.add([nums[i], nums[j], nums[left], nums[right]]); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (total < target) { left++; } else { right--; } } } }
return result;}
void main() { print(fourSum([1, 0, -1, 0, -2, 2], 0));}46. Chứa nhiều nước nhất (Container With Most Water)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng số nguyên dương height, trong đó height[i] là chiều cao của cột thứ i. Chọn 2 cột i, j sao cho cùng với trục hoành, chúng tạo thành một cái “thùng” chứa được nhiều nước nhất (diện tích = min(height[i], height[j]) * |i - j|). Trả về diện tích lớn nhất đó.
Ví dụ 1:
Input: height = [1, 8, 6, 2, 5, 4, 8, 3, 7]Output: 49Giải thích: Cột index 1 (cao 8) và index 8 (cao 7): min(8,7) * (8-1) = 7*7 = 49Ví dụ 2:
Input: height = [1, 1]Output: 1Ràng buộc:
2 <= len(height) <= 10^50 <= height[i] <= 3 * 10^4
Xem đáp án
def max_area(height): # O(n): hai con trỏ từ 2 đầu, luôn dịch con trỏ thấp hơn vào trong left, right = 0, len(height) - 1 best = 0 while left < right: area = min(height[left], height[right]) * (right - left) best = max(best, area) if height[left] < height[right]: left += 1 else: right -= 1 return best
print(max_area([1, 8, 6, 2, 5, 4, 8, 3, 7])) # 49print(max_area([1, 1])) # 1#include <iostream>#include <vector>#include <algorithm>using namespace std;
int maxArea(vector<int>& height) { int left = 0, right = height.size() - 1; int best = 0; while (left < right) { int area = min(height[left], height[right]) * (right - left); best = max(best, area); if (height[left] < height[right]) left++; else right--; } return best;}
int main() { vector<int> h1 = {1, 8, 6, 2, 5, 4, 8, 3, 7}; vector<int> h2 = {1, 1}; cout << maxArea(h1) << endl; // 49 cout << maxArea(h2) << endl; // 1 return 0;}public class Main { static int maxArea(int[] height) { int left = 0, right = height.length - 1; int best = 0; while (left < right) { int area = Math.min(height[left], height[right]) * (right - left); best = Math.max(best, area); if (height[left] < height[right]) left++; else right--; } return best; }
public static void main(String[] args) { System.out.println(maxArea(new int[]{1, 8, 6, 2, 5, 4, 8, 3, 7})); // 49 System.out.println(maxArea(new int[]{1, 1})); // 1 }}fun maxArea(height: List<Int>): Int { var left = 0 var right = height.size - 1 var best = 0 while (left < right) { val area = minOf(height[left], height[right]) * (right - left) best = maxOf(best, area) if (height[left] < height[right]) left++ else right-- } return best}
fun main() { println(maxArea(listOf(1, 8, 6, 2, 5, 4, 8, 3, 7))) // 49 println(maxArea(listOf(1, 1))) // 1}int maxArea(List<int> height) { int left = 0, right = height.length - 1; int best = 0; while (left < right) { int area = (height[left] < height[right] ? height[left] : height[right]) * (right - left); if (area > best) best = area; if (height[left] < height[right]) { left++; } else { right--; } } return best;}
void main() { print(maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7])); // 49 print(maxArea([1, 1])); // 1}47. Hứng nước mưa (Trapping Rain Water)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng số nguyên không âm height mô tả biểu đồ cột (bản đồ độ cao), tính tổng lượng nước mưa có thể bị “giữ lại” giữa các cột sau khi trời mưa.
Ví dụ 1:
Input: height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]Output: 6Ví dụ 2:
Input: height = [4, 2, 0, 3, 2, 5]Output: 9Ràng buộc:
1 <= len(height) <= 2 * 10^40 <= height[i] <= 10^5
Xem đáp án
def trap(height): # O(n) thời gian, O(1) bộ nhớ: hai con trỏ, giữ max trái/phải đã thấy if not height: return 0
left, right = 0, len(height) - 1 left_max, right_max = height[left], height[right] water = 0
while left < right: if left_max < right_max: left += 1 left_max = max(left_max, height[left]) water += left_max - height[left] else: right -= 1 right_max = max(right_max, height[right]) water += right_max - height[right]
return water
print(trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])) # 6print(trap([4, 2, 0, 3, 2, 5])) # 9#include <iostream>#include <vector>#include <algorithm>using namespace std;
int trap(vector<int>& height) { if (height.empty()) return 0; int left = 0, right = height.size() - 1; int leftMax = height[left], rightMax = height[right]; int water = 0;
while (left < right) { if (leftMax < rightMax) { left++; leftMax = max(leftMax, height[left]); water += leftMax - height[left]; } else { right--; rightMax = max(rightMax, height[right]); water += rightMax - height[right]; } } return water;}
int main() { vector<int> h1 = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}; vector<int> h2 = {4, 2, 0, 3, 2, 5}; cout << trap(h1) << endl; // 6 cout << trap(h2) << endl; // 9 return 0;}public class Main { static int trap(int[] height) { if (height.length == 0) return 0; int left = 0, right = height.length - 1; int leftMax = height[left], rightMax = height[right]; int water = 0;
while (left < right) { if (leftMax < rightMax) { left++; leftMax = Math.max(leftMax, height[left]); water += leftMax - height[left]; } else { right--; rightMax = Math.max(rightMax, height[right]); water += rightMax - height[right]; } } return water; }
public static void main(String[] args) { System.out.println(trap(new int[]{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1})); // 6 System.out.println(trap(new int[]{4, 2, 0, 3, 2, 5})); // 9 }}fun trap(height: List<Int>): Int { if (height.isEmpty()) return 0 var left = 0 var right = height.size - 1 var leftMax = height[left] var rightMax = height[right] var water = 0
while (left < right) { if (leftMax < rightMax) { left++ leftMax = maxOf(leftMax, height[left]) water += leftMax - height[left] } else { right-- rightMax = maxOf(rightMax, height[right]) water += rightMax - height[right] } } return water}
fun main() { println(trap(listOf(0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1))) // 6 println(trap(listOf(4, 2, 0, 3, 2, 5))) // 9}int trap(List<int> height) { if (height.isEmpty) return 0; int left = 0, right = height.length - 1; int leftMax = height[left], rightMax = height[right]; int water = 0;
while (left < right) { if (leftMax < rightMax) { left++; leftMax = leftMax > height[left] ? leftMax : height[left]; water += leftMax - height[left]; } else { right--; rightMax = rightMax > height[right] ? rightMax : height[right]; water += rightMax - height[right]; } } return water;}
void main() { print(trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])); // 6 print(trap([4, 2, 0, 3, 2, 5])); // 9}48. Phân loại 3 màu (Sort Colors)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng nums chỉ chứa các giá trị 0, 1, 2 (đại diện đỏ, trắng, xanh). Sắp xếp mảng tại chỗ (in-place) sao cho các phần tử cùng màu đứng cạnh nhau, theo thứ tự đỏ (0) → trắng (1) → xanh (2), không dùng hàm sort() có sẵn, chỉ duyệt 1 lần (thuật toán Dutch National Flag).
Ví dụ 1:
Input: nums = [2, 0, 2, 1, 1, 0]Output: [0, 0, 1, 1, 2, 2]Ví dụ 2:
Input: nums = [2, 0, 1]Output: [0, 1, 2]Ràng buộc:
1 <= len(nums) <= 300nums[i]chỉ nhận giá trị0,1, hoặc2.
Xem đáp án
def sort_colors(nums): # O(n) thời gian, O(1) bộ nhớ: 3 con trỏ low/mid/high low, mid, high = 0, 0, len(nums) - 1
while mid <= high: if nums[mid] == 0: nums[low], nums[mid] = nums[mid], nums[low] low += 1 mid += 1 elif nums[mid] == 1: mid += 1 else: nums[mid], nums[high] = nums[high], nums[mid] high -= 1
return nums
print(sort_colors([2, 0, 2, 1, 1, 0])) # [0, 0, 1, 1, 2, 2]print(sort_colors([2, 0, 1])) # [0, 1, 2]#include <iostream>#include <vector>using namespace std;
vector<int> sortColors(vector<int> nums) { int low = 0, mid = 0, high = nums.size() - 1; while (mid <= high) { if (nums[mid] == 0) { swap(nums[low], nums[mid]); low++; mid++; } else if (nums[mid] == 1) { mid++; } else { swap(nums[mid], nums[high]); high--; } } return nums;}
int main() { for (int x : sortColors({2, 0, 2, 1, 1, 0})) cout << x << " "; cout << endl; // 0 0 1 1 2 2 for (int x : sortColors({2, 0, 1})) cout << x << " "; cout << endl; // 0 1 2 return 0;}import java.util.Arrays;
public class Main { static int[] sortColors(int[] nums) { int low = 0, mid = 0, high = nums.length - 1; while (mid <= high) { if (nums[mid] == 0) { int tmp = nums[low]; nums[low] = nums[mid]; nums[mid] = tmp; low++; mid++; } else if (nums[mid] == 1) { mid++; } else { int tmp = nums[mid]; nums[mid] = nums[high]; nums[high] = tmp; high--; } } return nums; }
public static void main(String[] args) { System.out.println(Arrays.toString(sortColors(new int[]{2, 0, 2, 1, 1, 0}))); // [0, 0, 1, 1, 2, 2] System.out.println(Arrays.toString(sortColors(new int[]{2, 0, 1}))); // [0, 1, 2] }}fun sortColors(nums: MutableList<Int>): MutableList<Int> { var low = 0 var mid = 0 var high = nums.size - 1
while (mid <= high) { when (nums[mid]) { 0 -> { nums[low] = nums[mid].also { nums[mid] = nums[low] } low++ mid++ } 1 -> mid++ else -> { nums[mid] = nums[high].also { nums[high] = nums[mid] } high-- } } } return nums}
fun main() { println(sortColors(mutableListOf(2, 0, 2, 1, 1, 0))) // [0, 0, 1, 1, 2, 2] println(sortColors(mutableListOf(2, 0, 1))) // [0, 1, 2]}List<int> sortColors(List<int> nums) { int low = 0, mid = 0, high = nums.length - 1; while (mid <= high) { if (nums[mid] == 0) { final tmp = nums[low]; nums[low] = nums[mid]; nums[mid] = tmp; low++; mid++; } else if (nums[mid] == 1) { mid++; } else { final tmp = nums[mid]; nums[mid] = nums[high]; nums[high] = tmp; high--; } } return nums;}
void main() { print(sortColors([2, 0, 2, 1, 1, 0])); // [0, 0, 1, 1, 2, 2] print(sortColors([2, 0, 1])); // [0, 1, 2]}49. Xóa phần tử trùng lặp, giữ tối đa 2 lần (Remove Duplicates II)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng nums đã sắp xếp tăng dần, xóa bớt các phần tử trùng lặp tại chỗ sao cho mỗi giá trị xuất hiện tối đa 2 lần, giữ nguyên thứ tự tương đối. Trả về độ dài mảng mới (phần đầu của nums sau khi xử lý).
Ví dụ 1:
Input: nums = [1, 1, 1, 2, 2, 3]Output: 5, nums = [1, 1, 2, 2, 3]Ví dụ 2:
Input: nums = [0, 0, 1, 1, 1, 1, 2, 3, 3]Output: 7, nums = [0, 0, 1, 1, 2, 3, 3]Ràng buộc:
1 <= len(nums) <= 3 * 10^4numsđã sắp xếp tăng dần.
Xem đáp án
def remove_duplicates(nums): # O(n): con trỏ ghi "k" luôn cách con trỏ đọc "i" đúng phần cần giữ k = 0 for i, num in enumerate(nums): if k < 2 or num != nums[k - 2]: nums[k] = num k += 1 return k, nums[:k]
print(remove_duplicates([1, 1, 1, 2, 2, 3]))print(remove_duplicates([0, 0, 1, 1, 1, 1, 2, 3, 3]))#include <iostream>#include <vector>using namespace std;
int removeDuplicates(vector<int>& nums) { int k = 0; for (int num : nums) { if (k < 2 || num != nums[k - 2]) { nums[k] = num; k++; } } return k;}
int main() { vector<int> a = {1, 1, 1, 2, 2, 3}; int k1 = removeDuplicates(a); cout << k1 << ", ["; for (int i = 0; i < k1; i++) cout << a[i] << (i < k1 - 1 ? ", " : ""); cout << "]" << endl; // 5, [1, 1, 2, 2, 3]
vector<int> b = {0, 0, 1, 1, 1, 1, 2, 3, 3}; int k2 = removeDuplicates(b); cout << k2 << ", ["; for (int i = 0; i < k2; i++) cout << b[i] << (i < k2 - 1 ? ", " : ""); cout << "]" << endl; // 7, [0, 0, 1, 1, 2, 3, 3] return 0;}import java.util.Arrays;
public class Main { static int removeDuplicates(int[] nums) { int k = 0; for (int num : nums) { if (k < 2 || num != nums[k - 2]) { nums[k] = num; k++; } } return k; }
public static void main(String[] args) { int[] a = {1, 1, 1, 2, 2, 3}; int k1 = removeDuplicates(a); System.out.println(k1 + ", " + Arrays.toString(Arrays.copyOf(a, k1)));
int[] b = {0, 0, 1, 1, 1, 1, 2, 3, 3}; int k2 = removeDuplicates(b); System.out.println(k2 + ", " + Arrays.toString(Arrays.copyOf(b, k2))); }}fun removeDuplicates(nums: MutableList<Int>): Int { var k = 0 for (num in nums.toList()) { if (k < 2 || num != nums[k - 2]) { nums[k] = num k++ } } return k}
fun main() { val a = mutableListOf(1, 1, 1, 2, 2, 3) val k1 = removeDuplicates(a) println("$k1, ${a.subList(0, k1)}")
val b = mutableListOf(0, 0, 1, 1, 1, 1, 2, 3, 3) val k2 = removeDuplicates(b) println("$k2, ${b.subList(0, k2)}")}int removeDuplicates(List<int> nums) { int k = 0; final original = List<int>.from(nums); for (var num in original) { if (k < 2 || num != nums[k - 2]) { nums[k] = num; k++; } } return k;}
void main() { final a = [1, 1, 1, 2, 2, 3]; final k1 = removeDuplicates(a); print("$k1, ${a.sublist(0, k1)}");
final b = [0, 0, 1, 1, 1, 1, 2, 3, 3]; final k2 = removeDuplicates(b); print("$k2, ${b.sublist(0, k2)}");}50. Kiểm tra Sudoku hợp lệ (Valid Sudoku)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho một bảng Sudoku 9x9 (dùng "." cho ô trống), kiểm tra bảng đó có hợp lệ hay không theo luật: mỗi hàng, mỗi cột, và mỗi ô vuông 3x3 không được chứa số trùng lặp trong các số 1-9 đã điền. (Không cần kiểm tra bảng có giải được hay không.)
Ví dụ 1:
Input: board với hàng đầu = ["5","3",".",".","7",".",".",".","."], các hàng còn lại hợp lệOutput: TrueVí dụ 2:
Input: board có hai số "8" trong cùng cột đầu tiênOutput: FalseRàng buộc:
- Bảng luôn có kích thước
9x9. - Mỗi ô là ký tự số
1-9hoặc".".
Xem đáp án
def is_valid_sudoku(board): # O(81) ~ O(1): dùng set để phát hiện trùng lặp theo hàng/cột/ô 3x3 rows = [set() for _ in range(9)] cols = [set() for _ in range(9)] boxes = [set() for _ in range(9)]
for r in range(9): for c in range(9): val = board[r][c] if val == ".": continue box_index = (r // 3) * 3 + c // 3 if val in rows[r] or val in cols[c] or val in boxes[box_index]: return False rows[r].add(val) cols[c].add(val) boxes[box_index].add(val)
return True
board_ok = [ ["5","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"],]print(is_valid_sudoku(board_ok)) # True#include <iostream>#include <vector>#include <set>#include <string>using namespace std;
bool isValidSudoku(vector<vector<string>>& board) { vector<set<string>> rows(9), cols(9), boxes(9);
for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { string val = board[r][c]; if (val == ".") continue; int boxIndex = (r / 3) * 3 + c / 3; if (rows[r].count(val) || cols[c].count(val) || boxes[boxIndex].count(val)) return false; rows[r].insert(val); cols[c].insert(val); boxes[boxIndex].insert(val); } } return true;}
int main() { vector<vector<string>> board = { {"5","3",".",".","7",".",".",".","."}, {"6",".",".","1","9","5",".",".","."}, {".","9","8",".",".",".",".","6","."}, {"8",".",".",".","6",".",".",".","3"}, {"4",".",".","8",".","3",".",".","1"}, {"7",".",".",".","2",".",".",".","6"}, {".","6",".",".",".",".","2","8","."}, {".",".",".","4","1","9",".",".","5"}, {".",".",".",".","8",".",".","7","9"}, }; cout << (isValidSudoku(board) ? "true" : "false") << endl; // true return 0;}import java.util.HashSet;import java.util.Set;
public class Main { static boolean isValidSudoku(String[][] board) { @SuppressWarnings("unchecked") Set<String>[] rows = new HashSet[9]; Set<String>[] cols = new HashSet[9]; Set<String>[] boxes = new HashSet[9]; for (int i = 0; i < 9; i++) { rows[i] = new HashSet<>(); cols[i] = new HashSet<>(); boxes[i] = new HashSet<>(); }
for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { String val = board[r][c]; if (val.equals(".")) continue; int boxIndex = (r / 3) * 3 + c / 3; if (rows[r].contains(val) || cols[c].contains(val) || boxes[boxIndex].contains(val)) return false; rows[r].add(val); cols[c].add(val); boxes[boxIndex].add(val); } } return true; }
public static void main(String[] args) { String[][] board = { {"5","3",".",".","7",".",".",".","."}, {"6",".",".","1","9","5",".",".","."}, {".","9","8",".",".",".",".","6","."}, {"8",".",".",".","6",".",".",".","3"}, {"4",".",".","8",".","3",".",".","1"}, {"7",".",".",".","2",".",".",".","6"}, {".","6",".",".",".",".","2","8","."}, {".",".",".","4","1","9",".",".","5"}, {".",".",".",".","8",".",".","7","9"}, }; System.out.println(isValidSudoku(board)); // true }}fun isValidSudoku(board: Array<Array<String>>): Boolean { val rows = Array(9) { mutableSetOf<String>() } val cols = Array(9) { mutableSetOf<String>() } val boxes = Array(9) { mutableSetOf<String>() }
for (r in 0 until 9) { for (c in 0 until 9) { val v = board[r][c] if (v == ".") continue val boxIndex = (r / 3) * 3 + c / 3 if (v in rows[r] || v in cols[c] || v in boxes[boxIndex]) return false rows[r].add(v) cols[c].add(v) boxes[boxIndex].add(v) } } return true}
fun main() { val board = arrayOf( arrayOf("5","3",".",".","7",".",".",".","."), arrayOf("6",".",".","1","9","5",".",".","."), arrayOf(".","9","8",".",".",".",".","6","."), arrayOf("8",".",".",".","6",".",".",".","3"), arrayOf("4",".",".","8",".","3",".",".","1"), arrayOf("7",".",".",".","2",".",".",".","6"), arrayOf(".","6",".",".",".",".","2","8","."), arrayOf(".",".",".","4","1","9",".",".","5"), arrayOf(".",".",".",".","8",".",".","7","9") ) println(isValidSudoku(board)) // true}bool isValidSudoku(List<List<String>> board) { final rows = List.generate(9, (_) => <String>{}); final cols = List.generate(9, (_) => <String>{}); final boxes = List.generate(9, (_) => <String>{});
for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { final val = board[r][c]; if (val == ".") continue; final boxIndex = (r ~/ 3) * 3 + c ~/ 3; if (rows[r].contains(val) || cols[c].contains(val) || boxes[boxIndex].contains(val)) return false; rows[r].add(val); cols[c].add(val); boxes[boxIndex].add(val); } } return true;}
void main() { final board = [ ["5","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"], ]; print(isValidSudoku(board)); // true}51. Giao của hai mảng, giữ trùng lặp (Intersection of Two Arrays II)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho 2 mảng số nguyên nums1, nums2. Trả về mảng chứa các phần tử là giao của hai mảng, mỗi phần tử xuất hiện đúng số lần nhỏ nhất mà nó xuất hiện ở cả hai mảng. Thứ tự kết quả không quan trọng.
Ví dụ 1:
Input: nums1 = [1, 2, 2, 1], nums2 = [2, 2]Output: [2, 2]Ví dụ 2:
Input: nums1 = [4, 9, 5], nums2 = [9, 4, 9, 8, 4]Output: [4, 9] hoặc [9, 4]Ràng buộc:
1 <= len(nums1), len(nums2) <= 1000
Xem đáp án
from collections import Counter
def intersect(nums1, nums2): # O(m + n): đếm tần suất mảng nhỏ hơn rồi duyệt mảng còn lại count1 = Counter(nums1) result = [] for num in nums2: if count1.get(num, 0) > 0: result.append(num) count1[num] -= 1 return result
print(sorted(intersect([1, 2, 2, 1], [2, 2]))) # [2, 2]print(sorted(intersect([4, 9, 5], [9, 4, 9, 8, 4]))) # [4, 9]#include <iostream>#include <vector>#include <unordered_map>#include <algorithm>using namespace std;
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) { unordered_map<int, int> count1; for (int n : nums1) count1[n]++; vector<int> result; for (int n : nums2) { if (count1[n] > 0) { result.push_back(n); count1[n]--; } } return result;}
int main() { vector<int> a1 = {1, 2, 2, 1}, b1 = {2, 2}; vector<int> r1 = intersect(a1, b1); sort(r1.begin(), r1.end()); for (int x : r1) cout << x << " "; cout << endl; // 2 2
vector<int> a2 = {4, 9, 5}, b2 = {9, 4, 9, 8, 4}; vector<int> r2 = intersect(a2, b2); sort(r2.begin(), r2.end()); for (int x : r2) cout << x << " "; cout << endl; // 4 9 return 0;}import java.util.*;
public class Main { static List<Integer> intersect(int[] nums1, int[] nums2) { Map<Integer, Integer> count1 = new HashMap<>(); for (int n : nums1) count1.merge(n, 1, Integer::sum); List<Integer> result = new ArrayList<>(); for (int n : nums2) { if (count1.getOrDefault(n, 0) > 0) { result.add(n); count1.put(n, count1.get(n) - 1); } } return result; }
public static void main(String[] args) { List<Integer> r1 = intersect(new int[]{1, 2, 2, 1}, new int[]{2, 2}); Collections.sort(r1); System.out.println(r1); // [2, 2]
List<Integer> r2 = intersect(new int[]{4, 9, 5}, new int[]{9, 4, 9, 8, 4}); Collections.sort(r2); System.out.println(r2); // [4, 9] }}fun intersect(nums1: List<Int>, nums2: List<Int>): List<Int> { val count1 = nums1.groupingBy { it }.eachCount().toMutableMap() val result = mutableListOf<Int>() for (n in nums2) { val c = count1.getOrDefault(n, 0) if (c > 0) { result.add(n) count1[n] = c - 1 } } return result}
fun main() { println(intersect(listOf(1, 2, 2, 1), listOf(2, 2)).sorted()) // [2, 2] println(intersect(listOf(4, 9, 5), listOf(9, 4, 9, 8, 4)).sorted()) // [4, 9]}List<int> intersect(List<int> nums1, List<int> nums2) { final count1 = <int, int>{}; for (var n in nums1) count1[n] = (count1[n] ?? 0) + 1; final result = <int>[]; for (var n in nums2) { if ((count1[n] ?? 0) > 0) { result.add(n); count1[n] = count1[n]! - 1; } } return result;}
void main() { final r1 = intersect([1, 2, 2, 1], [2, 2])..sort(); print(r1); // [2, 2] final r2 = intersect([4, 9, 5], [9, 4, 9, 8, 4])..sort(); print(r2); // [4, 9]}52. Dãy con liên tiếp dài nhất (Longest Consecutive Sequence)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho mảng số nguyên nums không sắp xếp, tìm độ dài của dãy các số nguyên liên tiếp dài nhất (không cần liên tiếp trong mảng gốc, chỉ cần giá trị liên tiếp, ví dụ 1,2,3,4). Yêu cầu thuật toán chạy O(n).
Ví dụ 1:
Input: nums = [100, 4, 200, 1, 3, 2]Output: 4Giải thích: Dãy liên tiếp dài nhất là [1, 2, 3, 4]Ví dụ 2:
Input: nums = [0, 3, 7, 2, 5, 8, 4, 6, 0, 1]Output: 9Ràng buộc:
0 <= len(nums) <= 10^5
Xem đáp án
def longest_consecutive(nums): # O(n): dùng set, chỉ bắt đầu đếm từ số là "điểm đầu" của 1 dãy (num-1 not in set) num_set = set(nums) best = 0
for num in num_set: if num - 1 not in num_set: length = 1 while num + length in num_set: length += 1 best = max(best, length)
return best
print(longest_consecutive([100, 4, 200, 1, 3, 2])) # 4print(longest_consecutive([0, 3, 7, 2, 5, 8, 4, 6, 0, 1])) # 9#include <iostream>#include <vector>#include <unordered_set>using namespace std;
int longestConsecutive(vector<int>& nums) { unordered_set<int> numSet(nums.begin(), nums.end()); int best = 0;
for (int num : numSet) { if (numSet.find(num - 1) == numSet.end()) { int length = 1; while (numSet.find(num + length) != numSet.end()) length++; best = max(best, length); } } return best;}
int main() { vector<int> a = {100, 4, 200, 1, 3, 2}; vector<int> b = {0, 3, 7, 2, 5, 8, 4, 6, 0, 1}; cout << longestConsecutive(a) << endl; // 4 cout << longestConsecutive(b) << endl; // 9 return 0;}import java.util.HashSet;import java.util.Set;
public class Main { static int longestConsecutive(int[] nums) { Set<Integer> numSet = new HashSet<>(); for (int n : nums) numSet.add(n); int best = 0;
for (int num : numSet) { if (!numSet.contains(num - 1)) { int length = 1; while (numSet.contains(num + length)) length++; best = Math.max(best, length); } } return best; }
public static void main(String[] args) { System.out.println(longestConsecutive(new int[]{100, 4, 200, 1, 3, 2})); // 4 System.out.println(longestConsecutive(new int[]{0, 3, 7, 2, 5, 8, 4, 6, 0, 1})); // 9 }}fun longestConsecutive(nums: List<Int>): Int { val numSet = nums.toHashSet() var best = 0
for (num in numSet) { if (num - 1 !in numSet) { var length = 1 while (num + length in numSet) length++ best = maxOf(best, length) } } return best}
fun main() { println(longestConsecutive(listOf(100, 4, 200, 1, 3, 2))) // 4 println(longestConsecutive(listOf(0, 3, 7, 2, 5, 8, 4, 6, 0, 1))) // 9}int longestConsecutive(List<int> nums) { final numSet = nums.toSet(); int best = 0;
for (var num in numSet) { if (!numSet.contains(num - 1)) { int length = 1; while (numSet.contains(num + length)) length++; if (length > best) best = length; } } return best;}
void main() { print(longestConsecutive([100, 4, 200, 1, 3, 2])); // 4 print(longestConsecutive([0, 3, 7, 2, 5, 8, 4, 6, 0, 1])); // 9}53. K phần tử xuất hiện nhiều nhất (Top K Frequent Elements)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho mảng số nguyên nums và số nguyên k, trả về k phần tử xuất hiện nhiều nhất trong mảng, sắp xếp theo tần suất giảm dần.
Ví dụ 1:
Input: nums = [1, 1, 1, 2, 2, 3], k = 2Output: [1, 2]Ví dụ 2:
Input: nums = [1], k = 1Output: [1]Ràng buộc:
1 <= len(nums) <= 10^5kluôn nhỏ hơn hoặc bằng số phần tử phân biệt trongnums.
Xem đáp án
from collections import Counter
def top_k_frequent(nums, k): # O(n log k) với heap, hoặc O(n) với bucket sort theo tần suất; ở đây dùng Counter.most_common cho gọn count = Counter(nums) return [num for num, _ in count.most_common(k)]
print(top_k_frequent([1, 1, 1, 2, 2, 3], 2)) # [1, 2]print(top_k_frequent([1], 1)) # [1]#include <iostream>#include <vector>#include <unordered_map>#include <algorithm>using namespace std;
vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> count; for (int n : nums) count[n]++;
vector<pair<int, int>> items(count.begin(), count.end()); sort(items.begin(), items.end(), [](auto& a, auto& b) { return a.second > b.second; });
vector<int> result; for (int i = 0; i < k; i++) result.push_back(items[i].first); return result;}
int main() { vector<int> a = {1, 1, 1, 2, 2, 3}; for (int x : topKFrequent(a, 2)) cout << x << " "; cout << endl; // 1 2
vector<int> b = {1}; for (int x : topKFrequent(b, 1)) cout << x << " "; cout << endl; // 1 return 0;}import java.util.*;import java.util.stream.Collectors;
public class Main { static List<Integer> topKFrequent(int[] nums, int k) { Map<Integer, Integer> count = new HashMap<>(); for (int n : nums) count.merge(n, 1, Integer::sum);
return count.entrySet().stream() .sorted((a, b) -> b.getValue() - a.getValue()) .limit(k) .map(Map.Entry::getKey) .collect(Collectors.toList()); }
public static void main(String[] args) { System.out.println(topKFrequent(new int[]{1, 1, 1, 2, 2, 3}, 2)); // [1, 2] System.out.println(topKFrequent(new int[]{1}, 1)); // [1] }}fun topKFrequent(nums: List<Int>, k: Int): List<Int> { val count = nums.groupingBy { it }.eachCount() return count.entries.sortedByDescending { it.value }.take(k).map { it.key }}
fun main() { println(topKFrequent(listOf(1, 1, 1, 2, 2, 3), 2)) // [1, 2] println(topKFrequent(listOf(1), 1)) // [1]}List<int> topKFrequent(List<int> nums, int k) { final count = <int, int>{}; for (var n in nums) count[n] = (count[n] ?? 0) + 1;
final entries = count.entries.toList() ..sort((a, b) => b.value.compareTo(a.value));
return entries.take(k).map((e) => e.key).toList();}
void main() { print(topKFrequent([1, 1, 1, 2, 2, 3], 2)); // [1, 2] print(topKFrequent([1], 1)); // [1]}54. Tìm tất cả Anagram trong chuỗi (Find All Anagrams in a String)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho 2 chuỗi s và p, tìm tất cả vị trí bắt đầu của các chuỗi con trong s là anagram của p (chứa đúng các ký tự của p, khác thứ tự).
Ví dụ 1:
Input: s = "cbaebabacd", p = "abc"Output: [0, 6]Giải thích: "cba" (vị trí 0) và "bac" (vị trí 6) đều là anagram của "abc"Ví dụ 2:
Input: s = "abab", p = "ab"Output: [0, 1, 2]Ràng buộc:
1 <= len(s), len(p) <= 3 * 10^4
Xem đáp án
from collections import Counter
def find_anagrams(s, p): # O(len(s)): sliding window kích thước cố định len(p), so sánh Counter n, m = len(s), len(p) if m > n: return []
p_count = Counter(p) window_count = Counter(s[:m]) result = []
if window_count == p_count: result.append(0)
for i in range(m, n): window_count[s[i]] += 1 left_char = s[i - m] window_count[left_char] -= 1 if window_count[left_char] == 0: del window_count[left_char] if window_count == p_count: result.append(i - m + 1)
return result
print(find_anagrams("cbaebabacd", "abc")) # [0, 6]print(find_anagrams("abab", "ab")) # [0, 1, 2]#include <iostream>#include <vector>#include <string>using namespace std;
vector<int> findAnagrams(string s, string p) { int n = s.size(), m = p.size(); vector<int> result; if (m > n) return result;
vector<int> pCount(26, 0), windowCount(26, 0); for (char c : p) pCount[c - 'a']++; for (int i = 0; i < m; i++) windowCount[s[i] - 'a']++;
if (windowCount == pCount) result.push_back(0);
for (int i = m; i < n; i++) { windowCount[s[i] - 'a']++; windowCount[s[i - m] - 'a']--; if (windowCount == pCount) result.push_back(i - m + 1); } return result;}
int main() { for (int x : findAnagrams("cbaebabacd", "abc")) cout << x << " "; cout << endl; // 0 6 for (int x : findAnagrams("abab", "ab")) cout << x << " "; cout << endl; // 0 1 2 return 0;}import java.util.ArrayList;import java.util.Arrays;import java.util.List;
public class Main { static List<Integer> findAnagrams(String s, String p) { int n = s.length(), m = p.length(); List<Integer> result = new ArrayList<>(); if (m > n) return result;
int[] pCount = new int[26], windowCount = new int[26]; for (char c : p.toCharArray()) pCount[c - 'a']++; for (int i = 0; i < m; i++) windowCount[s.charAt(i) - 'a']++;
if (Arrays.equals(windowCount, pCount)) result.add(0);
for (int i = m; i < n; i++) { windowCount[s.charAt(i) - 'a']++; windowCount[s.charAt(i - m) - 'a']--; if (Arrays.equals(windowCount, pCount)) result.add(i - m + 1); } return result; }
public static void main(String[] args) { System.out.println(findAnagrams("cbaebabacd", "abc")); // [0, 6] System.out.println(findAnagrams("abab", "ab")); // [0, 1, 2] }}fun findAnagrams(s: String, p: String): List<Int> { val n = s.length val m = p.length val result = mutableListOf<Int>() if (m > n) return result
val pCount = IntArray(26) val windowCount = IntArray(26) for (c in p) pCount[c - 'a']++ for (i in 0 until m) windowCount[s[i] - 'a']++
if (windowCount.contentEquals(pCount)) result.add(0)
for (i in m until n) { windowCount[s[i] - 'a']++ windowCount[s[i - m] - 'a']-- if (windowCount.contentEquals(pCount)) result.add(i - m + 1) } return result}
fun main() { println(findAnagrams("cbaebabacd", "abc")) // [0, 6] println(findAnagrams("abab", "ab")) // [0, 1, 2]}List<int> findAnagrams(String s, String p) { final n = s.length, m = p.length; final result = <int>[]; if (m > n) return result;
final pCount = List<int>.filled(26, 0); final windowCount = List<int>.filled(26, 0); final aCode = 'a'.codeUnitAt(0); for (var c in p.codeUnits) pCount[c - aCode]++; for (var i = 0; i < m; i++) windowCount[s.codeUnitAt(i) - aCode]++;
bool listEq(List<int> a, List<int> b) { for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return true; }
if (listEq(windowCount, pCount)) result.add(0);
for (var i = m; i < n; i++) { windowCount[s.codeUnitAt(i) - aCode]++; windowCount[s.codeUnitAt(i - m) - aCode]--; if (listEq(windowCount, pCount)) result.add(i - m + 1); } return result;}
void main() { print(findAnagrams("cbaebabacd", "abc")); // [0, 6] print(findAnagrams("abab", "ab")); // [0, 1, 2]}55. Dãy con có tổng bằng K (Subarray Sum Equals K)
Độ khó: Trung bình · Chủ đề: Hash Map · Prefix Sum
Cho mảng số nguyên nums và số nguyên k, đếm số lượng dãy con liên tiếp có tổng đúng bằng k.
Ví dụ 1:
Input: nums = [1, 1, 1], k = 2Output: 2Giải thích: [1,1] (đầu) và [1,1] (cuối)Ví dụ 2:
Input: nums = [1, 2, 3], k = 3Output: 2Giải thích: [1,2] và [3]Ràng buộc:
1 <= len(nums) <= 2 * 10^4nums[i]có thể âm.
Xem đáp án
from collections import defaultdict
def subarray_sum(nums, k): # O(n): dùng prefix sum + hash map đếm số lần mỗi prefix sum đã xuất hiện count = defaultdict(int) count[0] = 1 # prefix sum = 0 xuất hiện 1 lần (trước khi bắt đầu) prefix_sum = 0 total = 0
for num in nums: prefix_sum += num total += count[prefix_sum - k] count[prefix_sum] += 1
return total
print(subarray_sum([1, 1, 1], 2)) # 2print(subarray_sum([1, 2, 3], 3)) # 2#include <iostream>#include <vector>#include <unordered_map>using namespace std;
int subarraySum(vector<int>& nums, int k) { unordered_map<int, int> count; count[0] = 1; int prefixSum = 0, total = 0;
for (int num : nums) { prefixSum += num; if (count.find(prefixSum - k) != count.end()) total += count[prefixSum - k]; count[prefixSum]++; } return total;}
int main() { vector<int> a = {1, 1, 1}, b = {1, 2, 3}; cout << subarraySum(a, 2) << endl; // 2 cout << subarraySum(b, 3) << endl; // 2 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int subarraySum(int[] nums, int k) { Map<Integer, Integer> count = new HashMap<>(); count.put(0, 1); int prefixSum = 0, total = 0;
for (int num : nums) { prefixSum += num; total += count.getOrDefault(prefixSum - k, 0); count.merge(prefixSum, 1, Integer::sum); } return total; }
public static void main(String[] args) { System.out.println(subarraySum(new int[]{1, 1, 1}, 2)); // 2 System.out.println(subarraySum(new int[]{1, 2, 3}, 3)); // 2 }}fun subarraySum(nums: List<Int>, k: Int): Int { val count = HashMap<Int, Int>() count[0] = 1 var prefixSum = 0 var total = 0
for (num in nums) { prefixSum += num total += count.getOrDefault(prefixSum - k, 0) count[prefixSum] = count.getOrDefault(prefixSum, 0) + 1 } return total}
fun main() { println(subarraySum(listOf(1, 1, 1), 2)) // 2 println(subarraySum(listOf(1, 2, 3), 3)) // 2}int subarraySum(List<int> nums, int k) { final count = <int, int>{0: 1}; int prefixSum = 0, total = 0;
for (var num in nums) { prefixSum += num; total += count[prefixSum - k] ?? 0; count[prefixSum] = (count[prefixSum] ?? 0) + 1; } return total;}
void main() { print(subarraySum([1, 1, 1], 2)); // 2 print(subarraySum([1, 2, 3], 3)); // 2}56. Mảng nhị phân cân bằng (Contiguous Array)
Độ khó: Trung bình · Chủ đề: Hash Map
Cho mảng nhị phân nums chỉ gồm 0 và 1, tìm độ dài dãy con liên tiếp dài nhất có số lượng 0 và 1 bằng nhau.
Ví dụ 1:
Input: nums = [0, 1]Output: 2Ví dụ 2:
Input: nums = [0, 1, 0]Output: 2Giải thích: [0, 1] hoặc [1, 0] đều có độ dài 2Ràng buộc:
1 <= len(nums) <= 10^5
Xem đáp án
def find_max_length(nums): # O(n): coi số 0 là -1, bài toán trở thành "tìm dãy con có tổng bằng 0" count = 0 max_len = 0 first_seen = {0: -1} # prefix_sum -> chỉ số đầu tiên xuất hiện
for i, num in enumerate(nums): count += 1 if num == 1 else -1 if count in first_seen: max_len = max(max_len, i - first_seen[count]) else: first_seen[count] = i
return max_len
print(find_max_length([0, 1])) # 2print(find_max_length([0, 1, 0])) # 2#include <iostream>#include <vector>#include <unordered_map>using namespace std;
int findMaxLength(vector<int>& nums) { int count = 0, maxLen = 0; unordered_map<int, int> firstSeen; firstSeen[0] = -1;
for (int i = 0; i < (int)nums.size(); i++) { count += nums[i] == 1 ? 1 : -1; if (firstSeen.find(count) != firstSeen.end()) { maxLen = max(maxLen, i - firstSeen[count]); } else { firstSeen[count] = i; } } return maxLen;}
int main() { vector<int> a = {0, 1}, b = {0, 1, 0}; cout << findMaxLength(a) << endl; // 2 cout << findMaxLength(b) << endl; // 2 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int findMaxLength(int[] nums) { int count = 0, maxLen = 0; Map<Integer, Integer> firstSeen = new HashMap<>(); firstSeen.put(0, -1);
for (int i = 0; i < nums.length; i++) { count += nums[i] == 1 ? 1 : -1; if (firstSeen.containsKey(count)) { maxLen = Math.max(maxLen, i - firstSeen.get(count)); } else { firstSeen.put(count, i); } } return maxLen; }
public static void main(String[] args) { System.out.println(findMaxLength(new int[]{0, 1})); // 2 System.out.println(findMaxLength(new int[]{0, 1, 0})); // 2 }}fun findMaxLength(nums: List<Int>): Int { var count = 0 var maxLen = 0 val firstSeen = HashMap<Int, Int>() firstSeen[0] = -1
for (i in nums.indices) { count += if (nums[i] == 1) 1 else -1 if (firstSeen.containsKey(count)) { maxLen = maxOf(maxLen, i - firstSeen[count]!!) } else { firstSeen[count] = i } } return maxLen}
fun main() { println(findMaxLength(listOf(0, 1))) // 2 println(findMaxLength(listOf(0, 1, 0))) // 2}int findMaxLength(List<int> nums) { int count = 0, maxLen = 0; final firstSeen = <int, int>{0: -1};
for (var i = 0; i < nums.length; i++) { count += nums[i] == 1 ? 1 : -1; if (firstSeen.containsKey(count)) { final len = i - firstSeen[count]!; if (len > maxLen) maxLen = len; } else { firstSeen[count] = i; } } return maxLen;}
void main() { print(findMaxLength([0, 1])); // 2 print(findMaxLength([0, 1, 0])); // 2}57. Đếm dãy con có tích nhỏ hơn K (Subarray Product Less Than K)
Độ khó: Trung bình · Chủ đề: Two Pointers · Sliding Window
Cho mảng số nguyên dương nums và số nguyên k, đếm số lượng dãy con liên tiếp có tích các phần tử nhỏ hơn k.
Ví dụ 1:
Input: nums = [10, 5, 2, 6], k = 100Output: 8Giải thích: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6] đều có tích < 100Ví dụ 2:
Input: nums = [1, 2, 3], k = 0Output: 0Ràng buộc:
1 <= len(nums) <= 3 * 10^41 <= nums[i] <= 10000 <= k <= 10^6
Xem đáp án
def num_subarray_product_less_than_k(nums, k): # O(n): sliding window, co cửa sổ bên trái khi tích >= k if k <= 1: return 0
product = 1 left = 0 count = 0
for right, num in enumerate(nums): product *= num while product >= k: product //= nums[left] left += 1 count += right - left + 1 # mọi dãy con kết thúc tại "right" và bắt đầu trong [left, right]
return count
print(num_subarray_product_less_than_k([10, 5, 2, 6], 100)) # 8print(num_subarray_product_less_than_k([1, 2, 3], 0)) # 0#include <iostream>#include <vector>using namespace std;
int numSubarrayProductLessThanK(vector<int>& nums, int k) { if (k <= 1) return 0;
long long product = 1; int left = 0, count = 0;
for (int right = 0; right < (int)nums.size(); right++) { product *= nums[right]; while (product >= k) { product /= nums[left]; left++; } count += right - left + 1; } return count;}
int main() { vector<int> a = {10, 5, 2, 6}, b = {1, 2, 3}; cout << numSubarrayProductLessThanK(a, 100) << endl; // 8 cout << numSubarrayProductLessThanK(b, 0) << endl; // 0 return 0;}public class Main { static int numSubarrayProductLessThanK(int[] nums, int k) { if (k <= 1) return 0;
long product = 1; int left = 0, count = 0;
for (int right = 0; right < nums.length; right++) { product *= nums[right]; while (product >= k) { product /= nums[left]; left++; } count += right - left + 1; } return count; }
public static void main(String[] args) { System.out.println(numSubarrayProductLessThanK(new int[]{10, 5, 2, 6}, 100)); // 8 System.out.println(numSubarrayProductLessThanK(new int[]{1, 2, 3}, 0)); // 0 }}fun numSubarrayProductLessThanK(nums: List<Int>, k: Int): Int { if (k <= 1) return 0
var product = 1L var left = 0 var count = 0
for (right in nums.indices) { product *= nums[right] while (product >= k) { product /= nums[left] left++ } count += right - left + 1 } return count}
fun main() { println(numSubarrayProductLessThanK(listOf(10, 5, 2, 6), 100)) // 8 println(numSubarrayProductLessThanK(listOf(1, 2, 3), 0)) // 0}int numSubarrayProductLessThanK(List<int> nums, int k) { if (k <= 1) return 0;
int product = 1, left = 0, count = 0;
for (var right = 0; right < nums.length; right++) { product *= nums[right]; while (product >= k) { product ~/= nums[left]; left++; } count += right - left + 1; } return count;}
void main() { print(numSubarrayProductLessThanK([10, 5, 2, 6], 100)); // 8 print(numSubarrayProductLessThanK([1, 2, 3], 0)); // 0}58. Chuỗi con dài nhất chỉ chứa tối đa 2 ký tự khác nhau
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho chuỗi s, tìm độ dài của chuỗi con liên tiếp dài nhất chỉ chứa tối đa 2 ký tự khác nhau.
Ví dụ 1:
Input: s = "eceba"Output: 3Giải thích: Chuỗi con "ece" có độ dài 3Ví dụ 2:
Input: s = "ccaabbb"Output: 5Giải thích: Chuỗi con "aabbb" có độ dài 5Ràng buộc:
1 <= len(s) <= 10^5
Xem đáp án
def length_of_longest_substring_two_distinct(s): # O(n): sliding window, hash map đếm tần suất ký tự trong cửa sổ char_count = {} left = 0 best = 0
for right, c in enumerate(s): char_count[c] = char_count.get(c, 0) + 1 while len(char_count) > 2: left_char = s[left] char_count[left_char] -= 1 if char_count[left_char] == 0: del char_count[left_char] left += 1 best = max(best, right - left + 1)
return best
print(length_of_longest_substring_two_distinct("eceba")) # 3print(length_of_longest_substring_two_distinct("ccaabbb")) # 5#include <iostream>#include <unordered_map>#include <string>using namespace std;
int lengthOfLongestSubstringTwoDistinct(string s) { unordered_map<char, int> charCount; int left = 0, best = 0;
for (int right = 0; right < (int)s.size(); right++) { charCount[s[right]]++; while (charCount.size() > 2) { char leftChar = s[left]; charCount[leftChar]--; if (charCount[leftChar] == 0) charCount.erase(leftChar); left++; } best = max(best, right - left + 1); } return best;}
int main() { cout << lengthOfLongestSubstringTwoDistinct("eceba") << endl; // 3 cout << lengthOfLongestSubstringTwoDistinct("ccaabbb") << endl; // 5 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int lengthOfLongestSubstringTwoDistinct(String s) { Map<Character, Integer> charCount = new HashMap<>(); int left = 0, best = 0;
for (int right = 0; right < s.length(); right++) { char c = s.charAt(right); charCount.merge(c, 1, Integer::sum); while (charCount.size() > 2) { char leftChar = s.charAt(left); charCount.put(leftChar, charCount.get(leftChar) - 1); if (charCount.get(leftChar) == 0) charCount.remove(leftChar); left++; } best = Math.max(best, right - left + 1); } return best; }
public static void main(String[] args) { System.out.println(lengthOfLongestSubstringTwoDistinct("eceba")); // 3 System.out.println(lengthOfLongestSubstringTwoDistinct("ccaabbb")); // 5 }}fun lengthOfLongestSubstringTwoDistinct(s: String): Int { val charCount = HashMap<Char, Int>() var left = 0 var best = 0
for (right in s.indices) { val c = s[right] charCount[c] = charCount.getOrDefault(c, 0) + 1 while (charCount.size > 2) { val leftChar = s[left] charCount[leftChar] = charCount[leftChar]!! - 1 if (charCount[leftChar] == 0) charCount.remove(leftChar) left++ } best = maxOf(best, right - left + 1) } return best}
fun main() { println(lengthOfLongestSubstringTwoDistinct("eceba")) // 3 println(lengthOfLongestSubstringTwoDistinct("ccaabbb")) // 5}int lengthOfLongestSubstringTwoDistinct(String s) { final charCount = <String, int>{}; int left = 0, best = 0;
for (var right = 0; right < s.length; right++) { final c = s[right]; charCount[c] = (charCount[c] ?? 0) + 1; while (charCount.length > 2) { final leftChar = s[left]; charCount[leftChar] = charCount[leftChar]! - 1; if (charCount[leftChar] == 0) charCount.remove(leftChar); left++; } final len = right - left + 1; if (len > best) best = len; } return best;}
void main() { print(lengthOfLongestSubstringTwoDistinct("eceba")); // 3 print(lengthOfLongestSubstringTwoDistinct("ccaabbb")); // 5}59. Bình phương mảng đã sắp xếp (Squares of a Sorted Array)
Độ khó: Trung bình · Chủ đề: Two Pointers
Cho mảng số nguyên nums đã sắp xếp tăng dần (có thể chứa số âm). Trả về mảng bình phương của từng phần tử, vẫn sắp xếp tăng dần, với độ phức tạp O(n) (không dùng sort()).
Ví dụ 1:
Input: nums = [-4, -1, 0, 3, 10]Output: [0, 1, 9, 16, 100]Ví dụ 2:
Input: nums = [-7, -3, 2, 3, 11]Output: [4, 9, 9, 49, 121]Ràng buộc:
1 <= len(nums) <= 10^4numsđã sắp xếp tăng dần.
Xem đáp án
def sorted_squares(nums): # O(n): hai con trỏ từ 2 đầu, giá trị tuyệt đối lớn nhất luôn nằm ở 1 trong 2 đầu n = len(nums) result = [0] * n left, right = 0, n - 1 pos = n - 1
while left <= right: left_sq = nums[left] ** 2 right_sq = nums[right] ** 2 if left_sq > right_sq: result[pos] = left_sq left += 1 else: result[pos] = right_sq right -= 1 pos -= 1
return result
print(sorted_squares([-4, -1, 0, 3, 10])) # [0, 1, 9, 16, 100]print(sorted_squares([-7, -3, 2, 3, 11])) # [4, 9, 9, 49, 121]#include <iostream>#include <vector>using namespace std;
vector<long long> sortedSquares(vector<int>& nums) { int n = nums.size(); vector<long long> result(n); int left = 0, right = n - 1, pos = n - 1;
while (left <= right) { long long leftSq = (long long)nums[left] * nums[left]; long long rightSq = (long long)nums[right] * nums[right]; if (leftSq > rightSq) { result[pos] = leftSq; left++; } else { result[pos] = rightSq; right--; } pos--; } return result;}
int main() { vector<int> a = {-4, -1, 0, 3, 10}; vector<int> b = {-7, -3, 2, 3, 11}; for (long long x : sortedSquares(a)) cout << x << " "; cout << endl; // 0 1 9 16 100 for (long long x : sortedSquares(b)) cout << x << " "; cout << endl; // 4 9 9 49 121 return 0;}import java.util.Arrays;
public class Main { static long[] sortedSquares(int[] nums) { int n = nums.length; long[] result = new long[n]; int left = 0, right = n - 1, pos = n - 1;
while (left <= right) { long leftSq = (long) nums[left] * nums[left]; long rightSq = (long) nums[right] * nums[right]; if (leftSq > rightSq) { result[pos] = leftSq; left++; } else { result[pos] = rightSq; right--; } pos--; } return result; }
public static void main(String[] args) { System.out.println(Arrays.toString(sortedSquares(new int[]{-4, -1, 0, 3, 10}))); // [0, 1, 9, 16, 100] System.out.println(Arrays.toString(sortedSquares(new int[]{-7, -3, 2, 3, 11}))); // [4, 9, 9, 49, 121] }}fun sortedSquares(nums: List<Int>): List<Long> { val n = nums.size val result = LongArray(n) var left = 0 var right = n - 1 var pos = n - 1
while (left <= right) { val leftSq = nums[left].toLong() * nums[left] val rightSq = nums[right].toLong() * nums[right] if (leftSq > rightSq) { result[pos] = leftSq left++ } else { result[pos] = rightSq right-- } pos-- } return result.toList()}
fun main() { println(sortedSquares(listOf(-4, -1, 0, 3, 10))) // [0, 1, 9, 16, 100] println(sortedSquares(listOf(-7, -3, 2, 3, 11))) // [4, 9, 9, 49, 121]}List<int> sortedSquares(List<int> nums) { final n = nums.length; final result = List<int>.filled(n, 0); int left = 0, right = n - 1, pos = n - 1;
while (left <= right) { final leftSq = nums[left] * nums[left]; final rightSq = nums[right] * nums[right]; if (leftSq > rightSq) { result[pos] = leftSq; left++; } else { result[pos] = rightSq; right--; } pos--; } return result;}
void main() { print(sortedSquares([-4, -1, 0, 3, 10])); // [0, 1, 9, 16, 100] print(sortedSquares([-7, -3, 2, 3, 11])); // [4, 9, 9, 49, 121]}60. Cứu người bằng thuyền (Boats to Save People)
Độ khó: Trung bình · Chủ đề: Two Pointers · Greedy
Cho mảng people là cân nặng của từng người, và limit là trọng tải tối đa mỗi thuyền chở được tối đa 2 người. Tìm số lượng thuyền tối thiểu để chở hết mọi người.
Ví dụ 1:
Input: people = [1, 2], limit = 3Output: 1Giải thích: 1 thuyền chở cả 2 người (1 + 2 = 3 <= 3)Ví dụ 2:
Input: people = [3, 2, 2, 1], limit = 3Output: 3Giải thích: (1,2), (2), (3)Ràng buộc:
1 <= len(people) <= 5 * 10^41 <= people[i] <= limit <= 3 * 10^4
Xem đáp án
def num_rescue_boats(people, limit): # O(n log n): sắp xếp, ghép người nhẹ nhất với người nặng nhất nếu vừa đủ tải people.sort() left, right = 0, len(people) - 1 boats = 0
while left <= right: if people[left] + people[right] <= limit: left += 1 right -= 1 boats += 1
return boats
print(num_rescue_boats([1, 2], 3)) # 1print(num_rescue_boats([3, 2, 2, 1], 3)) # 3#include <iostream>#include <vector>#include <algorithm>using namespace std;
int numRescueBoats(vector<int> people, int limit) { sort(people.begin(), people.end()); int left = 0, right = people.size() - 1, boats = 0;
while (left <= right) { if (people[left] + people[right] <= limit) left++; right--; boats++; } return boats;}
int main() { cout << numRescueBoats({1, 2}, 3) << endl; // 1 cout << numRescueBoats({3, 2, 2, 1}, 3) << endl; // 3 return 0;}import java.util.Arrays;
public class Main { static int numRescueBoats(int[] people, int limit) { Arrays.sort(people); int left = 0, right = people.length - 1, boats = 0;
while (left <= right) { if (people[left] + people[right] <= limit) left++; right--; boats++; } return boats; }
public static void main(String[] args) { System.out.println(numRescueBoats(new int[]{1, 2}, 3)); // 1 System.out.println(numRescueBoats(new int[]{3, 2, 2, 1}, 3)); // 3 }}fun numRescueBoats(people: List<Int>, limit: Int): Int { val sorted = people.sorted() var left = 0 var right = sorted.size - 1 var boats = 0
while (left <= right) { if (sorted[left] + sorted[right] <= limit) left++ right-- boats++ } return boats}
fun main() { println(numRescueBoats(listOf(1, 2), 3)) // 1 println(numRescueBoats(listOf(3, 2, 2, 1), 3)) // 3}int numRescueBoats(List<int> people, int limit) { final sorted = List<int>.from(people)..sort(); int left = 0, right = sorted.length - 1, boats = 0;
while (left <= right) { if (sorted[left] + sorted[right] <= limit) left++; right--; boats++; } return boats;}
void main() { print(numRescueBoats([1, 2], 3)); // 1 print(numRescueBoats([3, 2, 2, 1], 3)); // 3}Nhóm 4: Sliding Window & Prefix Sum
Phần tiêu đề “Nhóm 4: Sliding Window & Prefix Sum”61. Trung bình cộng lớn nhất của dãy con độ dài k (Maximum Average Subarray I)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho mảng số nguyên nums và số nguyên k, tìm dãy con liên tiếp có đúng k phần tử sao cho trung bình cộng của nó là lớn nhất. Trả về giá trị trung bình đó.
Ví dụ 1:
Input: nums = [1,12,-5,-6,50,3], k = 4Output: 12.75Giải thích: Dãy con [12,-5,-6,50] có tổng 51, trung bình 51/4 = 12.75, lớn nhất trong các dãy con độ dài 4.Ví dụ 2:
Input: nums = [5,5,5], k = 1Output: 5.0Giải thích: Mọi dãy con độ dài 1 đều có trung bình 5.Ràng buộc:
1 <= k <= len(nums) <= 10^5-10^4 <= nums[i] <= 10^4
Xem đáp án
def find_max_average(nums, k): # Cửa sổ trượt kích thước cố định k, O(n) window_sum = sum(nums[:k]) max_sum = window_sum for i in range(k, len(nums)): window_sum += nums[i] - nums[i - k] max_sum = max(max_sum, window_sum) return max_sum / k
print(find_max_average([1, 12, -5, -6, 50, 3], 4)) # 12.75print(find_max_average([5, 5, 5], 1)) # 5.0#include <iostream>#include <vector>using namespace std;
double findMaxAverage(vector<int>& nums, int k) { long long windowSum = 0; for (int i = 0; i < k; i++) windowSum += nums[i]; long long maxSum = windowSum;
for (int i = k; i < (int)nums.size(); i++) { windowSum += nums[i] - nums[i - k]; maxSum = max(maxSum, windowSum); } return (double) maxSum / k;}
int main() { vector<int> a = {1, 12, -5, -6, 50, 3}; vector<int> b = {5, 5, 5}; cout << findMaxAverage(a, 4) << endl; // 12.75 cout << findMaxAverage(b, 1) << endl; // 5 return 0;}public class Main { static double findMaxAverage(int[] nums, int k) { long windowSum = 0; for (int i = 0; i < k; i++) windowSum += nums[i]; long maxSum = windowSum;
for (int i = k; i < nums.length; i++) { windowSum += nums[i] - nums[i - k]; maxSum = Math.max(maxSum, windowSum); } return (double) maxSum / k; }
public static void main(String[] args) { System.out.println(findMaxAverage(new int[]{1, 12, -5, -6, 50, 3}, 4)); // 12.75 System.out.println(findMaxAverage(new int[]{5, 5, 5}, 1)); // 5.0 }}fun findMaxAverage(nums: List<Int>, k: Int): Double { var windowSum = nums.take(k).sumOf { it.toLong() } var maxSum = windowSum
for (i in k until nums.size) { windowSum += nums[i] - nums[i - k] maxSum = maxOf(maxSum, windowSum) } return maxSum.toDouble() / k}
fun main() { println(findMaxAverage(listOf(1, 12, -5, -6, 50, 3), 4)) // 12.75 println(findMaxAverage(listOf(5, 5, 5), 1)) // 5.0}double findMaxAverage(List<int> nums, int k) { int windowSum = 0; for (var i = 0; i < k; i++) windowSum += nums[i]; int maxSum = windowSum;
for (var i = k; i < nums.length; i++) { windowSum += nums[i] - nums[i - k]; if (windowSum > maxSum) maxSum = windowSum; } return maxSum / k;}
void main() { print(findMaxAverage([1, 12, -5, -6, 50, 3], 4)); // 12.75 print(findMaxAverage([5, 5, 5], 1)); // 5.0}62. Thay thế ký tự để chuỗi con dài nhất toàn ký tự giống nhau (Longest Repeating Character Replacement)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho một chuỗi s chỉ gồm chữ in hoa và một số nguyên k. Bạn được phép thay thế tối đa k ký tự bất kỳ trong chuỗi bằng ký tự khác. Tìm độ dài của chuỗi con liên tiếp dài nhất chứa toàn cùng một ký tự sau khi thay thế.
Ví dụ 1:
Input: s = "ABAB", k = 2Output: 4Giải thích: Thay 2 ký tự 'A' (hoặc 'B') để được "AAAA" hoặc "BBBB".Ví dụ 2:
Input: s = "AABABBA", k = 1Output: 4Giải thích: Thay ký tự ở vị trí 3 thành 'A' để được "AABAABA" hoặc "AAAA" liên tiếp, độ dài 4.Ràng buộc:
1 <= len(s) <= 10^5schỉ gồm chữ cái in hoa A-Z0 <= k <= len(s)
Xem đáp án
def character_replacement(s, k): # Sliding window: cửa sổ hợp lệ khi (độ dài cửa sổ - số lần xuất hiện ký tự nhiều nhất) <= k count = {} left = 0 max_count = 0 # tần suất ký tự xuất hiện nhiều nhất trong cửa sổ hiện tại result = 0
for right in range(len(s)): count[s[right]] = count.get(s[right], 0) + 1 max_count = max(max_count, count[s[right]])
while (right - left + 1) - max_count > k: count[s[left]] -= 1 left += 1
result = max(result, right - left + 1)
return result
print(character_replacement("ABAB", 2)) # 4print(character_replacement("AABABBA", 1)) # 4#include <iostream>#include <unordered_map>#include <string>using namespace std;
int characterReplacement(string s, int k) { unordered_map<char, int> count; int left = 0, maxCount = 0, result = 0;
for (int right = 0; right < (int)s.size(); right++) { count[s[right]]++; maxCount = max(maxCount, count[s[right]]);
while ((right - left + 1) - maxCount > k) { count[s[left]]--; left++; } result = max(result, right - left + 1); } return result;}
int main() { cout << characterReplacement("ABAB", 2) << endl; // 4 cout << characterReplacement("AABABBA", 1) << endl; // 4 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int characterReplacement(String s, int k) { Map<Character, Integer> count = new HashMap<>(); int left = 0, maxCount = 0, result = 0;
for (int right = 0; right < s.length(); right++) { char c = s.charAt(right); count.merge(c, 1, Integer::sum); maxCount = Math.max(maxCount, count.get(c));
while ((right - left + 1) - maxCount > k) { char leftChar = s.charAt(left); count.put(leftChar, count.get(leftChar) - 1); left++; } result = Math.max(result, right - left + 1); } return result; }
public static void main(String[] args) { System.out.println(characterReplacement("ABAB", 2)); // 4 System.out.println(characterReplacement("AABABBA", 1)); // 4 }}fun characterReplacement(s: String, k: Int): Int { val count = HashMap<Char, Int>() var left = 0 var maxCount = 0 var result = 0
for (right in s.indices) { val c = s[right] count[c] = count.getOrDefault(c, 0) + 1 maxCount = maxOf(maxCount, count[c]!!)
while ((right - left + 1) - maxCount > k) { val leftChar = s[left] count[leftChar] = count[leftChar]!! - 1 left++ } result = maxOf(result, right - left + 1) } return result}
fun main() { println(characterReplacement("ABAB", 2)) // 4 println(characterReplacement("AABABBA", 1)) // 4}int characterReplacement(String s, int k) { final count = <String, int>{}; int left = 0, maxCount = 0, result = 0;
for (var right = 0; right < s.length; right++) { final c = s[right]; count[c] = (count[c] ?? 0) + 1; if (count[c]! > maxCount) maxCount = count[c]!;
while ((right - left + 1) - maxCount > k) { final leftChar = s[left]; count[leftChar] = count[leftChar]! - 1; left++; } final len = right - left + 1; if (len > result) result = len; } return result;}
void main() { print(characterReplacement("ABAB", 2)); // 4 print(characterReplacement("AABABBA", 1)); // 4}63. Kiểm tra hoán vị chuỗi con (Permutation in String)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho 2 chuỗi s1 và s2. Kiểm tra xem s2 có chứa một chuỗi con nào là hoán vị của s1 hay không.
Ví dụ 1:
Input: s1 = "ab", s2 = "eidbaooo"Output: TrueGiải thích: s2 chứa "ba", là một hoán vị của "ab".Ví dụ 2:
Input: s1 = "ab", s2 = "eidboaoo"Output: FalseGiải thích: Không có chuỗi con nào của s2 là hoán vị của "ab".Ràng buộc:
1 <= len(s1) <= len(s2) <= 10^4s1,s2chỉ gồm chữ thường a-z
Xem đáp án
from collections import Counter
def check_inclusion(s1, s2): need = Counter(s1) window = Counter() k = len(s1)
for i, c in enumerate(s2): window[c] += 1 if i >= k: left_char = s2[i - k] window[left_char] -= 1 if window[left_char] == 0: del window[left_char] if window == need: return True
return False
print(check_inclusion("ab", "eidbaooo")) # Trueprint(check_inclusion("ab", "eidboaoo")) # False#include <iostream>#include <string>#include <vector>using namespace std;
bool checkInclusion(string s1, string s2) { int k = s1.size(); if (k > (int)s2.size()) return false;
vector<int> need(26, 0), window(26, 0); for (char c : s1) need[c - 'a']++;
for (int i = 0; i < (int)s2.size(); i++) { window[s2[i] - 'a']++; if (i >= k) window[s2[i - k] - 'a']--; if (i >= k - 1 && window == need) return true; } return false;}
int main() { cout << (checkInclusion("ab", "eidbaooo") ? "true" : "false") << endl; // true cout << (checkInclusion("ab", "eidboaoo") ? "true" : "false") << endl; // false return 0;}import java.util.Arrays;
public class Main { static boolean checkInclusion(String s1, String s2) { int k = s1.length(); if (k > s2.length()) return false;
int[] need = new int[26], window = new int[26]; for (char c : s1.toCharArray()) need[c - 'a']++;
for (int i = 0; i < s2.length(); i++) { window[s2.charAt(i) - 'a']++; if (i >= k) window[s2.charAt(i - k) - 'a']--; if (i >= k - 1 && Arrays.equals(window, need)) return true; } return false; }
public static void main(String[] args) { System.out.println(checkInclusion("ab", "eidbaooo")); // true System.out.println(checkInclusion("ab", "eidboaoo")); // false }}fun checkInclusion(s1: String, s2: String): Boolean { val k = s1.length if (k > s2.length) return false
val need = IntArray(26) val window = IntArray(26) for (c in s1) need[c - 'a']++
for (i in s2.indices) { window[s2[i] - 'a']++ if (i >= k) window[s2[i - k] - 'a']-- if (i >= k - 1 && window.contentEquals(need)) return true } return false}
fun main() { println(checkInclusion("ab", "eidbaooo")) // true println(checkInclusion("ab", "eidboaoo")) // false}bool checkInclusion(String s1, String s2) { final k = s1.length; if (k > s2.length) return false;
final need = List<int>.filled(26, 0); final window = List<int>.filled(26, 0); final aCode = 'a'.codeUnitAt(0); for (var c in s1.codeUnits) need[c - aCode]++;
bool listEq(List<int> a, List<int> b) { for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return true; }
for (var i = 0; i < s2.length; i++) { window[s2.codeUnitAt(i) - aCode]++; if (i >= k) window[s2.codeUnitAt(i - k) - aCode]--; if (i >= k - 1 && listEq(window, need)) return true; } return false;}
void main() { print(checkInclusion("ab", "eidbaooo")); // true print(checkInclusion("ab", "eidboaoo")); // false}64. Hái trái cây trong giỏ (Fruit Into Baskets)
Độ khó: Trung bình · Chủ đề: Sliding Window
Một hàng cây, mỗi cây cho một loại trái cây fruits[i]. Bạn có đúng 2 giỏ, mỗi giỏ chỉ chứa được một loại trái cây duy nhất (không giới hạn số lượng). Bắt đầu từ một cây bất kỳ, đi sang phải liên tục và hái mỗi cây một trái, dừng lại khi gặp loại trái thứ 3. Tìm số lượng trái cây tối đa có thể hái được.
Ví dụ 1:
Input: fruits = [1,2,1]Output: 3Giải thích: Hái được cả 3 cây vì chỉ có 2 loại (1 và 2).Ví dụ 2:
Input: fruits = [0,1,2,2]Output: 3Giải thích: Hái từ cây thứ 2 trở đi: [1,2,2].Ràng buộc:
1 <= len(fruits) <= 10^50 <= fruits[i] <= 10^4
Xem đáp án
def total_fruit(fruits): # Bài toán thực chất là: tìm cửa sổ dài nhất chứa tối đa 2 giá trị khác nhau count = {} left = 0 result = 0
for right, fruit in enumerate(fruits): count[fruit] = count.get(fruit, 0) + 1
while len(count) > 2: left_fruit = fruits[left] count[left_fruit] -= 1 if count[left_fruit] == 0: del count[left_fruit] left += 1
result = max(result, right - left + 1)
return result
print(total_fruit([1, 2, 1])) # 3print(total_fruit([0, 1, 2, 2])) # 3#include <iostream>#include <vector>#include <unordered_map>using namespace std;
int totalFruit(vector<int>& fruits) { unordered_map<int, int> count; int left = 0, result = 0;
for (int right = 0; right < (int)fruits.size(); right++) { count[fruits[right]]++;
while (count.size() > 2) { int leftFruit = fruits[left]; count[leftFruit]--; if (count[leftFruit] == 0) count.erase(leftFruit); left++; } result = max(result, right - left + 1); } return result;}
int main() { vector<int> a = {1, 2, 1}, b = {0, 1, 2, 2}; cout << totalFruit(a) << endl; // 3 cout << totalFruit(b) << endl; // 3 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int totalFruit(int[] fruits) { Map<Integer, Integer> count = new HashMap<>(); int left = 0, result = 0;
for (int right = 0; right < fruits.length; right++) { count.merge(fruits[right], 1, Integer::sum);
while (count.size() > 2) { int leftFruit = fruits[left]; count.put(leftFruit, count.get(leftFruit) - 1); if (count.get(leftFruit) == 0) count.remove(leftFruit); left++; } result = Math.max(result, right - left + 1); } return result; }
public static void main(String[] args) { System.out.println(totalFruit(new int[]{1, 2, 1})); // 3 System.out.println(totalFruit(new int[]{0, 1, 2, 2})); // 3 }}fun totalFruit(fruits: List<Int>): Int { val count = HashMap<Int, Int>() var left = 0 var result = 0
for (right in fruits.indices) { val f = fruits[right] count[f] = count.getOrDefault(f, 0) + 1
while (count.size > 2) { val leftFruit = fruits[left] count[leftFruit] = count[leftFruit]!! - 1 if (count[leftFruit] == 0) count.remove(leftFruit) left++ } result = maxOf(result, right - left + 1) } return result}
fun main() { println(totalFruit(listOf(1, 2, 1))) // 3 println(totalFruit(listOf(0, 1, 2, 2))) // 3}int totalFruit(List<int> fruits) { final count = <int, int>{}; int left = 0, result = 0;
for (var right = 0; right < fruits.length; right++) { final f = fruits[right]; count[f] = (count[f] ?? 0) + 1;
while (count.length > 2) { final leftFruit = fruits[left]; count[leftFruit] = count[leftFruit]! - 1; if (count[leftFruit] == 0) count.remove(leftFruit); left++; } final len = right - left + 1; if (len > result) result = len; } return result;}
void main() { print(totalFruit([1, 2, 1])); // 3 print(totalFruit([0, 1, 2, 2])); // 3}65. Số lượng tối đa số 1 liên tiếp III (Max Consecutive Ones III)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho mảng nhị phân nums và số nguyên k. Bạn được phép đổi tối đa k số 0 thành số 1. Trả về số lượng số 1 liên tiếp tối đa có thể đạt được.
Ví dụ 1:
Input: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2Output: 6Giải thích: Đổi 2 số 0 ở vị trí 5 và 10 thành 1, dãy con [1,1,1,0,0,1,1,1,1,1,0] có 6 số 1 liên tiếp từ vị trí 5 đến 10.Ví dụ 2:
Input: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], k = 3Output: 10Ràng buộc:
1 <= len(nums) <= 10^5nums[i]là 0 hoặc 10 <= k <= len(nums)
Xem đáp án
def longest_ones(nums, k): left = 0 zeros = 0 result = 0
for right in range(len(nums)): if nums[right] == 0: zeros += 1
while zeros > k: if nums[left] == 0: zeros -= 1 left += 1
result = max(result, right - left + 1)
return result
print(longest_ones([1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], 2)) # 6#include <iostream>#include <vector>using namespace std;
int longestOnes(vector<int>& nums, int k) { int left = 0, zeros = 0, result = 0;
for (int right = 0; right < (int)nums.size(); right++) { if (nums[right] == 0) zeros++;
while (zeros > k) { if (nums[left] == 0) zeros--; left++; } result = max(result, right - left + 1); } return result;}
int main() { vector<int> a = {1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0}; cout << longestOnes(a, 2) << endl; // 6 return 0;}public class Main { static int longestOnes(int[] nums, int k) { int left = 0, zeros = 0, result = 0;
for (int right = 0; right < nums.length; right++) { if (nums[right] == 0) zeros++;
while (zeros > k) { if (nums[left] == 0) zeros--; left++; } result = Math.max(result, right - left + 1); } return result; }
public static void main(String[] args) { System.out.println(longestOnes(new int[]{1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0}, 2)); // 6 }}fun longestOnes(nums: List<Int>, k: Int): Int { var left = 0 var zeros = 0 var result = 0
for (right in nums.indices) { if (nums[right] == 0) zeros++
while (zeros > k) { if (nums[left] == 0) zeros-- left++ } result = maxOf(result, right - left + 1) } return result}
fun main() { println(longestOnes(listOf(1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0), 2)) // 6}int longestOnes(List<int> nums, int k) { int left = 0, zeros = 0, result = 0;
for (var right = 0; right < nums.length; right++) { if (nums[right] == 0) zeros++;
while (zeros > k) { if (nums[left] == 0) zeros--; left++; } final len = right - left + 1; if (len > result) result = len; } return result;}
void main() { print(longestOnes([1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], 2)); // 6}66. Đếm dãy con có tổng chia hết cho k (Subarray Sums Divisible by K)
Độ khó: Trung bình · Chủ đề: Prefix Sum
Cho mảng số nguyên nums và số nguyên k, đếm số lượng dãy con liên tiếp (không rỗng) có tổng chia hết cho k.
Ví dụ 1:
Input: nums = [4,5,0,-2,-3,1], k = 5Output: 7Giải thích: Có 7 dãy con tổng chia hết cho 5: [4,5,0,-2,-3,1], [5], [5,0], [5,0,-2,-3], [0], [0,-2,-3], [-2,-3].Ví dụ 2:
Input: nums = [5], k = 9Output: 0Ràng buộc:
1 <= len(nums) <= 3*10^4-10^4 <= nums[i] <= 10^42 <= k <= 10^4
Xem đáp án
def subarrays_div_by_k(nums, k): # Prefix sum mod k: nếu 2 vị trí có cùng số dư -> đoạn giữa chia hết cho k remainder_count = {0: 1} prefix = 0 result = 0
for n in nums: prefix += n r = prefix % k result += remainder_count.get(r, 0) remainder_count[r] = remainder_count.get(r, 0) + 1
return result
print(subarrays_div_by_k([4, 5, 0, -2, -3, 1], 5)) # 7print(subarrays_div_by_k([5], 9)) # 0#include <iostream>#include <vector>#include <unordered_map>using namespace std;
int subarraysDivByK(vector<int>& nums, int k) { unordered_map<int, int> remainderCount; remainderCount[0] = 1; int prefix = 0, result = 0;
for (int n : nums) { prefix += n; int r = ((prefix % k) + k) % k; // đảm bảo số dư không âm, giống Python result += remainderCount[r]; remainderCount[r]++; } return result;}
int main() { vector<int> a = {4, 5, 0, -2, -3, 1}; vector<int> b = {5}; cout << subarraysDivByK(a, 5) << endl; // 7 cout << subarraysDivByK(b, 9) << endl; // 0 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int subarraysDivByK(int[] nums, int k) { Map<Integer, Integer> remainderCount = new HashMap<>(); remainderCount.put(0, 1); int prefix = 0, result = 0;
for (int n : nums) { prefix += n; int r = ((prefix % k) + k) % k; result += remainderCount.getOrDefault(r, 0); remainderCount.merge(r, 1, Integer::sum); } return result; }
public static void main(String[] args) { System.out.println(subarraysDivByK(new int[]{4, 5, 0, -2, -3, 1}, 5)); // 7 System.out.println(subarraysDivByK(new int[]{5}, 9)); // 0 }}fun subarraysDivByK(nums: List<Int>, k: Int): Int { val remainderCount = HashMap<Int, Int>() remainderCount[0] = 1 var prefix = 0 var result = 0
for (n in nums) { prefix += n val r = ((prefix % k) + k) % k result += remainderCount.getOrDefault(r, 0) remainderCount[r] = remainderCount.getOrDefault(r, 0) + 1 } return result}
fun main() { println(subarraysDivByK(listOf(4, 5, 0, -2, -3, 1), 5)) // 7 println(subarraysDivByK(listOf(5), 9)) // 0}int subarraysDivByK(List<int> nums, int k) { final remainderCount = <int, int>{0: 1}; int prefix = 0, result = 0;
for (var n in nums) { prefix += n; final r = ((prefix % k) + k) % k; result += remainderCount[r] ?? 0; remainderCount[r] = (remainderCount[r] ?? 0) + 1; } return result;}
void main() { print(subarraysDivByK([4, 5, 0, -2, -3, 1], 5)); // 7 print(subarraysDivByK([5], 9)); // 0}67. Tích của mảng trừ phần tử hiện tại (Product of Array Except Self)
Độ khó: Trung bình · Chủ đề: Prefix Sum
Cho mảng số nguyên nums, trả về mảng result sao cho result[i] bằng tích của tất cả phần tử trong nums trừ nums[i]. Không dùng phép chia, độ phức tạp O(n).
Ví dụ 1:
Input: nums = [1,2,3,4]Output: [24,12,8,6]Ví dụ 2:
Input: nums = [-1,1,0,-3,3]Output: [0,0,9,0,0]Ràng buộc:
2 <= len(nums) <= 10^5-30 <= nums[i] <= 30
Xem đáp án
def product_except_self(nums): n = len(nums) result = [1] * n
prefix = 1 for i in range(n): result[i] = prefix prefix *= nums[i]
suffix = 1 for i in range(n - 1, -1, -1): result[i] *= suffix suffix *= nums[i]
return result
print(product_except_self([1, 2, 3, 4])) # [24, 12, 8, 6]print(product_except_self([-1, 1, 0, -3, 3])) # [0, 0, 9, 0, 0]#include <iostream>#include <vector>using namespace std;
vector<int> productExceptSelf(vector<int>& nums) { int n = nums.size(); vector<int> result(n, 1);
int prefix = 1; for (int i = 0; i < n; i++) { result[i] = prefix; prefix *= nums[i]; }
int suffix = 1; for (int i = n - 1; i >= 0; i--) { result[i] *= suffix; suffix *= nums[i]; } return result;}
int main() { vector<int> a = {1, 2, 3, 4}; vector<int> b = {-1, 1, 0, -3, 3}; for (int x : productExceptSelf(a)) cout << x << " "; cout << endl; // 24 12 8 6 for (int x : productExceptSelf(b)) cout << x << " "; cout << endl; // 0 0 9 0 0 return 0;}import java.util.Arrays;
public class Main { static int[] productExceptSelf(int[] nums) { int n = nums.length; int[] result = new int[n];
int prefix = 1; for (int i = 0; i < n; i++) { result[i] = prefix; prefix *= nums[i]; }
int suffix = 1; for (int i = n - 1; i >= 0; i--) { result[i] *= suffix; suffix *= nums[i]; } return result; }
public static void main(String[] args) { System.out.println(Arrays.toString(productExceptSelf(new int[]{1, 2, 3, 4}))); // [24, 12, 8, 6] System.out.println(Arrays.toString(productExceptSelf(new int[]{-1, 1, 0, -3, 3}))); // [0, 0, 9, 0, 0] }}fun productExceptSelf(nums: List<Int>): List<Int> { val n = nums.size val result = IntArray(n) { 1 }
var prefix = 1 for (i in 0 until n) { result[i] = prefix prefix *= nums[i] }
var suffix = 1 for (i in n - 1 downTo 0) { result[i] *= suffix suffix *= nums[i] } return result.toList()}
fun main() { println(productExceptSelf(listOf(1, 2, 3, 4))) // [24, 12, 8, 6] println(productExceptSelf(listOf(-1, 1, 0, -3, 3))) // [0, 0, 9, 0, 0]}List<int> productExceptSelf(List<int> nums) { final n = nums.length; final result = List<int>.filled(n, 1);
int prefix = 1; for (var i = 0; i < n; i++) { result[i] = prefix; prefix *= nums[i]; }
int suffix = 1; for (var i = n - 1; i >= 0; i--) { result[i] *= suffix; suffix *= nums[i]; } return result;}
void main() { print(productExceptSelf([1, 2, 3, 4])); // [24, 12, 8, 6] print(productExceptSelf([-1, 1, 0, -3, 3])); // [0, 0, 9, 0, 0]}68. Truy vấn tổng đoạn - mảng bất biến (Range Sum Query - Immutable)
Độ khó: Trung bình · Chủ đề: Prefix Sum
Thiết kế cấu trúc dữ liệu cho mảng số nguyên bất biến nums, hỗ trợ nhiều truy vấn sum_range(i, j) trả về tổng các phần tử từ chỉ số i đến j (bao gồm cả hai đầu), mỗi truy vấn phải chạy O(1).
Ví dụ 1:
Input: nums = [-2, 0, 3, -5, 2, -1]sumRange(0, 2) -> 1 (vì -2+0+3 = 1)sumRange(2, 5) -> -1 (vì 3-5+2-1 = -1)sumRange(0, 5) -> -3Ràng buộc:
1 <= len(nums) <= 10^40 <= i <= j <= len(nums) - 1- Tối đa
10^4lượt gọisumRange
Xem đáp án
class NumArray: def __init__(self, nums): # prefix[i] = tổng nums[0..i-1], tính trước 1 lần O(n) self.prefix = [0] * (len(nums) + 1) for i, n in enumerate(nums): self.prefix[i + 1] = self.prefix[i] + n
def sum_range(self, i, j): return self.prefix[j + 1] - self.prefix[i]
arr = NumArray([-2, 0, 3, -5, 2, -1])print(arr.sum_range(0, 2)) # 1print(arr.sum_range(2, 5)) # -1print(arr.sum_range(0, 5)) # -3#include <iostream>#include <vector>using namespace std;
class NumArray {public: vector<int> prefix;
NumArray(vector<int>& nums) { prefix.assign(nums.size() + 1, 0); for (int i = 0; i < (int)nums.size(); i++) { prefix[i + 1] = prefix[i] + nums[i]; } }
int sumRange(int i, int j) { return prefix[j + 1] - prefix[i]; }};
int main() { vector<int> nums = {-2, 0, 3, -5, 2, -1}; NumArray arr(nums); cout << arr.sumRange(0, 2) << endl; // 1 cout << arr.sumRange(2, 5) << endl; // -1 cout << arr.sumRange(0, 5) << endl; // -3 return 0;}public class Main { static class NumArray { int[] prefix;
NumArray(int[] nums) { prefix = new int[nums.length + 1]; for (int i = 0; i < nums.length; i++) { prefix[i + 1] = prefix[i] + nums[i]; } }
int sumRange(int i, int j) { return prefix[j + 1] - prefix[i]; } }
public static void main(String[] args) { NumArray arr = new NumArray(new int[]{-2, 0, 3, -5, 2, -1}); System.out.println(arr.sumRange(0, 2)); // 1 System.out.println(arr.sumRange(2, 5)); // -1 System.out.println(arr.sumRange(0, 5)); // -3 }}class NumArray(nums: List<Int>) { private val prefix = IntArray(nums.size + 1)
init { for (i in nums.indices) { prefix[i + 1] = prefix[i] + nums[i] } }
fun sumRange(i: Int, j: Int): Int = prefix[j + 1] - prefix[i]}
fun main() { val arr = NumArray(listOf(-2, 0, 3, -5, 2, -1)) println(arr.sumRange(0, 2)) // 1 println(arr.sumRange(2, 5)) // -1 println(arr.sumRange(0, 5)) // -3}class NumArray { late List<int> prefix;
NumArray(List<int> nums) { prefix = List<int>.filled(nums.length + 1, 0); for (var i = 0; i < nums.length; i++) { prefix[i + 1] = prefix[i] + nums[i]; } }
int sumRange(int i, int j) => prefix[j + 1] - prefix[i];}
void main() { final arr = NumArray([-2, 0, 3, -5, 2, -1]); print(arr.sumRange(0, 2)); // 1 print(arr.sumRange(2, 5)); // -1 print(arr.sumRange(0, 5)); // -3}69. Dãy con liên tục có tổng chia hết cho k (Continuous Subarray Sum)
Độ khó: Trung bình · Chủ đề: Prefix Sum
Cho mảng số nguyên không âm nums và số nguyên k. Kiểm tra xem mảng có chứa một dãy con liên tiếp độ dài ít nhất 2 có tổng là bội số của k hay không (k = 0 nghĩa là tổng phải bằng 0).
Ví dụ 1:
Input: nums = [23,2,4,6,7], k = 6Output: TrueGiải thích: [2,4] có tổng 6, là bội số của 6.Ví dụ 2:
Input: nums = [23,2,6,4,7], k = 6Output: TrueGiải thích: [23,2,6,4,7] có tổng 42, là bội số của 6, độ dài 5.Ràng buộc:
1 <= len(nums) <= 10^50 <= nums[i] <= 10^90 <= sum(nums) <= 2^31 - 11 <= k <= 2^31 - 1
Xem đáp án
def check_subarray_sum(nums, k): # Lưu số dư đầu tiên gặp cùng vị trí; nếu gặp lại số dư đó cách xa >= 2 -> True remainder_index = {0: -1} prefix = 0
for i, n in enumerate(nums): prefix += n r = prefix % k if r in remainder_index: if i - remainder_index[r] >= 2: return True else: remainder_index[r] = i
return False
print(check_subarray_sum([23, 2, 4, 6, 7], 6)) # Trueprint(check_subarray_sum([23, 2, 6, 4, 7], 6)) # True#include <iostream>#include <vector>#include <unordered_map>using namespace std;
bool checkSubarraySum(vector<int>& nums, int k) { unordered_map<int, int> remainderIndex; remainderIndex[0] = -1; long long prefix = 0;
for (int i = 0; i < (int)nums.size(); i++) { prefix += nums[i]; int r = prefix % k; if (remainderIndex.count(r)) { if (i - remainderIndex[r] >= 2) return true; } else { remainderIndex[r] = i; } } return false;}
int main() { vector<int> a = {23, 2, 4, 6, 7}; vector<int> b = {23, 2, 6, 4, 7}; cout << (checkSubarraySum(a, 6) ? "true" : "false") << endl; // true cout << (checkSubarraySum(b, 6) ? "true" : "false") << endl; // true return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static boolean checkSubarraySum(int[] nums, int k) { Map<Integer, Integer> remainderIndex = new HashMap<>(); remainderIndex.put(0, -1); long prefix = 0;
for (int i = 0; i < nums.length; i++) { prefix += nums[i]; int r = (int) (prefix % k); if (remainderIndex.containsKey(r)) { if (i - remainderIndex.get(r) >= 2) return true; } else { remainderIndex.put(r, i); } } return false; }
public static void main(String[] args) { System.out.println(checkSubarraySum(new int[]{23, 2, 4, 6, 7}, 6)); // true System.out.println(checkSubarraySum(new int[]{23, 2, 6, 4, 7}, 6)); // true }}fun checkSubarraySum(nums: List<Int>, k: Int): Boolean { val remainderIndex = HashMap<Int, Int>() remainderIndex[0] = -1 var prefix = 0L
for (i in nums.indices) { prefix += nums[i] val r = (prefix % k).toInt() if (remainderIndex.containsKey(r)) { if (i - remainderIndex[r]!! >= 2) return true } else { remainderIndex[r] = i } } return false}
fun main() { println(checkSubarraySum(listOf(23, 2, 4, 6, 7), 6)) // true println(checkSubarraySum(listOf(23, 2, 6, 4, 7), 6)) // true}bool checkSubarraySum(List<int> nums, int k) { final remainderIndex = <int, int>{0: -1}; int prefix = 0;
for (var i = 0; i < nums.length; i++) { prefix += nums[i]; final r = prefix % k; if (remainderIndex.containsKey(r)) { if (i - remainderIndex[r]! >= 2) return true; } else { remainderIndex[r] = i; } } return false;}
void main() { print(checkSubarraySum([23, 2, 4, 6, 7], 6)); // true print(checkSubarraySum([23, 2, 6, 4, 7], 6)); // true}70. Tìm tất cả phần tử trùng lặp trong mảng (Find All Duplicates in an Array)
Độ khó: Trung bình · Chủ đề: Prefix Sum / Mảng
Cho mảng nums gồm n số nguyên trong khoảng [1, n], mỗi số xuất hiện 1 hoặc 2 lần. Tìm tất cả các số xuất hiện 2 lần, độ phức tạp O(n) thời gian và O(1) bộ nhớ phụ (không tính mảng kết quả).
Ví dụ 1:
Input: nums = [4,3,2,7,8,2,3,1]Output: [2,3]Ví dụ 2:
Input: nums = [1,1,2]Output: [1]Ràng buộc:
n == len(nums)1 <= n <= 10^51 <= nums[i] <= n
Xem đáp án
def find_duplicates(nums): # Đánh dấu bằng cách đảo dấu tại chỉ số (giá trị - 1); nếu đã âm -> trùng result = [] for n in nums: idx = abs(n) - 1 if nums[idx] < 0: result.append(abs(n)) else: nums[idx] = -nums[idx] return result
print(find_duplicates([4, 3, 2, 7, 8, 2, 3, 1])) # [2, 3]print(find_duplicates([1, 1, 2])) # [1]#include <iostream>#include <vector>#include <cmath>using namespace std;
vector<int> findDuplicates(vector<int> nums) { vector<int> result; for (int n : nums) { int idx = abs(n) - 1; if (nums[idx] < 0) { result.push_back(abs(n)); } else { nums[idx] = -nums[idx]; } } return result;}
int main() { for (int x : findDuplicates({4, 3, 2, 7, 8, 2, 3, 1})) cout << x << " "; cout << endl; // 2 3 for (int x : findDuplicates({1, 1, 2})) cout << x << " "; cout << endl; // 1 return 0;}import java.util.ArrayList;import java.util.List;
public class Main { static List<Integer> findDuplicates(int[] nums) { List<Integer> result = new ArrayList<>(); for (int n : nums) { int idx = Math.abs(n) - 1; if (nums[idx] < 0) { result.add(Math.abs(n)); } else { nums[idx] = -nums[idx]; } } return result; }
public static void main(String[] args) { System.out.println(findDuplicates(new int[]{4, 3, 2, 7, 8, 2, 3, 1})); // [2, 3] System.out.println(findDuplicates(new int[]{1, 1, 2})); // [1] }}fun findDuplicates(nums: MutableList<Int>): List<Int> { val result = mutableListOf<Int>() for (n in nums.toList()) { val idx = Math.abs(n) - 1 if (nums[idx] < 0) { result.add(Math.abs(n)) } else { nums[idx] = -nums[idx] } } return result}
fun main() { println(findDuplicates(mutableListOf(4, 3, 2, 7, 8, 2, 3, 1))) // [2, 3] println(findDuplicates(mutableListOf(1, 1, 2))) // [1]}List<int> findDuplicates(List<int> nums) { final result = <int>[]; final original = List<int>.from(nums); for (var n in original) { final idx = n.abs() - 1; if (nums[idx] < 0) { result.add(n.abs()); } else { nums[idx] = -nums[idx]; } } return result;}
void main() { print(findDuplicates([4, 3, 2, 7, 8, 2, 3, 1])); // [2, 3] print(findDuplicates([1, 1, 2])); // [1]}71. Dãy con ngắn nhất có tổng >= target (Minimum Size Subarray Sum)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho mảng số nguyên dương nums và số nguyên dương target, tìm độ dài của dãy con liên tiếp ngắn nhất mà tổng của nó >= target. Nếu không tồn tại, trả về 0.
Ví dụ 1:
Input: target = 7, nums = [2,3,1,2,4,3]Output: 2Giải thích: [4,3] có tổng 7, là dãy con ngắn nhất thỏa mãn.Ví dụ 2:
Input: target = 11, nums = [1,1,1,1,1,1,1,1]Output: 0Giải thích: Tổng cả mảng chỉ là 8, không đủ 11.Ràng buộc:
1 <= target <= 10^91 <= len(nums) <= 10^51 <= nums[i] <= 10^4
Xem đáp án
def min_sub_array_len(target, nums): left = 0 total = 0 result = float("inf")
for right, n in enumerate(nums): total += n while total >= target: result = min(result, right - left + 1) total -= nums[left] left += 1
return result if result != float("inf") else 0
print(min_sub_array_len(7, [2, 3, 1, 2, 4, 3])) # 2print(min_sub_array_len(11, [1, 1, 1, 1, 1, 1, 1, 1])) # 0#include <iostream>#include <vector>#include <climits>using namespace std;
int minSubArrayLen(int target, vector<int>& nums) { int left = 0, total = 0, result = INT_MAX;
for (int right = 0; right < (int)nums.size(); right++) { total += nums[right]; while (total >= target) { result = min(result, right - left + 1); total -= nums[left]; left++; } }
return result == INT_MAX ? 0 : result;}
int main() { vector<int> nums1 = {2, 3, 1, 2, 4, 3}; cout << minSubArrayLen(7, nums1) << endl; // 2
vector<int> nums2 = {1, 1, 1, 1, 1, 1, 1, 1}; cout << minSubArrayLen(11, nums2) << endl; // 0 return 0;}public class Main { static int minSubArrayLen(int target, int[] nums) { int left = 0, total = 0, result = Integer.MAX_VALUE;
for (int right = 0; right < nums.length; right++) { total += nums[right]; while (total >= target) { result = Math.min(result, right - left + 1); total -= nums[left]; left++; } }
return result == Integer.MAX_VALUE ? 0 : result; }
public static void main(String[] args) { System.out.println(minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3})); // 2 System.out.println(minSubArrayLen(11, new int[]{1, 1, 1, 1, 1, 1, 1, 1})); // 0 }}fun minSubArrayLen(target: Int, nums: IntArray): Int { var left = 0 var total = 0 var result = Int.MAX_VALUE
for (right in nums.indices) { total += nums[right] while (total >= target) { result = minOf(result, right - left + 1) total -= nums[left] left++ } }
return if (result == Int.MAX_VALUE) 0 else result}
fun main() { println(minSubArrayLen(7, intArrayOf(2, 3, 1, 2, 4, 3))) // 2 println(minSubArrayLen(11, intArrayOf(1, 1, 1, 1, 1, 1, 1, 1))) // 0}int minSubArrayLen(int target, List<int> nums) { int left = 0, total = 0, result = 1 << 31;
for (int right = 0; right < nums.length; right++) { total += nums[right]; while (total >= target) { if (right - left + 1 < result) result = right - left + 1; total -= nums[left]; left++; } }
return result == (1 << 31) ? 0 : result;}
void main() { print(minSubArrayLen(7, [2, 3, 1, 2, 4, 3])); // 2 print(minSubArrayLen(11, [1, 1, 1, 1, 1, 1, 1, 1])); // 0}72. Trung bình cộng của dãy con bán kính k (K Radius Subarray Averages)
Độ khó: Trung bình · Chủ đề: Prefix Sum
Cho mảng nums và số nguyên k. Với mỗi chỉ số i, nếu tồn tại đủ k phần tử ở cả hai bên (tức i - k >= 0 và i + k <= n - 1), tính trung bình cộng làm tròn xuống của 2k + 1 phần tử xung quanh i (từ i-k đến i+k); nếu không đủ, kết quả tại i là -1. Trả về mảng kết quả.
Ví dụ 1:
Input: nums = [7,4,3,9,1,8,5,2,6], k = 3Output: [-1,-1,-1,5,4,4,-1,-1,-1]Giải thích: Tại i=3: (7+4+3+9+1+8+5)/7 = 37/7 = 5 (làm tròn xuống).Ví dụ 2:
Input: nums = [100000], k = 0Output: [100000]Giải thích: k=0 nghĩa là mỗi phần tử tự là trung bình của chính nó.Ràng buộc:
n == len(nums)1 <= n <= 10^50 <= nums[i], k <= 10^5
Xem đáp án
def get_averages(nums, k): n = len(nums) result = [-1] * n if 2 * k + 1 > n: return result
prefix = [0] * (n + 1) for i, num in enumerate(nums): prefix[i + 1] = prefix[i] + num
for i in range(k, n - k): window_sum = prefix[i + k + 1] - prefix[i - k] result[i] = window_sum // (2 * k + 1)
return result
print(get_averages([7, 4, 3, 9, 1, 8, 5, 2, 6], 3)) # [-1,-1,-1,5,4,4,-1,-1,-1]print(get_averages([100000], 0)) # [100000]#include <iostream>#include <vector>using namespace std;
vector<long long> getAverages(vector<int>& nums, int k) { int n = nums.size(); vector<long long> result(n, -1); if (2LL * k + 1 > n) return result;
vector<long long> prefix(n + 1, 0); for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + nums[i];
for (int i = k; i < n - k; i++) { long long windowSum = prefix[i + k + 1] - prefix[i - k]; result[i] = windowSum / (2 * k + 1); }
return result;}
int main() { vector<int> nums1 = {7, 4, 3, 9, 1, 8, 5, 2, 6}; for (long long x : getAverages(nums1, 3)) cout << x << " "; cout << endl; // -1 -1 -1 5 4 4 -1 -1 -1
vector<int> nums2 = {100000}; for (long long x : getAverages(nums2, 0)) cout << x << " "; cout << endl; // 100000 return 0;}import java.util.Arrays;
public class Main { static long[] getAverages(int[] nums, int k) { int n = nums.length; long[] result = new long[n]; Arrays.fill(result, -1); if (2L * k + 1 > n) return result;
long[] prefix = new long[n + 1]; for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + nums[i];
for (int i = k; i < n - k; i++) { long windowSum = prefix[i + k + 1] - prefix[i - k]; result[i] = windowSum / (2 * k + 1); }
return result; }
public static void main(String[] args) { System.out.println(Arrays.toString(getAverages(new int[]{7, 4, 3, 9, 1, 8, 5, 2, 6}, 3))); System.out.println(Arrays.toString(getAverages(new int[]{100000}, 0))); }}fun getAverages(nums: IntArray, k: Int): LongArray { val n = nums.size val result = LongArray(n) { -1L } if (2L * k + 1 > n) return result
val prefix = LongArray(n + 1) for (i in 0 until n) prefix[i + 1] = prefix[i] + nums[i]
for (i in k until n - k) { val windowSum = prefix[i + k + 1] - prefix[i - k] result[i] = windowSum / (2 * k + 1) }
return result}
fun main() { println(getAverages(intArrayOf(7, 4, 3, 9, 1, 8, 5, 2, 6), 3).joinToString()) println(getAverages(intArrayOf(100000), 0).joinToString())}List<int> getAverages(List<int> nums, int k) { int n = nums.length; List<int> result = List.filled(n, -1); if (2 * k + 1 > n) return result;
List<int> prefix = List.filled(n + 1, 0); for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + nums[i];
for (int i = k; i < n - k; i++) { int windowSum = prefix[i + k + 1] - prefix[i - k]; result[i] = windowSum ~/ (2 * k + 1); }
return result;}
void main() { print(getAverages([7, 4, 3, 9, 1, 8, 5, 2, 6], 3)); // [-1, -1, -1, 5, 4, 4, -1, -1, -1] print(getAverages([100000], 0)); // [100000]}73. Số lượng dãy con có giá trị lớn nhất giới hạn trong khoảng (Number of Subarrays with Bounded Maximum)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho mảng số nguyên dương nums và 2 số left, right. Đếm số dãy con liên tiếp (không rỗng) mà giá trị lớn nhất trong dãy con nằm trong khoảng [left, right].
Ví dụ 1:
Input: nums = [2,1,4,3], left = 2, right = 3Output: 3Giải thích: 3 dãy con thỏa mãn: [2], [2,1], [3].Ví dụ 2:
Input: nums = [2,9,2,5,6], left = 2, right = 8Output: 7Ràng buộc:
1 <= len(nums) <= 10^50 <= nums[i] <= 10^90 <= left <= right <= 10^9
Xem đáp án
def num_subarray_bounded_max(nums, left, right): # count(bound) = số dãy con có max <= bound def count_le(bound): result = 0 current = 0 for n in nums: current = current + 1 if n <= bound else 0 result += current return result
return count_le(right) - count_le(left - 1)
print(num_subarray_bounded_max([2, 1, 4, 3], 2, 3)) # 3print(num_subarray_bounded_max([2, 9, 2, 5, 6], 2, 8)) # 7#include <iostream>#include <vector>using namespace std;
long long countLe(vector<int>& nums, int bound) { long long result = 0, current = 0; for (int n : nums) { current = (n <= bound) ? current + 1 : 0; result += current; } return result;}
long long numSubarrayBoundedMax(vector<int>& nums, int left, int right) { return countLe(nums, right) - countLe(nums, left - 1);}
int main() { vector<int> nums1 = {2, 1, 4, 3}; cout << numSubarrayBoundedMax(nums1, 2, 3) << endl; // 3
vector<int> nums2 = {2, 9, 2, 5, 6}; cout << numSubarrayBoundedMax(nums2, 2, 8) << endl; // 7 return 0;}public class Main { static long countLe(int[] nums, int bound) { long result = 0, current = 0; for (int n : nums) { current = (n <= bound) ? current + 1 : 0; result += current; } return result; }
static long numSubarrayBoundedMax(int[] nums, int left, int right) { return countLe(nums, right) - countLe(nums, left - 1); }
public static void main(String[] args) { System.out.println(numSubarrayBoundedMax(new int[]{2, 1, 4, 3}, 2, 3)); // 3 System.out.println(numSubarrayBoundedMax(new int[]{2, 9, 2, 5, 6}, 2, 8)); // 7 }}fun countLe(nums: IntArray, bound: Int): Long { var result = 0L var current = 0L for (n in nums) { current = if (n <= bound) current + 1 else 0 result += current } return result}
fun numSubarrayBoundedMax(nums: IntArray, left: Int, right: Int): Long { return countLe(nums, right) - countLe(nums, left - 1)}
fun main() { println(numSubarrayBoundedMax(intArrayOf(2, 1, 4, 3), 2, 3)) // 3 println(numSubarrayBoundedMax(intArrayOf(2, 9, 2, 5, 6), 2, 8)) // 7}int countLe(List<int> nums, int bound) { int result = 0, current = 0; for (int n in nums) { current = (n <= bound) ? current + 1 : 0; result += current; } return result;}
int numSubarrayBoundedMax(List<int> nums, int left, int right) { return countLe(nums, right) - countLe(nums, left - 1);}
void main() { print(numSubarrayBoundedMax([2, 1, 4, 3], 2, 3)); // 3 print(numSubarrayBoundedMax([2, 9, 2, 5, 6], 2, 8)); // 7}74. Dãy con nhị phân có tổng bằng goal (Binary Subarrays With Sum)
Độ khó: Trung bình · Chủ đề: Sliding Window
Cho mảng nhị phân nums và số nguyên goal, đếm số lượng dãy con liên tiếp (không rỗng) có tổng đúng bằng goal.
Ví dụ 1:
Input: nums = [1,0,1,0,1], goal = 2Output: 4Giải thích: 4 dãy con thỏa mãn: [1,0,1], [1,0,1,0], [0,1,0,1], [1,0,1] (2 vị trí bắt đầu khác nhau).Ví dụ 2:
Input: nums = [0,0,0,0,0], goal = 0Output: 15Ràng buộc:
1 <= len(nums) <= 3*10^4nums[i]là 0 hoặc 10 <= goal <= len(nums)
Xem đáp án
def num_subarrays_with_sum(nums, goal): # count(sum <= bound) rồi trừ nhau, tương tự bài 73 def at_most(bound): if bound < 0: return 0 left = 0 total = 0 result = 0 for right, n in enumerate(nums): total += n while total > bound: total -= nums[left] left += 1 result += right - left + 1 return result
return at_most(goal) - at_most(goal - 1)
print(num_subarrays_with_sum([1, 0, 1, 0, 1], 2)) # 4print(num_subarrays_with_sum([0, 0, 0, 0, 0], 0)) # 15#include <iostream>#include <vector>using namespace std;
long long atMost(vector<int>& nums, int bound) { if (bound < 0) return 0; int left = 0, total = 0; long long result = 0; for (int right = 0; right < (int)nums.size(); right++) { total += nums[right]; while (total > bound) { total -= nums[left]; left++; } result += right - left + 1; } return result;}
long long numSubarraysWithSum(vector<int>& nums, int goal) { return atMost(nums, goal) - atMost(nums, goal - 1);}
int main() { vector<int> nums1 = {1, 0, 1, 0, 1}; cout << numSubarraysWithSum(nums1, 2) << endl; // 4
vector<int> nums2 = {0, 0, 0, 0, 0}; cout << numSubarraysWithSum(nums2, 0) << endl; // 15 return 0;}public class Main { static long atMost(int[] nums, int bound) { if (bound < 0) return 0; int left = 0, total = 0; long result = 0; for (int right = 0; right < nums.length; right++) { total += nums[right]; while (total > bound) { total -= nums[left]; left++; } result += right - left + 1; } return result; }
static long numSubarraysWithSum(int[] nums, int goal) { return atMost(nums, goal) - atMost(nums, goal - 1); }
public static void main(String[] args) { System.out.println(numSubarraysWithSum(new int[]{1, 0, 1, 0, 1}, 2)); // 4 System.out.println(numSubarraysWithSum(new int[]{0, 0, 0, 0, 0}, 0)); // 15 }}fun atMost(nums: IntArray, bound: Int): Long { if (bound < 0) return 0 var left = 0 var total = 0 var result = 0L for (right in nums.indices) { total += nums[right] while (total > bound) { total -= nums[left] left++ } result += right - left + 1 } return result}
fun numSubarraysWithSum(nums: IntArray, goal: Int): Long { return atMost(nums, goal) - atMost(nums, goal - 1)}
fun main() { println(numSubarraysWithSum(intArrayOf(1, 0, 1, 0, 1), 2)) // 4 println(numSubarraysWithSum(intArrayOf(0, 0, 0, 0, 0), 0)) // 15}int atMost(List<int> nums, int bound) { if (bound < 0) return 0; int left = 0, total = 0, result = 0; for (int right = 0; right < nums.length; right++) { total += nums[right]; while (total > bound) { total -= nums[left]; left++; } result += right - left + 1; } return result;}
int numSubarraysWithSum(List<int> nums, int goal) { return atMost(nums, goal) - atMost(nums, goal - 1);}
void main() { print(numSubarraysWithSum([1, 0, 1, 0, 1], 2)); // 4 print(numSubarraysWithSum([0, 0, 0, 0, 0], 0)); // 15}75. Điểm số lớn nhất khi lấy k lá bài (Maximum Points You Can Obtain from Cards)
Độ khó: Trung bình · Chủ đề: Sliding Window
Có n lá bài xếp thành hàng, cardPoints[i] là điểm của lá thứ i. Mỗi lượt bạn chỉ được lấy 1 lá từ đầu hoặc cuối hàng, phải lấy đúng k lượt. Tìm điểm số lớn nhất có thể đạt được.
Ví dụ 1:
Input: cardPoints = [1,2,3,4,5,6,1], k = 3Output: 12Giải thích: Lấy 3 lá cuối cùng: 1+6+5 = 12.Ví dụ 2:
Input: cardPoints = [2,2,2], k = 2Output: 4Ràng buộc:
1 <= len(cardPoints) <= 10^51 <= cardPoints[i] <= 10^41 <= k <= len(cardPoints)
Xem đáp án
def max_score(card_points, k): # Tương đương: tìm dãy con liên tiếp độ dài (n-k) ở giữa có tổng NHỎ NHẤT, # phần còn lại (2 đầu) chính là k lá đã lấy. n = len(card_points) total = sum(card_points) window_size = n - k if window_size == 0: return total
window_sum = sum(card_points[:window_size]) min_window = window_sum for i in range(window_size, n): window_sum += card_points[i] - card_points[i - window_size] min_window = min(min_window, window_sum)
return total - min_window
print(max_score([1, 2, 3, 4, 5, 6, 1], 3)) # 12print(max_score([2, 2, 2], 2)) # 4#include <iostream>#include <vector>#include <numeric>using namespace std;
int maxScore(vector<int>& cardPoints, int k) { int n = cardPoints.size(); int total = accumulate(cardPoints.begin(), cardPoints.end(), 0); int windowSize = n - k; if (windowSize == 0) return total;
int windowSum = 0; for (int i = 0; i < windowSize; i++) windowSum += cardPoints[i]; int minWindow = windowSum;
for (int i = windowSize; i < n; i++) { windowSum += cardPoints[i] - cardPoints[i - windowSize]; minWindow = min(minWindow, windowSum); }
return total - minWindow;}
int main() { vector<int> cards1 = {1, 2, 3, 4, 5, 6, 1}; cout << maxScore(cards1, 3) << endl; // 12
vector<int> cards2 = {2, 2, 2}; cout << maxScore(cards2, 2) << endl; // 4 return 0;}public class Main { static int maxScore(int[] cardPoints, int k) { int n = cardPoints.length; int total = 0; for (int c : cardPoints) total += c; int windowSize = n - k; if (windowSize == 0) return total;
int windowSum = 0; for (int i = 0; i < windowSize; i++) windowSum += cardPoints[i]; int minWindow = windowSum;
for (int i = windowSize; i < n; i++) { windowSum += cardPoints[i] - cardPoints[i - windowSize]; minWindow = Math.min(minWindow, windowSum); }
return total - minWindow; }
public static void main(String[] args) { System.out.println(maxScore(new int[]{1, 2, 3, 4, 5, 6, 1}, 3)); // 12 System.out.println(maxScore(new int[]{2, 2, 2}, 2)); // 4 }}fun maxScore(cardPoints: IntArray, k: Int): Int { val n = cardPoints.size val total = cardPoints.sum() val windowSize = n - k if (windowSize == 0) return total
var windowSum = cardPoints.take(windowSize).sum() var minWindow = windowSum
for (i in windowSize until n) { windowSum += cardPoints[i] - cardPoints[i - windowSize] minWindow = minOf(minWindow, windowSum) }
return total - minWindow}
fun main() { println(maxScore(intArrayOf(1, 2, 3, 4, 5, 6, 1), 3)) // 12 println(maxScore(intArrayOf(2, 2, 2), 2)) // 4}int maxScore(List<int> cardPoints, int k) { int n = cardPoints.length; int total = cardPoints.fold(0, (a, b) => a + b); int windowSize = n - k; if (windowSize == 0) return total;
int windowSum = 0; for (int i = 0; i < windowSize; i++) windowSum += cardPoints[i]; int minWindow = windowSum;
for (int i = windowSize; i < n; i++) { windowSum += cardPoints[i] - cardPoints[i - windowSize]; if (windowSum < minWindow) minWindow = windowSum; }
return total - minWindow;}
void main() { print(maxScore([1, 2, 3, 4, 5, 6, 1], 3)); // 12 print(maxScore([2, 2, 2], 2)); // 4}76. Giá trị lớn nhất trong từng cửa sổ trượt (Sliding Window Maximum)
Độ khó: Khó · Chủ đề: Sliding Window
Cho mảng nums và một cửa sổ kích thước k trượt từ trái sang phải, mỗi lần trượt 1 bước. Với mỗi vị trí cửa sổ, trả về giá trị lớn nhất trong cửa sổ đó. Yêu cầu độ phức tạp O(n).
Ví dụ 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3Output: [3,3,5,5,6,7]Giải thích: Cửa sổ [1,3,-1]->3, [3,-1,-3]->3, [-1,-3,5]->5, [-3,5,3]->5, [5,3,6]->6, [3,6,7]->7.Ví dụ 2:
Input: nums = [1], k = 1Output: [1]Ràng buộc:
1 <= len(nums) <= 10^5-10^4 <= nums[i] <= 10^41 <= k <= len(nums)
Xem đáp án
from collections import deque
def max_sliding_window(nums, k): # Deque lưu CHỈ SỐ, giữ tính giảm dần theo giá trị -> đầu deque luôn là max hiện tại. O(n). dq = deque() result = []
for i, n in enumerate(nums): while dq and nums[dq[-1]] < n: dq.pop() dq.append(i)
if dq[0] <= i - k: dq.popleft()
if i >= k - 1: result.append(nums[dq[0]])
return result
print(max_sliding_window([1, 3, -1, -3, 5, 3, 6, 7], 3)) # [3, 3, 5, 5, 6, 7]print(max_sliding_window([1], 1)) # [1]#include <iostream>#include <vector>#include <deque>using namespace std;
vector<int> maxSlidingWindow(vector<int>& nums, int k) { deque<int> dq; // luu chi so, giam dan theo gia tri vector<int> result;
for (int i = 0; i < (int)nums.size(); i++) { while (!dq.empty() && nums[dq.back()] < nums[i]) dq.pop_back(); dq.push_back(i);
if (dq.front() <= i - k) dq.pop_front();
if (i >= k - 1) result.push_back(nums[dq.front()]); }
return result;}
int main() { vector<int> nums1 = {1, 3, -1, -3, 5, 3, 6, 7}; for (int x : maxSlidingWindow(nums1, 3)) cout << x << " "; cout << endl; // 3 3 5 5 6 7
vector<int> nums2 = {1}; for (int x : maxSlidingWindow(nums2, 1)) cout << x << " "; cout << endl; // 1 return 0;}import java.util.*;
public class Main { static int[] maxSlidingWindow(int[] nums, int k) { Deque<Integer> dq = new ArrayDeque<>(); List<Integer> result = new ArrayList<>();
for (int i = 0; i < nums.length; i++) { while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) dq.pollLast(); dq.offerLast(i);
if (dq.peekFirst() <= i - k) dq.pollFirst();
if (i >= k - 1) result.add(nums[dq.peekFirst()]); }
return result.stream().mapToInt(Integer::intValue).toArray(); }
public static void main(String[] args) { System.out.println(Arrays.toString(maxSlidingWindow(new int[]{1, 3, -1, -3, 5, 3, 6, 7}, 3))); System.out.println(Arrays.toString(maxSlidingWindow(new int[]{1}, 1))); }}import java.util.ArrayDeque
fun maxSlidingWindow(nums: IntArray, k: Int): List<Int> { val dq = ArrayDeque<Int>() val result = mutableListOf<Int>()
for (i in nums.indices) { while (dq.isNotEmpty() && nums[dq.peekLast()] < nums[i]) dq.pollLast() dq.offerLast(i)
if (dq.peekFirst() <= i - k) dq.pollFirst()
if (i >= k - 1) result.add(nums[dq.peekFirst()]) }
return result}
fun main() { println(maxSlidingWindow(intArrayOf(1, 3, -1, -3, 5, 3, 6, 7), 3)) // [3, 3, 5, 5, 6, 7] println(maxSlidingWindow(intArrayOf(1), 1)) // [1]}import 'dart:collection';
List<int> maxSlidingWindow(List<int> nums, int k) { final dq = Queue<int>(); final result = <int>[];
for (int i = 0; i < nums.length; i++) { while (dq.isNotEmpty && nums[dq.last] < nums[i]) dq.removeLast(); dq.addLast(i);
if (dq.first <= i - k) dq.removeFirst();
if (i >= k - 1) result.add(nums[dq.first]); }
return result;}
void main() { print(maxSlidingWindow([1, 3, -1, -3, 5, 3, 6, 7], 3)); // [3, 3, 5, 5, 6, 7] print(maxSlidingWindow([1], 1)); // [1]}77. Cửa sổ nhỏ nhất chứa toàn bộ ký tự (Minimum Window Substring)
Độ khó: Khó · Chủ đề: Sliding Window
Cho 2 chuỗi s và t. Tìm chuỗi con ngắn nhất của s sao cho chứa tất cả các ký tự của t (kể cả trùng lặp, không quan tâm thứ tự). Nếu không tồn tại, trả về chuỗi rỗng.
Ví dụ 1:
Input: s = "ADOBECODEBANC", t = "ABC"Output: "BANC"Giải thích: "BANC" là chuỗi con ngắn nhất chứa cả 'A', 'B', 'C'.Ví dụ 2:
Input: s = "a", t = "aa"Output: ""Giải thích: t cần 2 ký tự 'a' nhưng s chỉ có 1 -> không tồn tại.Ràng buộc:
1 <= len(s), len(t) <= 10^5s,tgồm chữ cái Latin (hoa và thường)
Xem đáp án
from collections import Counter
def min_window(s, t): if not s or not t: return ""
need = Counter(t) missing = len(t) # tổng số ký tự còn thiếu để cửa sổ hợp lệ left = 0 best_left, best_right = 0, float("inf")
for right, c in enumerate(s, 1): if need[c] > 0: missing -= 1 need[c] -= 1
while missing == 0: if right - left < best_right - best_left: best_left, best_right = left, right need[s[left]] += 1 if need[s[left]] > 0: missing += 1 left += 1
return "" if best_right == float("inf") else s[best_left:best_right]
print(min_window("ADOBECODEBANC", "ABC")) # "BANC"print(min_window("a", "aa")) # ""#include <iostream>#include <string>#include <unordered_map>#include <climits>using namespace std;
string minWindow(string s, string t) { if (s.empty() || t.empty()) return "";
unordered_map<char, int> need; for (char c : t) need[c]++;
int missing = t.size(); int left = 0, bestLeft = 0, bestLen = INT_MAX;
for (int right = 0; right < (int)s.size(); right++) { char c = s[right]; if (need[c] > 0) missing--; need[c]--;
while (missing == 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestLeft = left; } need[s[left]]++; if (need[s[left]] > 0) missing++; left++; } }
return bestLen == INT_MAX ? "" : s.substr(bestLeft, bestLen);}
int main() { cout << minWindow("ADOBECODEBANC", "ABC") << endl; // BANC cout << minWindow("a", "aa") << endl; // (empty) return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static String minWindow(String s, String t) { if (s.isEmpty() || t.isEmpty()) return "";
Map<Character, Integer> need = new HashMap<>(); for (char c : t.toCharArray()) need.merge(c, 1, Integer::sum);
int missing = t.length(); int left = 0, bestLeft = 0, bestLen = Integer.MAX_VALUE;
for (int right = 0; right < s.length(); right++) { char c = s.charAt(right); need.put(c, need.getOrDefault(c, 0) - 1); if (need.get(c) >= 0) missing--;
while (missing == 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestLeft = left; } char lc = s.charAt(left); need.put(lc, need.get(lc) + 1); if (need.get(lc) > 0) missing++; left++; } }
return bestLen == Integer.MAX_VALUE ? "" : s.substring(bestLeft, bestLeft + bestLen); }
public static void main(String[] args) { System.out.println(minWindow("ADOBECODEBANC", "ABC")); // BANC System.out.println(minWindow("a", "aa")); // (empty) }}fun minWindow(s: String, t: String): String { if (s.isEmpty() || t.isEmpty()) return ""
val need = HashMap<Char, Int>() for (c in t) need[c] = (need[c] ?: 0) + 1
var missing = t.length var left = 0 var bestLeft = 0 var bestLen = Int.MAX_VALUE
for (right in s.indices) { val c = s[right] need[c] = (need[c] ?: 0) - 1 if (need[c]!! >= 0) missing--
while (missing == 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1 bestLeft = left } val lc = s[left] need[lc] = need[lc]!! + 1 if (need[lc]!! > 0) missing++ left++ } }
return if (bestLen == Int.MAX_VALUE) "" else s.substring(bestLeft, bestLeft + bestLen)}
fun main() { println(minWindow("ADOBECODEBANC", "ABC")) // BANC println(minWindow("a", "aa")) // (empty)}String minWindow(String s, String t) { if (s.isEmpty || t.isEmpty) return "";
final need = <String, int>{}; for (var c in t.split('')) need[c] = (need[c] ?? 0) + 1;
int missing = t.length; int left = 0, bestLeft = 0, bestLen = 1 << 30;
for (int right = 0; right < s.length; right++) { final c = s[right]; need[c] = (need[c] ?? 0) - 1; if (need[c]! >= 0) missing--;
while (missing == 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestLeft = left; } final lc = s[left]; need[lc] = need[lc]! + 1; if (need[lc]! > 0) missing++; left++; } }
return bestLen == (1 << 30) ? "" : s.substring(bestLeft, bestLeft + bestLen);}
void main() { print(minWindow("ADOBECODEBANC", "ABC")); // BANC print(minWindow("a", "aa")); // (empty)}78. Chuỗi con dài nhất có tối đa k ký tự khác nhau (Longest Substring with At Most K Distinct Characters)
Độ khó: Khó · Chủ đề: Sliding Window
Cho chuỗi s và số nguyên k, tìm độ dài của chuỗi con liên tiếp dài nhất chứa tối đa k ký tự khác nhau. Xử lý cả trường hợp k = 0 (kết quả phải là 0) và k lớn hơn số ký tự khác nhau trong s.
Ví dụ 1:
Input: s = "eceba", k = 2Output: 3Giải thích: Chuỗi con "ece" có 2 ký tự khác nhau ('e', 'c'), độ dài 3.Ví dụ 2:
Input: s = "aa", k = 1Output: 2Ví dụ 3 (biên):
Input: s = "abc", k = 0Output: 0Giải thích: Không được phép có ký tự khác nhau nào nên không có cửa sổ hợp lệ.Ràng buộc:
0 <= len(s) <= 5*10^40 <= k <= 50
Xem đáp án
def length_of_longest_substring_k_distinct(s, k): if k == 0 or not s: return 0
count = {} left = 0 result = 0
for right, c in enumerate(s): count[c] = count.get(c, 0) + 1
while len(count) > k: left_char = s[left] count[left_char] -= 1 if count[left_char] == 0: del count[left_char] left += 1
result = max(result, right - left + 1)
return result
print(length_of_longest_substring_k_distinct("eceba", 2)) # 3print(length_of_longest_substring_k_distinct("aa", 1)) # 2print(length_of_longest_substring_k_distinct("abc", 0)) # 0#include <iostream>#include <string>#include <unordered_map>using namespace std;
int lengthOfLongestSubstringKDistinct(string s, int k) { if (k == 0 || s.empty()) return 0;
unordered_map<char, int> count; int left = 0, result = 0;
for (int right = 0; right < (int)s.size(); right++) { count[s[right]]++;
while ((int)count.size() > k) { char leftChar = s[left]; count[leftChar]--; if (count[leftChar] == 0) count.erase(leftChar); left++; }
result = max(result, right - left + 1); }
return result;}
int main() { cout << lengthOfLongestSubstringKDistinct("eceba", 2) << endl; // 3 cout << lengthOfLongestSubstringKDistinct("aa", 1) << endl; // 2 cout << lengthOfLongestSubstringKDistinct("abc", 0) << endl; // 0 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int lengthOfLongestSubstringKDistinct(String s, int k) { if (k == 0 || s.isEmpty()) return 0;
Map<Character, Integer> count = new HashMap<>(); int left = 0, result = 0;
for (int right = 0; right < s.length(); right++) { char c = s.charAt(right); count.merge(c, 1, Integer::sum);
while (count.size() > k) { char leftChar = s.charAt(left); count.put(leftChar, count.get(leftChar) - 1); if (count.get(leftChar) == 0) count.remove(leftChar); left++; }
result = Math.max(result, right - left + 1); }
return result; }
public static void main(String[] args) { System.out.println(lengthOfLongestSubstringKDistinct("eceba", 2)); // 3 System.out.println(lengthOfLongestSubstringKDistinct("aa", 1)); // 2 System.out.println(lengthOfLongestSubstringKDistinct("abc", 0)); // 0 }}fun lengthOfLongestSubstringKDistinct(s: String, k: Int): Int { if (k == 0 || s.isEmpty()) return 0
val count = HashMap<Char, Int>() var left = 0 var result = 0
for (right in s.indices) { val c = s[right] count[c] = (count[c] ?: 0) + 1
while (count.size > k) { val leftChar = s[left] count[leftChar] = count[leftChar]!! - 1 if (count[leftChar] == 0) count.remove(leftChar) left++ }
result = maxOf(result, right - left + 1) }
return result}
fun main() { println(lengthOfLongestSubstringKDistinct("eceba", 2)) // 3 println(lengthOfLongestSubstringKDistinct("aa", 1)) // 2 println(lengthOfLongestSubstringKDistinct("abc", 0)) // 0}int lengthOfLongestSubstringKDistinct(String s, int k) { if (k == 0 || s.isEmpty) return 0;
final count = <String, int>{}; int left = 0, result = 0;
for (int right = 0; right < s.length; right++) { final c = s[right]; count[c] = (count[c] ?? 0) + 1;
while (count.length > k) { final leftChar = s[left]; count[leftChar] = count[leftChar]! - 1; if (count[leftChar] == 0) count.remove(leftChar); left++; }
if (right - left + 1 > result) result = right - left + 1; }
return result;}
void main() { print(lengthOfLongestSubstringKDistinct("eceba", 2)); // 3 print(lengthOfLongestSubstringKDistinct("aa", 1)); // 2 print(lengthOfLongestSubstringKDistinct("abc", 0)); // 0}79. Đếm dãy con có đúng k số nguyên khác nhau (Subarrays with K Different Integers)
Độ khó: Khó · Chủ đề: Sliding Window
Cho mảng nums và số nguyên k, đếm số lượng dãy con liên tiếp có đúng k giá trị khác nhau (không phải “tối đa”).
Ví dụ 1:
Input: nums = [1,2,1,2,3], k = 2Output: 7Giải thích: Các dãy con thỏa mãn: [1,2],[2,1],[1,2],[2,3],[1,2,1],[2,1,2],[1,2,1,2].Ví dụ 2:
Input: nums = [1,2,1,3,4], k = 3Output: 3Ràng buộc:
1 <= len(nums) <= 2*10^41 <= nums[i], k <= len(nums)
Xem đáp án
def subarrays_with_k_distinct(nums, k): # Số dãy con "đúng k giá trị khác nhau" = (dãy con "tối đa k") - (dãy con "tối đa k-1") def at_most_k_distinct(k): if k == 0: return 0 count = {} left = 0 result = 0 for right, n in enumerate(nums): count[n] = count.get(n, 0) + 1 while len(count) > k: left_val = nums[left] count[left_val] -= 1 if count[left_val] == 0: del count[left_val] left += 1 result += right - left + 1 return result
return at_most_k_distinct(k) - at_most_k_distinct(k - 1)
print(subarrays_with_k_distinct([1, 2, 1, 2, 3], 2)) # 7print(subarrays_with_k_distinct([1, 2, 1, 3, 4], 3)) # 3#include <iostream>#include <vector>#include <unordered_map>using namespace std;
int atMostKDistinct(vector<int>& nums, int k) { if (k == 0) return 0; unordered_map<int, int> count; int left = 0, result = 0; for (int right = 0; right < (int)nums.size(); right++) { count[nums[right]]++; while ((int)count.size() > k) { int leftVal = nums[left]; count[leftVal]--; if (count[leftVal] == 0) count.erase(leftVal); left++; } result += right - left + 1; } return result;}
int subarraysWithKDistinct(vector<int>& nums, int k) { return atMostKDistinct(nums, k) - atMostKDistinct(nums, k - 1);}
int main() { vector<int> nums1 = {1, 2, 1, 2, 3}; cout << subarraysWithKDistinct(nums1, 2) << endl; // 7
vector<int> nums2 = {1, 2, 1, 3, 4}; cout << subarraysWithKDistinct(nums2, 3) << endl; // 3 return 0;}import java.util.HashMap;import java.util.Map;
public class Main { static int atMostKDistinct(int[] nums, int k) { if (k == 0) return 0; Map<Integer, Integer> count = new HashMap<>(); int left = 0, result = 0; for (int right = 0; right < nums.length; right++) { count.merge(nums[right], 1, Integer::sum); while (count.size() > k) { int leftVal = nums[left]; count.put(leftVal, count.get(leftVal) - 1); if (count.get(leftVal) == 0) count.remove(leftVal); left++; } result += right - left + 1; } return result; }
static int subarraysWithKDistinct(int[] nums, int k) { return atMostKDistinct(nums, k) - atMostKDistinct(nums, k - 1); }
public static void main(String[] args) { System.out.println(subarraysWithKDistinct(new int[]{1, 2, 1, 2, 3}, 2)); // 7 System.out.println(subarraysWithKDistinct(new int[]{1, 2, 1, 3, 4}, 3)); // 3 }}fun atMostKDistinct(nums: IntArray, k: Int): Int { if (k == 0) return 0 val count = HashMap<Int, Int>() var left = 0 var result = 0 for (right in nums.indices) { count[nums[right]] = (count[nums[right]] ?: 0) + 1 while (count.size > k) { val leftVal = nums[left] count[leftVal] = count[leftVal]!! - 1 if (count[leftVal] == 0) count.remove(leftVal) left++ } result += right - left + 1 } return result}
fun subarraysWithKDistinct(nums: IntArray, k: Int): Int { return atMostKDistinct(nums, k) - atMostKDistinct(nums, k - 1)}
fun main() { println(subarraysWithKDistinct(intArrayOf(1, 2, 1, 2, 3), 2)) // 7 println(subarraysWithKDistinct(intArrayOf(1, 2, 1, 3, 4), 3)) // 3}int atMostKDistinct(List<int> nums, int k) { if (k == 0) return 0; final count = <int, int>{}; int left = 0, result = 0; for (int right = 0; right < nums.length; right++) { count[nums[right]] = (count[nums[right]] ?? 0) + 1; while (count.length > k) { final leftVal = nums[left]; count[leftVal] = count[leftVal]! - 1; if (count[leftVal] == 0) count.remove(leftVal); left++; } result += right - left + 1; } return result;}
int subarraysWithKDistinct(List<int> nums, int k) { return atMostKDistinct(nums, k) - atMostKDistinct(nums, k - 1);}
void main() { print(subarraysWithKDistinct([1, 2, 1, 2, 3], 2)); // 7 print(subarraysWithKDistinct([1, 2, 1, 3, 4], 3)); // 3}80. Chuỗi con là ghép nối của tất cả các từ (Substring with Concatenation of All Words)
Độ khó: Khó · Chủ đề: Sliding Window
Cho chuỗi s và một danh sách từ words, tất cả các từ có cùng độ dài. Tìm mọi vị trí bắt đầu trong s mà chuỗi con bắt đầu từ đó là một cách ghép nối (không dư, không thiếu, không chồng chéo) của toàn bộ các từ trong words theo bất kỳ thứ tự nào, kể cả khi words có từ trùng lặp.
Ví dụ 1:
Input: s = "barfoothefoobarman", words = ["foo","bar"]Output: [0,9]Giải thích: Từ vị trí 0: "barfoo" = "bar"+"foo". Từ vị trí 9: "foobar" = "foo"+"bar".Ví dụ 2:
Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]Output: []Giải thích: Không có vị trí nào ghép đủ và đúng số lượng từng từ (words có "word" xuất hiện 2 lần).Ví dụ 3 (biên - từ trùng lặp):
Input: s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]Output: [6,9,12]Ràng buộc:
1 <= len(s) <= 10^41 <= len(words) <= 50001 <= len(words[i]) <= 30svàwords[i]chỉ gồm chữ thường
Xem đáp án
from collections import Counter
def find_substring(s, words): if not s or not words: return []
word_len = len(words[0]) total_len = word_len * len(words) if len(s) < total_len: return []
need = Counter(words) result = []
# Thử từng điểm bắt đầu lệch pha 0..word_len-1, dùng cửa sổ trượt theo từng "khối" từ for offset in range(word_len): left = offset count = Counter() words_used = 0
for right in range(offset, len(s) - word_len + 1, word_len): word = s[right:right + word_len] if word in need: count[word] += 1 words_used += 1 while count[word] > need[word]: left_word = s[left:left + word_len] count[left_word] -= 1 words_used -= 1 left += word_len if words_used == len(words): result.append(left) left_word = s[left:left + word_len] count[left_word] -= 1 words_used -= 1 left += word_len else: count.clear() words_used = 0 left = right + word_len
return sorted(result)
print(find_substring("barfoothefoobarman", ["foo", "bar"])) # [0, 9]print(find_substring("wordgoodgoodgoodbestword", ["word", "good", "best", "word"])) # []print(find_substring("barfoofoobarthefoobarman", ["bar", "foo", "the"])) # [6, 9, 12]#include <iostream>#include <vector>#include <string>#include <unordered_map>#include <algorithm>using namespace std;
vector<int> findSubstring(string s, vector<string>& words) { if (s.empty() || words.empty()) return {};
int wordLen = words[0].size(); int totalLen = wordLen * words.size(); if ((int)s.size() < totalLen) return {};
unordered_map<string, int> need; for (auto& w : words) need[w]++;
vector<int> result;
for (int offset = 0; offset < wordLen; offset++) { int left = offset; unordered_map<string, int> count; int wordsUsed = 0;
for (int right = offset; right <= (int)s.size() - wordLen; right += wordLen) { string word = s.substr(right, wordLen); if (need.count(word)) { count[word]++; wordsUsed++; while (count[word] > need[word]) { string leftWord = s.substr(left, wordLen); count[leftWord]--; wordsUsed--; left += wordLen; } if (wordsUsed == (int)words.size()) { result.push_back(left); string leftWord = s.substr(left, wordLen); count[leftWord]--; wordsUsed--; left += wordLen; } } else { count.clear(); wordsUsed = 0; left = right + wordLen; } } }
sort(result.begin(), result.end()); return result;}
int main() { vector<string> w1 = {"foo", "bar"}; for (int x : findSubstring("barfoothefoobarman", w1)) cout << x << " "; cout << endl; // 0 9
vector<string> w2 = {"word", "good", "best", "word"}; for (int x : findSubstring("wordgoodgoodgoodbestword", w2)) cout << x << " "; cout << endl; // (empty)
vector<string> w3 = {"bar", "foo", "the"}; for (int x : findSubstring("barfoofoobarthefoobarman", w3)) cout << x << " "; cout << endl; // 6 9 12 return 0;}import java.util.*;
public class Main { static List<Integer> findSubstring(String s, String[] words) { if (s.isEmpty() || words.length == 0) return new ArrayList<>();
int wordLen = words[0].length(); int totalLen = wordLen * words.length; if (s.length() < totalLen) return new ArrayList<>();
Map<String, Integer> need = new HashMap<>(); for (String w : words) need.merge(w, 1, Integer::sum);
List<Integer> result = new ArrayList<>();
for (int offset = 0; offset < wordLen; offset++) { int left = offset; Map<String, Integer> count = new HashMap<>(); int wordsUsed = 0;
for (int right = offset; right <= s.length() - wordLen; right += wordLen) { String word = s.substring(right, right + wordLen); if (need.containsKey(word)) { count.merge(word, 1, Integer::sum); wordsUsed++; while (count.get(word) > need.get(word)) { String leftWord = s.substring(left, left + wordLen); count.merge(leftWord, -1, Integer::sum); wordsUsed--; left += wordLen; } if (wordsUsed == words.length) { result.add(left); String leftWord = s.substring(left, left + wordLen); count.merge(leftWord, -1, Integer::sum); wordsUsed--; left += wordLen; } } else { count.clear(); wordsUsed = 0; left = right + wordLen; } } }
Collections.sort(result); return result; }
public static void main(String[] args) { System.out.println(findSubstring("barfoothefoobarman", new String[]{"foo", "bar"})); System.out.println(findSubstring("wordgoodgoodgoodbestword", new String[]{"word", "good", "best", "word"})); System.out.println(findSubstring("barfoofoobarthefoobarman", new String[]{"bar", "foo", "the"})); }}fun findSubstring(s: String, words: Array<String>): List<Int> { if (s.isEmpty() || words.isEmpty()) return emptyList()
val wordLen = words[0].length val totalLen = wordLen * words.size if (s.length < totalLen) return emptyList()
val need = HashMap<String, Int>() for (w in words) need[w] = (need[w] ?: 0) + 1
val result = mutableListOf<Int>()
for (offset in 0 until wordLen) { var left = offset val count = HashMap<String, Int>() var wordsUsed = 0
var right = offset while (right <= s.length - wordLen) { val word = s.substring(right, right + wordLen) if (need.containsKey(word)) { count[word] = (count[word] ?: 0) + 1 wordsUsed++ while (count[word]!! > need[word]!!) { val leftWord = s.substring(left, left + wordLen) count[leftWord] = count[leftWord]!! - 1 wordsUsed-- left += wordLen } if (wordsUsed == words.size) { result.add(left) val leftWord = s.substring(left, left + wordLen) count[leftWord] = count[leftWord]!! - 1 wordsUsed-- left += wordLen } } else { count.clear() wordsUsed = 0 left = right + wordLen } right += wordLen } }
return result.sorted()}
fun main() { println(findSubstring("barfoothefoobarman", arrayOf("foo", "bar"))) println(findSubstring("wordgoodgoodgoodbestword", arrayOf("word", "good", "best", "word"))) println(findSubstring("barfoofoobarthefoobarman", arrayOf("bar", "foo", "the")))}List<int> findSubstring(String s, List<String> words) { if (s.isEmpty || words.isEmpty) return [];
int wordLen = words[0].length; int totalLen = wordLen * words.length; if (s.length < totalLen) return [];
final need = <String, int>{}; for (var w in words) need[w] = (need[w] ?? 0) + 1;
final result = <int>[];
for (int offset = 0; offset < wordLen; offset++) { int left = offset; final count = <String, int>{}; int wordsUsed = 0;
for (int right = offset; right <= s.length - wordLen; right += wordLen) { final word = s.substring(right, right + wordLen); if (need.containsKey(word)) { count[word] = (count[word] ?? 0) + 1; wordsUsed++; while (count[word]! > need[word]!) { final leftWord = s.substring(left, left + wordLen); count[leftWord] = count[leftWord]! - 1; wordsUsed--; left += wordLen; } if (wordsUsed == words.length) { result.add(left); final leftWord = s.substring(left, left + wordLen); count[leftWord] = count[leftWord]! - 1; wordsUsed--; left += wordLen; } } else { count.clear(); wordsUsed = 0; left = right + wordLen; } } }
result.sort(); return result;}
void main() { print(findSubstring("barfoothefoobarman", ["foo", "bar"])); print(findSubstring("wordgoodgoodgoodbestword", ["word", "good", "best", "word"])); print(findSubstring("barfoofoobarthefoobarman", ["bar", "foo", "the"]));}Nhóm 5: Stack, Queue & Linked List
Phần tiêu đề “Nhóm 5: Stack, Queue & Linked List”81. Kiểm tra ngoặc hợp lệ (Valid Parentheses)
Độ khó: Dễ · Chủ đề: Stack
Cho một chuỗi s chỉ chứa các ký tự (, ), {, }, [, ]. Kiểm tra chuỗi ngoặc đó có “hợp lệ” hay không: mỗi dấu mở phải được đóng bởi đúng loại dấu đóng tương ứng, và theo đúng thứ tự.
Ví dụ 1:
Input: s = "()[]{}"Output: TrueGiải thích: Mỗi cặp ngoặc đều đóng đúng loại và đúng thứ tự.Ví dụ 2:
Input: s = "(]"Output: FalseGiải thích: Dấu "(" bị đóng bởi "]" sai loại.Ràng buộc:
1 <= len(s) <= 10^4schỉ gồm các ký tự trong"()[]{}"
Xem đáp án
def is_valid(s): pairs = {")": "(", "]": "[", "}": "{"} stack = []
for c in s: if c in pairs: if not stack or stack.pop() != pairs[c]: return False else: stack.append(c)
return not stack
print(is_valid("()[]{}")) # Trueprint(is_valid("(]")) # False#include <iostream>#include <stack>#include <unordered_map>#include <string>using namespace std;
bool isValid(string s) { unordered_map<char, char> pairs = {{')', '('}, {']', '['}, {'}', '{'}}; stack<char> st;
for (char c : s) { if (pairs.count(c)) { if (st.empty() || st.top() != pairs[c]) return false; st.pop(); } else { st.push(c); } }
return st.empty();}
int main() { cout << boolalpha << isValid("()[]{}") << endl; // true cout << boolalpha << isValid("(]") << endl; // false return 0;}import java.util.*;
public class Main { static boolean isValid(String s) { Map<Character, Character> pairs = Map.of(')', '(', ']', '[', '}', '{'); Deque<Character> stack = new ArrayDeque<>();
for (char c : s.toCharArray()) { if (pairs.containsKey(c)) { if (stack.isEmpty() || stack.pop() != pairs.get(c)) return false; } else { stack.push(c); } }
return stack.isEmpty(); }
public static void main(String[] args) { System.out.println(isValid("()[]{}")); // true System.out.println(isValid("(]")); // false }}fun isValid(s: String): Boolean { val pairs = mapOf(')' to '(', ']' to '[', '}' to '{') val stack = ArrayDeque<Char>()
for (c in s) { if (c in pairs) { if (stack.isEmpty() || stack.removeLast() != pairs[c]) return false } else { stack.addLast(c) } }
return stack.isEmpty()}
fun main() { println(isValid("()[]{}")) // true println(isValid("(]")) // false}bool isValid(String s) { final pairs = {')': '(', ']': '[', '}': '{'}; final stack = <String>[];
for (var c in s.split('')) { if (pairs.containsKey(c)) { if (stack.isEmpty || stack.removeLast() != pairs[c]) return false; } else { stack.add(c); } }
return stack.isEmpty;}
void main() { print(isValid("()[]{}")); // true print(isValid("(]")); // false}82. Cài đặt Queue bằng hai Stack (Implement Queue using Two Stacks)
Độ khó: Dễ · Chủ đề: Stack, Queue
Cài đặt một hàng đợi (queue - FIFO) chỉ dùng hai ngăn xếp (stack - LIFO), hỗ trợ các thao tác push(x) (thêm vào cuối hàng đợi), pop() (lấy ra và xóa phần tử đầu hàng đợi), peek() (xem phần tử đầu hàng đợi).
Ví dụ 1:
Input: push(1), push(2), peek(), pop(), pop()Output: peek() -> 1, pop() -> 1, pop() -> 2Giải thích: Hàng đợi hoạt động theo nguyên tắc vào trước ra trước (FIFO).Ví dụ 2:
Input: push(5), pop(), push(6), push(7), pop()Output: pop() -> 5, pop() -> 6Ràng buộc:
- Tối đa
100lệnh gọi các thao tác. pop/peekchỉ được gọi khi hàng đợi không rỗng.
Xem đáp án
class MyQueue: def __init__(self): self.in_stack = [] self.out_stack = []
def push(self, x): self.in_stack.append(x)
def _shift(self): if not self.out_stack: while self.in_stack: self.out_stack.append(self.in_stack.pop())
def pop(self): self._shift() return self.out_stack.pop()
def peek(self): self._shift() return self.out_stack[-1]
q = MyQueue()q.push(1)q.push(2)print(q.peek()) # 1print(q.pop()) # 1print(q.pop()) # 2#include <iostream>#include <stack>using namespace std;
class MyQueue { stack<int> inStack, outStack;
void shift() { if (outStack.empty()) { while (!inStack.empty()) { outStack.push(inStack.top()); inStack.pop(); } } }
public: void push(int x) { inStack.push(x); }
int pop() { shift(); int val = outStack.top(); outStack.pop(); return val; }
int peek() { shift(); return outStack.top(); }};
int main() { MyQueue q; q.push(1); q.push(2); cout << q.peek() << endl; // 1 cout << q.pop() << endl; // 1 cout << q.pop() << endl; // 2 return 0;}import java.util.*;
public class Main { static class MyQueue { Deque<Integer> inStack = new ArrayDeque<>(); Deque<Integer> outStack = new ArrayDeque<>();
void push(int x) { inStack.push(x); }
void shift() { if (outStack.isEmpty()) { while (!inStack.isEmpty()) outStack.push(inStack.pop()); } }
int pop() { shift(); return outStack.pop(); }
int peek() { shift(); return outStack.peek(); } }
public static void main(String[] args) { MyQueue q = new MyQueue(); q.push(1); q.push(2); System.out.println(q.peek()); // 1 System.out.println(q.pop()); // 1 System.out.println(q.pop()); // 2 }}class MyQueue { private val inStack = ArrayDeque<Int>() private val outStack = ArrayDeque<Int>()
fun push(x: Int) { inStack.addLast(x) }
private fun shift() { if (outStack.isEmpty()) { while (inStack.isNotEmpty()) outStack.addLast(inStack.removeLast()) } }
fun pop(): Int { shift() return outStack.removeLast() }
fun peek(): Int { shift() return outStack.last() }}
fun main() { val q = MyQueue() q.push(1) q.push(2) println(q.peek()) // 1 println(q.pop()) // 1 println(q.pop()) // 2}class MyQueue { final List<int> inStack = []; final List<int> outStack = [];
void push(int x) { inStack.add(x); }
void _shift() { if (outStack.isEmpty) { while (inStack.isNotEmpty) outStack.add(inStack.removeLast()); } }
int pop() { _shift(); return outStack.removeLast(); }
int peek() { _shift(); return outStack.last; }}
void main() { final q = MyQueue(); q.push(1); q.push(2); print(q.peek()); // 1 print(q.pop()); // 1 print(q.pop()); // 2}83. Min Stack (Stack hỗ trợ lấy giá trị nhỏ nhất)
Độ khó: Dễ · Chủ đề: Stack
Thiết kế một ngăn xếp (stack) hỗ trợ các thao tác push(x), pop(), top() (xem đỉnh stack), và get_min() — trả về phần tử nhỏ nhất hiện có trong stack, tất cả đều chạy trong thời gian O(1).
Ví dụ 1:
Input: push(-2), push(0), push(-3), get_min(), pop(), top(), get_min()Output: get_min() -> -3, top() -> 0, get_min() -> -2Ví dụ 2:
Input: push(5), push(3), push(7), get_min()Output: 3Ràng buộc:
-2^31 <= x <= 2^31 - 1pop,top,get_minchỉ được gọi khi stack không rỗng.
Xem đáp án
class MinStack: def __init__(self): self.stack = [] self.min_stack = []
def push(self, x): self.stack.append(x) current_min = x if not self.min_stack else min(x, self.min_stack[-1]) self.min_stack.append(current_min)
def pop(self): self.stack.pop() self.min_stack.pop()
def top(self): return self.stack[-1]
def get_min(self): return self.min_stack[-1]
s = MinStack()s.push(-2)s.push(0)s.push(-3)print(s.get_min()) # -3s.pop()print(s.top()) # 0print(s.get_min()) # -2#include <iostream>#include <stack>#include <algorithm>using namespace std;
class MinStack { stack<int> st, minSt;
public: void push(int x) { st.push(x); int currentMin = minSt.empty() ? x : min(x, minSt.top()); minSt.push(currentMin); }
void pop() { st.pop(); minSt.pop(); }
int top() { return st.top(); }
int getMin() { return minSt.top(); }};
int main() { MinStack s; s.push(-2); s.push(0); s.push(-3); cout << s.getMin() << endl; // -3 s.pop(); cout << s.top() << endl; // 0 cout << s.getMin() << endl; // -2 return 0;}import java.util.*;
public class Main { static class MinStack { Deque<Integer> stack = new ArrayDeque<>(); Deque<Integer> minStack = new ArrayDeque<>();
void push(int x) { stack.push(x); int currentMin = minStack.isEmpty() ? x : Math.min(x, minStack.peek()); minStack.push(currentMin); }
void pop() { stack.pop(); minStack.pop(); }
int top() { return stack.peek(); }
int getMin() { return minStack.peek(); } }
public static void main(String[] args) { MinStack s = new MinStack(); s.push(-2); s.push(0); s.push(-3); System.out.println(s.getMin()); // -3 s.pop(); System.out.println(s.top()); // 0 System.out.println(s.getMin()); // -2 }}class MinStack { private val stack = ArrayDeque<Int>() private val minStack = ArrayDeque<Int>()
fun push(x: Int) { stack.addLast(x) val currentMin = if (minStack.isEmpty()) x else minOf(x, minStack.last()) minStack.addLast(currentMin) }
fun pop() { stack.removeLast() minStack.removeLast() }
fun top(): Int = stack.last()
fun getMin(): Int = minStack.last()}
fun main() { val s = MinStack() s.push(-2) s.push(0) s.push(-3) println(s.getMin()) // -3 s.pop() println(s.top()) // 0 println(s.getMin()) // -2}class MinStack { final List<int> stack = []; final List<int> minStack = [];
void push(int x) { stack.add(x); int currentMin = minStack.isEmpty ? x : (x < minStack.last ? x : minStack.last); minStack.add(currentMin); }
void pop() { stack.removeLast(); minStack.removeLast(); }
int top() => stack.last;
int getMin() => minStack.last;}
void main() { final s = MinStack(); s.push(-2); s.push(0); s.push(-3); print(s.getMin()); // -3 s.pop(); print(s.top()); // 0 print(s.getMin()); // -2}84. Tính điểm bóng chày (Baseball Game)
Độ khó: Dễ · Chủ đề: Stack
Bạn đang ghi lại điểm số của một trận đấu qua danh sách các “hành động” ops. Mỗi phần tử có thể là: một số nguyên (dạng chuỗi) là điểm số của lượt đó; "+" nghĩa là điểm bằng tổng 2 lượt điểm gần nhất; "D" nghĩa là điểm gấp đôi lượt điểm gần nhất; "C" nghĩa là hủy lượt điểm gần nhất. Tính tổng điểm sau khi thực hiện hết các hành động.
Ví dụ 1:
Input: ops = ["5", "2", "C", "D", "+"]Output: 30Giải thích: 5 -> [5], 2 -> [5,2], C hủy 2 -> [5], D nhân đôi 5 -> [5,10], + = 5+10 -> [5,10,15]. Tổng = 5+10+15 = 30.Ví dụ 2:
Input: ops = ["5", "-2", "4", "C", "D", "9", "+", "+"]Output: 27Ràng buộc:
1 <= len(ops) <= 1000- Dữ liệu vào luôn hợp lệ (không cần xử lý lỗi).
Xem đáp án
def cal_points(ops): stack = []
for op in ops: if op == "+": stack.append(stack[-1] + stack[-2]) elif op == "D": stack.append(stack[-1] * 2) elif op == "C": stack.pop() else: stack.append(int(op))
return sum(stack)
print(cal_points(["5", "2", "C", "D", "+"])) # 30print(cal_points(["5", "-2", "4", "C", "D", "9", "+", "+"])) # 27#include <iostream>#include <vector>#include <string>#include <numeric>using namespace std;
int calPoints(vector<string>& ops) { vector<int> stack;
for (auto& op : ops) { if (op == "+") { stack.push_back(stack[stack.size() - 1] + stack[stack.size() - 2]); } else if (op == "D") { stack.push_back(stack.back() * 2); } else if (op == "C") { stack.pop_back(); } else { stack.push_back(stoi(op)); } }
return accumulate(stack.begin(), stack.end(), 0);}
int main() { vector<string> ops1 = {"5", "2", "C", "D", "+"}; cout << calPoints(ops1) << endl; // 30
vector<string> ops2 = {"5", "-2", "4", "C", "D", "9", "+", "+"}; cout << calPoints(ops2) << endl; // 27 return 0;}import java.util.*;
public class Main { static int calPoints(String[] ops) { Deque<Integer> stack = new ArrayDeque<>();
for (String op : ops) { if (op.equals("+")) { Iterator<Integer> it = stack.iterator(); int top = it.next(); int second = it.next(); stack.push(top + second); } else if (op.equals("D")) { stack.push(stack.peek() * 2); } else if (op.equals("C")) { stack.pop(); } else { stack.push(Integer.parseInt(op)); } }
int sum = 0; for (int x : stack) sum += x; return sum; }
public static void main(String[] args) { System.out.println(calPoints(new String[]{"5", "2", "C", "D", "+"})); // 30 System.out.println(calPoints(new String[]{"5", "-2", "4", "C", "D", "9", "+", "+"})); // 27 }}fun calPoints(ops: Array<String>): Int { val stack = ArrayDeque<Int>()
for (op in ops) { when (op) { "+" -> stack.addLast(stack[stack.size - 1] + stack[stack.size - 2]) "D" -> stack.addLast(stack.last() * 2) "C" -> stack.removeLast() else -> stack.addLast(op.toInt()) } }
return stack.sum()}
fun main() { println(calPoints(arrayOf("5", "2", "C", "D", "+"))) // 30 println(calPoints(arrayOf("5", "-2", "4", "C", "D", "9", "+", "+"))) // 27}int calPoints(List<String> ops) { final stack = <int>[];
for (var op in ops) { if (op == "+") { stack.add(stack[stack.length - 1] + stack[stack.length - 2]); } else if (op == "D") { stack.add(stack.last * 2); } else if (op == "C") { stack.removeLast(); } else { stack.add(int.parse(op)); } }
return stack.fold(0, (a, b) => a + b);}
void main() { print(calPoints(["5", "2", "C", "D", "+"])); // 30 print(calPoints(["5", "-2", "4", "C", "D", "9", "+", "+"])); // 27}85. Xóa phần tử trùng lặp trong danh sách liên kết đã sắp xếp
Độ khó: Dễ · Chủ đề: Linked List
Cho một danh sách liên kết đơn (singly linked list) đã sắp xếp tăng dần, xóa các node trùng giá trị sao cho mỗi giá trị chỉ xuất hiện một lần, giữ nguyên thứ tự.
Ví dụ 1:
Input: [1,1,2]Output: [1,2]Ví dụ 2:
Input: [1,1,2,3,3]Output: [1,2,3]Ràng buộc:
- Số node trong khoảng
[0, 300]. - Danh sách đầu vào đã được sắp xếp tăng dần.
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def delete_duplicates(head): current = head while current and current.next: if current.val == current.next.val: current.next = current.next.next else: current = current.next return head
print(to_list(delete_duplicates(build_list([1, 1, 2])))) # [1, 2]print(to_list(delete_duplicates(build_list([1, 1, 2, 3, 3])))) # [1, 2, 3]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy(0); ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* deleteDuplicates(ListNode* head) { ListNode* current = head; while (current && current->next) { if (current->val == current->next->val) { current->next = current->next->next; } else { current = current->next; } } return head;}
int main() { vector<int> v1 = {1, 1, 2}; for (int x : toList(deleteDuplicates(buildList(v1)))) cout << x << " "; cout << endl; // 1 2
vector<int> v2 = {1, 1, 2, 3, 3}; for (int x : toList(deleteDuplicates(buildList(v2)))) cout << x << " "; cout << endl; // 1 2 3 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode deleteDuplicates(ListNode head) { ListNode current = head; while (current != null && current.next != null) { if (current.val == current.next.val) { current.next = current.next.next; } else { current = current.next; } } return head; }
public static void main(String[] args) { System.out.println(toList(deleteDuplicates(buildList(new int[]{1, 1, 2})))); // [1, 2] System.out.println(toList(deleteDuplicates(buildList(new int[]{1, 1, 2, 3, 3})))); // [1, 2, 3] }}class ListNode(var value: Int) { var next: ListNode? = null}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.value) node = node.next } return result}
fun deleteDuplicates(head: ListNode?): ListNode? { var current = head while (current?.next != null) { if (current.value == current.next!!.value) { current.next = current.next!!.next } else { current = current.next } } return head}
fun main() { println(toList(deleteDuplicates(buildList(listOf(1, 1, 2))))) // [1, 2] println(toList(deleteDuplicates(buildList(listOf(1, 1, 2, 3, 3))))) // [1, 2, 3]}class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? deleteDuplicates(ListNode? head) { var current = head; while (current?.next != null) { if (current!.val == current.next!.val) { current.next = current.next!.next; } else { current = current.next; } } return head;}
void main() { print(toList(deleteDuplicates(buildList([1, 1, 2])))); // [1, 2] print(toList(deleteDuplicates(buildList([1, 1, 2, 3, 3])))); // [1, 2, 3]}86. Phát hiện chu trình trong danh sách liên kết (Linked List Cycle)
Độ khó: Dễ · Chủ đề: Linked List
Cho một danh sách liên kết đơn, xác định xem danh sách đó có tồn tại chu trình (cycle - một node trỏ ngược về node đã đi qua trước đó) hay không. Dùng thuật toán “rùa và thỏ” (hai con trỏ, một chạy chậm một chạy nhanh) để giải quyết với bộ nhớ O(1).
Ví dụ 1:
Input: [3,2,0,-4], vị trí node cuối trỏ về index 1 (tạo chu trình)Output: TrueVí dụ 2:
Input: [1,2], không có chu trìnhOutput: FalseRàng buộc:
- Số node trong khoảng
[0, 10^4]. - Không dùng thêm cấu trúc dữ liệu phụ để lưu các node đã thăm (yêu cầu O(1) bộ nhớ).
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def has_cycle(head): slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next if slow is fast: return True return False
# Ví dụ 1: tạo chu trình thủ côngn1, n2, n3, n4 = ListNode(3), ListNode(2), ListNode(0), ListNode(-4)n1.next, n2.next, n3.next, n4.next = n2, n3, n4, n2 # n4 trỏ về n2print(has_cycle(n1)) # True
# Ví dụ 2: không chu trìnhm1, m2 = ListNode(1), ListNode(2)m1.next = m2print(has_cycle(m1)) # False#include <iostream>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
bool hasCycle(ListNode* head) { ListNode* slow = head; ListNode* fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) return true; } return false;}
int main() { // Vi du 1: tao chu trinh thu cong ListNode *n1 = new ListNode(3), *n2 = new ListNode(2), *n3 = new ListNode(0), *n4 = new ListNode(-4); n1->next = n2; n2->next = n3; n3->next = n4; n4->next = n2; // n4 tro ve n2 cout << boolalpha << hasCycle(n1) << endl; // true
// Vi du 2: khong chu trinh ListNode *m1 = new ListNode(1), *m2 = new ListNode(2); m1->next = m2; cout << boolalpha << hasCycle(m1) << endl; // false return 0;}public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static boolean hasCycle(ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) return true; } return false; }
public static void main(String[] args) { // Vi du 1: tao chu trinh thu cong ListNode n1 = new ListNode(3), n2 = new ListNode(2), n3 = new ListNode(0), n4 = new ListNode(-4); n1.next = n2; n2.next = n3; n3.next = n4; n4.next = n2; // n4 tro ve n2 System.out.println(hasCycle(n1)); // true
// Vi du 2: khong chu trinh ListNode m1 = new ListNode(1), m2 = new ListNode(2); m1.next = m2; System.out.println(hasCycle(m1)); // false }}class ListNode(var value: Int) { var next: ListNode? = null}
fun hasCycle(head: ListNode?): Boolean { var slow = head var fast = head while (fast?.next != null) { slow = slow?.next fast = fast.next?.next if (slow === fast) return true } return false}
fun main() { // Vi du 1: tao chu trinh thu cong val n1 = ListNode(3); val n2 = ListNode(2); val n3 = ListNode(0); val n4 = ListNode(-4) n1.next = n2; n2.next = n3; n3.next = n4; n4.next = n2 // n4 tro ve n2 println(hasCycle(n1)) // true
// Vi du 2: khong chu trinh val m1 = ListNode(1); val m2 = ListNode(2) m1.next = m2 println(hasCycle(m1)) // false}class ListNode { int val; ListNode? next; ListNode(this.val);}
bool hasCycle(ListNode? head) { ListNode? slow = head; ListNode? fast = head; while (fast?.next != null) { slow = slow!.next; fast = fast!.next!.next; if (identical(slow, fast)) return true; } return false;}
void main() { // Vi du 1: tao chu trinh thu cong final n1 = ListNode(3), n2 = ListNode(2), n3 = ListNode(0), n4 = ListNode(-4); n1.next = n2; n2.next = n3; n3.next = n4; n4.next = n2; // n4 tro ve n2 print(hasCycle(n1)); // true
// Vi du 2: khong chu trinh final m1 = ListNode(1), m2 = ListNode(2); m1.next = m2; print(hasCycle(m1)); // false}87. Tìm phần tử giữa danh sách liên kết (Middle of the Linked List)
Độ khó: Dễ · Chủ đề: Linked List
Cho một danh sách liên kết đơn, trả về node ở giữa danh sách. Nếu có 2 node ở giữa (danh sách có số chẵn phần tử), trả về node giữa thứ hai. Dùng kỹ thuật hai con trỏ (nhanh - chậm), không đếm số phần tử trước.
Ví dụ 1:
Input: [1,2,3,4,5]Output: [3,4,5]Giải thích: Node giữa là 3, phần còn lại của danh sách kể từ đó là [3,4,5].Ví dụ 2:
Input: [1,2,3,4,5,6]Output: [4,5,6]Giải thích: Có 2 node giữa (3 và 4), trả về node giữa thứ hai là 4.Ràng buộc:
- Số node trong khoảng
[1, 100].
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def middle_node(head): slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next return slow
print(to_list(middle_node(build_list([1, 2, 3, 4, 5])))) # [3, 4, 5]print(to_list(middle_node(build_list([1, 2, 3, 4, 5, 6])))) # [4, 5, 6]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy(0); ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* middleNode(ListNode* head) { ListNode* slow = head; ListNode* fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow;}
int main() { vector<int> v1 = {1, 2, 3, 4, 5}; for (int x : toList(middleNode(buildList(v1)))) cout << x << " "; cout << endl; // 3 4 5
vector<int> v2 = {1, 2, 3, 4, 5, 6}; for (int x : toList(middleNode(buildList(v2)))) cout << x << " "; cout << endl; // 4 5 6 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode middleNode(ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; }
public static void main(String[] args) { System.out.println(toList(middleNode(buildList(new int[]{1, 2, 3, 4, 5})))); // [3, 4, 5] System.out.println(toList(middleNode(buildList(new int[]{1, 2, 3, 4, 5, 6})))); // [4, 5, 6] }}class ListNode(var value: Int) { var next: ListNode? = null}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.value) node = node.next } return result}
fun middleNode(head: ListNode?): ListNode? { var slow = head var fast = head while (fast?.next != null) { slow = slow?.next fast = fast.next?.next } return slow}
fun main() { println(toList(middleNode(buildList(listOf(1, 2, 3, 4, 5))))) // [3, 4, 5] println(toList(middleNode(buildList(listOf(1, 2, 3, 4, 5, 6))))) // [4, 5, 6]}class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? middleNode(ListNode? head) { ListNode? slow = head; ListNode? fast = head; while (fast?.next != null) { slow = slow!.next; fast = fast!.next!.next; } return slow;}
void main() { print(toList(middleNode(buildList([1, 2, 3, 4, 5])))); // [3, 4, 5] print(toList(middleNode(buildList([1, 2, 3, 4, 5, 6])))); // [4, 5, 6]}88. Đảo ngược danh sách liên kết (Reverse Linked List)
Độ khó: Dễ · Chủ đề: Linked List
Cho phần đầu (head) của một danh sách liên kết đơn, đảo ngược danh sách và trả về phần đầu mới. Giải cả hai cách: lặp (iterative) và đệ quy.
Ví dụ 1:
Input: [1,2,3,4,5]Output: [5,4,3,2,1]Ví dụ 2:
Input: [1,2]Output: [2,1]Ràng buộc:
- Số node trong khoảng
[0, 5000].
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def reverse_list(head): prev = None current = head while current: next_node = current.next current.next = prev prev = current current = next_node return prev
print(to_list(reverse_list(build_list([1, 2, 3, 4, 5])))) # [5, 4, 3, 2, 1]print(to_list(reverse_list(build_list([1, 2])))) # [2, 1]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy(0); ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* reverseList(ListNode* head) { ListNode* prev = nullptr; ListNode* current = head; while (current) { ListNode* nextNode = current->next; current->next = prev; prev = current; current = nextNode; } return prev;}
int main() { vector<int> v1 = {1, 2, 3, 4, 5}; for (int x : toList(reverseList(buildList(v1)))) cout << x << " "; cout << endl; // 5 4 3 2 1
vector<int> v2 = {1, 2}; for (int x : toList(reverseList(buildList(v2)))) cout << x << " "; cout << endl; // 2 1 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode reverseList(ListNode head) { ListNode prev = null; ListNode current = head; while (current != null) { ListNode nextNode = current.next; current.next = prev; prev = current; current = nextNode; } return prev; }
public static void main(String[] args) { System.out.println(toList(reverseList(buildList(new int[]{1, 2, 3, 4, 5})))); // [5, 4, 3, 2, 1] System.out.println(toList(reverseList(buildList(new int[]{1, 2})))); // [2, 1] }}class ListNode(var value: Int) { var next: ListNode? = null}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.value) node = node.next } return result}
fun reverseList(head: ListNode?): ListNode? { var prev: ListNode? = null var current = head while (current != null) { val nextNode = current.next current.next = prev prev = current current = nextNode } return prev}
fun main() { println(toList(reverseList(buildList(listOf(1, 2, 3, 4, 5))))) // [5, 4, 3, 2, 1] println(toList(reverseList(buildList(listOf(1, 2))))) // [2, 1]}class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? reverseList(ListNode? head) { ListNode? prev; ListNode? current = head; while (current != null) { final nextNode = current.next; current.next = prev; prev = current; current = nextNode; } return prev;}
void main() { print(toList(reverseList(buildList([1, 2, 3, 4, 5])))); // [5, 4, 3, 2, 1] print(toList(reverseList(buildList([1, 2])))); // [2, 1]}89. Gộp hai danh sách liên kết đã sắp xếp (Merge Two Sorted Lists)
Độ khó: Dễ · Chủ đề: Linked List
Cho hai danh sách liên kết đơn list1 và list2 đã sắp xếp tăng dần. Gộp chúng thành một danh sách liên kết mới cũng sắp xếp tăng dần, bằng cách nối lại các node có sẵn (không tạo node mới).
Ví dụ 1:
Input: list1 = [1,2,4], list2 = [1,3,4]Output: [1,1,2,3,4,4]Ví dụ 2:
Input: list1 = [], list2 = []Output: []Ràng buộc:
- Tổng số node trong khoảng
[0, 50]. -100 <= giá trị node <= 100
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def merge_two_lists(list1, list2): dummy = ListNode() tail = dummy
while list1 and list2: if list1.val <= list2.val: tail.next = list1 list1 = list1.next else: tail.next = list2 list2 = list2.next tail = tail.next
tail.next = list1 if list1 else list2 return dummy.next
print(to_list(merge_two_lists(build_list([1, 2, 4]), build_list([1, 3, 4])))) # [1, 1, 2, 3, 4, 4]print(to_list(merge_two_lists(build_list([]), build_list([])))) # []#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy(0); ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { ListNode dummy(0); ListNode* tail = &dummy;
while (list1 && list2) { if (list1->val <= list2->val) { tail->next = list1; list1 = list1->next; } else { tail->next = list2; list2 = list2->next; } tail = tail->next; }
tail->next = list1 ? list1 : list2; return dummy.next;}
int main() { vector<int> a = {1, 2, 4}, b = {1, 3, 4}; for (int x : toList(mergeTwoLists(buildList(a), buildList(b)))) cout << x << " "; cout << endl; // 1 1 2 3 4 4
vector<int> c = {}, d = {}; for (int x : toList(mergeTwoLists(buildList(c), buildList(d)))) cout << x << " "; cout << endl; // (empty) return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode dummy = new ListNode(0); ListNode tail = dummy;
while (list1 != null && list2 != null) { if (list1.val <= list2.val) { tail.next = list1; list1 = list1.next; } else { tail.next = list2; list2 = list2.next; } tail = tail.next; }
tail.next = (list1 != null) ? list1 : list2; return dummy.next; }
public static void main(String[] args) { System.out.println(toList(mergeTwoLists(buildList(new int[]{1, 2, 4}), buildList(new int[]{1, 3, 4})))); // [1, 1, 2, 3, 4, 4] System.out.println(toList(mergeTwoLists(buildList(new int[]{}), buildList(new int[]{})))); // [] }}class ListNode(var value: Int) { var next: ListNode? = null}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.value) node = node.next } return result}
fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? { val dummy = ListNode(0) var tail = dummy var l1 = list1 var l2 = list2
while (l1 != null && l2 != null) { if (l1.value <= l2.value) { tail.next = l1 l1 = l1.next } else { tail.next = l2 l2 = l2.next } tail = tail.next!! }
tail.next = l1 ?: l2 return dummy.next}
fun main() { println(toList(mergeTwoLists(buildList(listOf(1, 2, 4)), buildList(listOf(1, 3, 4))))) // [1, 1, 2, 3, 4, 4] println(toList(mergeTwoLists(buildList(listOf()), buildList(listOf())))) // []}class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? mergeTwoLists(ListNode? list1, ListNode? list2) { final dummy = ListNode(0); var tail = dummy; var l1 = list1, l2 = list2;
while (l1 != null && l2 != null) { if (l1.val <= l2.val) { tail.next = l1; l1 = l1.next; } else { tail.next = l2; l2 = l2.next; } tail = tail.next!; }
tail.next = l1 ?? l2; return dummy.next;}
void main() { print(toList(mergeTwoLists(buildList([1, 2, 4]), buildList([1, 3, 4])))); // [1, 1, 2, 3, 4, 4] print(toList(mergeTwoLists(buildList([]), buildList([])))); // []}90. Danh sách liên kết đối xứng (Palindrome Linked List)
Độ khó: Dễ · Chủ đề: Linked List
Cho phần đầu của một danh sách liên kết đơn, kiểm tra danh sách đó có phải là “palindrome” (đối xứng, đọc xuôi và đọc ngược giống nhau) hay không.
Ví dụ 1:
Input: [1,2,2,1]Output: TrueVí dụ 2:
Input: [1,2,3]Output: FalseRàng buộc:
- Số node trong khoảng
[1, 10^5]. - Cố gắng đạt độ phức tạp thời gian O(n) và bộ nhớ O(1).
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def is_palindrome(head): # Tìm node giữa slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next
# Đảo ngược nửa sau prev = None current = slow while current: next_node = current.next current.next = prev prev = current current = next_node
# So sánh nửa đầu với nửa sau đã đảo ngược left, right = head, prev while right: if left.val != right.val: return False left = left.next right = right.next
return True
print(is_palindrome(build_list([1, 2, 2, 1]))) # Trueprint(is_palindrome(build_list([1, 2, 3]))) # False#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy(0); ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
bool isPalindrome(ListNode* head) { ListNode* slow = head; ListNode* fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; }
ListNode* prev = nullptr; ListNode* current = slow; while (current) { ListNode* nextNode = current->next; current->next = prev; prev = current; current = nextNode; }
ListNode* left = head; ListNode* right = prev; while (right) { if (left->val != right->val) return false; left = left->next; right = right->next; }
return true;}
int main() { vector<int> v1 = {1, 2, 2, 1}; cout << boolalpha << isPalindrome(buildList(v1)) << endl; // true
vector<int> v2 = {1, 2, 3}; cout << boolalpha << isPalindrome(buildList(v2)) << endl; // false return 0;}public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static boolean isPalindrome(ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; }
ListNode prev = null, current = slow; while (current != null) { ListNode nextNode = current.next; current.next = prev; prev = current; current = nextNode; }
ListNode left = head, right = prev; while (right != null) { if (left.val != right.val) return false; left = left.next; right = right.next; }
return true; }
public static void main(String[] args) { System.out.println(isPalindrome(buildList(new int[]{1, 2, 2, 1}))); // true System.out.println(isPalindrome(buildList(new int[]{1, 2, 3}))); // false }}class ListNode(var value: Int) { var next: ListNode? = null}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun isPalindrome(head: ListNode?): Boolean { var slow = head var fast = head while (fast?.next != null) { slow = slow?.next fast = fast.next?.next }
var prev: ListNode? = null var current = slow while (current != null) { val nextNode = current.next current.next = prev prev = current current = nextNode }
var left = head var right = prev while (right != null) { if (left!!.value != right.value) return false left = left.next right = right.next }
return true}
fun main() { println(isPalindrome(buildList(listOf(1, 2, 2, 1)))) // true println(isPalindrome(buildList(listOf(1, 2, 3)))) // false}class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
bool isPalindrome(ListNode? head) { ListNode? slow = head; ListNode? fast = head; while (fast?.next != null) { slow = slow!.next; fast = fast!.next!.next; }
ListNode? prev; ListNode? current = slow; while (current != null) { final nextNode = current.next; current.next = prev; prev = current; current = nextNode; }
ListNode? left = head; ListNode? right = prev; while (right != null) { if (left!.val != right.val) return false; left = left.next; right = right.next; }
return true;}
void main() { print(isPalindrome(buildList([1, 2, 2, 1]))); // true print(isPalindrome(buildList([1, 2, 3]))); // false}91. Tính biểu thức hậu tố (Evaluate Reverse Polish Notation)
Độ khó: Trung bình · Chủ đề: Stack
Cho một mảng tokens biểu diễn biểu thức số học viết dưới dạng hậu tố (Reverse Polish Notation). Các toán tử hợp lệ là +, -, *, / (chia lấy phần nguyên hướng về 0). Tính giá trị của biểu thức.
Ví dụ 1:
Input: tokens = ["2","1","+","3","*"]Output: 9Giải thích: (2 + 1) * 3 = 9Ví dụ 2:
Input: tokens = ["4","13","5","/","+"]Output: 6Giải thích: 4 + (13 / 5) = 4 + 2 = 6Ràng buộc:
1 <= len(tokens) <= 10^4- Biểu thức đầu vào luôn hợp lệ.
Xem đáp án
def eval_rpn(tokens): stack = [] operators = {"+", "-", "*", "/"}
for token in tokens: if token in operators: b = stack.pop() a = stack.pop() if token == "+": stack.append(a + b) elif token == "-": stack.append(a - b) elif token == "*": stack.append(a * b) else: stack.append(int(a / b)) # chia lấy phần nguyên hướng về 0 else: stack.append(int(token))
return stack[-1]
print(eval_rpn(["2", "1", "+", "3", "*"])) # 9print(eval_rpn(["4", "13", "5", "/", "+"])) # 6#include <iostream>#include <vector>#include <string>#include <set>using namespace std;
int evalRPN(vector<string>& tokens) { vector<int> stack; set<string> operators = {"+", "-", "*", "/"};
for (auto& token : tokens) { if (operators.count(token)) { int b = stack.back(); stack.pop_back(); int a = stack.back(); stack.pop_back(); if (token == "+") stack.push_back(a + b); else if (token == "-") stack.push_back(a - b); else if (token == "*") stack.push_back(a * b); else stack.push_back(a / b); // chia lay phan nguyen huong ve 0 (C++ int div) } else { stack.push_back(stoi(token)); } }
return stack.back();}
int main() { vector<string> t1 = {"2", "1", "+", "3", "*"}; cout << evalRPN(t1) << endl; // 9
vector<string> t2 = {"4", "13", "5", "/", "+"}; cout << evalRPN(t2) << endl; // 6 return 0;}import java.util.*;
public class Main { static int evalRPN(String[] tokens) { Deque<Integer> stack = new ArrayDeque<>(); Set<String> operators = Set.of("+", "-", "*", "/");
for (String token : tokens) { if (operators.contains(token)) { int b = stack.pop(); int a = stack.pop(); switch (token) { case "+" -> stack.push(a + b); case "-" -> stack.push(a - b); case "*" -> stack.push(a * b); default -> stack.push(a / b); // chia lay phan nguyen huong ve 0 } } else { stack.push(Integer.parseInt(token)); } }
return stack.peek(); }
public static void main(String[] args) { System.out.println(evalRPN(new String[]{"2", "1", "+", "3", "*"})); // 9 System.out.println(evalRPN(new String[]{"4", "13", "5", "/", "+"})); // 6 }}fun evalRPN(tokens: Array<String>): Int { val stack = ArrayDeque<Int>() val operators = setOf("+", "-", "*", "/")
for (token in tokens) { if (token in operators) { val b = stack.removeLast() val a = stack.removeLast() when (token) { "+" -> stack.addLast(a + b) "-" -> stack.addLast(a - b) "*" -> stack.addLast(a * b) else -> stack.addLast(a / b) // chia lay phan nguyen huong ve 0 } } else { stack.addLast(token.toInt()) } }
return stack.last()}
fun main() { println(evalRPN(arrayOf("2", "1", "+", "3", "*"))) // 9 println(evalRPN(arrayOf("4", "13", "5", "/", "+"))) // 6}int evalRPN(List<String> tokens) { final stack = <int>[]; final operators = {"+", "-", "*", "/"};
for (var token in tokens) { if (operators.contains(token)) { final b = stack.removeLast(); final a = stack.removeLast(); if (token == "+") { stack.add(a + b); } else if (token == "-") { stack.add(a - b); } else if (token == "*") { stack.add(a * b); } else { stack.add((a / b).truncate()); // chia lay phan nguyen huong ve 0 } } else { stack.add(int.parse(token)); } }
return stack.last;}
void main() { print(evalRPN(["2", "1", "+", "3", "*"])); // 9 print(evalRPN(["4", "13", "5", "/", "+"])); // 6}92. Nhiệt độ hàng ngày (Daily Temperatures)
Độ khó: Trung bình · Chủ đề: Stack
Cho mảng temperatures là nhiệt độ mỗi ngày. Với mỗi ngày, tìm xem phải chờ bao nhiêu ngày nữa mới có một ngày ấm hơn. Nếu không có ngày nào ấm hơn trong tương lai, đáp án cho ngày đó là 0. Yêu cầu độ phức tạp O(n) bằng cách dùng stack đơn điệu (monotonic stack).
Ví dụ 1:
Input: temperatures = [73,74,75,71,69,72,76,73]Output: [1,1,4,2,1,1,0,0]Ví dụ 2:
Input: temperatures = [30,40,50,60]Output: [1,1,1,0]Ràng buộc:
1 <= len(temperatures) <= 10^530 <= temperatures[i] <= 100
Xem đáp án
def daily_temperatures(temperatures): n = len(temperatures) result = [0] * n stack = [] # lưu index của các ngày chưa tìm được ngày ấm hơn
for i, temp in enumerate(temperatures): while stack and temperatures[stack[-1]] < temp: prev_index = stack.pop() result[prev_index] = i - prev_index stack.append(i)
return result
print(daily_temperatures([73, 74, 75, 71, 69, 72, 76, 73])) # [1,1,4,2,1,1,0,0]print(daily_temperatures([30, 40, 50, 60])) # [1,1,1,0]#include <iostream>#include <vector>using namespace std;
vector<int> dailyTemperatures(vector<int>& temperatures) { int n = temperatures.size(); vector<int> result(n, 0); vector<int> stack; // luu index cac ngay chua tim duoc ngay am hon
for (int i = 0; i < n; i++) { while (!stack.empty() && temperatures[stack.back()] < temperatures[i]) { int prevIndex = stack.back(); stack.pop_back(); result[prevIndex] = i - prevIndex; } stack.push_back(i); }
return result;}
int main() { vector<int> t1 = {73, 74, 75, 71, 69, 72, 76, 73}; for (int x : dailyTemperatures(t1)) cout << x << " "; cout << endl; // 1 1 4 2 1 1 0 0
vector<int> t2 = {30, 40, 50, 60}; for (int x : dailyTemperatures(t2)) cout << x << " "; cout << endl; // 1 1 1 0 return 0;}import java.util.*;
public class Main { static int[] dailyTemperatures(int[] temperatures) { int n = temperatures.length; int[] result = new int[n]; Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) { while (!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) { int prevIndex = stack.pop(); result[prevIndex] = i - prevIndex; } stack.push(i); }
return result; }
public static void main(String[] args) { System.out.println(Arrays.toString(dailyTemperatures(new int[]{73, 74, 75, 71, 69, 72, 76, 73}))); System.out.println(Arrays.toString(dailyTemperatures(new int[]{30, 40, 50, 60}))); }}fun dailyTemperatures(temperatures: IntArray): IntArray { val n = temperatures.size val result = IntArray(n) val stack = ArrayDeque<Int>()
for (i in 0 until n) { while (stack.isNotEmpty() && temperatures[stack.last()] < temperatures[i]) { val prevIndex = stack.removeLast() result[prevIndex] = i - prevIndex } stack.addLast(i) }
return result}
fun main() { println(dailyTemperatures(intArrayOf(73, 74, 75, 71, 69, 72, 76, 73)).joinToString()) println(dailyTemperatures(intArrayOf(30, 40, 50, 60)).joinToString())}List<int> dailyTemperatures(List<int> temperatures) { int n = temperatures.length; final result = List.filled(n, 0); final stack = <int>[];
for (int i = 0; i < n; i++) { while (stack.isNotEmpty && temperatures[stack.last] < temperatures[i]) { final prevIndex = stack.removeLast(); result[prevIndex] = i - prevIndex; } stack.add(i); }
return result;}
void main() { print(dailyTemperatures([73, 74, 75, 71, 69, 72, 76, 73])); // [1, 1, 4, 2, 1, 1, 0, 0] print(dailyTemperatures([30, 40, 50, 60])); // [1, 1, 1, 0]}93. Phần tử lớn hơn kế tiếp II (Next Greater Element II)
Độ khó: Trung bình · Chủ đề: Stack
Cho một mảng số nguyên nums dạng vòng tròn (circular - phần tử cuối liền kề phần tử đầu). Với mỗi phần tử, tìm phần tử lớn hơn kế tiếp gần nhất theo chiều duyệt vòng tròn. Nếu không tồn tại, trả về -1.
Ví dụ 1:
Input: nums = [1,2,1]Output: [2,-1,2]Giải thích: Phần tử lớn hơn kế tiếp của 1 (index 0) là 2. Của 2 (index 1) không có. Của 1 (index 2) là 2 (đi vòng lại đầu mảng).Ví dụ 2:
Input: nums = [1,2,3,4,3]Output: [2,3,4,-1,4]Ràng buộc:
1 <= len(nums) <= 10^4-10^9 <= nums[i] <= 10^9
Xem đáp án
def next_greater_elements(nums): n = len(nums) result = [-1] * n stack = [] # lưu index
for i in range(2 * n): idx = i % n while stack and nums[stack[-1]] < nums[idx]: result[stack.pop()] = nums[idx] if i < n: stack.append(idx)
return result
print(next_greater_elements([1, 2, 1])) # [2, -1, 2]print(next_greater_elements([1, 2, 3, 4, 3])) # [2, 3, 4, -1, 4]#include <iostream>#include <vector>using namespace std;
vector<int> nextGreaterElements(vector<int>& nums) { int n = nums.size(); vector<int> result(n, -1); vector<int> stack; // luu index
for (int i = 0; i < 2 * n; i++) { int idx = i % n; while (!stack.empty() && nums[stack.back()] < nums[idx]) { result[stack.back()] = nums[idx]; stack.pop_back(); } if (i < n) stack.push_back(idx); }
return result;}
int main() { vector<int> nums1 = {1, 2, 1}; for (int x : nextGreaterElements(nums1)) cout << x << " "; cout << endl; // 2 -1 2
vector<int> nums2 = {1, 2, 3, 4, 3}; for (int x : nextGreaterElements(nums2)) cout << x << " "; cout << endl; // 2 3 4 -1 4 return 0;}import java.util.*;
public class Main { static int[] nextGreaterElements(int[] nums) { int n = nums.length; int[] result = new int[n]; Arrays.fill(result, -1); Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < 2 * n; i++) { int idx = i % n; while (!stack.isEmpty() && nums[stack.peek()] < nums[idx]) { result[stack.pop()] = nums[idx]; } if (i < n) stack.push(idx); }
return result; }
public static void main(String[] args) { System.out.println(Arrays.toString(nextGreaterElements(new int[]{1, 2, 1}))); System.out.println(Arrays.toString(nextGreaterElements(new int[]{1, 2, 3, 4, 3}))); }}fun nextGreaterElements(nums: IntArray): IntArray { val n = nums.size val result = IntArray(n) { -1 } val stack = ArrayDeque<Int>()
for (i in 0 until 2 * n) { val idx = i % n while (stack.isNotEmpty() && nums[stack.last()] < nums[idx]) { result[stack.removeLast()] = nums[idx] } if (i < n) stack.addLast(idx) }
return result}
fun main() { println(nextGreaterElements(intArrayOf(1, 2, 1)).joinToString()) println(nextGreaterElements(intArrayOf(1, 2, 3, 4, 3)).joinToString())}List<int> nextGreaterElements(List<int> nums) { int n = nums.length; final result = List.filled(n, -1); final stack = <int>[];
for (int i = 0; i < 2 * n; i++) { final idx = i % n; while (stack.isNotEmpty && nums[stack.last] < nums[idx]) { result[stack.removeLast()] = nums[idx]; } if (i < n) stack.add(idx); }
return result;}
void main() { print(nextGreaterElements([1, 2, 1])); // [2, -1, 2] print(nextGreaterElements([1, 2, 3, 4, 3])); // [2, 3, 4, -1, 4]}94. Xóa node thứ n từ cuối danh sách (Remove Nth Node From End of List)
Độ khó: Trung bình · Chủ đề: Linked List
Cho phần đầu của một danh sách liên kết đơn, xóa node thứ n tính từ cuối danh sách, rồi trả về phần đầu danh sách. Yêu cầu chỉ duyệt danh sách một lần (dùng kỹ thuật hai con trỏ cách nhau n bước).
Ví dụ 1:
Input: head = [1,2,3,4,5], n = 2Output: [1,2,3,5]Giải thích: Node thứ 2 từ cuối (giá trị 4) bị xóa.Ví dụ 2:
Input: head = [1], n = 1Output: []Ràng buộc:
- Số node trong khoảng
[1, 30]. 1 <= n <= số node
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def remove_nth_from_end(head, n): dummy = ListNode(0, head) fast = slow = dummy
for _ in range(n): fast = fast.next
while fast.next: fast = fast.next slow = slow.next
slow.next = slow.next.next return dummy.next
print(to_list(remove_nth_from_end(build_list([1, 2, 3, 4, 5]), 2))) # [1, 2, 3, 5]print(to_list(remove_nth_from_end(build_list([1]), 1))) # []#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy(0); ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode dummy(0); dummy.next = head; ListNode* fast = &dummy; ListNode* slow = &dummy;
for (int i = 0; i < n; i++) fast = fast->next;
while (fast->next) { fast = fast->next; slow = slow->next; }
slow->next = slow->next->next; return dummy.next;}
int main() { vector<int> v1 = {1, 2, 3, 4, 5}; for (int x : toList(removeNthFromEnd(buildList(v1), 2))) cout << x << " "; cout << endl; // 1 2 3 5
vector<int> v2 = {1}; for (int x : toList(removeNthFromEnd(buildList(v2), 1))) cout << x << " "; cout << endl; // (empty) return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode removeNthFromEnd(ListNode head, int n) { ListNode dummy = new ListNode(0); dummy.next = head; ListNode fast = dummy, slow = dummy;
for (int i = 0; i < n; i++) fast = fast.next;
while (fast.next != null) { fast = fast.next; slow = slow.next; }
slow.next = slow.next.next; return dummy.next; }
public static void main(String[] args) { System.out.println(toList(removeNthFromEnd(buildList(new int[]{1, 2, 3, 4, 5}), 2))); // [1, 2, 3, 5] System.out.println(toList(removeNthFromEnd(buildList(new int[]{1}), 1))); // [] }}class ListNode(var value: Int) { var next: ListNode? = null}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.value) node = node.next } return result}
fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { val dummy = ListNode(0) dummy.next = head var fast: ListNode? = dummy var slow: ListNode? = dummy
repeat(n) { fast = fast?.next }
while (fast?.next != null) { fast = fast?.next slow = slow?.next }
slow?.next = slow?.next?.next return dummy.next}
fun main() { println(toList(removeNthFromEnd(buildList(listOf(1, 2, 3, 4, 5)), 2))) // [1, 2, 3, 5] println(toList(removeNthFromEnd(buildList(listOf(1)), 1))) // []}class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? removeNthFromEnd(ListNode? head, int n) { final dummy = ListNode(0); dummy.next = head; ListNode? fast = dummy; ListNode? slow = dummy;
for (int i = 0; i < n; i++) fast = fast!.next;
while (fast!.next != null) { fast = fast.next; slow = slow!.next; }
slow!.next = slow.next!.next; return dummy.next;}
void main() { print(toList(removeNthFromEnd(buildList([1, 2, 3, 4, 5]), 2))); // [1, 2, 3, 5] print(toList(removeNthFromEnd(buildList([1]), 1))); // []}95. Cộng hai số biểu diễn bằng danh sách liên kết (Add Two Numbers)
Độ khó: Trung bình · Chủ đề: Linked List
Cho hai danh sách liên kết đơn không rỗng biểu diễn hai số nguyên không âm, mỗi node chứa một chữ số, các chữ số được lưu theo thứ tự ngược (chữ số hàng đơn vị ở đầu danh sách). Cộng hai số lại và trả về kết quả cũng dưới dạng danh sách liên kết theo thứ tự ngược.
Ví dụ 1:
Input: l1 = [2,4,3], l2 = [5,6,4]Output: [7,0,8]Giải thích: 342 + 465 = 807, biểu diễn ngược là [7,0,8].Ví dụ 2:
Input: l1 = [9,9], l2 = [1]Output: [0,0,1]Giải thích: 99 + 1 = 100.Ràng buộc:
- Số node mỗi danh sách trong khoảng
[1, 100]. 0 <= giá trị node <= 9
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def add_two_numbers(l1, l2): dummy = ListNode() current = dummy carry = 0
while l1 or l2 or carry: x = l1.val if l1 else 0 y = l2.val if l2 else 0 total = x + y + carry carry = total // 10 current.next = ListNode(total % 10) current = current.next l1 = l1.next if l1 else None l2 = l2.next if l2 else None
return dummy.next
print(to_list(add_two_numbers(build_list([2, 4, 3]), build_list([5, 6, 4])))) # [7, 0, 8]print(to_list(add_two_numbers(build_list([9, 9]), build_list([1])))) # [0, 0, 1]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v = 0, ListNode* n = nullptr) : val(v), next(n) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy; ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode dummy; ListNode* current = &dummy; int carry = 0;
while (l1 || l2 || carry) { int x = l1 ? l1->val : 0; int y = l2 ? l2->val : 0; int total = x + y + carry; carry = total / 10; current->next = new ListNode(total % 10); current = current->next; if (l1) l1 = l1->next; if (l2) l2 = l2->next; }
return dummy.next;}
int main() { vector<int> a = {2, 4, 3}, b = {5, 6, 4}; for (int x : toList(addTwoNumbers(buildList(a), buildList(b)))) cout << x << " "; cout << endl; // 7 0 8
vector<int> c = {9, 9}, d = {1}; for (int x : toList(addTwoNumbers(buildList(c), buildList(d)))) cout << x << " "; cout << endl; // 0 0 1 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(0); ListNode current = dummy; int carry = 0;
while (l1 != null || l2 != null || carry != 0) { int x = l1 != null ? l1.val : 0; int y = l2 != null ? l2.val : 0; int total = x + y + carry; carry = total / 10; current.next = new ListNode(total % 10); current = current.next; if (l1 != null) l1 = l1.next; if (l2 != null) l2 = l2.next; }
return dummy.next; }
public static void main(String[] args) { System.out.println(toList(addTwoNumbers(buildList(new int[]{2, 4, 3}), buildList(new int[]{5, 6, 4})))); // [7, 0, 8] System.out.println(toList(addTwoNumbers(buildList(new int[]{9, 9}), buildList(new int[]{1})))); // [0, 0, 1] }}class ListNode(var `val`: Int, var next: ListNode? = null)
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.`val`) node = node.next } return result}
fun addTwoNumbers(l1: ListNode?, l2: ListNode?): ListNode? { val dummy = ListNode(0) var current = dummy var carry = 0 var a = l1 var b = l2
while (a != null || b != null || carry != 0) { val x = a?.`val` ?: 0 val y = b?.`val` ?: 0 val total = x + y + carry carry = total / 10 current.next = ListNode(total % 10) current = current.next!! a = a?.next b = b?.next }
return dummy.next}
fun main() { println(toList(addTwoNumbers(buildList(listOf(2, 4, 3)), buildList(listOf(5, 6, 4))))) // [7, 0, 8] println(toList(addTwoNumbers(buildList(listOf(9, 9)), buildList(listOf(1))))) // [0, 0, 1]}class ListNode { int val; ListNode? next; ListNode(this.val, [this.next]);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? addTwoNumbers(ListNode? l1, ListNode? l2) { final dummy = ListNode(0); var current = dummy; var carry = 0; var a = l1, b = l2;
while (a != null || b != null || carry != 0) { final x = a?.val ?? 0; final y = b?.val ?? 0; final total = x + y + carry; carry = total ~/ 10; current.next = ListNode(total % 10); current = current.next!; a = a?.next; b = b?.next; }
return dummy.next;}
void main() { print(toList(addTwoNumbers(buildList([2, 4, 3]), buildList([5, 6, 4])))); // [7, 0, 8] print(toList(addTwoNumbers(buildList([9, 9]), buildList([1])))); // [0, 0, 1]}96. Hoán đổi từng cặp node (Swap Nodes in Pairs)
Độ khó: Trung bình · Chủ đề: Linked List
Cho phần đầu của một danh sách liên kết đơn, hoán đổi từng cặp node liền kề và trả về phần đầu mới. Chỉ được thay đổi các liên kết giữa các node (không được đổi giá trị val của node).
Ví dụ 1:
Input: [1,2,3,4]Output: [2,1,4,3]Ví dụ 2:
Input: [1,2,3]Output: [2,1,3]Giải thích: Node cuối lẻ ra (giá trị 3) giữ nguyên vị trí.Ràng buộc:
- Số node trong khoảng
[0, 100].
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def swap_pairs(head): dummy = ListNode(0, head) prev = dummy
while prev.next and prev.next.next: first = prev.next second = first.next
first.next = second.next second.next = first prev.next = second
prev = first
return dummy.next
print(to_list(swap_pairs(build_list([1, 2, 3, 4])))) # [2, 1, 4, 3]print(to_list(swap_pairs(build_list([1, 2, 3])))) # [2, 1, 3]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v = 0, ListNode* n = nullptr) : val(v), next(n) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy; ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* swapPairs(ListNode* head) { ListNode dummy(0, head); ListNode* prev = &dummy;
while (prev->next && prev->next->next) { ListNode* first = prev->next; ListNode* second = first->next;
first->next = second->next; second->next = first; prev->next = second;
prev = first; }
return dummy.next;}
int main() { vector<int> a = {1, 2, 3, 4}; for (int x : toList(swapPairs(buildList(a)))) cout << x << " "; cout << endl; // 2 1 4 3
vector<int> b = {1, 2, 3}; for (int x : toList(swapPairs(buildList(b)))) cout << x << " "; cout << endl; // 2 1 3 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } ListNode(int val, ListNode next) { this.val = val; this.next = next; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode swapPairs(ListNode head) { ListNode dummy = new ListNode(0, head); ListNode prev = dummy;
while (prev.next != null && prev.next.next != null) { ListNode first = prev.next; ListNode second = first.next;
first.next = second.next; second.next = first; prev.next = second;
prev = first; }
return dummy.next; }
public static void main(String[] args) { System.out.println(toList(swapPairs(buildList(new int[]{1, 2, 3, 4})))); // [2, 1, 4, 3] System.out.println(toList(swapPairs(buildList(new int[]{1, 2, 3})))); // [2, 1, 3] }}class ListNode(var `val`: Int, var next: ListNode? = null)
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.`val`) node = node.next } return result}
fun swapPairs(head: ListNode?): ListNode? { val dummy = ListNode(0, head) var prev = dummy
while (prev.next != null && prev.next!!.next != null) { val first = prev.next!! val second = first.next!!
first.next = second.next second.next = first prev.next = second
prev = first }
return dummy.next}
fun main() { println(toList(swapPairs(buildList(listOf(1, 2, 3, 4))))) // [2, 1, 4, 3] println(toList(swapPairs(buildList(listOf(1, 2, 3))))) // [2, 1, 3]}class ListNode { int val; ListNode? next; ListNode(this.val, [this.next]);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? swapPairs(ListNode? head) { final dummy = ListNode(0, head); var prev = dummy;
while (prev.next != null && prev.next!.next != null) { final first = prev.next!; final second = first.next!;
first.next = second.next; second.next = first; prev.next = second;
prev = first; }
return dummy.next;}
void main() { print(toList(swapPairs(buildList([1, 2, 3, 4])))); // [2, 1, 4, 3] print(toList(swapPairs(buildList([1, 2, 3])))); // [2, 1, 3]}97. Sắp xếp lại danh sách liên kết chẵn lẻ (Odd Even Linked List)
Độ khó: Trung bình · Chủ đề: Linked List
Cho phần đầu của một danh sách liên kết đơn, nhóm tất cả các node ở vị trí lẻ (1, 3, 5, … tính theo chỉ số bắt đầu từ 1) lại với nhau, tiếp theo là tất cả node ở vị trí chẵn, rồi trả về danh sách kết quả. Chỉ được dùng O(1) bộ nhớ phụ (không tạo node mới).
Ví dụ 1:
Input: [1,2,3,4,5]Output: [1,3,5,2,4]Ví dụ 2:
Input: [2,1,3,5,6,4,7]Output: [2,3,6,7,1,5,4]Ràng buộc:
- Số node trong khoảng
[0, 10^4].
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def odd_even_list(head): if not head or not head.next: return head
odd = head even = head.next even_head = even
while even and even.next: odd.next = even.next odd = odd.next even.next = odd.next even = even.next
odd.next = even_head return head
print(to_list(odd_even_list(build_list([1, 2, 3, 4, 5])))) # [1, 3, 5, 2, 4]print(to_list(odd_even_list(build_list([2, 1, 3, 5, 6, 4, 7])))) # [2, 3, 6, 7, 1, 5, 4]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v = 0, ListNode* n = nullptr) : val(v), next(n) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy; ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* oddEvenList(ListNode* head) { if (!head || !head->next) return head;
ListNode* odd = head; ListNode* even = head->next; ListNode* evenHead = even;
while (even && even->next) { odd->next = even->next; odd = odd->next; even->next = odd->next; even = even->next; }
odd->next = evenHead; return head;}
int main() { vector<int> a = {1, 2, 3, 4, 5}; for (int x : toList(oddEvenList(buildList(a)))) cout << x << " "; cout << endl; // 1 3 5 2 4
vector<int> b = {2, 1, 3, 5, 6, 4, 7}; for (int x : toList(oddEvenList(buildList(b)))) cout << x << " "; cout << endl; // 2 3 6 7 1 5 4 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode oddEvenList(ListNode head) { if (head == null || head.next == null) return head;
ListNode odd = head; ListNode even = head.next; ListNode evenHead = even;
while (even != null && even.next != null) { odd.next = even.next; odd = odd.next; even.next = odd.next; even = even.next; }
odd.next = evenHead; return head; }
public static void main(String[] args) { System.out.println(toList(oddEvenList(buildList(new int[]{1, 2, 3, 4, 5})))); // [1, 3, 5, 2, 4] System.out.println(toList(oddEvenList(buildList(new int[]{2, 1, 3, 5, 6, 4, 7})))); // [2, 3, 6, 7, 1, 5, 4] }}class ListNode(var `val`: Int, var next: ListNode? = null)
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.`val`) node = node.next } return result}
fun oddEvenList(head: ListNode?): ListNode? { if (head == null || head.next == null) return head
var odd = head var even = head.next val evenHead = even
while (even != null && even.next != null) { odd!!.next = even.next odd = odd.next even.next = odd!!.next even = even.next }
odd!!.next = evenHead return head}
fun main() { println(toList(oddEvenList(buildList(listOf(1, 2, 3, 4, 5))))) // [1, 3, 5, 2, 4] println(toList(oddEvenList(buildList(listOf(2, 1, 3, 5, 6, 4, 7))))) // [2, 3, 6, 7, 1, 5, 4]}class ListNode { int val; ListNode? next; ListNode(this.val, [this.next]);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? oddEvenList(ListNode? head) { if (head == null || head.next == null) return head;
var odd = head; var even = head.next; final evenHead = even;
while (even != null && even.next != null) { odd.next = even.next; odd = odd.next!; even.next = odd.next; even = even.next; }
odd.next = evenHead; return head;}
void main() { print(toList(oddEvenList(buildList([1, 2, 3, 4, 5])))); // [1, 3, 5, 2, 4] print(toList(oddEvenList(buildList([2, 1, 3, 5, 6, 4, 7])))); // [2, 3, 6, 7, 1, 5, 4]}98. Sắp xếp lại danh sách liên kết theo thứ tự zigzag (Reorder List)
Độ khó: Trung bình · Chủ đề: Linked List
Cho danh sách liên kết L0 -> L1 -> ... -> Ln-1 -> Ln, sắp xếp lại thành L0 -> Ln -> L1 -> Ln-1 -> L2 -> Ln-2 -> ... (xen kẽ đầu - cuối). Chỉ được thay đổi liên kết giữa các node.
Ví dụ 1:
Input: [1,2,3,4]Output: [1,4,2,3]Ví dụ 2:
Input: [1,2,3,4,5]Output: [1,5,2,4,3]Ràng buộc:
- Số node trong khoảng
[1, 5 * 10^4].
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def reorder_list(head): if not head or not head.next: return head
# Bước 1: tìm node giữa slow, fast = head, head while fast.next and fast.next.next: slow = slow.next fast = fast.next.next
# Bước 2: đảo ngược nửa sau second = slow.next slow.next = None prev = None while second: next_node = second.next second.next = prev prev = second second = next_node
# Bước 3: xen kẽ hai nửa first, second = head, prev while second: first_next = first.next second_next = second.next
first.next = second second.next = first_next
first = first_next second = second_next
return head
print(to_list(reorder_list(build_list([1, 2, 3, 4])))) # [1, 4, 2, 3]print(to_list(reorder_list(build_list([1, 2, 3, 4, 5])))) # [1, 5, 2, 4, 3]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v = 0, ListNode* n = nullptr) : val(v), next(n) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy; ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* reorderList(ListNode* head) { if (!head || !head->next) return head;
// Buoc 1: tim node giua ListNode* slow = head; ListNode* fast = head; while (fast->next && fast->next->next) { slow = slow->next; fast = fast->next->next; }
// Buoc 2: dao nguoc nua sau ListNode* second = slow->next; slow->next = nullptr; ListNode* prev = nullptr; while (second) { ListNode* nextNode = second->next; second->next = prev; prev = second; second = nextNode; }
// Buoc 3: xen ke hai nua ListNode* first = head; second = prev; while (second) { ListNode* firstNext = first->next; ListNode* secondNext = second->next;
first->next = second; second->next = firstNext;
first = firstNext; second = secondNext; }
return head;}
int main() { vector<int> a = {1, 2, 3, 4}; for (int x : toList(reorderList(buildList(a)))) cout << x << " "; cout << endl; // 1 4 2 3
vector<int> b = {1, 2, 3, 4, 5}; for (int x : toList(reorderList(buildList(b)))) cout << x << " "; cout << endl; // 1 5 2 4 3 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode reorderList(ListNode head) { if (head == null || head.next == null) return head;
ListNode slow = head, fast = head; while (fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; }
ListNode second = slow.next; slow.next = null; ListNode prev = null; while (second != null) { ListNode nextNode = second.next; second.next = prev; prev = second; second = nextNode; }
ListNode first = head; second = prev; while (second != null) { ListNode firstNext = first.next; ListNode secondNext = second.next;
first.next = second; second.next = firstNext;
first = firstNext; second = secondNext; }
return head; }
public static void main(String[] args) { System.out.println(toList(reorderList(buildList(new int[]{1, 2, 3, 4})))); // [1, 4, 2, 3] System.out.println(toList(reorderList(buildList(new int[]{1, 2, 3, 4, 5})))); // [1, 5, 2, 4, 3] }}class ListNode(var `val`: Int, var next: ListNode? = null)
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.`val`) node = node.next } return result}
fun reorderList(head: ListNode?): ListNode? { if (head == null || head.next == null) return head
var slow = head var fast = head while (fast!!.next != null && fast.next!!.next != null) { slow = slow!!.next fast = fast.next!!.next }
var second = slow!!.next slow.next = null var prev: ListNode? = null while (second != null) { val nextNode = second.next second.next = prev prev = second second = nextNode }
var first: ListNode? = head second = prev while (second != null) { val firstNext = first!!.next val secondNext = second.next
first.next = second second.next = firstNext
first = firstNext second = secondNext }
return head}
fun main() { println(toList(reorderList(buildList(listOf(1, 2, 3, 4))))) // [1, 4, 2, 3] println(toList(reorderList(buildList(listOf(1, 2, 3, 4, 5))))) // [1, 5, 2, 4, 3]}class ListNode { int val; ListNode? next; ListNode(this.val, [this.next]);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? reorderList(ListNode? head) { if (head == null || head.next == null) return head;
var slow = head; var fast = head; while (fast!.next != null && fast.next!.next != null) { slow = slow!.next; fast = fast.next!.next; }
var second = slow!.next; slow.next = null; ListNode? prev; while (second != null) { final nextNode = second.next; second.next = prev; prev = second; second = nextNode; }
ListNode? first = head; second = prev; while (second != null) { final firstNext = first!.next; final secondNext = second.next;
first.next = second; second.next = firstNext;
first = firstNext; second = secondNext; }
return head;}
void main() { print(toList(reorderList(buildList([1, 2, 3, 4])))); // [1, 4, 2, 3] print(toList(reorderList(buildList([1, 2, 3, 4, 5])))); // [1, 5, 2, 4, 3]}99. Xoay danh sách liên kết (Rotate List)
Độ khó: Trung bình · Chủ đề: Linked List
Cho phần đầu của một danh sách liên kết đơn, xoay danh sách sang phải k vị trí.
Ví dụ 1:
Input: head = [1,2,3,4,5], k = 2Output: [4,5,1,2,3]Ví dụ 2:
Input: head = [0,1,2], k = 4Output: [2,0,1]Giải thích: k = 4 nhưng danh sách chỉ có 3 phần tử nên tương đương xoay k % 3 = 1 vị trí.Ràng buộc:
- Số node trong khoảng
[0, 500]. 0 <= k <= 2 * 10^9
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def rotate_right(head, k): if not head or not head.next: return head
# Đếm số node và nối thành vòng tròn length = 1 tail = head while tail.next: tail = tail.next length += 1 tail.next = head
k = k % length steps_to_new_tail = length - k new_tail = head for _ in range(steps_to_new_tail - 1): new_tail = new_tail.next
new_head = new_tail.next new_tail.next = None return new_head
print(to_list(rotate_right(build_list([1, 2, 3, 4, 5]), 2))) # [4, 5, 1, 2, 3]print(to_list(rotate_right(build_list([0, 1, 2]), 4))) # [2, 0, 1]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v = 0, ListNode* n = nullptr) : val(v), next(n) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy; ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* rotateRight(ListNode* head, long long k) { if (!head || !head->next) return head;
int length = 1; ListNode* tail = head; while (tail->next) { tail = tail->next; length++; } tail->next = head;
k = k % length; int stepsToNewTail = length - (int)k; ListNode* newTail = head; for (int i = 0; i < stepsToNewTail - 1; i++) newTail = newTail->next;
ListNode* newHead = newTail->next; newTail->next = nullptr; return newHead;}
int main() { vector<int> a = {1, 2, 3, 4, 5}; for (int x : toList(rotateRight(buildList(a), 2))) cout << x << " "; cout << endl; // 4 5 1 2 3
vector<int> b = {0, 1, 2}; for (int x : toList(rotateRight(buildList(b), 4))) cout << x << " "; cout << endl; // 2 0 1 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode rotateRight(ListNode head, long k) { if (head == null || head.next == null) return head;
int length = 1; ListNode tail = head; while (tail.next != null) { tail = tail.next; length++; } tail.next = head;
k = k % length; int stepsToNewTail = length - (int) k; ListNode newTail = head; for (int i = 0; i < stepsToNewTail - 1; i++) newTail = newTail.next;
ListNode newHead = newTail.next; newTail.next = null; return newHead; }
public static void main(String[] args) { System.out.println(toList(rotateRight(buildList(new int[]{1, 2, 3, 4, 5}), 2))); // [4, 5, 1, 2, 3] System.out.println(toList(rotateRight(buildList(new int[]{0, 1, 2}), 4))); // [2, 0, 1] }}class ListNode(var `val`: Int, var next: ListNode? = null)
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.`val`) node = node.next } return result}
fun rotateRight(head: ListNode?, k: Long): ListNode? { if (head == null || head.next == null) return head
var length = 1 var tail = head while (tail!!.next != null) { tail = tail.next length++ } tail.next = head
val kMod = (k % length).toInt() val stepsToNewTail = length - kMod var newTail = head for (i in 0 until stepsToNewTail - 1) newTail = newTail!!.next
val newHead = newTail!!.next newTail.next = null return newHead}
fun main() { println(toList(rotateRight(buildList(listOf(1, 2, 3, 4, 5)), 2L))) // [4, 5, 1, 2, 3] println(toList(rotateRight(buildList(listOf(0, 1, 2)), 4L))) // [2, 0, 1]}class ListNode { int val; ListNode? next; ListNode(this.val, [this.next]);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? rotateRight(ListNode? head, int k) { if (head == null || head.next == null) return head;
var length = 1; var tail = head; while (tail!.next != null) { tail = tail.next; length++; } tail.next = head;
final kMod = k % length; final stepsToNewTail = length - kMod; var newTail = head; for (var i = 0; i < stepsToNewTail - 1; i++) newTail = newTail!.next;
final newHead = newTail!.next; newTail.next = null; return newHead;}
void main() { print(toList(rotateRight(buildList([1, 2, 3, 4, 5]), 2))); // [4, 5, 1, 2, 3] print(toList(rotateRight(buildList([0, 1, 2]), 4))); // [2, 0, 1]}100. Sắp xếp danh sách liên kết bằng Merge Sort (Sort List)
Độ khó: Trung bình · Chủ đề: Linked List
Cho phần đầu của một danh sách liên kết đơn, sắp xếp nó theo thứ tự tăng dần và trả về danh sách đã sắp xếp. Yêu cầu độ phức tạp thời gian O(n log n) — dùng thuật toán merge sort (chia danh sách làm đôi bằng kỹ thuật hai con trỏ, sắp xếp đệ quy rồi trộn lại).
Ví dụ 1:
Input: [4,2,1,3]Output: [1,2,3,4]Ví dụ 2:
Input: [-1,5,3,4,0]Output: [-1,0,3,4,5]Ràng buộc:
- Số node trong khoảng
[0, 5 * 10^4]. -10^5 <= giá trị node <= 10^5
Xem đáp án
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def build_list(values): dummy = ListNode() current = dummy for v in values: current.next = ListNode(v) current = current.next return dummy.next
def to_list(head): result = [] while head: result.append(head.val) head = head.next return result
def merge(l1, l2): dummy = ListNode() tail = dummy while l1 and l2: if l1.val <= l2.val: tail.next, l1 = l1, l1.next else: tail.next, l2 = l2, l2.next tail = tail.next tail.next = l1 if l1 else l2 return dummy.next
def sort_list(head): if not head or not head.next: return head
# Chia đôi danh sách bằng kỹ thuật hai con trỏ slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next
mid = slow.next slow.next = None
left = sort_list(head) right = sort_list(mid)
return merge(left, right)
print(to_list(sort_list(build_list([4, 2, 1, 3])))) # [1, 2, 3, 4]print(to_list(sort_list(build_list([-1, 5, 3, 4, 0])))) # [-1, 0, 3, 4, 5]#include <iostream>#include <vector>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v = 0, ListNode* n = nullptr) : val(v), next(n) {}};
ListNode* buildList(vector<int>& values) { ListNode dummy; ListNode* current = &dummy; for (int v : values) { current->next = new ListNode(v); current = current->next; } return dummy.next;}
vector<int> toList(ListNode* head) { vector<int> result; while (head) { result.push_back(head->val); head = head->next; } return result;}
ListNode* merge(ListNode* l1, ListNode* l2) { ListNode dummy; ListNode* tail = &dummy; while (l1 && l2) { if (l1->val <= l2->val) { tail->next = l1; l1 = l1->next; } else { tail->next = l2; l2 = l2->next; } tail = tail->next; } tail->next = l1 ? l1 : l2; return dummy.next;}
ListNode* sortList(ListNode* head) { if (!head || !head->next) return head;
ListNode* slow = head; ListNode* fast = head->next; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; }
ListNode* mid = slow->next; slow->next = nullptr;
ListNode* left = sortList(head); ListNode* right = sortList(mid);
return merge(left, right);}
int main() { vector<int> a = {4, 2, 1, 3}; for (int x : toList(sortList(buildList(a)))) cout << x << " "; cout << endl; // 1 2 3 4
vector<int> b = {-1, 5, 3, 4, 0}; for (int x : toList(sortList(buildList(b)))) cout << x << " "; cout << endl; // -1 0 3 4 5 return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode current = dummy; for (int v : values) { current.next = new ListNode(v); current = current.next; } return dummy.next; }
static List<Integer> toList(ListNode head) { List<Integer> result = new ArrayList<>(); while (head != null) { result.add(head.val); head = head.next; } return result; }
static ListNode merge(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(0); ListNode tail = dummy; while (l1 != null && l2 != null) { if (l1.val <= l2.val) { tail.next = l1; l1 = l1.next; } else { tail.next = l2; l2 = l2.next; } tail = tail.next; } tail.next = l1 != null ? l1 : l2; return dummy.next; }
static ListNode sortList(ListNode head) { if (head == null || head.next == null) return head;
ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; }
ListNode mid = slow.next; slow.next = null;
ListNode left = sortList(head); ListNode right = sortList(mid);
return merge(left, right); }
public static void main(String[] args) { System.out.println(toList(sortList(buildList(new int[]{4, 2, 1, 3})))); // [1, 2, 3, 4] System.out.println(toList(sortList(buildList(new int[]{-1, 5, 3, 4, 0})))); // [-1, 0, 3, 4, 5] }}class ListNode(var `val`: Int, var next: ListNode? = null)
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var current = dummy for (v in values) { current.next = ListNode(v) current = current.next!! } return dummy.next}
fun toList(head: ListNode?): List<Int> { val result = mutableListOf<Int>() var node = head while (node != null) { result.add(node.`val`) node = node.next } return result}
fun merge(l1: ListNode?, l2: ListNode?): ListNode? { val dummy = ListNode(0) var tail = dummy var a = l1 var b = l2 while (a != null && b != null) { if (a.`val` <= b.`val`) { tail.next = a a = a.next } else { tail.next = b b = b.next } tail = tail.next!! } tail.next = a ?: b return dummy.next}
fun sortList(head: ListNode?): ListNode? { if (head == null || head.next == null) return head
var slow = head var fast = head.next while (fast != null && fast.next != null) { slow = slow!!.next fast = fast.next!!.next }
val mid = slow!!.next slow.next = null
val left = sortList(head) val right = sortList(mid)
return merge(left, right)}
fun main() { println(toList(sortList(buildList(listOf(4, 2, 1, 3))))) // [1, 2, 3, 4] println(toList(sortList(buildList(listOf(-1, 5, 3, 4, 0))))) // [-1, 0, 3, 4, 5]}class ListNode { int val; ListNode? next; ListNode(this.val, [this.next]);}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); var current = dummy; for (var v in values) { current.next = ListNode(v); current = current.next!; } return dummy.next;}
List<int> toList(ListNode? head) { final result = <int>[]; var node = head; while (node != null) { result.add(node.val); node = node.next; } return result;}
ListNode? merge(ListNode? l1, ListNode? l2) { final dummy = ListNode(0); var tail = dummy; var a = l1, b = l2; while (a != null && b != null) { if (a.val <= b.val) { tail.next = a; a = a.next; } else { tail.next = b; b = b.next; } tail = tail.next!; } tail.next = a ?? b; return dummy.next;}
ListNode? sortList(ListNode? head) { if (head == null || head.next == null) return head;
var slow = head; var fast = head.next; while (fast != null && fast.next != null) { slow = slow!.next; fast = fast.next!.next; }
final mid = slow!.next; slow.next = null;
final left = sortList(head); final right = sortList(mid);
return merge(left, right);}
void main() { print(toList(sortList(buildList([4, 2, 1, 3])))); // [1, 2, 3, 4] print(toList(sortList(buildList([-1, 5, 3, 4, 0])))); // [-1, 0, 3, 4, 5]}Nhóm 6: Cây nhị phân & BST
Phần tiêu đề “Nhóm 6: Cây nhị phân & BST”101. Độ sâu lớn nhất của cây nhị phân (Maximum Depth of Binary Tree)
Độ khó: Dễ · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, tính độ sâu lớn nhất (số node trên đường đi dài nhất từ gốc đến lá).
Ví dụ 1:
Input: root = [3,9,20,null,null,15,7]Output: 3Giải thích: Đường đi dài nhất: 3 -> 20 -> 15 (hoặc 3 -> 20 -> 7), có 3 node.Ràng buộc:
- Số lượng node từ 0 đến 10^4.
-100 <= Node.val <= 100.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def max_depth(root): if root is None: return 0 return 1 + max(max_depth(root.left), max_depth(root.right))
# Cây mẫu: [3,9,20,null,null,15,7]root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))print(max_depth(root)) # 3#include <iostream>#include <algorithm>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
int maxDepth(TreeNode* root) { if (root == nullptr) return 0; return 1 + max(maxDepth(root->left), maxDepth(root->right));}
int main() { // Cay mau: [3,9,20,null,null,15,7] TreeNode* root = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); cout << maxDepth(root) << endl; // 3 return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static int maxDepth(TreeNode root) { if (root == null) return 0; return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); }
public static void main(String[] args) { // Cay mau: [3,9,20,null,null,15,7] TreeNode root = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); System.out.println(maxDepth(root)); // 3 }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun maxDepth(root: TreeNode?): Int { if (root == null) return 0 return 1 + maxOf(maxDepth(root.left), maxDepth(root.right))}
fun main() { // Cay mau: [3,9,20,null,null,15,7] val root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))) println(maxDepth(root)) // 3}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
int maxDepth(TreeNode? root) { if (root == null) return 0; return 1 + [maxDepth(root.left), maxDepth(root.right)].reduce((a, b) => a > b ? a : b);}
void main() { // Cay mau: [3,9,20,null,null,15,7] final root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))); print(maxDepth(root)); // 3}102. Hai cây có giống nhau không (Same Tree)
Độ khó: Dễ · Chủ đề: Cây nhị phân
Cho gốc của 2 cây nhị phân p và q, kiểm tra chúng có giống hệt nhau không (cùng cấu trúc và cùng giá trị mỗi node).
Ví dụ 1:
Input: p = [1,2,3], q = [1,2,3]Output: TrueRàng buộc:
- Số node mỗi cây từ 0 đến 100.
-10^4 <= Node.val <= 10^4.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def is_same_tree(p, q): if p is None and q is None: return True if p is None or q is None: return False return (p.val == q.val and is_same_tree(p.left, q.left) and is_same_tree(p.right, q.right))
p = TreeNode(1, TreeNode(2), TreeNode(3))q = TreeNode(1, TreeNode(2), TreeNode(3))print(is_same_tree(p, q)) # True#include <iostream>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
bool isSameTree(TreeNode* p, TreeNode* q) { if (p == nullptr && q == nullptr) return true; if (p == nullptr || q == nullptr) return false; return p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);}
int main() { TreeNode* p = new TreeNode(1, new TreeNode(2), new TreeNode(3)); TreeNode* q = new TreeNode(1, new TreeNode(2), new TreeNode(3)); cout << boolalpha << isSameTree(p, q) << endl; // true return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static boolean isSameTree(TreeNode p, TreeNode q) { if (p == null && q == null) return true; if (p == null || q == null) return false; return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right); }
public static void main(String[] args) { TreeNode p = new TreeNode(1, new TreeNode(2), new TreeNode(3)); TreeNode q = new TreeNode(1, new TreeNode(2), new TreeNode(3)); System.out.println(isSameTree(p, q)); // true }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { if (p == null && q == null) return true if (p == null || q == null) return false return p.`val` == q.`val` && isSameTree(p.left, q.left) && isSameTree(p.right, q.right)}
fun main() { val p = TreeNode(1, TreeNode(2), TreeNode(3)) val q = TreeNode(1, TreeNode(2), TreeNode(3)) println(isSameTree(p, q)) // true}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
bool isSameTree(TreeNode? p, TreeNode? q) { if (p == null && q == null) return true; if (p == null || q == null) return false; return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);}
void main() { final p = TreeNode(1, TreeNode(2), TreeNode(3)); final q = TreeNode(1, TreeNode(2), TreeNode(3)); print(isSameTree(p, q)); // true}103. Lật ngược cây nhị phân (Invert Binary Tree)
Độ khó: Dễ · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, đảo (mirror) toàn bộ cây — nghĩa là hoán đổi cây con trái và phải tại mọi node. Trả về gốc của cây đã đảo.
Ví dụ 1:
Input: root = [4,2,7,1,3,6,9]Output: [4,7,2,9,6,3,1]Ràng buộc:
- Số lượng node từ 0 đến 100.
-100 <= Node.val <= 100.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def invert_tree(root): if root is None: return None root.left, root.right = invert_tree(root.right), invert_tree(root.left) return root
def to_level_order(root): result = [] queue = [root] while any(node is not None for node in queue): node = queue.pop(0) if node is None: result.append(None) continue result.append(node.val) queue.append(node.left) queue.append(node.right) while result and result[-1] is None: result.pop() return result
root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7, TreeNode(6), TreeNode(9)))print(to_level_order(invert_tree(root))) # [4, 7, 2, 9, 6, 3, 1]#include <iostream>#include <vector>#include <deque>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
TreeNode* invertTree(TreeNode* root) { if (root == nullptr) return nullptr; TreeNode* newLeft = invertTree(root->right); TreeNode* newRight = invertTree(root->left); root->left = newLeft; root->right = newRight; return root;}
void printLevelOrder(TreeNode* root) { vector<int> result; deque<TreeNode*> queue; queue.push_back(root); while (!queue.empty()) { bool anyNotNull = false; for (auto n : queue) if (n) anyNotNull = true; if (!anyNotNull) break; TreeNode* node = queue.front(); queue.pop_front(); if (node == nullptr) { continue; } result.push_back(node->val); queue.push_back(node->left); queue.push_back(node->right); } for (int x : result) cout << x << " "; cout << endl;}
int main() { TreeNode* root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7, new TreeNode(6), new TreeNode(9))); printLevelOrder(invertTree(root)); // 4 7 2 9 6 3 1 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static TreeNode invertTree(TreeNode root) { if (root == null) return null; TreeNode newLeft = invertTree(root.right); TreeNode newRight = invertTree(root.left); root.left = newLeft; root.right = newRight; return root; }
static List<Integer> toLevelOrder(TreeNode root) { List<Integer> result = new ArrayList<>(); Deque<TreeNode> queue = new ArrayDeque<>(); queue.add(root); while (!queue.isEmpty()) { boolean anyNotNull = queue.stream().anyMatch(Objects::nonNull); if (!anyNotNull) break; TreeNode node = queue.poll(); if (node == null) continue; result.add(node.val); queue.add(node.left); queue.add(node.right); } return result; }
public static void main(String[] args) { TreeNode root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7, new TreeNode(6), new TreeNode(9))); System.out.println(toLevelOrder(invertTree(root))); // [4, 7, 2, 9, 6, 3, 1] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun invertTree(root: TreeNode?): TreeNode? { if (root == null) return null val newLeft = invertTree(root.right) val newRight = invertTree(root.left) root.left = newLeft root.right = newRight return root}
fun toLevelOrder(root: TreeNode?): List<Int> { val result = mutableListOf<Int>() val queue = ArrayDeque<TreeNode?>() queue.add(root) while (queue.isNotEmpty()) { if (queue.all { it == null }) break val node = queue.removeFirst() if (node == null) continue result.add(node.`val`) queue.add(node.left) queue.add(node.right) } return result}
fun main() { val root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7, TreeNode(6), TreeNode(9))) println(toLevelOrder(invertTree(root))) // [4, 7, 2, 9, 6, 3, 1]}import 'dart:collection';
class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
TreeNode? invertTree(TreeNode? root) { if (root == null) return null; final newLeft = invertTree(root.right); final newRight = invertTree(root.left); root.left = newLeft; root.right = newRight; return root;}
List<int> toLevelOrder(TreeNode? root) { final result = <int>[]; final queue = Queue<TreeNode?>(); queue.add(root); while (queue.isNotEmpty) { if (queue.every((n) => n == null)) break; final node = queue.removeFirst(); if (node == null) continue; result.add(node.val); queue.add(node.left); queue.add(node.right); } return result;}
void main() { final root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7, TreeNode(6), TreeNode(9))); print(toLevelOrder(invertTree(root))); // [4, 7, 2, 9, 6, 3, 1]}104. Cây đối xứng (Symmetric Tree)
Độ khó: Dễ · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, kiểm tra cây đó có đối xứng qua tâm hay không (cây con trái là ảnh gương của cây con phải).
Ví dụ 1:
Input: root = [1,2,2,3,4,4,3]Output: TrueVí dụ 2:
Input: root = [1,2,2,null,3,null,3]Output: FalseRàng buộc:
- Số lượng node từ 1 đến 1000.
-100 <= Node.val <= 100.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def is_symmetric(root): def is_mirror(t1, t2): if t1 is None and t2 is None: return True if t1 is None or t2 is None: return False return (t1.val == t2.val and is_mirror(t1.left, t2.right) and is_mirror(t1.right, t2.left))
return root is None or is_mirror(root.left, root.right)
root1 = TreeNode(1, TreeNode(2, TreeNode(3), TreeNode(4)), TreeNode(2, TreeNode(4), TreeNode(3)))print(is_symmetric(root1)) # True
root2 = TreeNode(1, TreeNode(2, None, TreeNode(3)), TreeNode(2, None, TreeNode(3)))print(is_symmetric(root2)) # False#include <iostream>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
bool isMirror(TreeNode* t1, TreeNode* t2) { if (t1 == nullptr && t2 == nullptr) return true; if (t1 == nullptr || t2 == nullptr) return false; return t1->val == t2->val && isMirror(t1->left, t2->right) && isMirror(t1->right, t2->left);}
bool isSymmetric(TreeNode* root) { return root == nullptr || isMirror(root->left, root->right);}
int main() { TreeNode* root1 = new TreeNode(1, new TreeNode(2, new TreeNode(3), new TreeNode(4)), new TreeNode(2, new TreeNode(4), new TreeNode(3))); cout << boolalpha << isSymmetric(root1) << endl; // true
TreeNode* root2 = new TreeNode(1, new TreeNode(2, nullptr, new TreeNode(3)), new TreeNode(2, nullptr, new TreeNode(3))); cout << boolalpha << isSymmetric(root2) << endl; // false return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static boolean isMirror(TreeNode t1, TreeNode t2) { if (t1 == null && t2 == null) return true; if (t1 == null || t2 == null) return false; return t1.val == t2.val && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left); }
static boolean isSymmetric(TreeNode root) { return root == null || isMirror(root.left, root.right); }
public static void main(String[] args) { TreeNode root1 = new TreeNode(1, new TreeNode(2, new TreeNode(3), new TreeNode(4)), new TreeNode(2, new TreeNode(4), new TreeNode(3))); System.out.println(isSymmetric(root1)); // true
TreeNode root2 = new TreeNode(1, new TreeNode(2, null, new TreeNode(3)), new TreeNode(2, null, new TreeNode(3))); System.out.println(isSymmetric(root2)); // false }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun isMirror(t1: TreeNode?, t2: TreeNode?): Boolean { if (t1 == null && t2 == null) return true if (t1 == null || t2 == null) return false return t1.`val` == t2.`val` && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left)}
fun isSymmetric(root: TreeNode?): Boolean { return root == null || isMirror(root.left, root.right)}
fun main() { val root1 = TreeNode(1, TreeNode(2, TreeNode(3), TreeNode(4)), TreeNode(2, TreeNode(4), TreeNode(3))) println(isSymmetric(root1)) // true
val root2 = TreeNode(1, TreeNode(2, null, TreeNode(3)), TreeNode(2, null, TreeNode(3))) println(isSymmetric(root2)) // false}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
bool isMirror(TreeNode? t1, TreeNode? t2) { if (t1 == null && t2 == null) return true; if (t1 == null || t2 == null) return false; return t1.val == t2.val && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);}
bool isSymmetric(TreeNode? root) { return root == null || isMirror(root.left, root.right);}
void main() { final root1 = TreeNode(1, TreeNode(2, TreeNode(3), TreeNode(4)), TreeNode(2, TreeNode(4), TreeNode(3))); print(isSymmetric(root1)); // true
final root2 = TreeNode(1, TreeNode(2, null, TreeNode(3)), TreeNode(2, null, TreeNode(3))); print(isSymmetric(root2)); // false}105. Đường đi tổng cho trước (Path Sum)
Độ khó: Dễ · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân và một số nguyên target_sum, kiểm tra cây có tồn tại đường đi từ gốc đến lá sao cho tổng giá trị các node trên đường đi bằng target_sum hay không.
Ví dụ 1:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], target_sum = 22Output: TrueGiải thích: Đường đi 5 -> 4 -> 11 -> 2 có tổng bằng 22.Ràng buộc:
- Số lượng node từ 0 đến 5000.
-1000 <= Node.val <= 1000.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def has_path_sum(root, target_sum): if root is None: return False if root.left is None and root.right is None: return root.val == target_sum remaining = target_sum - root.val return has_path_sum(root.left, remaining) or has_path_sum(root.right, remaining)
root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, None, TreeNode(1))))print(has_path_sum(root, 22)) # True#include <iostream>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
bool hasPathSum(TreeNode* root, int targetSum) { if (root == nullptr) return false; if (root->left == nullptr && root->right == nullptr) return root->val == targetSum; int remaining = targetSum - root->val; return hasPathSum(root->left, remaining) || hasPathSum(root->right, remaining);}
int main() { TreeNode* root = new TreeNode(5, new TreeNode(4, new TreeNode(11, new TreeNode(7), new TreeNode(2))), new TreeNode(8, new TreeNode(13), new TreeNode(4, nullptr, new TreeNode(1)))); cout << boolalpha << hasPathSum(root, 22) << endl; // true return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static boolean hasPathSum(TreeNode root, int targetSum) { if (root == null) return false; if (root.left == null && root.right == null) return root.val == targetSum; int remaining = targetSum - root.val; return hasPathSum(root.left, remaining) || hasPathSum(root.right, remaining); }
public static void main(String[] args) { TreeNode root = new TreeNode(5, new TreeNode(4, new TreeNode(11, new TreeNode(7), new TreeNode(2)), null), new TreeNode(8, new TreeNode(13), new TreeNode(4, null, new TreeNode(1)))); System.out.println(hasPathSum(root, 22)); // true }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun hasPathSum(root: TreeNode?, targetSum: Int): Boolean { if (root == null) return false if (root.left == null && root.right == null) return root.`val` == targetSum val remaining = targetSum - root.`val` return hasPathSum(root.left, remaining) || hasPathSum(root.right, remaining)}
fun main() { val root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, null, TreeNode(1)))) println(hasPathSum(root, 22)) // true}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
bool hasPathSum(TreeNode? root, int targetSum) { if (root == null) return false; if (root.left == null && root.right == null) return root.val == targetSum; final remaining = targetSum - root.val; return hasPathSum(root.left, remaining) || hasPathSum(root.right, remaining);}
void main() { final root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, null, TreeNode(1)))); print(hasPathSum(root, 22)); // true}106. Duyệt cây theo tầng (Binary Tree Level Order Traversal)
Độ khó: Trung bình · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, trả về danh sách các giá trị node theo thứ tự duyệt theo từng tầng (từ trái sang phải), mỗi tầng là một list con.
Ví dụ 1:
Input: root = [3,9,20,null,null,15,7]Output: [[3], [9, 20], [15, 7]]Ví dụ 2:
Input: root = [1]Output: [[1]]Ràng buộc:
- Số lượng node từ 0 đến 2000.
-1000 <= Node.val <= 1000.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def level_order(root): if root is None: return []
result = [] queue = [root] while queue: level_values = [] next_queue = [] for node in queue: level_values.append(node.val) if node.left: next_queue.append(node.left) if node.right: next_queue.append(node.right) result.append(level_values) queue = next_queue
return result
root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))print(level_order(root)) # [[3], [9, 20], [15, 7]]#include <iostream>#include <vector>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; if (root == nullptr) return result;
vector<TreeNode*> queue = {root}; while (!queue.empty()) { vector<int> levelValues; vector<TreeNode*> nextQueue; for (auto node : queue) { levelValues.push_back(node->val); if (node->left) nextQueue.push_back(node->left); if (node->right) nextQueue.push_back(node->right); } result.push_back(levelValues); queue = nextQueue; } return result;}
int main() { TreeNode* root = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); for (auto& level : levelOrder(root)) { cout << "["; for (int v : level) cout << v << " "; cout << "] "; } cout << endl; // [3 ] [9 20 ] [15 7 ] return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> result = new ArrayList<>(); if (root == null) return result;
List<TreeNode> queue = new ArrayList<>(); queue.add(root); while (!queue.isEmpty()) { List<Integer> levelValues = new ArrayList<>(); List<TreeNode> nextQueue = new ArrayList<>(); for (TreeNode node : queue) { levelValues.add(node.val); if (node.left != null) nextQueue.add(node.left); if (node.right != null) nextQueue.add(node.right); } result.add(levelValues); queue = nextQueue; } return result; }
public static void main(String[] args) { TreeNode root = new TreeNode(3, new TreeNode(9, null, null), new TreeNode(20, new TreeNode(15, null, null), new TreeNode(7, null, null))); System.out.println(levelOrder(root)); // [[3], [9, 20], [15, 7]] }}class TreeNode(val v: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun levelOrder(root: TreeNode?): List<List<Int>> { if (root == null) return emptyList()
val result = mutableListOf<List<Int>>() var queue = listOf(root) while (queue.isNotEmpty()) { val levelValues = mutableListOf<Int>() val nextQueue = mutableListOf<TreeNode>() for (node in queue) { levelValues.add(node.v) node.left?.let { nextQueue.add(it) } node.right?.let { nextQueue.add(it) } } result.add(levelValues) queue = nextQueue } return result}
fun main() { val root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))) println(levelOrder(root)) // [[3], [9, 20], [15, 7]]}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
List<List<int>> levelOrder(TreeNode? root) { if (root == null) return [];
final result = <List<int>>[]; var queue = [root]; while (queue.isNotEmpty) { final levelValues = <int>[]; final nextQueue = <TreeNode>[]; for (var node in queue) { levelValues.add(node.val); if (node.left != null) nextQueue.add(node.left!); if (node.right != null) nextQueue.add(node.right!); } result.add(levelValues); queue = nextQueue; } return result;}
void main() { final root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))); print(levelOrder(root)); // [[3], [9, 20], [15, 7]]}107. Kiểm tra cây tìm kiếm nhị phân hợp lệ (Validate Binary Search Tree)
Độ khó: Trung bình · Chủ đề: BST
Cho gốc của một cây nhị phân, kiểm tra đó có phải là cây tìm kiếm nhị phân (BST) hợp lệ hay không: với mọi node, toàn bộ cây con trái nhỏ hơn giá trị node, toàn bộ cây con phải lớn hơn.
Ví dụ 1:
Input: root = [2,1,3]Output: TrueVí dụ 2:
Input: root = [5,1,4,null,null,3,6]Output: FalseGiải thích: Node gốc có giá trị 5, nhưng node con phải của node 4 lại là 3 (< 5), vi phạm điều kiện BST.Ràng buộc:
- Số lượng node từ 1 đến 10^4.
-2^31 <= Node.val <= 2^31 - 1.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def is_valid_bst(root): def validate(node, lower, upper): if node is None: return True if not (lower < node.val < upper): return False return (validate(node.left, lower, node.val) and validate(node.right, node.val, upper))
return validate(root, float("-inf"), float("inf"))
root1 = TreeNode(2, TreeNode(1), TreeNode(3))print(is_valid_bst(root1)) # True
root2 = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6)))print(is_valid_bst(root2)) # False#include <iostream>#include <climits>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
bool validate(TreeNode* node, long long lower, long long upper) { if (node == nullptr) return true; if (!(lower < node->val && node->val < upper)) return false; return validate(node->left, lower, node->val) && validate(node->right, node->val, upper);}
bool isValidBST(TreeNode* root) { return validate(root, LLONG_MIN, LLONG_MAX);}
int main() { TreeNode* root1 = new TreeNode(2, new TreeNode(1), new TreeNode(3)); cout << boolalpha << isValidBST(root1) << endl; // true
TreeNode* root2 = new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6))); cout << boolalpha << isValidBST(root2) << endl; // false return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static boolean validate(TreeNode node, long lower, long upper) { if (node == null) return true; if (!(lower < node.val && node.val < upper)) return false; return validate(node.left, lower, node.val) && validate(node.right, node.val, upper); }
static boolean isValidBST(TreeNode root) { return validate(root, Long.MIN_VALUE, Long.MAX_VALUE); }
public static void main(String[] args) { TreeNode root1 = new TreeNode(2, new TreeNode(1), new TreeNode(3)); System.out.println(isValidBST(root1)); // true
TreeNode root2 = new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6))); System.out.println(isValidBST(root2)); // false }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun validate(node: TreeNode?, lower: Long, upper: Long): Boolean { if (node == null) return true if (!(lower < node.`val` && node.`val` < upper)) return false return validate(node.left, lower, node.`val`.toLong()) && validate(node.right, node.`val`.toLong(), upper)}
fun isValidBST(root: TreeNode?): Boolean { return validate(root, Long.MIN_VALUE, Long.MAX_VALUE)}
fun main() { val root1 = TreeNode(2, TreeNode(1), TreeNode(3)) println(isValidBST(root1)) // true
val root2 = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6))) println(isValidBST(root2)) // false}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
bool validate(TreeNode? node, double lower, double upper) { if (node == null) return true; if (!(lower < node.val && node.val < upper)) return false; return validate(node.left, lower, node.val.toDouble()) && validate(node.right, node.val.toDouble(), upper);}
bool isValidBST(TreeNode? root) { return validate(root, double.negativeInfinity, double.infinity);}
void main() { final root1 = TreeNode(2, TreeNode(1), TreeNode(3)); print(isValidBST(root1)); // true
final root2 = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6))); print(isValidBST(root2)); // false}108. Tổ tiên chung gần nhất trong BST (Lowest Common Ancestor of a BST)
Độ khó: Trung bình · Chủ đề: BST
Cho gốc của một cây BST và 2 node p, q (đều tồn tại trong cây), tìm tổ tiên chung gần nhất (LCA) của p và q.
Ví dụ 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8Output: 6Giải thích: Tổ tiên chung gần nhất của node 2 và node 8 là gốc 6.Ví dụ 2:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4Output: 2Giải thích: Node 2 chính là tổ tiên của node 4 (và của chính nó).Ràng buộc:
- Số lượng node từ 2 đến 10^5.
- Các giá trị node là duy nhất;
pvàqkhác nhau và đều tồn tại trong cây.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def lowest_common_ancestor(root, p, q): node = root while node: if p.val < node.val and q.val < node.val: node = node.left elif p.val > node.val and q.val > node.val: node = node.right else: return node return None
n0, n3, n5 = TreeNode(0), TreeNode(3), TreeNode(5)n4 = TreeNode(4, n3, n5)n2 = TreeNode(2, n0, n4)n7, n9 = TreeNode(7), TreeNode(9)n8 = TreeNode(8, n7, n9)root = TreeNode(6, n2, n8)
print(lowest_common_ancestor(root, n2, n8).val) # 6print(lowest_common_ancestor(root, n2, n4).val) # 2#include <iostream>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { TreeNode* node = root; while (node) { if (p->val < node->val && q->val < node->val) node = node->left; else if (p->val > node->val && q->val > node->val) node = node->right; else return node; } return nullptr;}
int main() { TreeNode *n0 = new TreeNode(0), *n3 = new TreeNode(3), *n5 = new TreeNode(5); TreeNode *n4 = new TreeNode(4, n3, n5); TreeNode *n2 = new TreeNode(2, n0, n4); TreeNode *n7 = new TreeNode(7), *n9 = new TreeNode(9); TreeNode *n8 = new TreeNode(8, n7, n9); TreeNode *root = new TreeNode(6, n2, n8);
cout << lowestCommonAncestor(root, n2, n8)->val << endl; // 6 cout << lowestCommonAncestor(root, n2, n4)->val << endl; // 2 return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { TreeNode node = root; while (node != null) { if (p.val < node.val && q.val < node.val) node = node.left; else if (p.val > node.val && q.val > node.val) node = node.right; else return node; } return null; }
public static void main(String[] args) { TreeNode n0 = new TreeNode(0), n3 = new TreeNode(3), n5 = new TreeNode(5); TreeNode n4 = new TreeNode(4, n3, n5); TreeNode n2 = new TreeNode(2, n0, n4); TreeNode n7 = new TreeNode(7), n9 = new TreeNode(9); TreeNode n8 = new TreeNode(8, n7, n9); TreeNode root = new TreeNode(6, n2, n8);
System.out.println(lowestCommonAncestor(root, n2, n8).val); // 6 System.out.println(lowestCommonAncestor(root, n2, n4).val); // 2 }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun lowestCommonAncestor(root: TreeNode?, p: TreeNode, q: TreeNode): TreeNode? { var node = root while (node != null) { node = when { p.`val` < node.`val` && q.`val` < node.`val` -> node.left p.`val` > node.`val` && q.`val` > node.`val` -> node.right else -> return node } } return null}
fun main() { val n0 = TreeNode(0); val n3 = TreeNode(3); val n5 = TreeNode(5) val n4 = TreeNode(4, n3, n5) val n2 = TreeNode(2, n0, n4) val n7 = TreeNode(7); val n9 = TreeNode(9) val n8 = TreeNode(8, n7, n9) val root = TreeNode(6, n2, n8)
println(lowestCommonAncestor(root, n2, n8)!!.`val`) // 6 println(lowestCommonAncestor(root, n2, n4)!!.`val`) // 2}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
TreeNode? lowestCommonAncestor(TreeNode? root, TreeNode p, TreeNode q) { var node = root; while (node != null) { if (p.val < node.val && q.val < node.val) { node = node.left; } else if (p.val > node.val && q.val > node.val) { node = node.right; } else { return node; } } return null;}
void main() { final n0 = TreeNode(0), n3 = TreeNode(3), n5 = TreeNode(5); final n4 = TreeNode(4, n3, n5); final n2 = TreeNode(2, n0, n4); final n7 = TreeNode(7), n9 = TreeNode(9); final n8 = TreeNode(8, n7, n9); final root = TreeNode(6, n2, n8);
print(lowestCommonAncestor(root, n2, n8)!.val); // 6 print(lowestCommonAncestor(root, n2, n4)!.val); // 2}109. Phần tử nhỏ thứ k trong BST (Kth Smallest Element in a BST)
Độ khó: Trung bình · Chủ đề: BST
Cho gốc của một cây BST và số nguyên k, tìm giá trị nhỏ thứ k (1-indexed) trong cây.
Ví dụ 1:
Input: root = [3,1,4,null,2], k = 1Output: 1Ví dụ 2:
Input: root = [5,3,6,2,4,null,null,1], k = 3Output: 3Ràng buộc:
- Số lượng node từ 1 đến 10^4.
1 <= k <= số lượng node trong cây.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def kth_smallest(root, k): stack = [] node = root while stack or node: while node: stack.append(node) node = node.left node = stack.pop() k -= 1 if k == 0: return node.val node = node.right return -1
root1 = TreeNode(3, TreeNode(1, None, TreeNode(2)), TreeNode(4))print(kth_smallest(root1, 1)) # 1
root2 = TreeNode(5, TreeNode(3, TreeNode(2, TreeNode(1)), TreeNode(4)), TreeNode(6))print(kth_smallest(root2, 3)) # 3#include <iostream>#include <stack>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
int kthSmallest(TreeNode* root, int k) { stack<TreeNode*> st; TreeNode* node = root; while (!st.empty() || node) { while (node) { st.push(node); node = node->left; } node = st.top(); st.pop(); k--; if (k == 0) return node->val; node = node->right; } return -1;}
int main() { TreeNode* root1 = new TreeNode(3, new TreeNode(1, nullptr, new TreeNode(2)), new TreeNode(4)); cout << kthSmallest(root1, 1) << endl; // 1
TreeNode* root2 = new TreeNode(5, new TreeNode(3, new TreeNode(2, new TreeNode(1)), new TreeNode(4)), new TreeNode(6)); cout << kthSmallest(root2, 3) << endl; // 3 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static int kthSmallest(TreeNode root, int k) { Deque<TreeNode> stack = new ArrayDeque<>(); TreeNode node = root; while (!stack.isEmpty() || node != null) { while (node != null) { stack.push(node); node = node.left; } node = stack.pop(); k--; if (k == 0) return node.val; node = node.right; } return -1; }
public static void main(String[] args) { TreeNode root1 = new TreeNode(3, new TreeNode(1, null, new TreeNode(2)), new TreeNode(4)); System.out.println(kthSmallest(root1, 1)); // 1
TreeNode root2 = new TreeNode(5, new TreeNode(3, new TreeNode(2, new TreeNode(1), null), new TreeNode(4)), new TreeNode(6)); System.out.println(kthSmallest(root2, 3)); // 3 }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun kthSmallest(root: TreeNode?, k: Int): Int { var kk = k val stack = ArrayDeque<TreeNode>() var node = root while (stack.isNotEmpty() || node != null) { while (node != null) { stack.addLast(node) node = node.left } node = stack.removeLast() kk-- if (kk == 0) return node.`val` node = node.right } return -1}
fun main() { val root1 = TreeNode(3, TreeNode(1, null, TreeNode(2)), TreeNode(4)) println(kthSmallest(root1, 1)) // 1
val root2 = TreeNode(5, TreeNode(3, TreeNode(2, TreeNode(1)), TreeNode(4)), TreeNode(6)) println(kthSmallest(root2, 3)) // 3}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
int kthSmallest(TreeNode? root, int k) { final stack = <TreeNode>[]; var node = root; while (stack.isNotEmpty || node != null) { while (node != null) { stack.add(node); node = node.left; } node = stack.removeLast(); k--; if (k == 0) return node.val; node = node.right; } return -1;}
void main() { final root1 = TreeNode(3, TreeNode(1, null, TreeNode(2)), TreeNode(4)); print(kthSmallest(root1, 1)); // 1
final root2 = TreeNode(5, TreeNode(3, TreeNode(2, TreeNode(1)), TreeNode(4)), TreeNode(6)); print(kthSmallest(root2, 3)); // 3}110. Dựng cây từ duyệt trước và duyệt giữa (Construct Binary Tree from Preorder and Inorder Traversal)
Độ khó: Trung bình · Chủ đề: Cây nhị phân
Cho 2 mảng preorder và inorder là kết quả duyệt trước (Node - Trái - Phải) và duyệt giữa (Trái - Node - Phải) của cùng một cây nhị phân (giá trị các node là duy nhất). Dựng lại cây và trả về gốc.
Ví dụ 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]Output: [3,9,20,null,null,15,7]Ràng buộc:
1 <= preorder.length <= 3000.- inorder.length == preorder.length.
- Các giá trị trong preorder và inorder là duy nhất.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def build_tree(preorder, inorder): if not preorder: return None
root_val = preorder[0] root = TreeNode(root_val) mid = inorder.index(root_val)
root.left = build_tree(preorder[1:mid + 1], inorder[:mid]) root.right = build_tree(preorder[mid + 1:], inorder[mid + 1:]) return root
def to_level_order(root): result = [] queue = [root] while any(node is not None for node in queue): node = queue.pop(0) if node is None: result.append(None) continue result.append(node.val) queue.append(node.left) queue.append(node.right) while result and result[-1] is None: result.pop() return result
root = build_tree([3, 9, 20, 15, 7], [9, 3, 15, 20, 7])print(to_level_order(root)) # [3, 9, 20, None, None, 15, 7]#include <iostream>#include <vector>#include <deque>#include <algorithm>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
TreeNode* buildTree(vector<int> preorder, vector<int> inorder) { if (preorder.empty()) return nullptr;
int rootVal = preorder[0]; TreeNode* root = new TreeNode(rootVal); int mid = find(inorder.begin(), inorder.end(), rootVal) - inorder.begin();
vector<int> leftPre(preorder.begin() + 1, preorder.begin() + 1 + mid); vector<int> leftIn(inorder.begin(), inorder.begin() + mid); vector<int> rightPre(preorder.begin() + 1 + mid, preorder.end()); vector<int> rightIn(inorder.begin() + mid + 1, inorder.end());
root->left = buildTree(leftPre, leftIn); root->right = buildTree(rightPre, rightIn); return root;}
void printLevelOrder(TreeNode* root) { deque<TreeNode*> queue; queue.push_back(root); vector<string> result; while (!queue.empty()) { bool anyNotNull = false; for (auto n : queue) if (n) anyNotNull = true; if (!anyNotNull) break; TreeNode* node = queue.front(); queue.pop_front(); if (node == nullptr) { result.push_back("None"); continue; } result.push_back(to_string(node->val)); queue.push_back(node->left); queue.push_back(node->right); } while (!result.empty() && result.back() == "None") result.pop_back(); for (auto& s : result) cout << s << " "; cout << endl;}
int main() { TreeNode* root = buildTree({3, 9, 20, 15, 7}, {9, 3, 15, 20, 7}); printLevelOrder(root); // 3 9 20 None None 15 7 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } }
static TreeNode buildTree(int[] preorder, int[] inorder) { if (preorder.length == 0) return null;
int rootVal = preorder[0]; TreeNode root = new TreeNode(rootVal); int mid = 0; for (int i = 0; i < inorder.length; i++) if (inorder[i] == rootVal) { mid = i; break; }
root.left = buildTree(Arrays.copyOfRange(preorder, 1, mid + 1), Arrays.copyOfRange(inorder, 0, mid)); root.right = buildTree(Arrays.copyOfRange(preorder, mid + 1, preorder.length), Arrays.copyOfRange(inorder, mid + 1, inorder.length)); return root; }
static List<String> toLevelOrder(TreeNode root) { List<String> result = new ArrayList<>(); Deque<TreeNode> queue = new ArrayDeque<>(); queue.add(root); while (!queue.isEmpty()) { boolean anyNotNull = queue.stream().anyMatch(Objects::nonNull); if (!anyNotNull) break; TreeNode node = queue.poll(); if (node == null) { result.add("None"); continue; } result.add(String.valueOf(node.val)); queue.add(node.left); queue.add(node.right); } while (!result.isEmpty() && result.get(result.size() - 1).equals("None")) { result.remove(result.size() - 1); } return result; }
public static void main(String[] args) { TreeNode root = buildTree(new int[]{3, 9, 20, 15, 7}, new int[]{9, 3, 15, 20, 7}); System.out.println(toLevelOrder(root)); // [3, 9, 20, None, None, 15, 7] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun buildTree(preorder: List<Int>, inorder: List<Int>): TreeNode? { if (preorder.isEmpty()) return null
val rootVal = preorder[0] val root = TreeNode(rootVal) val mid = inorder.indexOf(rootVal)
root.left = buildTree(preorder.subList(1, mid + 1), inorder.subList(0, mid)) root.right = buildTree(preorder.subList(mid + 1, preorder.size), inorder.subList(mid + 1, inorder.size)) return root}
fun toLevelOrder(root: TreeNode?): List<String> { val result = mutableListOf<String>() val queue = ArrayDeque<TreeNode?>() queue.add(root) while (queue.isNotEmpty()) { if (queue.all { it == null }) break val node = queue.removeFirst() if (node == null) { result.add("None") continue } result.add(node.`val`.toString()) queue.add(node.left) queue.add(node.right) } while (result.isNotEmpty() && result.last() == "None") result.removeAt(result.size - 1) return result}
fun main() { val root = buildTree(listOf(3, 9, 20, 15, 7), listOf(9, 3, 15, 20, 7)) println(toLevelOrder(root)) // [3, 9, 20, None, None, 15, 7]}import 'dart:collection';
class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
TreeNode? buildTree(List<int> preorder, List<int> inorder) { if (preorder.isEmpty) return null;
final rootVal = preorder[0]; final root = TreeNode(rootVal); final mid = inorder.indexOf(rootVal);
root.left = buildTree(preorder.sublist(1, mid + 1), inorder.sublist(0, mid)); root.right = buildTree(preorder.sublist(mid + 1), inorder.sublist(mid + 1)); return root;}
List<String> toLevelOrder(TreeNode? root) { final result = <String>[]; final queue = Queue<TreeNode?>(); queue.add(root); while (queue.isNotEmpty) { if (queue.every((n) => n == null)) break; final node = queue.removeFirst(); if (node == null) { result.add("None"); continue; } result.add(node.val.toString()); queue.add(node.left); queue.add(node.right); } while (result.isNotEmpty && result.last == "None") result.removeLast(); return result;}
void main() { final root = buildTree([3, 9, 20, 15, 7], [9, 3, 15, 20, 7]); print(toLevelOrder(root)); // [3, 9, 20, None, None, 15, 7]}111. Duyệt cây zigzag theo tầng (Binary Tree Zigzag Level Order Traversal)
Độ khó: Trung bình · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, trả về danh sách các giá trị node theo thứ tự duyệt zigzag theo tầng: tầng 1 từ trái sang phải, tầng 2 từ phải sang trái, xen kẽ như vậy.
Ví dụ 1:
Input: root = [3,9,20,null,null,15,7]Output: [[3], [20, 9], [15, 7]]Ràng buộc:
- Số lượng node từ 0 đến 2000.
-100 <= Node.val <= 100.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def zigzag_level_order(root): if root is None: return []
result = [] queue = [root] left_to_right = True while queue: level_values = [] next_queue = [] for node in queue: level_values.append(node.val) if node.left: next_queue.append(node.left) if node.right: next_queue.append(node.right) if not left_to_right: level_values.reverse() result.append(level_values) queue = next_queue left_to_right = not left_to_right
return result
root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))print(zigzag_level_order(root)) # [[3], [20, 9], [15, 7]]#include <iostream>#include <vector>#include <algorithm>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
vector<vector<int>> zigzagLevelOrder(TreeNode* root) { vector<vector<int>> result; if (root == nullptr) return result;
vector<TreeNode*> queue = {root}; bool leftToRight = true; while (!queue.empty()) { vector<int> levelValues; vector<TreeNode*> nextQueue; for (auto node : queue) { levelValues.push_back(node->val); if (node->left) nextQueue.push_back(node->left); if (node->right) nextQueue.push_back(node->right); } if (!leftToRight) reverse(levelValues.begin(), levelValues.end()); result.push_back(levelValues); queue = nextQueue; leftToRight = !leftToRight; } return result;}
int main() { TreeNode* root = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); for (auto& level : zigzagLevelOrder(root)) { cout << "["; for (int x : level) cout << x << " "; cout << "] "; } cout << endl; // [3 ] [20 9 ] [15 7 ] return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static List<List<Integer>> zigzagLevelOrder(TreeNode root) { List<List<Integer>> result = new ArrayList<>(); if (root == null) return result;
List<TreeNode> queue = new ArrayList<>(List.of(root)); boolean leftToRight = true; while (!queue.isEmpty()) { List<Integer> levelValues = new ArrayList<>(); List<TreeNode> nextQueue = new ArrayList<>(); for (TreeNode node : queue) { levelValues.add(node.val); if (node.left != null) nextQueue.add(node.left); if (node.right != null) nextQueue.add(node.right); } if (!leftToRight) Collections.reverse(levelValues); result.add(levelValues); queue = nextQueue; leftToRight = !leftToRight; } return result; }
public static void main(String[] args) { TreeNode root = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); System.out.println(zigzagLevelOrder(root)); // [[3], [20, 9], [15, 7]] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun zigzagLevelOrder(root: TreeNode?): List<List<Int>> { val result = mutableListOf<List<Int>>() if (root == null) return result
var queue = mutableListOf(root) var leftToRight = true while (queue.isNotEmpty()) { val levelValues = mutableListOf<Int>() val nextQueue = mutableListOf<TreeNode>() for (node in queue) { levelValues.add(node.`val`) node.left?.let { nextQueue.add(it) } node.right?.let { nextQueue.add(it) } } if (!leftToRight) levelValues.reverse() result.add(levelValues) queue = nextQueue leftToRight = !leftToRight } return result}
fun main() { val root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))) println(zigzagLevelOrder(root)) // [[3], [20, 9], [15, 7]]}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
List<List<int>> zigzagLevelOrder(TreeNode? root) { final result = <List<int>>[]; if (root == null) return result;
var queue = <TreeNode>[root]; var leftToRight = true; while (queue.isNotEmpty) { final levelValues = <int>[]; final nextQueue = <TreeNode>[]; for (var node in queue) { levelValues.add(node.val); if (node.left != null) nextQueue.add(node.left!); if (node.right != null) nextQueue.add(node.right!); } result.add(leftToRight ? levelValues : levelValues.reversed.toList()); queue = nextQueue; leftToRight = !leftToRight; } return result;}
void main() { final root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))); print(zigzagLevelOrder(root)); // [[3], [20, 9], [15, 7]]}112. Đường kính cây nhị phân (Diameter of Binary Tree)
Độ khó: Trung bình · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, tính đường kính của cây — độ dài (số cạnh) của đường đi dài nhất giữa 2 node bất kỳ, đường đi này có thể đi qua hoặc không đi qua gốc.
Ví dụ 1:
Input: root = [1,2,3,4,5]Output: 3Giải thích: Đường đi dài nhất là [4,2,1,3] hoặc [5,2,1,3], có độ dài 3 cạnh.Ràng buộc:
- Số lượng node từ 1 đến 10^4.
-100 <= Node.val <= 100.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def diameter_of_binary_tree(root): diameter = 0
def depth(node): nonlocal diameter if node is None: return 0 left_depth = depth(node.left) right_depth = depth(node.right) diameter = max(diameter, left_depth + right_depth) return 1 + max(left_depth, right_depth)
depth(root) return diameter
root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3))print(diameter_of_binary_tree(root)) # 3#include <iostream>#include <algorithm>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
int diameter = 0;
int depth(TreeNode* node) { if (node == nullptr) return 0; int leftDepth = depth(node->left); int rightDepth = depth(node->right); diameter = max(diameter, leftDepth + rightDepth); return 1 + max(leftDepth, rightDepth);}
int diameterOfBinaryTree(TreeNode* root) { diameter = 0; depth(root); return diameter;}
int main() { TreeNode* root = new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3)); cout << diameterOfBinaryTree(root) << endl; // 3 return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static int diameter = 0;
static int depth(TreeNode node) { if (node == null) return 0; int leftDepth = depth(node.left); int rightDepth = depth(node.right); diameter = Math.max(diameter, leftDepth + rightDepth); return 1 + Math.max(leftDepth, rightDepth); }
static int diameterOfBinaryTree(TreeNode root) { diameter = 0; depth(root); return diameter; }
public static void main(String[] args) { TreeNode root = new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3)); System.out.println(diameterOfBinaryTree(root)); // 3 }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
var diameter = 0
fun depth(node: TreeNode?): Int { if (node == null) return 0 val leftDepth = depth(node.left) val rightDepth = depth(node.right) diameter = maxOf(diameter, leftDepth + rightDepth) return 1 + maxOf(leftDepth, rightDepth)}
fun diameterOfBinaryTree(root: TreeNode?): Int { diameter = 0 depth(root) return diameter}
fun main() { val root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3)) println(diameterOfBinaryTree(root)) // 3}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
int diameter = 0;
int depth(TreeNode? node) { if (node == null) return 0; final leftDepth = depth(node.left); final rightDepth = depth(node.right); diameter = diameter > (leftDepth + rightDepth) ? diameter : (leftDepth + rightDepth); return 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);}
int diameterOfBinaryTree(TreeNode? root) { diameter = 0; depth(root); return diameter;}
void main() { final root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3)); print(diameterOfBinaryTree(root)); // 3}113. Kiểm tra cây cân bằng (Balanced Binary Tree)
Độ khó: Trung bình · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, kiểm tra đó có phải là cây cân bằng chiều cao hay không: với mọi node, chênh lệch chiều cao giữa cây con trái và cây con phải không quá 1.
Ví dụ 1:
Input: root = [3,9,20,null,null,15,7]Output: TrueVí dụ 2:
Input: root = [1,2,2,3,3,null,null,4,4]Output: FalseRàng buộc:
- Số lượng node từ 0 đến 5000.
-10^4 <= Node.val <= 10^4.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def is_balanced(root): def height(node): if node is None: return 0 left_height = height(node.left) if left_height == -1: return -1 right_height = height(node.right) if right_height == -1: return -1 if abs(left_height - right_height) > 1: return -1 return 1 + max(left_height, right_height)
return height(root) != -1
root1 = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))print(is_balanced(root1)) # True
n4a = TreeNode(4)n4b = TreeNode(4)n3a = TreeNode(3, n4a)n3b = TreeNode(3, n4b)n2a = TreeNode(2, n3a)n2b = TreeNode(2, n3b)root2 = TreeNode(1, n2a, n2b)print(is_balanced(root2)) # False#include <iostream>#include <cmath>#include <algorithm>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
int height(TreeNode* node) { if (node == nullptr) return 0; int leftHeight = height(node->left); if (leftHeight == -1) return -1; int rightHeight = height(node->right); if (rightHeight == -1) return -1; if (abs(leftHeight - rightHeight) > 1) return -1; return 1 + max(leftHeight, rightHeight);}
bool isBalanced(TreeNode* root) { return height(root) != -1;}
int main() { TreeNode* root1 = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); cout << boolalpha << isBalanced(root1) << endl; // true
TreeNode* n4a = new TreeNode(4); TreeNode* n4b = new TreeNode(4); TreeNode* n3a = new TreeNode(3, n4a); TreeNode* n3b = new TreeNode(3, n4b); TreeNode* n2a = new TreeNode(2, n3a); TreeNode* n2b = new TreeNode(2, n3b); TreeNode* root2 = new TreeNode(1, n2a, n2b); cout << boolalpha << isBalanced(root2) << endl; // false return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left) { this.val = val; this.left = left; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static int height(TreeNode node) { if (node == null) return 0; int leftHeight = height(node.left); if (leftHeight == -1) return -1; int rightHeight = height(node.right); if (rightHeight == -1) return -1; if (Math.abs(leftHeight - rightHeight) > 1) return -1; return 1 + Math.max(leftHeight, rightHeight); }
static boolean isBalanced(TreeNode root) { return height(root) != -1; }
public static void main(String[] args) { TreeNode root1 = new TreeNode(3, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); System.out.println(isBalanced(root1)); // true
TreeNode n4a = new TreeNode(4); TreeNode n4b = new TreeNode(4); TreeNode n3a = new TreeNode(3, n4a); TreeNode n3b = new TreeNode(3, n4b); TreeNode n2a = new TreeNode(2, n3a); TreeNode n2b = new TreeNode(2, n3b); TreeNode root2 = new TreeNode(1, n2a, n2b); System.out.println(isBalanced(root2)); // false }}import kotlin.math.absimport kotlin.math.max
class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun height(node: TreeNode?): Int { if (node == null) return 0 val leftHeight = height(node.left) if (leftHeight == -1) return -1 val rightHeight = height(node.right) if (rightHeight == -1) return -1 if (abs(leftHeight - rightHeight) > 1) return -1 return 1 + max(leftHeight, rightHeight)}
fun isBalanced(root: TreeNode?): Boolean { return height(root) != -1}
fun main() { val root1 = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))) println(isBalanced(root1)) // true
val n4a = TreeNode(4) val n4b = TreeNode(4) val n3a = TreeNode(3, n4a) val n3b = TreeNode(3, n4b) val n2a = TreeNode(2, n3a) val n2b = TreeNode(2, n3b) val root2 = TreeNode(1, n2a, n2b) println(isBalanced(root2)) // false}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
int height(TreeNode? node) { if (node == null) return 0; final leftHeight = height(node.left); if (leftHeight == -1) return -1; final rightHeight = height(node.right); if (rightHeight == -1) return -1; if ((leftHeight - rightHeight).abs() > 1) return -1; return 1 + (leftHeight > rightHeight ? leftHeight : rightHeight);}
bool isBalanced(TreeNode? root) { return height(root) != -1;}
void main() { final root1 = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))); print(isBalanced(root1)); // true
final n4a = TreeNode(4); final n4b = TreeNode(4); final n3a = TreeNode(3, n4a); final n3b = TreeNode(3, n4b); final n2a = TreeNode(2, n3a); final n2b = TreeNode(2, n3b); final root2 = TreeNode(1, n2a, n2b); print(isBalanced(root2)); // false}114. Đổi mảng đã sắp xếp thành BST cân bằng (Convert Sorted Array to Binary Search Tree)
Độ khó: Trung bình · Chủ đề: BST
Cho một mảng số nguyên nums đã sắp xếp tăng dần, xây dựng một cây BST cân bằng chiều cao từ mảng đó và trả về gốc.
Ví dụ 1:
Input: nums = [-10,-3,0,5,9]Output: [0,-3,9,-10,null,5]Giải thích: [0,-10,5,null,-3,null,9] cũng là một đáp án hợp lệ khác vì đề bài chấp nhận nhiều cây cân bằng khác nhau.Ràng buộc:
1 <= nums.length <= 10^4.- nums được sắp xếp tăng dần nghiêm ngặt.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def sorted_array_to_bst(nums): if not nums: return None mid = len(nums) // 2 root = TreeNode(nums[mid]) root.left = sorted_array_to_bst(nums[:mid]) root.right = sorted_array_to_bst(nums[mid + 1:]) return root
def to_level_order(root): result = [] queue = [root] while any(node is not None for node in queue): node = queue.pop(0) if node is None: result.append(None) continue result.append(node.val) queue.append(node.left) queue.append(node.right) while result and result[-1] is None: result.pop() return result
root = sorted_array_to_bst([-10, -3, 0, 5, 9])print(to_level_order(root)) # [0, -10, 5, None, -3, None, 9]#include <iostream>#include <vector>#include <deque>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
TreeNode* sortedArrayToBST(vector<int>& nums, int lo, int hi) { if (lo > hi) return nullptr; int mid = lo + (hi - lo) / 2; TreeNode* root = new TreeNode(nums[mid]); root->left = sortedArrayToBST(nums, lo, mid - 1); root->right = sortedArrayToBST(nums, mid + 1, hi); return root;}
void printLevelOrder(TreeNode* root) { deque<TreeNode*> queue; queue.push_back(root); vector<string> result; while (!queue.empty()) { bool anyNotNull = false; for (auto n : queue) if (n) anyNotNull = true; if (!anyNotNull) break; TreeNode* node = queue.front(); queue.pop_front(); if (node == nullptr) { result.push_back("None"); continue; } result.push_back(to_string(node->val)); queue.push_back(node->left); queue.push_back(node->right); } while (!result.empty() && result.back() == "None") result.pop_back(); for (auto& s : result) cout << s << " "; cout << endl;}
int main() { vector<int> nums = {-10, -3, 0, 5, 9}; TreeNode* root = sortedArrayToBST(nums, 0, nums.size() - 1); printLevelOrder(root); // 0 -10 5 None -3 None 9 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } }
static TreeNode sortedArrayToBST(int[] nums, int lo, int hi) { if (lo > hi) return null; int mid = lo + (hi - lo) / 2; TreeNode root = new TreeNode(nums[mid]); root.left = sortedArrayToBST(nums, lo, mid - 1); root.right = sortedArrayToBST(nums, mid + 1, hi); return root; }
static List<String> toLevelOrder(TreeNode root) { List<String> result = new ArrayList<>(); Deque<TreeNode> queue = new ArrayDeque<>(); queue.add(root); while (!queue.isEmpty()) { boolean anyNotNull = queue.stream().anyMatch(Objects::nonNull); if (!anyNotNull) break; TreeNode node = queue.poll(); if (node == null) { result.add("None"); continue; } result.add(String.valueOf(node.val)); queue.add(node.left); queue.add(node.right); } while (!result.isEmpty() && result.get(result.size() - 1).equals("None")) { result.remove(result.size() - 1); } return result; }
public static void main(String[] args) { int[] nums = {-10, -3, 0, 5, 9}; TreeNode root = sortedArrayToBST(nums, 0, nums.length - 1); System.out.println(toLevelOrder(root)); // [0, -10, 5, None, -3, None, 9] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun sortedArrayToBST(nums: List<Int>, lo: Int, hi: Int): TreeNode? { if (lo > hi) return null val mid = lo + (hi - lo) / 2 val root = TreeNode(nums[mid]) root.left = sortedArrayToBST(nums, lo, mid - 1) root.right = sortedArrayToBST(nums, mid + 1, hi) return root}
fun toLevelOrder(root: TreeNode?): List<String> { val result = mutableListOf<String>() val queue = ArrayDeque<TreeNode?>() queue.add(root) while (queue.isNotEmpty()) { if (queue.all { it == null }) break val node = queue.removeFirst() if (node == null) { result.add("None") continue } result.add(node.`val`.toString()) queue.add(node.left) queue.add(node.right) } while (result.isNotEmpty() && result.last() == "None") result.removeAt(result.size - 1) return result}
fun main() { val nums = listOf(-10, -3, 0, 5, 9) val root = sortedArrayToBST(nums, 0, nums.size - 1) println(toLevelOrder(root)) // [0, -10, 5, None, -3, None, 9]}import 'dart:collection';
class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
TreeNode? sortedArrayToBST(List<int> nums, int lo, int hi) { if (lo > hi) return null; final mid = lo + (hi - lo) ~/ 2; final root = TreeNode(nums[mid]); root.left = sortedArrayToBST(nums, lo, mid - 1); root.right = sortedArrayToBST(nums, mid + 1, hi); return root;}
List<String> toLevelOrder(TreeNode? root) { final result = <String>[]; final queue = Queue<TreeNode?>(); queue.add(root); while (queue.isNotEmpty) { if (queue.every((n) => n == null)) break; final node = queue.removeFirst(); if (node == null) { result.add("None"); continue; } result.add(node.val.toString()); queue.add(node.left); queue.add(node.right); } while (result.isNotEmpty && result.last == "None") result.removeLast(); return result;}
void main() { final nums = [-10, -3, 0, 5, 9]; final root = sortedArrayToBST(nums, 0, nums.length - 1); print(toLevelOrder(root)); // [0, -10, 5, None, -3, None, 9]}115. Tổng hợp các đường đi có tổng bằng target (Path Sum II)
Độ khó: Trung bình · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân và một số nguyên target_sum, trả về tất cả các đường đi từ gốc đến lá sao cho tổng giá trị các node trên đường đi bằng target_sum. Mỗi đường đi là một list các giá trị node.
Ví dụ 1:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], target_sum = 22Output: [[5, 4, 11, 2], [5, 8, 4, 5]]Ràng buộc:
- Số lượng node từ 0 đến 5000.
-1000 <= Node.val <= 1000.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def path_sum(root, target_sum): result = []
def dfs(node, remaining, path): if node is None: return path.append(node.val) remaining -= node.val if node.left is None and node.right is None and remaining == 0: result.append(list(path)) else: dfs(node.left, remaining, path) dfs(node.right, remaining, path) path.pop()
dfs(root, target_sum, []) return result
root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, TreeNode(5), TreeNode(1))))print(path_sum(root, 22)) # [[5, 4, 11, 2], [5, 8, 4, 5]]#include <iostream>#include <vector>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
void dfs(TreeNode* node, int remaining, vector<int>& path, vector<vector<int>>& result) { if (node == nullptr) return; path.push_back(node->val); remaining -= node->val; if (node->left == nullptr && node->right == nullptr && remaining == 0) { result.push_back(path); } else { dfs(node->left, remaining, path, result); dfs(node->right, remaining, path, result); } path.pop_back();}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) { vector<vector<int>> result; vector<int> path; dfs(root, targetSum, path, result); return result;}
int main() { TreeNode* root = new TreeNode(5, new TreeNode(4, new TreeNode(11, new TreeNode(7), new TreeNode(2))), new TreeNode(8, new TreeNode(13), new TreeNode(4, new TreeNode(5), new TreeNode(1)))); for (auto& path : pathSum(root, 22)) { cout << "["; for (int x : path) cout << x << " "; cout << "] "; } cout << endl; // [5 4 11 2 ] [5 8 4 5 ] return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static void dfs(TreeNode node, int remaining, List<Integer> path, List<List<Integer>> result) { if (node == null) return; path.add(node.val); remaining -= node.val; if (node.left == null && node.right == null && remaining == 0) { result.add(new ArrayList<>(path)); } else { dfs(node.left, remaining, path, result); dfs(node.right, remaining, path, result); } path.remove(path.size() - 1); }
static List<List<Integer>> pathSum(TreeNode root, int targetSum) { List<List<Integer>> result = new ArrayList<>(); dfs(root, targetSum, new ArrayList<>(), result); return result; }
public static void main(String[] args) { TreeNode root = new TreeNode(5, new TreeNode(4, new TreeNode(11, new TreeNode(7), new TreeNode(2)), null), new TreeNode(8, new TreeNode(13), new TreeNode(4, new TreeNode(5), new TreeNode(1)))); System.out.println(pathSum(root, 22)); // [[5, 4, 11, 2], [5, 8, 4, 5]] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun dfs(node: TreeNode?, remaining: Int, path: MutableList<Int>, result: MutableList<List<Int>>) { if (node == null) return path.add(node.`val`) val newRemaining = remaining - node.`val` if (node.left == null && node.right == null && newRemaining == 0) { result.add(path.toList()) } else { dfs(node.left, newRemaining, path, result) dfs(node.right, newRemaining, path, result) } path.removeAt(path.size - 1)}
fun pathSum(root: TreeNode?, targetSum: Int): List<List<Int>> { val result = mutableListOf<List<Int>>() dfs(root, targetSum, mutableListOf(), result) return result}
fun main() { val root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, TreeNode(5), TreeNode(1)))) println(pathSum(root, 22)) // [[5, 4, 11, 2], [5, 8, 4, 5]]}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
void dfs(TreeNode? node, int remaining, List<int> path, List<List<int>> result) { if (node == null) return; path.add(node.val); final newRemaining = remaining - node.val; if (node.left == null && node.right == null && newRemaining == 0) { result.add(List.from(path)); } else { dfs(node.left, newRemaining, path, result); dfs(node.right, newRemaining, path, result); } path.removeLast();}
List<List<int>> pathSum(TreeNode? root, int targetSum) { final result = <List<int>>[]; dfs(root, targetSum, [], result); return result;}
void main() { final root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, TreeNode(5), TreeNode(1)))); print(pathSum(root, 22)); // [[5, 4, 11, 2], [5, 8, 4, 5]]}116. Tổng đường đi lớn nhất trong cây (Binary Tree Maximum Path Sum)
Độ khó: Khó · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, đường đi được định nghĩa là một chuỗi node liên tiếp qua các cạnh, không nhất thiết đi qua gốc, mỗi node xuất hiện tối đa 1 lần. Tính tổng giá trị lớn nhất có thể của một đường đi như vậy.
Ví dụ 1:
Input: root = [1,2,3]Output: 6Giải thích: Đường đi tối ưu là 2 -> 1 -> 3 với tổng 2 + 1 + 3 = 6.Ví dụ 2:
Input: root = [-10,9,20,null,null,15,7]Output: 42Giải thích: Đường đi tối ưu là 15 -> 20 -> 7 với tổng 15 + 20 + 7 = 42.Ràng buộc:
- Số lượng node từ 1 đến 3*10^4.
-1000 <= Node.val <= 1000.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def max_path_sum(root): best = float("-inf")
def gain(node): nonlocal best if node is None: return 0 left_gain = max(gain(node.left), 0) right_gain = max(gain(node.right), 0) best = max(best, node.val + left_gain + right_gain) return node.val + max(left_gain, right_gain)
gain(root) return best
root1 = TreeNode(1, TreeNode(2), TreeNode(3))print(max_path_sum(root1)) # 6
root2 = TreeNode(-10, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))print(max_path_sum(root2)) # 42#include <iostream>#include <climits>#include <algorithm>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
long long best = LLONG_MIN;
long long gain(TreeNode* node) { if (node == nullptr) return 0; long long leftGain = max(gain(node->left), 0LL); long long rightGain = max(gain(node->right), 0LL); best = max(best, node->val + leftGain + rightGain); return node->val + max(leftGain, rightGain);}
long long maxPathSum(TreeNode* root) { best = LLONG_MIN; gain(root); return best;}
int main() { TreeNode* root1 = new TreeNode(1, new TreeNode(2), new TreeNode(3)); cout << maxPathSum(root1) << endl; // 6
TreeNode* root2 = new TreeNode(-10, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); cout << maxPathSum(root2) << endl; // 42 return 0;}public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static long best = Long.MIN_VALUE;
static long gain(TreeNode node) { if (node == null) return 0; long leftGain = Math.max(gain(node.left), 0); long rightGain = Math.max(gain(node.right), 0); best = Math.max(best, node.val + leftGain + rightGain); return node.val + Math.max(leftGain, rightGain); }
static long maxPathSum(TreeNode root) { best = Long.MIN_VALUE; gain(root); return best; }
public static void main(String[] args) { TreeNode root1 = new TreeNode(1, new TreeNode(2), new TreeNode(3)); System.out.println(maxPathSum(root1)); // 6
TreeNode root2 = new TreeNode(-10, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))); System.out.println(maxPathSum(root2)); // 42 }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
var best = Long.MIN_VALUE
fun gain(node: TreeNode?): Long { if (node == null) return 0 val leftGain = maxOf(gain(node.left), 0) val rightGain = maxOf(gain(node.right), 0) best = maxOf(best, node.`val` + leftGain + rightGain) return node.`val` + maxOf(leftGain, rightGain)}
fun maxPathSum(root: TreeNode?): Long { best = Long.MIN_VALUE gain(root) return best}
fun main() { val root1 = TreeNode(1, TreeNode(2), TreeNode(3)) println(maxPathSum(root1)) // 6
val root2 = TreeNode(-10, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))) println(maxPathSum(root2)) // 42}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
int best = -1 << 62;
int gain(TreeNode? node) { if (node == null) return 0; final rawLeft = gain(node.left); final rawRight = gain(node.right); final leftGain = rawLeft > 0 ? rawLeft : 0; final rightGain = rawRight > 0 ? rawRight : 0; final total = node.val + leftGain + rightGain; if (total > best) best = total; return node.val + (leftGain > rightGain ? leftGain : rightGain);}
int maxPathSum(TreeNode? root) { best = -1 << 62; gain(root); return best;}
void main() { final root1 = TreeNode(1, TreeNode(2), TreeNode(3)); print(maxPathSum(root1)); // 6
final root2 = TreeNode(-10, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))); print(maxPathSum(root2)); // 42}117. Serialize và Deserialize cây nhị phân (Serialize and Deserialize Binary Tree)
Độ khó: Khó · Chủ đề: Cây nhị phân
Thiết kế 2 hàm serialize(root) chuyển một cây nhị phân thành một chuỗi, và deserialize(data) khôi phục lại cây từ chuỗi đó. Đảm bảo cây khôi phục giống hệt cây gốc.
Ví dụ 1:
Input: root = [1,2,3,null,null,4,5]Output: [1,2,3,null,null,4,5]Giải thích: serialize(root) rồi deserialize(chuỗi đó) phải cho lại cây có cùng cấu trúc và giá trị như root ban đầu.Ràng buộc:
- Số lượng node từ 0 đến 10^4.
-1000 <= Node.val <= 1000.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def serialize(root): values = []
def dfs(node): if node is None: values.append("null") return values.append(str(node.val)) dfs(node.left) dfs(node.right)
dfs(root) return ",".join(values)
def deserialize(data): values = iter(data.split(","))
def build(): val = next(values) if val == "null": return None node = TreeNode(int(val)) node.left = build() node.right = build() return node
return build()
def to_level_order(root): result = [] queue = [root] while any(node is not None for node in queue): node = queue.pop(0) if node is None: result.append(None) continue result.append(node.val) queue.append(node.left) queue.append(node.right) while result and result[-1] is None: result.pop() return result
root = TreeNode(1, TreeNode(2), TreeNode(3, TreeNode(4), TreeNode(5)))data = serialize(root)restored = deserialize(data)print(to_level_order(restored)) # [1, 2, 3, None, None, 4, 5]#include <iostream>#include <sstream>#include <vector>#include <string>#include <deque>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}};
void dfsSerialize(TreeNode* node, vector<string>& values) { if (node == nullptr) { values.push_back("null"); return; } values.push_back(to_string(node->val)); dfsSerialize(node->left, values); dfsSerialize(node->right, values);}
string serialize(TreeNode* root) { vector<string> values; dfsSerialize(root, values); string result; for (size_t i = 0; i < values.size(); i++) { if (i > 0) result += ","; result += values[i]; } return result;}
TreeNode* buildTree(deque<string>& values) { string val = values.front(); values.pop_front(); if (val == "null") return nullptr; TreeNode* node = new TreeNode(stoi(val)); node->left = buildTree(values); node->right = buildTree(values); return node;}
TreeNode* deserialize(const string& data) { deque<string> values; stringstream ss(data); string token; while (getline(ss, token, ',')) values.push_back(token); return buildTree(values);}
vector<string> toLevelOrder(TreeNode* root) { vector<string> result; deque<TreeNode*> queue; queue.push_back(root); bool anyNotNull = root != nullptr; while (anyNotNull) { anyNotNull = false; int n = queue.size(); for (int i = 0; i < n; i++) { TreeNode* node = queue.front(); queue.pop_front(); if (node == nullptr) { result.push_back("null"); queue.push_back(nullptr); queue.push_back(nullptr); } else { result.push_back(to_string(node->val)); queue.push_back(node->left); queue.push_back(node->right); if (node->left != nullptr || node->right != nullptr) anyNotNull = true; } } } while (!result.empty() && result.back() == "null") result.pop_back(); return result;}
int main() { TreeNode* root = new TreeNode(1); root->left = new TreeNode(2); root->right = new TreeNode(3); root->right->left = new TreeNode(4); root->right->right = new TreeNode(5);
string data = serialize(root); TreeNode* restored = deserialize(data); for (auto& v : toLevelOrder(restored)) cout << v << " "; cout << endl; // 1 2 3 null null 4 5 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } }
static void dfsSerialize(TreeNode node, List<String> values) { if (node == null) { values.add("null"); return; } values.add(String.valueOf(node.val)); dfsSerialize(node.left, values); dfsSerialize(node.right, values); }
static String serialize(TreeNode root) { List<String> values = new ArrayList<>(); dfsSerialize(root, values); return String.join(",", values); }
static TreeNode buildTree(Deque<String> values) { String val = values.poll(); if (val.equals("null")) return null; TreeNode node = new TreeNode(Integer.parseInt(val)); node.left = buildTree(values); node.right = buildTree(values); return node; }
static TreeNode deserialize(String data) { Deque<String> values = new ArrayDeque<>(Arrays.asList(data.split(","))); return buildTree(values); }
static List<String> toLevelOrder(TreeNode root) { List<String> result = new ArrayList<>(); Deque<TreeNode> queue = new ArrayDeque<>(); queue.add(root); boolean anyNotNull = root != null; while (anyNotNull) { anyNotNull = false; int n = queue.size(); for (int i = 0; i < n; i++) { TreeNode node = queue.poll(); if (node == null) { result.add("null"); queue.add(null); queue.add(null); } else { result.add(String.valueOf(node.val)); queue.add(node.left); queue.add(node.right); if (node.left != null || node.right != null) anyNotNull = true; } } } while (!result.isEmpty() && result.get(result.size() - 1).equals("null")) { result.remove(result.size() - 1); } return result; }
public static void main(String[] args) { TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(3); root.right.left = new TreeNode(4); root.right.right = new TreeNode(5);
String data = serialize(root); TreeNode restored = deserialize(data); System.out.println(toLevelOrder(restored)); // [1, 2, 3, null, null, 4, 5] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun dfsSerialize(node: TreeNode?, values: MutableList<String>) { if (node == null) { values.add("null") return } values.add(node.`val`.toString()) dfsSerialize(node.left, values) dfsSerialize(node.right, values)}
fun serialize(root: TreeNode?): String { val values = mutableListOf<String>() dfsSerialize(root, values) return values.joinToString(",")}
fun buildTree(values: ArrayDeque<String>): TreeNode? { val v = values.removeFirst() if (v == "null") return null val node = TreeNode(v.toInt()) node.left = buildTree(values) node.right = buildTree(values) return node}
fun deserialize(data: String): TreeNode? { val values = ArrayDeque(data.split(",")) return buildTree(values)}
fun toLevelOrder(root: TreeNode?): List<String> { val result = mutableListOf<String>() val queue = ArrayDeque<TreeNode?>() queue.add(root) var anyNotNull = root != null while (anyNotNull) { anyNotNull = false val n = queue.size repeat(n) { val node = queue.removeFirst() if (node == null) { result.add("null") queue.add(null) queue.add(null) } else { result.add(node.`val`.toString()) queue.add(node.left) queue.add(node.right) if (node.left != null || node.right != null) anyNotNull = true } } } while (result.isNotEmpty() && result.last() == "null") result.removeAt(result.size - 1) return result}
fun main() { val root = TreeNode(1, TreeNode(2), TreeNode(3, TreeNode(4), TreeNode(5))) val data = serialize(root) val restored = deserialize(data) println(toLevelOrder(restored)) // [1, 2, 3, null, null, 4, 5]}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
void dfsSerialize(TreeNode? node, List<String> values) { if (node == null) { values.add("null"); return; } values.add(node.val.toString()); dfsSerialize(node.left, values); dfsSerialize(node.right, values);}
String serialize(TreeNode? root) { final values = <String>[]; dfsSerialize(root, values); return values.join(",");}
TreeNode? buildTree(List<String> values) { final v = values.removeAt(0); if (v == "null") return null; final node = TreeNode(int.parse(v)); node.left = buildTree(values); node.right = buildTree(values); return node;}
TreeNode? deserialize(String data) { final values = data.split(","); return buildTree(values);}
List<String> toLevelOrder(TreeNode? root) { final result = <String>[]; final queue = <TreeNode?>[root]; var anyNotNull = root != null; while (anyNotNull) { anyNotNull = false; final n = queue.length; for (var i = 0; i < n; i++) { final node = queue.removeAt(0); if (node == null) { result.add("null"); queue.add(null); queue.add(null); } else { result.add(node.val.toString()); queue.add(node.left); queue.add(node.right); if (node.left != null || node.right != null) anyNotNull = true; } } } while (result.isNotEmpty && result.last == "null") result.removeLast(); return result;}
void main() { final root = TreeNode(1, TreeNode(2), TreeNode(3, TreeNode(4), TreeNode(5))); final data = serialize(root); final restored = deserialize(data); print(toLevelOrder(restored)); // [1, 2, 3, null, null, 4, 5]}118. Nhìn cây từ bên phải (Binary Tree Right Side View)
Độ khó: Khó · Chủ đề: Cây nhị phân
Cho gốc của một cây nhị phân, tưởng tượng bạn đứng ở phía bên phải của cây, trả về danh sách giá trị các node bạn nhìn thấy được, xếp theo thứ tự từ trên xuống (tại mỗi tầng, node ngoài cùng bên phải là node nhìn thấy được).
Ví dụ 1:
Input: root = [1,2,3,null,5,null,4]Output: [1, 3, 4]Ví dụ 2:
Input: root = [1,null,3]Output: [1, 3]Ràng buộc:
- Số lượng node từ 0 đến 100.
-100 <= Node.val <= 100.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def right_side_view(root): if root is None: return []
result = [] queue = [root] while queue: result.append(queue[-1].val) next_queue = [] for node in queue: if node.left: next_queue.append(node.left) if node.right: next_queue.append(node.right) queue = next_queue
return result
root1 = TreeNode(1, TreeNode(2, None, TreeNode(5)), TreeNode(3, None, TreeNode(4)))print(right_side_view(root1)) # [1, 3, 4]
root2 = TreeNode(1, None, TreeNode(3))print(right_side_view(root2)) # [1, 3]#include <iostream>#include <vector>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
vector<int> rightSideView(TreeNode* root) { if (root == nullptr) return {};
vector<int> result; vector<TreeNode*> queue = {root}; while (!queue.empty()) { result.push_back(queue.back()->val); vector<TreeNode*> nextQueue; for (auto node : queue) { if (node->left) nextQueue.push_back(node->left); if (node->right) nextQueue.push_back(node->right); } queue = nextQueue; } return result;}
int main() { TreeNode* root1 = new TreeNode(1, new TreeNode(2, nullptr, new TreeNode(5)), new TreeNode(3, nullptr, new TreeNode(4))); for (int x : rightSideView(root1)) cout << x << " "; cout << endl; // 1 3 4
TreeNode* root2 = new TreeNode(1, nullptr, new TreeNode(3)); for (int x : rightSideView(root2)) cout << x << " "; cout << endl; // 1 3 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static List<Integer> rightSideView(TreeNode root) { if (root == null) return new ArrayList<>();
List<Integer> result = new ArrayList<>(); List<TreeNode> queue = new ArrayList<>(List.of(root)); while (!queue.isEmpty()) { result.add(queue.get(queue.size() - 1).val); List<TreeNode> nextQueue = new ArrayList<>(); for (TreeNode node : queue) { if (node.left != null) nextQueue.add(node.left); if (node.right != null) nextQueue.add(node.right); } queue = nextQueue; } return result; }
public static void main(String[] args) { TreeNode root1 = new TreeNode(1, new TreeNode(2, null, new TreeNode(5)), new TreeNode(3, null, new TreeNode(4))); System.out.println(rightSideView(root1)); // [1, 3, 4]
TreeNode root2 = new TreeNode(1, null, new TreeNode(3)); System.out.println(rightSideView(root2)); // [1, 3] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
fun rightSideView(root: TreeNode?): List<Int> { if (root == null) return emptyList()
val result = mutableListOf<Int>() var queue = mutableListOf(root) while (queue.isNotEmpty()) { result.add(queue.last().`val`) val nextQueue = mutableListOf<TreeNode>() for (node in queue) { node.left?.let { nextQueue.add(it) } node.right?.let { nextQueue.add(it) } } queue = nextQueue } return result}
fun main() { val root1 = TreeNode(1, TreeNode(2, null, TreeNode(5)), TreeNode(3, null, TreeNode(4))) println(rightSideView(root1)) // [1, 3, 4]
val root2 = TreeNode(1, null, TreeNode(3)) println(rightSideView(root2)) // [1, 3]}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
List<int> rightSideView(TreeNode? root) { if (root == null) return [];
final result = <int>[]; var queue = [root]; while (queue.isNotEmpty) { result.add(queue.last.val); final nextQueue = <TreeNode>[]; for (var node in queue) { if (node.left != null) nextQueue.add(node.left!); if (node.right != null) nextQueue.add(node.right!); } queue = nextQueue; } return result;}
void main() { final root1 = TreeNode(1, TreeNode(2, null, TreeNode(5)), TreeNode(3, null, TreeNode(4))); print(rightSideView(root1)); // [1, 3, 4]
final root2 = TreeNode(1, null, TreeNode(3)); print(rightSideView(root2)); // [1, 3]}119. Sửa lại BST bị hoán đổi 2 node (Recover Binary Search Tree)
Độ khó: Khó · Chủ đề: BST
Cho gốc của một cây BST, đúng 2 node của nó đã bị hoán đổi giá trị cho nhau một cách nhầm lẫn. Sửa lại cây (in-place, không tạo cây mới) để nó trở thành BST hợp lệ trở lại, không dùng cấu trúc dữ liệu phụ để lưu toàn bộ giá trị.
Ví dụ 1:
Input: root = [1,3,null,null,2]Output: [3,1,null,null,2]Giải thích: Node 3 và node 1 bị hoán đổi so với BST đúng.Ràng buộc:
- Số lượng node từ 2 đến 1000.
-2^31 <= Node.val <= 2^31 - 1.
Xem đáp án
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
def recover_tree(root): first = second = prev = None
def inorder(node): nonlocal first, second, prev if node is None: return inorder(node.left) if prev is not None and prev.val > node.val: if first is None: first = prev second = node prev = node inorder(node.right)
inorder(root) if first and second: first.val, second.val = second.val, first.val
def to_level_order(root): result = [] queue = [root] while any(node is not None for node in queue): node = queue.pop(0) if node is None: result.append(None) continue result.append(node.val) queue.append(node.left) queue.append(node.right) while result and result[-1] is None: result.pop() return result
root = TreeNode(1, TreeNode(3, None, TreeNode(2)))recover_tree(root)print(to_level_order(root)) # [3, 1, None, None, 2]#include <iostream>#include <vector>using namespace std;
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int v, TreeNode* l = nullptr, TreeNode* r = nullptr) : val(v), left(l), right(r) {}};
void inorder(TreeNode* node, TreeNode*& first, TreeNode*& second, TreeNode*& prev) { if (node == nullptr) return; inorder(node->left, first, second, prev); if (prev != nullptr && prev->val > node->val) { if (first == nullptr) first = prev; second = node; } prev = node; inorder(node->right, first, second, prev);}
void recoverTree(TreeNode* root) { TreeNode *first = nullptr, *second = nullptr, *prev = nullptr; inorder(root, first, second, prev); if (first && second) swap(first->val, second->val);}
vector<string> toLevelOrder(TreeNode* root) { vector<string> result; vector<TreeNode*> queue = {root}; bool anyNotNull = root != nullptr; while (anyNotNull) { anyNotNull = false; vector<TreeNode*> next; for (auto node : queue) { if (node == nullptr) { result.push_back("None"); next.push_back(nullptr); next.push_back(nullptr); } else { result.push_back(to_string(node->val)); next.push_back(node->left); next.push_back(node->right); if (node->left || node->right) anyNotNull = true; } } queue = next; } while (!result.empty() && result.back() == "None") result.pop_back(); return result;}
int main() { TreeNode* root = new TreeNode(1, new TreeNode(3, nullptr, new TreeNode(2))); recoverTree(root); for (auto& v : toLevelOrder(root)) cout << v << " "; cout << endl; // 3 1 None None 2 return 0;}import java.util.*;
public class Main { static class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
static TreeNode first, second, prev;
static void inorder(TreeNode node) { if (node == null) return; inorder(node.left); if (prev != null && prev.val > node.val) { if (first == null) first = prev; second = node; } prev = node; inorder(node.right); }
static void recoverTree(TreeNode root) { first = second = prev = null; inorder(root); if (first != null && second != null) { int tmp = first.val; first.val = second.val; second.val = tmp; } }
static List<String> toLevelOrder(TreeNode root) { List<String> result = new ArrayList<>(); List<TreeNode> queue = new ArrayList<>(Collections.singletonList(root)); boolean anyNotNull = root != null; while (anyNotNull) { anyNotNull = false; List<TreeNode> next = new ArrayList<>(); for (TreeNode node : queue) { if (node == null) { result.add("None"); next.add(null); next.add(null); } else { result.add(String.valueOf(node.val)); next.add(node.left); next.add(node.right); if (node.left != null || node.right != null) anyNotNull = true; } } queue = next; } while (!result.isEmpty() && result.get(result.size() - 1).equals("None")) { result.remove(result.size() - 1); } return result; }
public static void main(String[] args) { TreeNode root = new TreeNode(1, new TreeNode(3, null, new TreeNode(2)), null); recoverTree(root); System.out.println(toLevelOrder(root)); // [3, 1, None, None, 2] }}class TreeNode(var `val`: Int, var left: TreeNode? = null, var right: TreeNode? = null)
var first: TreeNode? = nullvar second: TreeNode? = nullvar prev: TreeNode? = null
fun inorder(node: TreeNode?) { if (node == null) return inorder(node.left) if (prev != null && prev!!.`val` > node.`val`) { if (first == null) first = prev second = node } prev = node inorder(node.right)}
fun recoverTree(root: TreeNode?) { first = null; second = null; prev = null inorder(root) if (first != null && second != null) { val tmp = first!!.`val` first!!.`val` = second!!.`val` second!!.`val` = tmp }}
fun toLevelOrder(root: TreeNode?): List<String> { val result = mutableListOf<String>() var queue = mutableListOf(root) var anyNotNull = root != null while (anyNotNull) { anyNotNull = false val next = mutableListOf<TreeNode?>() for (node in queue) { if (node == null) { result.add("None") next.add(null) next.add(null) } else { result.add(node.`val`.toString()) next.add(node.left) next.add(node.right) if (node.left != null || node.right != null) anyNotNull = true } } queue = next } while (result.isNotEmpty() && result.last() == "None") result.removeAt(result.size - 1) return result}
fun main() { val root = TreeNode(1, TreeNode(3, null, TreeNode(2))) recoverTree(root) println(toLevelOrder(root)) // [3, 1, None, None, 2]}class TreeNode { int val; TreeNode? left; TreeNode? right; TreeNode(this.val, [this.left, this.right]);}
TreeNode? first, second, prev;
void inorder(TreeNode? node) { if (node == null) return; inorder(node.left); if (prev != null && prev!.val > node.val) { first ??= prev; second = node; } prev = node; inorder(node.right);}
void recoverTree(TreeNode? root) { first = null; second = null; prev = null; inorder(root); if (first != null && second != null) { final tmp = first!.val; first!.val = second!.val; second!.val = tmp; }}
List<String> toLevelOrder(TreeNode? root) { final result = <String>[]; var queue = <TreeNode?>[root]; var anyNotNull = root != null; while (anyNotNull) { anyNotNull = false; final next = <TreeNode?>[]; for (var node in queue) { if (node == null) { result.add("None"); next.add(null); next.add(null); } else { result.add(node.val.toString()); next.add(node.left); next.add(node.right); if (node.left != null || node.right != null) anyNotNull = true; } } queue = next; } while (result.isNotEmpty && result.last == "None") result.removeLast(); return result;}
void main() { final root = TreeNode(1, TreeNode(3, null, TreeNode(2))); recoverTree(root); print(toLevelOrder(root)); // [3, 1, None, None, 2]}120. Đếm số phần tử nhỏ hơn bên phải (Count of Smaller Numbers After Self)
Độ khó: Khó · Chủ đề: BST
Cho mảng số nguyên nums, với mỗi phần tử nums[i], đếm số lượng phần tử nhỏ hơn nó nằm bên phải nó trong mảng. Trả về mảng kết quả counts cùng độ dài.
Ví dụ 1:
Input: nums = [5,2,6,1]Output: [2, 1, 1, 0]Giải thích: Bên phải số 5 có 2 và 1 nhỏ hơn (2 phần tử); bên phải số 2 có 1 nhỏ hơn (1 phần tử); bên phải số 6 có 1 nhỏ hơn (1 phần tử); số 1 không có phần tử nào bên phải.Ràng buộc:
1 <= nums.length <= 10^5.-10^4 <= nums[i] <= 10^4.
Xem đáp án
class BSTNode: def __init__(self, val): self.val = val self.left = None self.right = None # Số lượng node có giá trị nhỏ hơn val đã được chèn vào cây con trái self.left_count = 0 # Số lần val xuất hiện (để xử lý trùng lặp) self.duplicate_count = 1
def insert(node, val): """Chèn val vào cây, trả về (node gốc mới, số phần tử nhỏ hơn val đã có trong cây).""" if node is None: return BSTNode(val), 0
if val == node.val: node.duplicate_count += 1 return node, node.left_count elif val < node.val: node.left_count += 1 node.left, smaller = insert(node.left, val) return node, smaller else: node.right, smaller = insert(node.right, val) return node, smaller + node.left_count + node.duplicate_count
def count_smaller(nums): root = None counts = [0] * len(nums) for i in range(len(nums) - 1, -1, -1): root, smaller = insert(root, nums[i]) counts[i] = smaller return counts
print(count_smaller([5, 2, 6, 1])) # [2, 1, 1, 0]#include <iostream>#include <vector>using namespace std;
struct BSTNode { int val; BSTNode* left; BSTNode* right; int leftCount; int duplicateCount; BSTNode(int v) : val(v), left(nullptr), right(nullptr), leftCount(0), duplicateCount(1) {}};
BSTNode* insert(BSTNode* node, int val, int& smaller) { if (node == nullptr) { smaller = 0; return new BSTNode(val); } if (val == node->val) { node->duplicateCount++; smaller = node->leftCount; } else if (val < node->val) { node->leftCount++; node->left = insert(node->left, val, smaller); } else { node->right = insert(node->right, val, smaller); smaller += node->leftCount + node->duplicateCount; } return node;}
vector<int> countSmaller(vector<int>& nums) { BSTNode* root = nullptr; vector<int> counts(nums.size()); for (int i = (int)nums.size() - 1; i >= 0; i--) { int smaller; root = insert(root, nums[i], smaller); counts[i] = smaller; } return counts;}
int main() { vector<int> nums = {5, 2, 6, 1}; for (int x : countSmaller(nums)) cout << x << " "; cout << endl; // 2 1 1 0 return 0;}import java.util.*;
public class Main { static class BSTNode { int val; BSTNode left, right; int leftCount = 0; int duplicateCount = 1; BSTNode(int val) { this.val = val; } }
static int smaller;
static BSTNode insert(BSTNode node, int val) { if (node == null) { smaller = 0; return new BSTNode(val); } if (val == node.val) { node.duplicateCount++; smaller = node.leftCount; } else if (val < node.val) { node.leftCount++; node.left = insert(node.left, val); } else { node.right = insert(node.right, val); smaller += node.leftCount + node.duplicateCount; } return node; }
static int[] countSmaller(int[] nums) { BSTNode root = null; int[] counts = new int[nums.length]; for (int i = nums.length - 1; i >= 0; i--) { root = insert(root, nums[i]); counts[i] = smaller; } return counts; }
public static void main(String[] args) { int[] nums = {5, 2, 6, 1}; System.out.println(Arrays.toString(countSmaller(nums))); // [2, 1, 1, 0] }}class BSTNode(val v: Int) { var left: BSTNode? = null var right: BSTNode? = null var leftCount = 0 var duplicateCount = 1}
var smaller = 0
fun insert(node: BSTNode?, value: Int): BSTNode { if (node == null) { smaller = 0 return BSTNode(value) } if (value == node.v) { node.duplicateCount++ smaller = node.leftCount } else if (value < node.v) { node.leftCount++ node.left = insert(node.left, value) } else { node.right = insert(node.right, value) smaller += node.leftCount + node.duplicateCount } return node}
fun countSmaller(nums: List<Int>): List<Int> { var root: BSTNode? = null val counts = MutableList(nums.size) { 0 } for (i in nums.indices.reversed()) { root = insert(root, nums[i]) counts[i] = smaller } return counts}
fun main() { val nums = listOf(5, 2, 6, 1) println(countSmaller(nums)) // [2, 1, 1, 0]}class BSTNode { int val; BSTNode? left; BSTNode? right; int leftCount = 0; int duplicateCount = 1; BSTNode(this.val);}
int smaller = 0;
BSTNode insert(BSTNode? node, int val) { if (node == null) { smaller = 0; return BSTNode(val); } if (val == node.val) { node.duplicateCount++; smaller = node.leftCount; } else if (val < node.val) { node.leftCount++; node.left = insert(node.left, val); } else { node.right = insert(node.right, val); smaller += node.leftCount + node.duplicateCount; } return node;}
List<int> countSmaller(List<int> nums) { BSTNode? root; final counts = List<int>.filled(nums.length, 0); for (var i = nums.length - 1; i >= 0; i--) { root = insert(root, nums[i]); counts[i] = smaller; } return counts;}
void main() { final nums = [5, 2, 6, 1]; print(countSmaller(nums)); // [2, 1, 1, 0]}Nhóm 7: Sắp xếp & Tìm kiếm nhị phân
Phần tiêu đề “Nhóm 7: Sắp xếp & Tìm kiếm nhị phân”121. Tìm kiếm nhị phân (Binary Search)
Độ khó: Dễ · Chủ đề: Tìm kiếm nhị phân
Cho một mảng số nguyên nums đã được sắp xếp tăng dần (không có phần tử trùng lặp) và một số target. Trả về chỉ số (index) của target trong nums, hoặc -1 nếu không tìm thấy. Yêu cầu độ phức tạp O(log n).
Ví dụ 1:
Input: nums = [-1, 0, 3, 5, 9, 12], target = 9Output: 4Giải thích: 9 xuất hiện ở nums[4]Ví dụ 2:
Input: nums = [-1, 0, 3, 5, 9, 12], target = 2Output: -1Giải thích: 2 không có trong nums nên trả về -1Ràng buộc:
1 <= len(nums) <= 10^4numsđã sắp xếp tăng dần, các phần tử phân biệt- Bắt buộc giải với độ phức tạp
O(log n)
Xem đáp án
def search(nums, target): # Binary search - O(log n) left, right = 0, len(nums) - 1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return -1
print(search([-1, 0, 3, 5, 9, 12], 9)) # 4print(search([-1, 0, 3, 5, 9, 12], 2)) # -1#include <iostream>#include <vector>using namespace std;
int search(vector<int>& nums, int target) { int left = 0, right = (int)nums.size() - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1;}
int main() { vector<int> nums = {-1, 0, 3, 5, 9, 12}; cout << search(nums, 9) << endl; // 4 cout << search(nums, 2) << endl; // -1 return 0;}public class Main { static int search(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1; }
public static void main(String[] args) { int[] nums = {-1, 0, 3, 5, 9, 12}; System.out.println(search(nums, 9)); // 4 System.out.println(search(nums, 2)); // -1 }}fun search(nums: List<Int>, target: Int): Int { var left = 0 var right = nums.size - 1 while (left <= right) { val mid = (left + right) / 2 when { nums[mid] == target -> return mid nums[mid] < target -> left = mid + 1 else -> right = mid - 1 } } return -1}
fun main() { val nums = listOf(-1, 0, 3, 5, 9, 12) println(search(nums, 9)) // 4 println(search(nums, 2)) // -1}int search(List<int> nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = (left + right) ~/ 2; if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1;}
void main() { final nums = [-1, 0, 3, 5, 9, 12]; print(search(nums, 9)); // 4 print(search(nums, 2)); // -1}122. Căn bậc hai của một số (Sqrt(x))
Độ khó: Dễ · Chủ đề: Tìm kiếm nhị phân
Cho số nguyên không âm x, trả về phần nguyên của căn bậc hai của x (làm tròn xuống). Không được dùng hàm lũy thừa hoặc math.sqrt, phải dùng tìm kiếm nhị phân.
Ví dụ 1:
Input: x = 4Output: 2Ví dụ 2:
Input: x = 8Output: 2Giải thích: căn bậc hai của 8 là 2.828..., phần nguyên là 2Ràng buộc:
0 <= x <= 2^31 - 1- Không dùng
math.sqrthoặcx ** 0.5
Xem đáp án
def my_sqrt(x): # Binary search trên khoảng [0, x] - O(log x) left, right = 0, x answer = 0 while left <= right: mid = (left + right) // 2 if mid * mid <= x: answer = mid left = mid + 1 else: right = mid - 1 return answer
print(my_sqrt(4)) # 2print(my_sqrt(8)) # 2#include <iostream>using namespace std;
int mySqrt(int x) { long left = 0, right = x, answer = 0; while (left <= right) { long mid = (left + right) / 2; if (mid * mid <= x) { answer = mid; left = mid + 1; } else { right = mid - 1; } } return (int)answer;}
int main() { cout << mySqrt(4) << endl; // 2 cout << mySqrt(8) << endl; // 2 return 0;}public class Main { static int mySqrt(int x) { long left = 0, right = x, answer = 0; while (left <= right) { long mid = (left + right) / 2; if (mid * mid <= x) { answer = mid; left = mid + 1; } else { right = mid - 1; } } return (int) answer; }
public static void main(String[] args) { System.out.println(mySqrt(4)); // 2 System.out.println(mySqrt(8)); // 2 }}fun mySqrt(x: Int): Int { var left = 0L var right = x.toLong() var answer = 0L while (left <= right) { val mid = (left + right) / 2 if (mid * mid <= x) { answer = mid left = mid + 1 } else { right = mid - 1 } } return answer.toInt()}
fun main() { println(mySqrt(4)) // 2 println(mySqrt(8)) // 2}int mySqrt(int x) { int left = 0, right = x, answer = 0; while (left <= right) { int mid = left + (right - left) ~/ 2; if (mid * mid <= x) { answer = mid; left = mid + 1; } else { right = mid - 1; } } return answer;}
void main() { print(mySqrt(4)); // 2 print(mySqrt(8)); // 2}123. Phiên bản lỗi đầu tiên (First Bad Version)
Độ khó: Dễ · Chủ đề: Tìm kiếm nhị phân
Bạn có n phiên bản sản phẩm đánh số từ 1 đến n, đã kiểm thử tuần tự. Có một hàm is_bad_version(version) cho biết phiên bản đó có lỗi hay không; từ phiên bản lỗi đầu tiên trở đi, mọi phiên bản sau đều lỗi. Tìm phiên bản lỗi đầu tiên, gọi is_bad_version càng ít lần càng tốt.
Ví dụ 1:
Input: n = 5, phiên bản lỗi bắt đầu từ 4 (tức [False, False, False, True, True])Output: 4Ví dụ 2:
Input: n = 1, phiên bản lỗi bắt đầu từ 1Output: 1Ràng buộc:
1 <= n <= 2^31 - 1- Bắt buộc dùng tìm kiếm nhị phân, không gọi
is_bad_versioncho từng phiên bản một
Xem đáp án
def first_bad_version(n, is_bad_version): # Binary search - O(log n) lần gọi is_bad_version left, right = 1, n while left < right: mid = (left + right) // 2 if is_bad_version(mid): right = mid else: left = mid + 1 return left
bad_from = 4check = lambda v: v >= bad_fromprint(first_bad_version(5, check)) # 4#include <iostream>#include <functional>using namespace std;
int firstBadVersion(int n, function<bool(int)> isBadVersion) { int left = 1, right = n; while (left < right) { int mid = left + (right - left) / 2; if (isBadVersion(mid)) right = mid; else left = mid + 1; } return left;}
int main() { int badFrom = 4; auto check = [badFrom](int v) { return v >= badFrom; }; cout << firstBadVersion(5, check) << endl; // 4 return 0;}import java.util.function.IntPredicate;
public class Main { static int firstBadVersion(int n, IntPredicate isBadVersion) { int left = 1, right = n; while (left < right) { int mid = left + (right - left) / 2; if (isBadVersion.test(mid)) right = mid; else left = mid + 1; } return left; }
public static void main(String[] args) { int badFrom = 4; System.out.println(firstBadVersion(5, v -> v >= badFrom)); // 4 }}fun firstBadVersion(n: Int, isBadVersion: (Int) -> Boolean): Int { var left = 1 var right = n while (left < right) { val mid = left + (right - left) / 2 if (isBadVersion(mid)) right = mid else left = mid + 1 } return left}
fun main() { val badFrom = 4 println(firstBadVersion(5) { v -> v >= badFrom }) // 4}int firstBadVersion(int n, bool Function(int) isBadVersion) { int left = 1, right = n; while (left < right) { int mid = left + (right - left) ~/ 2; if (isBadVersion(mid)) { right = mid; } else { left = mid + 1; } } return left;}
void main() { const badFrom = 4; print(firstBadVersion(5, (v) => v >= badFrom)); // 4}124. Trộn 2 mảng đã sắp xếp tại chỗ (Merge Sorted Array)
Độ khó: Dễ · Chủ đề: Sắp xếp
Cho 2 mảng đã sắp xếp tăng dần nums1 (có đủ khoảng trống ở cuối) và nums2, với m, n là số phần tử thực sự của mỗi mảng. Trộn nums2 vào nums1 sao cho nums1 trở thành một mảng đã sắp xếp, thực hiện tại chỗ (in-place).
Ví dụ 1:
Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3Output: [1,2,2,3,5,6]Ví dụ 2:
Input: nums1 = [1], m = 1, nums2 = [], n = 0Output: [1]Giải thích: nums2 rỗng nên nums1 giữ nguyênRàng buộc:
nums1.length == m + n,nums2.length == nnums1,nums2đã sắp xếp tăng dần
Xem đáp án
def merge(nums1, m, nums2, n): # Trộn từ cuối về đầu - O(m + n), không cần mảng phụ i, j, k = m - 1, n - 1, m + n - 1 while j >= 0: if i >= 0 and nums1[i] > nums2[j]: nums1[k] = nums1[i] i -= 1 else: nums1[k] = nums2[j] j -= 1 k -= 1 return nums1
print(merge([1, 2, 3, 0, 0, 0], 3, [2, 5, 6], 3)) # [1, 2, 2, 3, 5, 6]print(merge([1], 1, [], 0)) # [1]#include <iostream>#include <vector>using namespace std;
vector<int> merge(vector<int> nums1, int m, vector<int> nums2, int n) { int i = m - 1, j = n - 1, k = m + n - 1; while (j >= 0) { if (i >= 0 && nums1[i] > nums2[j]) { nums1[k] = nums1[i]; i--; } else { nums1[k] = nums2[j]; j--; } k--; } return nums1;}
int main() { vector<int> nums1 = {1, 2, 3, 0, 0, 0}; for (int x : merge(nums1, 3, {2, 5, 6}, 3)) cout << x << " "; cout << endl; // 1 2 2 3 5 6
vector<int> nums2 = {1}; for (int x : merge(nums2, 1, {}, 0)) cout << x << " "; cout << endl; // 1 return 0;}import java.util.Arrays;
public class Main { static int[] merge(int[] nums1, int m, int[] nums2, int n) { int i = m - 1, j = n - 1, k = m + n - 1; while (j >= 0) { if (i >= 0 && nums1[i] > nums2[j]) { nums1[k] = nums1[i]; i--; } else { nums1[k] = nums2[j]; j--; } k--; } return nums1; }
public static void main(String[] args) { System.out.println(Arrays.toString(merge(new int[]{1, 2, 3, 0, 0, 0}, 3, new int[]{2, 5, 6}, 3))); // [1, 2, 2, 3, 5, 6] System.out.println(Arrays.toString(merge(new int[]{1}, 1, new int[]{}, 0))); // [1] }}fun merge(nums1: IntArray, m: Int, nums2: IntArray, n: Int): IntArray { var i = m - 1 var j = n - 1 var k = m + n - 1 while (j >= 0) { if (i >= 0 && nums1[i] > nums2[j]) { nums1[k] = nums1[i] i-- } else { nums1[k] = nums2[j] j-- } k-- } return nums1}
fun main() { println(merge(intArrayOf(1, 2, 3, 0, 0, 0), 3, intArrayOf(2, 5, 6), 3).joinToString(", ", "[", "]")) // [1, 2, 2, 3, 5, 6] println(merge(intArrayOf(1), 1, intArrayOf(), 0).joinToString(", ", "[", "]")) // [1]}List<int> merge(List<int> nums1, int m, List<int> nums2, int n) { int i = m - 1, j = n - 1, k = m + n - 1; while (j >= 0) { if (i >= 0 && nums1[i] > nums2[j]) { nums1[k] = nums1[i]; i--; } else { nums1[k] = nums2[j]; j--; } k--; } return nums1;}
void main() { print(merge([1, 2, 3, 0, 0, 0], 3, [2, 5, 6], 3)); // [1, 2, 2, 3, 5, 6] print(merge([1], 1, [], 0)); // [1]}125. Vị trí chèn trong mảng đã sắp xếp (Search Insert Position)
Độ khó: Dễ · Chủ đề: Tìm kiếm nhị phân
Cho mảng nums đã sắp xếp tăng dần, không trùng lặp, và một số target. Trả về chỉ số của target nếu tìm thấy; nếu không, trả về chỉ số mà target sẽ được chèn vào để mảng vẫn sắp xếp đúng thứ tự. Yêu cầu O(log n).
Ví dụ 1:
Input: nums = [1, 3, 5, 6], target = 5Output: 2Ví dụ 2:
Input: nums = [1, 3, 5, 6], target = 2Output: 1Giải thích: 2 nên được chèn vào giữa 1 và 3, tức chỉ số 1Ràng buộc:
1 <= len(nums) <= 10^4numssắp xếp tăng dần, các phần tử phân biệt- Bắt buộc
O(log n)
Xem đáp án
def search_insert(nums, target): # Binary search tìm vị trí chèn - O(log n) left, right = 0, len(nums) while left < right: mid = (left + right) // 2 if nums[mid] < target: left = mid + 1 else: right = mid return left
print(search_insert([1, 3, 5, 6], 5)) # 2print(search_insert([1, 3, 5, 6], 2)) # 1#include <iostream>#include <vector>using namespace std;
int searchInsert(vector<int>& nums, int target) { int left = 0, right = (int)nums.size(); while (left < right) { int mid = (left + right) / 2; if (nums[mid] < target) left = mid + 1; else right = mid; } return left;}
int main() { vector<int> nums = {1, 3, 5, 6}; cout << searchInsert(nums, 5) << endl; // 2 cout << searchInsert(nums, 2) << endl; // 1 return 0;}public class Main { static int searchInsert(int[] nums, int target) { int left = 0, right = nums.length; while (left < right) { int mid = (left + right) / 2; if (nums[mid] < target) left = mid + 1; else right = mid; } return left; }
public static void main(String[] args) { int[] nums = {1, 3, 5, 6}; System.out.println(searchInsert(nums, 5)); // 2 System.out.println(searchInsert(nums, 2)); // 1 }}fun searchInsert(nums: List<Int>, target: Int): Int { var left = 0 var right = nums.size while (left < right) { val mid = (left + right) / 2 if (nums[mid] < target) left = mid + 1 else right = mid } return left}
fun main() { val nums = listOf(1, 3, 5, 6) println(searchInsert(nums, 5)) // 2 println(searchInsert(nums, 2)) // 1}int searchInsert(List<int> nums, int target) { int left = 0, right = nums.length; while (left < right) { int mid = (left + right) ~/ 2; if (nums[mid] < target) left = mid + 1; else right = mid; } return left;}
void main() { final nums = [1, 3, 5, 6]; print(searchInsert(nums, 5)); // 2 print(searchInsert(nums, 2)); // 1}126. Tìm kiếm trong mảng xoay (Search in Rotated Sorted Array)
Độ khó: Trung bình · Chủ đề: Tìm kiếm nhị phân
Mảng nums ban đầu tăng dần, không trùng lặp, sau đó bị xoay tại một điểm chưa biết. Cho nums sau khi xoay và một số target, trả về chỉ số của target, hoặc -1 nếu không có. Yêu cầu O(log n).
Ví dụ 1:
Input: nums = [4,5,6,7,0,1,2], target = 0Output: 4Ví dụ 2:
Input: nums = [4,5,6,7,0,1,2], target = 3Output: -1Ràng buộc:
1 <= len(nums) <= 5000- Mọi giá trị trong
numslà duy nhất - Bắt buộc
O(log n)
Xem đáp án
def search_rotated(nums, target): # Binary search, mỗi bước xác định nửa nào đang "sắp xếp thật" - O(log n) left, right = 0, len(nums) - 1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: return mid if nums[left] <= nums[mid]: # nửa trái đang sắp xếp if nums[left] <= target < nums[mid]: right = mid - 1 else: left = mid + 1 else: # nửa phải đang sắp xếp if nums[mid] < target <= nums[right]: left = mid + 1 else: right = mid - 1 return -1
print(search_rotated([4, 5, 6, 7, 0, 1, 2], 0)) # 4print(search_rotated([4, 5, 6, 7, 0, 1, 2], 3)) # -1#include <iostream>#include <vector>using namespace std;
int searchRotated(vector<int>& nums, int target) { int left = 0, right = (int)nums.size() - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) return mid; if (nums[left] <= nums[mid]) { if (nums[left] <= target && target < nums[mid]) right = mid - 1; else left = mid + 1; } else { if (nums[mid] < target && target <= nums[right]) left = mid + 1; else right = mid - 1; } } return -1;}
int main() { vector<int> nums = {4, 5, 6, 7, 0, 1, 2}; cout << searchRotated(nums, 0) << endl; // 4 cout << searchRotated(nums, 3) << endl; // -1 return 0;}public class Main { static int searchRotated(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) return mid; if (nums[left] <= nums[mid]) { if (nums[left] <= target && target < nums[mid]) right = mid - 1; else left = mid + 1; } else { if (nums[mid] < target && target <= nums[right]) left = mid + 1; else right = mid - 1; } } return -1; }
public static void main(String[] args) { int[] nums = {4, 5, 6, 7, 0, 1, 2}; System.out.println(searchRotated(nums, 0)); // 4 System.out.println(searchRotated(nums, 3)); // -1 }}fun searchRotated(nums: List<Int>, target: Int): Int { var left = 0 var right = nums.size - 1 while (left <= right) { val mid = (left + right) / 2 if (nums[mid] == target) return mid if (nums[left] <= nums[mid]) { if (nums[left] <= target && target < nums[mid]) right = mid - 1 else left = mid + 1 } else { if (nums[mid] < target && target <= nums[right]) left = mid + 1 else right = mid - 1 } } return -1}
fun main() { val nums = listOf(4, 5, 6, 7, 0, 1, 2) println(searchRotated(nums, 0)) // 4 println(searchRotated(nums, 3)) // -1}int searchRotated(List<int> nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = (left + right) ~/ 2; if (nums[mid] == target) return mid; if (nums[left] <= nums[mid]) { if (nums[left] <= target && target < nums[mid]) { right = mid - 1; } else { left = mid + 1; } } else { if (nums[mid] < target && target <= nums[right]) { left = mid + 1; } else { right = mid - 1; } } } return -1;}
void main() { final nums = [4, 5, 6, 7, 0, 1, 2]; print(searchRotated(nums, 0)); // 4 print(searchRotated(nums, 3)); // -1}127. Vị trí đầu và cuối của phần tử (Find First and Last Position)
Độ khó: Trung bình · Chủ đề: Tìm kiếm nhị phân
Cho mảng nums đã sắp xếp tăng dần và số target, tìm vị trí xuất hiện đầu tiên và cuối cùng của target. Nếu không có, trả về [-1, -1]. Yêu cầu O(log n).
Ví dụ 1:
Input: nums = [5,7,7,8,8,10], target = 8Output: [3, 4]Ví dụ 2:
Input: nums = [5,7,7,8,8,10], target = 6Output: [-1, -1]Ràng buộc:
0 <= len(nums) <= 10^5numssắp xếp tăng dần- Bắt buộc
O(log n)
Xem đáp án
def search_range(nums, target): # Hai lần binary search: tìm biên trái và biên phải - O(log n) def find_bound(is_left): left, right = 0, len(nums) - 1 result = -1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: result = mid if is_left: right = mid - 1 else: left = mid + 1 elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return result
return [find_bound(True), find_bound(False)]
print(search_range([5, 7, 7, 8, 8, 10], 8)) # [3, 4]print(search_range([5, 7, 7, 8, 8, 10], 6)) # [-1, -1]#include <iostream>#include <vector>using namespace std;
int findBound(vector<int>& nums, int target, bool isLeft) { int left = 0, right = (int)nums.size() - 1, result = -1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) { result = mid; if (isLeft) right = mid - 1; else left = mid + 1; } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return result;}
vector<int> searchRange(vector<int>& nums, int target) { return {findBound(nums, target, true), findBound(nums, target, false)};}
int main() { vector<int> nums = {5, 7, 7, 8, 8, 10}; auto r1 = searchRange(nums, 8); cout << "[" << r1[0] << ", " << r1[1] << "]" << endl; // [3, 4] auto r2 = searchRange(nums, 6); cout << "[" << r2[0] << ", " << r2[1] << "]" << endl; // [-1, -1] return 0;}import java.util.Arrays;
public class Main { static int findBound(int[] nums, int target, boolean isLeft) { int left = 0, right = nums.length - 1, result = -1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) { result = mid; if (isLeft) right = mid - 1; else left = mid + 1; } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return result; }
static int[] searchRange(int[] nums, int target) { return new int[]{findBound(nums, target, true), findBound(nums, target, false)}; }
public static void main(String[] args) { int[] nums = {5, 7, 7, 8, 8, 10}; System.out.println(Arrays.toString(searchRange(nums, 8))); // [3, 4] System.out.println(Arrays.toString(searchRange(nums, 6))); // [-1, -1] }}fun findBound(nums: List<Int>, target: Int, isLeft: Boolean): Int { var left = 0 var right = nums.size - 1 var result = -1 while (left <= right) { val mid = (left + right) / 2 when { nums[mid] == target -> { result = mid if (isLeft) right = mid - 1 else left = mid + 1 } nums[mid] < target -> left = mid + 1 else -> right = mid - 1 } } return result}
fun searchRange(nums: List<Int>, target: Int): List<Int> { return listOf(findBound(nums, target, true), findBound(nums, target, false))}
fun main() { val nums = listOf(5, 7, 7, 8, 8, 10) println(searchRange(nums, 8)) // [3, 4] println(searchRange(nums, 6)) // [-1, -1]}int findBound(List<int> nums, int target, bool isLeft) { int left = 0, right = nums.length - 1, result = -1; while (left <= right) { int mid = (left + right) ~/ 2; if (nums[mid] == target) { result = mid; if (isLeft) { right = mid - 1; } else { left = mid + 1; } } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return result;}
List<int> searchRange(List<int> nums, int target) { return [findBound(nums, target, true), findBound(nums, target, false)];}
void main() { final nums = [5, 7, 7, 8, 8, 10]; print(searchRange(nums, 8)); // [3, 4] print(searchRange(nums, 6)); // [-1, -1]}128. Tìm đỉnh cực đại (Find Peak Element)
Độ khó: Trung bình · Chủ đề: Tìm kiếm nhị phân
Một “đỉnh” là phần tử lớn hơn cả hai phần tử liền kề. Cho mảng nums (coi nums[-1] = nums[n] = -infinity), tìm chỉ số của bất kỳ một đỉnh nào. Yêu cầu O(log n).
Ví dụ 1:
Input: nums = [1, 2, 3, 1]Output: 2Giải thích: nums[2] = 3 là đỉnh vì lớn hơn cả nums[1]=2 và nums[3]=1Ví dụ 2:
Input: nums = [1, 2, 1, 3, 5, 6, 4]Output: 1 hoặc 5Giải thích: nums[1]=2 là đỉnh (so với 1 và 1), nums[5]=6 cũng là đỉnh (so với 5 và 4)Ràng buộc:
1 <= len(nums) <= 1000nums[i] != nums[i+1]với mọi i liền kề hợp lệ- Bắt buộc
O(log n)
Xem đáp án
def find_peak_element(nums): # Binary search theo hướng dốc lên - O(log n) left, right = 0, len(nums) - 1 while left < right: mid = (left + right) // 2 if nums[mid] > nums[mid + 1]: right = mid else: left = mid + 1 return left
print(find_peak_element([1, 2, 3, 1])) # 2print(find_peak_element([1, 2, 1, 3, 5, 6, 4])) # 1 hoặc 5#include <iostream>#include <vector>using namespace std;
int findPeakElement(vector<int>& nums) { int left = 0, right = (int)nums.size() - 1; while (left < right) { int mid = (left + right) / 2; if (nums[mid] > nums[mid + 1]) right = mid; else left = mid + 1; } return left;}
int main() { vector<int> nums1 = {1, 2, 3, 1}; cout << findPeakElement(nums1) << endl; // 2 vector<int> nums2 = {1, 2, 1, 3, 5, 6, 4}; cout << findPeakElement(nums2) << endl; // 1 hoac 5 return 0;}public class Main { static int findPeakElement(int[] nums) { int left = 0, right = nums.length - 1; while (left < right) { int mid = (left + right) / 2; if (nums[mid] > nums[mid + 1]) right = mid; else left = mid + 1; } return left; }
public static void main(String[] args) { System.out.println(findPeakElement(new int[]{1, 2, 3, 1})); // 2 System.out.println(findPeakElement(new int[]{1, 2, 1, 3, 5, 6, 4})); // 1 hoac 5 }}fun findPeakElement(nums: List<Int>): Int { var left = 0 var right = nums.size - 1 while (left < right) { val mid = (left + right) / 2 if (nums[mid] > nums[mid + 1]) right = mid else left = mid + 1 } return left}
fun main() { println(findPeakElement(listOf(1, 2, 3, 1))) // 2 println(findPeakElement(listOf(1, 2, 1, 3, 5, 6, 4))) // 1 hoac 5}int findPeakElement(List<int> nums) { int left = 0, right = nums.length - 1; while (left < right) { int mid = (left + right) ~/ 2; if (nums[mid] > nums[mid + 1]) { right = mid; } else { left = mid + 1; } } return left;}
void main() { print(findPeakElement([1, 2, 3, 1])); // 2 print(findPeakElement([1, 2, 1, 3, 5, 6, 4])); // 1 hoac 5}129. Phần tử lớn thứ k (Kth Largest Element in an Array)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Cho mảng số nguyên nums chưa sắp xếp và số k, tìm phần tử lớn thứ k (tính theo thứ tự sắp xếp, không phải phần tử phân biệt thứ k).
Ví dụ 1:
Input: nums = [3,2,1,5,6,4], k = 2Output: 5Ví dụ 2:
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4Output: 4Ràng buộc:
1 <= k <= len(nums) <= 10^5- Nên giải với độ phức tạp tốt hơn
O(n log n)bằng heap kích thướck
Xem đáp án
import heapq
def find_kth_largest(nums, k): # Min-heap kích thước k - O(n log k) heap = [] for num in nums: heapq.heappush(heap, num) if len(heap) > k: heapq.heappop(heap) return heap[0]
print(find_kth_largest([3, 2, 1, 5, 6, 4], 2)) # 5print(find_kth_largest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)) # 4#include <iostream>#include <vector>#include <queue>using namespace std;
int findKthLargest(vector<int>& nums, int k) { priority_queue<int, vector<int>, greater<int>> heap; for (int num : nums) { heap.push(num); if ((int)heap.size() > k) heap.pop(); } return heap.top();}
int main() { vector<int> nums1 = {3, 2, 1, 5, 6, 4}; cout << findKthLargest(nums1, 2) << endl; // 5 vector<int> nums2 = {3, 2, 3, 1, 2, 4, 5, 5, 6}; cout << findKthLargest(nums2, 4) << endl; // 4 return 0;}import java.util.PriorityQueue;
public class Main { static int findKthLargest(int[] nums, int k) { PriorityQueue<Integer> heap = new PriorityQueue<>(); for (int num : nums) { heap.offer(num); if (heap.size() > k) heap.poll(); } return heap.peek(); }
public static void main(String[] args) { System.out.println(findKthLargest(new int[]{3, 2, 1, 5, 6, 4}, 2)); // 5 System.out.println(findKthLargest(new int[]{3, 2, 3, 1, 2, 4, 5, 5, 6}, 4)); // 4 }}import java.util.PriorityQueue
fun findKthLargest(nums: List<Int>, k: Int): Int { val heap = PriorityQueue<Int>() for (num in nums) { heap.offer(num) if (heap.size > k) heap.poll() } return heap.peek()}
fun main() { println(findKthLargest(listOf(3, 2, 1, 5, 6, 4), 2)) // 5 println(findKthLargest(listOf(3, 2, 3, 1, 2, 4, 5, 5, 6), 4)) // 4}int findKthLargest(List<int> nums, int k) { final sorted = List<int>.from(nums)..sort((a, b) => b.compareTo(a)); return sorted[k - 1];}
void main() { print(findKthLargest([3, 2, 1, 5, 6, 4], 2)); // 5 print(findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)); // 4}130. Gộp các khoảng chồng lấp (Merge Intervals)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Cho danh sách các khoảng intervals, trong đó intervals[i] = [start_i, end_i]. Gộp tất cả các khoảng chồng lấp lên nhau, trả về danh sách khoảng không chồng lấp bao phủ toàn bộ các khoảng ban đầu.
Ví dụ 1:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]Output: [[1,6],[8,10],[15,18]]Giải thích: [1,3] và [2,6] chồng lấp, gộp thành [1,6]Ví dụ 2:
Input: intervals = [[1,4],[4,5]]Output: [[1,5]]Giải thích: [1,4] và [4,5] được coi là chồng lấp vì chạm nhau tại 4Ràng buộc:
1 <= len(intervals) <= 10^4start_i <= end_i
Xem đáp án
def merge_intervals(intervals): # Sắp xếp theo điểm bắt đầu rồi duyệt gộp - O(n log n) intervals.sort(key=lambda x: x[0]) result = [intervals[0]]
for start, end in intervals[1:]: last_end = result[-1][1] if start <= last_end: result[-1][1] = max(last_end, end) else: result.append([start, end])
return result
print(merge_intervals([[1, 3], [2, 6], [8, 10], [15, 18]])) # [[1, 6], [8, 10], [15, 18]]print(merge_intervals([[1, 4], [4, 5]])) # [[1, 5]]#include <iostream>#include <vector>#include <algorithm>using namespace std;
vector<vector<int>> mergeIntervals(vector<vector<int>> intervals) { sort(intervals.begin(), intervals.end()); vector<vector<int>> result = {intervals[0]};
for (size_t i = 1; i < intervals.size(); i++) { int start = intervals[i][0], end = intervals[i][1]; int& lastEnd = result.back()[1]; if (start <= lastEnd) { lastEnd = max(lastEnd, end); } else { result.push_back({start, end}); } } return result;}
int main() { for (auto& iv : mergeIntervals({{1, 3}, {2, 6}, {8, 10}, {15, 18}})) { cout << "[" << iv[0] << "," << iv[1] << "] "; } cout << endl; // [1,6] [8,10] [15,18]
for (auto& iv : mergeIntervals({{1, 4}, {4, 5}})) { cout << "[" << iv[0] << "," << iv[1] << "] "; } cout << endl; // [1,5] return 0;}import java.util.*;
public class Main { static int[][] mergeIntervals(int[][] intervals) { Arrays.sort(intervals, (a, b) -> a[0] - b[0]); List<int[]> result = new ArrayList<>(); result.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) { int start = intervals[i][0], end = intervals[i][1]; int[] last = result.get(result.size() - 1); if (start <= last[1]) { last[1] = Math.max(last[1], end); } else { result.add(intervals[i]); } } return result.toArray(new int[0][]); }
public static void main(String[] args) { for (int[] iv : mergeIntervals(new int[][]{{1, 3}, {2, 6}, {8, 10}, {15, 18}})) { System.out.print(Arrays.toString(iv) + " "); } System.out.println(); // [1, 6] [8, 10] [15, 18]
for (int[] iv : mergeIntervals(new int[][]{{1, 4}, {4, 5}})) { System.out.print(Arrays.toString(iv) + " "); } System.out.println(); // [1, 5] }}fun mergeIntervals(intervals: List<IntArray>): List<IntArray> { val sorted = intervals.sortedBy { it[0] } val result = mutableListOf(sorted[0])
for (i in 1 until sorted.size) { val (start, end) = sorted[i] val last = result.last() if (start <= last[1]) { last[1] = maxOf(last[1], end) } else { result.add(sorted[i]) } } return result}
fun main() { val r1 = mergeIntervals(listOf(intArrayOf(1, 3), intArrayOf(2, 6), intArrayOf(8, 10), intArrayOf(15, 18))) println(r1.joinToString(" ") { "[${it[0]},${it[1]}]" }) // [1,6] [8,10] [15,18]
val r2 = mergeIntervals(listOf(intArrayOf(1, 4), intArrayOf(4, 5))) println(r2.joinToString(" ") { "[${it[0]},${it[1]}]" }) // [1,5]}List<List<int>> mergeIntervals(List<List<int>> intervals) { final sorted = List<List<int>>.from(intervals)..sort((a, b) => a[0].compareTo(b[0])); final result = [sorted[0]];
for (var i = 1; i < sorted.length; i++) { final start = sorted[i][0], end = sorted[i][1]; final last = result.last; if (start <= last[1]) { last[1] = last[1] > end ? last[1] : end; } else { result.add(sorted[i]); } } return result;}
void main() { print(mergeIntervals([[1, 3], [2, 6], [8, 10], [15, 18]])); // [[1, 6], [8, 10], [15, 18]] print(mergeIntervals([[1, 4], [4, 5]])); // [[1, 5]]}131. Chèn khoảng mới (Insert Interval)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Cho danh sách các khoảng intervals không chồng lấp, đã sắp xếp theo điểm bắt đầu, và một khoảng mới new_interval. Chèn new_interval vào danh sách, gộp lại nếu cần, sao cho các khoảng vẫn không chồng lấp và vẫn sắp xếp.
Ví dụ 1:
Input: intervals = [[1,3],[6,9]], new_interval = [2,5]Output: [[1,5],[6,9]]Ví dụ 2:
Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], new_interval = [4,8]Output: [[1,2],[3,10],[12,16]]Giải thích: [4,8] chồng lấp với [3,5],[6,7],[8,10], gộp thành [3,10]Ràng buộc:
0 <= len(intervals) <= 10^4intervalskhông chồng lấp và đã sắp xếp theostart
Xem đáp án
def insert_interval(intervals, new_interval): # Duyệt một lượt: khoảng trước, khoảng chồng lấp gộp lại, khoảng sau - O(n) result = [] i, n = 0, len(intervals)
while i < n and intervals[i][1] < new_interval[0]: result.append(intervals[i]) i += 1
while i < n and intervals[i][0] <= new_interval[1]: new_interval[0] = min(new_interval[0], intervals[i][0]) new_interval[1] = max(new_interval[1], intervals[i][1]) i += 1 result.append(new_interval)
while i < n: result.append(intervals[i]) i += 1
return result
print(insert_interval([[1, 3], [6, 9]], [2, 5])) # [[1, 5], [6, 9]]print(insert_interval([[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]], [4, 8])) # [[1, 2], [3, 10], [12, 16]]#include <iostream>#include <vector>#include <algorithm>using namespace std;
vector<vector<int>> insertInterval(vector<vector<int>>& intervals, vector<int> newInterval) { vector<vector<int>> result; int i = 0, n = (int)intervals.size();
while (i < n && intervals[i][1] < newInterval[0]) { result.push_back(intervals[i]); i++; }
while (i < n && intervals[i][0] <= newInterval[1]) { newInterval[0] = min(newInterval[0], intervals[i][0]); newInterval[1] = max(newInterval[1], intervals[i][1]); i++; } result.push_back(newInterval);
while (i < n) { result.push_back(intervals[i]); i++; }
return result;}
int main() { vector<vector<int>> intervals1 = {{1, 3}, {6, 9}}; for (auto& iv : insertInterval(intervals1, {2, 5})) cout << "[" << iv[0] << "," << iv[1] << "] "; cout << endl; // [1,5] [6,9]
vector<vector<int>> intervals2 = {{1, 2}, {3, 5}, {6, 7}, {8, 10}, {12, 16}}; for (auto& iv : insertInterval(intervals2, {4, 8})) cout << "[" << iv[0] << "," << iv[1] << "] "; cout << endl; // [1,2] [3,10] [12,16] return 0;}import java.util.*;
public class Main { static int[][] insertInterval(int[][] intervals, int[] newInterval) { List<int[]> result = new ArrayList<>(); int i = 0, n = intervals.length;
while (i < n && intervals[i][1] < newInterval[0]) { result.add(intervals[i]); i++; }
while (i < n && intervals[i][0] <= newInterval[1]) { newInterval[0] = Math.min(newInterval[0], intervals[i][0]); newInterval[1] = Math.max(newInterval[1], intervals[i][1]); i++; } result.add(newInterval);
while (i < n) { result.add(intervals[i]); i++; }
return result.toArray(new int[0][]); }
public static void main(String[] args) { for (int[] iv : insertInterval(new int[][]{{1, 3}, {6, 9}}, new int[]{2, 5})) { System.out.print(Arrays.toString(iv) + " "); } System.out.println(); // [1, 5] [6, 9]
for (int[] iv : insertInterval(new int[][]{{1, 2}, {3, 5}, {6, 7}, {8, 10}, {12, 16}}, new int[]{4, 8})) { System.out.print(Arrays.toString(iv) + " "); } System.out.println(); // [1, 2] [3, 10] [12, 16] }}fun insertInterval(intervals: List<IntArray>, newIntervalInput: IntArray): List<IntArray> { val result = mutableListOf<IntArray>() var newInterval = newIntervalInput var i = 0 val n = intervals.size
while (i < n && intervals[i][1] < newInterval[0]) { result.add(intervals[i]) i++ }
while (i < n && intervals[i][0] <= newInterval[1]) { newInterval = intArrayOf(minOf(newInterval[0], intervals[i][0]), maxOf(newInterval[1], intervals[i][1])) i++ } result.add(newInterval)
while (i < n) { result.add(intervals[i]) i++ }
return result}
fun main() { val r1 = insertInterval(listOf(intArrayOf(1, 3), intArrayOf(6, 9)), intArrayOf(2, 5)) println(r1.joinToString(" ") { "[${it[0]},${it[1]}]" }) // [1,5] [6,9]
val r2 = insertInterval(listOf(intArrayOf(1, 2), intArrayOf(3, 5), intArrayOf(6, 7), intArrayOf(8, 10), intArrayOf(12, 16)), intArrayOf(4, 8)) println(r2.joinToString(" ") { "[${it[0]},${it[1]}]" }) // [1,2] [3,10] [12,16]}List<List<int>> insertInterval(List<List<int>> intervals, List<int> newIntervalInput) { final result = <List<int>>[]; var newInterval = List<int>.from(newIntervalInput); int i = 0, n = intervals.length;
while (i < n && intervals[i][1] < newInterval[0]) { result.add(intervals[i]); i++; }
while (i < n && intervals[i][0] <= newInterval[1]) { newInterval = [ newInterval[0] < intervals[i][0] ? newInterval[0] : intervals[i][0], newInterval[1] > intervals[i][1] ? newInterval[1] : intervals[i][1], ]; i++; } result.add(newInterval);
while (i < n) { result.add(intervals[i]); i++; }
return result;}
void main() { print(insertInterval([[1, 3], [6, 9]], [2, 5])); // [[1, 5], [6, 9]] print(insertInterval([[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]], [4, 8])); // [[1, 2], [3, 10], [12, 16]]}132. Xóa bớt khoảng để không còn chồng lấp (Non-overlapping Intervals)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Cho danh sách khoảng intervals, tìm số lượng khoảng tối thiểu cần xóa để các khoảng còn lại không chồng lấp nhau.
Ví dụ 1:
Input: intervals = [[1,2],[2,3],[3,4],[1,3]]Output: 1Giải thích: xóa [1,3] thì các khoảng còn lại không chồng lấpVí dụ 2:
Input: intervals = [[1,2],[1,2],[1,2]]Output: 2Giải thích: cần xóa 2 trong 3 khoảng [1,2] trùng nhauRàng buộc:
1 <= len(intervals) <= 10^5
Xem đáp án
def erase_overlap_intervals(intervals): # Greedy: sắp xếp theo điểm kết thúc, giữ lại khoảng kết thúc sớm nhất - O(n log n) intervals.sort(key=lambda x: x[1]) removed = 0 prev_end = float("-inf")
for start, end in intervals: if start >= prev_end: prev_end = end else: removed += 1
return removed
print(erase_overlap_intervals([[1, 2], [2, 3], [3, 4], [1, 3]])) # 1print(erase_overlap_intervals([[1, 2], [1, 2], [1, 2]])) # 2#include <iostream>#include <vector>#include <algorithm>#include <climits>using namespace std;
int eraseOverlapIntervals(vector<vector<int>> intervals) { sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { return a[1] < b[1]; }); int removed = 0; long prevEnd = LONG_MIN;
for (auto& iv : intervals) { if (iv[0] >= prevEnd) prevEnd = iv[1]; else removed++; } return removed;}
int main() { cout << eraseOverlapIntervals({{1, 2}, {2, 3}, {3, 4}, {1, 3}}) << endl; // 1 cout << eraseOverlapIntervals({{1, 2}, {1, 2}, {1, 2}}) << endl; // 2 return 0;}import java.util.*;
public class Main { static int eraseOverlapIntervals(int[][] intervals) { Arrays.sort(intervals, (a, b) -> a[1] - b[1]); int removed = 0; long prevEnd = Long.MIN_VALUE;
for (int[] iv : intervals) { if (iv[0] >= prevEnd) prevEnd = iv[1]; else removed++; } return removed; }
public static void main(String[] args) { System.out.println(eraseOverlapIntervals(new int[][]{{1, 2}, {2, 3}, {3, 4}, {1, 3}})); // 1 System.out.println(eraseOverlapIntervals(new int[][]{{1, 2}, {1, 2}, {1, 2}})); // 2 }}fun eraseOverlapIntervals(intervals: List<IntArray>): Int { val sorted = intervals.sortedBy { it[1] } var removed = 0 var prevEnd = Long.MIN_VALUE
for (iv in sorted) { if (iv[0] >= prevEnd) prevEnd = iv[1].toLong() else removed++ } return removed}
fun main() { println(eraseOverlapIntervals(listOf(intArrayOf(1, 2), intArrayOf(2, 3), intArrayOf(3, 4), intArrayOf(1, 3)))) // 1 println(eraseOverlapIntervals(listOf(intArrayOf(1, 2), intArrayOf(1, 2), intArrayOf(1, 2)))) // 2}int eraseOverlapIntervals(List<List<int>> intervals) { final sorted = List<List<int>>.from(intervals)..sort((a, b) => a[1].compareTo(b[1])); int removed = 0; double prevEnd = double.negativeInfinity;
for (var iv in sorted) { if (iv[0] >= prevEnd) { prevEnd = iv[1].toDouble(); } else { removed++; } } return removed;}
void main() { print(eraseOverlapIntervals([[1, 2], [2, 3], [3, 4], [1, 3]])); // 1 print(eraseOverlapIntervals([[1, 2], [1, 2], [1, 2]])); // 2}133. Cài đặt Merge Sort (Sort an Array)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Viết hàm merge_sort(nums) sắp xếp mảng tăng dần bằng thuật toán trộn (merge sort), không dùng sorted()/.sort(). Yêu cầu độ phức tạp O(n log n).
Ví dụ 1:
Input: nums = [5, 2, 3, 1]Output: [1, 2, 3, 5]Ví dụ 2:
Input: nums = [5, 1, 1, 2, 0, 0]Output: [0, 0, 1, 1, 2, 5]Ràng buộc:
1 <= len(nums) <= 5 * 10^4- Bắt buộc
O(n log n), không dùng hàm sắp xếp có sẵn
Xem đáp án
def merge_sort(nums): # Chia đôi đệ quy rồi trộn 2 nửa đã sắp xếp - O(n log n) if len(nums) <= 1: return nums
mid = len(nums) // 2 left = merge_sort(nums[:mid]) right = merge_sort(nums[mid:])
result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result
print(merge_sort([5, 2, 3, 1])) # [1, 2, 3, 5]print(merge_sort([5, 1, 1, 2, 0, 0])) # [0, 0, 1, 1, 2, 5]#include <iostream>#include <vector>using namespace std;
vector<int> mergeSort(vector<int> nums) { if (nums.size() <= 1) return nums;
int mid = nums.size() / 2; vector<int> left = mergeSort(vector<int>(nums.begin(), nums.begin() + mid)); vector<int> right = mergeSort(vector<int>(nums.begin() + mid, nums.end()));
vector<int> result; size_t i = 0, j = 0; while (i < left.size() && j < right.size()) { if (left[i] <= right[j]) result.push_back(left[i++]); else result.push_back(right[j++]); } while (i < left.size()) result.push_back(left[i++]); while (j < right.size()) result.push_back(right[j++]); return result;}
int main() { for (int x : mergeSort({5, 2, 3, 1})) cout << x << " "; cout << endl; // 1 2 3 5 for (int x : mergeSort({5, 1, 1, 2, 0, 0})) cout << x << " "; cout << endl; // 0 0 1 1 2 5 return 0;}import java.util.*;
public class Main { static List<Integer> mergeSort(List<Integer> nums) { if (nums.size() <= 1) return nums;
int mid = nums.size() / 2; List<Integer> left = mergeSort(nums.subList(0, mid)); List<Integer> right = mergeSort(nums.subList(mid, nums.size()));
List<Integer> result = new ArrayList<>(); int i = 0, j = 0; while (i < left.size() && j < right.size()) { if (left.get(i) <= right.get(j)) result.add(left.get(i++)); else result.add(right.get(j++)); } while (i < left.size()) result.add(left.get(i++)); while (j < right.size()) result.add(right.get(j++)); return result; }
public static void main(String[] args) { System.out.println(mergeSort(new ArrayList<>(List.of(5, 2, 3, 1)))); // [1, 2, 3, 5] System.out.println(mergeSort(new ArrayList<>(List.of(5, 1, 1, 2, 0, 0)))); // [0, 0, 1, 1, 2, 5] }}fun mergeSort(nums: List<Int>): List<Int> { if (nums.size <= 1) return nums
val mid = nums.size / 2 val left = mergeSort(nums.subList(0, mid)) val right = mergeSort(nums.subList(mid, nums.size))
val result = mutableListOf<Int>() var i = 0 var j = 0 while (i < left.size && j < right.size) { if (left[i] <= right[j]) result.add(left[i++]) else result.add(right[j++]) } while (i < left.size) result.add(left[i++]) while (j < right.size) result.add(right[j++]) return result}
fun main() { println(mergeSort(listOf(5, 2, 3, 1))) // [1, 2, 3, 5] println(mergeSort(listOf(5, 1, 1, 2, 0, 0))) // [0, 0, 1, 1, 2, 5]}List<int> mergeSort(List<int> nums) { if (nums.length <= 1) return nums;
final mid = nums.length ~/ 2; final left = mergeSort(nums.sublist(0, mid)); final right = mergeSort(nums.sublist(mid));
final result = <int>[]; int i = 0, j = 0; while (i < left.length && j < right.length) { if (left[i] <= right[j]) { result.add(left[i++]); } else { result.add(right[j++]); } } result.addAll(left.sublist(i)); result.addAll(right.sublist(j)); return result;}
void main() { print(mergeSort([5, 2, 3, 1])); // [1, 2, 3, 5] print(mergeSort([5, 1, 1, 2, 0, 0])); // [0, 0, 1, 1, 2, 5]}134. Cài đặt Quick Sort (Sort an Array bằng phân hoạch)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Viết hàm quick_sort(nums) sắp xếp mảng tăng dần bằng thuật toán sắp xếp nhanh (quick sort, dùng phân hoạch Lomuto hoặc Hoare), không dùng sorted()/.sort().
Ví dụ 1:
Input: nums = [10, 7, 8, 9, 1, 5]Output: [1, 5, 7, 8, 9, 10]Ví dụ 2:
Input: nums = [4, 4, 4, 1]Output: [1, 4, 4, 4]Ràng buộc:
1 <= len(nums) <= 5 * 10^4- Không dùng hàm sắp xếp có sẵn
- Trung bình
O(n log n), tệ nhấtO(n^2)
Xem đáp án
def quick_sort(nums): # Phân hoạch quanh pivot, đệ quy 2 phần - trung bình O(n log n) if len(nums) <= 1: return nums
pivot = nums[len(nums) // 2] left = [x for x in nums if x < pivot] middle = [x for x in nums if x == pivot] right = [x for x in nums if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
print(quick_sort([10, 7, 8, 9, 1, 5])) # [1, 5, 7, 8, 9, 10]print(quick_sort([4, 4, 4, 1])) # [1, 4, 4, 4]#include <iostream>#include <vector>using namespace std;
vector<int> quickSort(vector<int> nums) { if (nums.size() <= 1) return nums;
int pivot = nums[nums.size() / 2]; vector<int> left, middle, right; for (int x : nums) { if (x < pivot) left.push_back(x); else if (x == pivot) middle.push_back(x); else right.push_back(x); }
vector<int> result = quickSort(left); result.insert(result.end(), middle.begin(), middle.end()); vector<int> sortedRight = quickSort(right); result.insert(result.end(), sortedRight.begin(), sortedRight.end()); return result;}
int main() { for (int x : quickSort({10, 7, 8, 9, 1, 5})) cout << x << " "; cout << endl; // 1 5 7 8 9 10 for (int x : quickSort({4, 4, 4, 1})) cout << x << " "; cout << endl; // 1 4 4 4 return 0;}import java.util.*;
public class Main { static List<Integer> quickSort(List<Integer> nums) { if (nums.size() <= 1) return nums;
int pivot = nums.get(nums.size() / 2); List<Integer> left = new ArrayList<>(), middle = new ArrayList<>(), right = new ArrayList<>(); for (int x : nums) { if (x < pivot) left.add(x); else if (x == pivot) middle.add(x); else right.add(x); }
List<Integer> result = new ArrayList<>(quickSort(left)); result.addAll(middle); result.addAll(quickSort(right)); return result; }
public static void main(String[] args) { System.out.println(quickSort(new ArrayList<>(List.of(10, 7, 8, 9, 1, 5)))); // [1, 5, 7, 8, 9, 10] System.out.println(quickSort(new ArrayList<>(List.of(4, 4, 4, 1)))); // [1, 4, 4, 4] }}fun quickSort(nums: List<Int>): List<Int> { if (nums.size <= 1) return nums
val pivot = nums[nums.size / 2] val left = nums.filter { it < pivot } val middle = nums.filter { it == pivot } val right = nums.filter { it > pivot }
return quickSort(left) + middle + quickSort(right)}
fun main() { println(quickSort(listOf(10, 7, 8, 9, 1, 5))) // [1, 5, 7, 8, 9, 10] println(quickSort(listOf(4, 4, 4, 1))) // [1, 4, 4, 4]}List<int> quickSort(List<int> nums) { if (nums.length <= 1) return nums;
final pivot = nums[nums.length ~/ 2]; final left = nums.where((x) => x < pivot).toList(); final middle = nums.where((x) => x == pivot).toList(); final right = nums.where((x) => x > pivot).toList();
return [...quickSort(left), ...middle, ...quickSort(right)];}
void main() { print(quickSort([10, 7, 8, 9, 1, 5])); // [1, 5, 7, 8, 9, 10] print(quickSort([4, 4, 4, 1])); // [1, 4, 4, 4]}135. Giá trị nhỏ nhất trong mảng xoay (Find Minimum in Rotated Sorted Array)
Độ khó: Trung bình · Chủ đề: Tìm kiếm nhị phân
Mảng nums tăng dần, không trùng lặp, bị xoay tại một điểm chưa biết. Tìm phần tử nhỏ nhất trong mảng. Yêu cầu O(log n).
Ví dụ 1:
Input: nums = [3, 4, 5, 1, 2]Output: 1Ví dụ 2:
Input: nums = [4, 5, 6, 7, 0, 1, 2]Output: 0Ràng buộc:
1 <= len(nums) <= 5000- Mọi phần tử trong
numslà duy nhất - Bắt buộc
O(log n)
Xem đáp án
def find_min(nums): # Binary search: so sánh nums[mid] với nums[right] - O(log n) left, right = 0, len(nums) - 1 while left < right: mid = (left + right) // 2 if nums[mid] > nums[right]: left = mid + 1 else: right = mid return nums[left]
print(find_min([3, 4, 5, 1, 2])) # 1print(find_min([4, 5, 6, 7, 0, 1, 2])) # 0#include <iostream>#include <vector>using namespace std;
int findMin(vector<int>& nums) { int left = 0, right = (int)nums.size() - 1; while (left < right) { int mid = (left + right) / 2; if (nums[mid] > nums[right]) left = mid + 1; else right = mid; } return nums[left];}
int main() { vector<int> nums1 = {3, 4, 5, 1, 2}; cout << findMin(nums1) << endl; // 1 vector<int> nums2 = {4, 5, 6, 7, 0, 1, 2}; cout << findMin(nums2) << endl; // 0 return 0;}public class Main { static int findMin(int[] nums) { int left = 0, right = nums.length - 1; while (left < right) { int mid = (left + right) / 2; if (nums[mid] > nums[right]) left = mid + 1; else right = mid; } return nums[left]; }
public static void main(String[] args) { System.out.println(findMin(new int[]{3, 4, 5, 1, 2})); // 1 System.out.println(findMin(new int[]{4, 5, 6, 7, 0, 1, 2})); // 0 }}fun findMin(nums: List<Int>): Int { var left = 0 var right = nums.size - 1 while (left < right) { val mid = (left + right) / 2 if (nums[mid] > nums[right]) left = mid + 1 else right = mid } return nums[left]}
fun main() { println(findMin(listOf(3, 4, 5, 1, 2))) // 1 println(findMin(listOf(4, 5, 6, 7, 0, 1, 2))) // 0}int findMin(List<int> nums) { int left = 0, right = nums.length - 1; while (left < right) { int mid = (left + right) ~/ 2; if (nums[mid] > nums[right]) { left = mid + 1; } else { right = mid; } } return nums[left];}
void main() { print(findMin([3, 4, 5, 1, 2])); // 1 print(findMin([4, 5, 6, 7, 0, 1, 2])); // 0}136. Chỉ số H (H-Index)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Cho mảng citations với citations[i] là số trích dẫn của bài báo thứ i. Chỉ số H là số lớn nhất h sao cho nhà nghiên cứu có ít nhất h bài báo được trích dẫn ít nhất h lần mỗi bài. Tính chỉ số H.
Ví dụ 1:
Input: citations = [3, 0, 6, 1, 5]Output: 3Giải thích: có 3 bài với ít nhất 3 trích dẫn (6, 5, 3) nên h = 3Ví dụ 2:
Input: citations = [1, 3, 1]Output: 1Ràng buộc:
1 <= len(citations) <= 50000 <= citations[i] <= 1000
Xem đáp án
def h_index(citations): # Sắp xếp giảm dần, tìm vị trí h thỏa mãn - O(n log n) citations.sort(reverse=True) h = 0 for i, c in enumerate(citations): if c >= i + 1: h = i + 1 else: break return h
print(h_index([3, 0, 6, 1, 5])) # 3print(h_index([1, 3, 1])) # 1#include <iostream>#include <vector>#include <algorithm>using namespace std;
int hIndex(vector<int> citations) { sort(citations.begin(), citations.end(), greater<int>()); int h = 0; for (int i = 0; i < (int)citations.size(); i++) { if (citations[i] >= i + 1) h = i + 1; else break; } return h;}
int main() { cout << hIndex({3, 0, 6, 1, 5}) << endl; // 3 cout << hIndex({1, 3, 1}) << endl; // 1 return 0;}import java.util.*;
public class Main { static int hIndex(int[] citations) { Integer[] boxed = Arrays.stream(citations).boxed().toArray(Integer[]::new); Arrays.sort(boxed, Collections.reverseOrder()); int h = 0; for (int i = 0; i < boxed.length; i++) { if (boxed[i] >= i + 1) h = i + 1; else break; } return h; }
public static void main(String[] args) { System.out.println(hIndex(new int[]{3, 0, 6, 1, 5})); // 3 System.out.println(hIndex(new int[]{1, 3, 1})); // 1 }}fun hIndex(citations: List<Int>): Int { val sorted = citations.sortedDescending() var h = 0 for (i in sorted.indices) { if (sorted[i] >= i + 1) h = i + 1 else break } return h}
fun main() { println(hIndex(listOf(3, 0, 6, 1, 5))) // 3 println(hIndex(listOf(1, 3, 1))) // 1}int hIndex(List<int> citations) { final sorted = List<int>.from(citations)..sort((a, b) => b.compareTo(a)); int h = 0; for (var i = 0; i < sorted.length; i++) { if (sorted[i] >= i + 1) { h = i + 1; } else { break; } } return h;}
void main() { print(hIndex([3, 0, 6, 1, 5])); // 3 print(hIndex([1, 3, 1])); // 1}137. Số phòng họp cần thiết (Meeting Rooms II)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Cho danh sách các cuộc họp intervals với intervals[i] = [start_i, end_i]. Tính số phòng họp tối thiểu cần có để tổ chức tất cả các cuộc họp mà không bị trùng giờ.
Ví dụ 1:
Input: intervals = [[0,30],[5,10],[15,20]]Output: 2Giải thích: [0,30] và [5,10] chồng giờ nên cần 2 phòng, sau đó [15,20] dùng lại 1 trong 2 phòngVí dụ 2:
Input: intervals = [[7,10],[2,4]]Output: 1Giải thích: 2 cuộc họp không chồng giờ nhau, dùng chung 1 phòngRàng buộc:
1 <= len(intervals) <= 10^40 <= start_i < end_i
Xem đáp án
import heapq
def min_meeting_rooms(intervals): # Sắp xếp theo start, dùng min-heap lưu end đang dùng - O(n log n) if not intervals: return 0
intervals.sort(key=lambda x: x[0]) heap = [] # lưu end_time của các phòng đang họp
for start, end in intervals: if heap and heap[0] <= start: heapq.heapreplace(heap, end) else: heapq.heappush(heap, end)
return len(heap)
print(min_meeting_rooms([[0, 30], [5, 10], [15, 20]])) # 2print(min_meeting_rooms([[7, 10], [2, 4]])) # 1#include <iostream>#include <vector>#include <queue>#include <algorithm>using namespace std;
int minMeetingRooms(vector<vector<int>> intervals) { if (intervals.empty()) return 0;
sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { return a[0] < b[0]; }); priority_queue<int, vector<int>, greater<int>> heap;
for (auto& iv : intervals) { if (!heap.empty() && heap.top() <= iv[0]) { heap.pop(); } heap.push(iv[1]); } return (int)heap.size();}
int main() { cout << minMeetingRooms({{0, 30}, {5, 10}, {15, 20}}) << endl; // 2 cout << minMeetingRooms({{7, 10}, {2, 4}}) << endl; // 1 return 0;}import java.util.*;
public class Main { static int minMeetingRooms(int[][] intervals) { if (intervals.length == 0) return 0;
Arrays.sort(intervals, (a, b) -> a[0] - b[0]); PriorityQueue<Integer> heap = new PriorityQueue<>();
for (int[] iv : intervals) { if (!heap.isEmpty() && heap.peek() <= iv[0]) { heap.poll(); } heap.offer(iv[1]); } return heap.size(); }
public static void main(String[] args) { System.out.println(minMeetingRooms(new int[][]{{0, 30}, {5, 10}, {15, 20}})); // 2 System.out.println(minMeetingRooms(new int[][]{{7, 10}, {2, 4}})); // 1 }}import java.util.PriorityQueue
fun minMeetingRooms(intervals: List<IntArray>): Int { if (intervals.isEmpty()) return 0
val sorted = intervals.sortedBy { it[0] } val heap = PriorityQueue<Int>()
for (iv in sorted) { if (heap.isNotEmpty() && heap.peek() <= iv[0]) { heap.poll() } heap.offer(iv[1]) } return heap.size}
fun main() { println(minMeetingRooms(listOf(intArrayOf(0, 30), intArrayOf(5, 10), intArrayOf(15, 20)))) // 2 println(minMeetingRooms(listOf(intArrayOf(7, 10), intArrayOf(2, 4)))) // 1}int minMeetingRooms(List<List<int>> intervals) { if (intervals.isEmpty) return 0;
final sorted = List<List<int>>.from(intervals)..sort((a, b) => a[0].compareTo(b[0])); final ends = <int>[]; // giữ danh sách end đang họp, luôn sắp xếp tăng dần
for (var iv in sorted) { if (ends.isNotEmpty && ends.first <= iv[0]) { ends.removeAt(0); } var pos = 0; while (pos < ends.length && ends[pos] < iv[1]) pos++; ends.insert(pos, iv[1]); } return ends.length;}
void main() { print(minMeetingRooms([[0, 30], [5, 10], [15, 20]])); // 2 print(minMeetingRooms([[7, 10], [2, 4]])); // 1}138. Sắp xếp lượn sóng (Wiggle Sort)
Độ khó: Trung bình · Chủ đề: Sắp xếp
Sắp xếp lại mảng nums sao cho nums[0] <= nums[1] >= nums[2] <= nums[3] >= ... (tăng giảm xen kẽ, “lượn sóng”).
Ví dụ 1:
Input: nums = [3, 5, 2, 1, 6, 4]Output: [3, 5, 1, 6, 2, 4]Giải thích: 3<=5, 5>=1, 1<=6, 6>=2, 2<=4 — một trong nhiều đáp án hợp lệVí dụ 2:
Input: nums = [1, 1, 1]Output: [1, 1, 1]Ràng buộc:
1 <= len(nums) <= 5 * 10^4
Xem đáp án
def wiggle_sort(nums): # Duyệt 1 lượt, đổi chỗ khi vi phạm quy tắc tăng/giảm xen kẽ - O(n) for i in range(len(nums) - 1): if (i % 2 == 0 and nums[i] > nums[i + 1]) or \ (i % 2 == 1 and nums[i] < nums[i + 1]): nums[i], nums[i + 1] = nums[i + 1], nums[i] return nums
print(wiggle_sort([3, 5, 2, 1, 6, 4])) # ví dụ: [3, 5, 1, 6, 2, 4]print(wiggle_sort([1, 1, 1])) # [1, 1, 1]#include <iostream>#include <vector>using namespace std;
vector<int> wiggleSort(vector<int> nums) { for (size_t i = 0; i + 1 < nums.size(); i++) { if ((i % 2 == 0 && nums[i] > nums[i + 1]) || (i % 2 == 1 && nums[i] < nums[i + 1])) { swap(nums[i], nums[i + 1]); } } return nums;}
int main() { for (int x : wiggleSort({3, 5, 2, 1, 6, 4})) cout << x << " "; cout << endl; // vi du: 3 5 1 6 2 4 for (int x : wiggleSort({1, 1, 1})) cout << x << " "; cout << endl; // 1 1 1 return 0;}import java.util.Arrays;
public class Main { static int[] wiggleSort(int[] nums) { for (int i = 0; i < nums.length - 1; i++) { if ((i % 2 == 0 && nums[i] > nums[i + 1]) || (i % 2 == 1 && nums[i] < nums[i + 1])) { int tmp = nums[i]; nums[i] = nums[i + 1]; nums[i + 1] = tmp; } } return nums; }
public static void main(String[] args) { System.out.println(Arrays.toString(wiggleSort(new int[]{3, 5, 2, 1, 6, 4}))); // vi du: [3, 5, 1, 6, 2, 4] System.out.println(Arrays.toString(wiggleSort(new int[]{1, 1, 1}))); // [1, 1, 1] }}fun wiggleSort(nums: MutableList<Int>): MutableList<Int> { for (i in 0 until nums.size - 1) { if ((i % 2 == 0 && nums[i] > nums[i + 1]) || (i % 2 == 1 && nums[i] < nums[i + 1])) { val tmp = nums[i] nums[i] = nums[i + 1] nums[i + 1] = tmp } } return nums}
fun main() { println(wiggleSort(mutableListOf(3, 5, 2, 1, 6, 4))) // vi du: [3, 5, 1, 6, 2, 4] println(wiggleSort(mutableListOf(1, 1, 1))) // [1, 1, 1]}List<int> wiggleSort(List<int> nums) { for (var i = 0; i < nums.length - 1; i++) { if ((i % 2 == 0 && nums[i] > nums[i + 1]) || (i % 2 == 1 && nums[i] < nums[i + 1])) { final tmp = nums[i]; nums[i] = nums[i + 1]; nums[i + 1] = tmp; } } return nums;}
void main() { print(wiggleSort([3, 5, 2, 1, 6, 4])); // vi du: [3, 5, 1, 6, 2, 4] print(wiggleSort([1, 1, 1])); // [1, 1, 1]}139. Phần tử nhỏ thứ k trong ma trận đã sắp xếp (Kth Smallest Element in a Sorted Matrix)
Độ khó: Trung bình · Chủ đề: Tìm kiếm nhị phân
Cho ma trận vuông matrix kích thước n x n, mỗi hàng và mỗi cột đều được sắp xếp tăng dần. Tìm phần tử nhỏ thứ k trong ma trận (tính theo thứ tự sắp xếp toàn bộ các phần tử).
Ví dụ 1:
Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8Output: 13Ví dụ 2:
Input: matrix = [[-5]], k = 1Output: -5Ràng buộc:
n == len(matrix) == len(matrix[0])1 <= n <= 300,1 <= k <= n^2- Nên giải tốt hơn
O(n^2 log(n^2))bằng binary search trên giá trị
Xem đáp án
def kth_smallest(matrix, k): # Binary search trên khoảng giá trị [min, max], đếm phần tử <= mid - O(n log(max-min)) n = len(matrix)
def count_less_equal(x): count = 0 row, col = n - 1, 0 while row >= 0 and col < n: if matrix[row][col] <= x: count += row + 1 col += 1 else: row -= 1 return count
left, right = matrix[0][0], matrix[-1][-1] while left < right: mid = (left + right) // 2 if count_less_equal(mid) < k: left = mid + 1 else: right = mid return left
print(kth_smallest([[1, 5, 9], [10, 11, 13], [12, 13, 15]], 8)) # 13print(kth_smallest([[-5]], 1)) # -5#include <iostream>#include <vector>using namespace std;
int countLessEqual(vector<vector<int>>& matrix, int n, int x) { int count = 0; int row = n - 1, col = 0; while (row >= 0 && col < n) { if (matrix[row][col] <= x) { count += row + 1; col++; } else { row--; } } return count;}
int kthSmallest(vector<vector<int>>& matrix, int k) { int n = (int)matrix.size(); int left = matrix[0][0], right = matrix[n - 1][n - 1]; while (left < right) { int mid = left + (right - left) / 2; if (countLessEqual(matrix, n, mid) < k) left = mid + 1; else right = mid; } return left;}
int main() { vector<vector<int>> m1 = {{1, 5, 9}, {10, 11, 13}, {12, 13, 15}}; cout << kthSmallest(m1, 8) << endl; // 13 vector<vector<int>> m2 = {{-5}}; cout << kthSmallest(m2, 1) << endl; // -5 return 0;}public class Main { static int countLessEqual(int[][] matrix, int n, int x) { int count = 0; int row = n - 1, col = 0; while (row >= 0 && col < n) { if (matrix[row][col] <= x) { count += row + 1; col++; } else { row--; } } return count; }
static int kthSmallest(int[][] matrix, int k) { int n = matrix.length; int left = matrix[0][0], right = matrix[n - 1][n - 1]; while (left < right) { int mid = left + (right - left) / 2; if (countLessEqual(matrix, n, mid) < k) left = mid + 1; else right = mid; } return left; }
public static void main(String[] args) { System.out.println(kthSmallest(new int[][]{{1, 5, 9}, {10, 11, 13}, {12, 13, 15}}, 8)); // 13 System.out.println(kthSmallest(new int[][]{{-5}}, 1)); // -5 }}fun countLessEqual(matrix: List<List<Int>>, n: Int, x: Int): Int { var count = 0 var row = n - 1 var col = 0 while (row >= 0 && col < n) { if (matrix[row][col] <= x) { count += row + 1 col++ } else { row-- } } return count}
fun kthSmallest(matrix: List<List<Int>>, k: Int): Int { val n = matrix.size var left = matrix[0][0] var right = matrix[n - 1][n - 1] while (left < right) { val mid = left + (right - left) / 2 if (countLessEqual(matrix, n, mid) < k) left = mid + 1 else right = mid } return left}
fun main() { println(kthSmallest(listOf(listOf(1, 5, 9), listOf(10, 11, 13), listOf(12, 13, 15)), 8)) // 13 println(kthSmallest(listOf(listOf(-5)), 1)) // -5}int countLessEqual(List<List<int>> matrix, int n, int x) { int count = 0; int row = n - 1, col = 0; while (row >= 0 && col < n) { if (matrix[row][col] <= x) { count += row + 1; col++; } else { row--; } } return count;}
int kthSmallest(List<List<int>> matrix, int k) { final n = matrix.length; int left = matrix[0][0], right = matrix[n - 1][n - 1]; while (left < right) { int mid = left + (right - left) ~/ 2; if (countLessEqual(matrix, n, mid) < k) { left = mid + 1; } else { right = mid; } } return left;}
void main() { print(kthSmallest([[1, 5, 9], [10, 11, 13], [12, 13, 15]], 8)); // 13 print(kthSmallest([[-5]], 1)); // -5}140. Tìm kiếm trong ma trận đã sắp xếp (Search a 2D Matrix)
Độ khó: Trung bình · Chủ đề: Tìm kiếm nhị phân
Cho ma trận matrix kích thước m x n: mỗi hàng sắp xếp tăng dần, và phần tử đầu tiên của mỗi hàng lớn hơn phần tử cuối cùng của hàng trước đó (coi như một mảng tăng dần “gấp khúc”). Kiểm tra target có trong ma trận không. Yêu cầu O(log(m*n)).
Ví dụ 1:
Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3Output: TrueVí dụ 2:
Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13Output: FalseRàng buộc:
1 <= m, n <= 100- Bắt buộc
O(log(m*n))
Xem đáp án
def search_matrix(matrix, target): # Coi ma trận như 1 mảng phẳng, binary search với ánh xạ index - O(log(m*n)) if not matrix or not matrix[0]: return False
m, n = len(matrix), len(matrix[0]) left, right = 0, m * n - 1
while left <= right: mid = (left + right) // 2 value = matrix[mid // n][mid % n] if value == target: return True elif value < target: left = mid + 1 else: right = mid - 1
return False
print(search_matrix([[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]], 3)) # Trueprint(search_matrix([[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]], 13)) # False#include <iostream>#include <vector>using namespace std;
bool searchMatrix(vector<vector<int>>& matrix, int target) { if (matrix.empty() || matrix[0].empty()) return false;
int m = (int)matrix.size(), n = (int)matrix[0].size(); int left = 0, right = m * n - 1;
while (left <= right) { int mid = (left + right) / 2; int value = matrix[mid / n][mid % n]; if (value == target) return true; else if (value < target) left = mid + 1; else right = mid - 1; } return false;}
int main() { vector<vector<int>> matrix = {{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 60}}; cout << boolalpha << searchMatrix(matrix, 3) << endl; // true cout << boolalpha << searchMatrix(matrix, 13) << endl; // false return 0;}public class Main { static boolean searchMatrix(int[][] matrix, int target) { if (matrix.length == 0 || matrix[0].length == 0) return false;
int m = matrix.length, n = matrix[0].length; int left = 0, right = m * n - 1;
while (left <= right) { int mid = (left + right) / 2; int value = matrix[mid / n][mid % n]; if (value == target) return true; else if (value < target) left = mid + 1; else right = mid - 1; } return false; }
public static void main(String[] args) { int[][] matrix = {{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 60}}; System.out.println(searchMatrix(matrix, 3)); // true System.out.println(searchMatrix(matrix, 13)); // false }}fun searchMatrix(matrix: List<List<Int>>, target: Int): Boolean { if (matrix.isEmpty() || matrix[0].isEmpty()) return false
val m = matrix.size val n = matrix[0].size var left = 0 var right = m * n - 1
while (left <= right) { val mid = (left + right) / 2 val value = matrix[mid / n][mid % n] when { value == target -> return true value < target -> left = mid + 1 else -> right = mid - 1 } } return false}
fun main() { val matrix = listOf(listOf(1, 3, 5, 7), listOf(10, 11, 16, 20), listOf(23, 30, 34, 60)) println(searchMatrix(matrix, 3)) // true println(searchMatrix(matrix, 13)) // false}bool searchMatrix(List<List<int>> matrix, int target) { if (matrix.isEmpty || matrix[0].isEmpty) return false;
final m = matrix.length, n = matrix[0].length; int left = 0, right = m * n - 1;
while (left <= right) { int mid = (left + right) ~/ 2; int value = matrix[mid ~/ n][mid % n]; if (value == target) return true; else if (value < target) left = mid + 1; else right = mid - 1; } return false;}
void main() { final matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]]; print(searchMatrix(matrix, 3)); // true print(searchMatrix(matrix, 13)); // false}Nhóm 8: Quy hoạch động (Dynamic Programming)
Phần tiêu đề “Nhóm 8: Quy hoạch động (Dynamic Programming)”141. Leo cầu thang (Climbing Stairs)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Bạn đang ở bậc thang thứ 0, cần lên đến bậc thứ n. Mỗi bước bạn có thể leo 1 hoặc 2 bậc. Hỏi có bao nhiêu cách khác nhau để lên đến bậc n?
Ví dụ 1:
Input: n = 2Output: 2Giải thích: Có 2 cách: (1 bậc + 1 bậc), (2 bậc).Ví dụ 2:
Input: n = 5Output: 8Giải thích: dp[5] = dp[4] + dp[3] = 5 + 3 = 8.Ràng buộc:
1 <= n <= 45
Xem đáp án
def climb_stairs(n): # dp[i] = dp[i-1] + dp[i-2]: đến bậc i bằng cách leo 1 bậc từ i-1 hoặc 2 bậc từ i-2 if n <= 2: return n dp = [0] * (n + 1) dp[1], dp[2] = 1, 2 for i in range(3, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n]
print(climb_stairs(2)) # 2print(climb_stairs(5)) # 8#include <iostream>#include <vector>using namespace std;
int climbStairs(int n) { if (n <= 2) return n; vector<int> dp(n + 1); dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) dp[i] = dp[i - 1] + dp[i - 2]; return dp[n];}
int main() { cout << climbStairs(2) << endl; // 2 cout << climbStairs(5) << endl; // 8 return 0;}public class Main { static int climbStairs(int n) { if (n <= 2) return n; int[] dp = new int[n + 1]; dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) dp[i] = dp[i - 1] + dp[i - 2]; return dp[n]; }
public static void main(String[] args) { System.out.println(climbStairs(2)); // 2 System.out.println(climbStairs(5)); // 8 }}fun climbStairs(n: Int): Int { if (n <= 2) return n val dp = IntArray(n + 1) dp[1] = 1 dp[2] = 2 for (i in 3..n) dp[i] = dp[i - 1] + dp[i - 2] return dp[n]}
fun main() { println(climbStairs(2)) // 2 println(climbStairs(5)) // 8}int climbStairs(int n) { if (n <= 2) return n; final dp = List<int>.filled(n + 1, 0); dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) dp[i] = dp[i - 1] + dp[i - 2]; return dp[n];}
void main() { print(climbStairs(2)); // 2 print(climbStairs(5)); // 8}142. Tên trộm thông minh (House Robber)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho một dãy số nums là giá trị tiền tại mỗi nhà dọc theo một con phố. Nếu trộm 2 nhà liền kề nhau, chuông báo động sẽ kêu. Tìm số tiền tối đa có thể trộm được mà không trộm 2 nhà liền kề.
Ví dụ 1:
Input: nums = [1, 2, 3, 1]Output: 4Giải thích: Trộm nhà 0 (1) và nhà 2 (3) -> tổng 4.Ví dụ 2:
Input: nums = [2, 7, 9, 3, 1]Output: 12Giải thích: Trộm nhà 0 (2) + nhà 2 (9) + nhà 4 (1) = 12.Ràng buộc:
1 <= len(nums) <= 1000 <= nums[i] <= 400
Xem đáp án
def rob(nums): # dp[i] = max(dp[i-1], dp[i-2] + nums[i]): bỏ qua nhà i, hoặc trộm nhà i cộng kết quả tốt nhất đến i-2 prev2, prev1 = 0, 0 for x in nums: prev2, prev1 = prev1, max(prev1, prev2 + x) return prev1
print(rob([1, 2, 3, 1])) # 4print(rob([2, 7, 9, 3, 1])) # 12#include <iostream>#include <vector>#include <algorithm>using namespace std;
int rob(vector<int>& nums) { int prev2 = 0, prev1 = 0; for (int x : nums) { int newPrev1 = max(prev1, prev2 + x); prev2 = prev1; prev1 = newPrev1; } return prev1;}
int main() { vector<int> a = {1, 2, 3, 1}; vector<int> b = {2, 7, 9, 3, 1}; cout << rob(a) << endl; // 4 cout << rob(b) << endl; // 12 return 0;}public class Main { static int rob(int[] nums) { int prev2 = 0, prev1 = 0; for (int x : nums) { int newPrev1 = Math.max(prev1, prev2 + x); prev2 = prev1; prev1 = newPrev1; } return prev1; }
public static void main(String[] args) { System.out.println(rob(new int[]{1, 2, 3, 1})); // 4 System.out.println(rob(new int[]{2, 7, 9, 3, 1})); // 12 }}fun rob(nums: List<Int>): Int { var prev2 = 0 var prev1 = 0 for (x in nums) { val newPrev1 = maxOf(prev1, prev2 + x) prev2 = prev1 prev1 = newPrev1 } return prev1}
fun main() { println(rob(listOf(1, 2, 3, 1))) // 4 println(rob(listOf(2, 7, 9, 3, 1))) // 12}int rob(List<int> nums) { int prev2 = 0, prev1 = 0; for (var x in nums) { final newPrev1 = prev1 > prev2 + x ? prev1 : prev2 + x; prev2 = prev1; prev1 = newPrev1; } return prev1;}
void main() { print(rob([1, 2, 3, 1])); // 4 print(rob([2, 7, 9, 3, 1])); // 12}143. Đổi tiền xu - ít đồng nhất (Coin Change)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho các loại tiền xu coins (số lượng mỗi loại là vô hạn) và số tiền amount. Tìm số lượng đồng xu ít nhất để đủ số tiền amount. Nếu không thể, trả về -1.
Ví dụ 1:
Input: coins = [1, 2, 5], amount = 11Output: 3Giải thích: 11 = 5 + 5 + 1.Ví dụ 2:
Input: coins = [2], amount = 3Output: -1Giải thích: Không thể tạo ra 3 chỉ với đồng xu 2.Ràng buộc:
1 <= len(coins) <= 121 <= coins[i] <= 2^31 - 10 <= amount <= 10^4
Xem đáp án
def coin_change(coins, amount): # dp[a] = số đồng xu tối thiểu để đủ số tiền a; dp[a] = min(dp[a - c] + 1) với mọi đồng xu c INF = float("inf") dp = [0] + [INF] * amount for a in range(1, amount + 1): for c in coins: if c <= a: dp[a] = min(dp[a], dp[a - c] + 1) return dp[amount] if dp[amount] != INF else -1
print(coin_change([1, 2, 5], 11)) # 3print(coin_change([2], 3)) # -1#include <iostream>#include <vector>#include <climits>using namespace std;
int coinChange(vector<int>& coins, int amount) { const int INF = INT_MAX / 2; vector<int> dp(amount + 1, INF); dp[0] = 0; for (int a = 1; a <= amount; a++) { for (int c : coins) { if (c <= a) dp[a] = min(dp[a], dp[a - c] + 1); } } return dp[amount] == INF ? -1 : dp[amount];}
int main() { vector<int> c1 = {1, 2, 5}; vector<int> c2 = {2}; cout << coinChange(c1, 11) << endl; // 3 cout << coinChange(c2, 3) << endl; // -1 return 0;}import java.util.Arrays;
public class Main { static int coinChange(int[] coins, int amount) { int INF = Integer.MAX_VALUE / 2; int[] dp = new int[amount + 1]; Arrays.fill(dp, INF); dp[0] = 0; for (int a = 1; a <= amount; a++) { for (int c : coins) { if (c <= a) dp[a] = Math.min(dp[a], dp[a - c] + 1); } } return dp[amount] == INF ? -1 : dp[amount]; }
public static void main(String[] args) { System.out.println(coinChange(new int[]{1, 2, 5}, 11)); // 3 System.out.println(coinChange(new int[]{2}, 3)); // -1 }}fun coinChange(coins: List<Int>, amount: Int): Int { val INF = Int.MAX_VALUE / 2 val dp = IntArray(amount + 1) { INF } dp[0] = 0 for (a in 1..amount) { for (c in coins) { if (c <= a) dp[a] = minOf(dp[a], dp[a - c] + 1) } } return if (dp[amount] == INF) -1 else dp[amount]}
fun main() { println(coinChange(listOf(1, 2, 5), 11)) // 3 println(coinChange(listOf(2), 3)) // -1}int coinChange(List<int> coins, int amount) { const INF = 1 << 30; final dp = List<int>.filled(amount + 1, INF); dp[0] = 0; for (int a = 1; a <= amount; a++) { for (var c in coins) { if (c <= a && dp[a - c] + 1 < dp[a]) dp[a] = dp[a - c] + 1; } } return dp[amount] == INF ? -1 : dp[amount];}
void main() { print(coinChange([1, 2, 5], 11)); // 3 print(coinChange([2], 3)); // -1}144. Dãy con tăng dài nhất (Longest Increasing Subsequence)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho một mảng số nguyên nums, tìm độ dài của dãy con tăng dần dài nhất (các phần tử không cần liên tiếp trong mảng gốc, nhưng phải giữ thứ tự).
Ví dụ 1:
Input: nums = [10, 9, 2, 5, 3, 7, 101, 18]Output: 4Giải thích: Dãy con tăng dài nhất là [2, 3, 7, 101] hoặc [2, 3, 7, 18], độ dài 4.Ví dụ 2:
Input: nums = [0, 1, 0, 3, 2, 3]Output: 4Giải thích: [0, 1, 2, 3].Ràng buộc:
1 <= len(nums) <= 2500-10^4 <= nums[i] <= 10^4
Xem đáp án
import bisect
def length_of_lis(nums): # tails[k] = giá trị nhỏ nhất có thể kết thúc một dãy con tăng độ dài k+1 (patience sorting), O(n log n) tails = [] for x in nums: pos = bisect.bisect_left(tails, x) if pos == len(tails): tails.append(x) else: tails[pos] = x return len(tails)
print(length_of_lis([10, 9, 2, 5, 3, 7, 101, 18])) # 4print(length_of_lis([0, 1, 0, 3, 2, 3])) # 4#include <iostream>#include <vector>#include <algorithm>using namespace std;
int lengthOfLIS(vector<int>& nums) { vector<int> tails; for (int x : nums) { auto it = lower_bound(tails.begin(), tails.end(), x); if (it == tails.end()) tails.push_back(x); else *it = x; } return tails.size();}
int main() { vector<int> a = {10, 9, 2, 5, 3, 7, 101, 18}; vector<int> b = {0, 1, 0, 3, 2, 3}; cout << lengthOfLIS(a) << endl; // 4 cout << lengthOfLIS(b) << endl; // 4 return 0;}import java.util.*;
public class Main { static int lengthOfLIS(int[] nums) { List<Integer> tails = new ArrayList<>(); for (int x : nums) { int pos = Collections.binarySearch(tails, x); if (pos < 0) pos = -(pos + 1); if (pos == tails.size()) tails.add(x); else tails.set(pos, x); } return tails.size(); }
public static void main(String[] args) { System.out.println(lengthOfLIS(new int[]{10, 9, 2, 5, 3, 7, 101, 18})); // 4 System.out.println(lengthOfLIS(new int[]{0, 1, 0, 3, 2, 3})); // 4 }}fun lengthOfLIS(nums: List<Int>): Int { val tails = mutableListOf<Int>() for (x in nums) { var pos = tails.binarySearch(x) if (pos < 0) pos = -(pos + 1) if (pos == tails.size) tails.add(x) else tails[pos] = x } return tails.size}
fun main() { println(lengthOfLIS(listOf(10, 9, 2, 5, 3, 7, 101, 18))) // 4 println(lengthOfLIS(listOf(0, 1, 0, 3, 2, 3))) // 4}int lengthOfLIS(List<int> nums) { final tails = <int>[]; for (var x in nums) { int lo = 0, hi = tails.length; while (lo < hi) { final mid = (lo + hi) ~/ 2; if (tails[mid] < x) { lo = mid + 1; } else { hi = mid; } } if (lo == tails.length) { tails.add(x); } else { tails[lo] = x; } } return tails.length;}
void main() { print(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])); // 4 print(lengthOfLIS([0, 1, 0, 3, 2, 3])); // 4}145. Số đường đi trong lưới (Unique Paths)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Một robot đứng ở góc trên-trái của lưới m x n. Robot chỉ có thể di chuyển xuống hoặc sang phải mỗi bước. Tìm số đường đi khác nhau để robot đến được góc dưới-phải.
Ví dụ 1:
Input: m = 3, n = 7Output: 28Ví dụ 2:
Input: m = 3, n = 2Output: 3Giải thích: 3 đường đi: Phải->Xuống->Xuống, Xuống->Phải->Xuống, Xuống->Xuống->Phải.Ràng buộc:
1 <= m, n <= 100
Xem đáp án
def unique_paths(m, n): # dp[i][j] = dp[i-1][j] + dp[i][j-1]: đến ô (i,j) từ trên hoặc từ trái dp = [[1] * n for _ in range(m)] for i in range(1, m): for j in range(1, n): dp[i][j] = dp[i - 1][j] + dp[i][j - 1] return dp[m - 1][n - 1]
print(unique_paths(3, 7)) # 28print(unique_paths(3, 2)) # 3#include <iostream>#include <vector>using namespace std;
int uniquePaths(int m, int n) { vector<vector<int>> dp(m, vector<int>(n, 1)); for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } return dp[m - 1][n - 1];}
int main() { cout << uniquePaths(3, 7) << endl; // 28 cout << uniquePaths(3, 2) << endl; // 3 return 0;}public class Main { static int uniquePaths(int m, int n) { int[][] dp = new int[m][n]; for (int[] row : dp) java.util.Arrays.fill(row, 1); for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } return dp[m - 1][n - 1]; }
public static void main(String[] args) { System.out.println(uniquePaths(3, 7)); // 28 System.out.println(uniquePaths(3, 2)); // 3 }}fun uniquePaths(m: Int, n: Int): Int { val dp = Array(m) { IntArray(n) { 1 } } for (i in 1 until m) { for (j in 1 until n) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1] } } return dp[m - 1][n - 1]}
fun main() { println(uniquePaths(3, 7)) // 28 println(uniquePaths(3, 2)) // 3}int uniquePaths(int m, int n) { final dp = List.generate(m, (_) => List<int>.filled(n, 1)); for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } return dp[m - 1][n - 1];}
void main() { print(uniquePaths(3, 7)); // 28 print(uniquePaths(3, 2)); // 3}146. Đường đi tổng nhỏ nhất (Minimum Path Sum)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho lưới grid chứa các số không âm, tìm đường đi từ góc trên-trái đến góc dưới-phải sao cho tổng các số trên đường đi là nhỏ nhất. Mỗi bước chỉ được đi xuống hoặc sang phải.
Ví dụ 1:
Input: grid = [[1,3,1],[1,5,1],[4,2,1]]Output: 7Giải thích: Đường đi 1->3->1->1->1 có tổng nhỏ nhất là 7.Ví dụ 2:
Input: grid = [[1,2,3],[4,5,6]]Output: 12Ràng buộc:
1 <= len(grid), len(grid[0]) <= 2000 <= grid[i][j] <= 200
Xem đáp án
def min_path_sum(grid): # dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1]) m, n = len(grid), len(grid[0]) dp = [[0] * n for _ in range(m)] for i in range(m): for j in range(n): if i == 0 and j == 0: dp[i][j] = grid[i][j] elif i == 0: dp[i][j] = dp[i][j - 1] + grid[i][j] elif j == 0: dp[i][j] = dp[i - 1][j] + grid[i][j] else: dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j] return dp[m - 1][n - 1]
print(min_path_sum([[1, 3, 1], [1, 5, 1], [4, 2, 1]])) # 7print(min_path_sum([[1, 2, 3], [4, 5, 6]])) # 12#include <iostream>#include <vector>#include <algorithm>using namespace std;
int minPathSum(vector<vector<int>>& grid) { int m = grid.size(), n = grid[0].size(); vector<vector<int>> dp(m, vector<int>(n, 0)); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i == 0 && j == 0) dp[i][j] = grid[i][j]; else if (i == 0) dp[i][j] = dp[i][j - 1] + grid[i][j]; else if (j == 0) dp[i][j] = dp[i - 1][j] + grid[i][j]; else dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; } } return dp[m - 1][n - 1];}
int main() { vector<vector<int>> g1 = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}}; vector<vector<int>> g2 = {{1, 2, 3}, {4, 5, 6}}; cout << minPathSum(g1) << endl; // 7 cout << minPathSum(g2) << endl; // 12 return 0;}public class Main { static int minPathSum(int[][] grid) { int m = grid.length, n = grid[0].length; int[][] dp = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i == 0 && j == 0) dp[i][j] = grid[i][j]; else if (i == 0) dp[i][j] = dp[i][j - 1] + grid[i][j]; else if (j == 0) dp[i][j] = dp[i - 1][j] + grid[i][j]; else dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; } } return dp[m - 1][n - 1]; }
public static void main(String[] args) { System.out.println(minPathSum(new int[][]{{1, 3, 1}, {1, 5, 1}, {4, 2, 1}})); // 7 System.out.println(minPathSum(new int[][]{{1, 2, 3}, {4, 5, 6}})); // 12 }}fun minPathSum(grid: Array<IntArray>): Int { val m = grid.size val n = grid[0].size val dp = Array(m) { IntArray(n) } for (i in 0 until m) { for (j in 0 until n) { dp[i][j] = when { i == 0 && j == 0 -> grid[i][j] i == 0 -> dp[i][j - 1] + grid[i][j] j == 0 -> dp[i - 1][j] + grid[i][j] else -> minOf(dp[i - 1][j], dp[i][j - 1]) + grid[i][j] } } } return dp[m - 1][n - 1]}
fun main() { println(minPathSum(arrayOf(intArrayOf(1, 3, 1), intArrayOf(1, 5, 1), intArrayOf(4, 2, 1)))) // 7 println(minPathSum(arrayOf(intArrayOf(1, 2, 3), intArrayOf(4, 5, 6)))) // 12}int minPathSum(List<List<int>> grid) { final m = grid.length, n = grid[0].length; final dp = List.generate(m, (_) => List<int>.filled(n, 0)); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i == 0 && j == 0) { dp[i][j] = grid[i][j]; } else if (i == 0) { dp[i][j] = dp[i][j - 1] + grid[i][j]; } else if (j == 0) { dp[i][j] = dp[i - 1][j] + grid[i][j]; } else { dp[i][j] = (dp[i - 1][j] < dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1]) + grid[i][j]; } } } return dp[m - 1][n - 1];}
void main() { print(minPathSum([[1, 3, 1], [1, 5, 1], [4, 2, 1]])); // 7 print(minPathSum([[1, 2, 3], [4, 5, 6]])); // 12}147. Tích lớn nhất của dãy con liên tiếp (Maximum Product Subarray)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho một mảng số nguyên nums, tìm dãy con liên tiếp có tích các phần tử lớn nhất, trả về tích đó.
Ví dụ 1:
Input: nums = [2, 3, -2, 4]Output: 6Giải thích: Dãy con [2, 3] có tích lớn nhất là 6.Ví dụ 2:
Input: nums = [-2, 0, -1]Output: 0Giải thích: Kết quả không thể là 2 vì -2 và -1 không liền kề nhau.Ràng buộc:
1 <= len(nums) <= 2*10^4-10 <= nums[i] <= 10
Xem đáp án
def max_product(nums): # Lưu cả max và min tích tính đến vị trí i, vì số âm có thể biến min thành max result = cur_max = cur_min = nums[0] for x in nums[1:]: candidates = (x, cur_max * x, cur_min * x) cur_max, cur_min = max(candidates), min(candidates) result = max(result, cur_max) return result
print(max_product([2, 3, -2, 4])) # 6print(max_product([-2, 0, -1])) # 0#include <iostream>#include <vector>#include <algorithm>using namespace std;
int maxProduct(vector<int>& nums) { int result = nums[0], curMax = nums[0], curMin = nums[0]; for (size_t i = 1; i < nums.size(); i++) { int x = nums[i]; int a = x, b = curMax * x, c = curMin * x; curMax = max({a, b, c}); curMin = min({a, b, c}); result = max(result, curMax); } return result;}
int main() { vector<int> a = {2, 3, -2, 4}; vector<int> b = {-2, 0, -1}; cout << maxProduct(a) << endl; // 6 cout << maxProduct(b) << endl; // 0 return 0;}public class Main { static int maxProduct(int[] nums) { int result = nums[0], curMax = nums[0], curMin = nums[0]; for (int i = 1; i < nums.length; i++) { int x = nums[i]; int a = x, b = curMax * x, c = curMin * x; curMax = Math.max(a, Math.max(b, c)); curMin = Math.min(a, Math.min(b, c)); result = Math.max(result, curMax); } return result; }
public static void main(String[] args) { System.out.println(maxProduct(new int[]{2, 3, -2, 4})); // 6 System.out.println(maxProduct(new int[]{-2, 0, -1})); // 0 }}fun maxProduct(nums: List<Int>): Int { var result = nums[0] var curMax = nums[0] var curMin = nums[0] for (i in 1 until nums.size) { val x = nums[i] val a = x val b = curMax * x val c = curMin * x curMax = maxOf(a, b, c) curMin = minOf(a, b, c) result = maxOf(result, curMax) } return result}
fun main() { println(maxProduct(listOf(2, 3, -2, 4))) // 6 println(maxProduct(listOf(-2, 0, -1))) // 0}int maxProduct(List<int> nums) { int result = nums[0], curMax = nums[0], curMin = nums[0]; for (int i = 1; i < nums.length; i++) { final x = nums[i]; final a = x, b = curMax * x, c = curMin * x; curMax = [a, b, c].reduce((p, q) => p > q ? p : q); curMin = [a, b, c].reduce((p, q) => p < q ? p : q); result = result > curMax ? result : curMax; } return result;}
void main() { print(maxProduct([2, 3, -2, 4])); // 6 print(maxProduct([-2, 0, -1])); // 0}148. Số lượng số chính phương ít nhất (Perfect Squares)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho số nguyên dương n, tìm số lượng ít nhất các số chính phương (1, 4, 9, 16, …) có tổng bằng n.
Ví dụ 1:
Input: n = 12Output: 3Giải thích: 12 = 4 + 4 + 4.Ví dụ 2:
Input: n = 13Output: 2Giải thích: 13 = 4 + 9.Ràng buộc:
1 <= n <= 10^4
Xem đáp án
def num_squares(n): # dp[i] = số lượng số chính phương ít nhất có tổng bằng i; dp[i] = min(dp[i - k*k]) + 1 dp = [0] + [float("inf")] * n for i in range(1, n + 1): k = 1 while k * k <= i: dp[i] = min(dp[i], dp[i - k * k] + 1) k += 1 return dp[n]
print(num_squares(12)) # 3print(num_squares(13)) # 2#include <iostream>#include <vector>#include <climits>using namespace std;
int numSquares(int n) { vector<int> dp(n + 1, INT_MAX); dp[0] = 0; for (int i = 1; i <= n; i++) { for (int k = 1; k * k <= i; k++) { dp[i] = min(dp[i], dp[i - k * k] + 1); } } return dp[n];}
int main() { cout << numSquares(12) << endl; // 3 cout << numSquares(13) << endl; // 2 return 0;}public class Main { static int numSquares(int n) { int[] dp = new int[n + 1]; java.util.Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for (int i = 1; i <= n; i++) { for (int k = 1; k * k <= i; k++) { dp[i] = Math.min(dp[i], dp[i - k * k] + 1); } } return dp[n]; }
public static void main(String[] args) { System.out.println(numSquares(12)); // 3 System.out.println(numSquares(13)); // 2 }}fun numSquares(n: Int): Int { val dp = IntArray(n + 1) { Int.MAX_VALUE } dp[0] = 0 for (i in 1..n) { var k = 1 while (k * k <= i) { dp[i] = minOf(dp[i], dp[i - k * k] + 1) k++ } } return dp[n]}
fun main() { println(numSquares(12)) // 3 println(numSquares(13)) // 2}int numSquares(int n) { final dp = List<int>.filled(n + 1, 1 << 30); dp[0] = 0; for (int i = 1; i <= n; i++) { int k = 1; while (k * k <= i) { if (dp[i - k * k] + 1 < dp[i]) dp[i] = dp[i - k * k] + 1; k++; } } return dp[n];}
void main() { print(numSquares(12)); // 3 print(numSquares(13)); // 2}149. Chia tập hợp thành 2 phần bằng nhau (Partition Equal Subset Sum)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho mảng số nguyên dương nums, xác định xem có thể chia mảng thành 2 tập con sao cho tổng 2 tập con bằng nhau hay không.
Ví dụ 1:
Input: nums = [1, 5, 11, 5]Output: TrueGiải thích: Chia thành [1, 5, 5] và [11], mỗi tập có tổng 11.Ví dụ 2:
Input: nums = [1, 2, 3, 5]Output: FalseGiải thích: Tổng mảng là 11 (lẻ), không thể chia đôi bằng nhau.Ràng buộc:
1 <= len(nums) <= 2001 <= nums[i] <= 100
Xem đáp án
def can_partition(nums): total = sum(nums) if total % 2 != 0: return False target = total // 2 # dp[s] = True nếu tồn tại tập con có tổng bằng s (bài toán con set/subset sum 0/1) dp = [False] * (target + 1) dp[0] = True for x in nums: for s in range(target, x - 1, -1): if dp[s - x]: dp[s] = True return dp[target]
print(can_partition([1, 5, 11, 5])) # Trueprint(can_partition([1, 2, 3, 5])) # False#include <iostream>#include <vector>#include <numeric>using namespace std;
bool canPartition(vector<int>& nums) { int total = accumulate(nums.begin(), nums.end(), 0); if (total % 2 != 0) return false; int target = total / 2; vector<bool> dp(target + 1, false); dp[0] = true; for (int x : nums) { for (int s = target; s >= x; s--) { if (dp[s - x]) dp[s] = true; } } return dp[target];}
int main() { vector<int> a = {1, 5, 11, 5}; vector<int> b = {1, 2, 3, 5}; cout << boolalpha << canPartition(a) << endl; // true cout << boolalpha << canPartition(b) << endl; // false return 0;}public class Main { static boolean canPartition(int[] nums) { int total = 0; for (int x : nums) total += x; if (total % 2 != 0) return false; int target = total / 2; boolean[] dp = new boolean[target + 1]; dp[0] = true; for (int x : nums) { for (int s = target; s >= x; s--) { if (dp[s - x]) dp[s] = true; } } return dp[target]; }
public static void main(String[] args) { System.out.println(canPartition(new int[]{1, 5, 11, 5})); // true System.out.println(canPartition(new int[]{1, 2, 3, 5})); // false }}fun canPartition(nums: List<Int>): Boolean { val total = nums.sum() if (total % 2 != 0) return false val target = total / 2 val dp = BooleanArray(target + 1) dp[0] = true for (x in nums) { for (s in target downTo x) { if (dp[s - x]) dp[s] = true } } return dp[target]}
fun main() { println(canPartition(listOf(1, 5, 11, 5))) // true println(canPartition(listOf(1, 2, 3, 5))) // false}bool canPartition(List<int> nums) { final total = nums.fold(0, (a, b) => a + b); if (total % 2 != 0) return false; final target = total ~/ 2; final dp = List<bool>.filled(target + 1, false); dp[0] = true; for (var x in nums) { for (int s = target; s >= x; s--) { if (dp[s - x]) dp[s] = true; } } return dp[target];}
void main() { print(canPartition([1, 5, 11, 5])); // true print(canPartition([1, 2, 3, 5])); // false}150. Cắt số để tích lớn nhất (Integer Break)
Độ khó: Trung bình · Chủ đề: Quy hoạch động
Cho số nguyên n >= 2, chia n thành tổng của ít nhất 2 số nguyên dương sao cho tích của các số đó là lớn nhất có thể. Trả về tích lớn nhất đó.
Ví dụ 1:
Input: n = 2Output: 1Giải thích: 2 = 1 + 1, tích = 1.Ví dụ 2:
Input: n = 10Output: 36Giải thích: 10 = 3 + 3 + 4, tích = 3 * 3 * 4 = 36.Ràng buộc:
2 <= n <= 58
Xem đáp án
def integer_break(n): # dp[i] = tích lớn nhất khi chia số i; thử mọi cách cắt i = j + (i - j) dp = [0] * (n + 1) dp[1] = 1 for i in range(2, n + 1): for j in range(1, i): dp[i] = max(dp[i], j * (i - j), j * dp[i - j]) return dp[n]
print(integer_break(2)) # 1print(integer_break(10)) # 36#include <iostream>#include <vector>#include <algorithm>using namespace std;
int integerBreak(int n) { vector<int> dp(n + 1, 0); dp[1] = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j < i; j++) { dp[i] = max({dp[i], j * (i - j), j * dp[i - j]}); } } return dp[n];}
int main() { cout << integerBreak(2) << endl; // 1 cout << integerBreak(10) << endl; // 36 return 0;}public class Main { static int integerBreak(int n) { int[] dp = new int[n + 1]; dp[1] = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j < i; j++) { dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j])); } } return dp[n]; }
public static void main(String[] args) { System.out.println(integerBreak(2)); // 1 System.out.println(integerBreak(10)); // 36 }}fun integerBreak(n: Int): Int { val dp = IntArray(n + 1) dp[1] = 1 for (i in 2..n) { for (j in 1 until i) { dp[i] = maxOf(dp[i], j * (i - j), j * dp[i - j]) } } return dp[n]}
fun main() { println(integerBreak(2)) // 1 println(integerBreak(10)) // 36}int integerBreak(int n) { final dp = List<int>.filled(n + 1, 0); dp[1] = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j < i; j++) { final best = [dp[i], j * (i - j), j * dp[i - j]].reduce((a, b) => a > b ? a : b); dp[i] = best; } } return dp[n];}
void main() { print(integerBreak(2)); // 1 print(integerBreak(10)); // 36}151. Dãy con chung dài nhất (Longest Common Subsequence)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho 2 chuỗi text1 và text2, tìm độ dài dãy con chung dài nhất giữa chúng (các ký tự không cần liên tiếp nhưng phải giữ đúng thứ tự).
Ví dụ 1:
Input: text1 = "abcde", text2 = "ace"Output: 3Giải thích: Dãy con chung dài nhất là "ace", độ dài 3.Ví dụ 2:
Input: text1 = "abc", text2 = "abc"Output: 3Ví dụ 3:
Input: text1 = "abc", text2 = "def"Output: 0Giải thích: Không có ký tự chung nào.Ràng buộc:
1 <= len(text1), len(text2) <= 1000
Xem đáp án
def longest_common_subsequence(text1, text2): # dp[i][j] = LCS của text1[:i] và text2[:j] # nếu ký tự khớp: dp[i][j] = dp[i-1][j-1] + 1, ngược lại: max(dp[i-1][j], dp[i][j-1]) m, n = len(text1), len(text2) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): if text1[i - 1] == text2[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) return dp[m][n]
print(longest_common_subsequence("abcde", "ace")) # 3print(longest_common_subsequence("abc", "abc")) # 3print(longest_common_subsequence("abc", "def")) # 0#include <iostream>#include <vector>#include <string>#include <algorithm>using namespace std;
int longestCommonSubsequence(string text1, string text2) { int m = text1.size(), n = text2.size(); vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1; else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } return dp[m][n];}
int main() { cout << longestCommonSubsequence("abcde", "ace") << endl; // 3 cout << longestCommonSubsequence("abc", "abc") << endl; // 3 cout << longestCommonSubsequence("abc", "def") << endl; // 0 return 0;}public class Main { static int longestCommonSubsequence(String text1, String text2) { int m = text1.length(), n = text2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (text1.charAt(i - 1) == text2.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1] + 1; else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } return dp[m][n]; }
public static void main(String[] args) { System.out.println(longestCommonSubsequence("abcde", "ace")); // 3 System.out.println(longestCommonSubsequence("abc", "abc")); // 3 System.out.println(longestCommonSubsequence("abc", "def")); // 0 }}fun longestCommonSubsequence(text1: String, text2: String): Int { val m = text1.length val n = text2.length val dp = Array(m + 1) { IntArray(n + 1) } for (i in 1..m) { for (j in 1..n) { dp[i][j] = if (text1[i - 1] == text2[j - 1]) dp[i - 1][j - 1] + 1 else maxOf(dp[i - 1][j], dp[i][j - 1]) } } return dp[m][n]}
fun main() { println(longestCommonSubsequence("abcde", "ace")) // 3 println(longestCommonSubsequence("abc", "abc")) // 3 println(longestCommonSubsequence("abc", "def")) // 0}int longestCommonSubsequence(String text1, String text2) { final m = text1.length, n = text2.length; final dp = List.generate(m + 1, (_) => List<int>.filled(n + 1, 0)); for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (text1[i - 1] == text2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = dp[i - 1][j] > dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1]; } } } return dp[m][n];}
void main() { print(longestCommonSubsequence("abcde", "ace")); // 3 print(longestCommonSubsequence("abc", "abc")); // 3 print(longestCommonSubsequence("abc", "def")); // 0}152. Khoảng cách chỉnh sửa (Edit Distance)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho 2 chuỗi word1 và word2, tìm số thao tác tối thiểu (thêm, xóa, thay thế 1 ký tự) để biến word1 thành word2.
Ví dụ 1:
Input: word1 = "horse", word2 = "ros"Output: 3Giải thích: horse -> rorse (thay h->r) -> rose (xóa r) -> ros (xóa e).Ví dụ 2:
Input: word1 = "intention", word2 = "execution"Output: 5Ràng buộc:
0 <= len(word1), len(word2) <= 500
Xem đáp án
def min_distance(word1, word2): # dp[i][j] = số thao tác để biến word1[:i] thành word2[:j] # khớp ký tự: dp[i][j] = dp[i-1][j-1]; khác: 1 + min(xóa, thêm, thay thế) m, n = len(word1), len(word2) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(m + 1): dp[i][0] = i for j in range(n + 1): dp[0][j] = j for i in range(1, m + 1): for j in range(1, n + 1): if word1[i - 1] == word2[j - 1]: dp[i][j] = dp[i - 1][j - 1] else: dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) return dp[m][n]
print(min_distance("horse", "ros")) # 3print(min_distance("intention", "execution")) # 5#include <iostream>#include <vector>#include <string>#include <algorithm>using namespace std;
int minDistance(string word1, string word2) { int m = word1.size(), n = word2.size(); vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for (int i = 0; i <= m; i++) dp[i][0] = i; for (int j = 0; j <= n; j++) dp[0][j] = j; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}); } } return dp[m][n];}
int main() { cout << minDistance("horse", "ros") << endl; // 3 cout << minDistance("intention", "execution") << endl; // 5 return 0;}public class Main { static int minDistance(String word1, String word2) { int m = word1.length(), n = word2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 0; i <= m; i++) dp[i][0] = i; for (int j = 0; j <= n; j++) dp[0][j] = j; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + Math.min(dp[i - 1][j], Math.min(dp[i][j - 1], dp[i - 1][j - 1])); } } return dp[m][n]; }
public static void main(String[] args) { System.out.println(minDistance("horse", "ros")); // 3 System.out.println(minDistance("intention", "execution")); // 5 }}fun minDistance(word1: String, word2: String): Int { val m = word1.length val n = word2.length val dp = Array(m + 1) { IntArray(n + 1) } for (i in 0..m) dp[i][0] = i for (j in 0..n) dp[0][j] = j for (i in 1..m) { for (j in 1..n) { dp[i][j] = if (word1[i - 1] == word2[j - 1]) dp[i - 1][j - 1] else 1 + minOf(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) } } return dp[m][n]}
fun main() { println(minDistance("horse", "ros")) // 3 println(minDistance("intention", "execution")) // 5}int minDistance(String word1, String word2) { final m = word1.length, n = word2.length; final dp = List.generate(m + 1, (_) => List<int>.filled(n + 1, 0)); for (int i = 0; i <= m; i++) dp[i][0] = i; for (int j = 0; j <= n; j++) dp[0][j] = j; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (word1[i - 1] == word2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; } else { dp[i][j] = 1 + [dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]].reduce((a, b) => a < b ? a : b); } } } return dp[m][n];}
void main() { print(minDistance("horse", "ros")); // 3 print(minDistance("intention", "execution")); // 5}153. Cái túi 0/1 (0/1 Knapsack)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho n món đồ, món thứ i có trọng lượng weights[i] và giá trị values[i]. Một cái túi chịu được trọng lượng tối đa capacity. Mỗi món chỉ được chọn tối đa 1 lần. Tìm giá trị lớn nhất có thể mang theo.
Ví dụ 1:
Input: weights = [1, 3, 4, 5], values = [1, 4, 5, 7], capacity = 7Output: 9Giải thích: Chọn món có weight=3 (value=4) và weight=4 (value=5) -> tổng weight 7, value 9.Ví dụ 2:
Input: weights = [2, 2, 3], values = [3, 4, 5], capacity = 4Output: 7Giải thích: Chọn 2 món weight=2 (value 3 và 4) -> weight 4, value 7.Ràng buộc:
1 <= n <= 10001 <= capacity <= 1000
Xem đáp án
def knapsack_01(weights, values, capacity): # dp[c] = giá trị lớn nhất đạt được với sức chứa c; duyệt trọng lượng giảm dần để mỗi món chỉ dùng 1 lần n = len(weights) dp = [0] * (capacity + 1) for i in range(n): for c in range(capacity, weights[i] - 1, -1): dp[c] = max(dp[c], dp[c - weights[i]] + values[i]) return dp[capacity]
print(knapsack_01([1, 3, 4, 5], [1, 4, 5, 7], 7)) # 9print(knapsack_01([2, 2, 3], [3, 4, 5], 4)) # 7#include <iostream>#include <vector>#include <algorithm>using namespace std;
int knapsack01(vector<int>& weights, vector<int>& values, int capacity) { int n = weights.size(); vector<int> dp(capacity + 1, 0); for (int i = 0; i < n; i++) { for (int c = capacity; c >= weights[i]; c--) { dp[c] = max(dp[c], dp[c - weights[i]] + values[i]); } } return dp[capacity];}
int main() { vector<int> w1 = {1, 3, 4, 5}, v1 = {1, 4, 5, 7}; vector<int> w2 = {2, 2, 3}, v2 = {3, 4, 5}; cout << knapsack01(w1, v1, 7) << endl; // 9 cout << knapsack01(w2, v2, 4) << endl; // 7 return 0;}public class Main { static int knapsack01(int[] weights, int[] values, int capacity) { int n = weights.length; int[] dp = new int[capacity + 1]; for (int i = 0; i < n; i++) { for (int c = capacity; c >= weights[i]; c--) { dp[c] = Math.max(dp[c], dp[c - weights[i]] + values[i]); } } return dp[capacity]; }
public static void main(String[] args) { System.out.println(knapsack01(new int[]{1, 3, 4, 5}, new int[]{1, 4, 5, 7}, 7)); // 9 System.out.println(knapsack01(new int[]{2, 2, 3}, new int[]{3, 4, 5}, 4)); // 7 }}fun knapsack01(weights: List<Int>, values: List<Int>, capacity: Int): Int { val n = weights.size val dp = IntArray(capacity + 1) for (i in 0 until n) { for (c in capacity downTo weights[i]) { dp[c] = maxOf(dp[c], dp[c - weights[i]] + values[i]) } } return dp[capacity]}
fun main() { println(knapsack01(listOf(1, 3, 4, 5), listOf(1, 4, 5, 7), 7)) // 9 println(knapsack01(listOf(2, 2, 3), listOf(3, 4, 5), 4)) // 7}int knapsack01(List<int> weights, List<int> values, int capacity) { final n = weights.length; final dp = List<int>.filled(capacity + 1, 0); for (int i = 0; i < n; i++) { for (int c = capacity; c >= weights[i]; c--) { final cand = dp[c - weights[i]] + values[i]; if (cand > dp[c]) dp[c] = cand; } } return dp[capacity];}
void main() { print(knapsack01([1, 3, 4, 5], [1, 4, 5, 7], 7)); // 9 print(knapsack01([2, 2, 3], [3, 4, 5], 4)); // 7}154. Dãy con đối xứng dài nhất (Longest Palindromic Subsequence)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho chuỗi s, tìm độ dài dãy con đối xứng (palindrome) dài nhất của s (các ký tự không cần liên tiếp).
Ví dụ 1:
Input: s = "bbbab"Output: 4Giải thích: Dãy con đối xứng dài nhất là "bbbb".Ví dụ 2:
Input: s = "cbbd"Output: 2Giải thích: "bb".Ràng buộc:
1 <= len(s) <= 1000
Xem đáp án
def longest_palindrome_subseq(s): # dp[i][j] = độ dài LPS trong s[i:j+1] # nếu s[i]==s[j]: dp[i][j] = dp[i+1][j-1] + 2, ngược lại: max(dp[i+1][j], dp[i][j-1]) n = len(s) dp = [[0] * n for _ in range(n)] for i in range(n - 1, -1, -1): dp[i][i] = 1 for j in range(i + 1, n): if s[i] == s[j]: dp[i][j] = dp[i + 1][j - 1] + 2 if j > i + 1 else 2 else: dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) return dp[0][n - 1]
print(longest_palindrome_subseq("bbbab")) # 4print(longest_palindrome_subseq("cbbd")) # 2#include <iostream>#include <vector>#include <string>#include <algorithm>using namespace std;
int longestPalindromeSubseq(string s) { int n = s.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); for (int i = n - 1; i >= 0; i--) { dp[i][i] = 1; for (int j = i + 1; j < n; j++) { if (s[i] == s[j]) dp[i][j] = (j > i + 1 ? dp[i + 1][j - 1] : 0) + 2; else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); } } return dp[0][n - 1];}
int main() { cout << longestPalindromeSubseq("bbbab") << endl; // 4 cout << longestPalindromeSubseq("cbbd") << endl; // 2 return 0;}public class Main { static int longestPalindromeSubseq(String s) { int n = s.length(); int[][] dp = new int[n][n]; for (int i = n - 1; i >= 0; i--) { dp[i][i] = 1; for (int j = i + 1; j < n; j++) { if (s.charAt(i) == s.charAt(j)) dp[i][j] = (j > i + 1 ? dp[i + 1][j - 1] : 0) + 2; else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); } } return dp[0][n - 1]; }
public static void main(String[] args) { System.out.println(longestPalindromeSubseq("bbbab")); // 4 System.out.println(longestPalindromeSubseq("cbbd")); // 2 }}fun longestPalindromeSubseq(s: String): Int { val n = s.length val dp = Array(n) { IntArray(n) } for (i in n - 1 downTo 0) { dp[i][i] = 1 for (j in i + 1 until n) { dp[i][j] = if (s[i] == s[j]) (if (j > i + 1) dp[i + 1][j - 1] else 0) + 2 else maxOf(dp[i + 1][j], dp[i][j - 1]) } } return dp[0][n - 1]}
fun main() { println(longestPalindromeSubseq("bbbab")) // 4 println(longestPalindromeSubseq("cbbd")) // 2}int longestPalindromeSubseq(String s) { final n = s.length; final dp = List.generate(n, (_) => List<int>.filled(n, 0)); for (int i = n - 1; i >= 0; i--) { dp[i][i] = 1; for (int j = i + 1; j < n; j++) { if (s[i] == s[j]) { dp[i][j] = (j > i + 1 ? dp[i + 1][j - 1] : 0) + 2; } else { dp[i][j] = dp[i + 1][j] > dp[i][j - 1] ? dp[i + 1][j] : dp[i][j - 1]; } } } return dp[0][n - 1];}
void main() { print(longestPalindromeSubseq("bbbab")); // 4 print(longestPalindromeSubseq("cbbd")); // 2}155. Nổ bóng bay (Burst Balloons)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho n quả bóng bay xếp thành hàng, quả thứ i có giá trị nums[i]. Khi nổ quả bóng i, bạn nhận được nums[left] * nums[i] * nums[right] (với left, right là 2 quả liền kề còn lại tại thời điểm đó; nếu ngoài biên coi như giá trị 1). Tìm số điểm tối đa có thể đạt được khi nổ hết tất cả bóng.
Ví dụ 1:
Input: nums = [3, 1, 5, 8]Output: 167Giải thích: Nổ theo thứ tự 1, 5, 3, 8: 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 15+120+24+8 = 167.Ví dụ 2:
Input: nums = [1, 5]Output: 10Giải thích: Nổ 1 trước: 1*1*5 + 1*5*1 = 5+5 = 10.Ràng buộc:
1 <= len(nums) <= 3000 <= nums[i] <= 100
Xem đáp án
def max_coins(nums): # Thêm biên 1 ở 2 đầu. dp[l][r] = điểm tối đa khi nổ hết bóng trong khoảng mở (l, r) # duyệt k là quả nổ CUỐI CÙNG trong khoảng: dp[l][r] = max(dp[l][k] + dp[k][r] + a[l]*a[k]*a[r]) a = [1] + nums + [1] n = len(a) dp = [[0] * n for _ in range(n)] for length in range(2, n): for l in range(0, n - length): r = l + length best = 0 for k in range(l + 1, r): best = max(best, dp[l][k] + dp[k][r] + a[l] * a[k] * a[r]) dp[l][r] = best return dp[0][n - 1]
print(max_coins([3, 1, 5, 8])) # 167print(max_coins([1, 5])) # 10#include <iostream>#include <vector>#include <algorithm>using namespace std;
int maxCoins(vector<int>& nums) { vector<int> a; a.push_back(1); for (int x : nums) a.push_back(x); a.push_back(1); int n = a.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); for (int length = 2; length < n; length++) { for (int l = 0; l < n - length; l++) { int r = l + length; int best = 0; for (int k = l + 1; k < r; k++) { best = max(best, dp[l][k] + dp[k][r] + a[l] * a[k] * a[r]); } dp[l][r] = best; } } return dp[0][n - 1];}
int main() { vector<int> a = {3, 1, 5, 8}; vector<int> b = {1, 5}; cout << maxCoins(a) << endl; // 167 cout << maxCoins(b) << endl; // 10 return 0;}public class Main { static int maxCoins(int[] nums) { int n = nums.length + 2; int[] a = new int[n]; a[0] = 1; a[n - 1] = 1; for (int i = 0; i < nums.length; i++) a[i + 1] = nums[i]; int[][] dp = new int[n][n]; for (int length = 2; length < n; length++) { for (int l = 0; l < n - length; l++) { int r = l + length; int best = 0; for (int k = l + 1; k < r; k++) { best = Math.max(best, dp[l][k] + dp[k][r] + a[l] * a[k] * a[r]); } dp[l][r] = best; } } return dp[0][n - 1]; }
public static void main(String[] args) { System.out.println(maxCoins(new int[]{3, 1, 5, 8})); // 167 System.out.println(maxCoins(new int[]{1, 5})); // 10 }}fun maxCoins(nums: List<Int>): Int { val a = mutableListOf(1) a.addAll(nums) a.add(1) val n = a.size val dp = Array(n) { IntArray(n) } for (length in 2 until n) { for (l in 0 until n - length) { val r = l + length var best = 0 for (k in l + 1 until r) { best = maxOf(best, dp[l][k] + dp[k][r] + a[l] * a[k] * a[r]) } dp[l][r] = best } } return dp[0][n - 1]}
fun main() { println(maxCoins(listOf(3, 1, 5, 8))) // 167 println(maxCoins(listOf(1, 5))) // 10}int maxCoins(List<int> nums) { final a = [1, ...nums, 1]; final n = a.length; final dp = List.generate(n, (_) => List<int>.filled(n, 0)); for (int length = 2; length < n; length++) { for (int l = 0; l < n - length; l++) { final r = l + length; int best = 0; for (int k = l + 1; k < r; k++) { final cand = dp[l][k] + dp[k][r] + a[l] * a[k] * a[r]; if (cand > best) best = cand; } dp[l][r] = best; } } return dp[0][n - 1];}
void main() { print(maxCoins([3, 1, 5, 8])); // 167 print(maxCoins([1, 5])); // 10}156. So khớp biểu thức chính quy đơn giản (Regular Expression Matching)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho chuỗi s và mẫu p chỉ chứa chữ cái thường, '.' (khớp 1 ký tự bất kỳ) và '*' (khớp 0 hoặc nhiều lần ký tự đứng trước nó). Kiểm tra p có khớp toàn bộ s hay không.
Ví dụ 1:
Input: s = "aa", p = "a*"Output: TrueGiải thích: "a*" khớp 0 hoặc nhiều 'a', ở đây khớp "aa".Ví dụ 2:
Input: s = "mississippi", p = "mis*is*p*."Output: FalseRàng buộc:
1 <= len(s) <= 20,1 <= len(p) <= 30
Xem đáp án
def is_match(s, p): # dp[i][j] = True nếu s[:i] khớp p[:j] m, n = len(s), len(p) dp = [[False] * (n + 1) for _ in range(m + 1)] dp[0][0] = True for j in range(1, n + 1): if p[j - 1] == "*": dp[0][j] = dp[0][j - 2] for i in range(1, m + 1): for j in range(1, n + 1): if p[j - 1] == "*": # 0 lần ký tự trước '*', hoặc >=1 lần nếu ký tự khớp dp[i][j] = dp[i][j - 2] or ( dp[i - 1][j] and (p[j - 2] == s[i - 1] or p[j - 2] == ".") ) elif p[j - 1] == "." or p[j - 1] == s[i - 1]: dp[i][j] = dp[i - 1][j - 1] return dp[m][n]
print(is_match("aa", "a*")) # Trueprint(is_match("mississippi", "mis*is*p*.")) # False#include <iostream>#include <vector>#include <string>using namespace std;
bool isMatch(string s, string p) { int m = s.size(), n = p.size(); vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false)); dp[0][0] = true; for (int j = 1; j <= n; j++) { if (p[j - 1] == '*') dp[0][j] = dp[0][j - 2]; } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (p[j - 1] == '*') { dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (p[j - 2] == s[i - 1] || p[j - 2] == '.')); } else if (p[j - 1] == '.' || p[j - 1] == s[i - 1]) { dp[i][j] = dp[i - 1][j - 1]; } } } return dp[m][n];}
int main() { cout << boolalpha << isMatch("aa", "a*") << endl; // true cout << boolalpha << isMatch("mississippi", "mis*is*p*.") << endl; // false return 0;}public class Main { static boolean isMatch(String s, String p) { int m = s.length(), n = p.length(); boolean[][] dp = new boolean[m + 1][n + 1]; dp[0][0] = true; for (int j = 1; j <= n; j++) { if (p.charAt(j - 1) == '*') dp[0][j] = dp[0][j - 2]; } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (p.charAt(j - 1) == '*') { dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (p.charAt(j - 2) == s.charAt(i - 1) || p.charAt(j - 2) == '.')); } else if (p.charAt(j - 1) == '.' || p.charAt(j - 1) == s.charAt(i - 1)) { dp[i][j] = dp[i - 1][j - 1]; } } } return dp[m][n]; }
public static void main(String[] args) { System.out.println(isMatch("aa", "a*")); // true System.out.println(isMatch("mississippi", "mis*is*p*.")); // false }}fun isMatch(s: String, p: String): Boolean { val m = s.length val n = p.length val dp = Array(m + 1) { BooleanArray(n + 1) } dp[0][0] = true for (j in 1..n) { if (p[j - 1] == '*') dp[0][j] = dp[0][j - 2] } for (i in 1..m) { for (j in 1..n) { if (p[j - 1] == '*') { dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (p[j - 2] == s[i - 1] || p[j - 2] == '.')) } else if (p[j - 1] == '.' || p[j - 1] == s[i - 1]) { dp[i][j] = dp[i - 1][j - 1] } } } return dp[m][n]}
fun main() { println(isMatch("aa", "a*")) // true println(isMatch("mississippi", "mis*is*p*.")) // false}bool isMatch(String s, String p) { final m = s.length, n = p.length; final dp = List.generate(m + 1, (_) => List<bool>.filled(n + 1, false)); dp[0][0] = true; for (int j = 1; j <= n; j++) { if (p[j - 1] == '*') dp[0][j] = dp[0][j - 2]; } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (p[j - 1] == '*') { dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (p[j - 2] == s[i - 1] || p[j - 2] == '.')); } else if (p[j - 1] == '.' || p[j - 1] == s[i - 1]) { dp[i][j] = dp[i - 1][j - 1]; } } } return dp[m][n];}
void main() { print(isMatch("aa", "a*")); // true print(isMatch("mississippi", "mis*is*p*.")); // false}157. Xen kẽ chuỗi (Interleaving String)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho 3 chuỗi s1, s2, s3. Kiểm tra s3 có được tạo thành bằng cách xen kẽ (giữ nguyên thứ tự nội bộ) các ký tự của s1 và s2 hay không.
Ví dụ 1:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"Output: TrueVí dụ 2:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"Output: FalseRàng buộc:
0 <= len(s1), len(s2) <= 100len(s3) = len(s1) + len(s2)
Xem đáp án
def is_interleave(s1, s2, s3): m, n = len(s1), len(s2) if m + n != len(s3): return False # dp[i][j] = True nếu s3[:i+j] được tạo bởi xen kẽ s1[:i] và s2[:j] dp = [[False] * (n + 1) for _ in range(m + 1)] dp[0][0] = True for i in range(1, m + 1): dp[i][0] = dp[i - 1][0] and s1[i - 1] == s3[i - 1] for j in range(1, n + 1): dp[0][j] = dp[0][j - 1] and s2[j - 1] == s3[j - 1] for i in range(1, m + 1): for j in range(1, n + 1): k = i + j - 1 dp[i][j] = (dp[i - 1][j] and s1[i - 1] == s3[k]) or ( dp[i][j - 1] and s2[j - 1] == s3[k] ) return dp[m][n]
print(is_interleave("aabcc", "dbbca", "aadbbcbcac")) # Trueprint(is_interleave("aabcc", "dbbca", "aadbbbaccc")) # False#include <iostream>#include <vector>#include <string>using namespace std;
bool isInterleave(string s1, string s2, string s3) { int m = s1.size(), n = s2.size(); if (m + n != (int)s3.size()) return false; vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false)); dp[0][0] = true; for (int i = 1; i <= m; i++) dp[i][0] = dp[i - 1][0] && s1[i - 1] == s3[i - 1]; for (int j = 1; j <= n; j++) dp[0][j] = dp[0][j - 1] && s2[j - 1] == s3[j - 1]; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { int k = i + j - 1; dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[k]) || (dp[i][j - 1] && s2[j - 1] == s3[k]); } } return dp[m][n];}
int main() { cout << boolalpha << isInterleave("aabcc", "dbbca", "aadbbcbcac") << endl; // true cout << boolalpha << isInterleave("aabcc", "dbbca", "aadbbbaccc") << endl; // false return 0;}public class Main { static boolean isInterleave(String s1, String s2, String s3) { int m = s1.length(), n = s2.length(); if (m + n != s3.length()) return false; boolean[][] dp = new boolean[m + 1][n + 1]; dp[0][0] = true; for (int i = 1; i <= m; i++) dp[i][0] = dp[i - 1][0] && s1.charAt(i - 1) == s3.charAt(i - 1); for (int j = 1; j <= n; j++) dp[0][j] = dp[0][j - 1] && s2.charAt(j - 1) == s3.charAt(j - 1); for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { int k = i + j - 1; dp[i][j] = (dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(k)) || (dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(k)); } } return dp[m][n]; }
public static void main(String[] args) { System.out.println(isInterleave("aabcc", "dbbca", "aadbbcbcac")); // true System.out.println(isInterleave("aabcc", "dbbca", "aadbbbaccc")); // false }}fun isInterleave(s1: String, s2: String, s3: String): Boolean { val m = s1.length val n = s2.length if (m + n != s3.length) return false val dp = Array(m + 1) { BooleanArray(n + 1) } dp[0][0] = true for (i in 1..m) dp[i][0] = dp[i - 1][0] && s1[i - 1] == s3[i - 1] for (j in 1..n) dp[0][j] = dp[0][j - 1] && s2[j - 1] == s3[j - 1] for (i in 1..m) { for (j in 1..n) { val k = i + j - 1 dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[k]) || (dp[i][j - 1] && s2[j - 1] == s3[k]) } } return dp[m][n]}
fun main() { println(isInterleave("aabcc", "dbbca", "aadbbcbcac")) // true println(isInterleave("aabcc", "dbbca", "aadbbbaccc")) // false}bool isInterleave(String s1, String s2, String s3) { final m = s1.length, n = s2.length; if (m + n != s3.length) return false; final dp = List.generate(m + 1, (_) => List<bool>.filled(n + 1, false)); dp[0][0] = true; for (int i = 1; i <= m; i++) dp[i][0] = dp[i - 1][0] && s1[i - 1] == s3[i - 1]; for (int j = 1; j <= n; j++) dp[0][j] = dp[0][j - 1] && s2[j - 1] == s3[j - 1]; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { final k = i + j - 1; dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[k]) || (dp[i][j - 1] && s2[j - 1] == s3[k]); } } return dp[m][n];}
void main() { print(isInterleave("aabcc", "dbbca", "aadbbcbcac")); // true print(isInterleave("aabcc", "dbbca", "aadbbbaccc")); // false}158. Ngục tối tử thần (Dungeon Game)
Độ khó: Khó · Chủ đề: Quy hoạch động
Một hiệp sĩ cần cứu công chúa trong ngục, di chuyển từ ô trên-trái đến ô dưới-phải của lưới dungeon (chỉ đi phải hoặc xuống). Mỗi ô có giá trị dương (hồi máu) hoặc âm (mất máu). Hiệp sĩ chết nếu HP <= 0 tại bất kỳ thời điểm nào. Tìm HP khởi điểm tối thiểu để đến được công chúa.
Ví dụ 1:
Input: dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]Output: 7Giải thích: Đi theo đường RIGHT -> RIGHT -> DOWN -> DOWN cần HP khởi điểm 7.Ví dụ 2:
Input: dungeon = [[0]]Output: 1Giải thích: HP tối thiểu luôn phải >= 1.Ràng buộc:
1 <= m, n <= 200
Xem đáp án
def calculate_minimum_hp(dungeon): # Tính ngược từ ô cuối: dp[i][j] = HP tối thiểu cần có KHI BƯỚC VÀO ô (i,j) để sống sót đến cuối m, n = len(dungeon), len(dungeon[0]) dp = [[float("inf")] * (n + 1) for _ in range(m + 1)] dp[m][n - 1] = dp[m - 1][n] = 1 for i in range(m - 1, -1, -1): for j in range(n - 1, -1, -1): need = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j] dp[i][j] = max(need, 1) return dp[0][0]
print(calculate_minimum_hp([[-2, -3, 3], [-5, -10, 1], [10, 30, -5]])) # 7print(calculate_minimum_hp([[0]])) # 1#include <iostream>#include <vector>#include <algorithm>#include <climits>using namespace std;
int calculateMinimumHP(vector<vector<int>>& dungeon) { int m = dungeon.size(), n = dungeon[0].size(); const int INF = INT_MAX / 2; vector<vector<int>> dp(m + 1, vector<int>(n + 1, INF)); dp[m][n - 1] = 1; dp[m - 1][n] = 1; for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { int need = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]; dp[i][j] = max(need, 1); } } return dp[0][0];}
int main() { vector<vector<int>> d1 = {{-2, -3, 3}, {-5, -10, 1}, {10, 30, -5}}; vector<vector<int>> d2 = {{0}}; cout << calculateMinimumHP(d1) << endl; // 7 cout << calculateMinimumHP(d2) << endl; // 1 return 0;}public class Main { static int calculateMinimumHP(int[][] dungeon) { int m = dungeon.length, n = dungeon[0].length; int INF = Integer.MAX_VALUE / 2; int[][] dp = new int[m + 1][n + 1]; for (int[] row : dp) java.util.Arrays.fill(row, INF); dp[m][n - 1] = 1; dp[m - 1][n] = 1; for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { int need = Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]; dp[i][j] = Math.max(need, 1); } } return dp[0][0]; }
public static void main(String[] args) { System.out.println(calculateMinimumHP(new int[][]{{-2, -3, 3}, {-5, -10, 1}, {10, 30, -5}})); // 7 System.out.println(calculateMinimumHP(new int[][]{{0}})); // 1 }}fun calculateMinimumHP(dungeon: Array<IntArray>): Int { val m = dungeon.size val n = dungeon[0].size val INF = Int.MAX_VALUE / 2 val dp = Array(m + 1) { IntArray(n + 1) { INF } } dp[m][n - 1] = 1 dp[m - 1][n] = 1 for (i in m - 1 downTo 0) { for (j in n - 1 downTo 0) { val need = minOf(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j] dp[i][j] = maxOf(need, 1) } } return dp[0][0]}
fun main() { println(calculateMinimumHP(arrayOf(intArrayOf(-2, -3, 3), intArrayOf(-5, -10, 1), intArrayOf(10, 30, -5)))) // 7 println(calculateMinimumHP(arrayOf(intArrayOf(0)))) // 1}int calculateMinimumHP(List<List<int>> dungeon) { final m = dungeon.length, n = dungeon[0].length; const INF = 1 << 30; final dp = List.generate(m + 1, (_) => List<int>.filled(n + 1, INF)); dp[m][n - 1] = 1; dp[m - 1][n] = 1; for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { final need = (dp[i + 1][j] < dp[i][j + 1] ? dp[i + 1][j] : dp[i][j + 1]) - dungeon[i][j]; dp[i][j] = need > 1 ? need : 1; } } return dp[0][0];}
void main() { print(calculateMinimumHP([[-2, -3, 3], [-5, -10, 1], [10, 30, -5]])); // 7 print(calculateMinimumHP([[0]])); // 1}159. Mua bán cổ phiếu có thời gian nghỉ (Best Time to Buy and Sell Stock with Cooldown)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho mảng prices là giá cổ phiếu mỗi ngày. Bạn có thể thực hiện nhiều giao dịch (mua rồi bán), nhưng sau khi bán phải nghỉ ít nhất 1 ngày trước khi mua lại (không được giữ nhiều hơn 1 cổ phiếu cùng lúc). Tìm lợi nhuận tối đa.
Ví dụ 1:
Input: prices = [1, 2, 3, 0, 2]Output: 3Giải thích: Mua(1)->Bán(2, lãi 1)->Nghỉ->Mua(0)->Bán(2, lãi 2). Tổng 3.Ví dụ 2:
Input: prices = [1]Output: 0Ràng buộc:
1 <= len(prices) <= 5000
Xem đáp án
def max_profit(prices): # 3 trạng thái mỗi ngày: hold (đang giữ cp), sold (vừa bán hôm nay), rest (rảnh rỗi/đã nghỉ) if not prices: return 0 hold, sold, rest = -prices[0], 0, 0 for p in prices[1:]: prev_sold = sold sold = hold + p hold = max(hold, rest - p) rest = max(rest, prev_sold) return max(sold, rest)
print(max_profit([1, 2, 3, 0, 2])) # 3print(max_profit([1])) # 0#include <iostream>#include <vector>#include <algorithm>using namespace std;
int maxProfit(vector<int>& prices) { if (prices.empty()) return 0; int hold = -prices[0], sold = 0, rest = 0; for (size_t i = 1; i < prices.size(); i++) { int p = prices[i]; int prevSold = sold; sold = hold + p; hold = max(hold, rest - p); rest = max(rest, prevSold); } return max(sold, rest);}
int main() { vector<int> a = {1, 2, 3, 0, 2}; vector<int> b = {1}; cout << maxProfit(a) << endl; // 3 cout << maxProfit(b) << endl; // 0 return 0;}public class Main { static int maxProfit(int[] prices) { if (prices.length == 0) return 0; int hold = -prices[0], sold = 0, rest = 0; for (int i = 1; i < prices.length; i++) { int p = prices[i]; int prevSold = sold; sold = hold + p; hold = Math.max(hold, rest - p); rest = Math.max(rest, prevSold); } return Math.max(sold, rest); }
public static void main(String[] args) { System.out.println(maxProfit(new int[]{1, 2, 3, 0, 2})); // 3 System.out.println(maxProfit(new int[]{1})); // 0 }}fun maxProfit(prices: List<Int>): Int { if (prices.isEmpty()) return 0 var hold = -prices[0] var sold = 0 var rest = 0 for (i in 1 until prices.size) { val p = prices[i] val prevSold = sold sold = hold + p hold = maxOf(hold, rest - p) rest = maxOf(rest, prevSold) } return maxOf(sold, rest)}
fun main() { println(maxProfit(listOf(1, 2, 3, 0, 2))) // 3 println(maxProfit(listOf(1))) // 0}int maxProfit(List<int> prices) { if (prices.isEmpty) return 0; int hold = -prices[0], sold = 0, rest = 0; for (int i = 1; i < prices.length; i++) { final p = prices[i]; final prevSold = sold; sold = hold + p; hold = hold > rest - p ? hold : rest - p; rest = rest > prevSold ? rest : prevSold; } return sold > rest ? sold : rest;}
void main() { print(maxProfit([1, 2, 3, 0, 2])); // 3 print(maxProfit([1])); // 0}160. Hình vuông lớn nhất trong ma trận (Maximal Square)
Độ khó: Khó · Chủ đề: Quy hoạch động
Cho ma trận nhị phân matrix chỉ gồm 0 và 1, tìm diện tích của hình vuông lớn nhất chỉ chứa toàn số 1.
Ví dụ 1:
Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]Output: 4Giải thích: Hình vuông lớn nhất có cạnh 2, diện tích 4.Ví dụ 2:
Input: matrix = [["0","1"],["1","0"]]Output: 1Ràng buộc:
1 <= m, n <= 300
Xem đáp án
def maximal_square(matrix): # dp[i][j] = cạnh hình vuông lớn nhất có góc dưới-phải tại (i,j) # nếu matrix[i][j]=='1': dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) m, n = len(matrix), len(matrix[0]) dp = [[0] * (n + 1) for _ in range(m + 1)] max_side = 0 for i in range(1, m + 1): for j in range(1, n + 1): if matrix[i - 1][j - 1] == "1": dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) max_side = max(max_side, dp[i][j]) return max_side * max_side
print(maximal_square([["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]])) # 4print(maximal_square([["0", "1"], ["1", "0"]])) # 1#include <iostream>#include <vector>#include <string>#include <algorithm>using namespace std;
int maximalSquare(vector<vector<string>>& matrix) { int m = matrix.size(), n = matrix[0].size(); vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); int maxSide = 0; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (matrix[i - 1][j - 1] == "1") { dp[i][j] = 1 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}); maxSide = max(maxSide, dp[i][j]); } } } return maxSide * maxSide;}
int main() { vector<vector<string>> m1 = {{"1","0","1","0","0"},{"1","0","1","1","1"},{"1","1","1","1","1"},{"1","0","0","1","0"}}; vector<vector<string>> m2 = {{"0", "1"}, {"1", "0"}}; cout << maximalSquare(m1) << endl; // 4 cout << maximalSquare(m2) << endl; // 1 return 0;}public class Main { static int maximalSquare(String[][] matrix) { int m = matrix.length, n = matrix[0].length; int[][] dp = new int[m + 1][n + 1]; int maxSide = 0; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (matrix[i - 1][j - 1].equals("1")) { dp[i][j] = 1 + Math.min(dp[i - 1][j], Math.min(dp[i][j - 1], dp[i - 1][j - 1])); maxSide = Math.max(maxSide, dp[i][j]); } } } return maxSide * maxSide; }
public static void main(String[] args) { String[][] m1 = {{"1","0","1","0","0"},{"1","0","1","1","1"},{"1","1","1","1","1"},{"1","0","0","1","0"}}; String[][] m2 = {{"0", "1"}, {"1", "0"}}; System.out.println(maximalSquare(m1)); // 4 System.out.println(maximalSquare(m2)); // 1 }}fun maximalSquare(matrix: Array<Array<String>>): Int { val m = matrix.size val n = matrix[0].size val dp = Array(m + 1) { IntArray(n + 1) } var maxSide = 0 for (i in 1..m) { for (j in 1..n) { if (matrix[i - 1][j - 1] == "1") { dp[i][j] = 1 + minOf(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) maxSide = maxOf(maxSide, dp[i][j]) } } } return maxSide * maxSide}
fun main() { val m1 = arrayOf(arrayOf("1","0","1","0","0"), arrayOf("1","0","1","1","1"), arrayOf("1","1","1","1","1"), arrayOf("1","0","0","1","0")) val m2 = arrayOf(arrayOf("0", "1"), arrayOf("1", "0")) println(maximalSquare(m1)) // 4 println(maximalSquare(m2)) // 1}int maximalSquare(List<List<String>> matrix) { final m = matrix.length, n = matrix[0].length; final dp = List.generate(m + 1, (_) => List<int>.filled(n + 1, 0)); int maxSide = 0; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (matrix[i - 1][j - 1] == "1") { dp[i][j] = 1 + [dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]].reduce((a, b) => a < b ? a : b); if (dp[i][j] > maxSide) maxSide = dp[i][j]; } } } return maxSide * maxSide;}
void main() { print(maximalSquare([["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]])); // 4 print(maximalSquare([["0", "1"], ["1", "0"]])); // 1}Nhóm 9: Đồ thị (Graph) & Backtracking
Phần tiêu đề “Nhóm 9: Đồ thị (Graph) & Backtracking”161. Số lượng đảo (Number of Islands)
Độ khó: Trung bình · Chủ đề: Đồ thị (BFS/DFS trên lưới)
Cho một lưới 2 chiều gồm '1' (đất) và '0' (nước), đếm số lượng “đảo”. Một đảo là nhóm các ô đất liền kề theo 4 hướng (trên/dưới/trái/phải), bao quanh bởi nước.
Ví dụ 1:
Input:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"]]Output: 3Giải thích: Có 3 nhóm đất liền kề tách biệt nhau.Ví dụ 2:
Input:grid = [ ["1","1","1"], ["0","1","0"], ["1","0","1"]]Output: 2Giải thích: Nhóm trên (5 ô đất nối liền) là 1 đảo, ô góc dưới phải tách biệt là đảo thứ 2.Ràng buộc:
1 <= số hàng, số cột <= 300- Mỗi ô chỉ chứa
'0'hoặc'1'.
Xem đáp án
def num_islands(grid): rows, cols = len(grid), len(grid[0]) visited = set()
def bfs(r, c): queue = [(r, c)] visited.add((r, c)) while queue: cr, cc = queue.pop() for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): nr, nc = cr + dr, cc + dc if (0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == "1" and (nr, nc) not in visited): visited.add((nr, nc)) queue.append((nr, nc))
islands = 0 for r in range(rows): for c in range(cols): if grid[r][c] == "1" and (r, c) not in visited: bfs(r, c) islands += 1 return islands
grid = [ ["1", "1", "0", "0", "0"], ["1", "1", "0", "0", "0"], ["0", "0", "1", "0", "0"], ["0", "0", "0", "1", "1"],]print(num_islands(grid)) # 3#include <iostream>#include <vector>#include <string>using namespace std;
int numIslands(vector<vector<string>>& grid) { int rows = grid.size(), cols = grid[0].size(); vector<vector<bool>> visited(rows, vector<bool>(cols, false)); int dr[] = {1, -1, 0, 0}; int dc[] = {0, 0, 1, -1};
auto bfs = [&](int r, int c) { vector<pair<int, int>> queue = {{r, c}}; visited[r][c] = true; while (!queue.empty()) { auto [cr, cc] = queue.back(); queue.pop_back(); for (int k = 0; k < 4; k++) { int nr = cr + dr[k], nc = cc + dc[k]; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && grid[nr][nc] == "1" && !visited[nr][nc]) { visited[nr][nc] = true; queue.push_back({nr, nc}); } } } };
int islands = 0; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (grid[r][c] == "1" && !visited[r][c]) { bfs(r, c); islands++; } } } return islands;}
int main() { vector<vector<string>> grid = { {"1", "1", "0", "0", "0"}, {"1", "1", "0", "0", "0"}, {"0", "0", "1", "0", "0"}, {"0", "0", "0", "1", "1"}, }; cout << numIslands(grid) << endl; // 3 return 0;}import java.util.*;
public class Main { static int numIslands(String[][] grid) { int rows = grid.length, cols = grid[0].length; boolean[][] visited = new boolean[rows][cols]; int[] dr = {1, -1, 0, 0}; int[] dc = {0, 0, 1, -1}; int islands = 0;
for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (grid[r][c].equals("1") && !visited[r][c]) { Deque<int[]> queue = new ArrayDeque<>(); queue.push(new int[]{r, c}); visited[r][c] = true; while (!queue.isEmpty()) { int[] cur = queue.pop(); for (int k = 0; k < 4; k++) { int nr = cur[0] + dr[k], nc = cur[1] + dc[k]; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && grid[nr][nc].equals("1") && !visited[nr][nc]) { visited[nr][nc] = true; queue.push(new int[]{nr, nc}); } } } islands++; } } } return islands; }
public static void main(String[] args) { String[][] grid = { {"1", "1", "0", "0", "0"}, {"1", "1", "0", "0", "0"}, {"0", "0", "1", "0", "0"}, {"0", "0", "0", "1", "1"}, }; System.out.println(numIslands(grid)); // 3 }}fun numIslands(grid: Array<Array<String>>): Int { val rows = grid.size val cols = grid[0].size val visited = Array(rows) { BooleanArray(cols) } val dr = intArrayOf(1, -1, 0, 0) val dc = intArrayOf(0, 0, 1, -1) var islands = 0
for (r in 0 until rows) { for (c in 0 until cols) { if (grid[r][c] == "1" && !visited[r][c]) { val queue = ArrayDeque<Pair<Int, Int>>() queue.addLast(r to c) visited[r][c] = true while (queue.isNotEmpty()) { val (cr, cc) = queue.removeLast() for (k in 0 until 4) { val nr = cr + dr[k] val nc = cc + dc[k] if (nr in 0 until rows && nc in 0 until cols && grid[nr][nc] == "1" && !visited[nr][nc]) { visited[nr][nc] = true queue.addLast(nr to nc) } } } islands++ } } } return islands}
fun main() { val grid = arrayOf( arrayOf("1", "1", "0", "0", "0"), arrayOf("1", "1", "0", "0", "0"), arrayOf("0", "0", "1", "0", "0"), arrayOf("0", "0", "0", "1", "1") ) println(numIslands(grid)) // 3}int numIslands(List<List<String>> grid) { final rows = grid.length, cols = grid[0].length; final visited = List.generate(rows, (_) => List<bool>.filled(cols, false)); final dr = [1, -1, 0, 0]; final dc = [0, 0, 1, -1]; int islands = 0;
for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (grid[r][c] == "1" && !visited[r][c]) { final queue = <List<int>>[[r, c]]; visited[r][c] = true; while (queue.isNotEmpty) { final cur = queue.removeLast(); for (int k = 0; k < 4; k++) { final nr = cur[0] + dr[k], nc = cur[1] + dc[k]; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && grid[nr][nc] == "1" && !visited[nr][nc]) { visited[nr][nc] = true; queue.add([nr, nc]); } } } islands++; } } } return islands;}
void main() { final grid = [ ["1", "1", "0", "0", "0"], ["1", "1", "0", "0", "0"], ["0", "0", "1", "0", "0"], ["0", "0", "0", "1", "1"], ]; print(numIslands(grid)); // 3}162. Tô màu vùng (Flood Fill)
Độ khó: Trung bình · Chủ đề: Đồ thị (DFS trên lưới)
Cho một ảnh biểu diễn bằng lưới số image, một điểm bắt đầu (sr, sc) và màu mới color. Tô lại toàn bộ vùng liên thông (4 hướng) chứa điểm bắt đầu, có cùng màu ban đầu, bằng màu mới.
Ví dụ 1:
Input: image=[[1,1,1],[1,1,0],[1,0,1]], sr=1, sc=1, color=2Output: [[2,2,2],[2,2,0],[2,0,1]]Giải thích: Vùng chứa (1,1) có màu 1, tô toàn bộ vùng đó thành 2.Ví dụ 2:
Input: image=[[0,0,0],[0,0,0]], sr=0, sc=0, color=0Output: [[0,0,0],[0,0,0]]Giải thích: Màu mới trùng màu cũ nên không đổi gì (tránh lặp vô hạn).Ràng buộc:
1 <= số hàng, số cột <= 500 <= sr < số hàng,0 <= sc < số cột
Xem đáp án
def flood_fill(image, sr, sc, color): old_color = image[sr][sc] if old_color == color: return image
def dfs(r, c): if not (0 <= r < len(image) and 0 <= c < len(image[0])): return if image[r][c] != old_color: return image[r][c] = color dfs(r + 1, c) dfs(r - 1, c) dfs(r, c + 1) dfs(r, c - 1)
dfs(sr, sc) return image
print(flood_fill([[1, 1, 1], [1, 1, 0], [1, 0, 1]], 1, 1, 2))#include <iostream>#include <vector>using namespace std;
void dfs(vector<vector<int>>& image, int r, int c, int oldColor, int color) { if (r < 0 || r >= (int)image.size() || c < 0 || c >= (int)image[0].size()) return; if (image[r][c] != oldColor) return; image[r][c] = color; dfs(image, r + 1, c, oldColor, color); dfs(image, r - 1, c, oldColor, color); dfs(image, r, c + 1, oldColor, color); dfs(image, r, c - 1, oldColor, color);}
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) { int oldColor = image[sr][sc]; if (oldColor == color) return image; dfs(image, sr, sc, oldColor, color); return image;}
int main() { vector<vector<int>> image = {{1, 1, 1}, {1, 1, 0}, {1, 0, 1}}; for (auto& row : floodFill(image, 1, 1, 2)) { for (int v : row) cout << v << " "; cout << endl; } return 0;}public class Main { static void dfs(int[][] image, int r, int c, int oldColor, int color) { if (r < 0 || r >= image.length || c < 0 || c >= image[0].length) return; if (image[r][c] != oldColor) return; image[r][c] = color; dfs(image, r + 1, c, oldColor, color); dfs(image, r - 1, c, oldColor, color); dfs(image, r, c + 1, oldColor, color); dfs(image, r, c - 1, oldColor, color); }
static int[][] floodFill(int[][] image, int sr, int sc, int color) { int oldColor = image[sr][sc]; if (oldColor == color) return image; dfs(image, sr, sc, oldColor, color); return image; }
public static void main(String[] args) { int[][] image = {{1, 1, 1}, {1, 1, 0}, {1, 0, 1}}; for (int[] row : floodFill(image, 1, 1, 2)) { System.out.println(java.util.Arrays.toString(row)); } }}fun dfs(image: Array<IntArray>, r: Int, c: Int, oldColor: Int, color: Int) { if (r < 0 || r >= image.size || c < 0 || c >= image[0].size) return if (image[r][c] != oldColor) return image[r][c] = color dfs(image, r + 1, c, oldColor, color) dfs(image, r - 1, c, oldColor, color) dfs(image, r, c + 1, oldColor, color) dfs(image, r, c - 1, oldColor, color)}
fun floodFill(image: Array<IntArray>, sr: Int, sc: Int, color: Int): Array<IntArray> { val oldColor = image[sr][sc] if (oldColor == color) return image dfs(image, sr, sc, oldColor, color) return image}
fun main() { val image = arrayOf(intArrayOf(1, 1, 1), intArrayOf(1, 1, 0), intArrayOf(1, 0, 1)) for (row in floodFill(image, 1, 1, 2)) println(row.toList())}void dfs(List<List<int>> image, int r, int c, int oldColor, int color) { if (r < 0 || r >= image.length || c < 0 || c >= image[0].length) return; if (image[r][c] != oldColor) return; image[r][c] = color; dfs(image, r + 1, c, oldColor, color); dfs(image, r - 1, c, oldColor, color); dfs(image, r, c + 1, oldColor, color); dfs(image, r, c - 1, oldColor, color);}
List<List<int>> floodFill(List<List<int>> image, int sr, int sc, int color) { final oldColor = image[sr][sc]; if (oldColor == color) return image; dfs(image, sr, sc, oldColor, color); return image;}
void main() { final image = [[1, 1, 1], [1, 1, 0], [1, 0, 1]]; print(floodFill(image, 1, 1, 2));}163. Sao chép đồ thị (Clone Graph)
Độ khó: Trung bình · Chủ đề: Đồ thị (DFS/BFS)
Cho một đồ thị vô hướng liên thông, biểu diễn bằng dictionary adj (đỉnh -> list đỉnh kề), và đỉnh bắt đầu start. Viết hàm tạo ra một bản sao độc lập (deep copy) của đồ thị, trả về dictionary kề mới.
Ví dụ 1:
Input: adj={1: [2, 4], 2: [1, 3], 3: [2, 4], 4: [1, 3]}, start=1Output: {1: [2, 4], 2: [1, 3], 3: [2, 4], 4: [1, 3]}Giải thích: Cấu trúc giống hệt bản gốc nhưng là các object/dictionary khác nhau trong bộ nhớ.Ví dụ 2:
Input: adj={1: []}, start=1Output: {1: []}Giải thích: Đồ thị chỉ có 1 đỉnh, không có cạnh nào.Ràng buộc:
- Số đỉnh tối đa 100, đồ thị không có khuyên (self-loop) trùng lặp.
- Đồ thị liên thông, không có đỉnh cô lập ngoài
start.
Xem đáp án
def clone_graph(adj, start): cloned = {}
def dfs(node): if node in cloned: return cloned[node] cloned[node] = [] for neighbor in adj[node]: cloned[node].append(neighbor) if neighbor not in cloned: dfs(neighbor) return cloned[node]
dfs(start) return cloned
print(clone_graph({1: [2, 4], 2: [1, 3], 3: [2, 4], 4: [1, 3]}, 1))#include <iostream>#include <map>#include <vector>#include <functional>using namespace std;
map<int, vector<int>> cloneGraph(map<int, vector<int>>& adj, int start) { map<int, vector<int>> cloned;
function<void(int)> dfs = [&](int node) { if (cloned.count(node)) return; cloned[node] = {}; for (int neighbor : adj[node]) { cloned[node].push_back(neighbor); if (!cloned.count(neighbor)) dfs(neighbor); } };
dfs(start); return cloned;}
int main() { map<int, vector<int>> adj = {{1, {2, 4}}, {2, {1, 3}}, {3, {2, 4}}, {4, {1, 3}}}; auto result = cloneGraph(adj, 1); for (auto& [node, neighbors] : result) { cout << node << ": "; for (int n : neighbors) cout << n << " "; cout << endl; } return 0;}import java.util.*;
public class Main { static Map<Integer, List<Integer>> cloneGraph(Map<Integer, List<Integer>> adj, int start) { Map<Integer, List<Integer>> cloned = new HashMap<>(); dfs(start, adj, cloned); return cloned; }
static void dfs(int node, Map<Integer, List<Integer>> adj, Map<Integer, List<Integer>> cloned) { if (cloned.containsKey(node)) return; cloned.put(node, new ArrayList<>()); for (int neighbor : adj.get(node)) { cloned.get(node).add(neighbor); if (!cloned.containsKey(neighbor)) dfs(neighbor, adj, cloned); } }
public static void main(String[] args) { Map<Integer, List<Integer>> adj = new HashMap<>(); adj.put(1, Arrays.asList(2, 4)); adj.put(2, Arrays.asList(1, 3)); adj.put(3, Arrays.asList(2, 4)); adj.put(4, Arrays.asList(1, 3)); System.out.println(cloneGraph(adj, 1)); }}fun cloneGraph(adj: Map<Int, List<Int>>, start: Int): MutableMap<Int, MutableList<Int>> { val cloned = mutableMapOf<Int, MutableList<Int>>()
fun dfs(node: Int) { if (cloned.containsKey(node)) return cloned[node] = mutableListOf() for (neighbor in adj[node]!!) { cloned[node]!!.add(neighbor) if (!cloned.containsKey(neighbor)) dfs(neighbor) } }
dfs(start) return cloned}
fun main() { val adj = mapOf(1 to listOf(2, 4), 2 to listOf(1, 3), 3 to listOf(2, 4), 4 to listOf(1, 3)) println(cloneGraph(adj, 1))}Map<int, List<int>> cloneGraph(Map<int, List<int>> adj, int start) { final cloned = <int, List<int>>{};
void dfs(int node) { if (cloned.containsKey(node)) return; cloned[node] = []; for (var neighbor in adj[node]!) { cloned[node]!.add(neighbor); if (!cloned.containsKey(neighbor)) dfs(neighbor); } }
dfs(start); return cloned;}
void main() { final adj = {1: [2, 4], 2: [1, 3], 3: [2, 4], 4: [1, 3]}; print(cloneGraph(adj, 1));}164. Lịch học có thể hoàn thành (Course Schedule)
Độ khó: Trung bình · Chủ đề: Đồ thị (Topological Sort / phát hiện chu trình)
Có numCourses môn học đánh số từ 0. Danh sách prerequisites chứa các cặp [a, b] nghĩa là muốn học a phải học b trước. Kiểm tra có thể hoàn thành tất cả các môn học hay không (đồ thị có chu trình hay không).
Ví dụ 1:
Input: numCourses=2, prerequisites=[[1,0]]Output: TrueGiải thích: Học 0 trước, rồi học 1. Không có chu trình.Ví dụ 2:
Input: numCourses=2, prerequisites=[[1,0],[0,1]]Output: FalseGiải thích: Học 1 cần 0, học 0 cần 1 -> chu trình, không thể hoàn thành.Ràng buộc:
1 <= numCourses <= 20000 <= a, b < numCourses
Xem đáp án
def can_finish(num_courses, prerequisites): adj = {i: [] for i in range(num_courses)} for a, b in prerequisites: adj[a].append(b)
# 0 = chưa thăm, 1 = đang xét (trên stack đệ quy), 2 = đã xong state = [0] * num_courses
def has_cycle(node): if state[node] == 1: return True if state[node] == 2: return False state[node] = 1 for neighbor in adj[node]: if has_cycle(neighbor): return True state[node] = 2 return False
for course in range(num_courses): if has_cycle(course): return False return True
print(can_finish(2, [[1, 0]])) # Trueprint(can_finish(2, [[1, 0], [0, 1]])) # False#include <iostream>#include <vector>#include <functional>using namespace std;
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { vector<vector<int>> adj(numCourses); for (auto& p : prerequisites) adj[p[0]].push_back(p[1]);
vector<int> state(numCourses, 0);
function<bool(int)> hasCycle = [&](int node) -> bool { if (state[node] == 1) return true; if (state[node] == 2) return false; state[node] = 1; for (int neighbor : adj[node]) { if (hasCycle(neighbor)) return true; } state[node] = 2; return false; };
for (int course = 0; course < numCourses; course++) { if (hasCycle(course)) return false; } return true;}
int main() { vector<vector<int>> p1 = {{1, 0}}; vector<vector<int>> p2 = {{1, 0}, {0, 1}}; cout << boolalpha << canFinish(2, p1) << endl; // true cout << boolalpha << canFinish(2, p2) << endl; // false return 0;}import java.util.*;
public class Main { static boolean canFinish(int numCourses, int[][] prerequisites) { List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i < numCourses; i++) adj.add(new ArrayList<>()); for (int[] p : prerequisites) adj.get(p[0]).add(p[1]);
int[] state = new int[numCourses];
for (int course = 0; course < numCourses; course++) { if (hasCycle(course, adj, state)) return false; } return true; }
static boolean hasCycle(int node, List<List<Integer>> adj, int[] state) { if (state[node] == 1) return true; if (state[node] == 2) return false; state[node] = 1; for (int neighbor : adj.get(node)) { if (hasCycle(neighbor, adj, state)) return true; } state[node] = 2; return false; }
public static void main(String[] args) { System.out.println(canFinish(2, new int[][]{{1, 0}})); // true System.out.println(canFinish(2, new int[][]{{1, 0}, {0, 1}})); // false }}fun canFinish(numCourses: Int, prerequisites: List<List<Int>>): Boolean { val adj = Array(numCourses) { mutableListOf<Int>() } for (p in prerequisites) adj[p[0]].add(p[1])
val state = IntArray(numCourses)
fun hasCycle(node: Int): Boolean { if (state[node] == 1) return true if (state[node] == 2) return false state[node] = 1 for (neighbor in adj[node]) { if (hasCycle(neighbor)) return true } state[node] = 2 return false }
for (course in 0 until numCourses) { if (hasCycle(course)) return false } return true}
fun main() { println(canFinish(2, listOf(listOf(1, 0)))) // true println(canFinish(2, listOf(listOf(1, 0), listOf(0, 1)))) // false}bool canFinish(int numCourses, List<List<int>> prerequisites) { final adj = List.generate(numCourses, (_) => <int>[]); for (var p in prerequisites) adj[p[0]].add(p[1]);
final state = List<int>.filled(numCourses, 0);
bool hasCycle(int node) { if (state[node] == 1) return true; if (state[node] == 2) return false; state[node] = 1; for (var neighbor in adj[node]) { if (hasCycle(neighbor)) return true; } state[node] = 2; return false; }
for (int course = 0; course < numCourses; course++) { if (hasCycle(course)) return false; } return true;}
void main() { print(canFinish(2, [[1, 0]])); // true print(canFinish(2, [[1, 0], [0, 1]])); // false}165. Tập con (Subsets)
Độ khó: Trung bình · Chủ đề: Backtracking
Cho một list các số nguyên phân biệt nums, trả về tất cả các tập con (power set), bao gồm tập rỗng và chính nó.
Ví dụ 1:
Input: nums=[1,2,3]Output: [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]Giải thích: Có 2^3 = 8 tập con.Ví dụ 2:
Input: nums=[0]Output: [[], [0]]Giải thích: Có 2^1 = 2 tập con.Ràng buộc:
1 <= len(nums) <= 10- Các phần tử trong
numslà duy nhất.
Xem đáp án
def subsets(nums): result = []
def backtrack(start, path): result.append(path[:]) for i in range(start, len(nums)): path.append(nums[i]) backtrack(i + 1, path) path.pop()
backtrack(0, []) return result
print(subsets([1, 2, 3]))#include <iostream>#include <vector>using namespace std;
void backtrack(int start, vector<int>& nums, vector<int>& path, vector<vector<int>>& result) { result.push_back(path); for (int i = start; i < (int)nums.size(); i++) { path.push_back(nums[i]); backtrack(i + 1, nums, path, result); path.pop_back(); }}
vector<vector<int>> subsets(vector<int>& nums) { vector<vector<int>> result; vector<int> path; backtrack(0, nums, path, result); return result;}
int main() { vector<int> nums = {1, 2, 3}; for (auto& s : subsets(nums)) { cout << "["; for (int x : s) cout << x << " "; cout << "] "; } cout << endl; return 0;}import java.util.*;
public class Main { static void backtrack(int start, int[] nums, List<Integer> path, List<List<Integer>> result) { result.add(new ArrayList<>(path)); for (int i = start; i < nums.length; i++) { path.add(nums[i]); backtrack(i + 1, nums, path, result); path.remove(path.size() - 1); } }
static List<List<Integer>> subsets(int[] nums) { List<List<Integer>> result = new ArrayList<>(); backtrack(0, nums, new ArrayList<>(), result); return result; }
public static void main(String[] args) { System.out.println(subsets(new int[]{1, 2, 3})); }}fun backtrack(start: Int, nums: List<Int>, path: MutableList<Int>, result: MutableList<List<Int>>) { result.add(path.toList()) for (i in start until nums.size) { path.add(nums[i]) backtrack(i + 1, nums, path, result) path.removeAt(path.size - 1) }}
fun subsets(nums: List<Int>): List<List<Int>> { val result = mutableListOf<List<Int>>() backtrack(0, nums, mutableListOf(), result) return result}
fun main() { println(subsets(listOf(1, 2, 3)))}void backtrack(int start, List<int> nums, List<int> path, List<List<int>> result) { result.add(List.from(path)); for (int i = start; i < nums.length; i++) { path.add(nums[i]); backtrack(i + 1, nums, path, result); path.removeLast(); }}
List<List<int>> subsets(List<int> nums) { final result = <List<int>>[]; backtrack(0, nums, [], result); return result;}
void main() { print(subsets([1, 2, 3]));}166. Tìm kiếm từ trên lưới (Word Search)
Độ khó: Khó · Chủ đề: Backtracking trên lưới
Cho một lưới ký tự 2 chiều board và một chuỗi word, kiểm tra word có thể được tạo thành từ các ký tự liền kề (4 hướng, không dùng lại 1 ô 2 lần) trên lưới hay không.
Ví dụ 1:
Input: board=[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word="ABCCED"Output: TrueVí dụ 2:
Input: board=[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word="ABCB"Output: FalseGiải thích: Chữ B thứ 2 sẽ phải dùng lại ô B đầu tiên, không hợp lệ.Ràng buộc:
1 <= số hàng, số cột <= 61 <= len(word) <= 15
Xem đáp án
def exist(board, word): rows, cols = len(board), len(board[0])
def backtrack(r, c, i): if i == len(word): return True if not (0 <= r < rows and 0 <= c < cols) or board[r][c] != word[i]: return False
temp, board[r][c] = board[r][c], "#" found = (backtrack(r + 1, c, i + 1) or backtrack(r - 1, c, i + 1) or backtrack(r, c + 1, i + 1) or backtrack(r, c - 1, i + 1)) board[r][c] = temp return found
for r in range(rows): for c in range(cols): if backtrack(r, c, 0): return True return False
board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]]print(exist(board, "ABCCED")) # Trueprint(exist(board, "ABCB")) # False#include <iostream>#include <vector>#include <string>using namespace std;
bool backtrack(vector<vector<char>>& board, const string& word, int r, int c, int i) { int rows = board.size(), cols = board[0].size(); if (i == (int)word.size()) return true; if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != word[i]) return false;
char temp = board[r][c]; board[r][c] = '#'; bool found = backtrack(board, word, r + 1, c, i + 1) || backtrack(board, word, r - 1, c, i + 1) || backtrack(board, word, r, c + 1, i + 1) || backtrack(board, word, r, c - 1, i + 1); board[r][c] = temp; return found;}
bool exist(vector<vector<char>> board, const string& word) { int rows = board.size(), cols = board[0].size(); for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++) if (backtrack(board, word, r, c, 0)) return true; return false;}
int main() { vector<vector<char>> board = {{'A', 'B', 'C', 'E'}, {'S', 'F', 'C', 'S'}, {'A', 'D', 'E', 'E'}}; cout << boolalpha << exist(board, "ABCCED") << endl; // true cout << boolalpha << exist(board, "ABCB") << endl; // false return 0;}public class Main { static boolean backtrack(char[][] board, String word, int r, int c, int i) { int rows = board.length, cols = board[0].length; if (i == word.length()) return true; if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != word.charAt(i)) return false;
char temp = board[r][c]; board[r][c] = '#'; boolean found = backtrack(board, word, r + 1, c, i + 1) || backtrack(board, word, r - 1, c, i + 1) || backtrack(board, word, r, c + 1, i + 1) || backtrack(board, word, r, c - 1, i + 1); board[r][c] = temp; return found; }
static boolean exist(char[][] board, String word) { int rows = board.length, cols = board[0].length; for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++) if (backtrack(board, word, r, c, 0)) return true; return false; }
public static void main(String[] args) { char[][] board = {{'A', 'B', 'C', 'E'}, {'S', 'F', 'C', 'S'}, {'A', 'D', 'E', 'E'}}; System.out.println(exist(board, "ABCCED")); // true System.out.println(exist(board, "ABCB")); // false }}fun backtrack(board: Array<CharArray>, word: String, r: Int, c: Int, i: Int): Boolean { val rows = board.size val cols = board[0].size if (i == word.length) return true if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != word[i]) return false
val temp = board[r][c] board[r][c] = '#' val found = backtrack(board, word, r + 1, c, i + 1) || backtrack(board, word, r - 1, c, i + 1) || backtrack(board, word, r, c + 1, i + 1) || backtrack(board, word, r, c - 1, i + 1) board[r][c] = temp return found}
fun exist(board: Array<CharArray>, word: String): Boolean { val rows = board.size val cols = board[0].size for (r in 0 until rows) for (c in 0 until cols) if (backtrack(board, word, r, c, 0)) return true return false}
fun main() { val board = arrayOf( charArrayOf('A', 'B', 'C', 'E'), charArrayOf('S', 'F', 'C', 'S'), charArrayOf('A', 'D', 'E', 'E') ) println(exist(board, "ABCCED")) // true println(exist(board, "ABCB")) // false}bool backtrack(List<List<String>> board, String word, int r, int c, int i) { int rows = board.length, cols = board[0].length; if (i == word.length) return true; if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != word[i]) return false;
String temp = board[r][c]; board[r][c] = '#'; bool found = backtrack(board, word, r + 1, c, i + 1) || backtrack(board, word, r - 1, c, i + 1) || backtrack(board, word, r, c + 1, i + 1) || backtrack(board, word, r, c - 1, i + 1); board[r][c] = temp; return found;}
bool exist(List<List<String>> board, String word) { int rows = board.length, cols = board[0].length; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (backtrack(board, word, r, c, 0)) return true; } } return false;}
void main() { final board = [ ['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E'] ]; print(exist(board, "ABCCED")); // true print(exist(board, "ABCB")); // false}167. Bài toán N-Quân hậu (N-Queens)
Độ khó: Khó · Chủ đề: Backtracking
Cho số nguyên n, đặt n quân hậu trên bàn cờ n x n sao cho không có 2 quân hậu nào tấn công nhau (cùng hàng, cùng cột, cùng đường chéo). Trả về số lượng cách đặt khác nhau.
Ví dụ 1:
Input: n=4Output: 2Giải thích: Có đúng 2 cách đặt 4 quân hậu hợp lệ trên bàn cờ 4x4.Ví dụ 2:
Input: n=1Output: 1Ràng buộc:
1 <= n <= 9
Xem đáp án
def total_n_queens(n): cols, diag1, diag2 = set(), set(), set() count = 0
def backtrack(row): nonlocal count if row == n: count += 1 return for col in range(n): if col in cols or (row - col) in diag1 or (row + col) in diag2: continue cols.add(col) diag1.add(row - col) diag2.add(row + col) backtrack(row + 1) cols.remove(col) diag1.remove(row - col) diag2.remove(row + col)
backtrack(0) return count
print(total_n_queens(4)) # 2print(total_n_queens(1)) # 1#include <iostream>#include <unordered_set>using namespace std;
int count_ = 0;unordered_set<int> cols, diag1, diag2;
void backtrack(int row, int n) { if (row == n) { count_++; return; } for (int col = 0; col < n; col++) { if (cols.count(col) || diag1.count(row - col) || diag2.count(row + col)) continue; cols.insert(col); diag1.insert(row - col); diag2.insert(row + col); backtrack(row + 1, n); cols.erase(col); diag1.erase(row - col); diag2.erase(row + col); }}
int totalNQueens(int n) { count_ = 0; cols.clear(); diag1.clear(); diag2.clear(); backtrack(0, n); return count_;}
int main() { cout << totalNQueens(4) << endl; // 2 cout << totalNQueens(1) << endl; // 1 return 0;}import java.util.*;
public class Main { static int count; static Set<Integer> cols, diag1, diag2;
static void backtrack(int row, int n) { if (row == n) { count++; return; } for (int col = 0; col < n; col++) { if (cols.contains(col) || diag1.contains(row - col) || diag2.contains(row + col)) continue; cols.add(col); diag1.add(row - col); diag2.add(row + col); backtrack(row + 1, n); cols.remove(col); diag1.remove(row - col); diag2.remove(row + col); } }
static int totalNQueens(int n) { count = 0; cols = new HashSet<>(); diag1 = new HashSet<>(); diag2 = new HashSet<>(); backtrack(0, n); return count; }
public static void main(String[] args) { System.out.println(totalNQueens(4)); // 2 System.out.println(totalNQueens(1)); // 1 }}fun totalNQueens(n: Int): Int { val cols = mutableSetOf<Int>() val diag1 = mutableSetOf<Int>() val diag2 = mutableSetOf<Int>() var count = 0
fun backtrack(row: Int) { if (row == n) { count++ return } for (col in 0 until n) { if (col in cols || (row - col) in diag1 || (row + col) in diag2) continue cols.add(col); diag1.add(row - col); diag2.add(row + col) backtrack(row + 1) cols.remove(col); diag1.remove(row - col); diag2.remove(row + col) } }
backtrack(0) return count}
fun main() { println(totalNQueens(4)) // 2 println(totalNQueens(1)) // 1}int totalNQueens(int n) { final cols = <int>{}; final diag1 = <int>{}; final diag2 = <int>{}; int count = 0;
void backtrack(int row) { if (row == n) { count++; return; } for (int col = 0; col < n; col++) { if (cols.contains(col) || diag1.contains(row - col) || diag2.contains(row + col)) continue; cols.add(col); diag1.add(row - col); diag2.add(row + col); backtrack(row + 1); cols.remove(col); diag1.remove(row - col); diag2.remove(row + col); } }
backtrack(0); return count;}
void main() { print(totalNQueens(4)); // 2 print(totalNQueens(1)); // 1}168. Hoán vị không trùng lặp (Permutations II)
Độ khó: Khó · Chủ đề: Backtracking
Cho một list số nguyên nums có thể chứa phần tử trùng lặp, trả về tất cả các hoán vị khác nhau (không lặp lại hoán vị giống hệt nhau).
Ví dụ 1:
Input: nums=[1,1,2]Output: [[1,1,2], [1,2,1], [2,1,1]]Giải thích: Dù có 3! = 6 cách sắp xếp vị trí, chỉ có 3 hoán vị thực sự khác nhau vì có 2 số 1 trùng nhau.Ví dụ 2:
Input: nums=[1,2,3]Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]Ràng buộc:
1 <= len(nums) <= 8
Xem đáp án
def permute_unique(nums): nums.sort() result = [] used = [False] * len(nums)
def backtrack(path): if len(path) == len(nums): result.append(path[:]) return for i in range(len(nums)): if used[i]: continue # Bỏ qua nhánh trùng: nếu số hiện tại giống số trước và số trước chưa dùng ở nhánh này if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]: continue used[i] = True path.append(nums[i]) backtrack(path) path.pop() used[i] = False
backtrack([]) return result
print(permute_unique([1, 1, 2]))#include <iostream>#include <vector>#include <algorithm>using namespace std;
void backtrack(vector<int>& nums, vector<bool>& used, vector<int>& path, vector<vector<int>>& result) { if ((int)path.size() == (int)nums.size()) { result.push_back(path); return; } for (int i = 0; i < (int)nums.size(); i++) { if (used[i]) continue; if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; used[i] = true; path.push_back(nums[i]); backtrack(nums, used, path, result); path.pop_back(); used[i] = false; }}
vector<vector<int>> permuteUnique(vector<int> nums) { sort(nums.begin(), nums.end()); vector<vector<int>> result; vector<bool> used(nums.size(), false); vector<int> path; backtrack(nums, used, path, result); return result;}
int main() { vector<int> nums = {1, 1, 2}; for (auto& perm : permuteUnique(nums)) { cout << "["; for (int x : perm) cout << x << " "; cout << "] "; } cout << endl; return 0;}import java.util.*;
public class Main { static void backtrack(int[] nums, boolean[] used, List<Integer> path, List<List<Integer>> result) { if (path.size() == nums.length) { result.add(new ArrayList<>(path)); return; } for (int i = 0; i < nums.length; i++) { if (used[i]) continue; if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; used[i] = true; path.add(nums[i]); backtrack(nums, used, path, result); path.remove(path.size() - 1); used[i] = false; } }
static List<List<Integer>> permuteUnique(int[] nums) { Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); backtrack(nums, new boolean[nums.length], new ArrayList<>(), result); return result; }
public static void main(String[] args) { System.out.println(permuteUnique(new int[]{1, 1, 2})); }}fun permuteUnique(nums: IntArray): List<List<Int>> { nums.sort() val result = mutableListOf<List<Int>>() val used = BooleanArray(nums.size) val path = mutableListOf<Int>()
fun backtrack() { if (path.size == nums.size) { result.add(path.toList()) return } for (i in nums.indices) { if (used[i]) continue if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue used[i] = true path.add(nums[i]) backtrack() path.removeAt(path.size - 1) used[i] = false } }
backtrack() return result}
fun main() { println(permuteUnique(intArrayOf(1, 1, 2)))}void backtrack(List<int> nums, List<bool> used, List<int> path, List<List<int>> result) { if (path.length == nums.length) { result.add(List.from(path)); return; } for (int i = 0; i < nums.length; i++) { if (used[i]) continue; if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; used[i] = true; path.add(nums[i]); backtrack(nums, used, path, result); path.removeLast(); used[i] = false; }}
List<List<int>> permuteUnique(List<int> nums) { nums.sort(); final result = <List<int>>[]; backtrack(nums, List.filled(nums.length, false), [], result); return result;}
void main() { print(permuteUnique([1, 1, 2]));}169. Tổng tổ hợp (Combination Sum)
Độ khó: Khó · Chủ đề: Backtracking
Cho list số nguyên dương phân biệt candidates và target, tìm tất cả các tổ hợp (được phép dùng lại phần tử nhiều lần) có tổng bằng target.
Ví dụ 1:
Input: candidates=[2,3,6,7], target=7Output: [[2,2,3], [7]]Ví dụ 2:
Input: candidates=[2,3,5], target=8Output: [[2,2,2,2], [2,3,3], [3,5]]Ràng buộc:
1 <= len(candidates) <= 30, các phần tử phân biệt và dương.1 <= target <= 40
Xem đáp án
def combination_sum(candidates, target): result = []
def backtrack(start, remaining, path): if remaining == 0: result.append(path[:]) return if remaining < 0: return for i in range(start, len(candidates)): path.append(candidates[i]) backtrack(i, remaining - candidates[i], path) # i (không phải i+1) để cho phép dùng lại path.pop()
backtrack(0, target, []) return result
print(combination_sum([2, 3, 6, 7], 7)) # [[2, 2, 3], [7]]#include <iostream>#include <vector>using namespace std;
void backtrack(vector<int>& candidates, int start, int remaining, vector<int>& path, vector<vector<int>>& result) { if (remaining == 0) { result.push_back(path); return; } if (remaining < 0) return; for (int i = start; i < (int)candidates.size(); i++) { path.push_back(candidates[i]); backtrack(candidates, i, remaining - candidates[i], path, result); path.pop_back(); }}
vector<vector<int>> combinationSum(vector<int> candidates, int target) { vector<vector<int>> result; vector<int> path; backtrack(candidates, 0, target, path, result); return result;}
int main() { vector<int> candidates = {2, 3, 6, 7}; for (auto& combo : combinationSum(candidates, 7)) { cout << "["; for (int x : combo) cout << x << " "; cout << "] "; } cout << endl; // [2 2 3 ] [7 ] return 0;}import java.util.*;
public class Main { static void backtrack(int[] candidates, int start, int remaining, List<Integer> path, List<List<Integer>> result) { if (remaining == 0) { result.add(new ArrayList<>(path)); return; } if (remaining < 0) return; for (int i = start; i < candidates.length; i++) { path.add(candidates[i]); backtrack(candidates, i, remaining - candidates[i], path, result); path.remove(path.size() - 1); } }
static List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); backtrack(candidates, 0, target, new ArrayList<>(), result); return result; }
public static void main(String[] args) { System.out.println(combinationSum(new int[]{2, 3, 6, 7}, 7)); // [[2, 2, 3], [7]] }}fun combinationSum(candidates: IntArray, target: Int): List<List<Int>> { val result = mutableListOf<List<Int>>() val path = mutableListOf<Int>()
fun backtrack(start: Int, remaining: Int) { if (remaining == 0) { result.add(path.toList()) return } if (remaining < 0) return for (i in start until candidates.size) { path.add(candidates[i]) backtrack(i, remaining - candidates[i]) path.removeAt(path.size - 1) } }
backtrack(0, target) return result}
fun main() { println(combinationSum(intArrayOf(2, 3, 6, 7), 7)) // [[2, 2, 3], [7]]}void backtrack(List<int> candidates, int start, int remaining, List<int> path, List<List<int>> result) { if (remaining == 0) { result.add(List.from(path)); return; } if (remaining < 0) return; for (int i = start; i < candidates.length; i++) { path.add(candidates[i]); backtrack(candidates, i, remaining - candidates[i], path, result); path.removeLast(); }}
List<List<int>> combinationSum(List<int> candidates, int target) { final result = <List<int>>[]; backtrack(candidates, 0, target, [], result); return result;}
void main() { print(combinationSum([2, 3, 6, 7], 7)); // [[2, 2, 3], [7]]}170. Giải Sudoku (Sudoku Solver)
Độ khó: Khó · Chủ đề: Backtracking
Cho một bảng Sudoku 9x9 với một số ô đã điền số ('1'-'9') và các ô trống là '.'. Điền các ô trống sao cho thỏa quy tắc Sudoku (mỗi hàng, cột, khối 3x3 chứa đủ 1-9 không lặp). Trả về bảng đã giải.
Ví dụ 1:
Input: board có nhiều ô "." và một số số đã điền sẵn hợp lệOutput: board đầy đủ, mỗi hàng/cột/khối 3x3 chứa các số 1-9 không lặp lạiVí dụ 2:
Input: board đã điền đầy đủ và hợp lệ sẵn (không có ô ".")Output: giữ nguyên board vì không cần điền gì thêmRàng buộc:
- Bảng luôn có kích thước cố định 9x9.
- Đề bài đảm bảo có đúng 1 lời giải.
Xem đáp án
def solve_sudoku(board): def is_valid(r, c, val): for i in range(9): if board[r][i] == val or board[i][c] == val: return False box_r, box_c = 3 * (r // 3), 3 * (c // 3) for i in range(box_r, box_r + 3): for j in range(box_c, box_c + 3): if board[i][j] == val: return False return True
def backtrack(): for r in range(9): for c in range(9): if board[r][c] == ".": for val in "123456789": if is_valid(r, c, val): board[r][c] = val if backtrack(): return True board[r][c] = "." return False return True
backtrack() return board#include <iostream>#include <vector>using namespace std;
bool isValid(vector<vector<char>>& board, int r, int c, char val) { for (int i = 0; i < 9; i++) { if (board[r][i] == val || board[i][c] == val) return false; } int boxR = 3 * (r / 3), boxC = 3 * (c / 3); for (int i = boxR; i < boxR + 3; i++) for (int j = boxC; j < boxC + 3; j++) if (board[i][j] == val) return false; return true;}
bool backtrack(vector<vector<char>>& board) { for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { if (board[r][c] == '.') { for (char val = '1'; val <= '9'; val++) { if (isValid(board, r, c, val)) { board[r][c] = val; if (backtrack(board)) return true; board[r][c] = '.'; } } return false; } } } return true;}
vector<vector<char>> solveSudoku(vector<vector<char>> board) { backtrack(board); return board;}public class Main { static boolean isValid(char[][] board, int r, int c, char val) { for (int i = 0; i < 9; i++) { if (board[r][i] == val || board[i][c] == val) return false; } int boxR = 3 * (r / 3), boxC = 3 * (c / 3); for (int i = boxR; i < boxR + 3; i++) for (int j = boxC; j < boxC + 3; j++) if (board[i][j] == val) return false; return true; }
static boolean backtrack(char[][] board) { for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { if (board[r][c] == '.') { for (char val = '1'; val <= '9'; val++) { if (isValid(board, r, c, val)) { board[r][c] = val; if (backtrack(board)) return true; board[r][c] = '.'; } } return false; } } } return true; }
static char[][] solveSudoku(char[][] board) { backtrack(board); return board; }}fun isValid(board: Array<CharArray>, r: Int, c: Int, v: Char): Boolean { for (i in 0 until 9) { if (board[r][i] == v || board[i][c] == v) return false } val boxR = 3 * (r / 3) val boxC = 3 * (c / 3) for (i in boxR until boxR + 3) for (j in boxC until boxC + 3) if (board[i][j] == v) return false return true}
fun backtrack(board: Array<CharArray>): Boolean { for (r in 0 until 9) { for (c in 0 until 9) { if (board[r][c] == '.') { for (v in '1'..'9') { if (isValid(board, r, c, v)) { board[r][c] = v if (backtrack(board)) return true board[r][c] = '.' } } return false } } } return true}
fun solveSudoku(board: Array<CharArray>): Array<CharArray> { backtrack(board) return board}bool isValid(List<List<String>> board, int r, int c, String val) { for (int i = 0; i < 9; i++) { if (board[r][i] == val || board[i][c] == val) return false; } int boxR = 3 * (r ~/ 3), boxC = 3 * (c ~/ 3); for (int i = boxR; i < boxR + 3; i++) { for (int j = boxC; j < boxC + 3; j++) { if (board[i][j] == val) return false; } } return true;}
bool backtrack(List<List<String>> board) { for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { if (board[r][c] == '.') { for (int v = 1; v <= 9; v++) { final val = v.toString(); if (isValid(board, r, c, val)) { board[r][c] = val; if (backtrack(board)) return true; board[r][c] = '.'; } } return false; } } } return true;}
List<List<String>> solveSudoku(List<List<String>> board) { backtrack(board); return board;}171. Thứ tự học môn học (Course Schedule II)
Độ khó: Khó · Chủ đề: Đồ thị (Topological Sort)
Tương tự bài “Lịch học có thể hoàn thành”, nhưng thay vì trả về True/False, hãy trả về một thứ tự học hợp lệ của tất cả các môn. Nếu không thể hoàn thành (có chu trình), trả về list rỗng.
Ví dụ 1:
Input: numCourses=4, prerequisites=[[1,0],[2,0],[3,1],[3,2]]Output: [0, 1, 2, 3]Giải thích: Học 0 trước, rồi 1 và 2 (theo thứ tự nào cũng được), cuối cùng học 3.Ví dụ 2:
Input: numCourses=2, prerequisites=[[1,0],[0,1]]Output: []Giải thích: Có chu trình 0 -> 1 -> 0, không thể hoàn thành.Ràng buộc:
1 <= numCourses <= 2000
Xem đáp án
from collections import deque
def find_order(num_courses, prerequisites): adj = {i: [] for i in range(num_courses)} indegree = [0] * num_courses for a, b in prerequisites: adj[b].append(a) indegree[a] += 1
queue = deque(i for i in range(num_courses) if indegree[i] == 0) order = []
while queue: node = queue.popleft() order.append(node) for neighbor in adj[node]: indegree[neighbor] -= 1 if indegree[neighbor] == 0: queue.append(neighbor)
return order if len(order) == num_courses else []
print(find_order(4, [[1, 0], [2, 0], [3, 1], [3, 2]])) # [0, 1, 2, 3]print(find_order(2, [[1, 0], [0, 1]])) # []#include <iostream>#include <vector>#include <queue>using namespace std;
vector<int> findOrder(int numCourses, vector<pair<int, int>> prerequisites) { vector<vector<int>> adj(numCourses); vector<int> indegree(numCourses, 0); for (auto& [a, b] : prerequisites) { adj[b].push_back(a); indegree[a]++; }
queue<int> q; for (int i = 0; i < numCourses; i++) if (indegree[i] == 0) q.push(i);
vector<int> order; while (!q.empty()) { int node = q.front(); q.pop(); order.push_back(node); for (int neighbor : adj[node]) { if (--indegree[neighbor] == 0) q.push(neighbor); } }
return (int)order.size() == numCourses ? order : vector<int>();}
int main() { for (int x : findOrder(4, {{1, 0}, {2, 0}, {3, 1}, {3, 2}})) cout << x << " "; cout << endl; // 0 1 2 3 auto empty = findOrder(2, {{1, 0}, {0, 1}}); cout << empty.size() << endl; // 0 return 0;}import java.util.*;
public class Main { static List<Integer> findOrder(int numCourses, int[][] prerequisites) { List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i < numCourses; i++) adj.add(new ArrayList<>()); int[] indegree = new int[numCourses]; for (int[] p : prerequisites) { adj.get(p[1]).add(p[0]); indegree[p[0]]++; }
Queue<Integer> queue = new LinkedList<>(); for (int i = 0; i < numCourses; i++) if (indegree[i] == 0) queue.add(i);
List<Integer> order = new ArrayList<>(); while (!queue.isEmpty()) { int node = queue.poll(); order.add(node); for (int neighbor : adj.get(node)) { if (--indegree[neighbor] == 0) queue.add(neighbor); } }
return order.size() == numCourses ? order : new ArrayList<>(); }
public static void main(String[] args) { System.out.println(findOrder(4, new int[][]{{1, 0}, {2, 0}, {3, 1}, {3, 2}})); // [0, 1, 2, 3] System.out.println(findOrder(2, new int[][]{{1, 0}, {0, 1}})); // [] }}fun findOrder(numCourses: Int, prerequisites: List<Pair<Int, Int>>): List<Int> { val adj = Array(numCourses) { mutableListOf<Int>() } val indegree = IntArray(numCourses) for ((a, b) in prerequisites) { adj[b].add(a) indegree[a]++ }
val queue = ArrayDeque<Int>() for (i in 0 until numCourses) if (indegree[i] == 0) queue.add(i)
val order = mutableListOf<Int>() while (queue.isNotEmpty()) { val node = queue.removeFirst() order.add(node) for (neighbor in adj[node]) { if (--indegree[neighbor] == 0) queue.add(neighbor) } }
return if (order.size == numCourses) order else emptyList()}
fun main() { println(findOrder(4, listOf(1 to 0, 2 to 0, 3 to 1, 3 to 2))) // [0, 1, 2, 3] println(findOrder(2, listOf(1 to 0, 0 to 1))) // []}List<int> findOrder(int numCourses, List<List<int>> prerequisites) { final adj = List.generate(numCourses, (_) => <int>[]); final indegree = List.filled(numCourses, 0); for (var p in prerequisites) { adj[p[1]].add(p[0]); indegree[p[0]]++; }
final queue = <int>[]; for (int i = 0; i < numCourses; i++) if (indegree[i] == 0) queue.add(i);
final order = <int>[]; int head = 0; while (head < queue.length) { final node = queue[head++]; order.add(node); for (var neighbor in adj[node]) { if (--indegree[neighbor] == 0) queue.add(neighbor); } }
return order.length == numCourses ? order : [];}
void main() { print(findOrder(4, [[1, 0], [2, 0], [3, 1], [3, 2]])); // [0, 1, 2, 3] print(findOrder(2, [[1, 0], [0, 1]])); // []}172. Độ trễ mạng lưới (Network Delay Time)
Độ khó: Khó · Chủ đề: Đồ thị (Dijkstra)
Có n node đánh số từ 1 đến n. times[i] = (u, v, w) nghĩa là tín hiệu đi từ u đến v mất w đơn vị thời gian (đồ thị có hướng, có trọng số). Gửi tín hiệu từ node k, tìm thời gian tối thiểu để tất cả các node nhận được tín hiệu. Nếu không thể, trả về -1.
Ví dụ 1:
Input: times=[(2,1,1),(2,3,1),(3,4,1)], n=4, k=2Output: 2Giải thích: Từ node 2, mất 1 đơn vị đến node 1 và 3, rồi từ 3 mất thêm 1 đơn vị đến 4 -> tổng 2.Ví dụ 2:
Input: times=[(1,2,1)], n=2, k=1Output: 1Ràng buộc:
1 <= k <= n <= 1001 <= w <= 100
Xem đáp án
import heapqfrom collections import defaultdict
def network_delay_time(times, n, k): adj = defaultdict(list) for u, v, w in times: adj[u].append((v, w))
dist = {} heap = [(0, k)]
while heap: d, node = heapq.heappop(heap) if node in dist: continue dist[node] = d for neighbor, weight in adj[node]: if neighbor not in dist: heapq.heappush(heap, (d + weight, neighbor))
return max(dist.values()) if len(dist) == n else -1
print(network_delay_time([(2, 1, 1), (2, 3, 1), (3, 4, 1)], 4, 2)) # 2print(network_delay_time([(1, 2, 1)], 2, 1)) # 1#include <iostream>#include <vector>#include <queue>#include <unordered_map>using namespace std;
int networkDelayTime(vector<tuple<int, int, int>> times, int n, int k) { unordered_map<int, vector<pair<int, int>>> adj; for (auto& [u, v, w] : times) adj[u].push_back({v, w});
unordered_map<int, int> dist; priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> heap; heap.push({0, k});
while (!heap.empty()) { auto [d, node] = heap.top(); heap.pop(); if (dist.count(node)) continue; dist[node] = d; for (auto& [neighbor, weight] : adj[node]) { if (!dist.count(neighbor)) heap.push({d + weight, neighbor}); } }
if ((int)dist.size() != n) return -1; int maxDist = 0; for (auto& [_, d] : dist) maxDist = max(maxDist, d); return maxDist;}
int main() { cout << networkDelayTime({{2, 1, 1}, {2, 3, 1}, {3, 4, 1}}, 4, 2) << endl; // 2 cout << networkDelayTime({{1, 2, 1}}, 2, 1) << endl; // 1 return 0;}import java.util.*;
public class Main { static int networkDelayTime(int[][] times, int n, int k) { Map<Integer, List<int[]>> adj = new HashMap<>(); for (int[] t : times) { adj.computeIfAbsent(t[0], x -> new ArrayList<>()).add(new int[]{t[1], t[2]}); }
Map<Integer, Integer> dist = new HashMap<>(); PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[0] - b[0]); heap.add(new int[]{0, k});
while (!heap.isEmpty()) { int[] cur = heap.poll(); int d = cur[0], node = cur[1]; if (dist.containsKey(node)) continue; dist.put(node, d); for (int[] edge : adj.getOrDefault(node, new ArrayList<>())) { if (!dist.containsKey(edge[0])) heap.add(new int[]{d + edge[1], edge[0]}); } }
if (dist.size() != n) return -1; return Collections.max(dist.values()); }
public static void main(String[] args) { System.out.println(networkDelayTime(new int[][]{{2, 1, 1}, {2, 3, 1}, {3, 4, 1}}, 4, 2)); // 2 System.out.println(networkDelayTime(new int[][]{{1, 2, 1}}, 2, 1)); // 1 }}import java.util.PriorityQueue
fun networkDelayTime(times: List<Triple<Int, Int, Int>>, n: Int, k: Int): Int { val adj = mutableMapOf<Int, MutableList<Pair<Int, Int>>>() for ((u, v, w) in times) adj.getOrPut(u) { mutableListOf() }.add(v to w)
val dist = mutableMapOf<Int, Int>() val heap = PriorityQueue<Pair<Int, Int>>(compareBy { it.first }) heap.add(0 to k)
while (heap.isNotEmpty()) { val (d, node) = heap.poll() if (dist.containsKey(node)) continue dist[node] = d for ((neighbor, weight) in adj.getOrDefault(node, mutableListOf())) { if (!dist.containsKey(neighbor)) heap.add((d + weight) to neighbor) } }
return if (dist.size == n) dist.values.max() else -1}
fun main() { println(networkDelayTime(listOf(Triple(2, 1, 1), Triple(2, 3, 1), Triple(3, 4, 1)), 4, 2)) // 2 println(networkDelayTime(listOf(Triple(1, 2, 1)), 2, 1)) // 1}import 'dart:collection';
int networkDelayTime(List<List<int>> times, int n, int k) { final adj = <int, List<List<int>>>{}; for (var t in times) { adj.putIfAbsent(t[0], () => []).add([t[1], t[2]]); }
final dist = <int, int>{}; final heap = PriorityQueue<List<int>>((a, b) => a[0].compareTo(b[0])); heap.add([0, k]);
while (heap.isNotEmpty) { final cur = heap.removeFirst(); final d = cur[0], node = cur[1]; if (dist.containsKey(node)) continue; dist[node] = d; for (var edge in adj[node] ?? []) { if (!dist.containsKey(edge[0])) heap.add([d + edge[1], edge[0]]); } }
if (dist.length != n) return -1; return dist.values.reduce((a, b) => a > b ? a : b);}
void main() { print(networkDelayTime([[2, 1, 1], [2, 3, 1], [3, 4, 1]], 4, 2)); // 2 print(networkDelayTime([[1, 2, 1]], 2, 1)); // 1}173. Chuyến bay rẻ nhất với K điểm dừng (Cheapest Flights Within K Stops)
Độ khó: Khó · Chủ đề: Đồ thị (Bellman-Ford biến thể)
Có n thành phố (0 đến n-1) và flights[i] = (from, to, price). Tìm giá vé rẻ nhất từ src đến dst với tối đa k điểm dừng (tức tối đa k+1 chặng bay). Nếu không thể đến, trả về -1.
Ví dụ 1:
Input: n=4, flights=[(0,1,100),(1,2,100),(2,0,100),(1,3,600),(2,3,200)], src=0, dst=3, k=1Output: 700Giải thích: Đường đi 0 -> 1 -> 3 (1 điểm dừng) giá 100+600=700, rẻ hơn đường 3 chặng.Ví dụ 2:
Input: n=3, flights=[(0,1,100),(1,2,100),(0,2,500)], src=0, dst=2, k=1Output: 200Giải thích: 0 -> 1 -> 2 với đúng 1 điểm dừng, giá 200, rẻ hơn bay thẳng 500.Ràng buộc:
1 <= n <= 100,0 <= k <= n-1
Xem đáp án
def find_cheapest_price(n, flights, src, dst, k): # Bellman-Ford giới hạn số vòng lặp = k+1 chặng bay dist = [float("inf")] * n dist[src] = 0
for _ in range(k + 1): new_dist = dist[:] for u, v, price in flights: if dist[u] != float("inf") and dist[u] + price < new_dist[v]: new_dist[v] = dist[u] + price dist = new_dist
return -1 if dist[dst] == float("inf") else dist[dst]
flights = [(0, 1, 100), (1, 2, 100), (2, 0, 100), (1, 3, 600), (2, 3, 200)]print(find_cheapest_price(4, flights, 0, 3, 1)) # 700#include <iostream>#include <vector>#include <climits>using namespace std;
int findCheapestPrice(int n, vector<tuple<int, int, int>> flights, int src, int dst, int k) { vector<long> dist(n, LONG_MAX); dist[src] = 0;
for (int i = 0; i <= k; i++) { vector<long> newDist = dist; for (auto& [u, v, price] : flights) { if (dist[u] != LONG_MAX && dist[u] + price < newDist[v]) { newDist[v] = dist[u] + price; } } dist = newDist; }
return dist[dst] == LONG_MAX ? -1 : (int)dist[dst];}
int main() { vector<tuple<int, int, int>> flights = {{0, 1, 100}, {1, 2, 100}, {2, 0, 100}, {1, 3, 600}, {2, 3, 200}}; cout << findCheapestPrice(4, flights, 0, 3, 1) << endl; // 700 return 0;}import java.util.*;
public class Main { static int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) { long[] dist = new long[n]; Arrays.fill(dist, Long.MAX_VALUE); dist[src] = 0;
for (int i = 0; i <= k; i++) { long[] newDist = dist.clone(); for (int[] f : flights) { int u = f[0], v = f[1], price = f[2]; if (dist[u] != Long.MAX_VALUE && dist[u] + price < newDist[v]) { newDist[v] = dist[u] + price; } } dist = newDist; }
return dist[dst] == Long.MAX_VALUE ? -1 : (int) dist[dst]; }
public static void main(String[] args) { int[][] flights = {{0, 1, 100}, {1, 2, 100}, {2, 0, 100}, {1, 3, 600}, {2, 3, 200}}; System.out.println(findCheapestPrice(4, flights, 0, 3, 1)); // 700 }}fun findCheapestPrice(n: Int, flights: List<Triple<Int, Int, Int>>, src: Int, dst: Int, k: Int): Int { var dist = LongArray(n) { Long.MAX_VALUE } dist[src] = 0
repeat(k + 1) { val newDist = dist.copyOf() for ((u, v, price) in flights) { if (dist[u] != Long.MAX_VALUE && dist[u] + price < newDist[v]) { newDist[v] = dist[u] + price } } dist = newDist }
return if (dist[dst] == Long.MAX_VALUE) -1 else dist[dst].toInt()}
fun main() { val flights = listOf(Triple(0, 1, 100), Triple(1, 2, 100), Triple(2, 0, 100), Triple(1, 3, 600), Triple(2, 3, 200)) println(findCheapestPrice(4, flights, 0, 3, 1)) // 700}int findCheapestPrice(int n, List<List<int>> flights, int src, int dst, int k) { var dist = List<int?>.filled(n, null); dist[src] = 0;
for (int i = 0; i <= k; i++) { final newDist = List<int?>.from(dist); for (var f in flights) { final u = f[0], v = f[1], price = f[2]; if (dist[u] != null && dist[u]! + price < (newDist[v] ?? 1 << 30)) { newDist[v] = dist[u]! + price; } } dist = newDist; }
return dist[dst] ?? -1;}
void main() { final flights = [[0, 1, 100], [1, 2, 100], [2, 0, 100], [1, 3, 600], [2, 3, 200]]; print(findCheapestPrice(4, flights, 0, 3, 1)); // 700}174. Cạnh dư thừa (Redundant Connection)
Độ khó: Khó · Chủ đề: Đồ thị (Union-Find)
Cho một cây có n đỉnh, thêm 1 cạnh thừa tạo thành 1 chu trình. Cho list edges với đúng n cạnh, tìm cạnh thừa đó (cạnh cuối cùng trong edges mà khi thêm vào sẽ tạo chu trình).
Ví dụ 1:
Input: edges=[[1,2],[1,3],[2,3]]Output: [2, 3]Giải thích: Cạnh [2,3] khi thêm vào sau cùng tạo ra chu trình 1-2-3-1.Ví dụ 2:
Input: edges=[[1,2],[2,3],[3,4],[1,4],[1,5]]Output: [1, 4]Ràng buộc:
3 <= n <= 1000- Đảm bảo
edgescó đúng 1 cạnh thừa tạo chu trình.
Xem đáp án
def find_redundant_connection(edges): parent = {}
def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x
for u, v in edges: parent.setdefault(u, u) parent.setdefault(v, v) root_u, root_v = find(u), find(v) if root_u == root_v: return [u, v] parent[root_u] = root_v
return []
print(find_redundant_connection([[1, 2], [1, 3], [2, 3]])) # [2, 3]#include <iostream>#include <vector>#include <unordered_map>using namespace std;
int find(unordered_map<int, int>& parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x;}
vector<int> findRedundantConnection(vector<vector<int>> edges) { unordered_map<int, int> parent;
for (auto& e : edges) { int u = e[0], v = e[1]; if (!parent.count(u)) parent[u] = u; if (!parent.count(v)) parent[v] = v; int rootU = find(parent, u), rootV = find(parent, v); if (rootU == rootV) return {u, v}; parent[rootU] = rootV; }
return {};}
int main() { for (int x : findRedundantConnection({{1, 2}, {1, 3}, {2, 3}})) cout << x << " "; cout << endl; // 2 3 return 0;}import java.util.*;
public class Main { static int find(Map<Integer, Integer> parent, int x) { while (parent.get(x) != x) { parent.put(x, parent.get(parent.get(x))); x = parent.get(x); } return x; }
static int[] findRedundantConnection(int[][] edges) { Map<Integer, Integer> parent = new HashMap<>();
for (int[] e : edges) { int u = e[0], v = e[1]; parent.putIfAbsent(u, u); parent.putIfAbsent(v, v); int rootU = find(parent, u), rootV = find(parent, v); if (rootU == rootV) return new int[]{u, v}; parent.put(rootU, rootV); }
return new int[]{}; }
public static void main(String[] args) { System.out.println(Arrays.toString(findRedundantConnection(new int[][]{{1, 2}, {1, 3}, {2, 3}}))); // [2, 3] }}fun find(parent: MutableMap<Int, Int>, x: Int): Int { var cur = x while (parent[cur] != cur) { parent[cur] = parent[parent[cur]!!]!! cur = parent[cur]!! } return cur}
fun findRedundantConnection(edges: List<List<Int>>): List<Int> { val parent = mutableMapOf<Int, Int>()
for ((u, v) in edges.map { it[0] to it[1] }) { parent.putIfAbsent(u, u) parent.putIfAbsent(v, v) val rootU = find(parent, u) val rootV = find(parent, v) if (rootU == rootV) return listOf(u, v) parent[rootU] = rootV }
return emptyList()}
fun main() { println(findRedundantConnection(listOf(listOf(1, 2), listOf(1, 3), listOf(2, 3)))) // [2, 3]}int find(Map<int, int> parent, int x) { int cur = x; while (parent[cur] != cur) { parent[cur] = parent[parent[cur]!]!; cur = parent[cur]!; } return cur;}
List<int> findRedundantConnection(List<List<int>> edges) { final parent = <int, int>{};
for (var e in edges) { final u = e[0], v = e[1]; parent.putIfAbsent(u, () => u); parent.putIfAbsent(v, () => v); final rootU = find(parent, u); final rootV = find(parent, v); if (rootU == rootV) return [u, v]; parent[rootU] = rootV; }
return [];}
void main() { print(findRedundantConnection([[1, 2], [1, 3], [2, 3]])); // [2, 3]}175. Số thành phần liên thông (Number of Connected Components)
Độ khó: Khó · Chủ đề: Đồ thị (Union-Find)
Cho n đỉnh đánh số từ 0 đến n-1 và list các cạnh vô hướng edges, đếm số thành phần liên thông của đồ thị.
Ví dụ 1:
Input: n=5, edges=[[0,1],[1,2],[3,4]]Output: 2Giải thích: {0,1,2} là 1 thành phần, {3,4} là 1 thành phần khác.Ví dụ 2:
Input: n=5, edges=[[0,1],[1,2],[2,3],[3,4]]Output: 1Ràng buộc:
1 <= n <= 2000
Xem đáp án
def count_components(n, edges): parent = list(range(n))
def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x
def union(x, y): root_x, root_y = find(x), find(y) if root_x != root_y: parent[root_x] = root_y
for u, v in edges: union(u, v)
return len({find(i) for i in range(n)})
print(count_components(5, [[0, 1], [1, 2], [3, 4]])) # 2#include <iostream>#include <vector>#include <unordered_set>using namespace std;
int find(vector<int>& parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x;}
void unite(vector<int>& parent, int x, int y) { int rootX = find(parent, x), rootY = find(parent, y); if (rootX != rootY) parent[rootX] = rootY;}
int countComponents(int n, vector<vector<int>> edges) { vector<int> parent(n); for (int i = 0; i < n; i++) parent[i] = i;
for (auto& e : edges) unite(parent, e[0], e[1]);
unordered_set<int> roots; for (int i = 0; i < n; i++) roots.insert(find(parent, i)); return roots.size();}
int main() { cout << countComponents(5, {{0, 1}, {1, 2}, {3, 4}}) << endl; // 2 return 0;}import java.util.*;
public class Main { static int find(int[] parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; }
static void union(int[] parent, int x, int y) { int rootX = find(parent, x), rootY = find(parent, y); if (rootX != rootY) parent[rootX] = rootY; }
static int countComponents(int n, int[][] edges) { int[] parent = new int[n]; for (int i = 0; i < n; i++) parent[i] = i;
for (int[] e : edges) union(parent, e[0], e[1]);
Set<Integer> roots = new HashSet<>(); for (int i = 0; i < n; i++) roots.add(find(parent, i)); return roots.size(); }
public static void main(String[] args) { System.out.println(countComponents(5, new int[][]{{0, 1}, {1, 2}, {3, 4}})); // 2 }}fun find(parent: IntArray, x: Int): Int { var cur = x while (parent[cur] != cur) { parent[cur] = parent[parent[cur]] cur = parent[cur] } return cur}
fun union(parent: IntArray, x: Int, y: Int) { val rootX = find(parent, x) val rootY = find(parent, y) if (rootX != rootY) parent[rootX] = rootY}
fun countComponents(n: Int, edges: List<List<Int>>): Int { val parent = IntArray(n) { it } for ((u, v) in edges.map { it[0] to it[1] }) union(parent, u, v) return (0 until n).map { find(parent, it) }.toSet().size}
fun main() { println(countComponents(5, listOf(listOf(0, 1), listOf(1, 2), listOf(3, 4)))) // 2}int find(List<int> parent, int x) { int cur = x; while (parent[cur] != cur) { parent[cur] = parent[parent[cur]]; cur = parent[cur]; } return cur;}
void union(List<int> parent, int x, int y) { final rootX = find(parent, x); final rootY = find(parent, y); if (rootX != rootY) parent[rootX] = rootY;}
int countComponents(int n, List<List<int>> edges) { final parent = List.generate(n, (i) => i); for (var e in edges) union(parent, e[0], e[1]); return {for (int i = 0; i < n; i++) find(parent, i)}.length;}
void main() { print(countComponents(5, [[0, 1], [1, 2], [3, 4]])); // 2}176. Chuỗi biến đổi từ (Word Ladder)
Độ khó: Khó · Chủ đề: Đồ thị (BFS tìm đường ngắn nhất)
Cho beginWord, endWord và một wordList. Tìm độ dài đường biến đổi ngắn nhất từ beginWord đến endWord, mỗi bước chỉ được đổi 1 ký tự, và từ mới sau khi đổi phải nằm trong wordList. Nếu không thể, trả về 0.
Ví dụ 1:
Input: beginWord="hit", endWord="cog", wordList=["hot","dot","dog","lot","log","cog"]Output: 5Giải thích: hit -> hot -> dot -> dog -> cog (5 từ, 4 bước biến đổi).Ví dụ 2:
Input: beginWord="hit", endWord="cog", wordList=["hot","dot","dog","lot","log"]Output: 0Giải thích: "cog" không có trong wordList nên không thể đến đích.Ràng buộc:
1 <= len(beginWord) <= 10, tất cả các từ cùng độ dài.1 <= len(wordList) <= 5000
Xem đáp án
from collections import dequeimport string
def ladder_length(begin_word, end_word, word_list): word_set = set(word_list) if end_word not in word_set: return 0
queue = deque([(begin_word, 1)]) visited = {begin_word}
while queue: word, steps = queue.popleft() if word == end_word: return steps for i in range(len(word)): for c in string.ascii_lowercase: new_word = word[:i] + c + word[i + 1:] if new_word in word_set and new_word not in visited: visited.add(new_word) queue.append((new_word, steps + 1))
return 0
print(ladder_length("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"])) # 5#include <iostream>#include <vector>#include <string>#include <unordered_set>#include <queue>using namespace std;
int ladderLength(string beginWord, string endWord, vector<string> wordList) { unordered_set<string> wordSet(wordList.begin(), wordList.end()); if (!wordSet.count(endWord)) return 0;
queue<pair<string, int>> q; q.push({beginWord, 1}); unordered_set<string> visited = {beginWord};
while (!q.empty()) { auto [word, steps] = q.front(); q.pop(); if (word == endWord) return steps; for (int i = 0; i < (int)word.size(); i++) { string newWord = word; for (char c = 'a'; c <= 'z'; c++) { newWord[i] = c; if (wordSet.count(newWord) && !visited.count(newWord)) { visited.insert(newWord); q.push({newWord, steps + 1}); } } } }
return 0;}
int main() { vector<string> wordList = {"hot", "dot", "dog", "lot", "log", "cog"}; cout << ladderLength("hit", "cog", wordList) << endl; // 5 return 0;}import java.util.*;
public class Main { static int ladderLength(String beginWord, String endWord, List<String> wordList) { Set<String> wordSet = new HashSet<>(wordList); if (!wordSet.contains(endWord)) return 0;
Queue<Object[]> queue = new LinkedList<>(); queue.add(new Object[]{beginWord, 1}); Set<String> visited = new HashSet<>(); visited.add(beginWord);
while (!queue.isEmpty()) { Object[] cur = queue.poll(); String word = (String) cur[0]; int steps = (int) cur[1]; if (word.equals(endWord)) return steps; for (int i = 0; i < word.length(); i++) { char[] chars = word.toCharArray(); for (char c = 'a'; c <= 'z'; c++) { chars[i] = c; String newWord = new String(chars); if (wordSet.contains(newWord) && !visited.contains(newWord)) { visited.add(newWord); queue.add(new Object[]{newWord, steps + 1}); } } } }
return 0; }
public static void main(String[] args) { List<String> wordList = Arrays.asList("hot", "dot", "dog", "lot", "log", "cog"); System.out.println(ladderLength("hit", "cog", wordList)); // 5 }}import java.util.LinkedList
fun ladderLength(beginWord: String, endWord: String, wordList: List<String>): Int { val wordSet = wordList.toHashSet() if (endWord !in wordSet) return 0
val queue = LinkedList<Pair<String, Int>>() queue.add(beginWord to 1) val visited = mutableSetOf(beginWord)
while (queue.isNotEmpty()) { val (word, steps) = queue.poll() if (word == endWord) return steps for (i in word.indices) { for (c in 'a'..'z') { val newWord = word.substring(0, i) + c + word.substring(i + 1) if (newWord in wordSet && newWord !in visited) { visited.add(newWord) queue.add(newWord to steps + 1) } } } }
return 0}
fun main() { val wordList = listOf("hot", "dot", "dog", "lot", "log", "cog") println(ladderLength("hit", "cog", wordList)) // 5}int ladderLength(String beginWord, String endWord, List<String> wordList) { final wordSet = wordList.toSet(); if (!wordSet.contains(endWord)) return 0;
final queue = <List<dynamic>>[[beginWord, 1]]; final visited = <String>{beginWord}; int head = 0;
while (head < queue.length) { final word = queue[head][0] as String; final steps = queue[head][1] as int; head++; if (word == endWord) return steps; for (int i = 0; i < word.length; i++) { for (int code = 97; code <= 122; code++) { final c = String.fromCharCode(code); final newWord = word.substring(0, i) + c + word.substring(i + 1); if (wordSet.contains(newWord) && !visited.contains(newWord)) { visited.add(newWord); queue.add([newWord, steps + 1]); } } } }
return 0;}
void main() { final wordList = ["hot", "dot", "dog", "lot", "log", "cog"]; print(ladderLength("hit", "cog", wordList)); // 5}177. Dòng nước chảy tới 2 đại dương (Pacific Atlantic Water Flow)
Độ khó: Khó · Chủ đề: Đồ thị (DFS/BFS đa nguồn)
Cho lưới độ cao heights, Thái Bình Dương giáp cạnh trên và trái, Đại Tây Dương giáp cạnh dưới và phải. Nước chảy từ ô cao xuống ô thấp hơn hoặc bằng (4 hướng). Tìm các ô mà nước từ đó có thể chảy tới cả 2 đại dương.
Ví dụ 1:
Input: heights=[[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]Ví dụ 2:
Input: heights=[[1]]Output: [[0,0]]Giải thích: Lưới chỉ có 1 ô, nó giáp cả 2 đại dương cùng lúc.Ràng buộc:
1 <= số hàng, số cột <= 200
Xem đáp án
def pacific_atlantic(heights): if not heights: return [] rows, cols = len(heights), len(heights[0]) pacific, atlantic = set(), set()
def dfs(r, c, visited, prev_height): if (r < 0 or r >= rows or c < 0 or c >= cols or (r, c) in visited or heights[r][c] < prev_height): return visited.add((r, c)) for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): dfs(r + dr, c + dc, visited, heights[r][c])
for c in range(cols): dfs(0, c, pacific, heights[0][c]) dfs(rows - 1, c, atlantic, heights[rows - 1][c]) for r in range(rows): dfs(r, 0, pacific, heights[r][0]) dfs(r, cols - 1, atlantic, heights[r][cols - 1])
return [list(cell) for cell in pacific & atlantic]
print(sorted(pacific_atlantic([[1]]))) # [[0, 0]]#include <iostream>#include <vector>#include <set>using namespace std;
void dfs(vector<vector<int>>& heights, int r, int c, set<pair<int,int>>& visited, int prevHeight, int rows, int cols) { if (r < 0 || r >= rows || c < 0 || c >= cols || visited.count({r, c}) || heights[r][c] < prevHeight) return; visited.insert({r, c}); int dr[] = {1, -1, 0, 0}, dc[] = {0, 0, 1, -1}; for (int i = 0; i < 4; i++) dfs(heights, r + dr[i], c + dc[i], visited, heights[r][c], rows, cols);}
vector<vector<int>> pacificAtlantic(vector<vector<int>> heights) { if (heights.empty()) return {}; int rows = heights.size(), cols = heights[0].size(); set<pair<int,int>> pacific, atlantic;
for (int c = 0; c < cols; c++) { dfs(heights, 0, c, pacific, heights[0][c], rows, cols); dfs(heights, rows - 1, c, atlantic, heights[rows - 1][c], rows, cols); } for (int r = 0; r < rows; r++) { dfs(heights, r, 0, pacific, heights[r][0], rows, cols); dfs(heights, r, cols - 1, atlantic, heights[r][cols - 1], rows, cols); }
vector<vector<int>> result; for (auto& cell : pacific) { if (atlantic.count(cell)) result.push_back({cell.first, cell.second}); } return result;}
int main() { vector<vector<int>> heights = {{1}}; for (auto& cell : pacificAtlantic(heights)) cout << "[" << cell[0] << "," << cell[1] << "] "; cout << endl; // [0,0] return 0;}import java.util.*;
public class Main { static void dfs(int[][] heights, int r, int c, Set<List<Integer>> visited, int prevHeight, int rows, int cols) { List<Integer> cell = Arrays.asList(r, c); if (r < 0 || r >= rows || c < 0 || c >= cols || visited.contains(cell) || heights[r][c] < prevHeight) return; visited.add(cell); int[] dr = {1, -1, 0, 0}, dc = {0, 0, 1, -1}; for (int i = 0; i < 4; i++) dfs(heights, r + dr[i], c + dc[i], visited, heights[r][c], rows, cols); }
static List<List<Integer>> pacificAtlantic(int[][] heights) { if (heights.length == 0) return new ArrayList<>(); int rows = heights.length, cols = heights[0].length; Set<List<Integer>> pacific = new HashSet<>(), atlantic = new HashSet<>();
for (int c = 0; c < cols; c++) { dfs(heights, 0, c, pacific, heights[0][c], rows, cols); dfs(heights, rows - 1, c, atlantic, heights[rows - 1][c], rows, cols); } for (int r = 0; r < rows; r++) { dfs(heights, r, 0, pacific, heights[r][0], rows, cols); dfs(heights, r, cols - 1, atlantic, heights[r][cols - 1], rows, cols); }
List<List<Integer>> result = new ArrayList<>(); for (List<Integer> cell : pacific) if (atlantic.contains(cell)) result.add(cell); return result; }
public static void main(String[] args) { int[][] heights = {{1}}; System.out.println(pacificAtlantic(heights)); // [[0, 0]] }}fun dfs(heights: Array<IntArray>, r: Int, c: Int, visited: MutableSet<Pair<Int, Int>>, prevHeight: Int, rows: Int, cols: Int) { if (r < 0 || r >= rows || c < 0 || c >= cols || (r to c) in visited || heights[r][c] < prevHeight) return visited.add(r to c) val dirs = arrayOf(1 to 0, -1 to 0, 0 to 1, 0 to -1) for ((dr, dc) in dirs) dfs(heights, r + dr, c + dc, visited, heights[r][c], rows, cols)}
fun pacificAtlantic(heights: Array<IntArray>): List<List<Int>> { if (heights.isEmpty()) return emptyList() val rows = heights.size val cols = heights[0].size val pacific = mutableSetOf<Pair<Int, Int>>() val atlantic = mutableSetOf<Pair<Int, Int>>()
for (c in 0 until cols) { dfs(heights, 0, c, pacific, heights[0][c], rows, cols) dfs(heights, rows - 1, c, atlantic, heights[rows - 1][c], rows, cols) } for (r in 0 until rows) { dfs(heights, r, 0, pacific, heights[r][0], rows, cols) dfs(heights, r, cols - 1, atlantic, heights[r][cols - 1], rows, cols) }
return pacific.intersect(atlantic).map { listOf(it.first, it.second) }}
fun main() { val heights = arrayOf(intArrayOf(1)) println(pacificAtlantic(heights)) // [[0, 0]]}void dfs(List<List<int>> heights, int r, int c, Set<List<int>> visited, int prevHeight, int rows, int cols) { if (r < 0 || r >= rows || c < 0 || c >= cols || heights[r][c] < prevHeight) return; if (visited.any((v) => v[0] == r && v[1] == c)) return; visited.add([r, c]); const dr = [1, -1, 0, 0], dc = [0, 0, 1, -1]; for (int i = 0; i < 4; i++) { dfs(heights, r + dr[i], c + dc[i], visited, heights[r][c], rows, cols); }}
List<List<int>> pacificAtlantic(List<List<int>> heights) { if (heights.isEmpty) return []; int rows = heights.length, cols = heights[0].length; final pacific = <List<int>>{}; final atlantic = <List<int>>{};
for (int c = 0; c < cols; c++) { dfs(heights, 0, c, pacific, heights[0][c], rows, cols); dfs(heights, rows - 1, c, atlantic, heights[rows - 1][c], rows, cols); } for (int r = 0; r < rows; r++) { dfs(heights, r, 0, pacific, heights[r][0], rows, cols); dfs(heights, r, cols - 1, atlantic, heights[r][cols - 1], rows, cols); }
return pacific.where((p) => atlantic.any((a) => a[0] == p[0] && a[1] == p[1])).toList();}
void main() { final heights = [[1]]; print(pacificAtlantic(heights)); // [[0, 0]]}178. Xây dựng lại lịch trình (Reconstruct Itinerary)
Độ khó: Khó · Chủ đề: Đồ thị (Đường đi Euler)
Cho list vé máy bay tickets = [[from, to], ...], xây dựng lại hành trình bắt đầu từ "JFK" sử dụng tất cả vé đúng 1 lần, sao cho hành trình có thứ tự từ điển nhỏ nhất.
Ví dụ 1:
Input: tickets=[["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]Output: ["JFK","MUC","LHR","SFO","SJC"]Ví dụ 2:
Input: tickets=[["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]Output: ["JFK","ATL","JFK","SFO","ATL","SFO"]Giải thích: Có nhiều hành trình hợp lệ dùng hết vé, chọn hành trình nhỏ nhất theo thứ tự từ điển.Ràng buộc:
1 <= len(tickets) <= 300- Đề bài đảm bảo luôn tồn tại ít nhất 1 hành trình hợp lệ.
Xem đáp án
from collections import defaultdict
def find_itinerary(tickets): graph = defaultdict(list) for src, dst in sorted(tickets, reverse=True): graph[src].append(dst)
route = []
def dfs(airport): while graph[airport]: dfs(graph[airport].pop()) # luôn lấy đích nhỏ nhất theo thứ tự từ điển (đã sort, pop từ cuối là nhỏ nhất do reverse) route.append(airport)
dfs("JFK") return route[::-1]
tickets = [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]print(find_itinerary(tickets)) # ['JFK', 'MUC', 'LHR', 'SFO', 'SJC']#include <iostream>#include <vector>#include <string>#include <map>#include <algorithm>using namespace std;
void dfs(map<string, vector<string>>& graph, const string& airport, vector<string>& route) { auto& dests = graph[airport]; while (!dests.empty()) { string next = dests.front(); dests.erase(dests.begin()); dfs(graph, next, route); } route.push_back(airport);}
vector<string> findItinerary(vector<pair<string, string>> tickets) { map<string, vector<string>> graph; sort(tickets.begin(), tickets.end()); for (auto& [src, dst] : tickets) graph[src].push_back(dst);
vector<string> route; dfs(graph, "JFK", route); reverse(route.begin(), route.end()); return route;}
int main() { vector<pair<string, string>> tickets = {{"MUC", "LHR"}, {"JFK", "MUC"}, {"SFO", "SJC"}, {"LHR", "SFO"}}; for (auto& a : findItinerary(tickets)) cout << a << " "; cout << endl; // JFK MUC LHR SFO SJC return 0;}import java.util.*;
public class Main { static void dfs(Map<String, List<String>> graph, String airport, List<String> route) { List<String> dests = graph.get(airport); while (dests != null && !dests.isEmpty()) { String next = dests.remove(0); dfs(graph, next, route); } route.add(airport); }
static List<String> findItinerary(List<String[]> tickets) { Map<String, List<String>> graph = new TreeMap<>(); tickets.sort((a, b) -> { int c = a[0].compareTo(b[0]); return c != 0 ? c : a[1].compareTo(b[1]); }); for (String[] t : tickets) graph.computeIfAbsent(t[0], k -> new ArrayList<>()).add(t[1]);
List<String> route = new ArrayList<>(); dfs(graph, "JFK", route); Collections.reverse(route); return route; }
public static void main(String[] args) { List<String[]> tickets = Arrays.asList( new String[]{"MUC", "LHR"}, new String[]{"JFK", "MUC"}, new String[]{"SFO", "SJC"}, new String[]{"LHR", "SFO"}); System.out.println(findItinerary(tickets)); // [JFK, MUC, LHR, SFO, SJC] }}fun dfs(graph: MutableMap<String, MutableList<String>>, airport: String, route: MutableList<String>) { val dests = graph[airport] while (!dests.isNullOrEmpty()) { val next = dests.removeAt(0) dfs(graph, next, route) } route.add(airport)}
fun findItinerary(tickets: List<Pair<String, String>>): List<String> { val graph = mutableMapOf<String, MutableList<String>>() for ((src, dst) in tickets.sortedWith(compareBy({ it.first }, { it.second }))) { graph.getOrPut(src) { mutableListOf() }.add(dst) }
val route = mutableListOf<String>() dfs(graph, "JFK", route) route.reverse() return route}
fun main() { val tickets = listOf("MUC" to "LHR", "JFK" to "MUC", "SFO" to "SJC", "LHR" to "SFO") println(findItinerary(tickets)) // [JFK, MUC, LHR, SFO, SJC]}void dfs(Map<String, List<String>> graph, String airport, List<String> route) { final dests = graph[airport]; while (dests != null && dests.isNotEmpty) { final next = dests.removeAt(0); dfs(graph, next, route); } route.add(airport);}
List<String> findItinerary(List<List<String>> tickets) { final graph = <String, List<String>>{}; tickets.sort((a, b) { final c = a[0].compareTo(b[0]); return c != 0 ? c : a[1].compareTo(b[1]); }); for (var t in tickets) { graph.putIfAbsent(t[0], () => []).add(t[1]); }
final route = <String>[]; dfs(graph, "JFK", route); return route.reversed.toList();}
void main() { final tickets = [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]; print(findItinerary(tickets)); // [JFK, MUC, LHR, SFO, SJC]}179. Cây khung nhỏ nhất (Minimum Spanning Tree)
Độ khó: Khó · Chủ đề: Đồ thị (Kruskal, Union-Find)
Cho n đỉnh và list cạnh có trọng số edges = [(u, v, w), ...] của một đồ thị liên thông, tìm tổng trọng số nhỏ nhất để nối tất cả các đỉnh thành 1 cây khung (dùng thuật toán Kruskal).
Ví dụ 1:
Input: n=4, edges=[(0,1,10),(0,2,6),(0,3,5),(1,3,15),(2,3,4)]Output: 19Giải thích: Cây khung nhỏ nhất gồm các cạnh (2,3,4), (0,3,5), (0,1,10) -> tổng 19.Ví dụ 2:
Input: n=3, edges=[(0,1,1),(1,2,2),(0,2,3)]Output: 3Giải thích: Chọn 2 cạnh nhẹ nhất (0,1,1) và (1,2,2) -> tổng 3.Ràng buộc:
1 <= n <= 1000, đồ thị liên thông.
Xem đáp án
def minimum_spanning_tree(n, edges): parent = list(range(n))
def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x
total_weight = 0 edges_used = 0
for u, v, w in sorted(edges, key=lambda e: e[2]): root_u, root_v = find(u), find(v) if root_u != root_v: parent[root_u] = root_v total_weight += w edges_used += 1 if edges_used == n - 1: break
return total_weight
edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)]print(minimum_spanning_tree(4, edges)) # 19#include <iostream>#include <vector>#include <algorithm>using namespace std;
int find(vector<int>& parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x;}
int minimumSpanningTree(int n, vector<tuple<int, int, int>> edges) { vector<int> parent(n); for (int i = 0; i < n; i++) parent[i] = i;
sort(edges.begin(), edges.end(), [](auto& a, auto& b) { return get<2>(a) < get<2>(b); });
int totalWeight = 0, edgesUsed = 0; for (auto& [u, v, w] : edges) { int rootU = find(parent, u), rootV = find(parent, v); if (rootU != rootV) { parent[rootU] = rootV; totalWeight += w; edgesUsed++; if (edgesUsed == n - 1) break; } }
return totalWeight;}
int main() { vector<tuple<int, int, int>> edges = {{0, 1, 10}, {0, 2, 6}, {0, 3, 5}, {1, 3, 15}, {2, 3, 4}}; cout << minimumSpanningTree(4, edges) << endl; // 19 return 0;}import java.util.*;
public class Main { static int find(int[] parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; }
static int minimumSpanningTree(int n, int[][] edges) { int[] parent = new int[n]; for (int i = 0; i < n; i++) parent[i] = i;
Arrays.sort(edges, (a, b) -> a[2] - b[2]);
int totalWeight = 0, edgesUsed = 0; for (int[] e : edges) { int rootU = find(parent, e[0]), rootV = find(parent, e[1]); if (rootU != rootV) { parent[rootU] = rootV; totalWeight += e[2]; edgesUsed++; if (edgesUsed == n - 1) break; } }
return totalWeight; }
public static void main(String[] args) { int[][] edges = {{0, 1, 10}, {0, 2, 6}, {0, 3, 5}, {1, 3, 15}, {2, 3, 4}}; System.out.println(minimumSpanningTree(4, edges)); // 19 }}fun find(parent: IntArray, x: Int): Int { var cur = x while (parent[cur] != cur) { parent[cur] = parent[parent[cur]] cur = parent[cur] } return cur}
fun minimumSpanningTree(n: Int, edges: List<Triple<Int, Int, Int>>): Int { val parent = IntArray(n) { it } var totalWeight = 0 var edgesUsed = 0
for ((u, v, w) in edges.sortedBy { it.third }) { val rootU = find(parent, u) val rootV = find(parent, v) if (rootU != rootV) { parent[rootU] = rootV totalWeight += w edgesUsed++ if (edgesUsed == n - 1) break } }
return totalWeight}
fun main() { val edges = listOf(Triple(0, 1, 10), Triple(0, 2, 6), Triple(0, 3, 5), Triple(1, 3, 15), Triple(2, 3, 4)) println(minimumSpanningTree(4, edges)) // 19}int find(List<int> parent, int x) { int cur = x; while (parent[cur] != cur) { parent[cur] = parent[parent[cur]]; cur = parent[cur]; } return cur;}
int minimumSpanningTree(int n, List<List<int>> edges) { final parent = List.generate(n, (i) => i); edges.sort((a, b) => a[2].compareTo(b[2]));
int totalWeight = 0, edgesUsed = 0; for (var e in edges) { final rootU = find(parent, e[0]); final rootV = find(parent, e[1]); if (rootU != rootV) { parent[rootU] = rootV; totalWeight += e[2]; edgesUsed++; if (edgesUsed == n - 1) break; } }
return totalWeight;}
void main() { final edges = [[0, 1, 10], [0, 2, 6], [0, 3, 5], [1, 3, 15], [2, 3, 4]]; print(minimumSpanningTree(4, edges)); // 19}180. Kiểm tra cây hợp lệ (Graph Valid Tree)
Độ khó: Khó · Chủ đề: Đồ thị (Union-Find)
Cho n đỉnh đánh số 0 đến n-1 và list cạnh vô hướng edges, kiểm tra đồ thị này có phải là một cây hợp lệ hay không (liên thông và không có chu trình).
Ví dụ 1:
Input: n=5, edges=[[0,1],[0,2],[0,3],[1,4]]Output: TrueGiải thích: 5 đỉnh, 4 cạnh, liên thông, không có chu trình -> là cây hợp lệ.Ví dụ 2:
Input: n=5, edges=[[0,1],[1,2],[2,3],[1,3],[1,4]]Output: FalseGiải thích: Có chu trình 1-2-3-1, không phải cây hợp lệ.Ràng buộc:
1 <= n <= 2000
Xem đáp án
def valid_tree(n, edges): if len(edges) != n - 1: return False # cây hợp lệ với n đỉnh phải có đúng n-1 cạnh
parent = list(range(n))
def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x
for u, v in edges: root_u, root_v = find(u), find(v) if root_u == root_v: return False # thêm cạnh này tạo chu trình parent[root_u] = root_v
return True
print(valid_tree(5, [[0, 1], [0, 2], [0, 3], [1, 4]])) # Trueprint(valid_tree(5, [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]])) # False#include <iostream>#include <vector>using namespace std;
int find(vector<int>& parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x;}
bool validTree(int n, vector<vector<int>> edges) { if ((int)edges.size() != n - 1) return false;
vector<int> parent(n); for (int i = 0; i < n; i++) parent[i] = i;
for (auto& e : edges) { int rootU = find(parent, e[0]), rootV = find(parent, e[1]); if (rootU == rootV) return false; parent[rootU] = rootV; }
return true;}
int main() { cout << boolalpha << validTree(5, {{0, 1}, {0, 2}, {0, 3}, {1, 4}}) << endl; // true cout << boolalpha << validTree(5, {{0, 1}, {1, 2}, {2, 3}, {1, 3}, {1, 4}}) << endl; // false return 0;}public class Main { static int find(int[] parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; }
static boolean validTree(int n, int[][] edges) { if (edges.length != n - 1) return false;
int[] parent = new int[n]; for (int i = 0; i < n; i++) parent[i] = i;
for (int[] e : edges) { int rootU = find(parent, e[0]), rootV = find(parent, e[1]); if (rootU == rootV) return false; parent[rootU] = rootV; }
return true; }
public static void main(String[] args) { System.out.println(validTree(5, new int[][]{{0, 1}, {0, 2}, {0, 3}, {1, 4}})); // true System.out.println(validTree(5, new int[][]{{0, 1}, {1, 2}, {2, 3}, {1, 3}, {1, 4}})); // false }}fun find(parent: IntArray, x: Int): Int { var cur = x while (parent[cur] != cur) { parent[cur] = parent[parent[cur]] cur = parent[cur] } return cur}
fun validTree(n: Int, edges: List<List<Int>>): Boolean { if (edges.size != n - 1) return false
val parent = IntArray(n) { it }
for ((u, v) in edges.map { it[0] to it[1] }) { val rootU = find(parent, u) val rootV = find(parent, v) if (rootU == rootV) return false parent[rootU] = rootV }
return true}
fun main() { println(validTree(5, listOf(listOf(0, 1), listOf(0, 2), listOf(0, 3), listOf(1, 4)))) // true println(validTree(5, listOf(listOf(0, 1), listOf(1, 2), listOf(2, 3), listOf(1, 3), listOf(1, 4)))) // false}int find(List<int> parent, int x) { int cur = x; while (parent[cur] != cur) { parent[cur] = parent[parent[cur]]; cur = parent[cur]; } return cur;}
bool validTree(int n, List<List<int>> edges) { if (edges.length != n - 1) return false;
final parent = List.generate(n, (i) => i);
for (var e in edges) { final rootU = find(parent, e[0]); final rootV = find(parent, e[1]); if (rootU == rootV) return false; parent[rootU] = rootV; }
return true;}
void main() { print(validTree(5, [[0, 1], [0, 2], [0, 3], [1, 4]])); // true print(validTree(5, [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]])); // false}Nhóm 10: Greedy & Bit Manipulation
Phần tiêu đề “Nhóm 10: Greedy & Bit Manipulation”181. Đếm số bit 1 (Number of 1 Bits)
Độ khó: Dễ · Chủ đề: Bit Manipulation
Cho một số nguyên không âm n, đếm số lượng bit 1 trong biểu diễn nhị phân của nó (còn gọi là Hamming weight).
Ví dụ 1:
Input: n = 11Output: 3Giải thích: 11 = 1011 (nhị phân), có 3 bit 1.Ví dụ 2:
Input: n = 128Output: 1Giải thích: 128 = 10000000 (nhị phân), có 1 bit 1.Ràng buộc:
0 <= n <= 2^31 - 1
Xem đáp án
def hamming_weight(n): count = 0 while n: # n & (n - 1) xóa bit 1 thấp nhất của n n &= n - 1 count += 1 return count
print(hamming_weight(11)) # 3print(hamming_weight(128)) # 1#include <iostream>using namespace std;
int hammingWeight(unsigned int n) { int count = 0; while (n) { n &= (n - 1); count++; } return count;}
int main() { cout << hammingWeight(11) << endl; // 3 cout << hammingWeight(128) << endl; // 1 return 0;}public class Main { static int hammingWeight(int n) { int count = 0; while (n != 0) { n &= (n - 1); count++; } return count; }
public static void main(String[] args) { System.out.println(hammingWeight(11)); // 3 System.out.println(hammingWeight(128)); // 1 }}fun hammingWeight(n: Int): Int { var num = n var count = 0 while (num != 0) { num = num and (num - 1) count++ } return count}
fun main() { println(hammingWeight(11)) // 3 println(hammingWeight(128)) // 1}int hammingWeight(int n) { int count = 0; while (n != 0) { n &= (n - 1); count++; } return count;}
void main() { print(hammingWeight(11)); // 3 print(hammingWeight(128)); // 1}182. Đếm bit 1 cho dãy số (Counting Bits)
Độ khó: Dễ · Chủ đề: Bit Manipulation, Quy hoạch động
Cho số nguyên n, trả về một mảng ans độ dài n + 1, trong đó ans[i] là số bit 1 trong biểu diễn nhị phân của i, với mọi 0 <= i <= n.
Ví dụ 1:
Input: n = 2Output: [0, 1, 1]Giải thích: 0 -> 0, 1 -> 1, 2 -> 10 (1 bit 1).Ví dụ 2:
Input: n = 5Output: [0, 1, 1, 2, 1, 2]Ràng buộc:
0 <= n <= 10^5
Xem đáp án
def count_bits(n): # ans[i] = ans[i >> 1] + (i & 1): bỏ 1 bit thấp nhất rồi cộng thêm bit đó ans = [0] * (n + 1) for i in range(1, n + 1): ans[i] = ans[i >> 1] + (i & 1) return ans
print(count_bits(2)) # [0, 1, 1]print(count_bits(5)) # [0, 1, 1, 2, 1, 2]#include <iostream>#include <vector>using namespace std;
vector<int> countBits(int n) { vector<int> ans(n + 1, 0); for (int i = 1; i <= n; i++) { ans[i] = ans[i >> 1] + (i & 1); } return ans;}
int main() { for (int x : countBits(2)) cout << x << " "; cout << endl; // 0 1 1 for (int x : countBits(5)) cout << x << " "; cout << endl; // 0 1 1 2 1 2 return 0;}public class Main { static int[] countBits(int n) { int[] ans = new int[n + 1]; for (int i = 1; i <= n; i++) { ans[i] = ans[i >> 1] + (i & 1); } return ans; }
public static void main(String[] args) { System.out.println(java.util.Arrays.toString(countBits(2))); // [0, 1, 1] System.out.println(java.util.Arrays.toString(countBits(5))); // [0, 1, 1, 2, 1, 2] }}fun countBits(n: Int): IntArray { val ans = IntArray(n + 1) for (i in 1..n) { ans[i] = ans[i shr 1] + (i and 1) } return ans}
fun main() { println(countBits(2).toList()) // [0, 1, 1] println(countBits(5).toList()) // [0, 1, 1, 2, 1, 2]}List<int> countBits(int n) { final ans = List.filled(n + 1, 0); for (int i = 1; i <= n; i++) { ans[i] = ans[i >> 1] + (i & 1); } return ans;}
void main() { print(countBits(2)); // [0, 1, 1] print(countBits(5)); // [0, 1, 1, 2, 1, 2]}183. Chia kẹo cho trẻ em (Assign Cookies)
Độ khó: Dễ · Chủ đề: Greedy
Mỗi trẻ em i có một “mức thèm ăn” g[i] — chiếc bánh quy nhỏ nhất có thể làm trẻ hài lòng. Mỗi chiếc bánh quy j có kích thước s[j]. Trẻ i hài lòng với bánh j nếu s[j] >= g[i]. Mỗi trẻ nhận tối đa 1 bánh. Tìm số trẻ tối đa có thể làm hài lòng.
Ví dụ 1:
Input: g = [1, 2, 3], s = [1, 1]Output: 1Giải thích: Chỉ đủ bánh làm hài lòng 1 trẻ (trẻ có mức thèm ăn 1).Ví dụ 2:
Input: g = [1, 2], s = [1, 2, 3]Output: 2Ràng buộc:
1 <= g.length, s.length <= 3 * 10^4
Xem đáp án
def find_content_children(g, s): # Sắp xếp cả 2, ghép tham lam: bánh nhỏ nhất đủ cho trẻ khó tính nhất còn lại g.sort() s.sort() child = 0 for cookie in s: if child < len(g) and g[child] <= cookie: child += 1 return child
print(find_content_children([1, 2, 3], [1, 1])) # 1print(find_content_children([1, 2], [1, 2, 3])) # 2#include <iostream>#include <vector>#include <algorithm>using namespace std;
int findContentChildren(vector<int> g, vector<int> s) { sort(g.begin(), g.end()); sort(s.begin(), s.end()); int child = 0; for (int cookie : s) { if (child < (int)g.size() && g[child] <= cookie) child++; } return child;}
int main() { cout << findContentChildren({1, 2, 3}, {1, 1}) << endl; // 1 cout << findContentChildren({1, 2}, {1, 2, 3}) << endl; // 2 return 0;}import java.util.*;
public class Main { static int findContentChildren(int[] g, int[] s) { Arrays.sort(g); Arrays.sort(s); int child = 0; for (int cookie : s) { if (child < g.length && g[child] <= cookie) child++; } return child; }
public static void main(String[] args) { System.out.println(findContentChildren(new int[]{1, 2, 3}, new int[]{1, 1})); // 1 System.out.println(findContentChildren(new int[]{1, 2}, new int[]{1, 2, 3})); // 2 }}fun findContentChildren(g: IntArray, s: IntArray): Int { g.sort() s.sort() var child = 0 for (cookie in s) { if (child < g.size && g[child] <= cookie) child++ } return child}
fun main() { println(findContentChildren(intArrayOf(1, 2, 3), intArrayOf(1, 1))) // 1 println(findContentChildren(intArrayOf(1, 2), intArrayOf(1, 2, 3))) // 2}int findContentChildren(List<int> g, List<int> s) { g.sort(); s.sort(); int child = 0; for (var cookie in s) { if (child < g.length && g[child] <= cookie) child++; } return child;}
void main() { print(findContentChildren([1, 2, 3], [1, 1])); // 1 print(findContentChildren([1, 2], [1, 2, 3])); // 2}184. Giao dịch cổ phiếu nhiều lần (Best Time to Buy and Sell Stock II)
Độ khó: Dễ · Chủ đề: Greedy
Cho mảng prices với prices[i] là giá cổ phiếu ngày thứ i. Bạn có thể mua và bán nhiều lần (mua rồi bán trước khi mua lại), nhưng không được giữ nhiều hơn 1 cổ phiếu cùng lúc. Tìm lợi nhuận tối đa có thể đạt được.
Ví dụ 1:
Input: prices = [7, 1, 5, 3, 6, 4]Output: 7Giải thích: Mua ngày giá 1 bán ngày giá 5 (lãi 4), mua ngày giá 3 bán ngày giá 6 (lãi 3). Tổng 7.Ví dụ 2:
Input: prices = [1, 2, 3, 4, 5]Output: 4Ràng buộc:
1 <= prices.length <= 3 * 10^4
Xem đáp án
def max_profit(prices): # Cộng dồn mọi khoảng tăng liên tiếp profit = 0 for i in range(1, len(prices)): if prices[i] > prices[i - 1]: profit += prices[i] - prices[i - 1] return profit
print(max_profit([7, 1, 5, 3, 6, 4])) # 7print(max_profit([1, 2, 3, 4, 5])) # 4#include <iostream>#include <vector>using namespace std;
int maxProfit(vector<int> prices) { int profit = 0; for (int i = 1; i < (int)prices.size(); i++) { if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1]; } return profit;}
int main() { cout << maxProfit({7, 1, 5, 3, 6, 4}) << endl; // 7 cout << maxProfit({1, 2, 3, 4, 5}) << endl; // 4 return 0;}public class Main { static int maxProfit(int[] prices) { int profit = 0; for (int i = 1; i < prices.length; i++) { if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1]; } return profit; }
public static void main(String[] args) { System.out.println(maxProfit(new int[]{7, 1, 5, 3, 6, 4})); // 7 System.out.println(maxProfit(new int[]{1, 2, 3, 4, 5})); // 4 }}fun maxProfit(prices: IntArray): Int { var profit = 0 for (i in 1 until prices.size) { if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1] } return profit}
fun main() { println(maxProfit(intArrayOf(7, 1, 5, 3, 6, 4))) // 7 println(maxProfit(intArrayOf(1, 2, 3, 4, 5))) // 4}int maxProfit(List<int> prices) { int profit = 0; for (int i = 1; i < prices.length; i++) { if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1]; } return profit;}
void main() { print(maxProfit([7, 1, 5, 3, 6, 4])); // 7 print(maxProfit([1, 2, 3, 4, 5])); // 4}185. Tìm số bị thiếu (Missing Number)
Độ khó: Dễ · Chủ đề: Bit Manipulation
Cho mảng nums chứa n số phân biệt lấy từ đoạn [0, n]. Tìm số duy nhất trong đoạn đó không xuất hiện trong mảng. Hãy giải với độ phức tạp thời gian O(n) và không dùng thêm bộ nhớ ngoài O(1) (gợi ý: dùng XOR).
Ví dụ 1:
Input: nums = [3, 0, 1]Output: 2Ví dụ 2:
Input: nums = [9, 6, 4, 2, 3, 5, 7, 0, 1]Output: 8Ràng buộc:
n == nums.length, 1 <= n <= 10^4- Tất cả các số trong nums đều phân biệt
Xem đáp án
def missing_number(nums): # XOR tất cả chỉ số 0..n và tất cả giá trị trong mảng, phần trùng tự triệt tiêu n = len(nums) result = n for i, num in enumerate(nums): result ^= i ^ num return result
print(missing_number([3, 0, 1])) # 2print(missing_number([9, 6, 4, 2, 3, 5, 7, 0, 1])) # 8#include <iostream>#include <vector>using namespace std;
int missingNumber(vector<int> nums) { int n = nums.size(); int result = n; for (int i = 0; i < n; i++) { result ^= i ^ nums[i]; } return result;}
int main() { cout << missingNumber({3, 0, 1}) << endl; // 2 cout << missingNumber({9, 6, 4, 2, 3, 5, 7, 0, 1}) << endl; // 8 return 0;}public class Main { static int missingNumber(int[] nums) { int n = nums.length; int result = n; for (int i = 0; i < n; i++) { result ^= i ^ nums[i]; } return result; }
public static void main(String[] args) { System.out.println(missingNumber(new int[]{3, 0, 1})); // 2 System.out.println(missingNumber(new int[]{9, 6, 4, 2, 3, 5, 7, 0, 1})); // 8 }}fun missingNumber(nums: IntArray): Int { val n = nums.size var result = n for (i in 0 until n) { result = result xor i xor nums[i] } return result}
fun main() { println(missingNumber(intArrayOf(3, 0, 1))) // 2 println(missingNumber(intArrayOf(9, 6, 4, 2, 3, 5, 7, 0, 1))) // 8}int missingNumber(List<int> nums) { int n = nums.length; int result = n; for (int i = 0; i < n; i++) { result ^= i ^ nums[i]; } return result;}
void main() { print(missingNumber([3, 0, 1])); // 2 print(missingNumber([9, 6, 4, 2, 3, 5, 7, 0, 1])); // 8}186. Trò chơi nhảy ô (Jump Game)
Độ khó: Trung bình · Chủ đề: Greedy
Cho mảng nums, ban đầu bạn đứng ở chỉ số 0. nums[i] là bước nhảy xa nhất có thể thực hiện từ vị trí i. Trả về True nếu có thể đến được chỉ số cuối cùng, ngược lại trả về False.
Ví dụ 1:
Input: nums = [2, 3, 1, 1, 4]Output: TrueGiải thích: Nhảy 1 bước từ index 0 đến 1, rồi 3 bước đến index cuối.Ví dụ 2:
Input: nums = [3, 2, 1, 0, 4]Output: FalseGiải thích: Luôn bị kẹt tại index 3 (nums[3] = 0), không thể đến index 4.Ràng buộc:
1 <= nums.length <= 10^40 <= nums[i] <= 10^5
Xem đáp án
def can_jump(nums): # Theo dõi tầm xa nhất có thể chạm tới; nếu vượt qua index hiện tại thì kẹt farthest = 0 for i, jump in enumerate(nums): if i > farthest: return False farthest = max(farthest, i + jump) return True
print(can_jump([2, 3, 1, 1, 4])) # Trueprint(can_jump([3, 2, 1, 0, 4])) # False#include <iostream>#include <vector>#include <algorithm>using namespace std;
bool canJump(vector<int> nums) { int farthest = 0; for (int i = 0; i < (int)nums.size(); i++) { if (i > farthest) return false; farthest = max(farthest, i + nums[i]); } return true;}
int main() { cout << boolalpha << canJump({2, 3, 1, 1, 4}) << endl; // true cout << boolalpha << canJump({3, 2, 1, 0, 4}) << endl; // false return 0;}public class Main { static boolean canJump(int[] nums) { int farthest = 0; for (int i = 0; i < nums.length; i++) { if (i > farthest) return false; farthest = Math.max(farthest, i + nums[i]); } return true; }
public static void main(String[] args) { System.out.println(canJump(new int[]{2, 3, 1, 1, 4})); // true System.out.println(canJump(new int[]{3, 2, 1, 0, 4})); // false }}fun canJump(nums: IntArray): Boolean { var farthest = 0 for (i in nums.indices) { if (i > farthest) return false farthest = maxOf(farthest, i + nums[i]) } return true}
fun main() { println(canJump(intArrayOf(2, 3, 1, 1, 4))) // true println(canJump(intArrayOf(3, 2, 1, 0, 4))) // false}bool canJump(List<int> nums) { int farthest = 0; for (int i = 0; i < nums.length; i++) { if (i > farthest) return false; farthest = farthest > i + nums[i] ? farthest : i + nums[i]; } return true;}
void main() { print(canJump([2, 3, 1, 1, 4])); // true print(canJump([3, 2, 1, 0, 4])); // false}187. Số bước nhảy ít nhất (Jump Game II)
Độ khó: Trung bình · Chủ đề: Greedy
Cũng với mảng nums như bài trên (luôn đến được đích), tìm số bước nhảy ít nhất để đi từ chỉ số 0 đến chỉ số cuối cùng.
Ví dụ 1:
Input: nums = [2, 3, 1, 1, 4]Output: 2Giải thích: Nhảy 1 bước từ index 0 đến index 1, rồi nhảy 3 bước đến index cuối.Ví dụ 2:
Input: nums = [1, 1, 1, 1]Output: 3Ràng buộc:
1 <= nums.length <= 10^4- Đảm bảo luôn có thể đến được chỉ số cuối
Xem đáp án
def jump(nums): # Greedy theo "tầng": mở rộng biên hiện tại đến khi phải tăng số bước jumps = 0 current_end = 0 farthest = 0 for i in range(len(nums) - 1): farthest = max(farthest, i + nums[i]) if i == current_end: jumps += 1 current_end = farthest return jumps
print(jump([2, 3, 1, 1, 4])) # 2print(jump([1, 1, 1, 1])) # 3#include <iostream>#include <vector>#include <algorithm>using namespace std;
int jump(vector<int> nums) { int jumps = 0, currentEnd = 0, farthest = 0; for (int i = 0; i < (int)nums.size() - 1; i++) { farthest = max(farthest, i + nums[i]); if (i == currentEnd) { jumps++; currentEnd = farthest; } } return jumps;}
int main() { cout << jump({2, 3, 1, 1, 4}) << endl; // 2 cout << jump({1, 1, 1, 1}) << endl; // 3 return 0;}public class Main { static int jump(int[] nums) { int jumps = 0, currentEnd = 0, farthest = 0; for (int i = 0; i < nums.length - 1; i++) { farthest = Math.max(farthest, i + nums[i]); if (i == currentEnd) { jumps++; currentEnd = farthest; } } return jumps; }
public static void main(String[] args) { System.out.println(jump(new int[]{2, 3, 1, 1, 4})); // 2 System.out.println(jump(new int[]{1, 1, 1, 1})); // 3 }}fun jump(nums: IntArray): Int { var jumps = 0 var currentEnd = 0 var farthest = 0 for (i in 0 until nums.size - 1) { farthest = maxOf(farthest, i + nums[i]) if (i == currentEnd) { jumps++ currentEnd = farthest } } return jumps}
fun main() { println(jump(intArrayOf(2, 3, 1, 1, 4))) // 2 println(jump(intArrayOf(1, 1, 1, 1))) // 3}int jump(List<int> nums) { int jumps = 0, currentEnd = 0, farthest = 0; for (int i = 0; i < nums.length - 1; i++) { farthest = farthest > i + nums[i] ? farthest : i + nums[i]; if (i == currentEnd) { jumps++; currentEnd = farthest; } } return jumps;}
void main() { print(jump([2, 3, 1, 1, 4])); // 2 print(jump([1, 1, 1, 1])); // 3}188. Trạm xăng (Gas Station)
Độ khó: Trung bình · Chủ đề: Greedy
Có n trạm xăng trên một vòng tròn, trạm i cho gas[i] lít xăng. Để đi từ trạm i đến i+1 cần tốn cost[i] lít. Xe bắt đầu với bình rỗng. Tìm chỉ số trạm xuất phát để có thể đi hết 1 vòng mà không hết xăng, hoặc trả về -1 nếu không tồn tại (đảm bảo nếu tồn tại thì đáp án là duy nhất).
Ví dụ 1:
Input: gas = [1, 2, 3, 4, 5], cost = [3, 4, 5, 1, 2]Output: 3Giải thích: Bắt đầu tại trạm 3, đi hết vòng vẫn còn dư xăng ở mỗi chặng.Ví dụ 2:
Input: gas = [2, 3, 4], cost = [3, 4, 3]Output: -1Ràng buộc:
n == gas.length == cost.length, 1 <= n <= 10^5
Xem đáp án
def can_complete_circuit(gas, cost): if sum(gas) < sum(cost): return -1 # Tổng xăng không đủ tổng chi phí thì chắc chắn không có lời giải
total = 0 start = 0 for i in range(len(gas)): total += gas[i] - cost[i] if total < 0: # Không trạm nào từ start..i có thể là điểm xuất phát hợp lệ start = i + 1 total = 0 return start
print(can_complete_circuit([1, 2, 3, 4, 5], [3, 4, 5, 1, 2])) # 3print(can_complete_circuit([2, 3, 4], [3, 4, 3])) # -1#include <iostream>#include <vector>#include <numeric>using namespace std;
int canCompleteCircuit(vector<int> gas, vector<int> cost) { if (accumulate(gas.begin(), gas.end(), 0) < accumulate(cost.begin(), cost.end(), 0)) return -1;
int total = 0, start = 0; for (int i = 0; i < (int)gas.size(); i++) { total += gas[i] - cost[i]; if (total < 0) { start = i + 1; total = 0; } } return start;}
int main() { cout << canCompleteCircuit({1, 2, 3, 4, 5}, {3, 4, 5, 1, 2}) << endl; // 3 cout << canCompleteCircuit({2, 3, 4}, {3, 4, 3}) << endl; // -1 return 0;}import java.util.*;
public class Main { static int canCompleteCircuit(int[] gas, int[] cost) { int totalGas = Arrays.stream(gas).sum(); int totalCost = Arrays.stream(cost).sum(); if (totalGas < totalCost) return -1;
int total = 0, start = 0; for (int i = 0; i < gas.length; i++) { total += gas[i] - cost[i]; if (total < 0) { start = i + 1; total = 0; } } return start; }
public static void main(String[] args) { System.out.println(canCompleteCircuit(new int[]{1, 2, 3, 4, 5}, new int[]{3, 4, 5, 1, 2})); // 3 System.out.println(canCompleteCircuit(new int[]{2, 3, 4}, new int[]{3, 4, 3})); // -1 }}fun canCompleteCircuit(gas: IntArray, cost: IntArray): Int { if (gas.sum() < cost.sum()) return -1
var total = 0 var start = 0 for (i in gas.indices) { total += gas[i] - cost[i] if (total < 0) { start = i + 1 total = 0 } } return start}
fun main() { println(canCompleteCircuit(intArrayOf(1, 2, 3, 4, 5), intArrayOf(3, 4, 5, 1, 2))) // 3 println(canCompleteCircuit(intArrayOf(2, 3, 4), intArrayOf(3, 4, 3))) // -1}int canCompleteCircuit(List<int> gas, List<int> cost) { if (gas.reduce((a, b) => a + b) < cost.reduce((a, b) => a + b)) return -1;
int total = 0, start = 0; for (int i = 0; i < gas.length; i++) { total += gas[i] - cost[i]; if (total < 0) { start = i + 1; total = 0; } } return start;}
void main() { print(canCompleteCircuit([1, 2, 3, 4, 5], [3, 4, 5, 1, 2])); // 3 print(canCompleteCircuit([2, 3, 4], [3, 4, 3])); // -1}189. Lên lịch tác vụ CPU (Task Scheduler)
Độ khó: Trung bình · Chủ đề: Greedy
Cho mảng ký tự tasks biểu diễn các tác vụ CPU cần thực hiện (mỗi ký tự là 1 loại tác vụ) và số nguyên n — thời gian nghỉ tối thiểu (số chu kỳ) giữa 2 lần thực hiện cùng 1 loại tác vụ. Mỗi chu kỳ CPU chạy 1 tác vụ hoặc nghỉ (idle). Tìm số chu kỳ CPU ít nhất để hoàn thành tất cả tác vụ.
Ví dụ 1:
Input: tasks = ["A","A","A","B","B","B"], n = 2Output: 8Giải thích: A -> B -> idle -> A -> B -> idle -> A -> BVí dụ 2:
Input: tasks = ["A","A","A","B","B","B"], n = 0Output: 6Giải thích: n = 0 nên không cần nghỉ giữa các lần lặp cùng tác vụ.Ràng buộc:
1 <= tasks.length <= 10^40 <= n <= 100
Xem đáp án
from collections import Counter
def least_interval(tasks, n): counts = Counter(tasks) max_count = max(counts.values()) # Số tác vụ có tần suất bằng max_count (chiếm ô cuối của mỗi "khung") num_max = sum(1 for c in counts.values() if c == max_count)
# Xếp các khung (max_count - 1) khung đầy đủ kích thước (n + 1), cộng num_max ô cuối frame_based = (max_count - 1) * (n + 1) + num_max return max(frame_based, len(tasks))
print(least_interval(["A", "A", "A", "B", "B", "B"], 2)) # 8print(least_interval(["A", "A", "A", "B", "B", "B"], 0)) # 6#include <iostream>#include <vector>#include <unordered_map>#include <algorithm>using namespace std;
int leastInterval(vector<char> tasks, int n) { unordered_map<char, int> counts; for (char t : tasks) counts[t]++;
int maxCount = 0; for (auto& [_, c] : counts) maxCount = max(maxCount, c);
int numMax = 0; for (auto& [_, c] : counts) if (c == maxCount) numMax++;
int frameBased = (maxCount - 1) * (n + 1) + numMax; return max(frameBased, (int)tasks.size());}
int main() { cout << leastInterval({'A', 'A', 'A', 'B', 'B', 'B'}, 2) << endl; // 8 cout << leastInterval({'A', 'A', 'A', 'B', 'B', 'B'}, 0) << endl; // 6 return 0;}import java.util.*;
public class Main { static int leastInterval(char[] tasks, int n) { Map<Character, Integer> counts = new HashMap<>(); for (char t : tasks) counts.merge(t, 1, Integer::sum);
int maxCount = Collections.max(counts.values()); int numMax = 0; for (int c : counts.values()) if (c == maxCount) numMax++;
int frameBased = (maxCount - 1) * (n + 1) + numMax; return Math.max(frameBased, tasks.length); }
public static void main(String[] args) { System.out.println(leastInterval(new char[]{'A', 'A', 'A', 'B', 'B', 'B'}, 2)); // 8 System.out.println(leastInterval(new char[]{'A', 'A', 'A', 'B', 'B', 'B'}, 0)); // 6 }}fun leastInterval(tasks: List<Char>, n: Int): Int { val counts = tasks.groupingBy { it }.eachCount() val maxCount = counts.values.max() val numMax = counts.values.count { it == maxCount }
val frameBased = (maxCount - 1) * (n + 1) + numMax return maxOf(frameBased, tasks.size)}
fun main() { println(leastInterval(listOf('A', 'A', 'A', 'B', 'B', 'B'), 2)) // 8 println(leastInterval(listOf('A', 'A', 'A', 'B', 'B', 'B'), 0)) // 6}int leastInterval(List<String> tasks, int n) { final counts = <String, int>{}; for (var t in tasks) counts[t] = (counts[t] ?? 0) + 1;
final maxCount = counts.values.reduce((a, b) => a > b ? a : b); final numMax = counts.values.where((c) => c == maxCount).length;
final frameBased = (maxCount - 1) * (n + 1) + numMax; return frameBased > tasks.length ? frameBased : tasks.length;}
void main() { print(leastInterval(["A", "A", "A", "B", "B", "B"], 2)); // 8 print(leastInterval(["A", "A", "A", "B", "B", "B"], 0)); // 6}190. Số xuất hiện một lần II (Single Number II)
Độ khó: Trung bình · Chủ đề: Bit Manipulation
Cho mảng số nguyên nums, mọi phần tử xuất hiện đúng 3 lần, ngoại trừ đúng 1 phần tử xuất hiện đúng 1 lần. Tìm phần tử đó, không dùng thêm bộ nhớ vượt O(1).
Ví dụ 1:
Input: nums = [2, 2, 3, 2]Output: 3Ví dụ 2:
Input: nums = [0, 1, 0, 1, 0, 1, 99]Output: 99Ràng buộc:
1 <= nums.length <= 3 * 10^4
Xem đáp án
def single_number(nums): # ones: bit xuất hiện đúng 1 lần trong chu kỳ hiện tại # twos: bit xuất hiện đúng 2 lần trong chu kỳ hiện tại ones, twos = 0, 0 for num in nums: ones = (ones ^ num) & ~twos twos = (twos ^ num) & ~ones return ones
print(single_number([2, 2, 3, 2])) # 3print(single_number([0, 1, 0, 1, 0, 1, 99])) # 99#include <iostream>#include <vector>using namespace std;
int singleNumber(vector<int> nums) { int ones = 0, twos = 0; for (int num : nums) { ones = (ones ^ num) & ~twos; twos = (twos ^ num) & ~ones; } return ones;}
int main() { cout << singleNumber({2, 2, 3, 2}) << endl; // 3 cout << singleNumber({0, 1, 0, 1, 0, 1, 99}) << endl; // 99 return 0;}public class Main { static int singleNumber(int[] nums) { int ones = 0, twos = 0; for (int num : nums) { ones = (ones ^ num) & ~twos; twos = (twos ^ num) & ~ones; } return ones; }
public static void main(String[] args) { System.out.println(singleNumber(new int[]{2, 2, 3, 2})); // 3 System.out.println(singleNumber(new int[]{0, 1, 0, 1, 0, 1, 99})); // 99 }}fun singleNumber(nums: IntArray): Int { var ones = 0 var twos = 0 for (num in nums) { ones = (ones xor num) and twos.inv() twos = (twos xor num) and ones.inv() } return ones}
fun main() { println(singleNumber(intArrayOf(2, 2, 3, 2))) // 3 println(singleNumber(intArrayOf(0, 1, 0, 1, 0, 1, 99))) // 99}int singleNumber(List<int> nums) { int ones = 0, twos = 0; for (var num in nums) { ones = (ones ^ num) & ~twos; twos = (twos ^ num) & ~ones; } return ones;}
void main() { print(singleNumber([2, 2, 3, 2])); // 3 print(singleNumber([0, 1, 0, 1, 0, 1, 99])); // 99}191. Số xuất hiện một lần III (Single Number III)
Độ khó: Trung bình · Chủ đề: Bit Manipulation
Cho mảng số nguyên nums, đúng 2 phần tử xuất hiện đúng 1 lần, còn lại đều xuất hiện đúng 2 lần. Tìm 2 phần tử đó (thứ tự trả về tùy ý).
Ví dụ 1:
Input: nums = [1, 2, 1, 3, 2, 5]Output: [3, 5]Ví dụ 2:
Input: nums = [-1, 0]Output: [-1, 0]Ràng buộc:
2 <= nums.length <= 3 * 10^4
Xem đáp án
def single_number(nums): xor_all = 0 for num in nums: xor_all ^= num # xor_all = a ^ b (2 số cần tìm)
# Lấy 1 bit khác nhau giữa a và b để tách nums thành 2 nhóm diff_bit = xor_all & (-xor_all)
a = 0 for num in nums: if num & diff_bit: a ^= num b = xor_all ^ a return [a, b]
print(single_number([1, 2, 1, 3, 2, 5])) # [3, 5] (thứ tự có thể khác)print(single_number([-1, 0])) # [-1, 0]#include <iostream>#include <vector>using namespace std;
vector<int> singleNumber(vector<int> nums) { long xorAll = 0; for (int num : nums) xorAll ^= num;
long diffBit = xorAll & (-xorAll);
long a = 0; for (int num : nums) { if (num & diffBit) a ^= num; } long b = xorAll ^ a; return {(int)a, (int)b};}
int main() { for (int x : singleNumber({1, 2, 1, 3, 2, 5})) cout << x << " "; cout << endl; // 3 5 (thu tu co the khac) for (int x : singleNumber({-1, 0})) cout << x << " "; cout << endl; // -1 0 return 0;}public class Main { static int[] singleNumber(int[] nums) { int xorAll = 0; for (int num : nums) xorAll ^= num;
int diffBit = xorAll & (-xorAll);
int a = 0; for (int num : nums) { if ((num & diffBit) != 0) a ^= num; } int b = xorAll ^ a; return new int[]{a, b}; }
public static void main(String[] args) { System.out.println(java.util.Arrays.toString(singleNumber(new int[]{1, 2, 1, 3, 2, 5}))); // [3, 5] System.out.println(java.util.Arrays.toString(singleNumber(new int[]{-1, 0}))); // [-1, 0] }}fun singleNumber(nums: IntArray): IntArray { var xorAll = 0 for (num in nums) xorAll = xorAll xor num
val diffBit = xorAll and (-xorAll)
var a = 0 for (num in nums) { if (num and diffBit != 0) a = a xor num } val b = xorAll xor a return intArrayOf(a, b)}
fun main() { println(singleNumber(intArrayOf(1, 2, 1, 3, 2, 5)).toList()) // [3, 5] println(singleNumber(intArrayOf(-1, 0)).toList()) // [-1, 0]}List<int> singleNumber(List<int> nums) { int xorAll = 0; for (var num in nums) xorAll ^= num;
int diffBit = xorAll & (-xorAll);
int a = 0; for (var num in nums) { if (num & diffBit != 0) a ^= num; } int b = xorAll ^ a; return [a, b];}
void main() { print(singleNumber([1, 2, 1, 3, 2, 5])); // [3, 5] print(singleNumber([-1, 0])); // [-1, 0]}192. Tổng hai số không dùng dấu + (Sum of Two Integers)
Độ khó: Trung bình · Chủ đề: Bit Manipulation
Cho 2 số nguyên a, b. Tính tổng a + b mà không dùng toán tử + hoặc -.
Ví dụ 1:
Input: a = 1, b = 2Output: 3Ví dụ 2:
Input: a = 2, b = 3Output: 5Ràng buộc:
-1000 <= a, b <= 1000
Xem đáp án
def get_sum(a, b): mask = 0xFFFFFFFF # giới hạn 32-bit để mô phỏng số nguyên có dấu while b & mask: carry = (a & b) << 1 # phần nhớ a = a ^ b # cộng không nhớ (XOR) b = carry return a & mask if b > mask else a
# Bản rút gọn dễ đọc, hoạt động đúng với input trong ràng buộc đề bài (không âm quá sâu)def get_sum_simple(a, b): while b != 0: carry = (a & b) << 1 a = a ^ b b = carry return a
print(get_sum_simple(1, 2)) # 3print(get_sum_simple(2, 3)) # 5#include <iostream>using namespace std;
int getSum(int a, int b) { while (b != 0) { int carry = (unsigned int)(a & b) << 1; a = a ^ b; b = carry; } return a;}
int main() { cout << getSum(1, 2) << endl; // 3 cout << getSum(2, 3) << endl; // 5 return 0;}public class Main { static int getSum(int a, int b) { while (b != 0) { int carry = (a & b) << 1; a = a ^ b; b = carry; } return a; }
public static void main(String[] args) { System.out.println(getSum(1, 2)); // 3 System.out.println(getSum(2, 3)); // 5 }}fun getSum(a: Int, b: Int): Int { var x = a var y = b while (y != 0) { val carry = (x and y) shl 1 x = x xor y y = carry } return x}
fun main() { println(getSum(1, 2)) // 3 println(getSum(2, 3)) // 5}int getSum(int a, int b) { int x = a, y = b; while (y != 0) { int carry = (x & y) << 1; x = x ^ y; y = carry; } return x;}
void main() { print(getSum(1, 2)); // 3 print(getSum(2, 3)); // 5}193. Đảo ngược bit (Reverse Bits)
Độ khó: Trung bình · Chủ đề: Bit Manipulation
Cho một số nguyên không dấu 32-bit n, đảo ngược thứ tự các bit của nó và trả về số nguyên không dấu tương ứng.
Ví dụ 1:
Input: n = 00000010100101000001111010011100 (binary)Output: 964176192 (00111001011110000010100101000000 binary)Ví dụ 2:
Input: n = 11111111111111111111111111111101 (binary)Output: 3221225471 (10111111111111111111111111111111 binary)Ràng buộc:
- Input là một số nguyên không dấu 32-bit
Xem đáp án
def reverse_bits(n): result = 0 for _ in range(32): result = (result << 1) | (n & 1) # đẩy bit thấp nhất của n vào cuối result n >>= 1 return result
print(reverse_bits(0b00000010100101000001111010011100)) # 964176192print(reverse_bits(0b11111111111111111111111111111101)) # 3221225471#include <iostream>using namespace std;
uint32_t reverseBits(uint32_t n) { uint32_t result = 0; for (int i = 0; i < 32; i++) { result = (result << 1) | (n & 1); n >>= 1; } return result;}
int main() { cout << reverseBits(0b00000010100101000001111010011100u) << endl; // 964176192 cout << reverseBits(0b11111111111111111111111111111101u) << endl; // 3221225471 return 0;}public class Main { static int reverseBits(int n) { int result = 0; for (int i = 0; i < 32; i++) { result = (result << 1) | (n & 1); n >>>= 1; } return result; }
public static void main(String[] args) { System.out.println(Integer.toUnsignedLong(reverseBits(0b00000010100101000001111010011100))); // 964176192 System.out.println(Integer.toUnsignedLong(reverseBits(0b11111111111111111111111111111101))); // 3221225471 }}fun reverseBits(n: Int): Int { var num = n var result = 0 repeat(32) { result = (result shl 1) or (num and 1) num = num ushr 1 } return result}
fun main() { println(reverseBits(0x02941E9Cu.toInt()).toUInt()) // 964176192 println(reverseBits(0xFFFFFFFDu.toInt()).toUInt()) // 3221225471}int reverseBits(int n) { int result = 0; for (int i = 0; i < 32; i++) { result = ((result << 1) | (n & 1)) & 0xFFFFFFFF; n >>= 1; } return result;}
void main() { print(reverseBits(0x02941E9C)); // 964176192 print(reverseBits(0xFFFFFFFD)); // 3221225471}194. Chia nhãn phân vùng (Partition Labels)
Độ khó: Trung bình · Chủ đề: Greedy
Cho chuỗi s, chia s thành số lượng phần (partition) tối đa sao cho mỗi ký tự chỉ xuất hiện trong đúng 1 phần. Trả về danh sách độ dài các phần theo đúng thứ tự.
Ví dụ 1:
Input: s = "ababcbacadefegdehijhklij"Output: [9, 7, 8]Giải thích: "ababcbaca", "defegde", "hijhklij".Ví dụ 2:
Input: s = "eccbbbbdec"Output: [10]Ràng buộc:
1 <= s.length <= 500- s chỉ gồm chữ cái thường
Xem đáp án
def partition_labels(s): last_index = {c: i for i, c in enumerate(s)} # vị trí xuất hiện cuối cùng của mỗi ký tự
result = [] start = end = 0 for i, c in enumerate(s): end = max(end, last_index[c]) if i == end: result.append(end - start + 1) start = i + 1 return result
print(partition_labels("ababcbacadefegdehijhklij")) # [9, 7, 8]print(partition_labels("eccbbbbdec")) # [10]#include <iostream>#include <vector>#include <string>#include <unordered_map>#include <algorithm>using namespace std;
vector<int> partitionLabels(string s) { unordered_map<char, int> lastIndex; for (int i = 0; i < (int)s.size(); i++) lastIndex[s[i]] = i;
vector<int> result; int start = 0, end = 0; for (int i = 0; i < (int)s.size(); i++) { end = max(end, lastIndex[s[i]]); if (i == end) { result.push_back(end - start + 1); start = i + 1; } } return result;}
int main() { for (int x : partitionLabels("ababcbacadefegdehijhklij")) cout << x << " "; cout << endl; // 9 7 8 for (int x : partitionLabels("eccbbbbdec")) cout << x << " "; cout << endl; // 10 return 0;}import java.util.*;
public class Main { static List<Integer> partitionLabels(String s) { Map<Character, Integer> lastIndex = new HashMap<>(); for (int i = 0; i < s.length(); i++) lastIndex.put(s.charAt(i), i);
List<Integer> result = new ArrayList<>(); int start = 0, end = 0; for (int i = 0; i < s.length(); i++) { end = Math.max(end, lastIndex.get(s.charAt(i))); if (i == end) { result.add(end - start + 1); start = i + 1; } } return result; }
public static void main(String[] args) { System.out.println(partitionLabels("ababcbacadefegdehijhklij")); // [9, 7, 8] System.out.println(partitionLabels("eccbbbbdec")); // [10] }}fun partitionLabels(s: String): List<Int> { val lastIndex = mutableMapOf<Char, Int>() for (i in s.indices) lastIndex[s[i]] = i
val result = mutableListOf<Int>() var start = 0 var end = 0 for (i in s.indices) { end = maxOf(end, lastIndex[s[i]]!!) if (i == end) { result.add(end - start + 1) start = i + 1 } } return result}
fun main() { println(partitionLabels("ababcbacadefegdehijhklij")) // [9, 7, 8] println(partitionLabels("eccbbbbdec")) // [10]}List<int> partitionLabels(String s) { final lastIndex = <String, int>{}; for (int i = 0; i < s.length; i++) lastIndex[s[i]] = i;
final result = <int>[]; int start = 0, end = 0; for (int i = 0; i < s.length; i++) { end = end > lastIndex[s[i]]! ? end : lastIndex[s[i]]!; if (i == end) { result.add(end - start + 1); start = i + 1; } } return result;}
void main() { print(partitionLabels("ababcbacadefegdehijhklij")); // [9, 7, 8] print(partitionLabels("eccbbbbdec")); // [10]}195. Số mũi tên tối thiểu bắn bóng bay (Minimum Number of Arrows to Burst Balloons)
Độ khó: Trung bình · Chủ đề: Greedy
Có các quả bóng bay hình cầu được biểu diễn dạng khoảng points[i] = [xstart, xend] trên trục ngang. Bắn mũi tên thẳng đứng tại tọa độ x sẽ làm nổ mọi bóng có xstart <= x <= xend. Tìm số mũi tên tối thiểu để làm nổ hết tất cả bóng.
Ví dụ 1:
Input: points = [[10,16],[2,8],[1,6],[7,12]]Output: 2Giải thích: Bắn tại x=6 nổ [2,8] và [1,6]; bắn tại x=11 nổ [10,16] và [7,12].Ví dụ 2:
Input: points = [[1,2],[3,4],[5,6],[7,8]]Output: 4Ràng buộc:
1 <= points.length <= 10^5
Xem đáp án
def find_min_arrow_shots(points): if not points: return 0
points.sort(key=lambda p: p[1]) # sắp theo điểm kết thúc
arrows = 1 current_end = points[0][1] for start, end in points[1:]: if start > current_end: arrows += 1 current_end = end
return arrows
print(find_min_arrow_shots([[10, 16], [2, 8], [1, 6], [7, 12]])) # 2print(find_min_arrow_shots([[1, 2], [3, 4], [5, 6], [7, 8]])) # 4#include <iostream>#include <vector>#include <algorithm>using namespace std;
int findMinArrowShots(vector<vector<int>> points) { if (points.empty()) return 0;
sort(points.begin(), points.end(), [](auto& a, auto& b) { return a[1] < b[1]; });
int arrows = 1; int currentEnd = points[0][1]; for (int i = 1; i < (int)points.size(); i++) { if (points[i][0] > currentEnd) { arrows++; currentEnd = points[i][1]; } }
return arrows;}
int main() { cout << findMinArrowShots({{10, 16}, {2, 8}, {1, 6}, {7, 12}}) << endl; // 2 cout << findMinArrowShots({{1, 2}, {3, 4}, {5, 6}, {7, 8}}) << endl; // 4 return 0;}import java.util.*;
public class Main { static int findMinArrowShots(int[][] points) { if (points.length == 0) return 0;
Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1]));
int arrows = 1; int currentEnd = points[0][1]; for (int i = 1; i < points.length; i++) { if (points[i][0] > currentEnd) { arrows++; currentEnd = points[i][1]; } }
return arrows; }
public static void main(String[] args) { System.out.println(findMinArrowShots(new int[][]{{10, 16}, {2, 8}, {1, 6}, {7, 12}})); // 2 System.out.println(findMinArrowShots(new int[][]{{1, 2}, {3, 4}, {5, 6}, {7, 8}})); // 4 }}fun findMinArrowShots(points: Array<IntArray>): Int { if (points.isEmpty()) return 0
points.sortBy { it[1] }
var arrows = 1 var currentEnd = points[0][1] for (i in 1 until points.size) { if (points[i][0] > currentEnd) { arrows++ currentEnd = points[i][1] } }
return arrows}
fun main() { println(findMinArrowShots(arrayOf(intArrayOf(10, 16), intArrayOf(2, 8), intArrayOf(1, 6), intArrayOf(7, 12)))) // 2 println(findMinArrowShots(arrayOf(intArrayOf(1, 2), intArrayOf(3, 4), intArrayOf(5, 6), intArrayOf(7, 8)))) // 4}int findMinArrowShots(List<List<int>> points) { if (points.isEmpty) return 0;
points.sort((a, b) => a[1].compareTo(b[1]));
int arrows = 1; int currentEnd = points[0][1]; for (int i = 1; i < points.length; i++) { if (points[i][0] > currentEnd) { arrows++; currentEnd = points[i][1]; } }
return arrows;}
void main() { print(findMinArrowShots([[10, 16], [2, 8], [1, 6], [7, 12]])); // 2 print(findMinArrowShots([[1, 2], [3, 4], [5, 6], [7, 8]])); // 4}196. Kẹo cho học sinh (Candy)
Độ khó: Khó · Chủ đề: Greedy
Có n học sinh đứng thành hàng, mỗi em có điểm đánh giá ratings[i]. Mỗi em nhận ít nhất 1 viên kẹo. Học sinh có điểm cao hơn bạn đứng cạnh phải nhận nhiều kẹo hơn bạn đó. Tìm số kẹo tối thiểu cần phát.
Ví dụ 1:
Input: ratings = [1, 0, 2]Output: 5Giải thích: Phát [2, 1, 2].Ví dụ 2:
Input: ratings = [1, 2, 2]Output: 4Giải thích: Phát [1, 2, 1]. Vị trí thứ 3 chỉ cần 1 kẹo vì không có yêu cầu tăng nghiêm ngặt so với vị trí thứ 2.Ràng buộc:
n == ratings.length, 1 <= n <= 2 * 10^4
Xem đáp án
def candy(ratings): n = len(ratings) candies = [1] * n
# Quét trái sang phải: đảm bảo điều kiện với bạn bên trái for i in range(1, n): if ratings[i] > ratings[i - 1]: candies[i] = candies[i - 1] + 1
# Quét phải sang trái: đảm bảo điều kiện với bạn bên phải for i in range(n - 2, -1, -1): if ratings[i] > ratings[i + 1]: candies[i] = max(candies[i], candies[i + 1] + 1)
return sum(candies)
print(candy([1, 0, 2])) # 5print(candy([1, 2, 2])) # 4#include <iostream>#include <vector>#include <numeric>#include <algorithm>using namespace std;
int candy(vector<int> ratings) { int n = ratings.size(); vector<int> candies(n, 1);
for (int i = 1; i < n; i++) { if (ratings[i] > ratings[i - 1]) candies[i] = candies[i - 1] + 1; }
for (int i = n - 2; i >= 0; i--) { if (ratings[i] > ratings[i + 1]) candies[i] = max(candies[i], candies[i + 1] + 1); }
return accumulate(candies.begin(), candies.end(), 0);}
int main() { cout << candy({1, 0, 2}) << endl; // 5 cout << candy({1, 2, 2}) << endl; // 4 return 0;}public class Main { static int candy(int[] ratings) { int n = ratings.length; int[] candies = new int[n]; java.util.Arrays.fill(candies, 1);
for (int i = 1; i < n; i++) { if (ratings[i] > ratings[i - 1]) candies[i] = candies[i - 1] + 1; }
for (int i = n - 2; i >= 0; i--) { if (ratings[i] > ratings[i + 1]) candies[i] = Math.max(candies[i], candies[i + 1] + 1); }
int total = 0; for (int c : candies) total += c; return total; }
public static void main(String[] args) { System.out.println(candy(new int[]{1, 0, 2})); // 5 System.out.println(candy(new int[]{1, 2, 2})); // 4 }}fun candy(ratings: IntArray): Int { val n = ratings.size val candies = IntArray(n) { 1 }
for (i in 1 until n) { if (ratings[i] > ratings[i - 1]) candies[i] = candies[i - 1] + 1 }
for (i in n - 2 downTo 0) { if (ratings[i] > ratings[i + 1]) candies[i] = maxOf(candies[i], candies[i + 1] + 1) }
return candies.sum()}
fun main() { println(candy(intArrayOf(1, 0, 2))) // 5 println(candy(intArrayOf(1, 2, 2))) // 4}int candy(List<int> ratings) { int n = ratings.length; final candies = List.filled(n, 1);
for (int i = 1; i < n; i++) { if (ratings[i] > ratings[i - 1]) candies[i] = candies[i - 1] + 1; }
for (int i = n - 2; i >= 0; i--) { if (ratings[i] > ratings[i + 1]) { candies[i] = candies[i] > candies[i + 1] + 1 ? candies[i] : candies[i + 1] + 1; } }
return candies.reduce((a, b) => a + b);}
void main() { print(candy([1, 0, 2])); // 5 print(candy([1, 2, 2])); // 4}197. Bitwise AND của một dải số (Bitwise AND of Numbers Range)
Độ khó: Khó · Chủ đề: Bit Manipulation
Cho 2 số nguyên left và right biểu diễn một dải [left, right], trả về kết quả phép AND theo bit của tất cả các số nguyên trong dải đó (tính cả 2 đầu).
Ví dụ 1:
Input: left = 5, right = 7Output: 4Giải thích: 5 & 6 & 7 = 4 (0101 & 0110 & 0111 = 0100).Ví dụ 2:
Input: left = 0, right = 0Output: 0Ràng buộc:
0 <= left <= right <= 2^31 - 1
Xem đáp án
def range_bitwise_and(left, right): # Tìm tiền tố bit chung của left và right bằng cách dịch phải đến khi bằng nhau shift = 0 while left != right: left >>= 1 right >>= 1 shift += 1 return left << shift
print(range_bitwise_and(5, 7)) # 4print(range_bitwise_and(0, 0)) # 0#include <iostream>using namespace std;
int rangeBitwiseAnd(int left, int right) { int shift = 0; while (left != right) { left >>= 1; right >>= 1; shift++; } return left << shift;}
int main() { cout << rangeBitwiseAnd(5, 7) << endl; // 4 cout << rangeBitwiseAnd(0, 0) << endl; // 0 return 0;}public class Main { static int rangeBitwiseAnd(int left, int right) { int shift = 0; while (left != right) { left >>= 1; right >>= 1; shift++; } return left << shift; }
public static void main(String[] args) { System.out.println(rangeBitwiseAnd(5, 7)); // 4 System.out.println(rangeBitwiseAnd(0, 0)); // 0 }}fun rangeBitwiseAnd(left: Int, right: Int): Int { var l = left var r = right var shift = 0 while (l != r) { l = l shr 1 r = r shr 1 shift++ } return l shl shift}
fun main() { println(rangeBitwiseAnd(5, 7)) // 4 println(rangeBitwiseAnd(0, 0)) // 0}int rangeBitwiseAnd(int left, int right) { int l = left, r = right; int shift = 0; while (l != r) { l >>= 1; r >>= 1; shift++; } return l << shift;}
void main() { print(rangeBitwiseAnd(5, 7)); // 4 print(rangeBitwiseAnd(0, 0)); // 0}198. XOR lớn nhất của hai số trong mảng (Maximum XOR of Two Numbers in an Array)
Độ khó: Khó · Chủ đề: Bit Manipulation, Trie
Cho mảng số nguyên không âm nums, tìm giá trị lớn nhất của nums[i] XOR nums[j] với 0 <= i, j < nums.length. Yêu cầu độ phức tạp O(n) (dùng Trie theo bit).
Ví dụ 1:
Input: nums = [3, 10, 5, 25, 2, 8]Output: 28Giải thích: 5 XOR 25 = 28.Ví dụ 2:
Input: nums = [14, 70, 53, 83, 49, 91, 36, 80, 92, 51, 66, 70]Output: 127Ràng buộc:
1 <= nums.length <= 2 * 10^50 <= nums[i] <= 2^31 - 1
Xem đáp án
def find_maximum_xor(nums): max_xor = 0 mask = 0 # Xây prefix theo từng bit từ cao xuống thấp, dùng set để tra cứu O(1) for i in range(31, -1, -1): mask |= (1 << i) prefixes = {num & mask for num in nums}
candidate = max_xor | (1 << i) # Nếu tồn tại 2 prefix p1, p2 sao cho p1 ^ p2 == candidate thì chấp nhận bit này if any((candidate ^ p) in prefixes for p in prefixes): max_xor = candidate
return max_xor
print(find_maximum_xor([3, 10, 5, 25, 2, 8])) # 28print(find_maximum_xor([14, 70, 53, 83, 49, 91, 36, 80, 92, 51, 66, 70])) # 127#include <iostream>#include <vector>#include <unordered_set>using namespace std;
int findMaximumXor(vector<int> nums) { int maxXor = 0, mask = 0; for (int i = 31; i >= 0; i--) { mask |= (1 << i); unordered_set<int> prefixes; for (int num : nums) prefixes.insert(num & mask);
int candidate = maxXor | (1 << i); for (int p : prefixes) { if (prefixes.count(candidate ^ p)) { maxXor = candidate; break; } } } return maxXor;}
int main() { cout << findMaximumXor({3, 10, 5, 25, 2, 8}) << endl; // 28 cout << findMaximumXor({14, 70, 53, 83, 49, 91, 36, 80, 92, 51, 66, 70}) << endl; // 127 return 0;}import java.util.*;
public class Main { static int findMaximumXor(int[] nums) { int maxXor = 0, mask = 0; for (int i = 31; i >= 0; i--) { mask |= (1 << i); Set<Integer> prefixes = new HashSet<>(); for (int num : nums) prefixes.add(num & mask);
int candidate = maxXor | (1 << i); for (int p : prefixes) { if (prefixes.contains(candidate ^ p)) { maxXor = candidate; break; } } } return maxXor; }
public static void main(String[] args) { System.out.println(findMaximumXor(new int[]{3, 10, 5, 25, 2, 8})); // 28 System.out.println(findMaximumXor(new int[]{14, 70, 53, 83, 49, 91, 36, 80, 92, 51, 66, 70})); // 127 }}fun findMaximumXor(nums: IntArray): Int { var maxXor = 0 var mask = 0 for (i in 31 downTo 0) { mask = mask or (1 shl i) val prefixes = nums.map { it and mask }.toHashSet()
val candidate = maxXor or (1 shl i) if (prefixes.any { (candidate xor it) in prefixes }) { maxXor = candidate } } return maxXor}
fun main() { println(findMaximumXor(intArrayOf(3, 10, 5, 25, 2, 8))) // 28 println(findMaximumXor(intArrayOf(14, 70, 53, 83, 49, 91, 36, 80, 92, 51, 66, 70))) // 127}int findMaximumXor(List<int> nums) { int maxXor = 0, mask = 0; for (int i = 31; i >= 0; i--) { mask |= (1 << i); final prefixes = nums.map((num) => num & mask).toSet();
final candidate = maxXor | (1 << i); if (prefixes.any((p) => prefixes.contains(candidate ^ p))) { maxXor = candidate; } } return maxXor;}
void main() { print(findMaximumXor([3, 10, 5, 25, 2, 8])); // 28 print(findMaximumXor([14, 70, 53, 83, 49, 91, 36, 80, 92, 51, 66, 70])); // 127}199. Tối đa hóa vốn (IPO)
Độ khó: Khó · Chủ đề: Greedy, Heap
Bạn có vốn ban đầu w và có thể thực hiện tối đa k dự án (mỗi dự án chỉ làm 1 lần). Dự án i cần vốn tối thiểu capital[i] và mang lại lợi nhuận thuần profits[i]. Chọn tối đa k dự án (chỉ làm được dự án nếu vốn hiện có >= capital[i], sau khi làm vốn tăng thêm profits[i]) để tối đa hóa vốn cuối cùng.
Ví dụ 1:
Input: k = 2, w = 0, profits = [1, 2, 3], capital = [0, 1, 1]Output: 4Giải thích: Làm dự án 0 (vốn 0 -> 1), rồi dự án 2 (vốn 1 -> 4).Ví dụ 2:
Input: k = 3, w = 0, profits = [1, 2, 3], capital = [0, 1, 2]Output: 6Ràng buộc:
1 <= k <= 10^50 <= w <= 10^9n == profits.length == capital.length, 1 <= n <= 10^5
Xem đáp án
import heapq
def find_maximized_capital(k, w, profits, capital): # Sắp dự án theo vốn cần thiết tăng dần projects = sorted(zip(capital, profits)) max_heap = [] # heap chứa lợi nhuận của các dự án đã "mở khóa" (đủ vốn), lấy giá trị lớn nhất i = 0 n = len(projects)
for _ in range(k): while i < n and projects[i][0] <= w: heapq.heappush(max_heap, -projects[i][1]) i += 1 if not max_heap: break w += -heapq.heappop(max_heap)
return w
print(find_maximized_capital(2, 0, [1, 2, 3], [0, 1, 1])) # 4print(find_maximized_capital(3, 0, [1, 2, 3], [0, 1, 2])) # 6#include <iostream>#include <vector>#include <queue>#include <algorithm>using namespace std;
long findMaximizedCapital(int k, long w, vector<int> profits, vector<int> capital) { int n = profits.size(); vector<pair<int, int>> projects; for (int i = 0; i < n; i++) projects.push_back({capital[i], profits[i]}); sort(projects.begin(), projects.end());
priority_queue<int> maxHeap; int i = 0; for (int iter = 0; iter < k; iter++) { while (i < n && projects[i].first <= w) { maxHeap.push(projects[i].second); i++; } if (maxHeap.empty()) break; w += maxHeap.top(); maxHeap.pop(); }
return w;}
int main() { cout << findMaximizedCapital(2, 0, {1, 2, 3}, {0, 1, 1}) << endl; // 4 cout << findMaximizedCapital(3, 0, {1, 2, 3}, {0, 1, 2}) << endl; // 6 return 0;}import java.util.*;
public class Main { static long findMaximizedCapital(int k, long w, int[] profits, int[] capital) { int n = profits.length; Integer[] idx = new Integer[n]; for (int i = 0; i < n; i++) idx[i] = i; Arrays.sort(idx, (a, b) -> capital[a] - capital[b]);
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); int i = 0; for (int iter = 0; iter < k; iter++) { while (i < n && capital[idx[i]] <= w) { maxHeap.add(profits[idx[i]]); i++; } if (maxHeap.isEmpty()) break; w += maxHeap.poll(); }
return w; }
public static void main(String[] args) { System.out.println(findMaximizedCapital(2, 0, new int[]{1, 2, 3}, new int[]{0, 1, 1})); // 4 System.out.println(findMaximizedCapital(3, 0, new int[]{1, 2, 3}, new int[]{0, 1, 2})); // 6 }}import java.util.PriorityQueue
fun findMaximizedCapital(k: Int, w: Long, profits: IntArray, capital: IntArray): Long { var capitalW = w val n = profits.size val projects = (0 until n).map { capital[it] to profits[it] }.sortedBy { it.first }
val maxHeap = PriorityQueue<Int>(compareByDescending { it }) var i = 0 repeat(k) { while (i < n && projects[i].first <= capitalW) { maxHeap.add(projects[i].second) i++ } if (maxHeap.isEmpty()) return@repeat capitalW += maxHeap.poll() }
return capitalW}
fun main() { println(findMaximizedCapital(2, 0, intArrayOf(1, 2, 3), intArrayOf(0, 1, 1))) // 4 println(findMaximizedCapital(3, 0, intArrayOf(1, 2, 3), intArrayOf(0, 1, 2))) // 6}import 'dart:collection';
int findMaximizedCapital(int k, int w, List<int> profits, List<int> capital) { int capitalW = w; final n = profits.length; final projects = List.generate(n, (i) => [capital[i], profits[i]]); projects.sort((a, b) => a[0].compareTo(b[0]));
final maxHeap = PriorityQueue<int>((a, b) => b.compareTo(a)); int i = 0; for (int iter = 0; iter < k; iter++) { while (i < n && projects[i][0] <= capitalW) { maxHeap.add(projects[i][1]); i++; } if (maxHeap.isEmpty) break; capitalW += maxHeap.removeFirst(); }
return capitalW;}
void main() { print(findMaximizedCapital(2, 0, [1, 2, 3], [0, 1, 1])); // 4 print(findMaximizedCapital(3, 0, [1, 2, 3], [0, 1, 2])); // 6}200. Gộp k danh sách liên kết đã sắp xếp (Merge k Sorted Lists)
Độ khó: Khó · Chủ đề: Tổng hợp (Heap, Linked List, Divide & Conquer)
Cho một mảng gồm k danh sách liên kết, mỗi danh sách đã được sắp xếp tăng dần. Gộp tất cả thành một danh sách liên kết duy nhất đã sắp xếp, rồi trả về danh sách đó. Đây là bài tổng hợp — vừa dùng cấu trúc dữ liệu (linked list), vừa dùng heap để chọn phần tử nhỏ nhất hiệu quả.
Ví dụ 1:
Input: lists = [[1,4,5],[1,3,4],[2,6]]Output: [1,1,2,3,4,4,5,6]Ví dụ 2:
Input: lists = []Output: []Ràng buộc:
k == lists.length, 0 <= k <= 10^4- Tổng số node trên tất cả các danh sách không vượt quá 5 * 10^4
Xem đáp án
import heapq
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
def merge_k_lists(lists): heap = [] # Đẩy node đầu tiên của mỗi danh sách vào heap; dùng index để tránh so sánh trực tiếp ListNode khi val bằng nhau for i, node in enumerate(lists): if node: heapq.heappush(heap, (node.val, i, node))
dummy = ListNode() tail = dummy
while heap: val, i, node = heapq.heappop(heap) tail.next = node tail = tail.next if node.next: heapq.heappush(heap, (node.next.val, i, node.next))
return dummy.next
def build_list(values): dummy = ListNode() tail = dummy for v in values: tail.next = ListNode(v) tail = tail.next return dummy.next
def list_to_array(node): result = [] while node: result.append(node.val) node = node.next return result
lists = [build_list([1, 4, 5]), build_list([1, 3, 4]), build_list([2, 6])]print(list_to_array(merge_k_lists(lists))) # [1, 1, 2, 3, 4, 4, 5, 6]print(list_to_array(merge_k_lists([]))) # []#include <iostream>#include <vector>#include <queue>using namespace std;
struct ListNode { int val; ListNode* next; ListNode(int v) : val(v), next(nullptr) {}};
ListNode* mergeKLists(vector<ListNode*> lists) { auto cmp = [](ListNode* a, ListNode* b) { return a->val > b->val; }; priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> heap(cmp);
for (auto node : lists) if (node) heap.push(node);
ListNode dummy(0); ListNode* tail = &dummy;
while (!heap.empty()) { ListNode* node = heap.top(); heap.pop(); tail->next = node; tail = tail->next; if (node->next) heap.push(node->next); }
return dummy.next;}
ListNode* buildList(vector<int> values) { ListNode dummy(0); ListNode* tail = &dummy; for (int v : values) { tail->next = new ListNode(v); tail = tail->next; } return dummy.next;}
void printList(ListNode* node) { while (node) { cout << node->val << " "; node = node->next; } cout << endl;}
int main() { vector<ListNode*> lists = {buildList({1, 4, 5}), buildList({1, 3, 4}), buildList({2, 6})}; printList(mergeKLists(lists)); // 1 1 2 3 4 4 5 6 printList(mergeKLists({})); // (empty) return 0;}import java.util.*;
public class Main { static class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
static ListNode mergeKLists(ListNode[] lists) { PriorityQueue<ListNode> heap = new PriorityQueue<>((a, b) -> a.val - b.val); for (ListNode node : lists) if (node != null) heap.add(node);
ListNode dummy = new ListNode(0); ListNode tail = dummy;
while (!heap.isEmpty()) { ListNode node = heap.poll(); tail.next = node; tail = tail.next; if (node.next != null) heap.add(node.next); }
return dummy.next; }
static ListNode buildList(int[] values) { ListNode dummy = new ListNode(0); ListNode tail = dummy; for (int v : values) { tail.next = new ListNode(v); tail = tail.next; } return dummy.next; }
static List<Integer> listToArray(ListNode node) { List<Integer> result = new ArrayList<>(); while (node != null) { result.add(node.val); node = node.next; } return result; }
public static void main(String[] args) { ListNode[] lists = {buildList(new int[]{1, 4, 5}), buildList(new int[]{1, 3, 4}), buildList(new int[]{2, 6})}; System.out.println(listToArray(mergeKLists(lists))); // [1, 1, 2, 3, 4, 4, 5, 6] System.out.println(listToArray(mergeKLists(new ListNode[]{}))); // [] }}import java.util.PriorityQueue
class ListNode(var value: Int) { var next: ListNode? = null}
fun mergeKLists(lists: List<ListNode?>): ListNode? { val heap = PriorityQueue<ListNode>(compareBy { it.value }) for (node in lists) if (node != null) heap.add(node)
val dummy = ListNode(0) var tail = dummy
while (heap.isNotEmpty()) { val node = heap.poll() tail.next = node tail = node node.next?.let { heap.add(it) } }
return dummy.next}
fun buildList(values: List<Int>): ListNode? { val dummy = ListNode(0) var tail = dummy for (v in values) { tail.next = ListNode(v) tail = tail.next!! } return dummy.next}
fun listToArray(node: ListNode?): List<Int> { val result = mutableListOf<Int>() var cur = node while (cur != null) { result.add(cur.value) cur = cur.next } return result}
fun main() { val lists = listOf(buildList(listOf(1, 4, 5)), buildList(listOf(1, 3, 4)), buildList(listOf(2, 6))) println(listToArray(mergeKLists(lists))) // [1, 1, 2, 3, 4, 4, 5, 6] println(listToArray(mergeKLists(emptyList()))) // []}import 'dart:collection';
class ListNode { int val; ListNode? next; ListNode(this.val);}
ListNode? mergeKLists(List<ListNode?> lists) { final heap = PriorityQueue<ListNode>((a, b) => a.val.compareTo(b.val)); for (var node in lists) { if (node != null) heap.add(node); }
final dummy = ListNode(0); ListNode tail = dummy;
while (heap.isNotEmpty) { final node = heap.removeFirst(); tail.next = node; tail = node; if (node.next != null) heap.add(node.next!); }
return dummy.next;}
ListNode? buildList(List<int> values) { final dummy = ListNode(0); ListNode tail = dummy; for (var v in values) { tail.next = ListNode(v); tail = tail.next!; } return dummy.next;}
List<int> listToArray(ListNode? node) { final result = <int>[]; var cur = node; while (cur != null) { result.add(cur.val); cur = cur.next; } return result;}
void main() { final lists = [buildList([1, 4, 5]), buildList([1, 3, 4]), buildList([2, 6])]; print(listToArray(mergeKLists(lists))); // [1, 1, 2, 3, 4, 4, 5, 6] print(listToArray(mergeKLists([]))); // []}Chúc mừng bạn đã hoàn thành 200 bài luyện thuật toán! Đây là nền tảng vững chắc để bạn tự tin giải các bài toán trên LeetCode, HackerRank hay chuẩn bị cho phỏng vấn kỹ thuật.