1 2 3 4 5 6 7 8 9 10 * public class Node { * public int val; * public Node left; * public Node right; * public Node (int val=0 , Node left=null , Node right=null ) { * this .val = val; * this .left = left; * this .right = right; * } * }
非递归实现遍历 使用栈进行先序遍历 头 - 左 - 右 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public List<int > presort (Node head ){ List<int > result = new List<int >(); Stack<Node> nodes = new Stack<Node>(); if (head != null ) { nodes.Push(head); while (nodes.Count > 0 ) { Node cur = nodes.Pop(); result.Add(cur.value ); if (cur.right != null ) nodes.Push(cur.right); if (cur.left != null ) nodes.Push(cur.left); } } return result; }
使用栈进行先序遍历 头 - 右 - 左 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public List<int > presort (Node head ){ List<int > result = new List<int >(); Stack<Node> nodes = new Stack<Node>(); if (head != null ) { nodes.Push(head); while (nodes.Count > 0 ) { Node cur = nodes.Pop(); result.Add(cur.value ); if (cur.left != null ) nodes.Push(cur.left); if (cur.right != null ) nodes.Push(cur.right); } } return result; }
后序遍历 左 - 右 -头 将上述的头 - 右 - 左进行再次的入栈即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public List<int > oldsort (Node head ){ List<int > result = new List<int >(); Stack<Node> nodes = new Stack<Node>(); Stack<Node> res = new Stack<Node>(); if (head != null ) { nodes.Push(head); while (nodes.Count > 0 ) { Node cur = nodes.Pop(); res.Push(cur); if (cur.left != null ) nodes.Push(cur.left); if (cur.right != null ) nodes.Push(cur.right); } } while (res.Count > 0 ) result.Add(res.Pop()); return result; }
中序遍历 左 - 头 - 右 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public List<int > InSort (Node head ) { List<int > res = new List<int > Stack<Node> s = new Stack<Node>(); if (head!=null ){ while (s.Count>0 ||head!=null ){ if (head!=null ){ s.Push(head); head = head.left; } else { head = s.Pop(); res.Add(head.val); head = head.rigth; } } } return r }
层级遍历 层级遍历,也称为广度优先遍历(BFS),是一种按层次访问树或图中所有节点的遍历方法。它从根节点开始,逐层向下访问,同一层的节点从左到右依次访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public List<int > LayerSort (Node head ){ List<int > res = new List<int >(); if (head == null ) return res; Queue<Node> Q = new Queue<Node>(); Q.Enqueue(head); while (Q.Count > 0 ) { Node cur = Q.Dequeue(); res.Add(cur.e); if (cur.Left != null ) Q.Enqueue(cur.Left); if (cur.Right != null ) Q.Enqueue(cur.Right); } return res; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Solution { public IList<IList<int >> LevelOrder(TreeNode root) { var r = new List<IList<int >>(); if (root == null ) return r ; Queue<TreeNode> Q = new Queue<TreeNode>(); Q.Enqueue(root); while (Q.Count > 0 ){ List<int > res = new List<int >(); int length = Q.Count; for (int i = 0 ; i < length;i++){ TreeNode cur = Q.Dequeue(); res.Add(cur.val); if (cur.left != null ) Q.Enqueue(cur.left); if (cur.right != null ) Q.Enqueue(cur.right); } r.Add(res); } return r; } }
序列化2叉树(队列结构) 先序序列化和反序列化2叉树 对于二叉树:
序列化结果为:"1,2,#,#,3,4,#,#,5,#,#"
反序列化过程会重建原始二叉树结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public Queue<string > Serialize (TreeNode head ) { Queue<string > ans = new Queue<string >(); pres(head, ans); return ans; } public void pres (TreeNode head,Queue<string > ans ) { if (head == null ){ ans.Enqueue("#" ); return ; } else { ans.Enqueue(head.val.ToString()); pres(head.left, ans); pres(head.right, ans); } } public TreeNode DeSerialize (Queue<string > a ns ) { if (ans == null || ans.Count > 0 ) return null ; return preb(ans); } public TreeNode preb (Queue<string > ans ) { string value = ans.Dequeue(); if (value == null ) return null ; TreeNode head = new TreeNode(int .Parse(value )); head.left = preb(ans); head.right = preb(ans); return head; }
层级遍历序列化和反序列化 使用2个队列(结果队列,操作队列)进行操作,如果当前节点的孩子节点不为null,则将孩子节点进行序列化以及放入遍历队列和操作队列,如果为空,则放入结果队列进行序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public Queue<string > Seri (TreeNode head ){ Queue<string > ans = new Queue<string >(); if (head == null ) return ans; else { Queue<TreeNode> treeNodes = new Queue<TreeNode>(); ans.Enqueue(head.val.ToString()); treeNodes.Enqueue(head); while (treeNodes.Count > 0 ) { TreeNode cur = treeNodes.Dequeue(); if (cur.left != null ) { ans.Enqueue(cur.left.val.ToString()); treeNodes.Enqueue(cur.left); } else ans.Enqueue(null ); if (cur.right != null ) { ans.Enqueue(head.right.val.ToString()); treeNodes.Enqueue(cur.right); } else ans.Enqueue(null ); } } return ans; } public TreeNode Deri (Queue<string > ans ){ if (ans.Count <= 0 ||ans ==null ) return null ; TreeNode head = generate(ans.Dequeue()); Queue<TreeNode > treeNodes = new Queue<TreeNode>(); if (head != null ) treeNodes.Enqueue(head); TreeNode node = null ; while (treeNodes.Count > 0 ) { node = treeNodes.Dequeue(); node.left = generate(ans.Dequeue()); node.right = generate(ans.Dequeue()); if (node.left != null ) treeNodes.Enqueue(node.left); if (node.right != null ) treeNodes.Enqueue(node.right); } return head; } public TreeNode generate (string val ){ if (val == null ) return null ; return new TreeNode(int .Parse(val)); }
序列化2叉树(数组结构) 先序遍历序列化2叉树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public int [] Serialize (TreeNode head ) { if (head == null ) return new int [0 ]; List<int > res = new List<int >(); pres(head, ans); return ans.ToArray; } public void pres (TreeNode head,List<int > ans ) { if (head == null ){ ans.Add("#" ); return ; } else { ans.Add(head.val); pres(head.left, ans); pres(head.right, ans); } }
先序遍历反序列化2叉树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int index = 0 ;public TreeNode DeSerialize (int [] nums ) { if (nums == null || nums.Length == 0 ) return preb(ans); } public TreeNode preb (int [] nums ){ if (index >= nums.Length || nums[index] == null ) { index++; return null ; } TreeNode head =new TreeNode(nums[index]); index++; head.left = preb(nums); head.right = preb(nums); return head; }
层级遍历序列化2叉树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int [] Seri (TreeNode head ) { if (head == null ) return new int [0 ]; List<int > res = new List<int >(); Queue<TreeNode> queue = new Queue<TreeNode>(); queue.EnQueue(head); while (queue.Count > 0 ){ TreeNode cur = new TreeNode(); cur = queue.DeQueue(); if (cur != null ){ res.Add(cur.val); queue.EnQueue(cur.left); queue.EnQueue(cur.right); } else { res.Arr(null ); } } while (res.Count > 0 && res[res.Count - 1 ] == null ) result.RemoveAt(result.Count - 1 ); return res.ToArray; }
层级遍历反序列化2叉树 求2叉树最宽层的节点数 按照层级遍历的方法,设置2个变量,一个代表当前层的最后一个节点,一个代码下一层的最后一个节点,在当前层的情况下得到下一层的最后一个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public int R (TreeNode head ) { if (head == null ) return 0 ; Queue<TreeNode> treeNodes = new Queue<TreeNode>(); treeNodes.EnQueue(head); int max = 0 ; int curMax = 0 ; TreeNode curEnd = null ; TreeNode NextEnd = null ; while (treeNode.Count > 0 ){ TreeNode cur = treeNodes.Dequeue(); if (cur.left != null ){ NextEnd = cur.left; treeNodes.EnQueue(cur.left); } if (cur.right != null ){ NextEnd = cur.right; treeNodes.EnQueue(cur.rigth); } curMax++; if (cur == curEnd){ max = Math.Max(max,curMax); curMax = 0 ; curEnd = NextEnd; } } return max; }
判断一个树是否是完全2叉树 按照层级遍历的思想,一层一层遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public bool isCBT(TreeNode head){ if(head == null ) return true; Queue<TreeNode> queue = new Queue<TreeNode>(); queue.Enqueue(head); TreeNode L; TreeNode R; bool leaf = false; while (queue.Count > 0 ){ TreeNode cur = queue.Dequeue(); L = cur.left; R = cur.rigth; if(lead&&(L!=null ||R!=null ) || (L==null && R!=null ) ) return false; if(L!=null ) queue.Enqueue(L); if(R!=null ) queue.Enqueue(R); if(L == null || R == null ) leaf = true; } return true; }
递归法
完全2叉树
左子树满,右子树满,左子树的高度 = 右子树的高度
左子树是完全2叉树,右子树满,左子树的高度 = 右子树的高度 + 1(多一层)
左子树满,右子树满,左子树的高度 = 右子树的高度 + 1(多一层)
左子树满,右子树是完全2叉树,左子树的高度 = 右子树的高度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Info { public int height; public bool isFull; public bool isCBT; public Info (bool full,bool CBt,int h ) { height = h; isFull = full; isCBT = CBt; } } public bool M (TreeNode head ) { return process(head).isCBT; } public bool process (TreeNode head ) { if (head == null ) return new Info(true ,true ,0 ); Info left = process(head.left); Info right = process(head.right); int height = Math.Max(left.height,right.height) + 1 ; bool IsFull = left.isFull && right.isFull && left.height == right.height; bool IsCBT = false ; if (left.isFull && right.isFull && left.height == right.height) IsCBT = true ; else if (left.isCBT && right.isFull && left.height == right.height + 1 ) IsCBT = true ; else if (left.isFull && right.isFull && left.height == right.height + 1 ) IsCBT = true ; else if (left.isFull && right.isCBT && left.height == right.height) IsCBT = true ; return new Info(IsFull,IsCBT,height); }
二叉树类型
定义特点
示例
满二叉树
每一层的节点数都达到最大值
所有叶子节点都在同一层
完全二叉树
除最后一层外是满的,最后一层左连续
最后一层可以不满,但必须从左开始填
普通二叉树
无特殊结构限制
任意形式的树结构
判定一个2叉树是否是平衡2叉树 平衡2叉树
特性
平衡二叉树
普通二叉搜索树
平衡条件
每个节点的左右子树高度差 ≤1
无限制
最坏时间复杂度
O(log n)
O(n)(退化成链表)
适用场景
需要高效动态操作(查找、插入、删除)
数据基本静态或随机插入
使用递归进行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Info { public bool isBalance; public int height; public Info (bool IsB,int H ) { isBalance = IsB; height = H; } } public bool IsB (TreeNode head ) { return process(head).isBalance; } public Info process (TreeNode head ) { if (head == null ) return new Info(true ,0 ); Info left = process(head.left); Info right = process(head.right); int height = (Math.Max(left.height,right.height) + 1 ); bool IsBalance = true ; if (!left.isBalance) IsBalance = false ; if (!right.isBalance) IsBalance = false ; if (Math.Abs(left.height,right.height) > 1 ) IsBalance = false ; return new Info(IsBalance,height); }
判定一个2叉树是否是搜索2叉树 搜索2叉树
有序性(BST Property)
对于树中的 任意节点 :
其 左子树 上的所有节点的值 小于 该节点的值。
其 右子树 上的所有节点的值 大于 该节点的值。
(如果允许重复值,可以定义为左子树 ≤ 当前节点 ≤ 右子树)
递归定义
左子树和右子树也必须是 BST (即整个树的所有子树都满足BST
使用中序遍历2叉树,如果一直上升则证明是;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class Info { public int max; public int min; public bool isSelect; public Info (int M,int m,int isS ) { max =M; min = m; isSelect = isS; } } public bool IsS (TreeNode head ) { if (head == null ) return true ; return process(head).isSelect; } public Info process (TreeNode head ) { if (head == null ) return null ; Info left = process(head.left); Info right = process(head.right); int max = head.val; int min = head.val; if (left!=null ){ max = Math.Max(max,left.max); min = Math.min(min,left.min); } if (right!=null ){ max = Math.Max(max,right.max); min = Math.min(min,right.min); } bool IsSelect = true ; if (left!=null && !left.isSelect) IsSelect =false ; if (right!=null && !right.isSelect) IsSelect =false ; if (left.max >= head.val && left!=null ) IsSelect =false ; if (right.max <= head.val && right!=null ) IsSelect =false ; return new Info(max,min,IsSelect); }
求二叉树中两个节点之间的最大距离
基本情况(Base Case) :
如果 head == null,返回 Info(0, 0)(空树的高度和最大距离均为 0)。
递归计算左右子树的信息 :
left = process(head.left)
right = process(head.right)
计算当前树的高度 :
计算当前树的最大距离 :
p1 = left.maxDistance(左子树的最大距离)
p2 = right.maxDistance(右子树的最大距离)
p3 = left.height + right.height + 1(经过当前节点的最长路径)
maxDistance = max(p1, p2, p3)
(当前树的最大距离 = 左/右子树的最大距离,或经过当前节点的路径)
返回当前树的 Info :
return new Info(maxDistance, height)
最大距离有2种情况
经过head节点,如果经过head节点,最大值可能为2树的高度 + head节点
不经过head节点,也分2种情况
完全在左子树上:依旧判断是否经过左子树的节点,找maxDistance
完全在右子树上:依旧判断是否经过右子树的节点,找maxDistance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Info { public int maxDistance; public int height; public Info (int max,int H ) { maxDistance = max; height = H; } } public int R (TreeNode head ) { if (head == null ) return 0 ; return process(head).maxDistance; } public Info process (TreeNode head ) { if (head == null ) return new Info(0 ,0 ); Info left = process(head.left); Info right = process(head.right); int height = Math.Max(left.height,right.height) + 1 ; int p1 = left.maxDistance; int p2 = right.maxDistance; int p3 = left.height + right.height + 1 ; int maxDistance = Math.Max(Math.Max(p1,p2),p3); return new Info(maxDistance,height); }
判断一个2叉树是否是满2叉树
每个节点要么是叶子节点(无子节点),要么必须有两个子节点
不存在 只有一个子节点 的节点。
即所有非叶子节点的度(子节点数)必须为 2。
递归定义
节点数量与高度的关系
设树的高度为 h(从根到叶子的最长路径的边数),则:
叶子节点数 = 2^h
总节点数 = 2^(h +1)−1
例如:高度 h=2的满二叉树,叶子数 = 4,总节点数 = 7。
严格平衡
所有叶子节点都在同一层(最后一层)。
是 完全二叉树(Complete Binary Tree) 的特例(完全二叉树不一定是满二叉树)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Info { public int nodes; public int height; public Info (int n,int h ) { nodes = n; heigth = h; } } public bool Is (TreeNode head ) { if (head == null ) return true ; Info all = process(head); return (1 <<all.heigth) -1 ==all.nodes; } public Info process (TreeNode head ) { if (head == null ) return new Info(0 ,0 ); Info left = process(head.left); Info right = process(head.right); int height = Math.Max(left.height,right.height) + 1 ; int nodes = left.nodes + right.nodes + 1 ; return new Info(nodes,height); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class Info { public int max; public int min; public int allS; public bool maxBstSubtreeSize; public Info (int M,int m,int allS,int maxBstSubtreeSize ) { max =M; min = m; allS = allS; this .maxBstSubtreeSize = maxBstSubtreeSize; } } public bool IsS (TreeNode head ) { if (head == null ) return true ; return process(head).isSelect; } public Info process (TreeNode head ) { if (head == null ) return null ; Info left = process(head.left); Info right = process(head.right); int maxBstSubtreeSize; int max = head.val; int min = head.val; int allsize = 1 ; if (left!=null ){ max = Math.Max(max,left.max); min = Math.min(min,left.min); allsize += left.allS } if (right!=null ){ max = Math.Max(max,right.max); min = Math.min(min,right.min); allsize += right.allS } int p1 = -1 ; if (left!= null ) p1 = left.maxBstSubtreeSize int p2 = -1 ; if (right!= null ) p1 = right.maxBstSubtreeSize return new Info (max,min,allsize,maxBstSubtreeSize ) ;}
给定一个2叉树的头节点head,和另外2个节点a,b。返回a,b的最低公共祖先
情况
条件
处理方式
1. 当前节点为空
head == null
返回 Info(false, false, null)
2. 递归左子树和右子树
-
获取 left和 right的 Info
3. 判断当前节点是否是 a或 b
head == a或 head == b
更新 findA或 findB
4. 判断是否找到 LCA
left.ans != null
返回 left.ans
right.ans != null
返回 right.ans
findA && findB(当前节点是 LCA)
返回 head
5. 返回当前节点的信息
-
返回 Info(findA, findB, ans)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Info { public bool findA; public bool findB; public TreeNode ans; public Info (bool fA,bool fB.TreeNode an ) { findA = fA; findB = fB; ans = an; } } public Info process (TreeNode head,TreeNode a,TreeNode b ) { if (head == null ) return new Info(false ,false ,null ); Info left = process(head.left); Info right = process(head.rigth); bool findA = (head == a) || left.findA || rigth.findA; bool findA = (head == b) || left.findB || rigth.findB; TreeNode ans = null ; if (left.ans != null ) ans = left.ans; else if (right.ans != null ) ans = rigth.ans; else { if (findA && findB) ans = head; } return new Info(findA,finB,ans); }
公司员工派对的最大快乐值问题 公司举办派对,邀请员工参加。每个员工都有一个快乐值 val,并且每个员工可能有多个直接下属(即多叉树结构)。派对的规则是:
如果某个员工参加派对,那么他的所有直接下属都不能参加。
如果某个员工不参加派对,那么他的直接下属可以选择参加或不参加。
目标是让整个派对的快乐值总和最大。
树形DP(动态规划) :由于员工关系是多叉树结构,我们需要递归处理每个员工及其下属。
两种状态 :
当前员工参加(yes) :则所有直接下属都不能参加,快乐值 = 当前员工.val + 所有下属不参加的总和。
当前员工不参加(no) :则每个下属可以自由选择参加或不参加,快乐值 = 所有下属的 max(参加, 不参加) 的总和
递归逻辑
基本情况(Base Case) :
如果当前员工 head是 null(即没有员工),返回 Info(0, 0)(不参加和参加的值都是 0)。
递归处理所有下属 :
遍历每个下属,递归计算其 Info(即下属参加或不参加的最大快乐值)。
计算当前员工的 yes和 no :
yes(当前员工参加) :
快乐值 = head.val + 所有下属的no`(因为下属不能参加)。
no(当前员工不参加) :
快乐值 = 所有下属的 max(yes, no)之和(因为下属可以自由选择)。
返回当前员工的 Info :
最终返回 Info(no, yes),表示当前员工不参加或参加时的最大快乐值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Employee { public int val; public List<Employee> nexts = new List<Employee>(); public Employee () { } } public class Info { public int no; public int yes; public Info (int n, int y ) { no = n; yes = y; } } public int maxHappy (Employee head ) { Info all = process(head); return Math.Max(all.no,all.yes); } public Info process (Employee head ) { if (head == null ) return new Info(0 ,0 ); int yes = head.val; int no = 0 ; foreach (var next in head.nexts){ Info nextInfo = process(next); no += Math.Max(nextInfo.yes,nextInfo.no); yes += nextInfo.no; } return new Info(no,yes); }