關於二叉樹的面試題

我之前面試了好幾家公司,都會考一些關於二叉樹的面試題,比如下面這幾個面試題:

1. 二叉樹有哪幾種遍歷方式

2.不用遞歸如何遍歷二叉樹3.如何判斷二叉樹是對稱二叉樹4.將二叉樹左右節點翻轉5.實現一個函數接收任意二叉樹,求二叉樹所有根節點到葉子路徑組成的數字之和

前端常考的算法題就是二叉樹和排序了,這些好像很多公司都會有一兩道這樣的題目,大家面試前可以重點看一下這些知識點,這篇文章主要講解二叉樹。

基礎知識

瞭解二叉樹之前我們先要知道什麼是二叉樹和二叉樹的組成。

二叉樹是每個節點不超過兩個。一棵最上面的節點稱爲根節點,如果一個節點下面連接多個節點,那麼該節點稱爲父節點,它下面的節點稱爲子節點,一個節點可以有0個或多個子節點,沒有任何子節點的節點稱爲葉子節點

下面代碼就是創建二叉樹的過程。

function Node(value, left, right) {
  this.value = value;
  this.left = left;
  this.right = right;
}


function BST() {
  this.root = null;
  this.insert = insert;
}


function insert(value) {
  let node = new Node(value, null, null)
  if (this.root == null) {
    // 根節點
    this.root = node;
  } else {
    // 子節點
    let current = this.root;
    let parent;
    while (true) {
      parent = current;
      if (value < current.value) {
        current = current.left;
        if (current == null) {
          parent.left = node;
          break;
        }
      } else {
        current = current.right;
        if(current == null) {
          parent.right = node;
          break;
        }
      }
    }
  }
}


let tree = new BST()


tree.insert(1)
tree.insert(2)
tree.insert(3)
tree.insert(4)
console.log(tree.root)

insert 方法是向二叉樹中出入一個節點,我們需要判斷節點的位置,分別對比左右節點的大小關係,然後選擇性的輸入到其中。

實戰題目

文章的開頭有5道面試題,下面開始做題啦!

問:二叉樹有哪幾種遍歷方式?

答:有三種遍歷方式,中序,先序,後序。中序遍歷按照節點上的鍵值,以升序訪問二叉樹上的所有節點。先序遍歷先訪問根節點,然後以同樣方式訪問左子樹和右子樹。後序遍歷先訪問葉子節點,從左子樹到右子樹,再到根節點。

下圖是中序遍歷的路徑圖,10->22->30->56->77->81->92(按照升序訪問的規則)

// 中序遍歷
function inOrder(node) {
  let result = []
  if (!(node == null)) {
    inOrder(node.left)
    result.push(node.value)
    inOrder(node.right)
  }
  return result
}
let tree = new BST()
tree.insert(56)
tree.insert(22)
tree.insert(81)
tree.insert(10)
tree.insert(30)
tree.insert(77)
tree.insert(92)
inOrder(tree.root)

下圖是先序遍歷的路徑圖,50->10->5->15->70->60->80(按照先根節點再子節點)

// 先序遍歷
function preOrder(node) {
  let result = []
  if (!(node == null)) {
    result.push(node.value)
    preOrder(node.left)
    preOrder(node.right)
  }
  return result
}
let tree = new BST()
tree.insert(50)
tree.insert(10)
tree.insert(70)
tree.insert(5)
tree.insert(15)
tree.insert(60)
tree.insert(80)
preOrder(tree.root)

下圖是後序遍歷的路徑圖,3->22->16->37->99->45->23(按照先子節點再根節點)

// 後序遍歷
function postOrder(node) {
  let result = []
  if (!(node == null)) {
    postOrder(node.left)
    postOrder(node.right)
    result.push(node.value)
  }
  return result
}
let tree = new BST()
tree.insert(23)
tree.insert(16)
tree.insert(45)
tree.insert(3)
tree.insert(22)
tree.insert(37)
tree.insert(99)
postOrder(tree.root)

問:不用遞歸如何遍歷二叉樹?

其實代碼中的 insert 方法就是沒有使遞歸而是使用 while 循環進行遍歷的,不知道你注意到了沒有。下面我再通過使用 while 實現遍歷。

使用循環遍歷二叉樹還必須使用棧進行回溯算法。

