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

C语言知识点补充(链表和队列)

文章目录

  • 单向链表总结快速一览
  • 链表
    • 链表创建
    • 链表遍历
    • 链表的释放
    • 链表的查找
    • 节点的删除
    • 节点的插入
    • 链表的排序
  • 双向链表
  • 队列
    • 链队列
      • 链表初始化
      • 链表入队
      • 链表出队


单向链表总结快速一览

链表创建:链表为空,该节点变为根节点,否则while循环到末尾加入新节点,for循环一个一个节点加入
链表遍历:while循环到末尾(末尾指向的NULL)
链表释放:判断根节点是否空,从根节点的地址一个一个释放,同样根节点要指向下一个节点
链表查找:和查找同理,while循环迭代
节点删除:看通过数据域还是指针域判断删除哪个节点,分为链表为空,不操作;删除根节点,下一节点变根节点;删除中间元素,迭代寻找中要保存上下节点地址,方便删除后重建链表。这里刚开始上下节点地址变量存放的都是根节点,这样根节点和中间元素删除就统一在一起了。
节点插入:同理分为链表为空还是,插入根节点,插入中间等

链表

操作系统里面因为用到非常多

链表分为单向链表和双向链表,使用最多的是双向链表

链表是一种物理存储上非连续,但通过内部指针链接次序,实现可以线性访问的结构。

链表:节点,动态生成(malloc)
节点包括:存储数据的数据域、存储下一节点指针的指针域

在这里插入图片描述

typedef struct studentint num;char name[20]struct student *next;}STU;

双向链表就是节点里有两个指针,分别指向前一节点和后一节点

链表是通过节点把离散的数据链接成一个表,通过对节点的插入和删除操作从而实现对数据的存取。而数组是通过开辟一段连续的内存来存储数据,这是数组和链表最大的区别。数组有起始地址和结束地址,而链表是一个圈,没有头和尾之分, 但是为了方便节点的插入和删除操作会人为的规定一个根节点。

链表创建

整体思想是,一个单向的链表,第一个根节点是通过看是不是NULL指针来判断,后面节点的添加是通过while循环到尾部,加入新节点

