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

数据结构之单链表C语言

单链表

定义单链表是由节点组成的,每个节点包含两部分,一部分是数据域,用于存储数据元素;另一部分是指针域,用于存储指向下一个节点的指针。通过指针将各个节点依次连接起来,形成一个链式结构,每个结点结点都有一个指针指向下一个结点,最后一个节点的指针通常指向空,表示链表的结束。

1.单链表结构体

//单链表的结构体
struct LNode{ElemType data;//定义单链表结点类型struct LNode *next;//定义指向下一个节点 
};
typedef struct LNode LNode;
typedef struct LNode LinkList;
||
typedef struct LNode{ElemType data;//定义单链表结点类型struct LNode *next;//定义指向下一个节点 
}LNode,*LinkList;

typedef关键字–数据类型重命名

typedef<数据类型><别名>

为了增加代码的维护性对部分代码进行包装

/*** tool function* 根据索引遍历链表,找到第i个节点* @param temp_p 链表头指针* @param i 要查找的位置* @param temp_j 输出参数,记录实际遍历到的位置* @return 找到的节点指针,如果位置不合法或链表为空则返回NULL*/
LNode* traverseByIndex(LNode* temp_p, int i, int &temp_j){LNode* p = temp_p;int j = 0;while (p!= NULL && j < i) {p = p->next;j++;}temp_j = j;return p;
}

2.单链表的插入

image-20250426135435834

/*** 在单链表的第i个位置插入元素e* @param L 链表头指针的引用* @param i 插入位置(从1开始计数)* @param e 要插入的元素* @return 插入成功返回true,否则返回false*/
bool LinkListInsert(LinkList &L, int i, ElemType e) {int j = 0;LNode* p = traverseByIndex(L, i - 1, j); // 遍历到第i-1个节点,为插入做准备if (p == NULL) { // 非法区域,位置i超出链表范围或链表为空return false;}LNode* s = (LNode*)malloc(sizeof(LNode)); // 申请要插入的结点s->data = e;s->next = p->next;p->next = s;return true; 
}

时间复杂度分析:插入操作主要的时间开销在于找到第i - 1个节点,调用了traverseByIndex函数,最坏情况下需要遍历i - 1个节点,因此时间复杂度为O(i),在最坏情况下(即i等于链表长度n)时间复杂度为O(n)。而插入节点本身的操作(修改指针指向)时间复杂度为O(1)。

[注意] :p->next = ss->next = p->next位置不可以调换 不然会出现后面的结点丢失的情况

3.单链表的删除

image-20250426135725444