// 中序遍歷
function inOrder(node) {
  let stack = []
  let result = []
  let parent = node;
  while (parent !== null || stack.length) {
    if (parent !== null) {
      stack.push(parent)
      parent = parent.left
    } else {
      parent = stack.pop()
      result.push(parent.value)
      parent = parent.right
    }
  }
  console.log(result)
}
// 先序遍歷
function preOrder(node) {
  let stack = []
  stack.push(node)
  let result = []
  while (stack.length !== 0) {
    let parent = stack.pop()
    if (parent.right !== null) {
      stack.push(parent.right)
    }
    if (parent.left !== null) {
      stack.push(parent.left)
    }
    result.push(parent.value)
  }
  console.log(result)
}
// 後序遍歷
function postOrder(node) {
  let stack = []
  stack.push(node)
  let result = []
  let parent = node
  while (stack.length !== 0) {
    parent = stack.pop()
    if (parent.left !== null) {
      stack.push(parent.left)
    }
    if (parent.right !== null) {
      stack.push(parent.right)
    }
    result.unshift(parent.value)
  }
  console.log(result)
}

這裏有一篇文章《非遞歸實現二叉樹先序、中序和後序遍歷[1]》講解了代碼實現的思路。但是是Java代碼寫的,哈哈!

如果你對棧數據結構不瞭解,可以閱讀我之前寫的一篇文章《關於JS括號匹配的面試題[2]》。

總結

先序:根左右, 中序:左根右, 後續:左右根。

這裏在提一句,深度優先和廣度優先的感念, “深度優先搜索就是樹的先序遍歷”,“廣度優先搜索就是樹的按層遍歷”。

深度優先,先序遍歷 ABEFGCD

廣度優先,按層遍歷 ABCDEFG

問:如何判斷二叉樹是對稱二叉樹

如果一個樹的左子樹與右子樹鏡像對稱,那麼這個樹是對稱的。

給定一個二叉樹,檢查它是否是鏡像對稱的。

var node1 = {
  value: 1,
  left: {
    value: 2,
    left: {
      value: 3
    },
    right: {
      value: 4
    }
  },
  right: {
    value: 2,
    left: {
      value: 4
    },
    right: {
      value: 3
    }
  }
}

遞歸

function isSymmetric (root) {
  return isMirror(root, root)
}


function isMirror (t1, t2) {
  if (t1 == null && t2 == null) return true;
  if (t1 == null || t2 == null) return false;
  return (t1.value === t2.value) && isMirror(t1.right, t2.left) && isMirror(t1.left, t2.right)
}


console.log(isSymmetric(node1))

問:將二叉樹左右節點翻轉

原題:二叉樹的數據結構如下,需要將二叉樹各節點左右翻轉

var node1 = {
  value: 1,
  left: {
    value: 2,
    left: {
      value: 4
    },
    right: {
      value: 5
    }
  },
  right: {
    value: 3,
    left: {
      value: 6
    },
    right: {
      value: 7
    }
  }
}

思路:

1.先將左右節點交換位置2.再遞歸子節點

function reverse(node) {
  if (node != null) {
    let temp = node.left;
    node.left = node.right;
    node.right = temp;
    reverse(node.left);
    reverse(node.right);
  }
}

偷偷告訴你,這個題目是滴滴的面試題,感覺還挺難的!哈哈。

5、實現一個函數接收任意二叉樹,求二叉樹所有根節點到葉子路徑組成的數字之和

function getPathSum(root) {
  let total = 0
  function next(node) {
    if (node != undefined) {
      total += node.value
      next(node.left)
      node(node.right)
    }
  }
  next(root)
  return total
}

就是使用先序遍歷完成

6、二叉樹定義如下,實現函數 getPathSum(node)返回7=(1+2)+(1+3)

var node = {
  value: 1,
  left: {
    value: 2,
    left: null,
    right: null
  },
  right: {
    value: 3,
    left: null,
    right: null
  }
}

按照先序遍歷

function getPathSum(root) {
  let total = 0
  let left = []
  let right = []
  function next(node, flag) {
    if (node != undefined) {
      if (flag === 0) {
        left.push(node.value)
        right.push(node.value)
        total = node.value + node.value
      }
      if (flag === 1) {
        left.push(node.value)
        total += node.value
      }
      if (flag === 2) {
        total += node.value
        right.push(node.value)
      }
      next(node.left, 1)
      next(node.right, 2)
    }
  }
  next(root, 0)
  return `${total}=(${left.join('+')})+(${right.join('+')})`
}

這些就是我最近面試的一些題目,大家感覺怎麼樣?

哪裏有問題,歡迎留言指正。

References

[1] 非遞歸實現二叉樹先序、中序和後序遍歷: https://www.cnblogs.com/hengzhezou/p/11027190.html
[2] 關於JS括號匹配的面試題: https://juejin.im/post/5e7cb854f265da429e3890ff

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章