❝
链表(Linked List)是一种常见的线性数据结构,它通过指针将一系列的数据节点连接在一起。与数组相比,链表在插入和删除元素时更具优势,因为它不需要移动其他元素❞
基本概念
链表是一种动态数据结构,其长度可以根据需要进行扩展或缩减。链表中的每个数据节点包含两个部分:数据域和指针域。数据域用于存储数据,指针域用于存储指向下一个节点的指针。根据指针域的不同,链表可分为单向链表、双向链表和循环链表
创建链表时,首先需要定义一个节点结构,包含数据域和指针域。然后创建一个头节点,用于存储链表的起始地址。
链表结构
节点(Node):链表的基本单位,包含数据和指向下一个节点的指针。
指针(Pointer):用于连接节点,指示下一个节点的位置。
头节点(Head):链表的起始节点,通常用于访问链表中的第一个元素。
尾节点(Tail):链表的最后一个节点,其指针指向空值(NULL)。节点代码示例:
class Node<T> { data: T; next: Node<T> | null; constructor(data: T) { this.data = data; this.next = null; }}链表类型
「单向链表」
向链表(Singly Linked List)是最简单的链表结构,每个节点只有一个指针域,指向其后继节点。单向链表的最后一个节点的指针域为空,表示链表的结束。class Node<T> { data: T; next: Node<T> | null; prev: Node<T> | null; constructor(data: T) { this.data = data; this.next = null; this.prev = null; }}class DoublyLinkedList<T> { head: Node<T> | null; tail: Node<T> | null; constructor() { this.head = null; this.tail = null; } append(data: T): void { const newNode = new Node(data); if (!this.head) { this.head = newNode; this.tail = newNode; return; } newNode.prev = this.tail; this.tail!.next = newNode; this.tail = newNode; } print(): void { let current = this.head; while (current) { console.log(current.data); current = current.next; } }}const doublyLinkedList = new DoublyLinkedList<number>();doublyLinkedList.append(1);doublyLinkedList.append(2);doublyLinkedList.append(3);doublyLinkedList.print(); // 输出:1 2 3
「双向链表」
![]()
双向链表(Doubly Linked List)中的每个节点有两个指针域,一个指向后继节点,另一个指向前驱节点。双向链表的头节点的前驱指针和尾节点的后继指针都为空。class Node<T> { data: T; next: Node<T> | null; prev: Node<T> | null; constructor(data: T) { this.data = data; this.next = null; this.prev = null; }}class DoublyLinkedList<T> { head: Node<T> | null; tail: Node<T> | null; constructor() { this.head = null; this.tail = null; } append(data: T): void { const newNode = new Node(data); if (!this.head) { this.head = newNode; this.tail = newNode; return; } newNode.prev = this.tail; this.tail!.next = newNode; this.tail = newNode; } print(): void { let current = this.head; while (current) { console.log(current.data); current = current.next; } }}const doublyLinkedList = new DoublyLinkedList<number>();doublyLinkedList.append(1);doublyLinkedList.append(2);doublyLinkedList.append(3);doublyLinkedList.print(); // 输出:1 2 3
「循环链表」
![]()
循环链表(Circular Linked List)是一种特殊的链表结构,其中最后一个节点的指针域指向头节点,形成一个环形结构。循环链表可以是单向的,也可以是双向的。class Node { data: T; next: Node | null; constructor(data: T) { this.data = data; this.next = null; }}class CircularLinkedList { head: Node | null; constructor() { this.head = null; } append(data: T): void { const newNode = new Node(data); if (!this.head) { this.head = newNode; newNode.next = this.head; return; } let current = this.head; while (current.next !== this.head) { current = current.next!; } current.next = newNode; newNode.next = this.head; } print(): void { let current = this.head; if (!current) return; do { console.log(current.data); current = current.next!; } while (current !== this.head); }}const circularLinkedList = new CircularLinkedList();circularLinkedList.append(1);circularLinkedList.append(2);circularLinkedList.append(3);circularLinkedList.print(); // 输出:1 2 3 1 2 3 ...
基本操作
「插入(Insertion)」:在链表中插入节点时,需要先找到插入位置的前驱节点。然后将新节点的指针域指向前驱节点的后继节点,再将前驱节点的指针域指向新节点。对于双向链表,还需要将新节点的前驱指针域指向前驱节点,后继节点的前驱指针域指向新节点
「删除(Deletion)」:删除链表中的节点时,首先需要找到待删除节点的前驱节点。然后将前驱节点的指针域指向待删除节点的后继节点。对于双向链表,还需要将后继节点的前驱指针域指向前驱节点。最后释放待删除节点的内存空间。
「查找(Search)」:查找链表中的节点通常需要遍历链表,根据数据域的值或者其他条件进行查找。找到目标节点后,可以返回其地址或者其他相关信息。
「遍历(Traversal)」:遍历链表是访问链表中每个节点的过程。从头节点开始,通过指针域依次访问每个节点,直到链表结束。遍历链表时可以执行各种操作,如打印节点数据、修改节点数据等。
单向链表实现链表的插入、删除、遍历和查找操作
// 单向链表节点类class SinglyNode<T> { data: T; next: SinglyNode<T> | null; constructor(data: T) { this.data = data; this.next = null; }}// 单向链表类class SinglyLinkedList<T> { head: SinglyNode<T> | null; constructor() { this.head = null; } // 插入操作 insert(data: T, position: number): void { const newNode = new SinglyNode(data); if (position < 0) return; if (position === 0) { newNode.next = this.head; this.head = newNode; return; } let current = this.head; let index = 0; while (current && index< position - 1) { current = current.next; index++; } if (!current) return; newNode.next = current.next; current.next = newNode; } // 删除操作 remove(position: number): void { if (position < 0 || !this.head) return; if (position === 0) { this.head = this.head.next; return; } let current = this.head; let index = 0; while (current.next && index< position - 1) { current = current.next; index++; } if (!current.next) return; current.next = current.next.next; } // 遍历操作 traverse(callback: (data: T) => void): void { let current = this.head; while (current) { callback(current.data); current = current.next; } } // 查找操作 find(data: T): SinglyNode<T> | null { let current = this.head; while (current) { if (current.data === data) { return current; } current = current.next; } return null; }}// 示例const singlyLinkedList = new SinglyLinkedList<number>();singlyLinkedList.insert(1, 0);singlyLinkedList.insert(2, 1);singlyLinkedList.insert(3, 2);singlyLinkedList.traverse((data) => { console.log(data); // 输出:1 2 3});singlyLinkedList.remove(1);singlyLinkedList.traverse((data) => { console.log(data); // 输出:1 3});const foundNode = singlyLinkedList.find(3);console.log(foundNode?.data); // 输出:3双向链表实现链表的插入、删除、遍历和查找操作
// 双向链表节点类class DoublyNode<T> { data: T; next: DoublyNode<T> | null; prev: DoublyNode<T> | null; constructor(data: T) { this.data = data; this.next = null; this.prev = null; }}// 双向链表类class DoublyLinkedList<T> { head: DoublyNode<T> | null; tail: DoublyNode<T> | null; constructor() { this.head = null; this.tail = null; } // 插入操作 insert(data: T, position: number): void { const newNode = new DoublyNode(data); if (position < 0) return; if (position === 0) { newNode.next = this.head; if (this.head) { this.head.prev = newNode; } this.head = newNode; if (!this.tail) { this.tail = newNode; } return; } let current = this.head; let index = 0; while (current && index< position - 1) { current = current.next; index++; } if (!current) return; newNode.next = current.next; newNode.prev = current; if (current.next) { current.next.prev = newNode; } else { this.tail = newNode; } current.next = newNode; } // 删除操作 remove(position: number): void { if (position < 0 || !this.head) return; if (position === 0) { this.head = this.head.next; if (this.head) { this.head.prev = null; } else { this.tail = null; } return; } let current = this.head; let index = 0; while (current.next && index< position - 1) { current = current.next; index++; } if (!current.next) return; const removedNode = current.next; current.next = current.next.next; if (current.next) { current.next.prev = current; } else { this.tail = current; } } // 遍历操作 traverse(callback: (data: T) => void): void { let current = this.head; while (current) { callback(current.data); current = current.next; } } // 查找操作 find(data: T): DoublyNode<T> | null { let current = this.head; while (current) { if (current.data === data) { return current; } current = current.next; } return null; }}const doublyLinkedList = new DoublyLinkedList<number>();doublyLinkedList.insert(1, 0);doublyLinkedList.insert(2, 1);doublyLinkedList.insert(3, 2);doublyLinkedList.traverse((data) => { console.log(data); // 输出:1 2 3});doublyLinkedList.remove(1);doublyLinkedList.traverse((data) => { console.log(data); // 输出:1 3});const foundDoublyNode = doublyLinkedList.find(3);console.log(foundDoublyNode?.data); // 输出:3应用场景
「动态内存管理」:链表被广泛用于操作系统的动态内存管理。操作系统使用链表来跟踪可用的内存块和已分配的内存块
「文件系统的实现」:在文件系统中,链表用于存储文件的各个部分的位置信息。这使得文件可以分散存储在磁盘的不同部分。
「图的表示」:在计算机科学中,链表是表示图的两种主要方式之一。链表可以用来表示图中的顶点和边
「实现高级数据结构」:链表还可以用来实现其他高级数据结构,如堆栈、队列和哈希表。