Symmetric Tree
Bạn được cung cấp một cây nhị phân lưu trong mảng tree theo thứ tự từng tầng. Nút gốc nằm ở chỉ số 0, các nút con của nút ở chỉ số i nằm tại 2*i+1 (bên trái) và 2*i+2 (bên phải), -1 đánh dấu một vị trí trống và mảng có thể kết thúc bằng các phần tử -1 thừa. Trả về true nếu cây là ảnh phản chiếu của chính nó qua đường thẳng đứng đi qua nút gốc, và false trong trường hợp còn lại. Cả hình dạng lẫn giá trị đều phải khớp.
Hàm
- treeinteger-array
- cây nhị phân theo thứ tự từng mức, dùng -1 cho vị trí trống
- Trả vềboolean
- đúng nếu cây đối xứng với chính nó, sai nếu không
Ràng buộc
1 ≤ tree.length ≤ 32767- Mỗi
tree[i]là-1hoặc một giá trị thỏa mãn0 ≤ tree[i] ≤ 1000. tree[0]không bao giờ là-1, vì vậy cây có ít nhất một nút.- Mảng có thể kết thúc bằng các phần tử
-1dư ra sau nút cuối cùng. - Cả hai nút con của một vị trí trống cũng đều trống, và độ sâu tối đa là
14.
Ví dụ
- Đầu vào
- tree = [1, 2, 2, 3, 4, 4, 3]
- Đầu ra
- true
- Giải thích
- Gập cây lại theo trục giữa. Hai số
2ở chỉ số1và2gặp nhau, hai số3ở ngoài tại chỉ số3và6gặp nhau, còn hai số4ở trong tại4và5gặp nhau.
- Đầu vào
- tree = [1, 2, 2, -1, 3, -1, 3]
- Đầu ra
- false
- Giải thích
- Cả hai số
3đều nằm bên phải nút cha của chúng. Trong ảnh phản chiếu, nút con bên phải của2bên trái (chỉ số4) đối diện với nút con bên trái của2bên phải (chỉ số5), và chỉ số5đang trống.
- Đầu vào
- tree = [4, 6, 6, 5, -1, -1, 9]
- Đầu ra
- false
- Giải thích
- Hình dạng là ảnh phản chiếu: chỉ số
3đối diện với chỉ số6và cả hai đều chứa một nút. Giá trị của chúng khác nhau,5so với9, nên cây không đối xứng.
Câu hỏi mở rộng
Nếu hình dạng phản chiếu qua chính nó nhưng một số giá trị không khớp, bạn cần thay đổi ít nhất bao nhiêu giá trị nút để cây đối xứng?
Gợi ý
Mở từng gợi ý một. Mỗi gợi ý tiết lộ thêm một chút.
Nút nào phải khớp với nút con bên trái của nút gốc? Và nút con bên trái của nút đó phải khớp với nút nào?
Hãy so sánh từng cặp vị trí. Hai vị trí đối xứng với nhau khi cả hai đều trống, hoặc khi cả hai chứa cùng một giá trị và các nút con đổi chéo cho nhau: nút con bên trái của vị trí này đối xứng với nút con bên phải của vị trí kia, và nút con bên phải của vị trí này đối xứng với nút con bên trái của vị trí kia.
Duy trì một ngăn xếp các cặp chỉ số, bắt đầu với
(1, 2). Lấy một cặp ra: bỏ qua nếu cả hai vị trí đều trống, thất bại nếu chỉ một vị trí trống hoặc các giá trị khác nhau, nếu không thì thêm(2*a+1, 2*b+2)và(2*a+2, 2*b+1)vào ngăn xếp.
Lời giải
Mỗi nút có một nút tương ứng ở vị trí đối xứng qua nút gốc, và nút tương ứng với nút con bên trái là nút con bên phải. Cùng lúc, duyệt hai nửa theo hai hướng ngược nhau, so sánh hình dạng và giá trị của từng cặp, rồi dừng ngay ở cặp đầu tiên không khớp.
So sánh từng cấp độ với cấp độ đảo ngược
Ý tưởng
Một ảnh đối xứng qua gương đọc giống nhau từ trái sang phải và từ phải sang trái, vì vậy mỗi tầng, khi ghi rõ cả những vị trí trống, phải đọc giống nhau theo cả hai hướng. Trong ví dụ thứ hai, tầng dưới cùng đọc là -1 3 -1 3, khi đảo ngược thành 3 -1 3 -1, nên đáp án là false.
Các vị trí trống phải được giữ lại trong hàng: nếu không có chúng, tầng đó sẽ đọc là 3 3 và vượt qua bài kiểm tra. Các nút con của vị trí trống cũng là vị trí trống, vì vậy chỉ các nút thực mới thêm phần tử. Mỗi nút được duyệt một lần, tốn thời gian O(n), và mỗi lần chỉ lưu một tầng trong bộ nhớ, tốn O(w) với w là độ rộng của tầng rộng nhất.
Thuật toán
- Bắt đầu với một danh sách chứa chỉ số gốc
0. - Với mỗi chỉ số trong danh sách, từ trái sang phải, ghi lại vị trí của cả hai nút con: giá trị của nút con nếu có,
-1nếu không có. Thu thập các nút con có thật cho cấp tiếp theo. - Nếu hàng đó khác với hàng đảo ngược, trả về
false. - Lặp lại ở cấp tiếp theo cho đến khi danh sách trống, rồi trả về
true.
def isSymmetric(tree):
n = len(tree)
level = [0] # the real nodes of one level, left to right
while level:
row = [] # the child spots under this level, -1 for an empty one
next_level = []
for i in level:
for child in (2 * i + 1, 2 * i + 2):
if child < n and tree[child] != -1:
row.append(tree[child])
next_level.append(child)
else:
row.append(-1)
if row != row[::-1]:
return False
level = next_level
return TrueĐệ quy trên các cặp đối xứng
Ý tưởng
So sánh cây con trái của nút gốc, ở chỉ số 1, với cây con phải của nút gốc, ở chỉ số 2. Hai vị trí đối xứng với nhau khi cả hai đều trống, hoặc khi cả hai chứa cùng một giá trị và các nút con của chúng bắt chéo nhau: nút con trái của vị trí này đối xứng với nút con phải của vị trí kia (cặp ngoài), còn nút con phải của vị trí này đối xứng với nút con trái của vị trí kia (cặp trong). Đó là định nghĩa của ảnh gương được áp dụng xuống thêm một mức, nên nếu mọi cặp đều khớp thì toàn bộ cây cũng đối xứng.
Mỗi nút thực thuộc nhiều nhất một cặp, nên thời gian chạy là O(n); ngăn xếp lời gọi có độ sâu bằng chiều cao của cây, O(h).
Thuật toán
- Viết
mirrors(a, b). Một vị trí trống nếu chỉ số vượt quá cuối mảng hoặc chứa-1. Nếu cả hai vị trí đều trống, trả vềtrue; nếu chỉ một vị trí trống, trả vềfalse. - Nếu
tree[a]vàtree[b]khác nhau, trả vềfalse. - Nếu không, trả về
mirrors(2*a+1, 2*b+2)vàmirrors(2*a+2, 2*b+1). - Trả về
mirrors(1, 2). Cây có gốc nhưng không có nút con sẽ có hai vị trí trống, nên kết quả làtrue.
def isSymmetric(tree):
n = len(tree)
def mirrors(a, b):
# Spots a and b must hold the same value, or both be empty.
empty_a = a >= n or tree[a] == -1
empty_b = b >= n or tree[b] == -1
if empty_a or empty_b:
return empty_a and empty_b
return (tree[a] == tree[b]
and mirrors(2 * a + 1, 2 * b + 2) # outer pair
and mirrors(2 * a + 2, 2 * b + 1)) # inner pair
return mirrors(1, 2)Ngăn xếp tường minh gồm các cặp đối xứng
Ý tưởng
Đệ quy chỉ ghi nhớ các cặp còn chờ được kiểm tra, vì vậy hãy tự lưu chúng trên một ngăn xếp. Thứ tự kiểm tra không quan trọng, vì cây chỉ đối xứng nếu mọi cặp đều khớp: ngăn xếp tạo ra thứ tự duyệt theo chiều sâu, còn hàng đợi tạo ra thứ tự duyệt theo từng mức. Ví dụ thứ ba dừng ở cặp không khớp đầu tiên, (3, 6), chứa 5 và 9.
Mỗi nút thực thuộc tối đa một cặp, vì vậy độ phức tạp thời gian là O(n). Ngăn xếp chứa khoảng một cặp đang chờ cho mỗi mức trên đường đi hiện tại, nên độ phức tạp không gian là O(h).
Thuật toán
- Đẩy cặp
(1, 2)vào ngăn xếp. - Lấy một cặp
(a, b)ra và kiểm tra như hàmmirrors: bỏ qua nếu cả hai vị trí đều trống, trả vềfalsenếu chỉ một vị trí trống hoặc các giá trị khác nhau. - Nếu không, đẩy
(2*a+1, 2*b+2)và(2*a+2, 2*b+1)vào ngăn xếp. - Khi ngăn xếp trống, trả về
true.
def isSymmetric(tree):
n = len(tree)
stack = [(1, 2)] # pairs of spots that must mirror each other
while stack:
a, b = stack.pop()
empty_a = a >= n or tree[a] == -1
empty_b = b >= n or tree[b] == -1
if empty_a and empty_b:
continue
if empty_a or empty_b or tree[a] != tree[b]:
return False
stack.append((2 * a + 1, 2 * b + 2)) # outer pair
stack.append((2 * a + 2, 2 * b + 1)) # inner pair
return True
Lỗi thường gặp và trường hợp biên
- Kiểm tra riêng từng cây con. Trong
[1, 2, 2, 3, 4, 4, 3], cây con bên trái2, 3, 4không đối xứng, nhưng cả cây lại đối xứng: nó phải phản chiếu cây con bên phải. - Ghép các nút con sai cách. Các cặp là
(2*a+1, 2*b+2)và(2*a+2, 2*b+1), không bao giờ là(2*a+1, 2*b+1). - Chỉ so sánh giá trị. Bỏ các vị trí trống khỏi
[1, 2, 2, -1, 3, -1, 3]thì các giá trị ở mỗi tầng đều giống nhau theo cả hai chiều, nhưng cây không đối xứng. - Đọc vượt quá cuối. Chỉ số vượt quá cuối là một vị trí trống; cây chỉ có một nút hoàn toàn không có chỉ số
1hoặc2. - Trong Lua và R, mảng bắt đầu từ 1: hãy giữ chỉ số nút bắt đầu từ 0 để dùng phép tính
2*i+1và đọctree[i + 1].
Câu hỏi thường gặp3
Độ phức tạp thời gian của cây đối xứng là bao nhiêu?
Mỗi nút thực được so sánh một lần, trong một cặp đối xứng, nên thời gian là O(n). Bộ nhớ bổ sung là O(h) với phiên bản đệ quy và phiên bản dùng ngăn xếp, còn với phiên bản duyệt từng tầng là O(w), trong đó w là độ rộng lớn nhất của một tầng.
Sự khác nhau giữa cây đối xứng và hai cây giống hệt nhau là gì?
Hai cây giống hệt nhau khi bạn so sánh nhánh trái với nhánh trái và nhánh phải với nhánh phải. Một cây đối xứng khi cây con bên trái là hình ảnh phản chiếu của cây con bên phải, vì vậy phép so sánh sẽ đổi chéo. Cùng một phép kiểm tra từng cặp sẽ giải quyết cả hai, chỉ cần đổi chỗ các cặp nút con.
Cây chỉ có một nút có đối xứng không?
Đúng vậy. Hai vị trí con trống của nó đối xứng nhau. Một nút gốc có đúng một nút con thì không bao giờ đối xứng, vì nút con đó đối diện với một vị trí trống.
Bài tập tương tự
Các bài tập dùng cùng ý tưởng. Giải hai hoặc ba bài là bạn sẽ nhớ được mẫu này.
def isSymmetric(tree):
# Viết code ở đâyKiểm thử #1
Kiểm thử #2
Kiểm thử #3
Đối số
tree = [1, 2, 2, 3, 4, 4, 3]
Đầu ra mong đợi
true