数据结构之线性表——(二、链式存储结构-单链表)

链式存储结构以及基本运算的实现

背景:由于线性表的存储特点是用物理上的相邻实现逻辑上的相邻,他要求用连续的存储单元顺序存储线性表中的各个元素,所以,对线性表中的元素进行插入、删除时要移动元素,很影响运算的效率。而在链式存储结构中,存储单元的地址可连续,也可不连续,这就意味着这些数据元素可以存放在内存中未被占用的任何位置。它不要求逻辑上相邻的两个数据元素物理上也相邻,其逻辑关系是通过“链”建立起数据元素之间的关系,因此对线性表的插入、删除操作时不需要移动数据,提高了效率。

一、单链表

单链表的表示

链表是通过一组任意的存储单元来存储线性表中的数据元素的,每个数据元素(ai),除了存放自身的数据外,还需要存放其后继ai+1所在的存储单元的地址,这两部分信息组成一个“结点”,存放数据元素信息的数据域为data,存放其后继地址的称为指针域next,所以n个数据元素的线性表通过每个结点的指针域连成一条“链”,故称为链表。其结点的结构如下:

注:在这里我们称为单链表,单链表是因为其每个结点中只有一个指向后继的指针,所以称为单链表。

1、单链表的定义

单链表是由一个个结点构成的,使用单链表首先要定义一个结点,也可称为结构体,其定义如下:

typedef struct node
{DataType data;
struct node *next;
}Lnode,*LinkList;
/*Lnode是结构体别名,用其定义结点如:Lnode n;
但是用  *LinkList定义的是指针。 */

定义头指针变量

LinkList H;           //linkList是指向Lnode类型结点的指针类型


  在上图中,顺序存储结构的地址是连续的,而且其数据域中的数据也是按照逻辑结构自上而下的。在链式存储结构中,每个结点(node)不仅存储了数据本身(A),还存放了下一个结点的地址(0x5),但头指针中没有存放数据,它存放的是第一个结点(A)的地址。在顺序存储结构里我们将A称为B的直接前驱,B称为A的直接后继,由于是单向链表,在这个链式存储中只有前驱到后继的指针,前驱和后继的关系由指针来链接,也就是说,如果我们要查找后面的元素必须要从头查找。
  作为线性表的一种存储结构,我们关心的是结点之间的逻辑结构,而对每个结点之间的实际地址不感兴趣。
  单链表分为带头和不带头两种
  

对于线性表来说,有头有尾,链表也同样,我们把链表中第一个结点存储的位置叫做头指针,通常用“头指针”来标识一个单链表。在上图带头结点单链表示意图中,第一个结点的地址放在了指针变量H中,链表的最后一个结点指针为空(NULL)。
在下图中,假设p是指向线性表第i个元素的指针,则结点ai的数据域用p->data来表示,即p->data的值是一个数据元素,p->data = ai;结点ai的指针域用p->next表示,即p->next是一个指针,(p->next)->data = ai+1

2、单链表的基本操作

(1)初始化创建单链表
在每次将新节点插入到单链表的尾部时,我们需要加入一个指针 r来始终指向单链表中的为节点。
步骤:
1、初始化,头指针H=NULL,尾指针r=NULL
2、线性表中元素的顺序依次读入数据元素,如果不是结束标志,申请结点。
3、将新节点插入到r所指结点的后面,然后r指向新节点。(第一个结点不同)

