二叉樹常見面試題(Go語言實現)
開心一刻
語文詩歌題三大基本方法:詩人被貶法,詩人開心法,詩人思鄉法。
數學做題三大基本方法:肉眼觀察法,暴力代值法,顯然成立法。
英語閱讀題三大基本方法:硬幣投擲法,筆尖墜落指向法,投骰法。
物理做題三大基本方法:直接代公式法,保守少選法,一看就不對法。
政治大題三大基本方法:照搬材料原文法,照搬選擇題幹法,照搬選擇選項法。
歷史史論題三大基本方法:他說的有理法,他說的片面法,他說的扯淡法。
地理做題三大基本方法:看地圖法,畫地圖法,創造地圖法。
最近面試被問到二叉樹的相關問題, 有的沒有回答好, 趁此機會記錄一些網上常見的面試題目, 做些總結。
二叉樹的結構
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
求二叉樹的最大深度
給定一個二叉樹,找出其最大深度。
二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。
說明: 葉子節點是指沒有子節點的節點。
演算法思路:節點為空時,返回節點深度為0;遞迴計算左右子樹的深度,然後用較大值加上父節點的深度1;然後返回,最終返回到根節點時即可求得最大深度。
func maxDepth(root *TreeNode) int { if root == nil { return 0 } var left = maxDepth(root.Left) var right = maxDepth(root.Right) if left > right { return left + 1 } return right + 1 }
求二叉樹的最小深度
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
說明:葉子節點是指沒有子節點的節點。
演算法思路:根節點需要特殊判斷,為空時返回深度為0;對於葉子節點,不計算葉子節點的左右子樹深度,而是直接返回深度為1;對於只有左子樹的節點,左子樹深度標記為不可達,只有右子樹的節點同理;然後計算左右子樹的最小深度,用最小深度加上父節點的深度1
func minDepth(root *TreeNode) int { if root == nil { return 0 } return getMin(root) } func getMin(root *TreeNode) int { if root == nil { return int(^uint(0) >> 1) } if root.Left == nil && root.Right == nil { return 1 } return Min(getMin(root.Left), getMin(root.Right)) + 1 } func Min(x, y int) int { if x > y { return y } return x }
求二叉樹中節點的個數
演算法思路:遞迴計算左右子樹節點個數後,返回左右子樹節點個數和父節點個數的和即可
func numOfTreeNode(root *TreeNode) int {
if root == nil {
return 0
}
var left = numOfTreeNode(root.Left)
var right = numOfTreeNode(root.Right)
return left + right + 1
}
求二叉樹中葉子節點的個數
演算法思路:葉子節點需要單獨判斷,並返回結果1;然後遞迴計算左子樹葉子節點數量和右子樹葉子節點數量,然後相加並返回
func numOfNoChildNode(root *TreeNode) int {
if root == nil {
return 0
}
if root.Left == nil && root.Right == nil {
return 1
}
var left = numOfTreeNode(root.Left)
var right = numOfTreeNode(root.Right)
return left + right
}
判斷二叉樹是否是平衡二叉樹
輸入一棵二叉樹的根節點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意節點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。
演算法思路:判斷是否是平衡二叉樹屬於求樹的最大深度的變種。當判斷某一節點的左右子樹的深度之差的絕對值大於1時,那麼整棵二叉樹就不是平衡二叉樹了,所以在此時可以返回一個特殊值來標記(此處用的-1,因為求二叉樹深度不可能出現負值),那麼最終的結果也會是這個特殊值,根據這個特殊值就可以判斷這個二叉樹是否是一個平衡二叉樹。
func isBalanced(root *TreeNode) bool {
if root == nil {
return true
}
return getDepth(root) != -1
}
func getDepth(root *TreeNode) int {
if root == nil {
return 0
}
var left = getDepth(root.Left)
var right = getDepth(root.Right)
if left == -1 || right == -1 || Abs(left, right) > 1 {
return -1
}
if left > right {
return left + 1
} else {
return right + 1
}
}
func Abs(x, y int) int {
if x > y {
return x - y
} else {
return y - x
}
}
求二叉樹中第K層節點的個數
與求葉子節點的數量相似, 只不過判斷葉子節點的時候修改成判斷當前k是否為1
func numOfKLevelNode(root *TreeNode, k int) int {
if root == nil {
return 0
}
if k == 1 {
return 1
}
var left = numOfKLevelNode(root.Left, k - 1)
var right = numOfKLevelNode(root.Right, k - 1)
return left + right
}
判斷二叉樹是否是完全二叉樹
若設二叉樹的深度為 h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。(注:第 h 層可能包含 1~2^h個節點。)
根據完全二叉樹的定義, 使用層次遍歷即可解決.
func isCompleteTree(root *TreeNode) bool {
if root == nil {
return true
}
var queue = []*TreeNode{root}
var num = 1
for i := 0; len(queue) != 0; i++ {
var q []*TreeNode
var flag bool
for j := 0; j < len(queue); j++ {
node := queue[j]
if flag && node.Left != nil {
return false
}
if node.Left != nil {
q = append(q, node.Left)
} else {
flag = true
}
if flag && node.Right != nil {
return false
}
if node.Right != nil {
q = append(q, node.Right)
} else {
flag = true
}
}
if len(q) != 0 {
if len(queue) != num {
return false
}
num *= 2
}
queue = q
}
return true
}
兩個二叉樹是否完全相同
func isSameTree(p *TreeNode, q *TreeNode) bool {
if p == nil && q == nil {
return true
}
if p == nil || q == nil || p.Val != q.Val {
return false
}
return isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)
}
兩個二叉樹是否互為映象
func isMirror(p *TreeNode, q *TreeNode) bool {
if p == nil && q == nil {
return true
}
if p == nil || q == nil || p.Val != q.Val {
return false
}
return isMirror(p.Left, q.Right) && isSameTree(p.Right, q.Left)
}
翻轉二叉樹or映象二叉樹
func mirrorTree(root *TreeNode) *TreeNode {
var dfs func(root *TreeNode)
dfs = func(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
dfs(root.Right)
root.Left, root.Right = root.Right, root.Left
}
dfs(root)
return root
}
二叉樹的最近公共祖先
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil {
return nil
}
if root.Val == p.Val || root.Val == q.Val {
return root
}
var left = lowestCommonAncestor(root.Left, p, q)
var right = lowestCommonAncestor(root.Right, p, q)
if left != nil && right != nil {
return root
}
if left == nil {
return right
}
return left
}
二叉樹的前序遍歷
func preorderTraversal(root *TreeNode) []int {
var ret []int
var preorder func(root *TreeNode)
preorder = func(root *TreeNode) {
if root == nil {
return
}
ret = append(ret, root.Val)
preorder(root.Left)
preorder(root.Right)
}
preorder(root)
return ret
}
func preorderTraversal(root *TreeNode) []int {
if root == nil {
return nil
}
var ret []int
var stack = []*TreeNode{root}
for len(stack) != 0 {
node := stack[len(stack) - 1]
stack = stack[: len(stack) - 1]
ret = append(ret, node.Val)
if node.Right != nil {
stack = append(stack, node.Right)
}
if node.Left != nil {
stack = append(stack, node.Left)
}
}
return ret
}
二叉樹的中序遍歷
func inorderTraversal(root *TreeNode) []int {
var ret []int
var inorder func(root *TreeNode)
inorder = func(root *TreeNode) {
if root == nil {
return
}
inorder(root.Left)
ret = append(ret, root.Val)
inorder(root.Right)
}
inorder(root)
return ret
}
func inorderTraversal(root *TreeNode) []int {
var (
ret []int
stack []*TreeNode
cur *TreeNode
)
cur = root
for len(stack) != 0 || cur != nil {
for cur != nil {
stack = append(stack, cur)
cur = cur.Left
}
node := stack[len(stack) - 1]
stack = stack[: len(stack) - 1]
ret = append(ret, node.Val)
cur = node.Right
}
return ret
}
二叉樹的後序遍歷
func postorderTraversal(root *TreeNode) []int {
var ret []int
var postorder func(root *TreeNode)
postorder = func(root *TreeNode) {
if root == nil {
return
}
postorder(root.Left)
postorder(root.Right)
ret = append(ret, root.Val)
}
postorder(root)
return ret
}
func postorderTraversal(root *TreeNode) []int {
var (
ret []int
stack1 []*TreeNode
)
if root == nil {
return nil
}
stack1 = append(stack1, root)
for len(stack1) != 0 {
node := stack1[len(stack1) - 1]
stack1 = stack1[: len(stack1) - 1]
ret = append(ret, node.Val)
if node.Left != nil {
stack1 = append(stack1, node.Left)
}
if node.Right != nil {
stack1 = append(stack1, node.Right)
}
}
for i, j := 0, len(ret) - 1; i < j; i, j = i + 1, j - 1 {
ret[i], ret[j] = ret[j], ret[i]
}
return ret
}
二叉樹中路徑和為k的路徑
func pathSum(root *TreeNode, targetSum int) [][]int {
var (
ret [][]int
path []int
)
var dfs func(root *TreeNode, target int)
dfs = func(root *TreeNode, target int) {
if root == nil {
return
}
if root.Left == nil && root.Right == nil {
if root.Val == target {
tmp := append(path, target)
ret = append(ret, append([]int(nil), tmp...))
}
return
}
path = append(path, root.Val)
dfs(root.Left, target - root.Val)
dfs(root.Right, target - root.Val)
path = path[: len(path) - 1]
}
dfs(root, targetSum)
return ret
}
二叉樹層次遍歷
func levelOrder(root *TreeNode) [][]int {
if root == nil {
return nil
}
var queue = []*TreeNode{root}
var ret [][]int
for i := 0; len(queue) != 0; i++ {
var q []*TreeNode
ret = append(ret, []int(nil))
for j := 0; j < len(queue); j++ {
node := queue[j]
ret[i] = append(ret[i], node.Val)
if node.Left != nil {
q = append(q, node.Left)
}
if node.Right != nil {
q = append(q, node.Right)
}
}
queue = q
}
return ret
}
判斷二叉樹是否是合法的二叉查詢樹
func isValidBST(root *TreeNode) bool {
left, right := math.MinInt64, math.MaxInt64
var in_order func(root *TreeNode, left, right int) bool
in_order = func(root *TreeNode, left, right int) bool {
if root == nil {
return true
}
if left >= root.Val || root.Val >= right {
return false
}
return in_order(root.Left, left, root.Val) && in_order(root.Right, root.Val, right)
}
return in_order(root, left, right)
}
func isValidBST(root *TreeNode) bool {
var (
stack []*TreeNode
cur *TreeNode
pre int
)
if root == nil {
return true
}
pre = -1 << 63
cur = root
for cur != nil || len(stack) != 0 {
for cur != nil {
stack = append(stack, cur)
cur = cur.Left
}
node := stack[len(stack) - 1]
stack = stack[: len(stack) - 1]
cur = node.Right
if pre >= node.Val {
return false
}
pre = node.Val
}
return true
}