thảo luận Leetcode mỗi ngày

  • Người tạo chủ đề Người tạo chủ đề _Gia_Cat_Luong_
  • Ngày bắt đầu Ngày bắt đầu
Trạng thái
Không mở để trả lời thêm.
01/09: Bài này 1 dòng là đủ. :p

Python:
def goodNodes(self, root: TreeNode, prev = -1e5) -> int:
        return 0 if not root else 1 + self.goodNodes(root.left, root.val) + self.goodNodes(root.right, root.val) if root.val >= prev else self.goodNodes(root.left, prev) + self.goodNodes(root.right, prev)
C++:
class Solution {
public:
    int goodNodes(TreeNode* root, int prev = -1e5) {
        if (root == NULL) return 0;
        if (root->val < prev) return goodNodes(root->left, prev) + goodNodes(root->right, prev);
        return 1 + goodNodes(root->left, root->val) + goodNodes(root->right, root->val);
    }
};
JavaScript:
var goodNodes = function(root, prev) {
    if (root === null) {
        return 0
    }
    if (prev===undefined || root.val >= prev) {
        return 1 + goodNodes(root.left, root.val) + goodNodes(root.right, root.val)
    }
    return goodNodes(root.left, prev) + goodNodes(root.right, prev)
};
Mã:
func goodNodes(root *TreeNode) int {
    return goodNodesRecursive(root, -1e5)
}
func goodNodesRecursive(root *TreeNode, prev int) int {
    if root == nil {
        return 0
    }
    if root.Val < prev {
        return goodNodesRecursive(root.Left, prev) + goodNodesRecursive(root.Right, prev)
    }
    return 1 + goodNodesRecursive(root.Left, root.Val) + goodNodesRecursive(root.Right, root.Val)
}

1661999003279.png


K biết testcase cho các ngôn ngữ khác nhau thì có giống nhau k. Chứ thằng Go vừa tốn ít mem mà lại chạy nhanh hơn hẳn, ảo thật đấy.
 
Sửa lần cuối:
C++ 1 dòng
MjfezZB.png

C++:
int goodNodes(TreeNode* p, int mx = INT_MIN) {
    return p ? mx = max(mx, p->val), (p->val >= mx) + goodNodes(p->left, mx) + goodNodes(p->right, mx) : 0;
}
 
đễ đọc dễ hỉu
1iF81i5.png


Mã:
import scala.math._

object Solution {
    val LowerBound = -10_001
  
    def goodNodes(node: TreeNode, maxRoot: Int = LowerBound): Int = {
      if (node == null) {
        0
      } else {
        val current = node.value
      
        val left = goodNodes(node.left, max(maxRoot, current))
        val right = goodNodes(node.right, max(maxRoot, current))
        left + right + (if (current >= maxRoot) 1 else 0)
      }
    }
}
 
Sửa lần cuối:
0FFPAjM.png
Cũng preorder traversal, nhưng là khử đệ quy (iterative approach)
cdGvfgg.png
Java chạy mất 60ms

Java:
class Solution {
    public int goodNodes(TreeNode root) {
        if(root == null)
        {
            throw new NullPointerException("NullRootArgument");
        }
        int ans = 0;
        Stack<TreeNode> s = new Stack<>();
        HashMap<TreeNode, Integer> hash = new HashMap<>();
        s.push(root);
        hash.put(root, root.val);
        while(!s.isEmpty())
        {
            TreeNode current = s.pop();
            int maxVal = hash.get(current);
            if(current.val >= maxVal)
            {
                ans++;
            }
            maxVal = Math.max(current.val, maxVal);
            if(current.right != null)
            {
                s.push(current.right);
                hash.put(current.right, maxVal);
            }
            if(current.left != null)
            {
                s.push(current.left);
                hash.put(current.left, maxVal);
            }
        }
        return ans;
    }
}

WawmAwM.png
làm cho vui chứ code này chắc chậm nhất thread. Java lại không có pair
Untitled.png
 
Khử đệ quy cho dễ tiếp cận, mỗi tội ngốn nhiều bộ nhớ. Thằng loop stack này với đệ quy thì time complexity như nhau mà sao thường đệ quy cho runtime nhanh hơn nhỉ
 
ae cho hỏi cái leetcode này dở hơi ko vậy, travel back 1 daily mất 70 LeetCoins, 1 ngày làm được có 10 coin thì lấy streak week có đc 35 coin thì redeem làm gì ta để mất 70 coins:angry:
 