LinkList Creat_LinkList()
{ LinkList L = NULL;    //头指针L置空Lnode *s,*r=NULL;char x,flag='0';printf("请输入数据,输入0结束\n");scanf("%d",&x);while(x!=flag){ s = (Lnode *)malloc(sizeof(Lnode));  //申请一个新结点s->data = x;      //将输入的x放在结点的数据域中if(L==NULL) L = s;    //如果头指针为空,将结点s的指针放在头指针中else r->next = s;     //若不为空放在指针r 中r = s;               //r始终指向最后一个结点printf("请输入数据,输入0结束\n");scanf("%c",&x);}if(r!=NULL)r->next = NULL;    //对于非空表,最后结点的指针域放空指针return L;}

(2)按序号查找结点
1、从链表的第一个数据元素结点起,判断当前结点是否是第i个结点。
2、若是第i个结点,则返回,该结点的指针,否则继续下一个,直至结束为止。
3、若没有第i个结点时返回为空。

//按序号查找结点
Lnode *Get_LinkList(LinkList L,int i)
{Lnode *p =L;                  //把第一个结点的指针放在p里面int j=1;if(i==1) return p;if(p==NULL) { printf("表为空");return NULL; }while((p->next!=NULL) && j<i)   //判断当前结点是否是第i个结点{  p = p->next; ++j;}if(j==i)    return p;         //如果是第i个结点,则返回该节点的指针else        return NULL;}

(3)单链表的插入
  设p指向单链表中某结点,s指向待插入的值为x的新节点,将结点s插入到结点p的后面,插入的示意图如下:
  

  1. s->next = p->next;
  2. p->next = s; 先挂链再改链,顺序不能交换

1、查找第i-1个结点,如果找到继续,否则结束
2、申请、填装新节点。
3、将新节点插入,结束。

Lnode * Insert_LinkList(LinkList L,int i,char x)
{Lnode *p,*s;  //定义p s 两个指针if(i==1)             //如果插入的位置是第一个呢{  s = (Lnode *)malloc(sizeof(Lnode));s->data = x;s->next = L;    //把老的第一个的地址放进新第一个里面L = s;  return L;//把新第一个的地址放在头指针里面}p = Get_LinkList(L,i-1);if( p==NULL)        //第i个结点前面位置不存在不能插入{    printf("插入位置前面没有元素!\n");return 0;}else                //在插入位置前面一个元素的地址放在新结点中{s = (Lnode *)malloc(sizeof(Lnode));//创建新节点来存放插入的元素s->data = x;            //将用户要插入的元素放入数据域中s->next = p->next;     //p->next是i-1的地址,s放在i位置的前面,接在i-1位置后面p->next = s;return L;}}

在插入函数中,如果我们在插入在第i个位置,那就查找第i-1个位置,如果i-1个位置不存在就无法插入,当查找到i-1个位置时,将i-1个结点中存储的第i个位置的地址放在新申请结点s的指针域中,再将s的地址放在i-1个位置的指针域中,这样s结点就插入到位置i中,i接在s的后面。
  (4)删除结点
  设p指向单链表中某结点,现在要根据用户输入的位置i 删除结点p。
  要删除结点p,首先要找到p的前驱结点q,然后完成指针的操作。
  

  1. q-next = p->next;
  2. free(q);

1、查找到第 i-1个结点,如果找到继续,否则结束
  2、若存在第i个结点,继续下一步,否则返回没找到
  3、删除这个结点

//按位置删除结点
LinkList Del_LinkList(LinkList L,int i)
{  LinkList p;Lnode *s =L; p = Get_LinkList(L,i-1);if(i==1){      L = s->next;return L;}else if(p->next == NULL){ printf("第%d个结点不存在\n",i); return L; }     else{s = p->next;p->next = s->next;free(s);return L;}
}

  • 在单链表中插入、删除一个结点,必须知道其前驱结点
  • 单链表不具有按序号随机访问的特点,只能从头指针开始一个又一个顺序进行
      实现单链表基本的创建、插入、删除等操作:
#include "stdio.h"
#include "malloc.h"
#include "stdlib.h"
typedef struct node
{  char data;struct node *next;
}Lnode,*LinkList;//单链表的创建
LinkList Creat_LinkList()
{   LinkList L = NULL;Lnode *s,*r=NULL;char flag='0';char x;printf("请输入,输入0结束\n");fflush(stdin);scanf("%c",&x);while(x!=flag){   s = (Lnode *)malloc(sizeof(Lnode));s->data =x;if(L==NULL) L=s;else r->next = s;r = s;scanf("%c",&x);}if(r!=NULL)r->next = NULL;return L;
}void Disp_LinkList(LinkList L)
{  LinkList p = L;while(p!=NULL){ printf("%c ",p->data);p=p->next;  }
}//按序号查找结点
Lnode *Get_LinkList(LinkList L,int i)
{Lnode *p =L;                  //把第一个结点的指针放在p里面int j=1;if(i==1) return p;if(p==NULL) { printf("表为空");return NULL; }while((p->next!=NULL) && j<i)   //判断当前结点是否是第i个结点{  p = p->next; ++j;}if(j==i)    return p;         //如果是第i个结点,则返回该节点的指针else        return NULL;}Lnode * Insert_LinkList(LinkList L,int i,char x)
{Lnode *p,*s;  //定义p s 两个指针if(i==1)             //如果插入的位置是第一个呢{  s = (Lnode *)malloc(sizeof(Lnode));s->data = x;s->next = L;    //把老的第一个的地址放进新第一个里面L = s;  return L;//把新第一个的地址放在头指针里面}p = Get_LinkList(L,i-1);if( p==NULL)        //第i个结点前面位置不存在不能插入{    printf("插入位置前面没有元素!\n");return 0;}else                //在插入位置前面一个元素的地址放在新结点中{s = (Lnode *)malloc(sizeof(Lnode));//创建新节点来存放插入的元素s->data = x;            //将用户要插入的元素放入数据域中s->next = p->next;     //p->next是i-1的地址,s放在i位置的前面,接在i-1位置后面p->next = s;return L;}}
//按位置删除结点
LinkList Del_LinkList(LinkList L,int i)
{  LinkList p;Lnode *s =L; p = Get_LinkList(L,i-1);if(i==1){      L = s->next;return L;}else if(p->next == NULL){ printf("第%d个结点不存在\n",i); return L; }     else{s = p->next;p->next = s->next;free(s);return L;}
}void main()
{LinkList L; int n;char x;L = Creat_LinkList();printf("你创建的单链表为:\n");Disp_LinkList(L);printf("\n输入你要插入的位置:\n");scanf("%d",&n);fflush(stdin);printf("输入你要插入的元素:\n");scanf("%c",&x);L = Insert_LinkList(L,n,x);printf("你插入后的单链表为:\n");Disp_LinkList(L); printf("\n输入你要删除的位置:\n");scanf("%d",&n);L = Del_LinkList(L,n);printf("你删除后的单链表为:\n");Disp_LinkList(L);
}

代码运行图:

  单链表的简单运用:使用单链表制作电子通讯录(单链表)
  需求:

  1. 输入联系人信息,包含姓名、电话号码两部分。
  2. 实现联系人信息的插入:输入要插入的位置和新的联系人信息,将此联系人插入的链表的相应位置。
  3. 实现联系人信息的删除:输入要删除的联系人姓名,将此姓名的联系人删除。
  4. 实现按联系人的姓名查找信息
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct numbook              //定义一个结构体
{   char name[10];          //定义一个数组来存放姓名char phone[15];         //定义一个数组来存放电话号码struct numbook * next;   //定义一个指针
};//判断用户输入的号码是否合法
int IsLegal(const char *str)   //用常量输入,只能使用无法修改
{while(*str!='\0')         //!='\0'.就是运行到字符串结尾时结束。{if(!(*str>='0' && *str<='9'))  //用户输入的号码不在0到9之间返回0return 0;str++;}return 1;
}//用户信息导入numbook * input(){  numbook *p,*q=NULL;numbook * L = NULL;char name[10],phone[15];while( printf("请输入姓名和电话号码,输入“ok”结束!\n"),scanf("%s",&name),strcmp(name,"ok")!=0){ p = (struct numbook *)malloc(sizeof(numbook));strcpy(p->name,name);   //将用户输入的姓名赋值到单链表中的p-name中while(scanf("%s",&phone),IsLegal(phone)==0){  printf("电话号码无效,重新输入。\n");}      //判断电话号码是否合法strcpy(p->phone,phone);if( L == NULL) {  L = p; }else {  q->next = p; }q = p;}if(q!=NULL) q->next =NULL;  return L;
}
//输出电话簿
void display(numbook * L)
{   while(L!=NULL)
{printf("%s - %s \n",L->name,L->phone);L = L->next;
}
}
//在电话簿中根据姓名查找联系人信息
numbook * find(numbook *L,char *na)
{while(L!=NULL){ if(strcmp(L->name,na)==0)     //如果姓名和电话簿中的相等返回Lreturn L;                 else  L = L->next;          //否则接续下一个结点}return NULL;
}
//删除联系人
numbook * DeleteByName(numbook *L,const char *na)
{    numbook *p,*q;  //定义两个指针,让q和p同时前进,便于操作指针if(L==NULL){  printf("空表无法再删除!\n");     //当L为空表时return L;  }if(strcmp(L->name,na)==0)        //当删除的是第一个结点时{p = L; L = L->next; free(p);printf("删除成功!\n"); return L;}p = L;           //将电话簿的头指针放在p中,p指向了第一个元素q = p->next;     //将第一个结点存放的指针放在q中,q指向了第二个结点while(q!=NULL && strcmp(q->name,na)!=0){ p = q;q = q->next;}if(q->next==NULL){   free(q); p->next = NULL;printf("删除成功!\n");return L; }//删除的元素为最后一个,q结点的指针是空的,直接释放q再将q前面的p指针置空else { p->next = q->next; free(q);printf("删除成功!\n");return L; }return L;   //将q中存放的地址放到q前面位置的p中,再释放q
}//插入联系人信息
numbook * InsertByName(numbook *L,const char *na,numbook *e)
{numbook *p,*q;  //同样定义两个指针,让q和p同时前进,便于操作指针if(L==NULL)  {   L = e;  e->next = NULL;  return L; } //如果头指针为空,代表输入的为第一个结点,就把它地址放在头指针里//在把这个结点的指针域置空,代表后面没有结点了,也就是它为第一个结点if(strcmp(L->name,na)==0)   //当用户输入的姓名是第一个联系人的姓名时{   e->next = L;  L = e;  return L;    }  //将这第一个人的地址放在新插入结点的指针域中,再将它的指针放在头指针中p = L; q = p->next;while(q!= NULL && strcmp(q->name,na)!=0){p = q; q = q->next;}if(q == NULL){   p->next = e; e->next = NULL;return L;}else {p->next = e;   e->next = q;  return L;}
}
void main(){char na[10];int i=0;numbook * L,*p,*e;L = input();display(L);while(i<10){fflush(stdin);while(printf("请输入你要操作的功能(1.查询联系人。2.插入联系人。3.删除联系人)\n"),scanf("%d",&i),i>3){printf("输入错误,重新输入");}switch(i){case 1:  printf("请输入要查找的姓名:\n");fflush(stdin);   //清除键盘缓冲区scanf("%s",&na);p = find(L,na);if(p==NULL) printf("查无此人\n");else printf("这个人的信息为:姓名“%s”,电话“%s”。\n",p->name,p->phone);break;case 2:     printf("请输入要插入的信息(姓名  电话)\n");e = (struct numbook *)malloc(sizeof(struct numbook));scanf("%s%s",e->name,e->phone);printf("请输入要插入位置的姓名:\n");scanf("%s",na);L = InsertByName(L,na,e);display(L);break;case 3:     printf("请输入要删除联系人的姓名:\n");scanf("%s",&na);p = find(L,na);if(p==NULL) printf("查无此人\n");L = DeleteByName(L,na);display(L);break;}i=NULL;}}

运行结果:

数据结构之线性表——(二、链式存储结构)[c语言]相关推荐

  1. Python 数据结构 之 线性表 的链式存储结构

    用Python 来实现 C语言中 线性表的链式存储结构. 文章转载请注明:  Python 数据结构 之 线性表 的链式存储结构 代码地址 https://github.com/WenkeZhou/P ...

  2. 从零开始学数据结构和算法(二)线性表的链式存储结构

    链表 链式存储结构 定义 线性表的链式存储结构的特点是用一组任意的存储单元的存储线性表的数据元素,这组存储单元是可以连续的,也可以是不连续的. 种类 结构图 单链表 应用:MessageQueue 插 ...

  3. 《数据结构》c语言版学习笔记——其他链表(线性表的链式存储结构Part2)

    线性表的链式存储结构 数据结构系列文章 第三章 循环链表.双向链表 文章目录 线性表的链式存储结构 前言 一.循环链表 (一)定义 (二)尾指针 二.双向链表 (一)定义 (二)代码 总结 前言 提示 ...

  4. 《数据结构》c语言版学习笔记——单链表结构(线性表的链式存储结构Part1)

    线性表的链式存储结构 数据结构系列文章 第二章 单链表结构 文章目录 线性表的链式存储结构 前言 一.单链表的建立 代码 二.单链表的读取 代码 三.单链表的插入 代码 四.单链表的删除 代码 五.单 ...

  5. 【数据结构】CH2 线性表的链式存储结构

    目录 一.链表概述 1.相关定义 二.单链表 1.插入和删除节点的操作 (1)插入结点 (2)删除结点 2.建立单链表 (1)头插法 (2)尾插法 3.线性表基本运算在单链表中的实现 (1)初始化线性 ...

  6. 数据结构和算法:(3)3.2线性表的链式存储结构

    线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素也就是说你这个可以放在A地点,这个可以放在E地点,A地点和E地点中间可以隔开一个C地点和D地点,这样是允许的),这组存储单元可以存在 ...

  7. 数据结构-线性表(链式存储结构)

    线性表(链式存储结构) 特点: 用一组任意的存储单元存储线性表的数据结构,这组存储单元可以是连续的,也可以是不连续的. 对数据结构ai来说,除了存储其本身的信息之外,还需存储一个指示其后继的信息(即直 ...

  8. 数据结构开发(5):线性表的链式存储结构

    0.目录 1.线性表的链式存储结构 2.单链表的具体实现 3.顺序表和单链表的对比分析 4.小结 1.线性表的链式存储结构 顺序存储结构线性表的最大问题是: 插入和删除需要移动大量的元素!如何解决? ...

  9. 线性表(链式存储结构)

    前言 线性表(顺序存储结构-用数组描述) 为了解决顺序存储不足:用线性表另外一种结构-链式存储.在顺序存储结构(数组描述)中,元素的地址是由数学公式决定的,而在链式储存结构中,元素的地址是随机分布的, ...

  10. 链表list(链式存储结构实现)_5 线性表的链式存储结构

    系列文章参考资料为<大话数据结构>,源码为个人私有,未经允许不得转载 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,可以使连续的,也可以不连续,也就意味这些元素可以 ...

最新文章

  1. Shell 十三问 的学习记录
  2. 防止Excel导入超大带格式文本乱码的方法
  3. [JUC-5]ConcurrentHashMap源码分析JDK8
  4. Centos7常用命令[系统的关机、重启以及登出]
  5. windows 搭建kms服务器激活_自建KMS激活服务器的两种方法
  6. CentOS中部署jar包时提示:org.quartz.SchedulerException: Couldn‘t get host name
  7. 微信小程序_Bug解决_setData失效
  8. IPTABLES封闭和开放端口
  9. 【探究】信号槽到底能不能有返回值?
  10. Debug Assertion Failed.Expression:_BLOCK_TYPE_IS_VALID(phead-nBlockUse)
  11. j2ee建立在线聊天室详细教程(第一天登陆页面)
  12. 搜狗拼音皮肤 php文件,搜狗输入法皮肤制作教程(组图版)
  13. odin学习3之numbers
  14. 利用matlab导入或读取数据至Excel(COM方法和xlswrite,xlsread方法)
  15. 关闭计算机 网络设备上的445端口,关闭445端口的方法,小编教你电脑如何关闭445端口...
  16. mysql列名重复_ORA-00957:重复的列名_MySQL
  17. VirtualBox实现文件与主机复制粘贴
  18. Surface Go1 与Surface Go2的不同 (非专业对比)
  19. 2015中兴笔试+面试
  20. 二手车之家业务缓存应用实战

热门文章

  1. LLMs模型速览(GPTs、LaMDA、GLM/ChatGLM、PaLM/Flan-PaLM、BLOOM、LLaMA、Alpaca)
  2. CF 379D NewYearLetter [dp+暴力]
  3. 会Python的淘宝商家可以横扫一切竞争对手,这就是会技术的魅力!(上)
  4. c 语言小白入门~~黎明
  5. 二、Git安装与配置
  6. gtid mysql_mysql gtid
  7. 变位词(leeetcode)
  8. Python爬虫数据保存到MongoDB中
  9. notepad++ 正则表达式多条件查找替换
  10. findIndex与indexOf 的区别