/*** 删除单链表的第i个位置的元素,并将删除的元素值存储到e中* @param L 链表头指针的引用* @param i 删除位置(从1开始计数)* @param e 用于存储删除的元素值* @return 删除成功返回true,否则返回false*/
bool ListDelete(LinkList &L, int i, ElemType &e) {int j = 0;LNode* p = traverseByIndex(L, i - 1, j);if (p == NULL || j > i - 1) { // 指向空区域或i不合法,位置i超出链表范围或链表为空return false; }LNode* q = p->next;e = q->data;p->next = q->next;free(q);return true; 
}

时间复杂度分析:删除操作主要的时间开销在于找到第i - 1个节点,调用了traverseByIndex函数,最坏情况下需要遍历i - 1个节点,因此时间复杂度为(O(i)),在最坏情况下(即i等于链表长度n)时间复杂度为(O(n))。而删除节点本身的操作(修改指针指向和释放内存)时间复杂度为(O(1))。

4.获取链表元素(位置||元素)

/*** 根据位置获取单链表中的元素* @param L 链表头指针的引用* @param i 要获取的元素位置(从1开始计数)* @return 找到的节点指针,如果位置不合法或链表为空则返回NULL*/
LNode* GetElemByIndex(LinkList &L, int i) {int j = 0;LNode* p = traverseByIndex(L, i, j);return p; // p为NULL或者想查找的结点 
}/*** 根据值获取单链表中的元素* @param L 链表头指针的引用* @param e 要查找的值* @return 找到的节点指针,如果未找到则返回NULL*/
LNode* GetElemByDate(LinkList &L, ElemType e) {LNode* p = L;while (p!= NULL && p->data != e) {p = p->next; } return p; // p为NULL或者想查找的结点 
}
  • GetElemByIndex函数:主要依赖于traverseByIndex函数,最坏情况下需要遍历i个节点,因此时间复杂度为(O(i)),在最坏情况下(即i等于链表长度n)时间复杂度为(O(n))。
  • GetElemByDate函数:需要从链表头开始依次遍历,直到找到值为e的节点或者遍历到链表末尾,最坏情况下需要遍历整个链表,因此时间复杂度为(O(n))。

5.头插法

顾名思义就是不断在第一个位置去插入元素

/*** 头插法创建单链表* @param L 链表头指针的引用* @return 创建好的链表头指针*/
LinkList List_HeadInsert(LinkList &L){LNode *s = NULL;ElemType x = 0;printf("请输入插入的元素,输入-1停止插入\n");scanf("%d", &x);while (x!=-1) {s = (LNode*)malloc(sizeof(LNode));s->data = x;s->next = L->next;L->next = s;scanf("%d", &x);}return L; 
}

6.尾插法

顾名思义就是不断在表尾位置去插入元素

/*** 尾插法创建单链表* @param L 链表头指针的引用* @return 创建好的链表头指针*/
LinkList List_RearInsert(LinkList &L){LNode *s = NULL;LNode *q = L;ElemType x = 0;printf("请输入插入的元素,输入-1停止插入\n");scanf("%d", &x);while (x!=-1) {s = (LNode*)malloc(sizeof(LNode));s->data = x;q->next = s;s->next = NULL;q = q->next;scanf("%d", &x);}return L; 
}

7.代码实现

#include <stdio.h>
#include <stdlib.h>typedef int ElemType;
// 单链表的结构体
typedef struct LNode {ElemType data;    // 定义单链表结点类型struct LNode *next;   // 定义指向下一个节点
} LNode, *LinkList;// 单链表功能
bool InitList(LinkList &L); // 初始化单链表
int Length(LinkList L);    // 求表长
LNode* GetElemByIndex(LinkList &L, int i); // 获取第i个结点
LNode* GetElemByDate(LinkList &L, ElemType e); // 根据值获得结点
bool LinkListInsert(LinkList &L, int i, ElemType e); // 在第i个结点插入
bool ListDelete(LinkList &L, int i, ElemType &e); // 删除第i个结点
LNode* traverseByIndex(LNode *temp_p, int i,int &temp_j);//temp_j:遍历到的位置 
LinkList List_HeadInsert(LinkList &L);//头插法
LinkList List_RearInsert(LinkList &L);//尾插法 
//遍历单链表
void traverse(LinkList L); int main() {LinkList L = NULL;ElemType e = 0;int choice, index;InitList(L); // 初始化链表LNode* nodeByIndex = NULL;LNode* nodeByDate = NULL;LNode* p = NULL;LNode* q = NULL;while (1) {printf("\n请选择操作:\n");printf("1. 求表长\n");printf("2. 按位置获取元素\n");printf("3. 按值获取元素\n");printf("4. 插入元素\n");printf("5. 删除元素\n");printf("6. 遍历\n");printf("7. 头插法\n");printf("8. 尾插法\n");printf("9. 退出\n");scanf("%d", &choice);switch (choice) {case 1:printf("链表长度为:%d\n", Length(L));break;case 2:printf("请输入要获取元素的位置:");scanf("%d", &index);nodeByIndex = GetElemByIndex(L, index);if (nodeByIndex != NULL) {printf("位置 %d 的元素值为:%d\n", index, nodeByIndex->data);} else {printf("位置不合法或链表为空\n");}break;case 3:printf("请输入要获取元素的值:");scanf("%d", &e);nodeByDate = GetElemByDate(L, e);if (nodeByDate != NULL) {printf("值为 %d 的元素已找到,其值为:%d\n", e, nodeByDate->data);} else {printf("未找到值为 %d 的元素\n", e);}break;case 4:printf("请输入要插入的位置:");scanf("%d", &index);printf("请输入要插入的元素值:");scanf("%d", &e);if (LinkListInsert(L, index, e)) {printf("元素 %d 已成功插入到位置 %d\n", e, index);} else {printf("插入失败,位置不合法或链表为空\n");}break;case 5:printf("请输入要删除的位置:");scanf("%d", &index);if (ListDelete(L, index, e)) {printf("已删除位置 %d 的元素,其值为:%d\n", index, e);} else {printf("删除失败,位置不合法或链表为空\n");}break;case 6:traverse(L);break;case 7:L = List_HeadInsert(L);break;case 8:L = List_RearInsert(L);break;case 9:// 释放链表内存p = L;while (p != NULL) {q = p;p = p->next;free(q);}return 0;default:printf("无效的选择,请重新输入\n");break;}}return 0;
}bool InitList(LinkList &L) {L = (LNode*)malloc(sizeof(LNode));// 头结点 L->next = NULL;// 相邻节点赋值为null L->data = 0;// 头结点数据域用来记录表长度 return true;
}int Length(LinkList L) {int len = 0;LNode* p = L;while (p->next != NULL) {p = p->next;len++;}return len;
}LNode* GetElemByIndex(LinkList &L, int i) {int j = 0;LNode* p = traverseByIndex(L,i,j);return p; // p为NULL或者想查找的结点 
}LNode* GetElemByDate(LinkList &L, ElemType e) {LNode* p = L;while (p!= NULL && p->data != e) {p = p->next; } return p; // p为NULL或者想查找的结点 
}bool LinkListInsert(LinkList &L, int i, ElemType e) {int j = 0;LNode* p = traverseByIndex(L,i-1,j);if (p == NULL) { // 非法区域 return false;}LNode* s = (LNode*)malloc(sizeof(LNode));// 申请要插入的结点 s->data = e;s->next = p->next;p->next = s;return true; 
}bool ListDelete(LinkList &L, int i, ElemType &e) {int j = 0;LNode* p = traverseByIndex(L,i-1,j);if (p == NULL || j > i - 1) { // 指向空区域或i不合法 return false; }LNode* q = p->next;e = q->data;p->next = q->next;free(q);return true; 
}
LinkList List_HeadInsert(LinkList &L){LNode *s = NULL;ElemType x = 0;printf("请输入插入的元素,输入-1停止插入\n");scanf("%d",&x);while(x!=-1){s = (LNode*)malloc(sizeof(LNode));s->data = x;s->next = L->next;L->next = s;scanf("%d",&x);}return L; 
}
LinkList List_RearInsert(LinkList &L){LNode *s = NULL;LNode *q = L;ElemType x = 0;printf("请输入插入的元素,输入-1停止插入\n");scanf("%d",&x);while(x!=-1){s = (LNode*)malloc(sizeof(LNode));s->data = x;q->next = s;s->next = NULL;q = q->next;scanf("%d",&x);}return L; 
}
/**tool function*/
LNode* traverseByIndex(LNode* temp_p, int i,int &temp_j){LNode* p = temp_p;int j = 0;while (p!= NULL && j < i) {p = p->next;j++;}temp_j = j;return p;
}void traverse(LinkList L){LNode* p = L->next;while(p!=NULL){printf("%d\t",p->data);p = p->next;}
}

相关文章:

  • React-Redux
  • 4.26-count部分的渲染
  • 基于STM32定时器中断讲解(HAL库)
  • 聊聊Spring AI Alibaba的YuQueDocumentReader
  • Rule.issuer(通过父路径配置loader处理器)
  • 启动你的RocketMQ之旅(五)-Broker详细——消息传输
  • 学习Spire.Office for Java版本的科学实践
  • 硬件须知的基本问题2
  • Claude系列模型-20250426
  • 系统架构设计中的ATAM方法:理论、实践与深度剖析
  • AWS中国区ICP备案全攻略:流程、注意事项与最佳实践
  • Power BI企业运营分析——BCG分析
  • 1软考系统架构设计师:第一章系统架构概述 - 超简记忆要点、知识体系全解、考点深度解析、真题训练附答案及解析
  • 【教程】Docker运行gitlab容器
  • w307MES生产制造执行系统设计与实现
  • Unity AssetBundle (AB) 打包详解
  • 【新技术】微软 Azure Test Impact Analyzer (TIA) 全面解析
  • 29-算法打卡-字符串-KMP算法理论2-第二十九天
  • Adobe Photoshop(PS)2022 版安装与下载教程
  • 一篇入门之-评分卡变量分箱(卡方分箱、决策树分箱、KS分箱等)实操例子
  • 5月动漫|“爱死机”即将回归,《明末》或是下一个大IP?
  • “下山虎”张名扬一回合摘下“狮心”:你们再嘘一个给我听听
  • 文化体验+商业消费+服务创新,上海搭建入境旅游新模式
  • 新城市志|中国消费第一城,迎来“补贴力度最大”购物节
  • 对外投资增长、消费市场持续升温,中国经济砥砺前行
  • 财政部部长蓝佛安:中国将采取更加积极有为的宏观政策