一、数组
数组是一种线性数据结构,它是由一组连续的内存单元组成的,用于存储相同类型的数据。在JavaScript中,数组可以包含任意类型的数据,不只限于基本数据类型。
1.存储方式
在内存中,数组的元素是连续存储的,通过下标来访问数组中的元素。例如,一个包含整型数据的数组可以用类似以下方式来表示:
let arr = [1, 2, 3, 4, 5];
2.访问方式
通过数组下标来访问数组中的元素,数组下标从0开始。例如,要访问数组中的第三个元素,可以使用以下方式:
console.log(arr[2]); // 输出3
3.时间复杂度
- 访问:由于数组中的元素是连续存储的,通过下标访问数组中的元素的时间复杂度是O(1)。因为可以直接通过下标计算出元素的内存地址。
4.插入删除操作的复杂度
-== 插入和删除操作对数组的时间复杂度为O(n)==。因为在插入或删除一个元素时,需要将数组中的元素进行移动以保持元素的连续性,这将导致额外的时间开销。例如,在数组的开头插入一个元素将需要将之后的元素全部向后移动一个位置,这将是一个线性操作。
综上所述,数组是一种灵活的数据结构,可以用于存储和访问大量元素。访问操作的时间复杂度是固定的O(1),但插入和删除操作的时间复杂度取决于操作的位置,可能是O(n)。
二、链表
链表是一种数据结构,它由多个节点组成,每个节点包含数据和指向下一个节点的指针。
从存储方式来看,链表的节点是通过指针相连接的方式存储在内存中。每个节点都包含一个指针,指向下一个节点的地址,这样就形成了节点之间的连接。
举例来说,可以用JavaScript实现一个简单的链表节点:
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
接着可以创建一个链表, 将多个节点连接起来:
let node1 = new Node(1);
let node2 = new Node(2);
node1.next = node2;
从访问方式来看,链表的访问是通过依次遍历节点来访问元素的。为了访问特定位置的元素,需要从链表的头节点开始顺着指针找到对应位置的节点。
访问链表的时间复杂度为O(n),其中n为链表的长度。
对于插入和删除操作,链表的复杂度取决于要插入或删除的位置。如果要在链表头部插入或删除元素,时间复杂度为O(1);如果要在链表尾部插入或删除元素,仍然是O(n)。
综上所述,链表是一种灵活的数据结构,适合频繁进行插入或删除操作。
三、数组和链表的区别
数组和链表是两种常见的数据结构,它们之间的主要区别在于数据的存储方式和访问方式。
1. 存储方式:
- 数组是一种连续的内存结构,所有元素在内存中相邻存储。数组的大小在创建时就确定了,可以通过下标来访问任意位置的元素。
- 链表是一种非连续的内存结构,元素在内存中通过指针相连。链表的大小可以动态增长或减少,每个节点保存了下一个节点的指针,通过遍历来访问元素。
2. 访问方式:
- 数组的元素可以直接通过下标来访问,时间复杂度为O(1)。但插入或删除元素时,需要移动其他元素,时间复杂度为O(n)。
- 链表的元素需要通过遍历从头节点开始找到目标节点,时间复杂度为O(n)。但插入或删除元素时,只需要修改指针,时间复杂度为O(1)。
综上所述,数组适合需要频繁随机访问元素的情况,而链表适合需要频繁插入、删除元素的情况。在实际应用中,我们根据具体的需求选择不同的数据结构。
四、用js实现链表
// 定义节点类
class Node {
// 节点类构造函数,接收数据作为参数
constructor(data) {
this.data = data; // 节点存储的数据
this.next = null; // 指向下一个节点的指针
}
}
// 定义链表类
class LinkedList {
// 链表类构造函数
constructor() {
this.head = null; // 链表的头节点
}
// 添加节点到链表末尾
append(data) {
let newNode = new Node(data); // 创建新节点
if (this.head === null) { // 如果链表为空
this.head = newNode; // 新节点就是头节点
} else { // 如果链表不为空
let current = this.head; // 从头节点开始
while (current.next !== null) { // 遍历链表
current = current.next; // 移动到下一个节点
}
current.next = newNode; // 将新节点添加到链表末尾
}
}
// 删除指定值的节点
delete(data) {
if (this.head === null) { // 如果链表为空
return; // 直接返回
}
if (this.head.data === data) { // 如果头节点就是要删除的节点
this.head = this.head.next; // 头节点指向下一个节点
return;
}
let current = this.head; // 从头节点开始
let prev = null; // 记录当前节点的前一个节点
while (current !== null) { // 遍历链表
if (current.data === data) { // 如果找到要删除的节点
prev.next = current.next; // 前一个节点指向当前节点的下一个节点
return;
}
prev = current; // 记录当前节点
current = current.next; // 移动到下一个节点
}
}
// 查找指定值的节点
find(data) {
let current = this.head; // 从头节点开始
while (current !== null) { // 遍历链表
if (current.data === data) { // 如果找到指定值的节点
return current; // 返回该节点
}
current = current.next; // 移动到下一个节点
}
return null; // 如果没有找到,返回 null
}
// 修改指定节点的值
update(data, newData) {
let node = this.find(data); // 查找指定值的节点
if (node !== null) { // 如果找到该节点
node.data = newData; // 修改该节点的值
}
}
// 打印链表
print() {
let current = this.head; // 从头节点开始
let arr = [];
while (current !== null) { // 遍历链表
arr.push(current.data); // 将节点的数据添加到数组中
current = current.next; // 移动到下一个节点
}
console.log(arr.join('=>')); // 打印链表
}
}
// 测试链表功能
let list = new LinkedList();
list.append(1); // 添加节点 1
list.append(2); // 添加节点 2
list.append(3); // 添加节点 3
console.log("Initial Linked List:");
list.print(); // 打印链表
list.delete(2); // 删除节点 2
console.log("After deleting '2':");
list.print(); // 打印链表
list.update(3, 4); // 将节点 3 的值修改为 4
console.log("After updating '3' to '4':");
list.print(); // 打印链表
let newNode = new Node(1);
console.log(newNode); // 输出 Node { data: 1, next: null }