JavaScript中常见数据结构
Posted 圆圆测试日记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript中常见数据结构相关的知识,希望对你有一定的参考价值。
数据结构
- 栈:一种遵从先进后出 (LIFO) 原则的有序集合;新添加的或待删除的元素都保存在栈的末尾,称作栈顶,另一端为栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
- 队列:与上相反,一种遵循先进先出 (FIFO / First In First Out) 原则的一组有序的项;队列在尾部添加新元素,并从头部移除元素。最新添加的元素必须排在队列的末尾。
- 链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的;每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(指针/链接)组成。
- 集合:由一组无序且唯一(即不能重复)的项组成;这个数据结构使用了与有限集合相同的数学概念,但应用在计算机科学的数据结构中。
- 字典:以 [键,值] 对为数据形态的数据结构,其中键名用来查询特定元素,类似于 javascript 中的
Object
。 - 散列:根据关键码值(Key value)直接进行访问的数据结构;它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度;这个映射函数叫做散列函数,存放记录的数组叫做散列表。
- 树:由 n(n>=1)个有限节点组成一个具有层次关系的集合;把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的,基本呈一对多关系,树也可以看做是图的特殊形式。
- 图:图是网络结构的抽象模型;图是一组由边连接的节点(顶点);任何二元关系都可以用图来表示,常见的比如:道路图、关系图,呈多对多关系。
栈
生活中常见的Stack的例子比如一摞书,你最后放上去的那本你之后会最先拿走;又比如浏览器的访问历史,当点击返回按钮,最后访问的网站最先从历史记录中弹出。
Stack一般具备以下方法:
- push:将一个元素推入栈顶
- pop:移除栈顶元素,并返回被移除的元素
- peek:返回栈顶元素
- isEmpty(): 栈中是否有元素
- clear(): 移除栈中所有元素
- size:返回栈中元素的个数
以对象形式实现栈
<script type="text/javascript"> function Stack() { this.count = 0; this.storage = {}; //将一个元素推入栈顶 this.push = function (value) { this.storage[this.count] = value; this.count++; } //移除栈顶元素,并返回被移除的元素 this.pop = function () { if (this.count === 0) { return undefined; } this.count--; var result = this.storage[this.count]; delete this.storage[this.count]; return result; } //返回栈顶元素 this.peek = function () { return this.storage[this.count - 1]; } //栈中是否有元素 this.isEmpty = function () { //使用es6语法判断对象中属性长度 //return Object.keys(this.storage).length==0; return this.count==0; } //移除栈中所有元素 this.clear = function () { this.count = 0 //return this.storage={}; } //返回栈中元素的个数 this.size = function () { return this.count; } } var newStack = new Stack(); newStack.push("第一个元素"); newStack.push("第二个元素"); newStack.push("第三个元素"); console.log("打印栈中元素个数:" + newStack.size()); console.log("打印栈中栈顶元素:" + newStack.peek()); console.log("打印栈中移除元素:" + newStack.pop()); console.log("移除栈顶元素后再次打印栈中栈顶元素:" + newStack.peek()); console.log("判断栈中是否有元素:" + newStack.isEmpty()); console.log("移除栈中所有元素:" + newStack.clear()); console.log("移除后判断栈中是否有元素:" + newStack.isEmpty()); console.log("打印栈中移除元素:" + newStack.pop()); </script>
以数组形式实现栈
<script type="text/javascript"> function Stack() { //保存栈内元素的数组 this.dataStore = []; //top用来记录栈顶位置,初始化为0 this.top = 0; this.push = function (element) { this.dataStore[this.top++] = element; // 先在top位置加入元素,之后top加1 } this.pop = function () { // top先减1,然后返回top位置的元素 return this.dataStore[--this.top]; } this.peek = function peek() { return this.dataStore[this.top - 1]; } this.isEmpty = function clear() { return this.top ==0; } this.clear = function clear() { this.top = 0; } this.length = function length() { return this.top; } } var newStack = new Stack(); newStack.push("第一个元素"); newStack.push("第二个元素"); newStack.push("第三个元素"); console.log("打印栈中元素个数:" + newStack.length()); console.log("打印栈中栈顶元素:" + newStack.peek()); console.log("打印栈中移除元素:" + newStack.pop()); console.log("移除栈顶元素后再次打印栈中栈顶元素:" + newStack.peek()); console.log("判断栈中是否有元素:" + newStack.isEmpty()); console.log("移除栈中所有元素:" + newStack.clear()); console.log("移除后判断栈中是否有元素:" + newStack.isEmpty()); </script>
Queue(队列)
Queue和Stack有一些类似,不同的是Stack是先进后出,而Queue是先进先出。Queue在生活中的例子比如排队上公交,排在第一个的总是最先上车;又比如打印机的打印队列,排在前面的最先打印。
Queue一般具有以下常见方法:
- enqueue:入列,向队列尾部增加一个元素
- dequeue:出列,移除队列头部的一个元素并返回被移除的元素
- front:获取队列的第一个元素
- isEmpty:判断队列是否为空
- size:获取队列中元素的个数
Javascript中的Array已经具备了Queue的一些特性,所以我们可以借助Array实现一个Queue类型:
<script type="text/javascript"> function Queue() { var collection = []; //返回队列所有元素 this.print = function () { return collection; } //入列,向队列尾部增加一个元素 this.enqueue = function (element) { collection.push(element); } //出列,移除队列头部的一个元素并返回被移除的元素 this.dequeue = function () { return collection.shift(); } //获取队列的第一个元素 this.front = function () { return collection[0]; } //判断队列是否为空 this.isEmpty = function () { return collection.length === 0; } //清除队列 this.clear = function () { items = []; }; //获取队列中元素的个数 this.size = function () { return collection.length; } } var newQueue= new Queue(); newQueue.enqueue("第一个元素"); newQueue.enqueue("第二个元素"); newQueue.enqueue("第三个元素"); console.log("打印队列中元素个数:" + newQueue.size()); console.log("打印队列中第一个元素:" + newQueue.front()); console.log("打印队列中所有元素:" + newQueue.print()); console.log("打印队列中移除元素:" + newQueue.dequeue()); console.log("移除队列元素后再次打印队列中所有元素:" + newQueue.print()); console.log("判断队列中是否有元素:" + newQueue.isEmpty()); console.log("移除队列中所有元素:" + newQueue.clear()); console.log("移除后判断队列中是否有元素:" + newQueue.isEmpty()); </script>
Priority Queue(优先队列)
Queue还有个升级版本,给每个元素赋予优先级,优先级高的元素入列时将排到低优先级元素之前。区别主要是enqueue
方法的实现:
<script type="text/javascript"> function PriorityQueue() { var collection = []; //返回队列所有元素 this.print = function () { return collection; } //入列,向队列尾部增加一个元素 this.enqueue = function (element) { collection.push(element); } //出列,移除队列头部的一个元素并返回被移除的元素 this.dequeue = function () { return collection.shift(); } //获取队列的第一个元素 this.front = function () { return collection[0]; } //判断队列是否为空 this.isEmpty = function () { return collection.length === 0; } //清除队列 this.clear = function () { items = []; }; //获取队列中元素的个数 this.size = function () { return collection.length; } //优先队列 this.priorityEnqueue = function (element) { if (this.isEmpty()) { collection.push(element); } else { var added = false; for (var i = 0; i < collection.length; i++) { if (element[1] < collection[i][1]) { collection.splice(i, 0, element); added = true; break; } } if (!added) { collection.push(element); } } } } var newQueue = new PriorityQueue(); newQueue.enqueue("第一个元素"); newQueue.enqueue("第二个元素"); newQueue.enqueue("第三个元素"); console.log("打印队列中元素个数:" + newQueue.size()); console.log("打印队列中第一个元素:" + newQueue.front()); console.log("打印队列中所有元素:" + newQueue.print()); console.log("打印队列中移除元素:" + newQueue.dequeue()); console.log("移除队列元素后再次打印队列中所有元素:" + newQueue.print()); console.log("判断队列中是否有元素:" + newQueue.isEmpty()); console.log("移除队列中所有元素:" + newQueue.clear()); console.log("移除后判断队列中是否有元素:" + newQueue.isEmpty()); newQueue.priorityEnqueue([\'gannicus\', 3]); newQueue.priorityEnqueue([\'spartacus\', 1]); newQueue.priorityEnqueue([\'crixus\', 2]); newQueue.priorityEnqueue([\'oenomaus\', 4]); console.log("优先队列中所有元素:" + newQueue.print()); </script>
Linked List(链表)
链表是一种链式数据结构,链上的每个节点包含两种信息:节点本身的数据和指向下一个节点的指针。链表和传统的数组都是线性的数据结构,存储的都是一个序列的数据,但也有很多区别,如下表:
比较维度 | 数组 | 链表 |
---|---|---|
内存分配 | 静态内存分配,编译时分配且连续 | 动态内存分配,运行时分配且不连续 |
元素获取 | 通过Index获取,速度较快 | 通过遍历顺序访问,速度较慢 |
添加删除元素 | 因为内存位置连续且固定,速度较慢 | 因为内存分配灵活,只有一个开销步骤,速度更快 |
空间结构 | 可以是一维或者多维数组 | 可以是单向、双向或者循环链表 |
一个单向链表通常具有以下方法:
- size:返回链表中节点的个数
- head:返回链表中的头部元素
- add:向链表尾部增加一个节点
- remove:删除某个节点
- indexOf:返回某个节点的index
- elementAt:返回某个index处的节点
- addAt:在某个index处插入一个节点
- removeAt:删除某个index处的节点
单向链表的Javascript实现:
<script type="text/javascript"> /** * 链表中的节点 */ function Node(element) { // 节点中的数据 this.element = element; // 指向下一个节点的指针 this.next = null; } function LinkedList() { var length = 0; var head = null; //返回链表中节点的个数 this.size = function () { return length; } //返回链表中的头部元素 this.head = function () { return head; } //向链表尾部增加一个节点 this.add = function (element) { var node = new Node(element); if (head == null) { head = node; } else { var currentNode = head; while (currentNode.next) { currentNode = currentNode.next; } currentNode.next = node; } length++; } //删除某个节点 this.remove = function (element) { var currentNode = head; var previousNode; if (currentNode.element === element) { head = currentNode.next; } else { while (currentNode.element !== element) { previousNode = currentNode; currentNode = currentNode.next; } previousNode.next = currentNode.next; } length--; } this.isEmpty = function () { return length === 0; } //返回链表所有节点 this.value = function () { var currentNode = head; var nodeList = []; if (currentNode==null) { return undefined } else { while (currentNode.next) { nodeList.push(currentNode.element); currentNode = currentNode.next; } nodeList.push(currentNode.element); } return nodeList; } //返回某个节点的index this.indexOf = function (element) { var currentNode = head; var index = -1; while (currentNode) { index++; if (currentNode.element === element) { return index; } currentNode = currentNode.next; } return -1; } //返回某个index处的节点 this.elementAt = function (index) { var currentNode = head; var count = 0; while (count < index) { count++; currentNode = currentNode.next; } return currentNode.element; } //在某个index处插入一个节点 this.addAt = function (index, element) { var node = new Node(element); var currentNode = head; var previousNode; var currentIndex = 0; if (index > length) { return false; } if (index === 0) { node.next = currentNode; head = node; } else { while (currentIndex < index) { currentIndex++; previousNode = currentNode; currentNode = currentNode.next; } node.next = currentNode; previousNode.next = node; } length++; } //删除某个index处的节点 this.removeAt = function (index) { var currentNode = head; var previousNode; var currentIndex = 0; if (index < 0 || index >= length) { return null; } if (index === 0) { head = currentIndex.next; } else { while (currentIndex < index) { currentIndex++; previousNode = currentNode; currentNode = currentNode.next; } previousNode.next = currentNode.next; } length--; return currentNode.element; } } var newLinkedList = new LinkedList(); newLinkedList.indexOf("第一个节点"); newLinkedList.add("第一个节点"); newLinkedList.add("第二个节点"); newLinkedList.add("第三个节点"); newLinkedList.add("第四个节点"); newLinkedList.add("第五个节点"); newLinkedList.add("第六个节点"); newLinkedList.add("第七个节点"); newLinkedList.add("第八个节点"); console.log("打印链表中所有元素:" + newLinkedList.value()); newLinkedList.remove("第一个节点"); newLinkedList.remove("第三个节点"); console.log("打印删除后链表中所有元素:" + newLinkedList.value()); </script>
单链表
talk is easy, show the code, 下面用 JavaScript 实现一个相对完整的单链表,并提供以下方法供调用:
- push(element) // 链表尾部插入节点
- pop() // 链表尾部删除节点
- shift() // 删除头部节点、
- unshift(element) // 插入头部节点
- find(index) // 查找指定位置节点
- insert(element, index) // 指定位置插入节点
- edit(element, index) // 修改指定位置节点
- delete(index) // 链表删除指定位置节点
- cycle() // 使链表首尾成环
function initList() { class Node { constructor(item) { this.element = item } } class List { constructor() { this.head = null this.size = 0 this.last = null } /** * 链表查找元素 * @param index 查找的位置 */ find(index) { let current = this.head for (let i = 0; i < index; i++) { current = current.next } return current } /** * 链表尾部插入元素 * @param element 插入的元素 */ push(element) { let newNode = new Node(element) if (this.size === 0) { this.head = newNode this.head.next = null this.last = this.head } else { this.last.next = newNode this.last = newNode newNode.next = null } this.size += 1 } /** * 链表尾部删除元素 * @param element 删除的位置 */ pop(element) { this.last.next = null } /** * 链表头部删除元素 */ shift() { if (this.size === 0) return this.head = this.head.next if (this.size === 1) this.last = null this.size -= 1 } /** * 链表头部插入元素 * @param element 插入的元素 */ unshift(element) { let newNode = new Node(element) newNode.next = this.head this.head = newNode if (this.size === 0) this.last = this.head this.size += 1 } /** * 链表插入元素 * @param element 插入的位置, index 插入的位置 */ insert(element, index) { if (index < 0 || index > this.size) { console.error(\'超出链表节点范围\') return } let newNode = new Node(element) if (this.size === 0) { // 空链表 newNode.next = null this.head = newNode this.last = newNode } else if (index === 0) { // 插入头部 newNode.next = this.head this.head = newNode } else if (index == this.size) { //插入尾部 newNode.next = null this.last.next = newNode this.last = newNode } else { // 中间插入 let preNode = this.find(index - 1) newNode.next = preNode.next preNode.next = newNode } this.size += 1 } /* *链表编辑元素 * @param element 编辑的元素,index 元素位置 */ edit(element, index) { let current = this.find(index) current.element = element } /* *链表删除元素 * @param index 删除元素位置 */ delete(index) { let current = this.find(index) if (index === 0) { // 删除头节点 this.head = this.head.next } else if (index === ((this.size) - 1)) { // 删除尾节点 let preNode = this.find(index - 1) preNode.next = null } else { // 删除中间节点 let preNode = this.find(index - 1) let nextNode = preNode.next.next let removeNode = preNode.next preNode.next = nextNode } this.size -= 1 } /* *链表使首尾成环 */ cycle() { this.last.next = this.head } } return new List() } let list = initList()
双向链表
双向链表的特点就是添加了指向上一个节点的指针(prev),比较单链表来说,稍微复杂一些,也更强大,这里把上面的单链表修改一下。
function initList() { class Node { constructor(item) { this.element = item this.next = null this.prev = null } } class List { constructor() { this.head = null this.size = 0 this.last = null } /** * 链表查找元素 * @param index 查找的位置 */ find(index) { let current = this.head for (let i = 0; i < index; i++) { current = current.next } return current } /** * 链表尾部插入元素 * @param element 插入的元素 */ push(element) { let newNode = new Node(element) if (this.size === 0) { this.head = newNode this.head.next = null this.last = this.head } else { this.last.next = newNode newNode.next = null newNode.prev = this.last this.last = newNode } this.size += 1 } /*以上是关于JavaScript中常见数据结构的主要内容,如果未能解决你的问题,请参考以下文章