数据结构与算法之二叉树

Posted simpul

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法之二叉树相关的知识,希望对你有一定的参考价值。

排序二叉树

排序二叉树要求父节点的值大于左节点的值,小于有节点的值。
没有父亲节点的节点称为根节点,没有子节点的节点称为叶子节点,其他都称为中间节点。

用JS实现一个排序二叉树

function BinaryTree()
    this.root = null;  //初始化根节点为null

BinaryTree.prototype = 
    constructor: BinaryTree,
    insert(key)
        var Node = function(key)  //定义一个节点类
            this.key = key;
            //左右节点初始化为null
            this.right = null;  
            this.left = null;
        
        var newNode = new Node(key);  //将传入的数值实例化为一个节点
        if(this.root === null)
            this.root = newNode;
        else
            this.insertNode(this.root, newNode);
        
    ,
    insertNode(node, newNode)  //根据排序二叉树的规则进行节点的插入
        if(node.key < newNode.key)  
            if(node.right === null)
                node.right = newNode;
            else
                this.insertNode(node.right, newNode);
            
        else
            if(node.left === null)
                node.left = newNode;
            else
                this.insertNode(node.left, newNode);
            
        
    

var nodes = [8, 3, 10, 1, 6, 14, 4, 7, 13];
var binaryTree = new BinaryTree();
nodes.forEach((i)=>
    binaryTree.insert(i);
);
console.log(binaryTree); 

遍历方式

  1. 前序遍历(中间节点→左节点→右节点)
  2. 中序遍历(左节点→中间节点→右节点)
  3. 后序遍历(左节点→右节点→中间节点)

技术图片

前序遍历的作用是复制一棵二叉树,它的效率要比重新插入节点来构造要快得多。(8→3→1→6→4→7→10→14→13)

BinaryTree.prototype.preOrderTraverse = function(callback)  //回调函数用于处理遍历的节点值
    this.preOrderTraverseNode(this.root, callback);

BinaryTree.prototype.preOrderTraverseNode = function(node, callback)
    if(node !== null)
        callback(node.key);
        this.preOrderTraverseNode(node.left, callback);
        this.preOrderTraverseNode(node.right, callback);
    


var callback = function(key)
    console.log(key);

binaryTree.preOrderTraverse(callback);

对于排序二叉树来说,中序遍历的结果是升序排序。(1→3→4→6→7→8→10→13→14)

BinaryTree.prototype.inOrderTraverse = function(callback)  //回调函数用于处理遍历的节点值
    this.inOrderTracerseNode(this.root, callback);

BinaryTree.prototype.inOrderTraverseNode = function(node, callback)
    if(node !== null)
        this.inOrderTraverseNode(node.left, callback);
        callback(node.key);
        this.inOrderTraverseNode(node.right, callback);
    


var callback = function(key)
    console.log(key);

binaryTree.inOrderTraverse(callback);

后序遍历

BinaryTree.prototype.postOrderTraverse = function(callback)  //回调函数用于处理遍历的节点值
    this.postOrderTracerseNode(this.root, callback);

BinaryTree.prototype.postOrderTraverseNode = function(node, callback)
    if(node !== null)
        this.postOrderTraverseNode(node.left, callback);
        this.postOrderTraverseNode(node.right, callback);
        callback(node.key);
    


var callback = function(key)
    console.log(key);

binaryTree.postOrderTraverse(callback);

总结

在基于上述的内容我还自己拓展了其他的一些方法(最小最大值,查找节点,删除节点,计算节点数和边数,返回计数对象),并用自己的思路从新将排序二叉树写了一遍,经过测试应该是没有大问题的。

//建立二叉树类
function BinaryTree()
    //初始化根节点为null
    this.root = null;


//建立节点对象(添加到静态方法中)
BinaryTree.Node = function(key)
    this.key = key;
    this.right = null;
    this.left = null;
    this.count = 1;
    this.show = function()
        return this.key;
    