Chắc leetcode biết hnay ae ĐL nghỉ lễ nên cho bài thể dục nhẹ nhàng :big_smile:
Level order traversal, classic problem
Ruby:
def average_of_levels(root)
  queue = [root]
  res = []
  until queue.empty?
    level_len = queue.length
    sum = 0.0
    level_len.times do
      curr = queue.shift
      queue << curr.left if curr.left
      queue << curr.right if curr.right
      sum += curr.val
    end
    res << sum / level_len
  end
  res
end
 
Sửa lần cuối:
Python:
class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        d = defaultdict(list)

        def dfs(node, i):
            d[i].append(node.val)
            if node.left:
                dfs(node.left, i+1)
            if node.right:
                dfs(node.right, i+1)

        dfs(root, 0)
        return [sum(d[i])/len(d[i]) for i in d]

Python:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        queue = [root]
        res = []
      
        while queue:
            level_len = len(queue)
            level_sum = 0
          
            for i in range(level_len):
                node = queue.pop(0)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
                level_sum += node.val
              
            res.append(level_sum/level_len)
      
        return res
 
Sửa lần cuối:
Cây cối thì ta lại đệ quy
1iF81i5.png


Mã:
defmodule Solution do
  def average_of_levels(root) do
    average_of_levels(root, %{}, 0)
    |> Enum.sort()
    |> Enum.map(fn {_, {v, c}} -> v / c end)
  end

  defp average_of_levels(root, levels, curr_level) do
    case root do
        nil -> levels
        %TreeNode{left: l, right: r, val: v} ->
            Map.update(levels, curr_level, {v, 1}, fn {val, count} -> {val + v, count + 1} end)
            |> Map.merge(average_of_levels(l, %{}, curr_level + 1), &merge/3)
            |> Map.merge(average_of_levels(r, %{}, curr_level + 1), &merge/3)
    end
  end
      
  defp merge(_k, {v1, c1}, {v2, c2}), do: {v1 + v2, c1 + c2}
end
 
Python:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
    bfs=[root]
    while len(bfs)>0:
        length=len(bfs)
        res=0
        for i in range(0,length):
            iteam=bfs.pop(0)
            res+=iteam.val
            if iteam.left is not None: bfs.append(iteam.left)
            if iteam.right is not None: bfs.append(iteam.right)
        yield res/length
 
hkNtitg.png
Quá dễ, lại levelorder traversal
Java:
class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        // level-order traversal
        if(root == null)
        {
            throw new NullPointerException("NullArgument");
        }
        List<Double> ans = new ArrayList<>();
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty())
        {
            int size = q.size();
            double sum = 0;
            for(int i = 0; i < size; i++)
            {
                TreeNode current = q.poll();
                sum += current.val;
                if(current.left != null)
                {
                    q.offer(current.left);
                }
                if(current.right != null)
                {
                    q.offer(current.right);
                }
            }
            ans.add(sum / size);
        }
        return ans;
    }
}
 
Khử đệ quy cho dễ tiếp cận, mỗi tội ngốn nhiều bộ nhớ. Thằng loop stack này với đệ quy thì time complexity như nhau mà sao thường đệ quy cho runtime nhanh hơn nhỉ
khử đệ quy cho vào Stack thì có cấp phát động nên chậm hơn so với xài đệ quy ko có cấp phát động. Chỉ nên khử đệ quy khi nào đệ quy làm tràn stack thoy
MjfezZB.png
 
khử đệ quy cho vào Stack thì có cấp phát động nên chậm hơn so với xài đệ quy ko có cấp phát động. Chỉ nên khử đệ quy khi nào đệ quy làm tràn stack thoy
MjfezZB.png
:doubt: ủa mà khoan, recursive thì allocate stack frame, stack pointer -> tốn và chậm hơn nhiều so với iterative chứ nhỉ?

Trừ khi kiến trúc máy tính không phải Vou-Neumann, hoặc chỉnh cái compiler để nó biên dịch ra loop và jump. Chứ recursive thường tốn kém và chậm hơn iterative chứ?

https://stackoverflow.com/questions/2651112/is-recursion-ever-faster-than-looping
 
:doubt: ủa mà khoan, recursive thì allocate stack frame, stack pointer -> tốn và chậm hơn nhiều so với iterative chứ nhỉ?

Trừ khi kiến trúc máy tính không phải Vou-Neumann, hoặc chỉnh cái compiler để nó biên dịch ra loop và jump. Chứ recursive thường tốn kém và chậm hơn iterative chứ?

