特殊的二叉树

174 阅读3分钟

二叉搜索树

二叉搜索树定义如下:

  • 空树是二叉搜索树
  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。 考察思路:特性(判定)、操作(构造)
    参考:juejin.cn/book/684473…

验证二叉搜索树 【力扣98】

示例

示例1

image.png

输入: [5,4,6,null,null,3,7]
输出: false

示例2

image.png

输入: [5,3,6,2,4,null,7]
输出:true

思路

根据二叉搜索树的定义,我们只需要递归地对非空树中的左右子树进行遍历,检验每棵子树中是否都满足左子树中的结点值<根结点<右子树。
示例1中每个结点都满足左儿子结点<根结点<右儿子结点,但③小于⑤,显然不应该在⑤的右子树中,因此需要注意数据域的继承。

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isValidBST = function(root) {
    if(!root) return true;
    const isValid = function(root,minVal,maxVal) {
        if(!root) return true;
        if(root.val>minVal && root.val<maxVal) {
            return isValid(root.left,minVal,root.val)&&isValid(root.right,root.val,maxVal);
        }else {
            return false;
        }
    }
    return isValid(root,-Infinity,Infinity);
};

将有序数组转换为二叉搜索树【力扣108】

将升序序列转换成高度平衡的二叉搜索树。高度平衡:每个节点的左右两个子树的高度差的绝对值不超过 1

示例

image.png

输入:[1,2,3,4,5,6,7]
输出:[4,2,6,1,3,5,7]

思路

观察示例,可以看出平衡二叉搜索树像是从序列中间把树根拎起来一样,左半边序列和右半边序列再分别从中间拎起,那么我们只需要递归的对序列进行二分即可。

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var sortedArrayToBST = function(nums) {
    const buildBST = function(low,high) {
        if( low > high ) {//序列已经遍历完
            return null;
        }
        let mid = Math.floor(low + (high - low)/2);//获取序列中间的数字
        let curr=new TreeNode(nums[mid]);//中间数作为当前结点
        curr.left = buildBST(low,mid-1);//用较小数列递归建立左子树
        curr.right = buildBST(mid+1,high);//用较大数列递归建立右子树
        return curr;
    }
    const root=buildBST(0,nums.length-1);
    return root;
};

平衡二叉树

判定:【力扣110】

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isBalanced = function(root) {
    let flag = true;
    const dfs = function(root) {
        if(!root||!flag) return 0;
        let left=dfs(root.left);
        let right=dfs(root.right);
        if(Math.abs(left-right)>1){
            flag=false;
            return 0;
        }
        return Math.max(left,right)+1;
    }
    dfs(root);
    return flag;
};

构造:【力扣1382】

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var balanceBST = function(root) {
    //中序遍历,将二叉搜索树转换为有序数组
    let vals = [];
    const inOrderTravel = function(root){
        if(!root) return;
        inOrderTravel(root.left);
        vals.push(root.val);
        inOrderTravel(root.right);
    }
    inOrderTravel(root);
    // console.log(vals);
    //有序数组->平衡二叉搜索树
    const BST = function(low,high){
        if(low>high) return null;
        let mid=Math.floor(low+(high-low)/2);
        let root=new TreeNode(vals[mid]);
        root.left=BST(low,mid-1);
        root.right=BST(mid+1,high);
        return root;
    }
    return BST(0,vals.length-1);
};

完全二叉树:

  • 除了倒数第一层外,每层都是满的
  • 倒数第一层所有结点都排列在这一层的最左边连续不断
  • 某结点的索引值为n,则其父结点索引为(n-1)/2,子结点索引值为2n+1和2n+2

小顶堆:所有结点的值都大于父结点的值
大顶堆:所有结点的值都小于父结点的值 关键点为堆结点的插入与删除,以下题为例:数组中的第k个最大元素

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var findKthLargest = function(nums, k) {
    const len = nums.length;
    const heap = [];
    let n = 0;
    //建立堆
    const createHeap = function(){//初始化一个大小为k的小顶堆
        for(let i=0;i<k;i++){
            insert(nums[i]);
        }
    }
    const insert = function(num){//堆的插入操作
        heap[n]=num;
        upHeap(0,n);
        n++;
    }
    const upHeap = function(low,high){//自底向上调整,维持小顶堆
        let i = high;
        let j = Math.floor((i-1)/2);//父结点
        while(j>=low){
            if(heap[i] < heap[j]){
                let temp = heap[i];
                heap[i] = heap[j];
                heap[j] = temp;
            }
            i = j;//当前结点更新为父结点
            j = Math.floor((j-1)/2);//父结点更新为祖父结点
        }
    }
    //更新堆
    const updateHeap = function(){//数组中剩余元素用来更新小顶堆
        for(let i=k;i<len;i++){
            if(nums[i]>heap[0]){
                heap[0]=nums[i];
                downHeap(0,n)
            }
        }
    }
    const downHeap = function(low,high){//自顶向下调整
        let i = low;
        let j = i*2+1;//子结点
        while(j<=high){
            if(j+1<=high && heap[j]>heap[j+1]) j = j+1;//与儿子里小的那个交换
            if(heap[i]>heap[j]){
                let temp = heap[i];
                heap[i] = heap[j];
                heap[j] = temp;
            }
            i = j;
            j = j*2+1;
        }
    }
    createHeap();
    updateHeap();
    return heap[0];//堆顶元素为最大的K个数中的最小数,即第k大的元素
};