高级数据结构
JavaScript 提供了一些内置的数据结构,可以满足多数常见需求,但在处理复杂问题时,可以结合这些数据结构或实现自定义高级数据结构。以下是 JavaScript 中的高级数据结构及其实现方式和应用:
1. Map
Map 是一种无序的键值对集合,可以用来存储和检索键值对。它类似于对象,但有以下几个不同点:
- Map 中的键值对是无序的,因此没有索引,只能通过键来获取值。
- Map 中的键可以是任意值,而对象只能是字符串或 Symbol。
- Map 中的值可以是任意类型,而对象的值只能是基本类型。
javascript
const map = new Map();
// 添加键值对
map.set('key1', 'value1');
map.set('key2', 'value2');
// 获取值
console.log(map.get('key1'));
// 检查是否存在
console.log(map.has('key2'));
// 删除键值对
map.delete('key2');
// 获取大小
console.log(map.size);
// 遍历
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}2. WeakMap
WeakMap 与 Map 类似,也是一种键值对集合,但它的键是弱引用的,即如果没有其他引用指向这个键,那么它会被垃圾回收。
javascript
const weakMap = new WeakMap();
const obj = {name: 'John'};
weakMap.set(obj, 'age');
console.log(weakMap.get(obj)); // 'age'3. Set
Set 是一种无序的集合,它类似于数组,但只能存储唯一的值。
javascript
const set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(2);
console.log(set); // Set(3) { 1, 2, 3 }4. WeakSet
WeakSet 与 Set 类似,也是一种集合,但它的成员是弱引用的,即如果没有其他引用指向这个成员,那么它会被垃圾回收。
javascript
const weakSet = new WeakSet();
const obj = {name: 'John'};
weakSet.add(obj);
console.log(weakSet.has(obj)); // true集合运算
javascript
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
// 并集
const union = new Set([...setA, ...setB]);
// 交集
const intersection = new Set([...setA].filter(x => setB.has(x)));
// 差集
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log(union); // 输出: Set(5) { 1, 2, 3, 4, 5 }
console.log(intersection); // 输出: Set(1) { 3 }
console.log(difference); // 输出: Set(2) { 1, 2 }5.链表 (Linked List)
链表是一种数据结构,它是由节点组成的线性结构。每个节点包含一个数据项和一个指向下一个节点的引用。链表的优点是可以在 O(1) 时间内插入和删除节点,但缺点是查找节点需要 O(n) 时间。
单向链表
javascript
// 定义节点类
class Node {
constructor(value) {
this.value = value; // 节点的值
this.next = null; // 指向下一个节点的指针,初始为 null
}
}
// 定义链表类
class LinkedList {
constructor() {
this.head = null; // 链表的头节点,初始为 null
}
// 在链表末尾添加新节点
append(value) {
const newNode = new Node(value); // 创建一个新节点
if (!this.head) {
// 如果链表为空,将新节点设为头节点
this.head = newNode;
return;
}
// 遍历链表,找到最后一个节点
let current = this.head;
while (current.next) {
current = current.next;
}
// 将新节点连接到最后一个节点的 next 指针
current.next = newNode;
}
// 显示链表中的所有节点值
display() {
let current = this.head; // 从头节点开始
while (current) {
console.log(current.value); // 输出当前节点的值
current = current.next; // 移动到下一个节点
}
}
}
// 创建一个链表实例并测试功能
const list = new LinkedList();
list.append(1); // 向链表中添加值为 1 的节点
list.append(2); // 向链表中添加值为 2 的节点
list.append(3); // 向链表中添加值为 3 的节点
list.display(); // 输出链表中的所有节点值:1 2 3双向链表
javascript
// 定义节点类(用于双向链表的节点)
class Node {
constructor(value) {
this.value = value; // 节点的值
this.prev = null; // 指向前一个节点的指针,初始为 null
this.next = null; // 指向后一个节点的指针,初始为 null
}
}
// 定义双向链表类
class DoublyLinkedList {
constructor() {
this.head = null; // 链表的头节点,初始为 null
this.tail = null; // 链表的尾节点,初始为 null
}
// 在链表末尾添加新节点
append(value) {
const newNode = new Node(value); // 创建一个新节点
if (!this.head) {
// 如果链表为空,将新节点设为头节点和尾节点
this.head = newNode;
this.tail = newNode;
return;
}
// 将新节点连接到尾节点的 next 指针
this.tail.next = newNode;
// 将尾节点连接到新节点的 prev 指针
newNode.prev = this.tail;
// 更新尾节点为新节点
this.tail = newNode;
}
// 在链表头部添加新节点
prepend(value) {
const newNode = new Node(value); // 创建一个新节点
if (!this.head) {
// 如果链表为空,将新节点设为头节点和尾节点
this.head = newNode;
this.tail = newNode;
return;
}
// 将新节点的 next 指针指向当前头节点
newNode.next = this.head;
// 将当前头节点的 prev 指针指向新节点
this.head.prev = newNode;
// 更新头节点为新节点
this.head = newNode;
}
// 显示链表中的所有节点值(从头到尾遍历)
display() {
let current = this.head; // 从头节点开始遍历
while (current) {
console.log(current.value); // 输出当前节点的值
current = current.next; // 移动到下一个节点
}
}
}
// 创建一个双向链表实例并测试功能
const list = new DoublyLinkedList();
list.append(1); // 向链表尾部添加值为 1 的节点
list.append(2); // 向链表尾部添加值为 2 的节点
list.append(3); // 向链表尾部添加值为 3 的节点
list.prepend(0); // 向链表头部添加值为 0 的节点
list.display(); // 输出链表中的所有节点值:0 1 2 36.栈 (Stack)
栈是一种线性数据结构,只能在一端进行插入和删除操作,遵循先进后出 (FILO) 原则。栈的应用场景包括函数调用栈、表达式求值、浏览器的前进后退、撤销操作等。
javascript
// 定义栈类
class Stack {
constructor() {
this.items = []; // 栈的元素数组
}
// 入栈
push(item) {
this.items.push(item);
}
// 出栈
pop() {
return this.items.pop();
}
// 栈是否为空
isEmpty() {
return this.items.length === 0;
}
// 栈的大小
size() {
return this.items.length;
}
// 栈顶元素
peek() {
return this.items[this.items.length - 1];
}
}
// 创建一个栈实例并测试功能
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.peek()); // 输出栈顶元素:3
console.log(stack.pop()); // 输出栈顶元素:3
console.log(stack.pop()); // 输出栈顶元素:2
console.log(stack.pop()); // 输出栈顶元素:1
console.log(stack.isEmpty()); // 输出栈是否为空:true7.队列 (Queue)
队列是一种线性数据结构,只能在一端进行插入操作,另一端进行删除操作,遵循先进先出 (FIFO) 原则。队列的应用场景包括排队、打印任务、CPU 任务调度等。
javascript
// 定义队列类
class Queue {
constructor() {
this.items = []; // 队列的元素数组
}
// 入队
enqueue(item) {
this.items.push(item);
}
// 出队
dequeue() {
return this.items.shift();
}
// 队列是否为空
isEmpty() {
return this.items.length === 0;
}
// 队列的大小
size() {
return this.items.length;
}
// 队列头元素
peek() {
return this.items[0];
}
}
// 创建一个队列实例并测试功能
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.peek()); // 输出队列头元素:1
console.log(queue.dequeue()); // 输出队列头元素:1
console.log(queue.dequeue()); // 输出队列头元素:2
console.log(queue.dequeue()); // 输出队列头元素:3
console.log(queue.isEmpty()); // 输出队列是否为空:true8.二叉树 (Binary Tree)
二叉树是一种树形数据结构,每个节点最多有两个子节点,分别为左子节点和右子节点。二叉树的应用场景包括文件系统、二叉查找树、二叉堆、二叉搜索树等。
javascript
// 定义节点类
class Node {
constructor(value) {
this.value = value; // 节点的值
this.left = null; // 左子节点
this.right = null; // 右子节点
}
}
// 定义二叉搜索树类
class BST {
constructor() {
this.root = null; // 根节点
}
// 插入新节点
insert(value) {
const newNode = new Node(value); // 创建一个新节点
if (!this.root) {
// 如果树为空,将新节点设为根节点
this.root = newNode;
return;
}
let current = this.root; // 从根节点开始
while (true) {
if (value < current.value) {
// 如果新节点的值小于当前节点的值,则移动到左子节点
if (!current.left) {
current.left = newNode;
break;
}
current = current.left;
} else {
// 如果新节点的值大于等于当前节点的值,则移动到右子节点
if (!current.right) {
current.right = newNode;
break;
}
current = current.right;
}
}
}
// 搜索节点
search(value) {
let current = this.root; // 从根节点开始
while (current) {
if (value === current.value) {
// 如果找到了节点,返回节点的值
return current.value;
} else if (value < current.value) {
// 如果值小于当前节点的值,则移动到左子节点
current = current.left;
} else {
// 如果值大于当前节点的值,则移动到右子节点
current = current.right;
}
// 如果没有找到节点,返回 null
return null;
}
// 删除节点
delete (value)
{
this.root = this._deleteNode(this.root, value);
}
// 递归删除节点
_deleteNode(node, value)
{
if (!node) {
// 如果节点不存在,返回 null
return null;
}
if (value < node.value) {
// 如果值小于当前节点的值,则递归删除左子节点
node.left = this._deleteNode(node.left, value);
return node;
} else if (value > node.value) {
// 如果值大于当前节点的值,则递归删除右子节点
node.right = this._deleteNode(node.right, value);
return node;
} else {
// 如果找到了节点,则删除该节点
if (!node.left && !node.right) {
// 如果节点没有左右子节点,则直接删除该节点
return null;
} else if (!node.left) {
// 如果节点只有右子节点,则将右子节点提升为当前节点
return node.right;
} else if (!node.right) {
// 如果节点只有左子节点,则将左子节点提升为当前节点
return node.left;
} else {
// 如果节点有左右子节点,则找到右子树中最小节点,替换当前节点的值,然后递归删除右子树中最小节点
const minNode = this._findMin(node.right);
node.value = minNode.value;
node.right = this._deleteNode(node.right, minNode.value);
return node;
}
}
}
// 找到右子树中最小节点
_findMin(node)
{
while (node.left) {
node = node.left;
}
return node;
}
}
}
// 创建一个二叉搜索树实例并测试功能
const bst = new BST();
bst.insert(5);
bst.insert(3);
bst.insert(7);
bst.insert(2);
bst.insert(4);
bst.insert(6);
bst.insert(8);
console.log(bst.search(5)); // 输出节点值:5
console.log(bst.search(3)); // 输出节点值:3
console.log(bst.search(7)); // 输出节点值:7
console.log(bst.search(2)); // 输出节点值:2
console.log(bst.search(4)); // 输出节点值:4
console.log(bst.search(6)); // 输出节点值:6
console.log(bst.search(8)); // 输出节点值:89.图 (Graph)
图是一种复杂的非线性数据结构,由节点和边组成。图的应用场景包括网络、社交网络、地图等。
邻接表表示图
javascript
// 定义节点类
class Node {
constructor(value) {
this.value = value; // 节点的值
this.neighbors = []; // 邻居节点数组
}
}
// 定义图类
class Graph {
constructor() {
this.nodes = []; // 节点数组
}
// 添加节点
addNode(value) {
const node = new Node(value); // 创建一个新节点
this.nodes.push(node);
return node;
}
// 添加边
addEdge(node1, node2) {
node1.neighbors.push(node2);
node2.neighbors.push(node1);
}
// 显示图
display() {
for (const node of this.nodes) {
console.log(node.value + '的邻居:');
for (const neighbor of node.neighbors) {
console.log(neighbor.value);
}
}
}
}
// 创建一个图实例并测试功能
const graph = new Graph();
const node1 = graph.addNode(1);
const node2 = graph.addNode(2);
const node3 = graph.addNode(3);
graph.addEdge(node1, node2);
graph.addEdge(node1, node3);
graph.display(); // 输出图:1的邻居:2 3 2的邻居:1 3 3的邻居:1 2