https://stackoverflow.com/questions/2651112/is-recursion-ever-faster-than-looping
nếu mà iterative ko cần cái stack kia thì lẹ hơn (vì nó ko ngốn thêm bộ nhớ?) chứ còn chuyển từ đệ quy là xài stack trên vùng stack thành ko đệ quy xài stack trên vùng heap Stack<TreeNode> thì là để tránh tràn stack chứ nếu ko tràn thì code xài Stack<TreeNode> này chạy chậm hơn do phải cấp phát động Stack<TreeNode> trên heap
JEWoIdl.png
Đáng lẽ toy nên nói là "Chỉ nên khử đệ quy như thế này khi nào đệ quy làm tràn stack thoy" thêm chữ "như thế này" nghĩa là xài stack trên vùng heap.

ví dụ nếu nó cho cái cây n node chỉ có 1 bên là bên trái hoặc bên phải thì lúc này cái cây như 1 linked list, cách đệ quy bình thường sẽ đệ quy n lần, nếu n > 10 triệu thì vùng stack bình thường ở Linux là 8MB, Windows là 2MB sẽ ko chứa nổi 10 triệu lần đệ quy (cho là mỗi lần đệ quy push lên stack vài byte, 10 triệu lần là hơn 10MB), phải xài stack trên heap như thế kia
 
Sửa lần cuối:
Bài nay (2.9) y hệt 1.9. Thay vì so sánh thì append vào 1 hash chứa level là len(stack).
Nay ít phải append với pop hơn nên python chạy mượt.
 
:doubt: ủa mà khoan, recursive thì allocate stack frame, stack pointer -> tốn và chậm hơn nhiều so với iterative chứ nhỉ?

Trừ khi kiến trúc máy tính không phải Vou-Neumann, hoặc chỉnh cái compiler để nó biên dịch ra loop và jump. Chứ recursive thường tốn kém và chậm hơn iterative chứ?

https://stackoverflow.com/questions/2651112/is-recursion-ever-faster-than-looping

Nếu không bị tràn stack thì tất cả những thao tác bộ nhớ khi recursive đều là thao tác trên stack:
VD với x86:
  • allocate stack frame, gán stack pointer bản chất chỉ là giảm giá trị thanh ghi rsp. Không phải allocate cái gì ở đây cả,
  • return address thì được lưu tại ô nhớ rất gần địa chỉ hiện tại ==> cache friendly
  • parameter nếu không quá nhiều thì có thể lưu vào thanh ghi.

Trong khi nếu dùng CTDL Stack thì:
  • Data lưu tại bộ nhớ heap, cần cấp phát động trước,
  • Có thể phải giãn nở trong quá trình sử dụng, mỗi lần như vậy là phải cấp phát động lại, copy phần bộ nhớ cũ vào.

Tóm là là dùng CTDL Stack thì ít thao tác hơn, nhưng truy cập memory lại chậm hơn. Nếu có thể khắc phục, biết để allocate trước lượng bộ nhớ cần thiết thì chắc chắn nhanh hơn. Còn không thì phải benchmark.
 
tui code dài quá, thím nào thạo python chỉ tui cách viết ngắn lại với :sweat:

updated: sửa lại theo solution ngắn hơn rùi
Python:
class Solution:
    def numsSameConsecDiff(self, n: int, k: int) -> List[int]:
        res = set()
    
        def gen_num(val, index, arr):
        
            if index == n:
                res.add(arr)
                return
        
            if val < 0 or val > 9:
                return
        
            arr = arr*10 + val
            
            gen_num(val - k, index + 1, arr)
            gen_num(val + k, index + 1, arr)
        
            arr = arr//10
        
        for i in range(1, 10):
            gen_num(i, 0, 0)

        return list(res)
 
Sửa lần cuối:
Python:
class Solution:
    def numsSameConsecDiff(self, n: int, k: int) -> List[int]:
        res=set()
        def help(ind: int,number: int):
            if ind == n:
                res.add(number)
            else:
                lst_num = number%10
                if lst_num + k < 10:
                    help(ind+1,number*10+lst_num+k)
                if lst_num - k >=0:
                    help(ind+1,number*10+lst_num-k)
        for i in range(1,10):
            help(1,i)
        return list(res)
 
Trạng thái
Không mở để trả lời thêm.

Thống kê chủ đề

Ngày tạo
_Gia_Cat_Luong_,
Người trả lời cuối
Vipluckystar,
Trả lời
17.755
Lượt xem
1.212.956
Quay lại
Lên đầu trang