链表是一种常见的数据结构,用于存储和组织数据。它由一系列节点组成,每个节点包含数据和一个指向下一个节点的引用(指针或链接)。与数组不同,链表中的元素在内存中不必是相邻的,它们通过指针相互连接。
链表分为单向链表和双向链表两种主要类型:(今天我们主要围绕单向链表进行学习)
-
单向链表(Singly Linked List): 在单向链表中,每个节点只有一个指针,指向下一个节点。链表的尾节点指向空值(null),表示链表的结束。
-
双向链表(Doubly Linked List): 在双向链表中,每个节点有两个指针,一个指向前一个节点,另一个指向后一个节点。这使得在双向链表中可以更方便地在节点之间进行前后移动。
链表相对于数组的优点包括:
- 动态大小: 链表可以动态地分配内存,而数组需要在开始时确定大小。
- 插入和删除效率高: 在链表中插入或删除节点比在数组中更为高效,因为不需要移动其他节点。
- 不需要预分配内存: 链表可以根据需要动态分配内存,而数组需要预分配固定大小的内存。
然而,链表的缺点是访问元素时需要遍历整个链表,而数组可以通过索引直接访问元素,这导致链表在某些操作上效率较低。链表在实际应用中常用于需要频繁插入和删除操作的场景。
接下来我们通过一些代码来更好的理解链表
{
val: 1, // 数据域
next: { // 指针域
val: 2,
next: ... //如此循环
}
}
function ListNode(val){
this.val = val
this.next = null
}
let node = new ListNode(1)
node.next = new ListNode(2) //让节点1的指针指向节点2
node.next.next = new ListNode(3)//节点2的指针指向节点3
如果要在1,2之间增加一个节点呢?如下
只需要让节点1的指针指向节点3,再让节点3指向节点2即可。
如果是要删除中间的节点呢?
let node1 = new ListNode(1)
node1.next = new ListNode(2)
node1.next.next = new ListNode(3)
const target = node1.next //node1.next表示节点2(节点1的指针指向节点2)
node1.next = target.next // target.next 表示节点2的指针,指向节点3,你把它赋给了节点1的指针,也就是说此时节点1的指针指向了节点3
数组与链表:
数组中增加或删除一个元素会导致n个元素移动,时间复杂度为O(n)
链表的增删一个元素时间复杂度为O(1)