剑指offer-二叉树结构

本文详细介绍了二叉树的概念,包括其结构、插入节点的方法以及三种遍历方式(前序、中序、后序)。同时,讨论了在二叉树中查找最大值、最小值和特定元素的策略,以及如何删除元素。此外,还对比了深度优先搜索(DFS)和广度优先搜索(BFS)的特点和适用场景。
摘要由CSDN通过智能技术生成

1. 什么是二叉树?

  • 简单来说二叉树是一个特殊的树,每个节点最多有两个节点
  • 二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。

在这里插入图片描述

2. 如何新增二叉树节点?

  • 二叉树插入节点,比根节点小的插入左子树,比根节点大的插入右子树
//节点类
class Node {
    constructor(key) {
        this.left = null
        this.right = null
        this.key = key
    }
}

//二叉树类
class BinaryTree {
    constructor() {
        // 根节点
        this.root = null
    }
    // 插入节点
    insert (key) {
        const newNode = new Node(key)
        if (this.root === null) {
            this.root = newNode
        } else {
            this.inOrderTraversNode(this.root, newNode)
        }
    }
    // 插入节点具体方法
    inOrderTraversNode (node, newNode) {
        if (newNode.key < node.key) { // 左插
            if (node.left === null) {
                node.left = newNode
            }else{
                this.inOrderTraversNode(node.left, newNode)
            }    
        } else { // 右插
            if (node.right === null) {
                node.right = newNode
            } else {
                this.inOrderTraversNode(node.right, newNode)
            }
        }
    }
}

3. 如何遍历二叉树?

递归的缺点:时间和空间的消耗比较大、重复计算、调用栈溢出
遍历二叉树一般用递归比较简单,但是如果二叉树层级较多,则会出现调用栈溢出

  • 先序遍历 中序遍历 后序遍历
  • 前序遍历:首先访问根结点,然后遍历左子树,最后遍历右子树(根->左->右)
  • 中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树(左->根->右)
  • 后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点(左->右->根)
//节点类
class Node {
    constructor(key) {
        this.left = null
        this.right = null
        this.key = key
    }
}

//二叉树类
class BinaryTree {
    constructor() {
        // 根节点
        this.root = null
    }
    // 中序遍历--采用递归实现
    inOrder(node) {
        if (!(node == null)) { // 判断当前节点是否为null
            inOrder(node.left); //递归调用,传入当前节点的左节点
            console.log(node.show() + " "); // 打印当前节点
            inOrder(node.right); // 递归调用,当前节点的右节点
        }
    }
    // 先序遍历
    preOrder(node) {
        if (!(node == null)) {
            console.log(node.show());
            preOrder(node.left);
            preOrder(node.right);
        }
    }

    // 后序遍历
    postOrder(node) {
        if (!(node == null)) {
            postOrder(node.left);
            postOrder(node.right);
            console.log(node.show());
        }
    }
}

4. 二叉树如何查找最大值、最小值?

	// 查找最小值
    getMin() {
        // 由于二叉树是根据大小插入的(大于父节点的在右边,小于父节点的在左边)
        // 所以最小值一定在左节点的尽头,反之最大值在右节点的尽头
        var current = this.root;// 将当前节点指向根节点
        while (!(current.left == null)) { // 判断当前节点的左节点是否为null
            current = current.left; // 向下找
        }
        return current.data; // 返回最左节点
    }
    // 查找最大值
    getMax() {
        var current = this.root;
        while (!(current.right == null)) {
            current = current.right;
        }
        return current.data;
    }

5. 二叉树中如何查找到指定元素?

// 查找特定值
    find(data) {
        var current = this.root;
        while (current != null) {
            // 根据数据大小一层一层向下遍历寻找
            if (current.data == data) {
                return current;
            } else if (data < current.data) {
                current = current.left;
            } else {
                current = current.right;
            }
        }
        return null;
    }

6. 二叉树中如何删除元素?

function removeSurface(data){  
// 重写树,以为删除节点,树发生啦变化
    this.root=removeNode(this.root,data);  
}  

function removeNode(node,data){  
    if(node==null){  //当树为空树时
        return null;  
    }else if(node.data==data){ //当当前节点的值为data时
        if(node.left==null&&node.right==null){ //当当前节点为叶子时
            return null;
        }else if(node.left==null){ //左子树为空
            return node.right;
        }else if(node.right==null){ //右子树为空
            return node.left;
        }else{
			// 先通过getMin函数查找以当前元素为根节点的子树中右子树最小值所对应的节点
			//(其实也可以找左子树的最大值)
            var tempNode=getMin(node.right);
			// 将当前元素替换成右子树最小值所对应的节点
            node.data=tempNode.data;
            // 然后用removeNode递归的方式
            // 在以当前元素为根节点的子树中删除右子树最小值所对应的节点。
            node.right=removeNode(node.right,tempNode.data);
            return node;
            // 为什么要找右子树最小值(或者左子树的最大值)呢?
            // 因为右子树最小值比左子树的所有值都大,却比右子树的所有值小
            // 这正是根节点的特性,用它来替代根节点再适合不过了。
            // 当被删除节点的值小于当前节点值时
            // 通过node.left=removeNode(node.left,data)将当前节点设置为
            // 原当前节点的左节点,并重写以当前节点为根节点的树。
			// 当被删除节点的值大于当前节点值时,原理也是如此。
        }
    }else if(data<node.data){
        node.left=removeNode(node.left,data);
        return node;
    }else{
        node.right=removeNode(node.right,data);
        return node;
    }
}

7. BFS和DFS是什么?

  • BFS(广度优先)
  • DFS(深度优先)
  • 深度优先和广度优先是一种遍历的思想
  • 深度优先是先往下遍历
  • 广度优先的思想是一层一层往下遍历
BFS和DFS的区别
  1. 深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大
  2. 深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点
  3. 深度优先采用的是堆栈的形式, 即先进后出
  4. 广度优先则采用的是队列的形式, 即先进先出

深度优先DFS

  • 先序遍历非递归式方法
let deepfirst = function(nodes){
	let stack = []
	let result = []
	stack.push(nodes)
	while(stack.length){
		let node = stack.pop()
		result.push(node)
		if(node.left){
			result.push(node.left)
		}
		if(node.right){
			result.push(node.right)
		}
	}
	return result
}

广度优先BFS

// 广度优先
    function bsf(root){
        let quen = []; // 用来遍历的数组
        quen.push(root);
        // 从队列取,然后再追加
        for(let i = 0;i<=quen.length-1;i++){
            let k = quen[i];
            if(k.left){
                quen = quen.concat(k.left);
            }
            if(k.right){
                quen = quen.concat(k.right);
            }
        }
        return quen;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值