数据结构与算法---二叉树与二叉查找树

86 阅读4分钟

树是计算机科学中经常用到的一种数据结构。树是一种非线性的数据结构,以分层的方式存储数据。这里介绍一种特殊的树:二叉树。 二叉树的优点:添加、删除、查找等操作非常快。

二叉树和二叉查找树

二叉树是指树的节点不超过2个,分别为左节点和右节点。二叉查找树是一种特殊的二叉树,相对较小的值保存在左节点,较大的值保存在右节点中,即左节点小于当前节点节点小于右节点BxhJ8VOMZp.jpg

实现二叉查找树

二叉查找树由节点组成,所以要定义的第一个对象就是Node,其次建立一个二叉查找树BST,BST要有一个insert方法用来向树中插入新节点。步骤如下:

  1. 设根节点为当前节点。
  2. 如果待插入节点保存的数据小于当前节点,则设新的当前节点为原节点的左节点;反之,执行第 4 步。
  3. 如果当前节点的左节点为 null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。
  4. 设新的当前节点为原节点的右节点。
  5. 如果当前节点的右节点为 null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。
function Node(data,left,right) {
    this.data = data;
    this.left = left;
    this.right = right;
    this.show = show;
}

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

function show() {
    return this.data;
}

function insert(data) {
    const n = new Node(data,null,null);
    if(this.root === null) {
        this.root = n;
    } else {
        let current = this.root;
        let parent;
        while(true) {
            parent = current;
            if(data < current.data) {
                current = current.left;
                if(current === null) {
                    parent.left = n
                }
            } else {
                current =current.right;
                if(current === null) {
                    parent.right = n
                }
            }
        }
    }
}

遍历二叉查找树

遍历BST的三种方式:中序、先序和后序。中序遍历按照节点上的键值,以升序访问BST上的所有节点。先序遍历先访问根节点,然后以同样方式访问左子树和右子树。后序遍历先访问叶子节点,从左子树到右子树,再到根节点。

中序遍历

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

先序遍历

function preOrder(node) {
    if(!(node == null)) {
        console.log(node.show());
        preOrder(node.left);
        preOrder(node.right);
    }
}

后序遍历

function postOrder(node) {
    if(!(node == null)) {
        postOrder(node.left);
        postOrder(node.right);
        console.log(node.show());
    }
}

在二叉查找树上进行查找

BST通常有下列三种类型的查找:查找给定值、查找最小值、查找最大值。

查找最小值

较小的值总是在左子节点上,在BST上查找最小值,只需要遍历左子树,直到找到最后一个节点。

function getMins() {
    var current = this.root;
    while(current.left !== null) {
        current = current.left;
    }
    return current.data;
}

查找最大值

查找最大值同理。

function getMaxs() {
    var current = this.root;
    while(current.right !== null) {
        current = current.right;
    }
    return current.data;
}

查找给定值

查找给定值,需要比较该值和当前节点上的值的大小。

function find(data) {
    let 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
}

删除节点

删除节点的操作比较复杂,复杂程度取决于删除哪个节点。为了管理删除操作的复杂度,我们使用递归操作,同时定义两个方法:remove()和removeNode()。

  1. 判断当前节点是否包含待删除的数据,如果包含,则删除该节点;如果不包含,比较当前节点上的数据和待删除的数据。如果待删除数据小于当前节点上的数据,则移至当前节点的左子节点继续比较,如果删除数据大于当前节点上的数据,则移至当前节点的右子节点继续比较。
  2. 待删除节点是叶子节点(没有子节点的节点),只需要从父节点指向它的链接指向null。
  3. 待删除节点只包含一个子节点,将原本指向它的节点指向它的子节点。
  4. 待删除节点包含两个子节点,可以选用两种方式:一个是查找待删除节点左子树上的最大值,另一个是查找其右子树上的最小值。 4GbFZyud54.jpg
function remove(data) {
    this.root = removeNode(this.root,data);
}

function getSmallest(node) {
    if (node === null) {
        return null;
    }
    while (node.left !== null) {
        node = node.left;
    }

    return node;
}
function removeNode(node,data) {
    if(node === null) {
        return null;
    }
    if(data === node.data) {
        // node是叶子节点
        if(node.left === null && node.right === null) {
            return null;
        }
        // node没有左子节点
        else if(node.left === null) {
            return node.right;
        }
        // node没有右子节点
        else if(node.right === null) {
            return node.left;
        }
        // node有两个子节点,查找右子节点的最小值
        const tempNode = getSmallest(node.right);
        node.data = tempNode.data;
        node.right = removeNode(node.right,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;
    }
}