链表的基本操作

单链表

  • 链表的基本操作
  • 一:单链表的基础操作
    • 二:单链表的建立
      • 头插法
      • 尾插法
    • 三:单链表的遍历
    • 四:单链表结点数目判断
    • 五:单链表的插入
    • 链表头插入
    • 任意结点插入
    • 链表尾部插入
    • 六:单链表的删除
    • 七 :单链表的查询

一:单链表的基础操作

为什么需要链表?

我们在使用数组存放数据是非常方便,但是由于数组的长度是固定的,所以当存储不同的元素数量时,就很容易出现问题。如果向数组中添加的数量大于数组大小时候,信息无法完全被保存。所以我们需要另一种存储方式来存储数据,其中存储的元素的个数不受限制。这种存储方式就是链表

链表结构示意


链表的基础知识:

每一个结点包含数据域和指针域:
数据域:存放用户需要的数据信息
指针域:指向下一个结点的地址

头指针:head就是头指针变量,我们把指向第一个结点的指针成为头指针
(无论链表是不是空,头指针是必不可少的
头结点:第一个结点前可以虚加一个头结点,头指针指向头结点,头结点的指针域(head->next)指向第一个实际有效的结点(即首元结点),头结点的数据域可以不使用,在单链表中可以不添加头结点
首元结点:第一个实际有效的结点

链表是环环相扣的,head头指针指向头结点,头结点指向首元结点,首元结点指向第二个结点…直到最后的结点。


二:单链表的建立

单链表的建立即从无到有创建一个链表,一个一个的分配结点的储存空间,然后输出每一个结点的数据域,然后建立结点之间的关系
单链表的建立可以分为两种方法,(1)头插法,(2)尾插法(更易理解)

头插法

即在单链表的头部插入新的结点的方法成为头插法。
数据读入顺序和链表的结点顺序正好相反
图解:
结点的结构体类型定义如下:

struct Student
{char name[100];   //学生姓名 int number;    //学号  struct Student *next;   //指向下一个结点的指针
};

头插法创建链表的函数代码如下

struct Student* Create()
{struct Student *Head;Head = (struct Student *)malloc(sizeof(struct Student));  //头指针 Head->next = NULL;    //头指针指向空 struct Student *s;int num;char a[20];while(1)   //当 学号>0时 {printf("please input the name:\n");scanf("%s",&a);printf("please input the number:\n");   scanf("%d",&num);if(num <= 0){break;}s = (struct Stduent *)malloc(sizeof(struct Student));s->number = num;strcpy(s->name,a);//用头插法创建链表 s->next = Head->next;    //新结点指向原来的首元结点 Head->next = s;      //链表的头结点指向新结点 }return Head;
}

运行结果:
倒序输出

步骤:

1.对头指针进行初始化,对其开辟动态空间,并且将头结点的指针域置空(顺序不要弄反)
2.定义指针变量s,用来指向新创建的结点
3.循环,在循环中开辟s(新结点)的动态空间,并赋予新结点数据域的信息
4.头插法关键的两行代码,新结点指向原来的首结点,链表的头结点指向新结点,结合上面的图解去了解(不可写反,写反之后,链表的头结点无法与新结点相连,无法创建链表,输出时只会循环输出该结点的信息


尾插法

图解:

代码实现:

struct Student *Creat()     //初始化链表
{struct Student *Head;Head = (struct Student *)malloc(sizeof(struct Student));Head->next = NULL;struct Student *r,*s; //定义指针变量r,s,r指向当前单链表的表尾结点//s用来指向新创建的结点r = Head;        //r指向头结点int num;char a[20];while(1){printf("please input the name:\n");scanf("%s",&a);printf("please input the number:\n");scanf("%d",&num);if(num <= 0){break;}s = (struct Student *)malloc(sizeof(struct Student));strcpy(s->name ,a);s->number = num;//尾插法创立链表 r->next = s;    //原来的结点指向新结点    r = s;     //r指向新的结点 }s->next = NULL;      //链表的尾结点指针为空 return Head;
}

正序输出
运行结果:

相较于头插法创立链表,尾插法更易于结合图解理解

步骤注意点:

1.在空链表时候,r指针指向头结点
2.尾插法的关键两行代码也不可以互相调换顺序,调换顺序的结果并不会循环输出,而是无法读取存储的信息,即输入了5个姓名,输出0个信息
3.注意的是,在循环结束时,新结点的指针域一定要指向空


三:单链表的遍历

代码实现:

void print(struct Student *Head)   //输出链表
{struct Student *Temp = Head->next ;    //临时指针指向首元结点 printf("****学生信息如下*****\n");while(Temp!=NULL){printf("姓名: %s\n",Temp->name );printf("学号: %d\n",Temp->number );printf("\n");Temp = Temp->next ; //移动临时指针到下一个结点 }
}

步骤注意点:

1.定义临时指针变量Temp指向首元结点
2.循环输出
3.关键:每输出一个结点的内容,就移动Temp指针到下一个结点的地址,如果是最后一个结点,指针指向NULL,循环结束


四:单链表结点数目判断

代码实现:

int length(struct Student *Head)  //链表长度计数
{struct Student *p = Head->next ;  //p指针指向首元结点 int iCount = 0; //计数器 while(p!=NULL){iCount++;p = p->next ;   //移动p指针到下一个结点的地址 }return iCount;
}

运行结果:

步骤注意点:

定义iCount计数器,每移动一次p指针且p指向不为空,iCount++;


五:单链表的插入

链表的插入,有三种方式,可以从链表的头部插入,可以从链表的尾部插入,也可以在指定位置进行插入。

链表头插入

图解:

代码实现:

void insert(struct Student *Head)    //在链表头部插入
{struct Student *s;s = (struct Student *)malloc(sizeof(struct Student));  //定义s指向新分配的空间 printf("please input the insert name:\n");scanf("%s",&s->name );printf("please input the insert number:\n");scanf("%d",&s->number );//s->next = Head->next ; //新结点的指针指向首元结点  Head->next = s;   //头结点的指针指向新结点
}

步骤注意点:

1.首先为插入的新结点分配内存
2.首先将新结点的指针指向链表的首元结点(s->next = Head->next)
3.将头结点的指针指向新结点(Head->next = s)


任意结点插入

图解:

代码实现:

void insert(struct Student *Head,int i)  //在第i个位置上插入新结点
{struct Student *p = Head;struct Student *s;int j = 0;while(j<i-1 && p != NULL)   //找到第i-1个地址 {p = p->next ;j++;}if(p != NULL){s = (struct Student *)malloc(sizeof(struct Student)); //定义s指向新分配的空间 printf("please input the insert name:\n");scanf("%s",&s->name );printf("please input the insert number:\n");scanf("%d",&s->number );//s->next = p->next ; //新结点指向原来第i个结点 p->next = s;   //新结点成为新链表第i个结点 }
}

步骤注意点:

1.首先找到链表第i-1个结点的地址p,如果存在,则在i-1后面插入第i个结点
2.为插入的新结点分配空间
3.注意插入的两行代码联系图解理解


链表尾部插入

图解:

代码实现:

void insert(struct Student *Head)    //在链表尾部插入
{ struct Student *p,*s;   //s是需要插入的结点 p = Head;while(p && p->next )    //找到最后一个结点p {p = p->next ;}s = (struct Student *)malloc(sizeof(struct Student));printf("please input the insert name:\n");scanf("%s",&s->name );printf("please input the insert number:\n");scanf("%d",&s->number );//p->next = s;       //尾结点指针指向新结点 s->next = NULL;    //新结点指针指向空 }

(尾部插入较好理解)
步骤注意点:

1.首先找到尾结点,即循环中的条件,每一次p指针移动到下一个结点的地址
2.插入时为新插入的结点分配空间
3.尾部插入的两行代码联系图解理解,新结点指针指向空


六:单链表的删除

图解:

代码实现:

void Delete(struct Student *Head,int pos)   //删除函数
{int j = 1;     //定义循环变量去寻找p结点 struct Student *p,*q;  //q是要删除的结点p = Head;  while(j < pos && p)    //p是q的前一个结点 {j++;p = p->next ;} if(p== NULL || p->next == NULL)   //如果没有,就报错 {printf("ERROR!\n");}else{q = p->next ;   //q指针指向需要删除的结点 p->next = q->next ;   //跨过删除的结点连接q的前一个结点和后一个结点 free(q);   //删除q结点 }
}

运行结果:

步骤注意点:

1.pos表示的是需要删除结点的位置,定义j用来控制循环次数
2.定义指针q和p,利用循环找到要删除结点之前的结点p,然后让q指向准备删除的结点即(q = p->next)
3.连接删除结点两边的结点(p->next = q->next)
4.用free(q)释放q指向的内存空间达到删除的目的


七 :单链表的查询

代码实现:
相当于遍历查找

struct Student *search(struct Student *Head,char name[])  //查询函数
{struct Student *p = Head->next ;   //p指针指向首元结点 while(p!=NULL)     {if(strcmp(p->name ,name) != 0)    //利用字符串函数查询 {p = p->next ;        //如果没有,移动p指针到下一个结点地址 }else{break;    //查到了跳出循环 }}if(p == NULL)     {printf("没有查到该学生的信息\n");}return p;    //返回指针p的地址
}

步骤注意点:

1.定义指针变量p,使其从首元结点开始到链表结束
2.利用字符串函数strcmp来查询


增删改查完整代码如下

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Student
{int number;char name[20];struct Student *next;
};struct Student *Creat()     //初始化链表
{struct Student *Head;Head = (struct Student *)malloc(sizeof(struct Student));Head->next = NULL;struct Student *r,*s;r = Head;int num;char a[20];while(1){printf("please input the name:\n");scanf("%s",&a);printf("please input the number:\n");scanf("%d",&num);if(num <= 0){break;}s = (struct Student *)malloc(sizeof(struct Student));strcpy(s->name ,a);s->number = num;//尾插法创立链表 r->next = s;    //原来的结点指向新结点    r = s;  //r指向新的结点 }s->next = NULL;      //链表的尾结点指针为空 return Head;
}int length(struct Student *Head)  //链表长度计数
{struct Student *p = Head->next ;  //p指针指向首元结点 int iCount = 0; //计数器 while(p!=NULL){iCount++;p = p->next ;   //移动p指针到下一个结点的地址 }return iCount;
}//void insert(struct Student *Head)    //在链表头部插入
//{//  struct Student *s;
//  s = (struct Student *)malloc(sizeof(struct Student));  //定义s指向新分配的空间
//  printf("please input the insert name:\n");
//  scanf("%s",&s->name );
//  printf("please input the insert number:\n");
//  scanf("%d",&s->number );
//  //
//  s->next = Head->next ; //新结点的指针指向首元结点
//  Head->next = s;   //头结点的指针指向新结点
//}void insert(struct Student *Head)    //在链表尾部插入
{ struct Student *p,*s;   //s是需要插入的结点 p = Head;while(p && p->next )    //找到最后一个结点p {p = p->next ;}s = (struct Student *)malloc(sizeof(struct Student));printf("please input the insert name:\n");scanf("%s",&s->name );printf("please input the insert number:\n");scanf("%d",&s->number );//p->next = s;       //尾结点指针指向新结点 s->next = NULL;    //新结点指针指向空 }//void insert(struct Student *Head,int i)  //在第i个位置上插入新结点
//{//  struct Student *p = Head;
//  struct Student *s;
//  int j = 0;
//  while(j<i-1 && p != NULL)   //找到第i-1个地址
//  {//      p = p->next ;
//      j++;
//  }
//  if(p != NULL)
//  {//      s = (struct Student *)malloc(sizeof(struct Student)); //定义s指向新分配的空间
//      printf("please input the insert name:\n");
//      scanf("%s",&s->name );
//      printf("please input the insert number:\n");
//      scanf("%d",&s->number );
//      //
//      s->next = p->next ; //新结点指向原来第i个结点
//      p->next = s;   //新结点成为新链表第i个结点
//  }
//}void Delete(struct Student *Head,int pos)   //删除函数
{int j = 1;     //定义循环变量去寻找p结点 struct Student *p,*q;  //q是要删除的结点p = Head;  while(j < pos && p)    //p是q的前一个结点 {j++;p = p->next ;} if(p== NULL || p->next == NULL)   //如果没有,就报错 {printf("ERROR!\n");}else{q = p->next ;   //q指针指向需要删除的结点 p->next = q->next ;   //跨过删除的结点连接q的前一个结点和后一个结点 free(q);   //删除q结点 }
}void print(struct Student *Head)   //输出链表
{struct Student *Temp = Head->next ;    //临时指针指向首元结点 printf("****学生信息如下*****\n");while(Temp!=NULL){printf("姓名: %s\n",Temp->name );printf("学号: %d\n",Temp->number );printf("\n");Temp = Temp->next ; //移动临时指针到下一个节点 }
}struct Student *search(struct Student *Head,char name[])  //查询函数
{struct Student *p = Head->next ;   while(p!=NULL){if(strcmp(p->name ,name) != 0)   {p = p->next ;}else{break;}}if(p == NULL){printf("没有查到该学生的信息\n");}return p;
}int main()
{int n;struct Student *Head;  //创建头指针 Head = Creat();   //返回头指针 print(Head);      //输出链表函数 printf("一共有%d个学生信息\n",length(Head));insert(Head);   //插入链表 print(Head);printf("please input the delete the number:\n");scanf("%d",&n);Delete(Head,n);    //删除结点函数 print(Head);char name[20];printf("please input the search name:\n");scanf("%s",&name);struct Student *p = search(Head,name);   //查询函数 printf("***查询信息如下:****\n");printf("姓名:%s\n",p->name );printf("学号:%d\n",p->number );
}

链表基本操作(详解)相关推荐

  1. c++实现双链表基本操作详解

    本人是来自双非本科的一只大一菜鸟,加入校队(我是吊车尾┭┮﹏┭┮)已有一个月的时间,现在开始写我的第一篇博客记录我的学习历程,废话不多说,如理解有误,请勘正. int r[N],l[N],e[N],i ...

  2. 链表c++语言 解析,C++ 单链表的基本操作(详解)

    链表一直是面试的高频题,今天先总结一下单链表的使用,下节再总结双向链表的.本文主要有单链表的创建.插入.删除节点等. 1.概念 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数 ...

  3. 二叉树前序中序后续线索树_后序线索二叉树怎么画 线索二叉树基本操作详解 - 办公软件 - 服务器之家...

    后序线索二叉树怎么画 线索二叉树基本操作详解 发布时间:2017-05-23 来源:服务器之家 遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序,中序或后序序列.这实际上 ...

  4. java 链表逆转_java 实现单链表逆转详解及实例代码

    java 实现单链表逆转详解 实例代码: class Node { Node next; String name; public Node(String name) { this.name = nam ...

  5. CSS基本操作详解及截图演示

    Web前端基础修炼 HTML基本标签详解与运行截图 CSS基本操作详解及截图演示 JavaScript基础(ECMAScript) JavaScript中DOM操作 JavaScript中BOM操作 ...

  6. 【MySQL基础】MySQL基本操作详解

    系列文章目录 第1篇:[MySQL基础]MySQL介绍及安装 第2篇:[MySQL基础]MySQL基本操作详解 文章目录 ✍1,数据库操作     

  7. DedeCMS织梦网站目录创建及后台基本操作详解

    DedeCMS织梦网站是比较流行的自助建站的一种方式,开放性使其具有很大的优势,也成为很多中小型企业及个人站长节省成本的选择.前面小编已经为大家分享了<新手搭建DEDE织梦网站的详细操作流程&g ...

  8. 链表 java详解_链表详解——Java版

    什么是链表? 链表是一个线性结构,但是存储的数据可以是非线性的.链表由一个个子节点构成,每个节点有两个部分:数据域和指针域,数据域就是实际存储数据的,指针域可以有一个和两个,单链表就是单个指针域指向后 ...

  9. Eclipse的安装与基本操作(详解配图)

    不为失败找理由,只为成功找方法.所有的不甘,都是因为还心存梦想,在你放弃之前,好好拼一把,只怕心老,不怕路长. 文章目录 一.简介 二.下载 三.使用Eclipse编写第一个Java程序 四.Ecli ...

  10. Git(一)之基本操作详解

    工作中一直都是用Git作为版本控制,只是知道简单的几个命令,没有去了解它的内部原理.所以周末有时间来系统学习. 现在的公司基本上都是用Git作为版本控制,当然也有SVN的. Git是目前世界上最先进的 ...

最新文章

  1. 【跃迁之路】【578天】程序员高效学习方法论探索系列(实验阶段335-2018.09.06)...
  2. python就业方向及工资-Python的就业的方向和前景
  3. linux C库编译
  4. 物联网平台构架系列 (四):Amazon, Microsoft, IBM IoT 解决方案导论 之 平台
  5. 成功解决AttributeError: module 'string' has no attribute 'find'
  6. 多边形三角剖分问题的综述
  7. SAP 电商云 Spartacus UI ROUTING_FEATURE 的使用场景
  8. 【itext学习之路】--2.设置pdf的一些常用属性
  9. 01背包、完全背包、多重背包问题的C++实现及路径记录
  10. android仿优酷菜单,Android编程实现仿优酷旋转菜单效果(附demo源码)
  11. 百度每周更新时间列表
  12. 常用应用层传输协议和端口
  13. QT最常用的字符串操作
  14. 基于公司云平台的素材归档系统(一)
  15. MySQL如何按天统计数据,没有记录的天自动补充0
  16. 四川多多开店:拼多多如何检查聊天记录
  17. 关于虚拟偶像的面部表情捕捉
  18. 应届生面试的5大技巧,附600字自我介绍范文
  19. 一个内嵌全国高校地图的小程序,走到哪导航到哪
  20. 如何解决跨站点请求伪造

热门文章

  1. jmeter SSL证书相关配置
  2. Linux多线程间通信和多进程间通信的方式
  3. 一班洽谈框架细化_理解层次:框架细化篇(以供参悟)
  4. 跟曹操学做事,向孔子学做人!
  5. tomcat6类加载器与类加载顺序
  6. 【VB与数据库】——数据库连接
  7. 软件测试的性能测试包括什么?
  8. ZYNQ - 以太网远程更新SD卡应用程序
  9. java drawstring 模糊_使用Java Graphics.drawString替换完全合理化?
  10. 百度AI手写诗文字识别使用攻略