二叉树的遍历方式
前序遍历、中序遍历、后序遍历都属于DFS(深度优先遍历),往往用递归实现。
递归就是思考这个函数的功能是什么!一定注意要有递归终止条件!(1,定义终止条件;2,实现功能;3,调用递归。23顺序不一定)
## 二叉树的前序遍历:
此函数的功能就是给一个根节点,返回一个此节点的前序遍历数组。前序遍历的顺序是(中左右):[root,(传入左子树根结点,返回)左子树的前序遍历,(传入右子树根结点,返回)右子树的前序遍历]
var preorderTraversal = function(root) {
if(root===null) return [];
const res=[];
res.push(root.val);
if(root.left) res.push(...preorderTraversal(root.left));
if(root.right) res.push(...preorderTraversal(root.right));
return res;
};
## 二叉树的中序遍历:
左中右:[(传入左子树根结点,返回)左子树的中序遍历,root,(传入右子树根结点,返回)右子树的中序遍历]
var inorderTraversal = function(root) {
if(root===null) return [];
const res=[];
res.push(...inorderTraversal(root.left));
res.push(root.val);
res.push(...inorderTraversal(root.right));
return res;
};
## 二叉树的后序遍历:
左右中:[(传入左子树根结点,返回)左子树的后序遍历,(传入右子树根结点,返回)右子树的后序遍历,root]
var postorderTraversal = function(root) {
if(root===null) return [];
const res=[];
res.push(...postorderTraversal(root.left));
res.push(...postorderTraversal(root.right));
res.push(root.val);
return res;
};
## 二叉树的层序遍历
属于BFS(广度优先搜索),往往用队列实现。
①(Medium版)
var levelOrder = function(root) {
if(root===null) return [];
const queue=[];
const res=[];
queue.push(root);
while(queue.length!==0){
let levelSize=queue.length;
res.push([]);
for(let i=0;i<levelSize;i++){
let node=queue.shift();
res[res.length-1].push(node.val);
if(node.left!==null){
queue.push(node.left);
}
if(node.right!==null){
queue.push(node.right);
}
}
}
return res;
};
②从上到下打印二叉树(简单版),要求返回一个数组就可,不用分层。
var levelOrder = function(root) {
if(root===null) return [];
const queue=[];
const res=[];
queue.push(root);
while(queue.length!==0){
let node=queue.shift();
res.push(node.val);
if(node.left!==null){
queue.push(node.left);
}
if(node.right!==null){
queue.push(node.right);
}
}
return res;
};
medium版while里四行是要进for循环的且在while下面for循环前面多了两行。
二叉树的锯齿形层次遍历
var zigzagLevelOrder = function(root) {
if(root===null) return [];
const res=[];
const queue=[];
queue.push(root);
let isOdd=true;
while(queue.length!==0){
let levelSize=queue.length;
res.push([]);
for(let i=0;i<levelSize;i++){
let node=queue.shift();
if(isOdd){
res[res.length-1].push(node.val);
}else{
res[res.length-1].unshift(node.val);
}
if(node.left) queue.push(node.left);
if(node.right) queue.push(node.right);
}
//!isOdd&&res[res.length-1].reverse();
isOdd=!isOdd;
}
return res;
};
二叉树的右视图
var rightSideView = function(root) {
if (root===null) return [];
const queue = [];
const res = [];
queue.push(root);
while(queue.length!==0){
res.push([]);
let levelSize=queue.length;
for(let i=0;i<levelSize;i++){
let node=queue.shift();
res[res.length-1].push(node.val);
if(node.left!==null) queue.push(node.left);
if(node.right!==null) queue.push(node.right);
}
}
const result=[];
for(let i=0;i<res.length;i++){
result.push(res[i][res[i].length-1]);
}
return result;
};
递归的题:
二叉树的最小深度
var minDepth = function(root) {
if(root===null) return 0;
if(root.left===null) return minDepth(root.right)+1;//比二叉树的最大深度多了判断左右结点存不存在的一步
if(root.right===null) return minDepth(root.left)+1;//
return Math.min(minDepth(root.left),minDepth(root.right))+1;
};
二叉树的最大深度:
var maxDepth = function(root) {
if(root===null) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
};
二叉树的直径!!!!!!!!!!!!!!!!!!
深度是结点的个数,直径是所有结点间边的个数(也等于左结点数+右结点数)。
var diameterOfBinaryTree = function(root) {
let ans=0;
function maxDepth(root){
if(root===null) return 0;
let leftDepth=maxDepth(root.left);
let rightDepth=maxDepth(root.right);
ans=Math.max(ans,leftDepth+rightDepth);
return Math.max(leftDepth,rightDepth)+1;
}
maxDepth(root);
return ans;
};
判断是不是平衡二叉树与二叉树的最大深度有关系
平衡二叉树:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1
var isBalanced = function(root) {
if(root===null) return true;
let leftDepth=maxDepth(root.left);
let rightDepth=maxDepth(root.right);
return Math.abs(leftDepth-rightDepth)<=1&&isBalanced(root.left)&&isBalanced(root.right);
};
var maxDepth=function(pRoot){
if(pRoot===null) return 0;
return Math.max(maxDepth(pRoot.left),maxDepth(pRoot.right))+1;
}
重建二叉树(从前序和中序遍历序列构造二叉树):
var buildTree = function(preorder, inorder) {
if(preorder.length===0||inorder.length===0){return null;}
const root =new TreeNode(preorder[0]);//创建根结点
const target=preorder[0];//取出根结点的值
const rootIndex=inorder.indexOf(target);
let leftIn=inorder.slice(0,rootIndex);//左中
let rightIn=inorder.slice(rootIndex+1);//右中
let leftPre=preorder.slice(1,rootIndex+1);//左前
let rightPre=preorder.slice(rootIndex+1);//右前
root.left=buildTree(leftPre,leftIn);
root.right=buildTree(rightPre,rightIn);
return root;
};
二叉树的镜像(翻转二叉树)
function Mirror( pRoot ) {
if(pRoot===null){
return null;
}
[pRoot.left,pRoot.right]=[pRoot.right,pRoot.left];//解构赋值,实现二叉树根结点下面俩子结点反转
Mirror(pRoot.left);//递归调用Mirror函数实现将以pRoot为根节点的树反转
Mirror(pRoot.right);//递归调用Mirror函数实现将以pRoot为根节点的树反转
return pRoot;
}
对称的二叉树
var isSymmetric = function(root) {
if(root===null) return true;
return compareRoots(root.left,root.right);
};
var compareRoots=function(leftRoot,rightRoot){
if(leftRoot===null){
if(rightRoot===null){
return true;
}else{
return false;
}
}
if(rightRoot===null){
return false;
}
if(leftRoot.val!==rightRoot.val){
return false;
}
return compareRoots(leftRoot.left,rightRoot.right)&&compareRoots(leftRoot.right,rightRoot.left);
}
相同的树
var isSameTree = function(p, q) {
if(p===null&&q===null) return true;
if((p===null&&q!==null)||(p!==null&&q===null)) return false;
if(p.val===q.val){return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right)}
else if(p.val!==q.val){return false;}
};
求根结点到叶子结点数字之和!!!
var sumNumbers = function(root) {
return dfs(root,0);
};
const dfs=(root,prevSum)=>{
if(root===null) return 0;
const sum=prevSum*10+root.val;
if(root.left===null&&root.right===null){
return sum;
}else{
return dfs(root.left,sum)+dfs(root.right,sum);
}
}
二叉树中和为某一值的路径(easy版)
属于先序遍历
function hasPathSum( root , sum ) {
if(root===null) return false;
if(root.val===sum&&root.left===null&&root.right===null){return true;}
return (hasPathSum(root.left,sum-root.val)||hasPathSum(root.right,sum-root.val));
}
树的子结构
思路:子树B的根结点可能为A树的任意一个根结点nA,所以一:要先序遍历A树(因为先序遍历是中优先);二:判断以nA为根结点的子树是否包含B(另写函数实现)。
递归要注意终止条件:
在这里插入代码片
二叉搜索树的第k大结点
二叉搜索树:根结点左侧节点如果存在,值都小于根结点,右侧节点如果存在,值都大于根结点。
中序遍历:左根右
利用性质:二叉搜索树的中序遍历是递增序列
var kthLargest = function(root, k) {
let res=inorderTraversal(root);
return res[res.length-k];
};
var inorderTraversal=function(root){
if(root===null) return [];
const res=[];
if(root.left){
res.push(...inorderTraversal(root.left));
}
res.push(root.val);
if(root.right){
res.push(...inorderTraversal(root.right));
}
return res;
}
二叉搜索树中的搜索
var searchBST = function(root, val) {
if(root===null) return null;
if(root.val===val) return root;
return searchBST(root.val<val?root.right:root.left,val);
};
验证二叉搜索树
先中序遍历再查看是否是严格递增序列
var isValidBST = function(root) {
const inOrder=(root)=>{
if(root===null) return [];
const res=[];
if(root.left!==null) res.push(...inOrder(root.left));
res.push(root.val);
if(root.right!==null) res.push(...inOrder(root.right));
return res;
}
const arr=inOrder(root);
for(let i=1;i<arr.length;i++){
if(arr[i-1]>=arr[i]){
return false;
}
}
return true;
};
不同的二叉搜索树规律题,死记硬背公式
var numTrees = function(n) {
let C = 1;
for (let i = 0; i < n; ++i) {
C = C * 2 * (2 * i + 1) / (i + 2);
}
return C;
};
二叉树展开为链表先进行前序遍历
var flatten = function(root) {
const preOrder=(root)=>{
if(root===null) return [];
const res=[];
res.push(root);//这里是push(root)而不是push(root.val)
if(root.left!==null) res.push(...preOrder(root.left));
if(root.right!==null) res.push(...preOrder(root.right));
return res;
}
let arr=preOrder(root);
for(let i=1;i<arr.length;i++){
const pre=arr[i-1],cur=arr[i];
pre.left=null;
pre.right=cur;
}
};
合并二叉树
var mergeTrees = function(root1, root2) {
if(root1===null) return root2;
if(root2===null) return root1;
root1.val=root1.val+root2.val;
root1.left=mergeTrees(root1.left,root2.left);
root1.right=mergeTrees(root1.right,root2.right);
return root1;
};
二叉树的最近公共祖先!!!!!!!!!!!!!!!
const lowestCommonAncestor = (root, p, q) => {
if (!root) return null;
// 根节点等于p或q,那么root是最近公共祖先
if (root === p || root === q) return root;
// 向左子树寻找节点相同的点
const left = lowestCommonAncestor(root.left, p, q);
// 向右子树寻找节点相同的点
const right = lowestCommonAncestor(root.right, p, q);
// 若左右各找到一个,那么当前根节点就是最近公共祖先
if (left && right) return root;
// 只有左边找到,那么最近公共祖先在左边
if (left) return left;
// 只有右边找到,那么最近公共祖先在右边
if (right) return right;
};