单链表是表示一系列节点的数据结构,其中每个节点指向链表中的下一个节点。 相反,双向链表具有指向其前后元素的节点。
与数组不同,链表不提供对链表表中特定索引访问。 因此,如果需要链表表中的第三个元素,则必须遍历第一个和第二个节点才能到得到它。
链表的一个好处是能够在固定的时间内从链表的开头和结尾添加和删除项。
这些都是在技术面试中经常被问到的数据结构,所以让我们开始吧。
另外,可以对链表进行排序。 这意味着当每个节点添加到链表中时,它将被放置在相对于其他节点的适当位置。
链表只是一系列节点,所以让我们从 Node 对象开始。
一个节点有两条信息
对于我们的节点,我们只需要创建一个函数,该函数接受一个值,并返回一个具有上面两个信息的对象:指向下一个节点的指针和该节点的值。
注意,我们可以只声明 value 而不是 value: value。这是因为变量名称相同(ES6 语法)
现在,让我们深入研究 NodeList 类,以下就是节点链表样子。
节点链表将包含五个方法:
printList():不是链表的原生方法,它将打印出我们的链表,主要用于调试
构造函数中需要三个信息:
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
}
isEmpty() 方法是一个帮助函数,如果链表为空,则返回true。
isEmpty() {
return this.length === 0;
}
这个实用程序方法用于打印链表中的节点,仅用于调试目的。
printList () {
const nodes = [];
let current = this.head;
while (current) {
nodes.push(current.value);
current = current.next;
}
return nodes.join(' -> ');
}
在添加新节点之前,push 方法需要检查链表是否为空。如何知道链表是否为空? 两种方式:
对于这个例子,我们使用 head是否为null来判断链表是否为空。
如果链表中没有项,我们可以简单地将head 指针和tail指针都设置为新节点并更新链表的长度。
if (this.head === null) {
this.head = node;
this.tail = node;
this.length++;
return node;
}
如果链表不是空的,我们必须执行以下操作:
以下是完整的 push 方法:
push(value) {
const node = Node(value);
// The list is empty
if (this.head === null) {
this.head = node;
this.tail = node;
this.length++;
return node;
}
this.tail.next = node;
this.tail = node;
this.length++;
}
在删除链表中的最后一项之前,我们的pop方法需要检查以下两项内容:
可以使用isEmpty方法检查链表是否包含节点。
if (this.isEmpty()) {
return null;
}
我们如何知道链表中只有一个节点? 如果 head 和tail 指向同一个节点。但是在这种情况下我们需要做什么呢? 删除唯一的节点意味着我们实际上要重新设置链表。
if (this.head === this.tail) {
this.head = null;
this.tail = null;
this.length--;
return nodeToRemove;
}
如果链表中有多个元素,我们可以执行以下操作
当链表中有节点时,
如果链表中的下一个节点是 tail
更新 tail 指向当前节点
当前节点设置为 null,
更新链表的长度
返回前一个 tail 元素
它看起来像这样:
1 let currentNode = this.head;
2 let secondToLastNode;
3
4 //从前面开始并迭代直到找到倒数第二个节点
5
6 while (currentNode) {
7 if (currentNode.next === this.tail) {
8 // 将第二个节点的指针移动到最后一个节点
9 secondToLastNode = currentNode;
10 break;
11 }
12 currentNode = currentNode.next;
13 }
14 // 弹出该节点
15 secondToLastNode.next = null;
16 // 将 tail 移动到倒数第二个节点
17 this.tail = secondToLastNode;
18 this.length--;
19
20 // 初始化 this.tail
21 return nodeToRemove;
如果你无法想象这一点,那么让我们来看看它。
第6-10行:如果链表中的下一个节点是最后一个项,那么这个当前项目就是新tail,因此我们需要保存它的引用。
if (currentNode.next === this.tail) {
secondToLastNode = currentNode;
}
第15行:将secondToLastNode更新为null,这是从链表中“弹出”最后一个元素的行为。
secondToLastNode.next = null;
第17行:更新tail以指向secondToLastNode。
this.tail = secondToLastNode;
第18行:更新链表的长度,因为我们刚删除了一个节点。
第21行:返回刚刚弹出的节点。
以下是完整的pop方法:
pop() {
if (this.isEmpty()) {
return null;
}
const nodeToRemove = this.tail;
// There's only one node!
if (this.head === this.tail) {
this.head = null;
this.tail = null;
this.length--;
return nodeToRemove;
}
let currentNode = this.head;
let secondToLastNode;
// Start at the front and iterate until
// we find the second to last node
while (currentNode) {
if (currentNode.next === this.tail) {
// Move the pointer for the second to last node
secondToLastNode = currentNode;
break;
}
currentNode = currentNode.next;
}
// Pop off that node
secondToLastNode.next = null;
// Move the tail to the second to last node
this.tail = secondToLastNode;
this.length--;
// Initialized to this.tail
return nodeToRemove;
}
get方法必须检查三种情况:
如果链表中不存在请求的索引,则返回null。
// Index is outside the bounds of the list
if (index < 0 || index > this.length) {
return null;
}
如果链表为空,则返回null。你可以把这些if语句组合起来,但是为了保持清晰,我把它们分开了。
if (this.isEmpty()) {
return null;
}
如果我们请求第一个元素,返回 head。
// We're at the head!
if (index === 0 ) {
return this.head;
}
否则,我们只是一个一个地遍历链表,直到找到要查找的索引。
let current = this.head;
let iterator = 0;
while (iterator < index) {
iterator++;
current = current.next;
}
return current;
以下是完整的get(index)方法:
get(index) {
// Index is outside the bounds of the list
if (index < 0 || index > this.length) {
return null;
}
if (this.isEmpty()) {
return null;
}
// We're at the head!
if (index === 0 ) {
return this.head;
}
let current = this.head;
let iterator = 0;
while (iterator < index) {
iterator++;
current = current.next;
}
return current;
}
delete方法需要考虑到三个地方
如果链表中不存在我们要删除的索引,则返回 null。
// Index is outside the bounds of the list
if (index < 0 || index > this.length) {
return null;
}
如果我们想删除head,将head设置为链表中的下一个值,减小长度,并返回我们刚刚删除的值。
if (index === 0) {
const nodeToDelete = this.head;
this.head = this.head.next;
this.length--;
return nodeToDelete;
}
如果以上都 不是,则删除节点的逻辑如下:
循环遍历正在查找的索引
增加索引值
将前一个和当前指针向上移动一个
将当前值保存为要删除的节点
更新上一个节点的指针以指向下一个节点
如果下一个值为 `null`
将`tail`设置为新的最后一个节点
更新链表长度
返回已删除的节点
如果你需要可视化图片,请参考Pop部分中的图表。
以下是完整的 delete 方法:
delete(index) {
// Index is outside the bounds of the list
if (index < 0 || index > this.length - 1) {
return null;
}
if (this.isEmpty()) {
return null;
}
if (index === 0) {
const nodeToDelete = this.head;
this.head = this.head.next;
this.length--;
return nodeToDelete;
}
let current = this.head;
let previous;
let iterator = 0;
while (iterator < index) {
iterator++;
previous = current;
current = current.next;
}
const nodeToDelete = current;
// Re-direct pointer to skip the element we're deleting
previous.next = current.next;
// We're at the end
if(previous.next === null) {
this.tail = previous;
}
this.length--;
return nodeToDelete;
}
原文来自:https://itnext.io/creating-linked-lists-in-JavaScript-2980b0559324
js 把一个正整数分解成若干个质数因子的过程称为分解质因数,在计算机方面,我们可以用一个哈希表来存储这个结果。首先,你需要一个判断是否为质数的方法,然后,利用短除法来分解。
看了很多文章,梯度下降算法描述都比较艰涩难懂,比如说: 目标函数f(θ)关于参数θ的梯度将是损失函数(loss function)上升最快的方向。然后会推导出下面这个公式。
AI概念在2015年起就红得发紫,不论是送外卖,搞团购,卖车,或是推荐莆田医院的,是个公司都会标榜自己是搞人工智能的。在21世纪的第二个十年,计算机专业相关的学生不说自己是搞AI算法的,同学聚会都抬不起头,相亲机会都变少了
我们在表单验证时,经常遇到字符串的包含问题,比如说邮件必须包含indexOf。我们现在说一下indexOf。这是es3.1引进的API ,与lastIndexOf是一套的。可以用于字符串与数组中。一些面试经常用问数组的indexOf是如何实现的
随着科技发展,计算能力越来越强,特别是量子计算的兴起,我们对超大质数的位数要求也越来越高,512 bit 的 RSA 已经被破解,而 1024 bit 也已经摇摇欲坠,现阶段 2048 bit 长度还是安全的,可是未来,谁又知道呢?
计数排序是一个非基于比较的[排序算法],该算法于1954年由 Harold H. Seward 提出。 它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围), 快于任何比较排序算法。
语言是一种复杂的自然习得的人类运动能力。成人的特点是通过大约100块肌肉的协调运动,每秒发出14种不同的声音。说话人识别是指软件或硬件接收语音信号,识别语音信号中出现的说话人,然后识别说话人的能力
一个三位的整数,个、十、百的立方和等于该整数(例:153=1*1*1+5*5*5+3*3*3),步骤构思:1、依次循环遍历输出所有三位数,取整,2、设置条件判断
在后面的例子中,我将引用这两个数组,一个包含 5 个元素,另一个包含 50 个元素。我还会用到 JavaScript 中方便的 performance API 来衡量执行时间的差异
一个具有层级结构的数据,实现这个功能非常容易,因为这个结构和组件的结构是一致的,递归遍历就可以了。但是,由于后端通常采用的是关系型数据库,所以返回的数据通常会是这个样子:前端这边想要将数据转换一下其实也不难,因为要合并重复项
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!