数据结构 -- 栈队列树

Posted 我真的爱敲代码

tags:

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


线性表

线性表是一种可以在任意位置进行插入和删除数据元素操作的、由n(n>=0)个相同类型数据元素a0、a1、a2,…,a(n-1)组成的线性结构。

线性表是一种最简单的线性结构。

特点:除第一个和最后一个元素以外,每个数据元素只有一个前驱数据元素和一个后继数据元素。


堆栈和队列都是特殊的线性表。
线性表、堆栈和队列三者的数据元素之间的逻辑关系完全相同,差别是:线性表的插入和删除操作不受限制,而堆栈只能在栈顶插入和删除,队列只能在队尾插入,在队头删除。
堆栈和队列在各种类型的软件中应用十分广泛,堆栈可以用来完成数据元素序列的特定转换,队列可以用作数据元素序列的缓冲存储。

堆栈是一种特殊的线性表,堆栈中的数据元素以及数据元素之间的逻辑关系和线性表完全相同。差别是:线性表允许在任意位置插入和删除元素操作。而堆栈只允许在固定一端进行插入和删除数据元素的操作。

栈中允许进行插入和删除元素操作的一端称为栈顶,另一端称为栈底。栈顶的当前元素位置是动态的,用于标记栈顶当前位置的变量称为栈顶指示器(或栈顶指针)。

堆栈的插入操作通常称为进栈或入栈,删除操作通常称为出栈或退栈。

  • 后进先出
  • 在同一端插入和删除
function Stack() {
    // 用数组来模拟栈
    // @type {Array}
    var items = [];

    /**
     * 将元素送入栈,放置于数组的最后一位(末位入栈,后进先出)
     * @param {Any} element  接收的元素,不限制类型
    */
   this.push = function(element) {
       items.push(element)
   };


    /**
   弹出栈顶元素
   @return {Any}   返回被弹出的值
    */
   this.pop = function() {
       return items.pop();
   };

   /**
    * 查看栈顶元素
    * @return {Any}  返回栈顶元素
    */
   this.peek = function() {
       return items[items.length-1];
   }
   

   /**
    * 确定栈是否为空
    * @return {Boolean}   若栈为空则返回true,否则则返回false
    */
   this.isAmpty = function() {
       return items.length === 0
   };


   /**
    * 清空栈中所有内容
    */
   this.clear = function() {
       items = [];
   };

   /**
    * 返回栈的长度
    * @return {Number}  栈的长度
    */
   this.size = function() {
       return items.length;
   };

   /**
    * 以字符串显示栈中所有内容
    */
   this.print = function() {
       console.log(items.toString());
   };

   /**
    * 将10进制数字转为2进制数字
    * 原理:输入要转换的数字,不断地除以2并取整。并且在最后运用while循环,将栈中所有数字拼接成字符串输出
    * @param {Number} decNumber  要转换的10进制数字
    * @return {Number}           要转换后的2进制数字
    */
   function divideBy2(decNumber) {
       var remStack = new Stack(),
       rem,
       binaryString = '',

       while (decNumber > 0) {
           rem = Math.floor(decNumber % 2);
           remStack.push(rem);
           decNumber = Math.floor(decNumber / 2);
       }

       while (!remStack.isAmpty) {
           binaryString += remStack.pop().toString();
       }

       return binaryString;
   };

   console.log(divideBy2(10));      
}

队列

队列也是一种特殊的线性表,队列的数据元素及数据元素之间的逻辑关系和线性表的完全相同。差别是:线性表允许在任意位置插入和删除元素

  • 先进先出
  • 队尾插入队头删除
function Queue() {

    var items = [];

    // 将元素推入列
    this.enqueue = function (ele) {
        items.push(ele);
    };

    // 将队列的第一个元素弹出
    this.dequeue = function () {
        return items.shift()
    };

    // 查看队列的第一个元素
    this.front = function () {
        return items[0];
    }

    // 确定队列是否为空
    this.isAmpty = function () {
        return items.length === 0;
    };

    // 返回队列的长度
    this.size = function() {
        return items.length;
    }
    
    // 清空队列中所有内容
    this.clear = function() {
        items = [];
    };

    // 以字符串显示队列中所有内容
    this.print = function() {
        console.log(items.toString());
    };
}

// 击鼓传花小游戏
/**
 * @description: 
 * @param {Array} nameList    参与人员列表
 * @param {Number} num        在循环中要被弹出的位置
 * @return {String}           返回赢家(也就是最后活下来的那个)
 */
function hotPotato(nameList,num) {
   var queue = new Queue();
   
   for(var i = 0; i< nameList.length; i++) {
       queue.enqueue = (nameList[i]);       //出队的人从队头再入队
   }

   var eliminated = '';

   while (queue.size() > 1) {
       for (var i = 0; i<num; i++) {
           queue.enqueue(queue.dequeue());
       }

       eliminated = queue.dequeue();
       console.log(eliminated + "Get out!")
   }
   return '胜利者'+queue.dequeue()
}

var nameList = ['小明', '小红', '小王', '小强']


console.log(hotPotato(nameList,3))

二叉树

