数据结构 -- 栈队列树
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);
以上是关于数据结构 -- 栈队列树的主要内容,如果未能解决你的问题,请参考以下文章