链表(带头结点)的基础操作:(王道书的实现,附带main函数)

1.链表的初始化

2.头插法建立单链表

3.尾插法建立单链表

4.指定结点的前插

5.指定结点的后插

6.按位查询

7.按值查询

8.链表的长度

9.头插法逆置单链表

10.三指针法逆置单链表

11.La和Lb链表合并为Lc

12.删除指定结点

13.查找链表中间结点(快慢指针法)

14.在单链表中查找倒数第k个结点

15.删除单链表中的重复元素

16.打印单链表

/*单链表:每个结点除了存放数据元素外,还要存储指向下一个元素的指针
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存储,要消耗一定空间存放指针
*/
//1.带头结点的单链表
//结构体定义
/**/
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode {  //定义单链表结点类型ElemType data;   //每个节点存放一个数据元素struct LNode* next;   //指针指向下一个结点
}LNode,*LinkList;
//1.初始化一个单链表(带头结点)
bool InitList(LinkList& L) {L = (LNode*)malloc(sizeof(LNode));if (L == NULL) //内存不足,分配失败return false;L->next = NULL;  //头结点之后暂时还没有结点return true;
}
//2.头插法建立单链表
LinkList List_HeadInsert(LinkList& L) { //逆向建立单链表LNode* s;int x;L = (LinkList)malloc(sizeof(LNode));//创建头结点L->next = NULL; //初始化空链表printf("请输入链表:\n");scanf_s("%d", &x);    //输入结点的值while (x != 9999) {s = (LNode*)malloc(sizeof(LNode));//创建新的结点s->data = x;s->next = L->next;L->next = s;    //将新结点插入表中,L为头指针scanf_s("%d", &x);}return L;
}
//3.尾插法建立单链表
LinkList List_TailInsert(LinkList& L) {int x;L = (LinkList)malloc(sizeof(LNode));  //建立头结点LNode* s, * r = L;  //r为表尾指针printf("请输入链表:\n");scanf_s("%d", &x);    //输入结点的值while (x != 9999) {    //输入9999表示结束s = (LNode*)malloc(sizeof(LNode));s->data = x;r->next = s; //在r结点后插入元素xr = s; //r指向新的表尾结点(永远保持r指向最后一个结点)scanf_s("%d", &x);}r->next = NULL;    //表尾指针置空return L;
}
//3.前插操作:在p结点之前插入结点s
bool InsertPriorNode(LNode* p, int e) {if (p == NULL) { //判断p指针是否指向空return false;}LNode* s = (LNode*)malloc(sizeof(LNode)); //创建一个新结点if (s == NULL) {//内存分配失败return false;}s->next = p->next;p->next = s;   //新结点s连接到p之后s->data = p->data;   //将p结点中的值复制到s的结点中p->data = e;   //p中元素覆盖为ereturn true;
}
//王道书版本的前插操作
bool InsertPriorNode1(LNode* p, LNode* s) {if (p == NULL || s == NULL) {return false;}s->next = p->next;p->next = s; //连接p和sint temp = s->data;  //设置中间变量暂存s->data,交换数据域部分s->data = p->data;p->data = temp;return true;
}
//4.后插操作
//实现步骤:1.循环找到第i-1个结点。2.在该结点进行后插操作
//(1)在第i个位置插入元素e(带头结点)
bool InsertNextNode(LNode* p, int e) {if (p == NULL) {return false;}LNode* s = (LNode*)malloc(sizeof(LNode));//创建新结点if (s == NULL) {   //内存分配失败return false;}s->data = e;//用s结点保存数据es->next = p->next;p->next = s;  //将结点s连接到p之后return true;
}
//5.指定节点的后插操作
bool ListInsert(LinkList &L ,int i,int e) {if (i < 1) {return false;}LNode* p;   //指针p指向当前扫描到的结点int j = 0;  //当前p指向的是第几个结点p = L;   //L指向头结点,头结点是第0个结点(不存数据)while (p != NULL && j < i - 1) {p = p->next;//指针p依次往后遍历链表中的元素j++;}return InsertNextNode(p, e);
}//6.按位查询
//注:强调这是一个单链表 --使用LinkList。强调这是一个结点  --使用LNode*
LNode* GetElem(LinkList L, ElemType i) {int j = 1;LNode* p = L->next;if (i == 0)return L;if (i < 1)return NULL;while (p != NULL && j < i) {p = p->next;j++;}return p;
}
//7.按值查找
LNode* LocateElem(LinkList L, ElemType e) {LNode* p = L->next;//int i = 0;//从第1个结点开始查找数据域为e的结点while (p != NULL && p->data != e){p = p->next;//i++;}return p; //找到后返回该结点指针,否则返回NULL
}
//8.求表的长度
int Length(LinkList L) {int len = 0;   //统计表长LNode* p = L;while (p->next != NULL) {p = p->next;len++;}return len;
}//9.头插法逆置单链表
LinkList Reverse_1(LinkList L) {LNode* p, * r;//LinkList p,r;//循环前操作p = L->next;L->next = NULL;//循环操作while (p != NULL) {r = p->next;p->next = L->next;L->next = p;p = r;}return L;
}
//10.三指针法逆置单链表(pre,p,r),r作工作指针,暂存结点地址
LinkList Reverse_2(LinkList L) {LinkList pre, p, r;//循环前操作p = L->next;r = p->next;p->next = NULL;//循环操作while (r != NULL) {pre = p;p = r;r = r->next;p->next = pre;}L->next = p;return L;
}
//11.有序链表的合并
//注:链表必须有序!
void MergeLinkList(LinkList& La, LinkList& Lb, LinkList& Lc) {LinkList p, q, r;p = La->next;q = Lb->next;Lc = La;r = Lc;while (p && q) {if (p->data <= q->data) {r->next = p;r = p;p = p->next;}else {r->next = q;r = q;q = q->next;}}//如果p不空,则把p后面剩余节点链表接起来,即r->next=p;否则r->next=q;r->next = p ? p : q;  //相当于if(p) r->next=p; else r->next=q;delete Lb;
}
//按位序删除(带头结点)
// 删除表L中第i个位置的元素,并用e返回删除元素的值。
//步骤:1.循环找到第i-1个结点。2.删除第i个结点
//时间复杂度O(n)
bool ListDelete(LinkList& L, int i, ElemType& e) {if (i < 1)return false;//LNode* p = GetElem(L,i-1);LNode* p;  //创建一个指针pint j = 0;    //当前p指向的是第几个结点p = L;   //L指向头结点,头结点是第0个结点(不存数据)while (p != NULL && j < i - 1) {   //循环找到第i-1个结点p = p->next;   //指针p每次移动一个元素j++;}if (p == NULL) {  //判断p指针指向的链表元素是否为空return false;}if (p->next == NULL) { //若i-1个结点之后其他结点return false;}LNode* q = p->next;//创建一个q指针指向p的后继e = q->data; //把q的data赋值给ep->next = q->next;  //p与q的后继相连,即断开qfree(q);  //释放掉q结点return true;
}
//12.删除指定结点p
bool DeleteNode(LNode* p) {if (p == NULL) {   //如果p指向的元素是空的话return false;}LNode* q = p->next; //创建指针q指向p的后继元素//下面一条代码:是和后继结点交换数据域,这里存在一个问题,如果p是最后一个结点,他没有后继结点,不能交换数据域p->data = p->next->data;p->next = q->next;   //将q结点从连接中断开free(q);    //释放q结点return true;
}
//13.查找链表中间结点(快慢指针法)
//思路:设置一个快指针p,每次走两步,一个满指针q,每次走一步,
//当快指针走到表尾时,此时慢指针即为中间结点。
LinkList FindMiddle(LinkList L) {LinkList p, q;//p为快指针,q为慢指针p = L;q = L;//p和q开始都指向头结点//循环条件解读,大家可以尝试着画一下,不懂私信我//1.偶数单链表,p=NULL结束//2.奇数单链表,p->next=NULL结束。while (p != NULL && p->next != NULL) {  p = p->next->next;   //快指针每次走两步q = q->next;  //慢指针每次走一步}return q;//返回中间结点的指针
}
//14.补充,如何在单链表中查找倒数第k个结点?
//思路:快慢指针法,慢指针q先不动,快指针p先走k-1步,然后两只真一起以同样的速度走。
//当快指针走到表尾时,慢指针刚好停留在倒数第k个结点
//注:此时的慢指针和快指针都是一次走一步
LinkList Find_reverse_k(LinkList L, int k) {LinkList p, q;p = L->next;  //p为快指针q = L->next; //q为慢指针while (p->next != NULL) {if (--k <= 0)   //当k减到0是,慢指针q开始走q = q->next;p = p->next;}if (k > 0)//k不合法,k不能大于链表的长度return NULL;elsereturn q;//返回倒数第k个结点
}
//15.删除单链表中的重复元素
void Delete_Repeate_Elem(LinkList& L) {LinkList p, q;int x;int n = 10;int* flag = new int[ n + 1];//定义flag数组,分配n+1个空间,0空间不用。for (int i = 0; i < n + 1; i++) {  //初始化flag数组,每个元素都为0flag[i] = 0;}p = L;//p指向头结点while (p->next != NULL) {x = abs(p->next->data);if (flag[x] == 0) {flag[x] = 1;//标志出现过的元素p = p->next;  //p指针每次后移一位}else {//出现重复q = p->next;p->next = q->next;//普通的链表删除操作//free(q);delete q;}}delete[]flag;
}
//16.打印单链表
void PrintList(LinkList L) {LinkList p; //创建一个指针pp = L->next;   //p指向头结点printf("链表为:\n");while (p != NULL) {  //判断p是否为空printf("%d ", p->data);   //依次输入p的数据p = p->next;  //指针p每循环一次指向下一个结点元素}printf("\n");
}int main() {//LNode* p = L->next;//LNode* s = NULL;// LNode* p = (LNode*)malloc(sizeof(LNode));LinkList L, La, Lb, Lc;InitList(L);int command;while (1) {printf("*******************************\n");printf("下面是操作界面\n");printf("1.链表的初始化                        2.头插法建立单链表\n");printf("3.尾插法建立单链表                    4.指定结点的前插\n");printf("5.指定结点的后插                      6.按位查询\n");printf("7.按值查询                            8.链表的长度\n");printf("9.头插法逆置单链表                    10.三指针法逆置单链表\n");printf("11.La和Lb链表合并为Lc                 12.删除指定结点\n");printf("13.查找链表中间结点(快慢指针法)     14.在单链表中查找倒数第k个结点\n");printf("15.删除单链表中的重复元素             16.按位序删除 \n");printf("17.退出 \n");printf("*******************************\n");printf("请输入您要进行的操作:\n");scanf_s("%d", &command);switch (command){case 1://1.链表的初始化if (InitList(L) == 1) printf("初始化成功!\n");else printf("初始化失败!\n");break;case 2://2.头插法建立单链表List_HeadInsert(L);  break;case 3://3.尾插法建立单链表List_TailInsert(L);break;case 4://4.指定结点的前插前插printf("进行前插操作(在第二个位置前插8):\n");InsertPriorNode(L->next->next, 8);break;case 5://5.指定结点的后插操作printf("进行后插操作(在第2个位置后插5):\n");ListInsert(L, 3, 5);//前插(王道书版本)//InsertPriorNode1(p, s);//按位序删除//ListDelete(L, 4, e);//printf("删除的元素为:%d\n",e);break;case 6://6.按位查询int i;printf("请输入要查结点的位次:\n");scanf_s("%d", &i);printf("按位查找的结点值为:%d\n", GetElem(L, i)->data);printf("按位查找的结点地址为:%d\n", GetElem(L, i));break;case 7://7.按值查询int e;printf("请输入要查结点的值:\n");scanf_s("%d", &e);printf("按值查找的结点的地址为:%d\n", LocateElem(L, e));break;case 8://8.链表长度printf("链表的长度为%d\n", Length(L));break;case 9://9.头插法逆置单链表Reverse_1(L);break;case 10://10.三指针法逆置单链表Reverse_2(L);break;case 11://11.链表La和Lb的合并操作InitList(La);InitList(Lb);InitList(Lc);//尾插法建立单链表La,Lbprintf("尾插法建立La");List_TailInsert(La);PrintList(La);printf("尾插法建立Lb");List_TailInsert(Lb);PrintList(Lb);printf("进行链表La和Lb的合并操作:\n");MergeLinkList(La, Lb, Lc);PrintList(Lc);break;case 12://12.指定结点删除DeleteNode(L->next);PrintList(L);break;case 13://13.查找链表中间结点(快慢指针法)printf("链表的中间结点的值为:%d\n", FindMiddle(L)->data);printf("链表的中间结点的地址为:%d\n", FindMiddle(L));break;case 14://14.补充,在单链表中查找倒数第k个结点int k;printf("请输入要查找倒数第几个结点:\n");scanf_s("%d", &k);printf("倒数第%d个结点的值为%d\n",k,Find_reverse_k(L, k)->data);//printf("倒数第%d个结点的地址为%d\n", k, Find_reverse_k(L, k));break;case 15://15.删除单链表中的重复元素Delete_Repeate_Elem(L);break;case 16://16.按位序删除(带头结点)// 删除表L中第i个位置的元素,并用e返回删除元素的值。int x1,z;printf("请输入要删除元素的位置:\n");scanf_s("%d", &z);ListDelete(L,z,x1);printf("删除的元素为:%d\n",x1);break;case 17://17.退出break;default:printf("输入错误\n");break;}PrintList(L);printf("\n");}return 0;
}

我来带大家运行一下上面我写的代码

1.首先我们必须要初始化滴

2.然后头插法(倒序)建立单链表(注:头插法和尾插法输入元素都是9999结束哈,忘记写了)

 3.试一下尾插法(正序)倒序建表

 4.前插操作,这里我在main函数中指定的是在第二个元素前插一个8

InsertPriorNode(L->next->next, 8);

5.后插操作,这里是在第3个位置上插入5(注:因为带头结点,所以函数中的第三个位置其实是实际的第二个位置)

 ListInsert(L, 3, 5);

 6.按位查询,可以查询到结点的地址,同样也可以获得结点的值。

 7.按值查询,可以查询到结点的地址

8.查看链表的长度

9.头插法逆置单链表

10.三指针法逆置单链表

11.La和Lb链表合并为Lc

注意嗷,你的输入的La和Lb必须有序

我再注:这里有L是因为我在循环外放了个PrintList(L),这样就不用每一次小情况都单独加一个 PrintList(L)了,太懒了 哈哈哈

12.删除指定结点(我这里在main函数中写的是删除第一个数据结点(头结点后面那个))

注:为什么会输出两次删除指定结点后的呢,是因为我在case12里多加了个PrintList(L),尴尬了,凑合着看吧,我一会要去背单词了没时间改了。

后面是补充的:

13.查找链表中间结点(快慢指针法)

14.在单链表中查找倒数第k个结点

15.删除单链表中的重复元素

16.按位序删除元素,(即删除第z个位置的元素并用x1返回删除的元素值)

最后啊,这是头插法逆置和三指针法逆置的图解(看不懂或者看不清的加我QQ2725271785可以问我)写的有点丑,大家凑活着看

单链表的基础操作(带头结点)相关推荐

  1. 单链表的创建(带头结点和不带头结点)

    伪代码: 创建结点 创建头结点(单独定义一个结构体来保存单链表的首地址和尾地址还有链表的长度) 创建带头结点的单链表 注意:创建头结点中的首尾指针都要指空,长度等于0: 从终端接收数据 创建结点保存数 ...

  2. 数据结构单链表的基础操作(C语言)

    效果如图: 代码及详情如下:(文末总结) 目录 //主函数 //菜单 //创建链表 //插入结点 //删除结点 //查找结点 //链表长度 //打印链表 //清空链表 //逆置链表 //删除偶数元素结 ...

  3. 单链表的基础操作(头插法、尾插法、插入和删除)

    一.链表的建立(头插法和尾插法) 1.头插法:把后建立的结点插在头部.用这种方法建立起来的链表的实际顺序与输入顺序刚好向反,输出时为倒序! 下面附上代码: struct node *headcreat ...

  4. 【c++版数据结构】之循环单链表的实现(带头结点以及尾节点)

    所实现的循环单链表的结构例如以下图所看到的: 循环单链表的实现,和上一篇文章单链表的实现大致同样点击打开链接,略有差别: 1:循环推断的条件不再是s == NULL或者s->next == NU ...

  5. 单链表的基础操作练习

    对单链表的创建,查找,删除,添加. #include<stdio.h > #include<stdlib.h> struct link {int date;struct lin ...

  6. (不带头结点的)单链表增删查改,逆置单链表(两种方法),求两个单链表的第一个公共结点,合并两个单链表,单循环链表中判断第一个入环点,约瑟夫环

    补充了每个算法的基本思想,并且画了思路图,源代码都经过调试成功 1.SlistNode.c文件 (1) (不带头结点的)单链表增删查改 #include "SlistNode.h" ...

  7. TypeScript算法专题 - blog2 - 单链表节点的索引、结点删除与链表反转

    TypeScript算法专题 - [单链表2] 单链表节点的索引.结点删除与链表反转 李俊才 CSDN:jcLee95 邮箱:291148484@163.com 专题目录:https://blog.c ...

  8. java 结构体_Java实现单链表的简单操作

    文章目录 前言 一.基本实现思路 二.代码实现 1.定义结点类2.定义链表类3.测试调用4.结果 总结 前言 用Java实现单链表的简单操作,阅读本文和上一篇文章体会Java中类与C++中结构体指针的 ...

  9. 单链表的简单操作与演示

    单链表的简单操作与演示 单链表 单链表概念和简单的设计 单链表是一种链式存取的数据结构,链表中的数据是以结点来表示的,每个结点由元素和指针构成. 元素表示数据元素的映象,就是存储数据的存储单元:指针指 ...

最新文章

  1. Rocketmq原理最佳实践
  2. 如何给容器服务的Docker增加数据盘
  3. emqx使用webhook数据持久化到mysql
  4. 【HDU - 1847】Good Luck in CET-4 Everybody! (巴什博奕,PN图或sg函数)
  5. MIPI CSI-2规范一——概述及层级
  6. hdu4614 Vases and Flowers 线段树+二分
  7. JavaScript学习(五十六)—寄生式继承(临时构造器的使用)
  8. webpack打包vue项目IE报错,“对象不支持“use”属性或方法”
  9. 极速办公(word)如何文字居中
  10. 酒店小程序线上营销解决方案-思途智旅
  11. inSSIDer无线信号扫描工具
  12. 一文掌握数仓中auto analyze的使用
  13. 如何运营一家数据标注公司 (基础架构篇)
  14. 【贪玩巴斯】大一暑假自学Linux笔记记录Day4 //纯新手入门基于ubantu
  15. OBS studio 显示器捕获黑屏问题
  16. 缺失MSVCP120D.dll和MSVCR120D.dll
  17. 这两天猎头的电话有点多
  18. Away3D学习笔记(4)
  19. 李开复:凭良心发微博 不认识网络推手
  20. phpword替换模板内容+解决中文乱码

热门文章

  1. Ubuntu资源暂时不可用 E: 无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用)
  2. 【嵌入式知识12】使用PWM实现LED呼吸灯
  3. Verilog学习笔记
  4. 旅游网项目案例以及源码
  5. ESP32+INMP441+DHT11+OLED+网页+Arduino——“智能”语音天气站(2):INMP441录音生成wav文件
  6. 通过Date函数制作倒计时程序,计算日期相差时间!
  7. 计算机应用vr好学吗,我想去学VR虚拟现实技术,虚拟现实培训难不难?,虚拟现实该怎么选,都来说说?...
  8. 【高质量github项目合辑】视频、文本的特征提取
  9. RabbitMQ work quene 模式
  10. SQL学习指南:SQL和大数据