Skip to content

Commit 89994d3

Browse files
Obsession-kaiminibear2333
Obsession-kai
authored andcommitted
update day7/31
1 parent 328d404 commit 89994d3

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// 题目链接:https://leetcode.cn/problems/er-cha-shu-de-jing-xiang-lcof/?envType=study-plan&id=lcof
2+
// day7/31
3+
// 第 7 天主题为:搜索与回溯算法(简单),与前一天主题相同
4+
// 包含三道题目:
5+
// 剑指offer26.树的子结构
6+
// 剑指offer27.二叉树的镜像
7+
// 剑指offer28.对称的二叉树
8+
9+
10+
package main
11+
12+
13+
// Definition for a binary tree node.
14+
type TreeNode struct {
15+
Val int
16+
Left *TreeNode
17+
Right *TreeNode
18+
}
19+
20+
// 解题思路:观察例子可以发现,镜像 就是 每个节点的左右子树进行翻转。所以,两步完成本题:
21+
//1. 遍历二叉树保存所有节点(前中后序遍历都可以),用节点的指针类型切片保存;
22+
//2. 遍历切片,交换其左右子节点 node.Left,node.Right = node.Right,node.Left
23+
func mirrorTree(root *TreeNode) *TreeNode {
24+
nodes := []*TreeNode{}
25+
var preorder func(root *TreeNode)
26+
preorder = func(root *TreeNode){
27+
if root == nil{
28+
return
29+
}
30+
nodes = append(nodes,root)
31+
if root.Left != nil{
32+
preorder(root.Left)
33+
}
34+
if root.Right != nil{
35+
preorder(root.Right)
36+
}
37+
}
38+
preorder(root)
39+
for _,node := range nodes{
40+
node.Left,node.Right = node.Right,node.Left
41+
}
42+
return root
43+
}
44+
45+
// 上面这种解法的时间空间复杂度均为 O(n)
46+
// 我们也可以在遍历的过程中交换节点的左右子节点,省去 nodes 切片占用的内存空间,但这本质上并不会降低时间和空间复杂度,
47+
// 因为我们在对二叉树进行遍历的过程中,调用了系统栈,系统需要使用 O(n) 大小的栈空间。
48+
// 前序遍历的过程中交换左右子节点
49+
// 为避免函数命名冲突,次函数名添加后缀 “_2”
50+
func mirrorTree_2(root *TreeNode) *TreeNode {
51+
var preorder func(root *TreeNode)
52+
preorder = func(root *TreeNode){
53+
if root == nil{
54+
return
55+
}
56+
root.Left,root.Right = root.Right,root.Left
57+
if root.Left != nil{
58+
preorder(root.Left)
59+
}
60+
if root.Right != nil{
61+
preorder(root.Right)
62+
}
63+
}
64+
preorder(root)
65+
return root
66+
}
67+
68+
69+
// 另外,在LeetCode题解区我还看到了有人用层序遍历的方式解题,本质上和上面两种思路是一样的,就是选择了另一种遍历二叉树的方式,
70+
// 交换左右子节点的核心操作无任何变化,代码开头的判断 root 节点是否为空不可省略,因为 q 初始化默认加入root节点,若该节点为空,后续代码会出错。
71+
// 为避免函数命名冲突,次函数名添加后缀 “_3”
72+
func mirrorTree_3(root *TreeNode) *TreeNode {
73+
if root == nil{
74+
return root
75+
}
76+
q := []*TreeNode{root}
77+
for len(q) != 0{
78+
node := q[0]
79+
node.Left,node.Right = node.Right,node.Left
80+
if node.Left != nil{
81+
q = append(q,node.Left)
82+
}
83+
if node.Right != nil{
84+
q = append(q,node.Right)
85+
}
86+
q = q[1:]
87+
}
88+
return root
89+
}
90+
91+
92+
// 二叉树的定义就是递归的,所以做二叉树的题目一定要有递归的想法,有思路后开始着手写代码,不要在脑海中模拟太多层的递归,否则很容易绕进去。
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// 题目链接:https://leetcode.cn/problems/dui-cheng-de-er-cha-shu-lcof/?envType=study-plan&id=lcof
2+
3+
package main
4+
5+
6+
// 解题思路:如果二叉树只有一个根节点,那其必然是对称的,本题主要是判断节点的左右子树是否对称。
7+
//
8+
// 判断左右子树是否对称,若两子树根节点均为空,说明对称,若某子树为空而另一颗子树不为空,说明不对称,两子树根节点值不相等的话也必定不对称吗,否则,对称,之后递归地判断
9+
//- 左子树的左子树 与 右子树的右子树 是否对称
10+
//- 左子树的右子树 与 右子树的左子树 是否对称
11+
// 两者均对称时,说明该左右子树对称;否则,不对称。
12+
13+
func isSymmetric(root *TreeNode) bool {
14+
// 根节点为空,对称
15+
if root == nil{
16+
return true
17+
}
18+
var sym func(x,y *TreeNode) bool
19+
// 递归判断左右子树是否对称
20+
sym = func(x,y *TreeNode) bool {
21+
// 两者均为空说明对称
22+
if x == nil && y == nil{
23+
return true
24+
// 其中某一子树为空,而另一子树不为空,不对称
25+
} else if x == nil || y == nil{
26+
return false
27+
}
28+
// 两者根节点值不相等时也不对称
29+
if x.Val != y.Val{
30+
return false
31+
}
32+
// 递归判断x的左右子树 与 y 的右左子树是否对称
33+
return sym(x.Left,y.Right) && sym(x.Right,y.Left)
34+
}
35+
return sym(root.Left,root.Right)
36+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// 题目链接:https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof/
2+
3+
package main
4+
5+
6+
// 此题解参考自:https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof/solution/mian-shi-ti-26-shu-de-zi-jie-gou-xian-xu-bian-li-p/
7+
// 若树 B 是 A 的子结构,则 A 子结构的根节点可能是 A 中任意一个节点,我们判断 B 是否为 A 的子结构,就需要遍历 A 的所有节点,
8+
// 然后判断是否有某节点 以 该节点为 根节点的子树包含(有点绕,其实就是,若 A 与 B 根节点相同, B 是否为 A 的子结构,
9+
// 但当 B 为空的时候,B 必为 A 的子结构)。
10+
// 名词规定:树 A 的根节点记作 节点 A,树 B 的根节点称为 节点 B 。
11+
// recur(A, B)函数,用于判断树 A 中以 A为根节点的子结构是否包含树 B
12+
//终止条件:
13+
//- 当节点 B 为空:说明树 B 已匹配完成(越过叶子节点),因此返回 true ;
14+
//- 当节点 A 为空:说明已经越过树 A 叶子节点(而 B 节点非空),即匹配失败,返回 false ;
15+
//- 当节点 A 和 B 的值不同:说明匹配失败,返回 false ;
16+
//返回值:
17+
//- 判断 A 和 B 的左子节点是否相等,即 recur(A.left, B.left) ;
18+
//- 判断 A 和 B 的右子节点是否相等,即 recur(A.right, B.right) ;
19+
//两者取逻辑与后返回。
20+
// 特例处理:当 树 A 为空 或 树 B 为空时,直接返回 false(对应题目中的约定:空树不是任意一个树的子结构) 。
21+
// 之后我们对 A 作遍历(前中后序都可以),对其中每个节点与 B 进行 recur 判断,若存在 true 结果,返回最终的 true;若全为 false,说明 B 不是 A 的子结构,返回 false。
22+
func isSubStructure(A *TreeNode, B *TreeNode) bool {
23+
if A == nil || B == nil{
24+
return false
25+
}
26+
nodes := []*TreeNode{}
27+
// 对树的遍历,这里我采用前序遍历
28+
// 使用 中序遍历、后序遍历 or 层序遍历都是可以的
29+
var perorder func(root *TreeNode)
30+
perorder = func(root *TreeNode){
31+
if root == nil{
32+
return
33+
}
34+
nodes = append(nodes,root)
35+
if root.Left != nil{
36+
perorder(root.Left)
37+
}
38+
if root.Right != nil{
39+
perorder(root.Right)
40+
}
41+
}
42+
perorder(A)
43+
// 判断B是否为以为根节点的子树的在子结构
44+
var sub func(A,B *TreeNode) bool
45+
sub = func(A,B *TreeNode) bool{
46+
if B == nil{
47+
return true
48+
}
49+
if A == nil || A.Val != B.Val {
50+
return false
51+
}
52+
return sub(A.Left,B.Left) && sub(A.Right,B.Right)
53+
}
54+
for _,node := range nodes{
55+
if sub(node,B){
56+
return true
57+
}
58+
}
59+
return false
60+
}

0 commit comments

Comments
 (0)