目录

  • 列表(链表)
  • 数组
  • 队列
  • 哈希表
  • 二叉查找树

分享一句话:算法即原力,即正义


列表(链表)

链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点

链上的每个节点包含两种信息:节点本身的数据和指向下一个节点的指针

一个单向链表通常具有以下方法:

  1. size:返回链表中节点的个数
  2. head:返回链表中的头部元素
  3. add:向链表尾部增加一个节点
  4. remove:删除某个节点
  5. indexOf:返回某个节点的index
  6. elementAt:返回某个index处的节点
  7. addAt:在某个index处插入一个节点
  8. removeAt:删除某个index处的节点

图示

/*** 链表中的节点 */
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.indexOf = function (element) {var currentNode = head;var index = -1;while (currentNode) {index++;if (currentNode.element === element) {return index;}currentNode = currentNode.next;}return -1;}this.elementAt = function (index) {var currentNode = head;var count = 0;while (count < index) {count++;currentNode = currentNode.next;}return currentNode.element;}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++;}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;}
}

我们使用 Javascript 再次创建一个链表类:

// 链表节点
class Node {constructor(element) {this.element = elementthis.next = null}
}// 链表
class LinkedList {constructor() {this.head = nullthis.length = 0}// 追加元素append(element) {const node = new Node(element)let current = nullif (this.head === null) {this.head = node} else {current = this.headwhile(current.next) {current = current.next}current.next = node}this.length++}// 任意位置插入元素insert(position, element) {if (position >= 0 && position <= this.length) {const node = new Node(element)let current = this.headlet previous = nulllet index = 0if (position === 0) {this.head = node} else {while (index++ < position) {previous = currentcurrent = current.next}node.next = currentprevious.next = node}this.length++return true}return false}// 移除指定位置元素removeAt(position) {// 检查越界值if (position > -1 && position < length) {let current = this.headlet previous = nulllet index = 0if (position === 0) {this.head = current.next} else {while (index++ < position) {previous = currentcurrent = current.next}previous.next = current.next}this.length--return current.element}return null}// 寻找元素下标findIndex(element) {let current = this.headlet index = -1while (current) {if (element === current.element) {return index + 1}index++current = current.next}return -1}// 删除指定文档remove(element) {const index = this.indexOf(element)return this.removeAt(index)}isEmpty() {return !this.length}size() {return this.length}// 转为字符串toString() {let current = this.headlet string = ''while (current) {string += ` ${current.element}`current = current.next}return string}
}

链表类的使用:

const linkedList = new LinkedList()console.log(linkedList)
linkedList.append(2)
linkedList.append(6)
linkedList.append(24)
linkedList.append(152)linkedList.insert(3, 18)
console.log(linkedList)
console.log(linkedList.findIndex(24))

数组

数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起集合

栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作

的特点是后进先出

生活中常见的栈的例子比如一摞书,你最后放上去的那本你之后会最先拿走
又比如浏览器的访问历史,当点击返回按钮,最后访问的网站最先从历史记录中弹出

栈一般具备以下方法:

  1. push:将一个元素推入栈顶
  2. pop:移除栈顶元素,并返回被移除的元素
  3. peek:返回栈顶元素
  4. length:返回栈中元素的个数

图示

在 Javascript 中我们可以使用数组的原生方法实现一个栈/队列的功能,鉴于学习目的,我们使用类来实现一个栈。

class Stack {constructor() {this.items = []}// 入栈push(element) {this.items.push(element)}// 出栈pop() {return this.items.pop()}// 末位get peek() {return this.items[this.items.length - 1]}// 是否为空栈get isEmpty() {return !this.items.length}// 尺寸get size() {return this.items.length}// 清空栈clear() {this.items = []}// 打印栈数据print() {console.log(this.items.toString())}
}

使用栈类:

// 实例化一个栈
const stack = new Stack()
console.log(stack.isEmpty) // true// 添加元素
stack.push(5)
stack.push(8)// 读取属性再添加
console.log(stack.peek) // 8
stack.push(11)
console.log(stack.size) // 3
console.log(stack.isEmpty) // false

队列

队列和栈类似,也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,而在另一端进行删除操作

Queue是先进先出

队列是先进先出。队列在生活中的例子比如排队上公交,排在第一个的总是最先上车 又比如打印机的打印队列,排在前面的最先打印。

Queue一般具有以下常见方法:

  1. enqueue:入列,向队列尾部增加一个元素
  2. dequeue:出列,移除队列头部的一个元素并返回被移除的元素
  3. front:获取队列的第一个元素
  4. isEmpty:判断队列是否为空
  5. size:获取队列中元素的个数

