总目录

文章目录

  • 1.基本操作
    • 1.1 结构体定义
    • 1.2 初始化
    • 1.3 判空
    • 1.4 按位序插入
    • 1.5 指定结点后插操作
    • 1.6 指定结点前插操作
    • 1.7 按位序删除
    • 1.8 按位查找
    • 1.9 按值查找
    • 1.10 表的长度
    • 1.11 单链表建立(尾插法)
    • 1.12 单链表建立(头插法)
    • 1.13 输出所有链表元素
  • 2.完整代码
  • 3.运行截图

各部分的解释已经以注释形式写于代码中。

1.基本操作

1.1 结构体定义

// 带头结点
typedef struct LNode {  // 定义单链表结点类型int data;   // 数据域struct LNode *next; // 指针域
} LNode, *LinkList;

1.2 初始化

// 初始化一个单链表
bool InitList(LinkList &L) {L = (LNode *)malloc(sizeof(LNode)); // 分配一个结点的空间,当作头结点// 内存分配失败得到情况if(L == NULL) {return false;}// 将头结点指针域设为NULL,表示头结点之后暂时还没有结点L->next = NULL;return true;
}

1.3 判空

// 判断单链表是否为空
bool Empty(LinkList L) {// 有头结点,那么头结点指针域指向NULL,单链表即为空if(L->next == NULL) {return true;} else {return false;}
}

1.4 按位序插入

// 在第i个位置插入元素e
bool ListInsert(LinkList &L, int i, int e) {// 判断插入位序是否存在问题if(i < 1)return false;LNode *p;   // 指针p指向当前扫描到的结点int j = 0;  // 当前p指向的是第几个结点p = L;      // 初始令p指向头结点// 通过循环,找到第i-1个结点// 如果i大于链表长度,则不可插入,此时p指向最后结点的指针域,即NULLwhile(p != NULL && j < i - 1) {p = p->next;j++;}// p值为NULL说明遍历完成后仍未为找到位序为i的结点,说明i大于链表长度,i不合法if (p == NULL) {return false;}// 在第i-1个结点后插入新结点LNode *s = (LNode *)malloc(sizeof(LNode));// 注意顺序,应该先让要插入结点的指针域指向后一结点// 之后再断开前一结点与后一结点的指针,使前一结点指向要插入结点s->data = e; s->next = p->next;//将结点s连到p后p->next = s;return true;
}

1.5 指定结点后插操作

// 指定结点后插操作:在p结点之后插入元素e
// LNode强调为结点
bool InsertNextNode(LNode *p, int e) {// p不合理的情况if(p == NULL) {return false;}LNode *s = (LNode *)malloc(sizeof(LNode));// 内存分配失败的情况if(s == NULL) {return false;}// 此处先将要插入结点的指针域指向前一个结点的指针域,即后一个结点的地址// 之后再令前一个结点的指针域指向要插入结点// 顺序不可颠倒,否则可能出现找不到之后结点的情况s->data = e;s->next = p->next;p->next = s;return true;
}

1.6 指定结点前插操作

// 指定结点前插操作:在p结点之前
// 此处可以通过一种特殊方法完成前插
// 本质上还是后插操作
bool InsertPriorNode(LNode *p, int e) {// p不合理的情况if(p == NULL) {return false;}LNode *s = (LNode *)malloc(sizeof(LNode));// 内存分配失败的情况if(s == NULL) {return false;}// 插入操作// 通过将s插入p结点之后,然后将s结点与p结点的数据域进行互换// 可以视作完成了前插操作s->next = p->next;p->next = s;s->data = p->data;p->data =e;return true;
}

1.7 按位序删除

// 按位序删除
bool ListDelete(LinkList &L, int i, int &e) {// i不合理的情况if(i < 1) {return false;}LNode *p;   // 指针p指向当前扫描到的结点int j = 0;  // 当前p指向的是第几个结点// 初始令p指向头结点,即第0个结点p = L;// 通过遍历找到第i - 1个结点,即要删除结点的前驱结点while(p != NULL && j < i - 1) {p = p->next;j++;}// 若p等于NULL,则要么此链表为空,要么i值大于链表长度,显然不合理if(p == NULL) {return false;}// p->next为空表示第i-1个结点之后已经没有其他结点了,无法进行删除操作if(p->next == NULL) {return false;}// 删除操作// 就是将前驱结点指针域指向后继结点LNode *q = p->next;    e = q->data;p->next = q->next;// 不要忘记释放空间!!!free(q); return true;
}

