顺序表的链式存储(链表)
链表是一种常见的数据结构,由一系列节点组成。每个节点包含两部分:数据和指向下一个节点的指针。
链表中的节点通过指针连接在一起,形成一个线性序列。与数组不同,链表的节点在内存中可以不连续存储,因此可以动态地添加或删除节点,而无需移动其他节点。
常见的链表类型有单链表、双向链表和循环链表。
链表的优点是插入和删除节点的时间复杂度为O(1),而不受数据规模的限制。然而,链表在查找特定节点时需要顺序遍历,时间复杂度为O(n)。
需要注意的是,链表相对于数组在访问任意节点的效率较低,且占用的额外空间较多(每个节点需要存储指针)。因此,在选择数据结构时,要根据具体的需求进行权衡和选择。
单链表
如果序列中的节点只包含指向后继节点的链接,则该链表称之为单向链表。
下面我们来了解链表的基本操作:
1. 插入

2. 删除

下面我们给出单向链表的代码,其中包含:增(头插入、尾插入);删(删除第一个节点、删除最后一个节点,删除指定数据节点);查(查找特定数据是否在链表中),能查既能改。
#include <iostream>
using namespace std;
/*
data储存数据
next存储指针地址
SingleLinkedList()声明一个单一节点,无赋值,next指向空地址
IntSLLLNode(el, prt)声明一个节点,data=el,next指向prt;
*/
class SingleLinkedList {//表中的节点
public:
int data;
SingleLinkedList* next;
SingleLinkedList() {//函数重载
next = 0;
}
SingleLinkedList(int el, SingleLinkedList* ptr = 0) {//函数重载
data = el;
next = ptr;
}
};
class LinkedListAccess {//访问链表
private:
SingleLinkedList* head, * tail;//分别指向表中的第一个节点和最后一个节点
public:
LinkedListAccess() {
head = tail = 0;
}
~LinkedListAccess();
int isEmpty() {//链表是否为空链表
return head == 0;
}
void addToHead(int);//头插入
void addToTail(int);//尾插入
int deleteFromHead();//删除第一个节点
int deleteFromTail();//删除最后一个节点
void deleteNode(int);//删除特定数据节点
bool isInList(int);//查找特定数据是否在链表中
void outputNode();//输出链表中的所有数据
};
LinkedListAccess::~LinkedListAccess()//删除头指针
{
for (SingleLinkedList* p; !isEmpty(); ) {
p = head->next;
delete head;
head = p;
}
}
void LinkedListAccess::addToHead(int el)
{
head = new SingleLinkedList(el, head);
if (tail == 0) tail = head;
}
void LinkedListAccess::addToTail(int el)
{
if (tail != 0) {
tail->next = new SingleLinkedList(el);
tail = tail->next;
}
else head = tail = new SingleLinkedList(el);
}
int LinkedListAccess::deleteFromHead()
{
int el = head->data;
SingleLinkedList* temp = head;
if (head = tail) head = tail = 0;
else head = head->next;
delete temp;
return el;
}
int LinkedListAccess::deleteFromTail()
{
int el = tail->data;
if (head == tail) {
delete tail;
head = tail = 0;
}
else {
SingleLinkedList* temp;
for (temp = head; temp->next != tail; temp = temp->next);
delete tail;
tail = temp;
tail->next = 0;
}
return el;
}
void LinkedListAccess::deleteNode(int el)
{
if (head != 0) {
if (head == tail && el == head->data) {
delete head;
head = tail = 0;
}
}
else if(el == head->data){
SingleLinkedList* temp = head;
head = head->next;
delete temp;
}
else {
SingleLinkedList* pred, *temp;
for (pred = head, temp = head->next; temp != 0 && !(temp->data == el); pred = pred->next, temp = temp->next);
if (temp != 0) {
pred->next = temp->next;
if (temp == tail) tail = pred;
delete temp;
}
}
}
bool LinkedListAccess::isInList(int el)
{
SingleLinkedList* temp;
for(temp = head; temp != 0 && !(temp->data == el); temp = temp->next);
return temp != 0;
}
void LinkedListAccess::outputNode()
{
SingleLinkedList* temp;
for (temp = head; temp != 0; temp = temp->next) {
cout << temp->data << " ";
}
}
int main() {
LinkedListAccess a;
a.addToHead(1);
a.addToHead(2);
a.addToTail(3);
a.outputNode();
return 0;
}
循环链表
循环链表:是一种头尾结点相连接的链表。特点是最后一个结点的指针域指向链表的头结点,整个链表的指针域链接成一个环。
从循环链表的任意一个结点出发都可以找到链表中的其它结点,是得表处理更加灵活
循环链表的简单修改:
- 判断是否是空链表: h e a d − > n e x t = = h e a d head->next==head head−>next==head
- 判断是否是表尾结点: p − > n e x t = = h e a d p->next==head p−>next==head
双向链表
双向链表:是构成链表的每个结点中设立两个指针域,一个指向其直接前驱的指针域 p r i o r prior prior,一个指向其直接后续的指针域 n e x t next next。
双向链表是为了克服单链表的单向性的缺陷而引入的
顺序表和链表的比较
空间的比较
- 存储分配的方式:顺序表的存储空间是一次性的分配,链表的存储空间是多次分配的。
- 存储密度( = 结点值域所占的存储量 结点结构所占的存储总量 ):顺序表 = 1 ,链表 < 1 存储密度(=\frac{结点值域所占的存储量}{结点结构所占的存储总量}):顺序表=1,链表<1 存储密度(=结点结构所占的存储总量结点值域所占的存储量):顺序表=1,链表<1
时间的比较
- 存取方式:顺序表可以随机存取,也可以顺序存取;链表只能顺序存取。
- 插入和删除是移动元素的个数:顺序表平均移动一半元素;链表不需要移动元素,只需要修改指针。
本文深入探讨了链表这一重要的数据结构,包括单链表、双向链表和循环链表。链表允许动态添加和删除节点,插入和删除操作的时间复杂度为O(1),但查找操作为O(n)。文章通过代码示例展示了如何在C++中实现链表的基本操作,并对比了链表与顺序表在空间和时间效率上的差异。此外,还提到了循环链表的特性,使表处理更为灵活。

617

被折叠的 条评论
为什么被折叠?



