文章目录
1. 特殊二叉树
- 满二叉树:所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上
- 完全二叉树:按层序编号,编号为 i 的结点与同样深度的满二叉树中编号为 i 的结点在二叉树中位置完全相同
- 平衡二叉树:左子树的高度与右子树的高度差的绝对值小于等于1,同样左子树是平衡二叉树,右子树为平衡二叉树
- 二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
( 二叉搜索树的中序遍历为从小到大的有序序列)
2. 二叉树的性质
- 在二叉树的第 i i i层上至多有 2 i − 1 2^ {i-1} 2i−1个结点
- 深度为 k k k的二叉树至多有 2 k − 1 2^k-1 2k−1个结点
- 对任何一棵二叉树T,如果其终端结点数为 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
- 具有 n n n个结点的完全二叉树的深度为 [ l o g 2 n ] + 1 [log_2n]+1 [log2n]+1( [ x ] [x] [x]表示不大于 x x x的最大整数)
3. 二叉树的存储结构
二叉树通常采用链式存储结构,存储结点由数据域和指针域(指针域:左指针域和右指针域)组成,二叉树的链式存储结构也称为二叉链表,对满二叉树和完全二叉树可按层次进行顺序存储
4. 遍历二叉树
二叉树的遍历方式有4种:先序遍历,中序遍历,后序遍历以及层次遍历
根据中序遍历和另外任意一种遍历方式都可以唯一的重建二叉树!
如果没有中序遍历,其余任意两种组合均无法实现二叉树的重建,为什么?
其实原因很简单,先中后说的均为根结点的顺序,那么左子树一定先于右子树遍历到,中序遍历可以根据根结点,将左右子树分开!
1. 前序遍历(根->左->右)
递归实现:
var res = []
var preTraverse = function (pRoot) {
if (pRoot == null) return
res.push(pRoot.val)
preTraverse(pRoot.left)
preTraverse(pRoot.right)
}
2. 中序遍历 (左->根->右)
递归实现:
var res = []
var midTraverse = function (pRoot) {
if (pRoot == null) return
midTraverse (pRoot.left)
res.push(pRoot.val)
midTraverse (pRoot.right)
}
3. 后序遍历(左->右->根)
递归实现:
var res = []
var rearTraverse = function (pRoot) {
if (pRoot == null) return
rearTraverse (pRoot.left)
rearTraverse (pRoot.right)
res.push(pRoot.val)
}
4. 层序遍历
原理:队列( 把二叉树打印成多行 )
var levelTraverse = function (pRoot) {
if (pRoot == null) {
return null
}
let res = [] //存放遍历结果
let queue = [] //定义辅助队列
queue.push(pRoot)
while (queue.length) {
let temp = queue.shift()
res.push(temp.val)
if (temp.left) {
queue.push(temp.left)
}
if (temp.right) {
queue.push(temp.right)
}
}
return res
}
5. 其他二叉树操作
JZ 4 重建二叉树
前序+中序
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function reConstructBinaryTree(pre, vin)
{
if (!pre.length || !vin.length){
return null
}
//创建根节点
let rootVal = pre[0]
let node = new TreeNode(rootVal)
//找到根节点在中序数组中的坐标
let index = vin.indexOf(rootVal)
//递归
node.left = reConstructBinaryTree(pre.slice(1, index + 1), vin.slice(0, index))
//left中为何是index+1????因为slice是左闭右开
node.right = reConstructBinaryTree(pre.slice(index + 1), vin.slice(index + 1))
return node
}
JZ 18 镜像二叉树
操作给定的二叉树,将其变换为源二叉树的镜像。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function Mirror(root)
{
if(root==null){
return
}else{
var item = root.left;
root.left = root.right;
root.right = item;
Mirror(root.left);
Mirror(root.right);
}
}
JZ 22 JZ 60 打印二叉树
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
//每次出队一个元素,就将该元素的孩子节点加入队列中,直至队列中元素个数为0时,出队的顺序就是该二叉树的层次遍历结果
function Print(pRoot)
{
if(pRoot == null){
return []
}
var queue = []
var res = []
queue.push(pRoot)
while(queue.length){
var tempArr = [] //每一层的数组
var len = queue.length //这里如果不先写出来,定死每次循环的len,是无法通过的
for(var i = 0; i < len; i++){ //len如果直接写成queue.length,那每次循环会实时变化
var tempNode = queue.shift()
tempArr.push(tempNode.val)
if(tempNode.left){
queue.push(tempNode.left)
}
if(tempNode.right){
queue.push(tempNode.right)
}
}
res.push(tempArr)
}
return res
}
JZ 38 二叉树深度
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function TreeDepth(pRoot)
{
if (pRoot==null){
return 0
}else{
var left = TreeDepth(pRoot.left) + 1;
var right = TreeDepth(pRoot.right) + 1;
return Math.max(left, right)
}
}
JZ 39 平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
解法一:
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function IsBalanced_Solution(pRoot)
{
if (pRoot == null) {
return true
}
return IsBalanced_Solution(pRoot.left) && IsBalanced_Solution(pRoot.right) && Math.abs(height(pRoot.left)-height(pRoot.right)) <= 1
}
function height(pRoot)
{
if (pRoot == null) {
return 0
}
return Math.max(height(pRoot.left), height(pRoot.right)) + 1
}
解法二(没看懂):
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function IsBalanced_Solution(pRoot)
{
if(TreeDepth(pRoot) == -1){
return false
}
return true;
}
function TreeDepth(pRoot){
if(pRoot == null) { return 0 }
var left = TreeDepth(pRoot.left);
if(left == -1) { return -1 } //-1表示不平衡
var right = TreeDepth(pRoot.right);
if(right == -1) { return -1 }
return Math.abs(left - right) > 1 ? -1 : Math.max(left, right) + 1;
}
JZ 57 二叉树的下一个结点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
/*function TreeLinkNode(x){
this.val = x;
this.left = null;
this.right = null;
this.next = null;
}*/
function GetNext(pNode)
{
if (pNode == null){return null}
if (pNode.right != null){
var res = pNode.right
while(res.left != null){
res = res.left
}
return res
}else{
var parent = pNode.next
if(parent == null){
return null
}
if(parent.left == pNode){
return parent
}
if(parent.right == pNode){
let pparent = parent.next
while(pparent != null && pparent.left != parent){
parent = pparent
pparent = parent.next
}
return pparent
}
}
}
JZ 58 对称二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
//个人理解为原函数形参为树,为一个
function isSymmetrical(pRoot)
{
return isSymmetrical2(pRoot, pRoot)
}
//而实际判断时需要比较的形参个数有两个,所以在原函数返回值中调用判断的函数,这样得到实际的返回值
function isSymmetrical2(root1, root2){
if(root1 == null && root2 == null){
return true
}
if(root1 == null || root2 == null){
return false
}
if(root1.val != root2.val){
return false
}
return isSymmetrical2(root1.left, root2.right) && isSymmetrical2(root1.right, root2.left)
}
JZ 61 序列化二叉树
6. 二叉搜索树
JZ 26 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
//1.中序遍历二叉树,用一个数组储存 2.为每个节点赋予双向关系
var midorderRes = [] //储存中序遍历结果
function Convert(pRootOfTree)
{
if(pRootOfTree == null){
return null
}
if(pRootOfTree.left == null && pRootOfTree.right == null){
return pRootOfTree
}
Midorder(pRootOfTree) //先中序遍历
let head = midorderRes.shift() //双向连报表的头节点
let pre = head //pre指针,首先指向头节点
let cur //声明cur指针
while(midorderRes.length){
cur = midorderRes.shift() //cur指针指向了pre指针的下一个
cur.left = pre //建立两指针的对应关系
pre.right = cur
pre = cur
}
head.left = null //头节点的left指向空,否则就变成了循环链表。但不加也可以通过
cur.right = null //尾节点的right指向空。不加也可以
return head
}
function Midorder (pHead){ //递归中序遍历。注意理解!
if(pHead == null) return
Midorder(pHead.left)
midorderRes.push(pHead)
Midorder(pHead.right)
}
JZ 62 二叉搜索树的下一个结点
特点:某一个节点左子树的所有节点的值都比该节点小,右子树的所有节点的值都比该节点大
- 给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
//解题关键字 "二叉搜索树"
//看到二叉搜索树字眼大家脑海要立刻想起,左子节点值<根节点值<右子节点值
//中序遍历就是有序列表,那么问题就迎刃而解了
function KthNode(pRoot, k)
{
var res = []
MidOrder(pRoot)
function MidOrder(pRoot){
if(pRoot == null) return
MidOrder(pRoot.left)
res.push(pRoot)
MidOrder(pRoot.right)
}
return res[k-1]
}