数据结构中的单链表

什么是单链表?
单链表是一种线性单向数据结构,数据存储在节点上,每个节点通过一条链接连接到下一个节点。每个节点包含一个数据字段和一条指向下一个节点的链接。单链表只能沿一个方向遍历,而双链表则可以沿多个方向遍历。 双向链表 可以双向通行。
以下是单链表的节点结构:
链接列表中节点的结构
为什么使用链表而不是数组?
在某些情况下,链表比……更合适。 排列:
- 未知元素的数量: 当编译时无法确定所需的元素数量时,链表会在添加元素时动态分配内存。
- 随机访问: 当不需要随机索引访问时,链表是一个合适的选择。
- 中间插入: 在数组中间插入元素需要移动元素。而链表只需修改几个指针即可在任意位置插入元素。
Opera单链表
单链表非常适合动态分配内存。它支持链表的标准操作,例如插入、删除、搜索、更新、合并两个链表和遍历。
本文将讨论以下操作:
- 插入头部
- 插入尾部
- 在节点后插入
- 在节点前插入
- 删除头节点
- 删除尾节点
- 搜索并删除节点
- 遍历链接列表
这是一个包含四个节点的链表示例。
单链表示例
在单链表头部插入元素
这是一个简单的操作,通常称为向单链表中添加新节点。新节点会被创建一个并放置在链表的头部。
要执行此操作,请遵循以下两个重要条件:
- 如果列表为空,则新创建的节点成为头节点,并且它的 下页 指针为空。
- 如果列表不为空,则新节点成为头节点,并且它的 下页 指针指向前一个头节点。
以下是向链表头部插入节点的伪代码:
function insertAtHead(head, value): newNode = Node(value) if head is NULL: head = newNode return head else: newNode.next = head return newNode
插入头部
在单链表末尾插入元素
在链表末尾插入节点与在头部插入节点类似。遍历到尾节点,然后指向它的尾节点。 下页 指向新节点的指针。如果 head 为 NULL,则新节点成为 head。
步骤1) 横穿直至 下页 当前节点的指针变为 NULL。
步骤2) 创建具有指定值的新节点。
步骤3) 将新节点指定为尾节点的下一个节点。
在单列列表末尾插入元素的伪代码:
function insertAtEnd(head, value): newNode = Node(value) if head is NULL: head = newNode return head while head.next is not NULL: head = head.next head.next = newNode newNode.next = NULL
插入尾部
在单链表中插入节点
在节点后插入节点分为两个步骤:查找目标节点,并将新节点附加到其后。遍历列表直到找到匹配项,然后将新节点插入其中。
步骤1) 遍历直到当前节点的值等于搜索项。
步骤2) 设置新节点的 下页 指向当前节点的指针 下页 指针。
步骤3) 指向当前节点的 下页 指向新节点的指针。
伪代码:
function insertAfter(head, value, searchItem): newNode = Node(value) while head.value != searchItem: head = head.next newNode.next = head.next head.next = newNode
在单链表中的节点后插入一个节点
在单链表中插入节点
这类似于在现有节点后插入节点。遍历节点直到下一个节点与搜索值匹配,然后将新节点插入到该节点之前。
步骤1) 遍历直到下一个节点的值等于搜索项。
步骤2) 创建一个新节点并设置其 下页 指向当前节点的指针 下页.
步骤3) 指向当前节点的 下页 到新节点。
function insertBefore(head, value, searchItem): newNode = Node(value) while head.next.value != searchItem: head = head.next newNode.next = head.next head.next = newNode
在单链表中的节点前插入一个节点
删除单链表的头节点
头部指针作为参数提供。头节点被移除,下一个节点成为新的头节点。必须释放已删除节点的内存以避免内存泄漏。
步骤1) 将下一个节点指定为新的头节点。
步骤2) 释放前一个头节点分配的内存。
步骤3) 返回新的头节点。
function deleteHead(head): temp = head head = head.next free(temp) return head
删除链接列表的头部
删除单链表的尾部
删除尾节点与删除头节点类似。区别在于,删除尾节点需要遍历到链表的末尾。在单链表中,其尾节点的删除操作需要遍历到链表的末尾。 下页 指针为 NULL 时,即为尾节点。
步骤1) 遍历到尾节点之前。保存当前节点。
步骤2) 释放下一个节点(尾节点)的内存。
步骤3) 将当前节点的下一个节点设置为 NULL。
function deleteTail(head): while head.next.next is not NULL: head = head.next free(head.next) head.next = NULL
删除单链表的尾部
在单链表中查找并删除节点
此函数执行两个任务:搜索和删除。遍历列表直至末尾。如果找到匹配的节点,则将其删除并重新链接前一个节点。 下页 指针。
步骤1) 遍历列表直到末尾。检查当前节点是否等于搜索节点。
步骤2) 如果找到匹配项,则存储指向当前节点的指针。
步骤3) 此 下页 前一个节点成为当前节点的下一个节点。
步骤4) 删除当前节点并释放其内存。
function searchAndDelete(head, searchItem): while head.next.next is not NULL and head.next.value != searchItem: head = head.next temp = head.next head.next = head.next.next free(temp)
从单链表中搜索并删除节点
遍历单链表
单链表仅支持从头到尾的遍历。由于没有指向前一个节点的指针,因此无法进行反向遍历。链表会依次访问每个节点,并打印其值,直到遇到 NULL 为止。
步骤1) 遍历每个节点,直到遇到 NULL 为止。
步骤2) 打印当前节点的值。
function traverse(head): while head is not NULL: print head.value head = head.next
单链表示例 C++
#include<iostream> using namespace std; struct Node{ int data; struct Node *next; }; void insertAtHead(Node* &head, int value){ Node* newNode = new Node(); newNode->data = value; newNode->next = NULL; if(head != NULL){ newNode->next = head; } head = newNode; cout<<"Added "<<newNode->data<<" at the front"<<endl; } void insertEnd(Node* &head, int value){ if(head == NULL){ insertAtHead(head, value); return; } Node* newNode = new Node(); newNode->data = value; newNode->next = NULL; Node *temp = head; while(temp->next != NULL){ temp = temp->next; } temp->next = newNode; cout<<"Added "<<newNode->data<<" at the end"<<endl; } void searchAndDelete(Node **headPtr, int searchItem){ Node *temp = NULL; if((*headPtr)->data == searchItem){ temp = *headPtr; *headPtr = (*headPtr)->next; free(temp); } else { Node *currentNode = *headPtr; while(currentNode->next != NULL){ if(currentNode->next->data == searchItem){ temp = currentNode->next; currentNode->next = currentNode->next->next; free(temp); break; } else { currentNode = currentNode->next; } } } cout<<"Deleted Node\t"<<searchItem<<endl; } void insertAfter(Node* &headPtr, int searchItem, int value){ Node* newNode = new Node(); newNode->data = value; newNode->next = NULL; Node *head = headPtr; while(head->next != NULL && head->data != searchItem){ head = head->next; } newNode->next = head->next; head->next = newNode; cout<<"Inserted "<<value<<" after node\t"<<searchItem<<endl; } void insertBefore(Node* &headPtr, int searchItem, int value){ Node* newNode = new Node(); newNode->data = value; newNode->next = NULL; Node *head = headPtr; while(head->next != NULL && head->next->data != searchItem){ head = head->next; } newNode->next = head->next; head->next = newNode; cout<<"Inserted "<<value<<" before node\t"<<searchItem<<endl; } void traverse(Node *headPointer){ Node* tempNode = headPointer; cout<<"Traversal from head:\t"; while(tempNode != NULL){ cout<<tempNode->data; if(tempNode->next) cout<<" --> "; tempNode = tempNode->next; } cout<<endl; } int main(){ Node *head = NULL; insertAtHead(head, 5); insertAtHead(head, 6); insertAtHead(head, 7); insertEnd(head, 9); traverse(head); searchAndDelete(&head, 6); traverse(head); insertAfter(head, 7, 10); insertBefore(head, 9, 11); traverse(head); }
输出
Added 5 at the front Added 6 at the front Added 7 at the front Added 9 at the end Traversal from head: 7 --> 6 --> 5 --> 9 Deleted Node 6 Traversal from head: 7 --> 5 --> 9 Inserted 10 after node 7 Inserted 11 before node 9 Traversal from head: 7 --> 10 --> 5 --> 11 --> 9
单链表示例 Python
class Node: def __init__(self, data=None, next=None): self.data = data self.next = next class SinglyLinkedList: def __init__(self): self.head = None def insertAtHead(self, value): newNode = Node(data=value) if self.head is not None: newNode.next = self.head self.head = newNode print(f'Added {newNode.data} at the front.') def insertAtEnd(self, value): if self.head is None: self.insertAtHead(value) return newNode = Node(value) temp = self.head while temp.next is not None: temp = temp.next temp.next = newNode print(f'Added {newNode.data} at the end.') def searchAndDelete(self, searchItem): if self.head is None: return if self.head.data == searchItem: self.head = self.head.next print(f'Deleted node\t{searchItem}') return currentNode = self.head while currentNode.next is not None: if currentNode.next.data == searchItem: currentNode.next = currentNode.next.next print(f'Deleted node\t{searchItem}') return currentNode = currentNode.next def insertAfter(self, searchItem, value): newNode = Node(data=value) temp = self.head while temp.next is not None and temp.data != searchItem: temp = temp.next newNode.next = temp.next temp.next = newNode print(f'Inserted {value} after node\t{searchItem}') def insertBefore(self, searchItem, value): newNode = Node(data=value) temp = self.head while temp.next is not None and temp.next.data != searchItem: temp = temp.next newNode.next = temp.next temp.next = newNode print(f'Inserted {value} before node\t{searchItem}') def traverse(self): temp = self.head print("Traversing from head:\t", end="") while temp: print("{}\t".format(temp.data), end="") temp = temp.next print() singlyLinkedList = SinglyLinkedList() singlyLinkedList.insertAtHead(5) singlyLinkedList.insertAtHead(6) singlyLinkedList.insertAtHead(7) singlyLinkedList.insertAtEnd(9) singlyLinkedList.traverse() singlyLinkedList.searchAndDelete(6) singlyLinkedList.traverse() singlyLinkedList.insertAfter(7, 10) singlyLinkedList.insertBefore(9, 11) singlyLinkedList.traverse()
输出
Added 5 at the front. Added 6 at the front. Added 7 at the front. Added 9 at the end. Traversing from head: 7 6 5 9 Deleted node 6 Traversing from head: 7 5 9 Inserted 10 after node 7 Inserted 11 before node 9 Traversing from head: 7 10 5 11 9
单链表的复杂性
复杂度分为两种:时间复杂度和空间复杂度。对于单链表,最坏情况的时间复杂度和平均时间复杂度相同。
最佳情况时间复杂度:
- 在链表头部插入元素的时间复杂度为 O(1)。无需遍历链表内部。
- 如果目标元素位于头节点,则搜索和删除可以在 O(1) 时间内完成。
平均情况时间复杂度:
- 在链表中插入元素的时间复杂度为 O(n),其中 n 是元素的总数。
- 搜索和删除操作的时间复杂度也可能为 O(n),因为目标元素可以位于尾节点之前的任何位置。
单链表的空间复杂度
单链表动态分配内存。用于存储 n 它分配元素 n 存储单元。因此,空间复杂度为 O(n)。
单链表的应用
单链表在许多需要向前遍历和动态内存的地方都会用到:
- 栈和队列: 由节点构建的 LIFO 栈和 FIFO 队列的底层存储。
- 哈希表链式法: 通过将条目链接到每个桶的单链表中来解决冲突。
- 邻接表: 稀疏图使用单链表来表示每个顶点的邻居。
- 符号表: 编译器和解释器会将标识符按作用域链接成一个单链表。
- 内存分配器: 自由列表分配器 trac将 k 个空闲块表示为单链表。









