当前位置: 首页 > news >正文

链表基础与操作全解析

链表是什么?

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

指针是什么?

在C++编程中,指针函数是一种特殊的函数,它的返回类型是指针。这意味着,这类函数执行完毕后返回的是一个地址,而不是一个具体的值。指针函数的声明方式通常是在函数返回类型前加上一个星号(*)来表示返回的是一个指针。

在明白了什么是指针和链表后

相信你们都对链表和指针有了一个概念了

链表分为几类?

链表分为四类

1.单链表

创建单链表

使用方法

单链表是一种链式存储的数据结构,每个节点包含数据域和指针域。数据域存储数据,指针域指向下一个节点。单链表的特点是不需要连续的存储空间,适用于频繁插入和删除操作的场景。

单链表的结构定义

首先定义单链表的节点结构:

typedef struct Node {

int data; // 数据域

struct Node *next; // 指针域

} Node, *LinkedList;

初始化单链表

初始化单链表时,需要创建一个头节点,并将其指针域置为空:

void InitList(LinkedList *L) {

*L = (Node *)malloc(sizeof(Node)); // 分配内存

if (*L == NULL) {

printf("内存分配失败\n");

exit(0);

}

(*L)->next = NULL; // 指针域置空

}

头插法建立单链表

头插法是将新节点插入到链表的表头,生成的链表顺序与输入数据顺序相反:

LinkedList HeadInsert(LinkedList L) {

InitList(&L); // 初始化

int x;

scanf("%d", &x);

while (x != 9999) { // 输入9999表示结束

Node *s = (Node *)malloc(sizeof(Node)); // 分配新节点

s->data = x; // 赋值

s->next = L->next; // 插入到表头

L->next = s;

scanf("%d", &x);

}

return L;

}

尾插法建立单链表

尾插法是将新节点插入到链表的表尾,生成的链表顺序与输入数据顺序一致:

LinkedList TailInsert(LinkedList L) {

InitList(&L); // 初始化

Node *r = L; // 尾指针

int x;

scanf("%d", &x);

while (x != 9999) { // 输入9999表示结束

Node *s = (Node *)malloc(sizeof(Node)); // 分配新节点

s->data = x; // 赋值

r->next = s; // 插入到表尾

r = s;

scanf("%d", &x);

}

r->next = NULL; // 尾节点指针域置空

return L;

}

遍历单链表

遍历单链表时,从头节点开始,依次输出每个节点的数据:

void PrintList(LinkedList L) {

Node *p = L->next;

while (p) {

printf("%d ", p->data);

p = p->next;

}

printf("\n");

}

求单链表的长度

计算单链表的长度时,从头节点开始,依次遍历每个节点,计数器加一:

int Length(LinkedList L) {

Node *p = L->next;

int len = 0;

while (p) {

len++;

p = p->next;

}

return len;

}

插入操作

在单链表的第i个位置插入值为x的新节点:

void Insert(LinkedList L, int i, int x) {

Node *p = GetElem(L, i - 1); // 查找第i-1个节点

Node *s = (Node *)malloc(sizeof(Node)); // 分配新节点

s->data = x; // 赋值

s->next = p->next; // 插入节点

p->next = s;

}

删除操作

删除单链表的第i个节点:

void Delete(LinkedList L, int i) {

if (i < 1 || i > Length(L)) {

printf("删除失败:索引错误\n");

return;

}

Node *p = GetElem(L, i - 1); // 查找第i-1个节点

Node *q = p->next; // 被删除节点

p->next = q->next; // 修改指针

free(q); // 释放内存

}

判空操作

判断单链表是否为空,只需检查头节点的指针域是否为空:

bool Empty(LinkedList L) {

if (L->next == NULL) {

printf("链表为空\n");

return true;

} else {

printf("链表不为空\n");

return false;

}

}

通过以上步骤,我们可以创建一个功能完整的单链表,并实现其基本操作

2.双向链表

双向链表是一种常见的线性数据结构,它由一系列节点组成,每个节点包含两个指针,一个指向前一个节点(prev指针),一个指向后一个节点(next指针)。与单链表相比,双向链表具有双向遍历、方便插入和删除、更灵活的操作等优势。

节点定义和初始化

首先,我们需要定义节点的结构体,并实现链表的初始化函数:

typedef int LTDataType;

typedef struct ListNode {

LTDataType _data;

struct ListNode* _next;

struct ListNode* _prev;

} ListNode;