BinaryTree.prototype = 
    constructor: BinaryTree,

    //插入节点
    insert(key)
        if(this.isContain(key))
            this.search(key).count += 1;
        else
            var node = new BinaryTree.Node(key);
            if(this.root === null)
                this.root = node;
            else
                this.insertNode(this.root, node);
                               
        
    ,
    insertNode(ref, node)
        //根据排序二叉树的规律,左子节点要比父节点小,右子节点要比父节点大
        if(ref.key > node.key)
            if(ref.left === null)
                ref.left = node;
            else
                this.insertNode(ref.left, node);
            
        else
            if(ref.right === null)
                ref.right = node;
            else
                this.insertNode(ref.right, node);
            
        
    ,

    //前序遍历
    preOrder(cb)  //回调函数用来处理每一个遍历的键值
        this.preOrderNode(this.root, cb);
    ,
    preOrderNode(node, cb)
        //根据前序遍历的规律,先中间,再左边,最后右边
        if(node !== null)
            cb(node.key);
            this.preOrderNode(node.left, cb);
            this.preOrderNode(node.right, cb);
        
    ,

    //中序遍历
    inOrder(cb)
        this.inOrderNode(this.root, cb);
    ,
    inOrderNode(node, cb)
        //根据中序遍历的规律,先左边,再中间,最后右边
        if(node !== null)
            this.inOrderNode(node.left, cb);
            cb(node.key);
            this.inOrderNode(node.right, cb);
        
    ,

    //后序遍历
    postOrder(cb)
        this.postOrderNode(this.root, cb);
    ,
    postOrderNode(node, cb)
        //根据后序遍历的规律,先左边,再右边,最后中间
        if(node !== null)
            this.postOrderNode(node.left, cb);
            this.postOrderNode(node.right, cb);
            cb(node.key);
        
    ,

    //取得键值最小的节点
    getMin()
        //取最左边的子节点
        var node = this.root;
        while(node.left !== null)
            node = node.left;
        
        return node;
    ,

    //取得键值最大的节点
    getMax()
        //取最右边的子节点
        var node = this.root;
        while(node.right !== null)
            node = node.right;
        
        return node;
    ,

    //搜索二叉树中是否包含该键值的节点
    isContain(key)
        return this.isContainNode(this.root, key);
    ,
    isContainNode(node, key)
        if(node === null) return false;
        if(node.key === key)
            return true;
        else if(node.key > key)
            return this.isContainNode(node.left, key);
        else
            return this.isContainNode(node.right, key);
        
    ,

    //给定键值,返回对应的节点
    search(key)
        return this.searchNode(this.root, key);
    ,
    searchNode(node, key)
        if(node === null) return null;
        if(node.key === key)
            return node;
        else if(node.key > key)
            return this.searchNode(node.left, key);
        else
            return this.searchNode(node.right, key);
        
    ,

    //给定键值,返回对象,包含父节点和方向
    getParent(key)
        var node = this.search(key);
        return node ? this.getParentNode(this.root, node) : node;
    ,
    getParentNode(node, child)
        if(node === null) return null;
        if(node.left === child)
            return parent: node, direction: 'left';
        else if(node.right === child)
            return parent: node, direction: 'right';
        else if(child.key > node.key)
            return this.getParentNode(node.right, child);
        else
            return this.getParentNode(node.left, child);
        
    ,

    //删除给定键值的节点
    remove(key)
        var node = this.search(key);
        if(node !== this.root)
            var parent, direction = this.getParent(key);
        
        if(node)
            if(node.right === null && node.left === null)
                //要删除的节点是一个叶子节点
                parent[direction] = null;
            else if(node.right === null)
                //只含有左子节点
                parent[direction] = node.left;
            else if(node.left === null)
                //只含有右子节点
                parent[direction] = node.right;
            else
                //含有左右两个子节点(选取左子树的最大值或右子树的最小值替换,这里取后者)
                var maxNode = node;
                while(maxNode.right !== null)
                    maxNode = maxNode.right;
                
                //得到右子树最小值的节点后,先把它自身从二叉树中删除,再把它的键值赋给要删除的节点
                this.remove(maxNode.key);
                node.key = maxNode.key;
            
        else
            console.log("没有找到相应的节点");
        
    ,

    //返回节点总数
    sum()
        var sum = 0;
        this.preOrder(()=>sum++;);
        this.sum = sum;
        return sum;
    ,

    //返回边的总数
    edges()
        this.edges = 0;
        this.countEdges(this.root);
        return this.edges;
    ,
    countEdges(node)
        if(node === null) return false;
        if(node.right !== null)
            this.edges++;
            this.countEdges(node.right);
        
        if(node.left !== null)
            this.edges++;
            this.countEdges(node.left);
        
    ,

    //返回一个对象,属性名是二叉树中节点的键值,属性值是该键值被插入的次数count,用中序法排序
    count()
        var obj = ;
        var cb = function(key)
            obj[key] = this.search(key).count;  //这里实际上有性能浪费,排序遍历一次,search又遍历了一次,最好是修改排序遍历中的方法,让它遍历节点而不是键值
        
        this.inOrder(cb.bind(this));
        return obj;
    

以上是关于数据结构与算法之二叉树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法之二叉树

JAVA数据结构与算法之二叉树

数据结构与算法实验报告之二叉树高度的求解

数据结构与算法实验报告之二叉树高度的求解

JavaScript--数据结构与算法之二叉树

数据结构与算法之二叉树 ——in dart