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.
30/08/2022

Ý tưởng: viết lại cái iterator để duyệt matrix theo chiều kim đồng hồ, sau đó dùng std::rotate(top-left, bottom-left, top-left) để quay trái, đưa bottom-left trở thành top-left.

Độ phức tạp: O(n^2) time, O(1) space

https://leetcode.com/submissions/detail/786807820/
t thấy run time khá lớn. Khả năng cần phải implement thêm một số operator để iterator có thể random access
1661828958746.png
 
Sửa lần cuối:
Cay vãi, debug cả tiếng, chả biết sai ở đâu

https://leetcode.com/submissions/detail/786821498/

Python:
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """

        def recursive_rotate(k, start):
            if k <= 1:
                return
            
            end = start + k - 1
            for i in range(0, k - 1):
                matrix[start][start + i], matrix[start + i][end], matrix[end][end-i], matrix[end-i][start] = \
                matrix[end-i][start], matrix[start][start + i], matrix[start + i][end], matrix[end][end-i]
            
            recursive_rotate(k-2, start + 1)
        
        recursive_rotate(len(matrix), 0)
 
t thấy run time khá lớn. Khả năng cần phải implement thêm một số operator để iterator có thể random access
Xem tệp đính kèm 1352196

Tính mỗi phép ++ là đã quá phức tạp rồi, giờ đòi random access nữa thì thôi.
Bài này vectorization hơi khó vì phải access theo cột, nhớ là không có instruction nào load kiểu đó cả.
Nhưng data trên cùng 1 hàng, 1 cột thì khi rotate không bị data dependency nên chắc là vẫn tối ưu được.
 
Tính mỗi phép ++ là đã quá phức tạp rồi, giờ đòi random access nữa thì thôi.
Bài này vectorization hơi khó vì phải access theo cột, nhớ là không có instruction nào load kiểu đó cả.
Nhưng data trên cùng 1 hàng, 1 cột thì khi rotate không bị data dependency nên chắc là vẫn tối ưu được.
bài này lấy ra 4 vị trí tương ứng rồi swap cho nhau sao bác phải làm phức tạp thế?
 
mọi người có hay dùng cái GCC optimize("O3/Ofast") không, em thấy dùng nó lúc nhanh lúc chậm :D
 
bài này lấy ra 4 vị trí tương ứng rồi swap cho nhau sao bác phải làm phức tạp thế?
viết custom iterator để xài std::rotate 1 dòng cho sát chuẩn
1xEuo02.gif


thư viện chuẩn có sẵn hàm std::rotate dịch "1 2 3 4 5" ví dụ 2 lần về bên phải nó ra "4 5 1 2 3" rất tiện lợi. Bài này cũng là right rotate thoy nhưng ko phải trên cái mảng mà là trên cái vòng ngoài của ma trận mxm. Mà cái vòng này nếu duỗi thẳng ra cũng chả khác gì cái mảng kia. Chỉ cần viết 1 cái adaptor (là iterator) là xài được ctdl "vòng ngoài ma trận" này với thuật toán right rotate.

nếu có X ctdl và Y thuật toán thì phải viết X*Y hàm, mỗi hàm cho 1 ctdl là X lần, mà Y thuật toán như vậy là phải viết XY hàm. Nhưng nếu viết cái trung gian liên kết được ctdl với thuật toán thì chỉ cần viết X cái adaptor cho X ctdl và Y hàm có input là cái adaptor chứ ko phải là ctdl, vậy chỉ cần viết X+Y lần thoy. X+Y < X*Y nhiều nên viết custom iterator này thặc ra là tiết kiệm code đó
irGoYrZ.gif
 
Sửa lần cuối:
Hí hí

Bài nay thấy đã medium lại còn phải sửa cái list đó luôn chứ không được return, tưởng không code 1 dòng được cuối cùng loằng ngoằng vẫn ăn. :D

Cách 1 zip xong lộn.

Python:
list(map(lambda p: setitem(matrix, p[0], p[1]), enumerate(map(lambda t: list(t)[::-1], zip(*matrix)))))

Cách 2 lộn xong zip:

Python:
list(map(lambda p: setitem(matrix, p[0], p[1]), enumerate(zip(*matrix[::-1]))))

# Vào xem ông thần https://leetcode.com/problems/rotate-image/discuss/18884/Seven-Short-Solutions-(1-to-7-lines) sốc mẹ luôn. =]]

Python:
A[:] = zip(*A[::-1])
 
Sửa lần cuối:
mọi người có hay dùng cái GCC optimize("O3/Ofast") không, em thấy dùng nó lúc nhanh lúc chậm :D

  • Khi test perf thì phải test nhiều lần, đầu vào giống nhau, đảm bảo hạn chế các yếu tố gây nhiều,
  • Đúng là O3/Ofast nó thêm nhiều loại optimization tùy trường hợp mà có thể làm chậm hơn. Một số trong đó có thể làm code phình to lên ==> tăng tỉ lệ miss cache.
 
Hôm nay phải mutate state thì viết scala vậy
cVL81H2.gif


Mã:
import scala.util.chaining._
import scala.language.implicitConversions

object Solution {
    def rotate(matrix: Array[Array[Int]]): Unit = {
      val n = matrix.length
      (0 until (n + 1) / 2)
        .foreach {
          row =>
            (0 until n/ 2).foreach {
              col =>
                  val rotated = rotateNeg90(n, (row, col))
                 
                  (rotated.drop(3) ++ rotated.take(3))
                      .map {
                        case (r, c) =>  matrix(r)(c)
                      }
                      .zip(rotated)
                      .foreach {
                        case (v, (r, c)) => matrix(r)(c) = v
                      }
            }
        }
    }
   
    private def toGeo(point: (Int, Int)) = {
      val (row, col) = point
      (col, -row)
    }
 
    private def fromGeo(point: (Int, Int)) = {
      val (x, y) = point
      (-y, x)
    }
 
    private def shiftX(n: Int)(point: (Int, Int)) = {
      val (x, y) = point
      (x + n - 1, y)
    }
 
    private def rotateNeg90(n: Int, point: (Int, Int)) = {
      (1 to 3)
        .foldLeft(point :: Nil) {
          case (p :: rest, _) =>
              p
                .pipe(toGeo)
                .pipe {
                  case (x, y) => (y, -x)
                }
                .pipe(shiftX(n))
                .pipe(fromGeo) :: p :: rest
           case _ => ???
        }
        .reverse
    }
}
 
Sửa lần cuối:
Tính mỗi phép ++ là đã quá phức tạp rồi, giờ đòi random access nữa thì thôi.
Bài này vectorization hơi khó vì phải access theo cột, nhớ là không có instruction nào load kiểu đó cả.
Nhưng data trên cùng 1 hàng, 1 cột thì khi rotate không bị data dependency nên chắc là vẫn tối ưu được.
Do cách viết của fen nên cái logic nó bị phân tán đi nhiều chỗ dẫn đến phức tạp thôi.

T viết lại, dùng random access iterator beat 100% đây fen, :p.
https://leetcode.com/submissions/detail/786986677/


Lưu ý: cái iterator t chỉ mới viết cho thằng rotate chạy được thôi, còn nếu muốn đảm bảo nó work với những algorithm khác trong std yêu cầu random access iterator thì cần viết thêm 1 vài operator nữa, cụ thể ở đây: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
 
Sửa lần cuối:
nay có tiến bộ. Beat 91% ae. Hàm clock để lấy viền lười chỉnh nên thêm 1 vòng for đi check :D
Python:
n = len(matrix)
  
        def clock(i, n):
        
            k = n-i
            res = [(i, j) for j in range(i, k)] + \
            [(j, k-1) for j in range(i, k)] + \
            [(k-1, j) for j in range(i, k)[::-1]] + \
            [(j, i) for j in range(i+1, k)[::-1]]
            lst = []
            a = -1
            for v in res:
                if a != v:
                    lst.append(v)
                a = v
            return lst

        def roll(vals):
            for i in range(int(len(vals)/4)):
                a = vals.pop()
                vals.insert(0, a)
                # print(vals)
            return vals

        #%%
        for i in range(0, int(n/2)):
            lst = clock(i, n)
            vals = [matrix[val[0]][val[1]] for val in lst]
            vals = roll(vals)
            for idx, val in enumerate(vals):
                matrix[lst[idx][0]][lst[idx][1]] = vals[idx]
 
Các bác cho em tham khảo các tip để giảm thiểu độ phức tạp của thuật toán với ạ. chung chung hoặc có doc cũng được ạ
 
Người mới xin kiến thức ạ.
Mn cho em hỏi nguồn tham khảo học Cấu Trúc Dữ Liệu Giải Thuật với ạ, vừa làm 1 bài chạy được mà quá thơì gian của nó. Mà ko biết học thuật toán ntn :))))
 
Các bác cho em tham khảo các tip để giảm thiểu độ phức tạp của thuật toán với ạ. chung chung hoặc có doc cũng được ạ
Người mới xin kiến thức ạ.
Mn cho em hỏi nguồn tham khảo học Cấu Trúc Dữ Liệu Giải Thuật với ạ, vừa làm 1 bài chạy được mà quá thơì gian của nó. Mà ko biết học thuật toán ntn :))))
fen vào mục discuss của bài đó mà đọc mấy bài upvote nhiều nhất ấy, thường sẽ có giải thích dễ hiểu. 1 số bài thì có mục solution trình bày các cách giải luôn
 
Đây là ý tưởng của tui:

Python:
def bfs(node):
            visited.append(node)
            x, y = node

            if (y + 1 < len(heights[0])
                    and heights[x][y] >= heights[x][y+1] and (x, y+1) not in visited):
                visited.append((x, y+1))
                bfs((x, y+1))

            if (y - 1 > -1
                    and heights[x][y] >= heights[x][y-1] and (x, y-1) not in visited):
                visited.append((x, y-1))
                bfs((x, y-1))

            if (x + 1 < len(heights)
                    and heights[x][y] >= heights[x+1][y] and (x+1, y) not in visited):
                visited.append((x+1, y))
                bfs((x+1, y))

            if (x - 1 > -1
                    and heights[x][y] >= heights[x-1][y] and (x-1, y) not in visited):
                visited.append((x-1, y))
                bfs((x-1, y))

            for i in len(heights):
                for j in len(heights[0]):
                    visited = []
                    bfs((i, j))


Đương nhiên là không giải được vì tui ko biết code như nào để có cái điều kiện dừng thỏa mãn 2 điều kiện. Nếu thỏa mãn 1 điều kiện thì dễ

Python:
def bfs(node):
    if condition:
        return True
    for nei in graph[node]:
        if bfs(nei):
            return True

Nhưng 2 condition thì code như nào nhỉ :sweat:


---

Tui xem giải rùi, nó làm kiểu tư duy ngược lại @@
Python:
class Solution:
    def pacificAtlantic(self, matrix: List[List[int]]) -> List[List[int]]:
        # Check if input is empty
        if not matrix or not matrix[0]:
            return []
        
        # Initialize variables, including sets used to keep track of visited cells
        num_rows, num_cols = len(matrix), len(matrix[0])
        pacific_reachable = set()
        atlantic_reachable = set()
        
        def dfs(row, col, reachable):
            # This cell is reachable, so mark it
            reachable.add((row, col))
            for (x, y) in [(1, 0), (0, 1), (-1, 0), (0, -1)]: # Check all 4 directions
                new_row, new_col = row + x, col + y
                # Check if the new cell is within bounds
                if new_row < 0 or new_row >= num_rows or new_col < 0 or new_col >= num_cols:
                    continue
                # Check that the new cell hasn't already been visited
                if (new_row, new_col) in reachable:
                    continue
                # Check that the new cell has a higher or equal height,
                # So that water can flow from the new cell to the old cell
                if matrix[new_row][new_col] < matrix[row][col]:
                    continue
                # If we've gotten this far, that means the new cell is reachable
                dfs(new_row, new_col, reachable)
        
        # Loop through each cell adjacent to the oceans and start a DFS
        for i in range(num_rows):
            dfs(i, 0, pacific_reachable)
            dfs(i, num_cols - 1, atlantic_reachable)
        for i in range(num_cols):
            dfs(0, i, pacific_reachable)
            dfs(num_rows - 1, i, atlantic_reachable)
        
        # Find all cells that can reach both oceans, and convert to list
        return list(pacific_reachable.intersection(atlantic_reachable))
 
Sửa lần cuối:
Quay lại khả năng thật, top 5% từ dưới lên :byebye:
Mã:
class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        import numpy as np
        matrix = np.array(heights)
        m, n = matrix.shape
        graph = {}
        for iy, ix in np.ndindex(m, n):
            val = matrix[iy, ix]
            paths = []
            if iy - 1 >= 0:
                if matrix[iy-1, ix] <= val:
                    paths.append((iy-1, ix))
            if ix - 1 >= 0:
                if matrix[iy, ix-1] <= val:
                    paths.append((iy, ix-1))
            if iy + 1 <= m - 1:
                if matrix[iy+1, ix] <= val:
                    paths.append((iy+1, ix))
            if ix + 1 <= n - 1:
                if matrix[iy, ix+1] <= val:
                    paths.append((iy, ix+1))
            # if len(paths) > 0:
            graph[(iy, ix)] = paths

        def reach_Pacific(visited: list) -> bool:
            for x in visited:
                if min(x) == 0:
                    return True
            return False

        def reach_Atlantic(visited: list, m: int, n: int) -> bool:
            for x in visited:
                if (x[1] == n-1) | (x[0] == m-1):
                    return True
            return False

        # node = (0, 3)
        def find_visited_a_node(node):
            stack = deque()
            visited = []
            stack.append(node)
            while len(stack) > 0:
                u = stack.pop()
                # print('pop', u)
                if u not in visited:
                    visited.append(u)
                    if reach_Pacific(visited) & reach_Atlantic(visited, m, n):
                        return True
                    for e in graph[u]:
                        if e in res:
                            return True
                        if e not in visited:
                            stack.append(e)
                            # print('add', e)
                            # visited.append(e)
            # print(visited)
            if reach_Pacific(visited) & reach_Atlantic(visited, m, n):
                return True
            else:
                return False

        res = []
        for node in graph:
            if find_visited_a_node(node):
                res.append(node)
                    
        return 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