ListNode* ListCreate() {

ListNode* node = (ListNode*)malloc(sizeof(ListNode));

if (!node) {

perror("malloc fail:");

exit(-1);

}

node->_next = node;

node->_prev = node;

return node;

}

插入和删除操作

双向链表的插入和删除操作相对简单,通过修改前后指针,可以方便地调整节点的连接关系。

插入节点

ListNode* BuyNode(LTDataType x) {

ListNode* node = (ListNode*)malloc(sizeof(ListNode));

if (node == NULL) {

perror("malloc fail");

exit(-1);

}

node->_data = x;

node->_next = NULL;

node->_prev = NULL;

return node;

}

void ListInsert(ListNode* pos, LTDataType x) {

assert(pos);

ListNode* newnode = BuyNode(x);

ListNode* dist = pos->_prev;

dist->_next = newnode;

newnode->_prev = dist;

newnode->_next = pos;

pos->_prev = newnode;

}

删除节点

void ListErase(ListNode* pos) {

assert(pos);

ListNode* posPrev = pos->_prev;

ListNode* posNext = pos->_next;

posPrev->_next = posNext;

posNext->_prev = posPrev;

free(pos);

}

其他操作

查找节点

ListNode* ListFind(ListNode* pHead, LTDataType x) {

assert(pHead);

ListNode* cur = pHead->_next;

while (cur != pHead) {

if (cur->_data == x)

return cur;

cur = cur->_next;

}

return NULL;

}

打印链表

void ListPrint(ListNode* pHead) {

ListNode* cur = pHead->_next;

printf("pHead<=>");

while (cur != pHead) {

printf("%d<=>", cur->_data);

cur = cur->_next;

}

printf("\n");

}

销毁链表

void ListDestory(ListNode* pHead) {

assert(pHead);

ListNode* cur = pHead->_next;

while (cur != pHead) {

ListNode* tmp = cur->_next;

free(cur);

cur = tmp;

}

free(pHead);

}

通过以上代码,我们实现了双向链表的基本操作,包括创建、插入、删除、查找、打印和销毁。双向链表在某些特定场景下相比单链表具有更多的优势和灵活性

3.循环链表

循环链表是一种链表数据结构,其中最后一个节点的指针指向第一个节点,形成一个环。这种结构使得从链表中的任何一个节点都可以访问整个链表。在C++中实现循环链表涉及节点的创建、链表的初始化、数据的插入与删除、以及链表的销毁等操作。

循环链表的节点定义

在C++中,循环链表的节点通常定义为一个结构体或类,包含数据域和指向下一个节点的指针域。例如:

struct ListNode {

int data; // 数据域

ListNode* next; // 指向下一个节点的指针

};

初始化循环链表

初始化循环链表时,需要创建一个头节点,并使其指针域指向自身,表示一个空的循环链表。初始化函数可能如下所示:

ListNode* ListInit() {

ListNode* head = new ListNode;

head->next = head; // 指向自己,形成环

head->data = -1; // 可以用特殊值标记头节点

return head;

}

插入数据

循环链表的插入操作与单链表类似,但需要考虑环形结构。插入新节点时,需要先将新节点的next指向当前节点的下一个节点,然后再将当前节点的next指向新节点。插入操作的代码示例:

void ListInsert(ListNode* head, int data, int position) {

ListNode* newNode = new ListNode;

newNode->data = data;

ListNode* current = head;

for (int i = 0; i < position - 1; ++i) {

current = current->next;

}

newNode->next = current->next;

current->next = newNode;

}

删除数据

删除操作需要找到待删除节点的前一个节点,然后将其next指向待删除节点的下一个节点。以下是删除操作的代码示例:

void ListDelete(ListNode* head, int position) {

ListNode* current = head;

for (int i = 0; i < position - 1; ++i) {

current = current->next;

}

ListNode* deleteNode = current->next;

current->next = deleteNode->next;

delete deleteNode;

}

销毁循环链表

销毁循环链表时,需要遍历链表并释放每个节点的内存。最后,将头节点的next指向自身,表示链表已被销毁。销毁函数可能如下所示:

void ListDestroy(ListNode* head) {

ListNode* current = head->next;

while (current != head) {

ListNode* nextNode = current->next;

delete current;

current = nextNode;

}

head->next = head; // 指向自己,表示链表已销毁

}