1.8 按位查找

// 按位查找,返回第i个元素
// 之前操作部分按位查找部分可以以此部分代码代替,提高代码复用性(封装)
LNode* GetElem(LinkList L, int i) {// 判断插入位序是否存在问题// 由于此是带头结点的情况,而头结点可以视作是第0个结点// 所以与之前遍历情况不同if(i < 0)return NULL;LNode *p;   // 指针p指向当前扫描到的结点int j = 0;  // 当前p指向的是第几个结点p = L;      // 初始令p指向头结点// 通过循环,找到第i个结点// 如果i大于链表长度,则找不到此节点,此时p指向最后结点的指针域,即NULL// 如果i初始为0,会直接返回头结点地址while(p != NULL && j < i) {p = p->next;j++;}return p;
}

1.9 按值查找

// 按值查找,找到数据域等于e的结点
LNode* LocateElem(LinkList L, int e) {LNode* p = L->next;// 从第一个结点开始查找数据域为e的结点// 找不到,则会在遍历后指向NULLwhile (p != NULL && p->data != e) {p = p->next;}return p;
}

1.10 表的长度

// 求表的长度
// 就是通过遍历所有结点,然后通过计数器来累加得到长度
int Length(LinkList L) {int len = 0;LNode* p = L;while(p->next != NULL) {p = p->next;len++;}return len;
}

1.11 单链表建立(尾插法)

// 尾插法
// 通过尾插法建立的链表,顺序为插入时元素的顺序
LinkList TailInsert(LinkList &L) {// 设置ElemType为整型int x;// 建立头结点(申请的空间为一个结点大小的空间)L = (LinkList)malloc(sizeof(LNode));// 初始化为空链表// 由于L在分配空间后,头指针可能并非指向NULL,而是其他任意区域,这可能导致问题发生// 动态分配的空间之前可能存在脏数据L->next = NULL;// 定义表尾指针// 对于尾插法,如果只通过头结点遍历的话// 每次插入时都要先遍历到表尾,时间复杂度为O(1+2+...+n-1)=O(n^2)// 所以选择增加一个尾指针,来避免遍历// s为要插入的结点,r为表尾指针LNode *s, *r = L;// 输入结点的值scanf("%d", &x);// 表示键入9999后建表结束while(x != 9999) {s = (LNode*)malloc(sizeof(LNode));s->data = x;r->next = s;// r指向新的表尾结点r = s;// 继续输入下一个结点的值,直至输入9999结束scanf("%d", &x);}// 尾指针置空r->next = NULL;return L;
}

1.12 单链表建立(头插法)

// 头插法
// 通过头插法建立的链表,顺序为插入时元素的逆序
LinkList HeadInsert(LinkList &L) {// 设置ElemType为整型int x;// 建立头结点(申请的空间为一个结点大小的空间)L = (LinkList)malloc(sizeof(LNode));// 初始化为空链表// 由于L在分配空间后,头指针可能并非指向NULL,而是其他任意区域,这可能导致问题发生// 动态分配的空间之前可能存在脏数据L->next = NULL;// 不同于尾插法,头插法无需定义更多指针LNode* s;// 输入结点的值scanf("%d", &x);// 表示键入9999后建表结束while(x != 9999) {s = (LNode*)malloc(sizeof(LNode));s->data = x;s->next = L->next;L->next = s;// 继续输入下一个结点的值,直至输入9999结束scanf("%d", &x);}
}

1.13 输出所有链表元素

// 输出所有链表元素
void PrintList(LinkList L) {if(Empty(L)) {printf("the list is empty");}LNode* p = L;while(p !=  NULL) {printf("%d  ", p->data);p = p->next;}
}

2.完整代码

