算法与数据结构之二叉树知多少!

二叉树和二叉查找树

描述:

  • 树是计算机科学中经常用到的一种数据结构。
  • 树是一种非线性的数据结构,以分层的方式存储数据。
  • 树被用来存储具有层级关系的数据,比如文件系统中的文件。

选择树而不是那些基本的数据结构,是因为:

  • 在二叉树上进行查找非常快(而在链表上查找则不是这样)。
  • 为二叉树添加或删除元素也非常快(而对数组执行添加或删除操作则不是这样)。

一、树的定义

相关术语:

  • 树的结点(node):包含一个数据元素及若干指向子树的分支;
  • 孩子结点(child node):结点的子树的根称为该结点的孩子;
  • 父结点:B 结点是 A 结点的孩子,则 A 结点是 B 结点的双亲;
  • 兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;
  • 祖先结点: 从根到该结点的所经分支上的所有结点
  • 子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
  • 结点层:根结点的层定义为 1;根的孩子为第二层结点,依此类推;
  • 树的深度:树中最大的结点层
  • 结点的度:结点子树的个数
  • 树的度: 树中最大的结点度。
  • 叶子结点:也叫终端结点,是度为 0 的结点;
  • 分枝结点:度不为 0 的结点;
  • 有序树:子树有序的树,如:家族树;
  • 无序树:不考虑子树的顺序;

二叉树是一种特殊的树,它的子节点个数不超过两个。二叉树具有一些特殊的计算性质, 使得在它们之上的一些操作异常高效。

二、叉树和二叉查找树

二叉树每个节点的子节点不允许超过两个。通过将子节点的个数限定为 2,可以写出高效的程序在树中插入、查找和删除数据。

一个父节点的两个子节点分别称为左节点右节点

二叉查找树——确定子节点非常重要。
二叉查找树是一种 特殊的二叉树,相对较小的值保存在左节点中,较大的值保存在右节点中。

2.1 创建二叉查找树

2.1.1 定义的第一个对象就是表示一个节点的Node
function Node(data, left, right) {
    this.data = data; //当前值
    this.left = left; // 左节点
    this.right = right; // 右节点
    this.show = show;
}
function show() {
    return this.data;
}
  1. 设根节点为当前节点。
  2. 如果待插入节点保存的数据小于当前节点,则设新的当前节点为原节点的左节点;反之,执行第 4 步。
  3. 如果当前节点的左节点为 null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。
  4. 设新的当前节点为原节点的右节点。
  5. 如果当前节点的右节点为 null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。
2.1.2 定义包含完整树结构、插入的BST类
function Node(data, left, right) {
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = show;
}

function show() {
  return this.data;
}

function BST() {
  this.root = null;
  this.insert = insert;
  this.inOrder = inOrder;
}

function insert (data) {
    var n = new Node(data, null, null)
    if (this.root == null) {
        this.root = n
    } else {
        var current = this.root
        var parent // 定义当前节点为父节点
        while (true) {
            parent = current
            if (data < current.data) {
                current = current.left; //如果当前节点的左节点为空,再插入
                if (current == null) {
                    parent.left = n
                    break;
                }
            } else {
                current = current.right; //如果当前节点的右节点为空,再插入
                if (current == null) {
                    parent.right = n
                    break;
                }
            }
        }
    }
}

2.2 遍历二叉查找树

现在 BST 类已经初步成型,但是操作上还只能插入节点,我们需要有能力遍历 BST的树。

  1. **中序遍历:**按照节点上的键值,以升序访问 BST 上的所有节点。先访问左子树,再访问根节点,最后访问右子树
  2. **先序遍历:**先访问根节点,然后以同样方式访问左子树右子树
  3. **后序遍历:**先访问叶子节点,从左子树右子树,再到根节点

中序遍历的递归思路:

function inOrder(node) {
    if (!(node == null)) {
        inOrder(node.left);
        node.show()
        inOrder(node.right);
    }
}

中序遍历的访问路径

先序遍历的递归思路:

function preOrder(node) {
    if(!(nide == null)){
        node.show()
        preOrder(node.left)
        preOrder(node.right)
    }
}

先序遍历的访问路径

后序遍历的递归思路:

function preOrder(node) {
    if(!(nide == null)){
        node.show()
        preOrder(node.left)
        preOrder(node.right)
    }
}

后序遍历的访问路径

三种遍历结果对比:

var nums = new BST();
nums.insert(23);
nums.insert(45);
nums.insert(16);
nums.insert(37);
nums.insert(3);
nums.insert(99);
nums.insert(22);
类型 结果
中序:inOrder(nums.root); 3 16 22 23 37 45 99
先序:preOrder(nums.root); 23 16 3 22 45 37 99
后序:postOrder(nums.root); 3 22 16 37 99 45 23

2.3 查询二叉树上的节点

BST通常有下列三种类型的查找:

  • (1) 查找给定值;
  • (2) 查找最小值;
  • (3) 查找最大值:
2.3.1 查找最小值和最大值

查找 BST 上的最小值和最大值非常简单:

  • 因为较小的值总是在左子节点上,所以只需要遍历左子树,直到找到最后一个节点。
  • 因为较大的值总是在右子节点上,所以只需要遍历右子树,直到找到最后一个节点。
/**
 * @description: 查找最小值
 * @param {type}
 * @return:
 */
function getMin() {
    var current = this.root;
    while (current.left != null) {
        current = current.left
    }
    return current.data
}
/**
 * @description: 查找最大值
 * @param {type}
 * @return:
 */
function getMax() {
    var current = this.root;
    while (current.right != null) {
        current = current.right
    }
    return current.data
}
2.3.2 查找某个值是否在树上

BST 上查找给定值,需要比较该值和当前节点上的值的大小。通过比较,就能确定如果给定值不在当前节点时,该向左遍历还是向右遍历。

/**
 * @description: 查找指定值
 * @param {type}
 * @return:
 */
function 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
}

2.4 删除二叉树上的节点

BST 上删除节点的操作最复杂,毕竟你删除的节点包含下面几种特点:

  • 没有子节点
  • 只有一个子节点
  • 包含两个子节点的节点
function remove(data) {
    root = removeNode(this.root, data);
}
function removeNode(node, data) {
    if (node == null) {
        return null;
    }
    if (data == node.data) {
        // 没有子节点的节点
        if (node.left == null && node.right == null) return null;
        // 没有左子节点的节点
        if (node.left == null) return node.right;
        // 没有右子节点的节点
        if (node.right == null) return node.left;
        // 有两个子节点的节点
        var tempNode = getMin(node.right);
        node.data = tempNode.data;
        node.right = removeNode(node.right, tempNode.data);
        return node;
    }else if (data < node.data) {
        node.left = removeNode(node.left, data);
        return node;
    }else {
        node.right = removeNode(node.right, data);
        return node;
    }

二叉树的基本知识就总结到这里,这之后就是不断加深对它的理解,知道可以随意的运用这种数据结构去解决合适的问题,so,刷题去吧~

发布了292 篇原创文章 · 获赞 48 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/jbj6568839z/article/details/104769772