在实现循环链表时,需要注意处理空链表和非空链表的一致性,确保所有操作都能在环形结构中正确执行。循环链表的实现可以应用于多种场景,如操作系统的任务调度、约瑟夫问题等。通过上述步骤,可以在C++中有效地实现和操作循环链表。

4.静态链表

静态链表是一种线性存储结构,它结合了顺序表和链表的优点。静态链表使用数组存储数据,但数据的位置是随机的,通过一个整形变量(称为“游标”)来维持数据之间的逻辑关系。

存储结构

静态链表的存储结构如下:

struct Component {

ElemType data;

int cur; // 游标,0表示无指向

};

typedef Component StaticLinkList[MAXSIZE];

数组的第一个元素和最后一个元素作为特殊元素处理,不存放数据。第一个元素的cur存放备用链表的第一个结点的下标,最后一个元素的cur存放第一个有数值的元素的下标。

初始化

初始化静态链表,将数组链接成备用链表:

Status InitList(StaticLinkList &space) {

for (int i = 0; i < MAXSIZE - 1; i++)

space[i].cur = i + 1;

space[MAXSIZE - 1].cur = 0; // 静态链表为空

return OK;

}

插入操作

插入新元素前,需要从备用链表中分配空间:

int Malloc_SL(StaticLinkList space) {

int i = space[0].cur;

if (space[0].cur)

space[0].cur = space[i].cur;

return i;

}

插入新元素:

Status Listinsert(StaticLinkList &L, int i, ElemType e) {

int k = MAXSIZE - 1;

if (i < 1 || i > ListLength(L) + 1)

return ERROR;

int j = Malloc_SL(L);

if (j) {

L[j].data = e;

for (int n = 1; n <= i - 1; n++)

k = L[k].cur;

L[j].cur = L[k].cur;

L[k].cur = j;

return OK;

}

return ERROR;

}

删除操作

删除元素时,需要将其空间回收到备用链表中:

void Free_SL(StaticLinkList &space, int k) {

space[k].cur = space[0].cur;

space[0].cur = k;

}

删除元素:

Status ListDelete(StaticLinkList &L, int i) {

int j, k = MAXSIZE - 1;

if (i < 1 || i > ListLength(L))

return ERROR;

for (j = 1; j <= i - 1; j++)

k = L[k].cur;

j = L[k].cur;

L[k].cur = L[j].cur;

Free_SL(L, j);

return OK;

}

示例代码

完整的静态链表实现代码如下:

#include "iostream"

using namespace std;

#define MAXSIZE 1000

#define OK 1

#define ERROR 0

typedef int Status;

typedef int ElemType;

struct Component {

ElemType data;

int cur;

};

typedef Component StaticLinkList[MAXSIZE];

Status InitList(StaticLinkList &space) {

for (int i = 0; i < MAXSIZE - 1; i++)

space[i].cur = i + 1;

space[MAXSIZE - 1].cur = 0;

return OK;

}

int Malloc_SL(StaticLinkList space) {

int i = space[0].cur;

if (space[0].cur)

space[0].cur = space[i].cur;

return i;

}

int ListLength(StaticLinkList L) {

int count = 0;

int i = L[MAXSIZE - 1].cur;

while (i) {

i = L[i].cur;

count++;

}

return count;

}

Status Listinsert(StaticLinkList &L, int i, ElemType e) {

int k = MAXSIZE - 1;

if (i < 1 || i > ListLength(L) + 1)

return ERROR;

int j = Malloc_SL(L);

if (j) {

L[j].data = e;

for (int n = 1; n <= i - 1; n++)

k = L[k].cur;

L[j].cur = L[k].cur;

L[k].cur = j;

return OK;

}

return ERROR;

}

void Free_SL(StaticLinkList &space, int k) {

space[k].cur = space[0].cur;

space[0].cur = k;

}

Status ListDelete(StaticLinkList &L, int i) {

int j, k = MAXSIZE - 1;

if (i < 1 || i > ListLength(L))

return ERROR;

for (j = 1; j <= i - 1; j++)

k = L[k].cur;

j = L[k].cur;

L[k].cur = L[j].cur;

Free_SL(L, j);

return OK;

}