#include<stdio.h>
#include<stdlib.h>// 带头结点
typedef struct LNode {  // 定义单链表结点类型int data;   // 数据域struct LNode *next; // 指针域
} LNode, *LinkList;// 初始化一个单链表
bool InitList(LinkList &L) {L = (LNode *)malloc(sizeof(LNode)); // 分配一个结点的空间,当作头结点// 内存分配失败得到情况if(L == NULL) {return false;}// 将头结点指针域设为NULL,表示头结点之后暂时还没有结点L->next = NULL;return true;
}// 判断单链表是否为空
bool Empty(LinkList L) {// 有头结点,那么头结点指针域指向NULL,单链表即为空if(L->next == NULL) {return true;} else {return false;}
}// 在第i个位置插入元素e
bool ListInsert(LinkList &L, int i, int e) {// 判断插入位序是否存在问题if(i < 1)return false;LNode *p;   // 指针p指向当前扫描到的结点int j = 0;  // 当前p指向的是第几个结点p = L;      // 初始令p指向头结点// 通过循环,找到第i-1个结点// 如果i大于链表长度,则不可插入,此时p指向最后结点的指针域,即NULLwhile(p != NULL && j < i - 1) {p = p->next;j++;}// p值为NULL说明遍历完成后仍未为找到位序为i的结点,说明i大于链表长度,i不合法if (p == NULL) {return false;}// 在第i-1个结点后插入新结点LNode *s = (LNode *)malloc(sizeof(LNode));// 注意顺序,应该先让要插入结点的指针域指向后一结点// 之后再断开前一结点与后一结点的指针,使前一结点指向要插入结点s->data = e; s->next = p->next;//将结点s连到p后p->next = s;return true;
}// 指定结点后插操作:在p结点之后插入元素e
// LNode强调为结点
bool InsertNextNode(LNode *p, int e) {// p不合理的情况if(p == NULL) {return false;}LNode *s = (LNode *)malloc(sizeof(LNode));// 内存分配失败的情况if(s == NULL) {return false;}// 此处先将要插入结点的指针域指向前一个结点的指针域,即后一个结点的地址// 之后再令前一个结点的指针域指向要插入结点// 顺序不可颠倒,否则可能出现找不到之后结点的情况s->data = e;s->next = p->next;p->next = s;return true;
}// 指定结点前插操作:在p结点之前
// 此处可以通过一种特殊方法完成前插
// 本质上还是后插操作
bool InsertPriorNode(LNode *p, int e) {// p不合理的情况if(p == NULL) {return false;}LNode *s = (LNode *)malloc(sizeof(LNode));// 内存分配失败的情况if(s == NULL) {return false;}// 插入操作// 通过将s插入p结点之后,然后将s结点与p结点的数据域进行互换// 可以视作完成了前插操作s->next = p->next;p->next = s;s->data = p->data;p->data =e;return true;
}// 按位序删除
bool ListDelete(LinkList &L, int i, int &e) {// i不合理的情况if(i < 1) {return false;}LNode *p;   // 指针p指向当前扫描到的结点int j = 0;  // 当前p指向的是第几个结点// 初始令p指向头结点,即第0个结点p = L;// 通过遍历找到第i - 1个结点,即要删除结点的前驱结点while(p != NULL && j < i - 1) {p = p->next;j++;}// 若p等于NULL,则要么此链表为空,要么i值大于链表长度,显然不合理if(p == NULL) {return false;}// p->next为空表示第i-1个结点之后已经没有其他结点了,无法进行删除操作if(p->next == NULL) {return false;}// 删除操作// 就是将前驱结点指针域指向后继结点LNode *q = p->next;    e = q->data;p->next = q->next;// 不要忘记释放空间!!!free(q); return true;
}// 删除指定节点
// 类似于之前前插操作,一般情况下,显然必须先遍历找到前驱结点
// 或者类似于之前前插的第二种方法,来规避遍历
bool DeleteNode(LNode *p) {// p不合理的情况if(p == NULL) {return false;}// 此操作可以理解为将p节点与其后继结点进行数据域交换// 之后再删除其后继结点,可以视作完成了删除,且避免了寻找前驱结点的遍历操作// 注意:如果p是最后一个结点则会报错,因为p->next即是NULL,不能从NULL获取数据域// 此种情况,只能通过之前的遍历寻找前驱结点来完成删除操作// 类似于之前插入,在找到前驱结点后,删除操作与按位序删除相同LNode *q = p->next;p->data = p->next->data;p->next = q->next;free(q);return true;
}// 按位查找,返回第i个元素
// 之前操作部分按位查找部分可以以此部分代码代替,提高代码复用性(封装)
LNode* GetElem(LinkList L, int i) {// 判断插入位序是否存在问题// 由于此是带头结点的情况,而头结点可以视作是第0个结点// 所以与之前遍历情况不同if(i < 0)return NULL;LNode *p;   // 指针p指向当前扫描到的结点int j = 0;  // 当前p指向的是第几个结点p = L;      // 初始令p指向头结点// 通过循环,找到第i个结点// 如果i大于链表长度,则找不到此节点,此时p指向最后结点的指针域,即NULL// 如果i初始为0,会直接返回头结点地址while(p != NULL && j < i) {p = p->next;j++;}return p;
}// 按值查找,找到数据域等于e的结点
LNode* LocateElem(LinkList L, int e) {LNode* p = L->next;// 从第一个结点开始查找数据域为e的结点// 找不到,则会在遍历后指向NULLwhile (p != NULL && p->data != e) {p = p->next;}return p;
}// 求表的长度
// 就是通过遍历所有结点,然后通过计数器来累加得到长度
int Length(LinkList L) {int len = 0;LNode* p = L;while(p->next != NULL) {p = p->next;len++;}return len;
}// 尾插法
// 通过尾插法建立的链表,顺序为插入时元素的顺序
LinkList TailInsert(LinkList &L) {// 设置ElemType为整型int x;// 建立头结点(申请的空间为一个结点大小的空间)L = (LinkList)malloc(sizeof(LNode));// 初始化为空链表// 由于L在分配空间后,头指针可能并非指向NULL,而是其他任意区域,这可能导致问题发生// 动态分配的空间之前可能存在脏数据L->next = NULL;// 定义表尾指针// 对于尾插法,如果只通过头结点遍历的话// 每次插入时都要先遍历到表尾,时间复杂度为O(1+2+...+n-1)=O(n^2)// 所以选择增加一个尾指针,来避免遍历// s为要插入的结点,r为表尾指针LNode *s, *r = L;// 输入结点的值scanf("%d", &x);// 表示键入9999后建表结束while(x != 9999) {s = (LNode*)malloc(sizeof(LNode));s->data = x;r->next = s;// r指向新的表尾结点r = s;// 继续输入下一个结点的值,直至输入9999结束scanf("%d", &x);}// 尾指针置空r->next = NULL;return L;
}// 头插法
// 通过头插法建立的链表,顺序为插入时元素的逆序
LinkList HeadInsert(LinkList &L) {// 设置ElemType为整型int x;// 建立头结点(申请的空间为一个结点大小的空间)L = (LinkList)malloc(sizeof(LNode));// 初始化为空链表// 由于L在分配空间后,头指针可能并非指向NULL,而是其他任意区域,这可能导致问题发生// 动态分配的空间之前可能存在脏数据L->next = NULL;// 不同于尾插法,头插法无需定义更多指针LNode* s;// 输入结点的值scanf("%d", &x);// 表示键入9999后建表结束while(x != 9999) {s = (LNode*)malloc(sizeof(LNode));s->data = x;s->next = L->next;L->next = s;// 继续输入下一个结点的值,直至输入9999结束scanf("%d", &x);}return L;
}// 输出所有链表元素
void PrintList(LinkList L) {if(Empty(L)) {printf("the list is empty");}LNode* p = L->next;while(p !=  NULL) {printf("%d  ", p->data);p = p->next;}printf("\n");
}int main() {LinkList L;InitList(L);TailInsert(L);PrintList(L);printf("get_2:%d\n",  GetElem(L, 2)->data);printf("locate_5:%d\n",  LocateElem(L, 5)->data);printf("length:%d\n", Length(L));InsertPriorNode(GetElem(L, 2), 7);PrintList(L);InsertNextNode(GetElem(L, 2), 5);PrintList(L);ListInsert(L, 3, 3);PrintList(L);int e;ListDelete(L, 3, e);PrintList(L);printf("e:%d\n", e);
}

