力扣關於樹的題目(三)(一般二叉樹+套路總結)
阿新 • • 發佈:2022-04-21
二叉樹的屬性問題
1. 101. 對稱二叉樹
檢查自己,左子樹,右子樹
/** * @param {TreeNode} root * @return {boolean} */ var isSymmetric = function(root) { const check = (left,right) => { if (left === null && right === null) { return true; } if (left === null || right === null) { return false; } let leftData = check(left.left,right.right); let rightData = check(right.left,left.right); return leftData && rightData && (left.val === right.val); } return check(root.left,root.right) };
2. 104. 二叉樹的最大深度
套路,左子樹的深度和右子樹的深度,最大的+1
/** * @param {TreeNode} root * @return {number} */ var maxDepth = function(root) { let deep = 0; if (!root) { return deep; } const findDeep = (root) => { if (!root) { return 0; } let left = findDeep(root.left); let right = findDeep(root.right); return Math.max(left,right)+1; } return findDeep(root); };
3. 111. 二叉樹的最小深度
跟上一題一樣,套路,左右兩邊小的+1
/** * @param {TreeNode} root * @return {number} */ var minDepth = function(root) { const findMin = (root) => { if(!root){ return 0; } if(root.left == null && root.right == null){ return 1; } let min = Infinity; if(root.left){ min = Math.min(min,findMin(root.left)) } if(root.right){ min = Math.min(min,findMin(root.right)) } return min + 1; } return findMin(root); };
4. 222. 完全二叉樹的節點個數
套路,左邊+右邊+1
/**
* @param {TreeNode} root
* @return {number}
*/
var countNodes = function(root) {
const count = (root) => {
if (!root) {
return 0;
}
let leftData = count(root.left);
let rightData = count(root.right);
return leftData + rightData + 1;
}
return count(root);
};
5. 110. 平衡二叉樹
套路,向左邊要資訊,右邊要資訊,再判斷返回
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
function returnData(is, height) {
this.isBan = is;
this.height = height;
}
const check = (root) => {
if (!root){
return new returnData(true,0);
}
let leftData = check(root.left);
let rightData = check(root.right);
let is = true;
if (!leftData.isBan || !rightData.isBan || Math.abs(leftData.height-rightData.height) > 1) {
is = false;
}
return new returnData(is,Math.max(leftData.height,rightData.height)+1);
}
return check(root).isBan;
};
6. 257. 二叉樹的所有路徑
這題有點不一樣,像全排列,回溯
/**
* @param {TreeNode} root
* @return {string[]}
*/
var binaryTreePaths = function(root) {
let res = [];
let path = [];
function changeToStr() {
let str = "";
for (let i = 0; i < path.length; i++) {
if (i === path.length-1) {
str = str.concat(path[i]);
} else {
str = str.concat(path[i] + "->");
}
}
return str;
}
const backtrace = (root) => {
if (root) {
path.push(root.val);
}
if (!root.left && !root.right){
res.push(changeToStr());
return;
}
if (root.left){
backtrace(root.left)
path.pop();
}
if (root.right) {
backtrace(root.right);
path.pop();
}
}
backtrace(root);
return res;
};
7. 404. 左葉子之和
這裡實際上還是遍歷,遍歷到所有的節點,看它是否存在左節點,而且是葉子節點
/**
* @param {TreeNode} root
* @return {number}
*/
var sumOfLeftLeaves = function(root) {
let sum = 0;
function checkLeave(root) {
if (!root.left && !root.right) {
return true;
}else {
return false;
}
}
const bianli = (root) => {
if(!root){
return;
}
if(root.left && checkLeave(root.left)){
sum += root.left.val;
}
bianli(root.left);
bianli(root.right);
}
bianli(root)
return sum;
};
8. 112. 路徑總和
回溯問題,後面應該會單獨再歸納回溯的專題
/**
* @param {TreeNode} root
* @param {number} targetSum
* @return {boolean}
*/
var hasPathSum = function(root, targetSum) {
if(!root) {
return false;
}
function checkLeave(root) {
if (!root.left && !root.right){
return true;
}else {
return false;
}
}
// backtrace
const check = (root,sum) => {
if (checkLeave(root) && sum === 0){
return true;
}
if (checkLeave(root)) {
return false;
}
if (root.left) {
if (check(root.left,sum- root.left.val)){
return true;
}
}
if (root.right) {
if (check(root.right,sum- root.right.val)){
return true;
}
}
return false;
}
return check(root,targetSum-root.val);
};
二叉樹的修改和構造問題
套路:
修改:找到對應的節點,修改左右子樹
構造:先構造根節點,再遞迴構造左右子樹
1. 226. 翻轉二叉樹
遍歷,每一個節點都交換它的左右節點
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var invertTree = function(root) {
// if (!root) {
// return root;
// }
function swap(root) {
let tmp = root.left;
root.left = root.right;
root.right = tmp;
}
const order = (root) => {
if (!root) {
return;
}
if (root.left || root.right) {
swap(root);
}
order(root.left);
order(root.right)
}
order(root);
return root;
};
2. 106. 從中序與後序遍歷序列構造二叉樹
在後序找到根節點,再根據中序構造左右子樹
/**
* @param {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
if (postorder.length === 0) {
return null;
}
let root = new TreeNode(postorder[postorder.length-1]);
let mid = inorder.findIndex((number) => number === root.val);
root.left = buildTree(inorder.slice(0,mid),postorder.slice(0,mid));
root.right = buildTree(inorder.slice(mid+1,inorder.length),postorder.slice(mid,postorder.length-1));
return root;
};
3. 105. 從前序與中序遍歷序列構造二叉樹
跟上一題一樣,在前序找到根節點,再根據中序構造左右子樹
/**
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
if (preorder.length === 0) {
return null;
}
let root = new TreeNode(preorder[0]);
let mid = inorder.findIndex((number) => number === root.val);
root.left = buildTree(preorder.slice(1,mid+1),inorder.slice(0,mid));
root.right = buildTree(preorder.slice(mid+1,preorder.length),inorder.slice(mid+1,inorder.length));
return root;
};
4. 654. 最大二叉樹
套路,找到對應的節點構造根,再構造左右子樹
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var constructMaximumBinaryTree = function(nums) {
function getMax(start, end) {
let res = start;
for (let i = start; i <= end; i++) {
if (nums[i]>nums[res]) {
res = i;
}
}
return res;
}
const backtrace = (start=0,end=nums.length-1) => {
if (start > end){
return null;
}
let index = getMax(start,end);
let root = new TreeNode(nums[index],backtrace(start,index-1),backtrace(index+1,end))
return root;
}
return backtrace();
};
5. 617. 合併二叉樹
套路,找到對應的節點構造根,再構造左右子樹
/**
* @param {TreeNode} root1
* @param {TreeNode} root2
* @return {TreeNode}
*/
var mergeTrees = function(root1, root2) {
const backtrace = (node1,node2) => {
if (!node1) {
return node2;
}
if (!node2) {
return node1;
}
let root = new TreeNode(node1.val+node2.val,backtrace(node1.left,node2.left),backtrace(node1.right,node2.right))
return root;
}
return backtrace(root1,root2);
};
二叉樹的祖先問題
1. 236. 二叉樹的最近公共祖先
- map儲存所有的節點以及其父節點
- 利用map記錄p的所有祖宗節點,一直往上竄,直到root
- 利用map找q的祖宗節點,直到找到的節點出現在p的祖宗節點中
/**
* 最低公共祖先
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
let map = new Map();
map.set(root,root);
const preOrder = (root) => {
if (!root) {
return;
}
map.set(root.left,root);
map.set(root.right,root);
preOrder(root.left);
preOrder(root.right);
}
preOrder(root);
let hashset = [];
let cur = p;
while (cur !== map.get(cur)) {
hashset.push(cur);
cur = map.get(cur);
}
hashset.push(root)
cur = q;
while (cur !== map.get(cur)) {
let index = hashset.findIndex((item) => item.val === cur.val)
if (index === -1){
cur = map.get(cur);
}else {
return hashset[index];
}
}
return root;
};
優化的程式碼
- 如果是p和q分別在root的左右兩邊,也就是說left和right都有值,那麼root就是祖先
- left和right有一方是沒有值的,說明另一方就是這一方的祖先
/**
* 最低公共祖先
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
const check = (root,p,q) => {
if(!root || root === p || root === q) {
return root;
}
let left = check(root.left,p,q)
let right = check(root.right,p,q);
if(left && right) {
return root;
}
return left !== null ? left : right;
}
return check(root,p,q)
};
二叉樹的套路
- 定義資料型別
- 向左邊要資訊,要右邊要資訊
- 然後根據左右的資訊,算出自己要返回的資訊