int main() {

StaticLinkList mylist;

InitList(mylist);

int h = Malloc_SL(mylist);

mylist[h].cur = 0;

mylist[h].data = 50;

mylist[999].cur = h;

Listinsert(mylist, 1, 1);

Listinsert(mylist, 2, 2);

Listinsert(mylist, 2, 3);

cout << "遍历并输出该链表上的所有数据:" << endl;

int i = mylist[999].cur;

while (i) {

cout << mylist[i].data << " ";

i = mylist[i].cur;

}

cout << endl;

cout << "please hello world!" << endl;

ListDelete(mylist, 2);

cout << "遍历并输出该链表上的所有数据:" << endl;

i = mylist[999].cur;

while (i) {

cout << mylist[i].data << " ";

i = mylist[i].cur;

}

cout << endl;

system("pause");

return 0;

}


文章转载自:

http://tsOeqdSS.Lcxdm.cn
http://g0a3ERWb.Lcxdm.cn
http://L0eIO3fr.Lcxdm.cn
http://EGSpJ38c.Lcxdm.cn
http://dYhOyhvn.Lcxdm.cn
http://1GZazzrS.Lcxdm.cn
http://1itnYrgh.Lcxdm.cn
http://X2B6ZT8n.Lcxdm.cn
http://DljWawbJ.Lcxdm.cn
http://dkDRktUh.Lcxdm.cn
http://NmW77Lwl.Lcxdm.cn
http://fBUKRKHU.Lcxdm.cn
http://FG614wXS.Lcxdm.cn
http://8gnTYmsC.Lcxdm.cn
http://EfbH1Qet.Lcxdm.cn
http://rBVSpfcd.Lcxdm.cn
http://pro1WL4r.Lcxdm.cn
http://DMOWHccj.Lcxdm.cn
http://IBKmvHHf.Lcxdm.cn
http://KRy3MhNM.Lcxdm.cn
http://QaNOrGT6.Lcxdm.cn
http://uOxlxtAk.Lcxdm.cn
http://z1fZ38yE.Lcxdm.cn
http://W2oNJMlg.Lcxdm.cn
http://oFoULSCv.Lcxdm.cn
http://6yUZE0Am.Lcxdm.cn
http://NczLFqWE.Lcxdm.cn
http://SXx4SORy.Lcxdm.cn
http://YRsdrI8Q.Lcxdm.cn
http://XH7JQZC9.Lcxdm.cn
http://www.dtcms.com/a/378629.html

相关文章:

  • GitHub 热榜项目 - 日榜(2025-09-11)
  • 中山GEO哪家好?技术视角解析关键词选词
  • 从零到一上手 Protocol Buffers用 C# 打造可演进的通讯录
  • 当DDoS穿上马甲:CC攻击的本质
  • 【ThreeJs】【自带依赖】Three.js 自带依赖指南
  • STM32短按,长按,按键双击实现
  • Flutter与原生混合开发:实现完美的暗夜模式同步方案
  • AT_abc422_f [ABC422F] Eat and Ride 题解
  • 面试问题详解十八:QT中自定义控件的三种实现方式
  • sql 中的 over() 窗口函数
  • Nginx优化与 SSL/TLS配置
  • Git远程操作(三)
  • 深入解析Spring AOP核心原理
  • 虫情测报仪:通过自动化、智能化的手段实现害虫的实时监测与预警
  • Python快速入门专业版(二十二):if语句进阶:嵌套if与条件表达式(简洁写法技巧)
  • 研发文档分类混乱如何快速查找所需内容
  • Java Web实现“十天内免登录”功能
  • CH347使用笔记:CH347在Vivado下的使用教程
  • 【linux内存管理】【基础知识 1】【pgd,p4d,pud,pmd,pte,pfn,pg,ofs,PTRS概念介绍】
  • 详解mcp以及agent java应用架构设计与实现
  • 硬件开发2-ARM裸机开发2-IMX6ULL
  • 电商网站被DDoS攻击了怎么办?
  • Java NIO的底层原理
  • QT 常用控件(概述、QWidget核心属性、按钮类控件、显示类控件、输入类控件、多元素控件、容器类控件、布局管理器)
  • MATLAB2-结构化编程和自定义函数-台大郭彦甫视频
  • 鸿蒙的编程软件的介绍
  • 鸿蒙审核问题——Scroll中嵌套了List/Grid时滑动问题
  • REDPlayer 鸿蒙原生视频播放库组件介绍与使用指南
  • HarmonyOS 应用开发深度解析:ArkUI 声明式 UI 与现代化状态管理最佳实践
  • redis 入门-1