3.运行截图

【数据结构】【王道】【线性表】单链表的实现及基本操作(带头结点)(可直接运行)相关推荐

  1. python的线性链表_Python线性表——单链表-阿里云开发者社区

    Python线性表--单链表 线性表简介 线性表是一种线性结构,它是由零个或多个数据元素构成的有限序列.线性表的特征是在一个序列中,除了头尾元素,每个元素都有且只有一个直接前驱,有且只有一个直接后继, ...

  2. 数据结构_Java_基于 线性表-单链表的初始化、逆序、去重、非递减序列的合并(开辟新链表先整体插入一个链表全部元素,再遍历另外一个链表寻找合适位置插入 、开辟新链表实现舍弃原链表)等操作实现

    写在前面 不久前学习了数据结构线性表-数组-链表的相关知识,用C/C++语言实现了 单链表的系列相关操作 .见往期博客: 数据结构实验2_C语言_基于顺序表的非递减有序表的合并.线性表元素的增.删.改 ...

  3. 理论基础 —— 线性表 —— 单链表

    [实现类] 单链表的基本思想就是用指针表示结点之间的逻辑关系,因此要正确的对指针变量.指针.指针所指结点.结点的值进行区分. 设 p 是一个指针变量,则 p 的值是一个指针,若指针 p 指向某个 No ...

  4. 数据结构-王道-线性表

    目录 线性表-链表 双链表 顺序表和链表的比较 线性表-链表 线性表的链式储存又称为链表,他是指通过一组任意的存储单元来存储线性表中的数据元素. 单链表 typedf struct LNode {El ...

  5. 第二章:2线性表---单链表表示和实现

    前言: 为避免在使用线性表顺序存储结构的时,需插入和删除需大量移动元素的弊端. 本节讨论线性表的另外一种表示方法---链式存储结构: 由于它不要求逻辑上相邻的元素在物理位置上相邻,因此它对元素的插入和 ...

  6. 线性表(单链表)实验

    一.实验目的 1.线性表(LINE)的概念:数据元素之间存在着线性关系. 2.线性表的顺序表示和实现. 3.线性表的链式表示和实现. 4.线性表的基本操作:初始化.插入.修改.删除.遍历. 二.实验内 ...

  7. 2.2线性表——单链表基本操作的实现

    注意:以下内容均省略思路,只有代码和时间复杂度.此内容为本人学习过程中的一些学习记录,如有错误,恳请各位指正.建议,末学将感激不尽! 目录 用结构指针描述单链表 初始化链表 判断空表 销毁单链表 清空 ...

  8. 线性表----单链表

    一,实验内容:链表的创建.插入与删除操作 二.程序清单 三.思考 l.如果需要将新结点 插入  到     第i个数据元素之后,算法将如何改动? 2. 双向链表和循环链表的定义和构造方法. //链表的 ...

  9. 数据结构c/c++ 头插法尾插法建立带头结点的单链表,以数组创建带头结点的单链表和不带头结点的单链表,输出打印单链表

    // // Created by 焦娇 on 2021/9/17. //#ifndef CHAPTER2_LINELINK_LLK_H #define CHAPTER2_LINELINK_LLK_H# ...

  10. c语言带头节点单链表创建,C语言建立带头结点的单链表

    满意答案 TS老妹儿 2017.08.31 采纳率:57%    等级:9 已帮助:1719人 单链表的生成有2种方式:头插法和尾插法. 1.头插法/************************* ...

最新文章

  1. AI战“疫“之路:​揭秘高精准无感测温系统的全栈AI 技术
  2. RxJava 从源码到使用
  3. B3log Solo 0.2.5.1 发布了!
  4. 两个栈实现一个队列,两个队列实现一个栈
  5. python网络爬虫(14)使用Scrapy搭建爬虫框架
  6. 增改删(python 版)
  7. can't select mysql database_ERROR 1006 (HY000) Can't create database (errno: 13) MySQL 5.6.12
  8. Windows8 Metro应用开发之C#(1)- 项目模板(Project Templates)
  9. 电力企业信息化建设方案之调度信息报送系统
  10. systemstate dump 介绍
  11. 来吧,给自己提个醒,哭着复习一下当初没好好学习的内容。
  12. php地图找房代码,vue-baidu-map简单实现地图找房
  13. 小马激活工具对比暴风激活工具
  14. 测井 时深转换 matlab,时深转换操作步骤.pdf
  15. SSIS(简单数据抽取过程介绍)
  16. 宝塔php7.1安装ioncube,如何使用宝塔安装ionCube扩展
  17. 麦考瑞大学计算机专业,麦考瑞大学计算机专业
  18. 小锤子要出来?老罗傲娇属性不再
  19. 分析型CRM软件能帮到你什么?
  20. 非递归中序遍历二叉树

热门文章

  1. mysql视图创建以及权限
  2. Linux中各种 command not found问题解决
  3. 利用爬虫数据做的研究_利用研究周增强数据科学能力
  4. matlab脚本栏中间有条线,word中间有一根线 word中间有一条线如何去掉
  5. 计算机病毒课后讨论题,《防治计算机病毒》答辩题目及解析
  6. 列表套字典三者匹配对应关系
  7. 混合开发Ionic+angular快速开发App
  8. 分割整数构成字母字符串(动态规划)
  9. 01_基于蒲公英R300A的异地组网PLC调试实现
  10. 家(单位)电信宽带,50M上行带宽,不用浪费了,发布Web(网站)应用刚好