首先我们在 Javascript 中实现一个队列

function Queue() {var collection = [];this.print = function () {console.log(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.size = function () {return collection.length;}
}

另一种方式, 我们通过类实现一个队列

class Queue {constructor(items) {this.items = items || []}enqueue(element){this.items.push(element)}dequeue(){return this.items.shift()}front(){return this.items[0]}clear(){this.items = []}get size(){return this.items.length}get isEmpty(){return !this.items.length}print() {console.log(this.items.toString())}
}

使用队列类:

const queue = new Queue()
console.log(queue.isEmpty) // truequeue.enqueue('John')
queue.enqueue('Jack')
queue.enqueue('Camila')
console.log(queue.size) // 3
console.log(queue.isEmpty) // false
queue.dequeue()
queue.dequeue()
queue.print() // 'Camila'

队列是遵行FIFO(First In First Out, 先进先出)原则的一组有序的项。队列再尾部添加新元素,并从顶部移除元素。

在现实中,最常见的队列的例子就是排队。

1.创建队列

现在,我们来创建一个类来表示一个队列。先从最基本的声明类开始:

function Queue(){// 这里是属性和方法
}

首先,需要一个用户存储队列中元素的数据结构,我们可以使用数组。

var items = [];

接下来,声明一些队列可用的方法:

  • enqueue(element(s)):进队,向队列尾部添加一个(或多个)新项。
  • dequeue():移除队列的第一项,并返回被移除的元素。
  • front():返回队列中第一个元素-最先被添加,也会是最先被移除的元素。(只返回,不移除)。
  • isEmpty():如果队列为空,返回true,否则,返回false。
  • size():返回队列的长度。

首先,我们来实现enqueue的方法,这个方法负责向队列中添加新元素。只能是添加到队列的尾部。

this.enqueue = function(element) {items.push(element);}

接下来要实现的是dequeue方法,这个方法负责从队列移除项。由于队列遵循的是先进先出原则,所以最先移除的就是最先添加的,元素是排在数组的第一位。

this.dequeue = function() {return items.shift();}

只有enqueue方法和dequeue方法可以添加和移除元素,这样就确保了Queue类遵循先进先出原则。
现在来为我们的类实现一些额外的辅助方法:

// front():返回队列中第一个元素this.front = function() {return items[0];}// isEmpty():如果队列为空,返回true,否则,返回falsethis.isEmpty = function() {return items.length === 0;}// size():返回队列的长度this.size = function() {return items.length;}

完成,我们的Queue类实现好了,现在来看看Queue完整的实现是怎么样的:

function Queue() {var items = [];this.enqueue = function(element) {items.push(element);}this.dequeue = function() {return items.shift();}this.front = function() {return items[0];}this.isEmpty = function() {return items.length === 0;}this.clear = function() {items = [];}this.size = function() {return items.length;}this.print = function() {console.log(items.toString());}
}

2.使用Queue类

var queue = new Queue();
console.log(queue.isEmpty()); // 输出 true
queue.enqueue('John');        // 添加元素 John
queue.enqueue('Jam');         // 添加元素 Jam
queue.enqueue('Camila');      // 添加元素 Camila
queue.print();
console.log(queue.size);      // 输出 3
console.log(queue.isEmpty);   // 输出 false
queue.dequeue();              // 移除元素
queue.dequeue();
queue.print();

运行上面的代码,我们可以看出,我们已经实现了队列,遵循了先入先出原则。

优先队列

上面我们已经实现了一个队列,现在,逐步深入,我们来看看什么是优先队列。

队列还有个升级版本,给每个元素赋予优先级,优先级高的元素入列时将排到低优先级元素之前。

优先队列是默认队列的变种,它的元素的添加和移除是基于优先级的。一个现实的例子就是医院的(急诊科)候诊室。医生会优先处理病情比较严重的患者。

实现一个优先队列,有两种选择:设置优先级,然后在正确的位置添加元素;或者用默认入列操作添加元素,任何按照优先级移除它们。下面,我们将会在正确的位置添加元素,任何用默认你的出列操作。

区别主要是enqueue方法的实现:

function PriorityQueue() {var items = [];// {1}function QueueElement(element, priority) {this.element = element;this.priority = priority;}this.enqueue = function(element, priority) {var queueElement = new QueueElement(element, priority);if(this.isEmpty()) {items.push(queueElement);  // {2}} else {var added = false;for(var i = 0; i < items.length; i++) {if(queueElement.priority < items.[i].priority) {items.splice(i, 0, queueElement);    // {3}added = true;break;}}if(!added) {    // {4}items.push(queueElement);}}}// 其他方法与默认队列一样}

我们创建了一个特殊的元素(行{1}),这个元素包含了要添加到队列的元素及其优先级。

如果队列为空,则直接将元素入列(行{2})。否则,就要进行比较。当找到一个比要添加的元素的priority值更大(优先级更低)时,就将元素插入到它之前(行{3})。

如果要添加的元素的priority指大于任何已有的元素,则直接将其添加到队列的末尾(行{4})。

var priorityQueue = new PriorityQueue();
priorityQueue.enqueue('John', 2);
priorityQueue.enqueue('Jam', 1);
priorityQueue.enqueue('Sam', 1);
priorityQueue.print();

至此,我们已经实现了优先队列,下面,将再介绍一种队列——循环队列

测试一下:

var pQ = new PriorityQueue();pQ.enqueue(['gannicus', 3]);
pQ.enqueue(['spartacus', 1]);
pQ.enqueue(['crixus', 2]);
pQ.enqueue(['oenomaus', 4]);pQ.print();

结果:

[[ 'spartacus', 1 ],[ 'crixus', 2 ],[ 'gannicus', 3 ],[ 'oenomaus', 4 ]
]

循环队列——击鼓传花

循环队列是默认队列的另一种修改版,什么是循环队列呢?举个现实中的例子,记得小时候玩过的传花游戏吗?

几个孩子围成一圈,开始击鼓了,孩子就把花尽快地传递给旁边的人,某一时刻鼓声停止了,传花也就停止了,这个时候花落在谁手上,谁就被淘汰。鼓声响起,继续传花,如此循环,直至只剩下一个孩子,即胜者。

function hotPotato(namelist, num) {var queue = new Queue();for (var i = 0; i < namelist.length; i++) {     // {1}queue.enqueue(namelist[i]);}var eliminated = "";while (queue.size() > 1) {                 // {2}for (var i = 0; i < num; i++) {queue.enqueue(queue.dequeue());    // {3}}eliminated = queue.dequeue();    // {4}console.log(eliminated + "在击鼓传花游戏中被淘汰");}return queue.dequeue();    // {5}
}
var names = ['john', 'jack', 'camila', 'ingrid', 'carl'];
var winner = hotPotato(names, 7);
console.log("胜利者: " + winner);      //john

首先,先把名单添加到队列里面(行{1})。

当队列的的长度大于1的时候(行{2}),根据指定的一个数字(num)迭代队列,将队列的头一个移除并将其添加到队尾(行{3})。

一旦传递次数达到给定的数字,则删除此时的队列第一项(行{4}),即拿着花的那个人,他将被淘汰。

如此循环,直至队列的长度等于1,返回胜者(行{5})。

哈希表

Hash Table是一种用于存储键值对(key value pair)的数据结构,因为Hash Table根据key查询value的速度很快,所以它常用于实现Map、Dictinary、Object等数据结构。

如上图所示,Hash Table内部使用一个hash函数将传入的键转换成一串数字,而这串数字将作为键值对实际的key,通过这个key查询对应的value非常快,时间复杂度将达到O(1)。Hash函数要求相同输入对应的输出必须相等,而不同输入对应的输出必须不等,相当于对每对数据打上唯一的指纹。

一个Hash Table通常具有下列方法:

  1. add:增加一组键值对
  2. remove:删除一组键值对
  3. lookup:查找一个键对应的值

堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆

图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。

图示

二叉查找树

Tree的数据结构和自然界中的树极其相似,有根、树枝、叶子

tree是一种多层数据结构,与Array、Stack、Queue相比是一种非线性的数据结构,在进行插入和搜索操作时很高效。

一个二叉查找树应该具有以下常用方法:

  1. add:向树中插入一个节点
  2. findMin:查找树中最小的节点
  3. findMax:查找树中最大的节点
  4. find:查找树中的某个节点
  5. isPresent:判断某个节点在树中是否存在
  6. remove:移除树中的某个节点

图示

测试一下:

const bst = new BST();bst.add(4);
bst.add(2);
bst.add(6);
bst.add(1);
bst.add(3);
bst.add(5);
bst.add(7);
bst.remove(4);
console.log(bst.findMin());
console.log(bst.findMax());
bst.remove(7);
console.log(bst.findMax());
console.log(bst.isPresent(4));

打印结果:

1
7
6
false

https://blog.fundebug.com/2019/08/12/8-common-data-structure-and-javascript-implementation/​blog.fundebug.com在 JavaScript 中学习数据结构与算法​juejin.im

js 中meta 移除head_浅析JS中数据结构相关推荐

  1. js 中meta 移除head_JS函数和winform函数之间的相互调用

    1.写一个简单的html页面,用于输入日志,代码如下: <html><head> <meta charset="UTF-8"> <scri ...

  2. python中int函数的用法浅析_Python中int()函数的用法浅析

    int()是Python的一个内部函数 Python系统帮助里面是这么说的 >>> help(int) Help on class int in module __builtin__ ...

  3. matlab中imadjust函数的用法,浅析matlab中imadjust函数

    imadjust imadjust是一个计算机函数,该函数用于调节灰度图像的亮度或彩色图像的颜色矩阵.在matlab的命令窗口中键入: doc imadjust或者help imadjust即可获得该 ...

  4. python中main的作用_浅析python 中__name__ = '__main__' 的作用

    很多新手刚开始学习python的时候经常会看到python 中__name__ = \'__main__\' 这样的代码,可能很多新手一开始学习的时候都比较疑惑,python 中__name__ = ...

  5. eclipse中无法移除jar包_IDEA中已配置阿里镜像,但maven无法下载jar包的问题

    在网上拷贝的所有阿里云镜像比如: <mirror> <id>nexus-aliyunid> <mirrorOf>centralmirrorOf> < ...

  6. php中的 i详解,浅析PHP中的i++与++i的区别及效率

    先看看基本区别: i++ :先在i所在的表达式中使用i的当前值,后让i加1 ++i :让i先加1,然后在i所在的表达式中使用i的新值 看一些视频教程里面写for循环的时候都是写 ++i 而不是 i++ ...

  7. formdata 接受参数中带有class 对象_浅析JAVA中的反射机制及对Servlet的优化

    今天来聊聊java中的反射机制,工作以后发现很多东西动不动就要使用反射或者动态代理,如果不能很好的理解反射,那么对于动态代理等一些重要的设计模式就会有种不够通透的感觉. 所谓的反射,就是在运行状态中, ...

  8. python中int函数的用法浅析_python中int函数怎么用,

    详细内容 int() 函数用于将一个字符串会数字转换为整型.接下来通过本文给大家介绍python 中的int()函数的相关知识,感兴趣的朋友一起看看吧 int(x, [base]) 功能: 函数的作用 ...

  9. oracle中having的用法,深入浅析SQL中的group by 和 having 用法

    一.sql中的group by 用法解析: Group By语句从英文的字面意义上理解就是"根据(by)一定的规则进行分组(Group)". 作用:通过一定的规则将一个数据集划分成 ...

最新文章

  1. 前后端分离的探索(一)
  2. chrono 使用备注
  3. 电抗电路的串并联的转换
  4. golang int 转string_Golang的逃逸分析
  5. php语言cookie,如何创建一个简单的PHP cookie语言切换?
  6. 树莓派IO口驱动代码的编写、微机总线地址、物理地址、虚拟地址、BCM2835芯片手册
  7. 小 Q 与函数求和 1(牛客练习赛 81 E)
  8. Java用户修改密码
  9. HTML 5 aside 标签
  10. php请求要通过什么协议,php – 发送多个应用程序协议请求(类似于mailto:)
  11. LeetCode(620)——有趣的电影(MySQL)
  12. resnet18实现cifar10分类
  13. PopWindow:基本使用与自定义PopWindow
  14. 《Spring》AOP实现原理
  15. 如何舒服地在图书馆用ipad入门深度学习【windows jupyter远程】
  16. [BZOJ2959]长跑——新技能:LCT+缩圈
  17. 矩阵的对称性,自反性和反对称性的判断
  18. 计算机考研专业课838考什么,17年管理学838专业课初试110分经验贴
  19. docker Starting MySQL database server mysqld fail解决办法
  20. 微型计算机常见接口设备,微型计算机的外部设备和内部设备各有哪些?

热门文章

  1. 我们都在努力做自己,我的编程之路开篇
  2. Spring使用ajax异步上传文件
  3. BootStrap Table和Mybatis Plus实现服务端分页
  4. Zabbix实战-简易教程--拓扑图(Maps)
  5. 负离子程序员的一组未来手绘,酷毙了
  6. ZOJ 1094 带括号的矩阵连乘
  7. WinAPI: SetTimer、KillTimer - 创建与移除高性能定时器
  8. prometheus监控redis(无metric接口)
  9. Linux系统通过Squid配置实现代理上网
  10. 巧用iptables五招免费搞定SYN洪水攻击