Blue Amethyst
Junior Member
nhìn hơi cồng kềnh tí, may vẫn được 100
https://leetcode.com/submissions/detail/752520167/
https://leetcode.com/submissions/detail/752520167/
Tui mà phỏng vấn là tui loại ngay nha.Mấy bài linkedlist này tôi hay dùng stack với queue cho nhẹ đầu. Khi nào phỏng vấn họ không chịu thì tính sau Như bài sáng nay tôi dùng 2 queue + 1 stack https://leetcode.com/submissions/detail/752503120/
Tui mà phỏng vấn là tui loại ngay nha.
Cái khó của mấy cái bài LinkedList này là không được dùng thêm memory (bởi nếu dùng thêm memory người ta tạo Array luôn rồi làm việc trên array thì bài toán thành trivial)
Chú tạo thêm queue và đặc biệt stack (stack là array backed) là biến cái bài này thành trivial rồi
Tui mà phỏng vấn là tui loại ngay nha.
Cái khó của mấy cái bài LinkedList này là không được dùng thêm memory (bởi nếu dùng thêm memory người ta tạo Array luôn rồi làm việc trên array thì bài toán thành trivial)
Chú tạo thêm queue và đặc biệt stack (stack là array backed) là biến cái bài này thành trivial rồi
Đa số đệ quy đơn giản có thể được tối ưu hóa bằng tail-call recursion bởi compiler nhé.Dùng đệ quy thì vẫn là tốn thêm O(n * sizeof(stackframe)) memory rồi, có gì khác nhau đâu.
Tui không nóng mà.Thím nóng thế. Tôi thấy đề bài không yêu cầu phải dùng constant memory, cũng không đòi 1 pass nên cứ tìm cách dễ nhất mà làm thôi. Khi nào đề bài bắt buộc thì tính sau
Đa số đệ quy đơn giản có thể được tối ưu hóa bằng tail-call recursion bởi compiler nhé.
Những trường hợp này không increase stack size tí nào đâu. Memory vẫn constant O(1) thôi
Bác có thể tìm hiểu tail call recursion optimization.
function f() {
do something
return do another thing with f()
}
function f() {
do some thing
return f() - 2
}
Ha, cũng chẳng muốn bình luận thêm gì nữa.Kiểu chương trình duy áp dụng được tail call optimization là:
Code:function f() { do something return do another thing with f() }
Kể cả kiểu này cũng không phải tail call.
Mặc dù có thể sửa lại code thành (-2 + f()), nhưng nếu đoạn đó là cái gì đó khác (bình phương chẳng hạn) thì chưa chắc làm được.Code:function f() { do some thing return f() - 2 }
Thường những bài toán hơi phức tạp chút là hầu như không có cách để đưa về dạng tail call.
Nên không nên dựa vào nó quá nhiều.
Ha, cũng chẳng muốn bình luận thêm gì nữa.
Chỉ hỏi bác câu này để xem bác trả lời thế nào:
- Thế cái đáp án recursion của bài hôm nay https://leetcode.com/problems/reverse-linked-list-ii/solution/ có optimize để khử stack increase được không?
- Có thì vì sao và không thì vì sao?
Mình nhớ có bài 234 nói về linked list palindrome, cách làm one-pass bài đó có phần reverse nửa sau của linked list tương tự như bài hôm nayBài hnay e chỉ có nghĩ đc cách này thôi. Hóng các cao nhân one-pass
Lâu ko dùng c# hay java giờ e sợ mấy cái ngoặc quáBài sáng nay tôi không dùng Stack hay Queue nhé https://leetcode.com/submissions/detail/753321799/
class Solution:
def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
dummy=ListNode(next=head)
cur=head
last_s=dummy #keep track of last small node
last_b=dummy #keep track of last big node
while cur:
next_node=cur.next
if cur.val<x:
if cur!=last_s.next: # move current node to next of last small
tmp=last_s.next
last_s.next=cur
last_b.next=cur.next
cur.next=tmp
last_s=cur
else:
last_b=cur
cur=next_node
return dummy.next
Mấy bài LL này chả có gì nhiều mà bàn luận nữa. Phúc cho ai gặp LL trong phỏng vấn, kakaka.Dạo này vẫn làm leetcode daily đều đều mà lười vào đây.
Còn đây là solution của t, code phát ăn ngay,
https://leetcode.com/submissions/detail/753369667/
std::stable_partition<Iterator>(head, nullptr, [x](int n) { return n < x; });
return head;
// https://www.internalpointers.com/post/writing-custom-iterators-modern-cpp
struct Iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = int;
using pointer = int*;
using reference = int&;
Iterator(ListNode* ptr) : m_ptr(ptr) {}
reference operator*() const { return m_ptr->val; }
pointer operator->() { return &m_ptr->val; }
// Prefix increment
Iterator& operator++() { m_ptr = m_ptr->next; return *this; }
// Postfix increment
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
friend bool operator== (const Iterator& a, const Iterator& b) { return a.m_ptr == b.m_ptr; };
friend bool operator!= (const Iterator& a, const Iterator& b) { return a.m_ptr != b.m_ptr; };
ListNode* m_ptr;
};
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
std::stable_partition<Iterator>(head, nullptr, [x](int n) { return n < x; });
return head;
}
};
Nếu biết dùng thư viện thì dễ hơn bài easy bình thường.Cơ bản thì bài hôm nay hơi dễ để xếp vào medium và hơi khó để xếp vào easy.
O(log(n))
nhưng thao tác insert vào mảng sẽ tốn O(n)
=> tổng chung sẽ là O(n*(log(n)+n)) = O(n*n)
=> rất có sẽ bị TLE (tôi submit thử lần 1 và y như dự đoán, TLE ngay lập tức )O(n*log(n))
trở xuống. Tìm mãi không ra cách nào vừa có thể search và insert đều là O(log(n))
nên phải xem related topics và thấy Binary Indexed Tree. Tới đây thì mọi thứ ez rồi:0
.[5,2,6,2,1]
sẽ lọc ra được [1,2,5,6]
=> tạo BIT có 4 items tương ứng với mảng lọc được.1
=> ứng với index 0
của BIT => sum BIT của các item trước index 0 sẽ là 0 => mảng output sẽ là [_, _, _, _, 0]
. Sau đó tăng index 0
của BIT thêm 1 đơn vị.2
=> ứng với index 1
của BIT => sum BIT của các item trước index 1 sẽ là 1 => mảng output sẽ là [_, _, _, 1, 0]
. Sau đó tăng index 1
của BIT thêm 1 đơn vị.6
=> ứng với index 3
của BIT => sum BIT của các item trước index 3 sẽ là 2 => mảng output sẽ là [_, _, 2, 1, 0]
. Sau đó tăng index 3
của BIT thêm 1 đơn vị.2
=> ứng với index 1
của BIT => sum BIT của các item trước index 1 sẽ là 1 => mảng output sẽ là [_, 1, 2, 1, 0]
. Sau đó tăng index 1
của BIT thêm 1 đơn vị.5
=> ứng với index 2
của BIT => sum BIT của các item trước index 2 sẽ là 3 => mảng output sẽ là [3, 1, 2, 1, 0]
. Sau đó tăng index 2
của BIT thêm 1 đơn vị.O(log(n))
nên tổng phức tạp sẽ là O(nlog(n))
=> không bị TLE.public class Solution {
public IList<int> CountSmaller(int[] nums) {
SortedSet<int> set = new ();
foreach (var num in nums)
set.Add(num);
Dictionary<int, int> indexes = new ();
int len = 0;
foreach (var num in set)
{
indexes[num] = len++;
}
int[] BIT = new int[len+1];
int[] ret = new int[nums.Length];
for (var i = nums.Length - 1; i >= 0; i--)
{
int index = indexes[nums[i]];
ret[i] = sum(BIT, index-1);
update(BIT, index, 1);
}
return ret;
}
void update(int[] BIT, int index, int val)
{
index = index + 1;
while (index < BIT.Length)
{
BIT[index] += val;
index += index & (-index);
}
}
int sum(int[] BIT, int index)
{
int sum = 0;
index = index + 1;
while (index > 0)
{
sum += BIT[index];
index -= index & (-index);
}
return sum;
}
}