typedef struct student
{
//数据域
int score;
....//指针域
sturct student *next;
}STU;void link_creat_head(STU **p_head,STU *p_new)  //使用双重指针是因为需要修改根节点的地址
{STU *p_mov = *p_head;if(*p_head == NULL)	//当第一次加入链表为空时,head执行p_new{*p_head = p_new;p_new->next=NULL;}else //第二次及以后加入链表{while(p_mov->next!=NULL){p_mov=p_mov->next;	//找到原有链表的最后一个节点}p_mov->next = p_new;	//将新申请的节点加入链表p_new->next = NULL;}
}int main()
{STU *head = NULL,*p_new = NULL;int num,i;printf("请输入链表初始个数:\n");scanf("%d",&num);for(i = 0; i < num;i++){p_new = (STU*)malloc(sizeof(STU));//申请一个新节点printf("请输入学号、分数、名字:\n"); //给新节点赋值scanf("%d %d %s",&p_new->num,&p_new->score,p_new->name);link_creat_head(&head,p_new);	//将新节点加入链表}
}

链表遍历

//链表的遍历
void link_print(STU *head)
{STU *p_mov;//定义新的指针保存链表的首地址,防止使用head改变原本链表p_mov = head;//当指针保存最后一个结点的指针域为NULL时,循环结束while(p_mov!=NULL){//先打印当前指针保存结点的指针域printf("num=%d score=%d name:%s\n",p_mov->num,\p_mov->score,p_mov->name);//指针后移,保存下一个结点的地址p_mov = p_mov->next;}
}

链表的释放

重新定义一个指针q,保存p指向节点的地址,然后p后移保存下一个节点的地址,然后释放q对应的节点

 //链表的释放,这里是从根节点全部释放void link_free(STU **p_head){//定义一个指针变量保存头结点的地址STU *pb=*p_head; while(*p_head!=NULL){//先保存p_head指向的结点的地址pb=*p_head;//p_head保存下一个结点地址*p_head=(*p_head)>next;//释放结点并防止野指针free(pb);pb = NULL;}}

链表的查找

//链表的查找
//按照学号查找
STU * link_search_num(STU *head,int num)
{STU *p_mov;//定义的指针变量保存第一个结点的地址p_mov=head;//当没有到达最后一个结点的指针域时循环继续while(p_mov!=NULL){//如果找到是当前结点的数据,则返回当前结点的地址if(p_mov->num == num)//找到了{return p_mov;}//如果没有找到,则继续对比下一个结点的指针域p_mov=p_mov->next;}//当循环结束的时候还没有找到,说明要查找的数据不存在,返回NULL进行标识return NULL;//没有找到
}//按照姓名查找
STU * link_search_name(STU *head,char *name)
{STU *p_mov;p_mov=head;while(p_mov!=NULL){if(strcmp(p_mov->name,name)==0)//找到了,string compare{return p_mov;}p_mov=p_mov->next;}return NULL;//没有找到
}

节点的删除

如果链表为空,不需要删除;
如果删除的是第一个结点,则需要将链表首地址的指针保存第一个结点的下一个结点的地址;
如果删除的是中间结点,则找到中间结点的前一个结点,让前一个结点的指针域保存这个结点的后一个结点的地址即可;

//链表结点的删除
void link_delete_num(STU **p_head,int num)
{STU *pb,*pf;  //前一个节点和后一个节点pb=pf=*p_head;if(*p_head == NULL)//链表为空,不用删{printf("链表为空,没有您要删的节点");\return ;}while(pb->num != num && pb->next !=NULL)//循环找,要删除的节点{pf=pb;pb=pb->next;}if(pb->num == num)//找到了一个节点的num和num相同{if(pb == *p_head)//要删除的节点是头节点{//让保存头结点的指针保存后一个结点的地址*p_head = pb->next;}else{//前一个结点的指针域保存要删除的后一个结点的地址pf->next = pb->next;}//释放空间free(pb);pb = NULL; //重要!!!}else//没有找到{printf("没有您要删除的节点\n");}
}

节点的插入

这里按学号顺序插入,不是指定位置插入,实际中也没有什么意义,指定位置插入

//链表的插入:按照学号的顺序插入
void link_insert_num(STU **p_head,STU *p_new)
{STU *pb,*pf;pb=pf=*p_head;if(*p_head ==NULL)// 链表为空链表{*p_head = p_new;p_new->next=NULL;return ;}while((p_new->num >= pb->num)  && (pb->next !=NULL) ){pf=pb;pb=pb->next;}if(p_new->num < pb->num)//找到一个节点的num比新来的节点num大,插在pb的前面{if(pb== *p_head)//找到的节点是头节点,插在最前面{p_new->next= *p_head;*p_head =p_new;}else{pf->next=p_new;p_new->next = pb;}}else//没有找到pb的num比p_new->num大的节点,插在最后{pb->next =p_new;p_new->next =NULL;}
}

链表的排序

如果链表为空,不需要排序。
如果链表只有一个结点,不需要排序。
先将第一个结点与后面所有的结点依次对比数据域,只要有比第一个结点数据域小的,则交 换位置。
交换之后,拿新的第一个结点的数据域与下一个结点再次对比,如果比他小,再次交换,依 次类推。
第一个结点确定完毕之后,接下来再将第二个结点与后面所有的结点对比,直到最后一个结 点也对比完毕为止。

//链表的排序
void link_order(STU *head)
{STU *pb,*pf,temp;pf=head;if(head==NULL){printf("链表为空,不用排序\n");return ;}if(head->next ==NULL){printf("只有一个节点,不用排序\n");return ;}while(pf->next !=NULL)//以pf指向的节点为基准节点,{pb=pf->next;//pb从基准元素的下个元素开始while(pb!=NULL){if(pf->num > pb->num){temp=*pb;*pb=*pf;*pf=temp;temp.next=pb->next;pb->next=pf->next;pf->next=temp.next;}pb=pb->next;}pf=pf->next;}
}

双向链表

类似的,创建一个节点作为头节点,将两个指针域都保存NULL;先找到链表中的最后一个节点,然后让最后一个节点的指针域保存新插入节点的地址,新插入节点的两个指针域,一个保存上一个节点的地址,一个保存NULL;

#include <stdio.h>
#include <stdlib.h>//定义结点结构体
typedef struct student
{//数据域int num;		//学号int score;      //分数char name[20];  //姓名//指针域struct student *front;  //保存上一个结点的地址struct student *next;   //保存下一个结点的地址
}STU;void double_link_creat_head(STU **p_head,STU *p_new)
{STU *p_mov=*p_head;if(*p_head==NULL)				//当第一次加入链表为空时,head执行p_new{*p_head = p_new;p_new->front = NULL;p_new->next = NULL;}else	//第二次及以后加入链表{while(p_mov->next!=NULL){p_mov=p_mov->next;	//找到原有链表的最后一个节点}p_mov->next = p_new;		//将新申请的节点加入链表p_new->front = p_mov;p_new->next = NULL;}
}void double_link_print(STU *head)
{STU *pb;pb=head;while(pb->next!=NULL){printf("num=%d score=%d name:%s\n",pb->num,pb->score,pb->name);pb=pb->next;}printf("num=%d score=%d name:%s\n",pb->num,pb->score,pb->name);printf("***********************\n");while(pb!=NULL){printf("num=%d score=%d name:%s\n",pb->num,pb->score,pb->name);pb=pb->front;}
}int main()
{STU *head=NULL,*p_new=NULL;int num,i;printf("请输入链表初始个数:\n");scanf("%d",&num);for(i=0;i<num;i++){p_new=(STU*)malloc(sizeof(STU));//申请一个新节点printf("请输入学号、分数、名字:\n");	//给新节点赋值scanf("%d %d %s",&p_new->num,&p_new->score,p_new->name);double_link_creat_head(&head,p_new);	//将新节点加入链表}double_link_print(head);
}

队列

栈只允许在一端进行插入或删除的线性表,所以是后进先出(Last In First Out)LIFO
队列只允许在一端进行插入操作,在另一端进行删除操作,先进先出(First In First Out)的线性表,简称FIFO
队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针 front指向队头元素,队尾指针 rear 指向队尾元素的下一个位置。

#define MAXSIZE 50	//定义队列中元素的最大个数
typedef struct{ElemType data[MAXSIZE];	//存放队列元素int front,rear;
}SqQueue;

为了防止上溢出,就是队头指针指向了队尾,所以要构成循环

队列初始化:

/*初始化一个空队列Q*/
Status InitQueue(SqQueue *Q){Q->front = 0;Q->rear = 0;return OK;
}

循环队列判队空:

/*判队空*/
bool isEmpty(SqQueue Q){if(Q.rear == Q.front){return true;}else{return false;}
}

循环队列的长度

/*返回Q的元素个数,也就是队列的当前长度*/
int QueueLength(SqQueue Q){return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}

循环队列入队:

/*若队列未满,则插入元素e为Q新的队尾元素*/
Status EnQueue(SqQueue *Q, ElemType e){if((Q->rear + 1) % MAXSIZE == Q->front){return ERROR;   //队满}Q->data[Q->rear] = e;   //将元素e赋值给队尾Q->rear = (Q->rear + 1) % MAXSIZE;  //rear指针向后移一位置,若到最后则转到数组头部return OK;
}

循环队列出队

/*若队列不空,则删除Q中队头元素,用e返回其值*/
Status DeQueue(SqQueue *Q, ElemType *e){if(isEmpty(Q)){return REEOR;   //队列空的判断}*e = Q->data[Q->front]; //将队头元素赋值给eQ->front = (Q->front + 1) % MAXSIZE;    //front指针向后移一位置,若到最后则转到数组头部
}

链队列

用链式存储结构

/*链式队列结点*/
typedef struct {ElemType data;struct LinkNode *next;
}LinkNode;
/*链式队列*/
typedef struct{LinkNode *front, *rear;	//队列的队头和队尾指针
}LinkQueue;

当Q->front == NULL 并且 Q->rear == NULL 时,链队列为空。

链表初始化

void InitQueue(LinkQueue *Q){Q->front = Q->rear = (LinkNode)malloc(sizeof(LinkNode));	//建立头结点Q->front->next = NULL;	//初始为空
}

链表入队

Status EnQueue(LinkQueue *Q, ElemType e){LinkNode s = (LinkNode)malloc(sizeof(LinkNode));s->data = e;s->next = NULL;Q->rear->next = s;	//把拥有元素e新结点s赋值给原队尾结点的后继Q->rear = s;	//把当前的s设置为新的队尾结点return OK;
}

链表出队

/*若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR*/
Status DeQueue(LinkQueue *Q, Elemtype *e){LinkNode p;if(Q->front == Q->rear){return ERROR;}p = Q->front->next;	//将欲删除的队头结点暂存给p*e = p->data;	//将欲删除的队头结点的值赋值给eQ->front->next = p->next;	//将原队头结点的后继赋值给头结点后继//若删除的队头是队尾,则删除后将rear指向头结点if(Q->rear == p){	Q->rear = Q->front;}free(p);return OK;
}
http://www.dtcms.com/a/356066.html

相关文章:

  • 8.变量和数据类型
  • 浏览器访问 ASP.NET Core wwwroot 目录下静态资源的底层实现
  • 多线程 线程池 并发
  • 机器视觉学习-day08-图像缩放
  • MBA/EMBA毕业论文写作总结
  • 第20章|轻松实现远程控制
  • NumPy 2.x 完全指南【三十二】通用函数(ufunc)之数学运算函数
  • 面试tips--JVM(1)--对象分配内存的方式TLAB
  • CTFshow系列——命令执行web61-68
  • C++之多态篇
  • 君正T31学习(四)- MT7682+VLC出图
  • 【python】python进阶——as关键字
  • 程序代码篇---类
  • SpringCloud Alibaba Nacos 注册中心/配置中心
  • SpringBoot 配置文件在运维开发中的应用
  • 基于springboot的商业店铺租赁系统
  • 在 Vue 前端(Vue2/Vue3 通用)载入 JSON 格式的动图
  • 校园文化活动管理系统设计与实现(代码+数据库+LW)
  • web前端知识——第一阶段
  • 【buildroot】【1. Buildroot版本与Linux内核调试对应关系】
  • 基于SpringBoot的旅游景点推荐系统【2026最新】
  • 域名所有权变更,需要重新备案吗
  • Day16_【机器学习分类】
  • 软磁材料与硬磁材料
  • MTK Linux DRM分析(十九)- KMS drm_framebuffer.c
  • LeetCode 141.环形链表
  • 软考中级【网络工程师】第6版教材 第4章 无线通信网 (上)
  • 8.28 JS移动端事件
  • HTTP 范围请求:为什么你的下载可以“断点续传”?
  • 现在购买PCIe 5.0 SSD是否是最好的时机?