数据结构可分为线性结构和非线性结构两大类。
树结构包括树和二叉树。树和二叉树属于非线性结构。
在树结构中,每个节点只允许有一个直接前驱结点,但允许有一个以上直接后继节点。
大多二叉树的操作实现需要用递归方法。

二叉树是n(n>=0)个有限结点构成的集合。

二叉树中所有结点的形态共有5种:空结点、无左右子树结点、只有左子树结点、只有右子树结点和左右子树均存在的结点。

满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,则这样的二叉树称作满二叉树。

完全二叉树:如果一棵具有n个结点的二叉树的结构与满二叉树的前n个结点的结构相同,这样的二叉树称作完全二叉树。

var tree = {
    value: 1,
    left: {
        value: 4,
        left: {
            value: 6
        },
        right: {
            value: 9,
            left: {
                value: 15
            }
        }
    },
    right: {
        value: 35,
        left: {
            value: 30,
            right: {
                value: 45
            }
        }
    }
}
var preOrder = function(node) {
    if(node) {
        console.log(node.value)
        preOrder(node.left)
        preOrder(node.right)
    }
}
preOrder(tree)
/**
 * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树
 * 假设输入的前序遍历和中序遍历的结果都不包含重复的数字
 * 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},
 * 则重建二叉树并返回
 * 
 * 输入:    前序:[1,2,3,4,5,6,7],
 *          中序:[3,2,4,1,6,5,7]
 * 
 * 输出:
 *          层次遍历:[1,2,5,3,4,6,7]
 */

/**
 * 二叉树的结构
 * val:表示节点的值
 * left:表示左子树
 * right:表示右子树
 */

class binaryTree {
    constructor(v,l,r) {
        this.val = v;
        this.left = l;
        this.right = r
    }
}


function reConstructBinaryTree(pre,next) {
    // 递归出口
    if(pre.length == 0 ||next.length == 0 || pre.length != next.length) {
        return null;
    }
    // 拿到根节点
    let root = new binaryTree(pre[0]);

    // 找的根节点在中序遍历中的位置
    let i = 0;
    while (next[i] != root.val) {
        i++;
    }
    // 确定左子树前序遍历长度
    preLeft = new Array(i);
    // 确定左子树中序遍历长度
    inLeft = new Array(i);

    // 确定右子树前序遍历长度              ????
    let preRight = new Array(next.length - i - 1);
    // 确定右子树中序遍历长度
    let inRight = new Array(next.length - i - 1);

    // 遍历依次拿到左右子树   前中序遍历的值
    for (let j=0; j< next.length; j++) {
        if (j < i) {
            preLeft[j] = pre[j+1];
            inLeft[j] = next[j];
        }else if(j > i) {
            preRight[j-i-1] = pre[j];
            inRight[j-i-1] = next[j];
        }
    }
    // 递归
    root.left = reConstructBinaryTree(preLeft,inLeft);
    root.right = reConstructBinaryTree(preRight,inRight);
    return root;
}

var pre_arr = []         //存放前序遍历序列
var in_arr = []          //存放中序遍历序列
var post_arr = []        //存放后序遍历序列
var level_arr = []       //存放层次遍历序列

/**
 * D:访问根节点,L:遍历根节点的左子树,R:遍历根节点的右子树
 * 前序遍历:DLR
 */
 var preOrder = function(node) {
    if(node) {
        pre_arr.push(node.val)
        preOrder(node.left)
        preOrder(node.right)
    }
}
/**
 * D:访问根节点,L:遍历根节点的左子树,R:遍历根节点的右子树
 * 中序遍历:LDR
 */
 var inOrder = function(node) {
    if(node) {
        inOrder(node.left);
        in_arr.push(node.val);
        inOrder(node.right);
    }
}
/**
 * D:访问根节点,L:遍历根节点的左子树,R:遍历根节点的右子树。
 * 后序遍历:LRD
 */
 var postOrder = function (node) { 
    if (node) {
     postOrder(node.left);
     postOrder(node.right);  
     post_arr.push(node.val);
    }
}
l
/**
 * 广度优先遍历(层次遍历)
 * 广度优先遍历是从二叉树的第一层(根节点)开始,自上至下逐层遍历:在同一层中,按照从左到右的顺序对节点注意访问
 */
var levelOrderTraversal = function(node) {
    if(!node) {
        throw new Error('Empty Tree')
    }
    var que = []
    que.push(node)
    while (que.length !== 0) {
        node = que.shift()
        level_arr.push(node.val)
        if(node.left) que.push(node.left)
        if(node.right) que.push(node.right)
    }
}



// 测试
a = [1,2,3,4,5,6,7];
b = [3,2,4,1,6,5,7];
root = reConstructBinaryTree(a,b);

preOrder(root)
console.log('前序序列是:'+pre_arr);
inOrder(root)
console.log('中序序列是:'+in_arr);
postOrder(root)
console.log('后序序列是:'+post_arr);
levelOrderTraversal(root)
console.log('层序序列是:'+level_arr);

以上是关于数据结构 -- 栈队列树的主要内容,如果未能解决你的问题,请参考以下文章

基础数据结构 例:栈队列链表数据字典树等

数据结构-栈,队列,链表,树

JDK常用数据结构

数据结构:栈队列数组链表红黑树

用JS实现栈队列二叉树遍历等操作

用JS实现栈队列二叉树遍历等操作