什么是链表?
链表是一种常见的数据结构,用于存储一系列元素,并按照一定顺序连接它们。在链表中,
元素被存储在独立的节点中,每个节点都包含一个值和一个指向下一个节点的指针。
通过这些指针,链表的一部分可以被轻松地插入到另一个链表的结尾或中间。
链表的一个重要特点是它可以动态增长和缩小,并且在内存中非常紧凑。
链表和数组不同,数组的所有元素都是存储在连续的内存块中的,而链表的节点可以分散在内存中的任意位置。
因此,在数组中,可以通过索引来直接访问元素,但在链表中,必须通过遍历来访问元素。
在链表中,遍历从头开始,直到到达目标节点。
链表主要有两种类型:单向链表和双向链表。在单向链表中,每个节点只包含一个指向下一个节点的指针。
而在双向链表中,每个节点同时包含一个指向下一个节点和上一个节点的指针。
双向链表因此可以实现双向遍历,但需要更多的内存空间。
链表的常见操作包括:在链表头部插入一个元素、在链表尾部插入一个元素、
在链表的中间位置插入一个元素、删除链表中的指定元素、遍历整个链表等等。
链表比较适合用于频繁的删除和插入操作,但不如数组快速访问元素。
在JavaScript中如何如何处理?
在JavaScript中,链表可以用对象来表示。一个链表节点通常包含两个属性:value
和next
。
value
存储节点的值,next
指向下一个节点。链表节点可以使用以下的代码来创建:
function ListNode(val) {
this.val = val
this.next = null
}
函数ListNode
定义了链表节点的构造函数,其中val
表示节点存储的值,
next
表示指向下一个节点的指针。这个构造函数用于创建新的链表节点。
在链表中有许多操作,比如添加、删除、翻转等等,下面我们来一一讲解:
- 添加操作
链表最基本的操作之一是向其中添加元素。可以添加在列表开始、末尾或者中间。
比如,我们要在末尾添加一个新节点,可以使用如下方式:
function insertAtTail(list, val) {
const newNode = new ListNode(val)
if (!list) return newNode
let tail = list
while (tail.next) {
tail = tail.next
}
tail.next = newNode
return list
}
// 示例
const list = new ListNode(1) // 创建头节点
insertAtTail(list, 2) // 1 -> 2 在尾部插入一个新节点
insertAtTail(list, 3) // 1 -> 2 -> 3 在尾部插入一个新节点
函数insertAtTail
接受两个参数,list
表示链表的头节点,val
表示向链表中添加的新值。
在这个函数中,首先创建一个新的链表节点并将其值初始化为val
,然后检查链表是否为空。
如果链表为空,直接返回新创建的节点作为新的头节点。
否则,遍历整个链表,找到尾节点。在尾节点之后添加新节点作为新的尾节点,并将其连接到链表中。
最后,函数返回链表的头节点。
- 删除操作
从链表中删除节点也是一种重要的操作。同样,这也可以在开始、末尾或者中间进行。比如,我们要删除一个为val
的节点,可以使用如下方式:
function deleteNode(list, val) {
if (!list) return null
if (list.val === val) return list.next
let prev = list
let curr = list.next
while (curr) {
if (curr.val === val) {
prev.next = curr.next
break
}
prev = curr
curr = curr.next
}
return list
}
// 示例
const list = new ListNode(1)
list.next = new ListNode(2)
list.next.next = new ListNode(3)
deleteNode(list, 2) // 1 -> 3
这里的操作首先检查链表是否为空。接下来,我们查看链表头部节点是否为待删除节点,
否则我们遍历整个链表并查找包含待删除值的节点。
一旦找到,我们将当前节点的next
连接到前一个节点的next
,从而删除当前节点。
- 翻转操作
链表的另一个重要操作是将其指针翻转,从而得到一个新链表。下面是翻转一个链表的示例代码:
function reverseList(list) {
let prev = null
let curr = list
while (curr) {
let next = curr.next
curr.next = prev
prev = curr
curr = next
}
return prev
}
// 示例
const list = new ListNode(1)
list.next = new ListNode(2)
list.next.next = new ListNode(3)
reverseList(list) // 3 -> 2 -> 1
在这个例子中,我们从链表的头节点开始遍历,将每个节点的指针翻转。
使用三个指针变量来实现翻转,prev
始终指向前一个节点,curr
指向当前节点,
next
表示当前节点的后继节点,我们需要反转当前节点和前一个节点的关联,并将三个指针向后移动。