C语言单向链表的表示与实现实例详解

1.概述:

C语言中的单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。
链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。
如下图所示:


一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接

一个单向链表的节点被分成两个部分。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。单向链表只可向一个方向遍历。

链表最基本的结构是在每个节点保存数据和到下一个节点的地址,在最后一个节点保存一个特殊的结束标记,另外在一个固定的位置保存指向第一个节点的指针,有的时候也会同时储存指向最后一个节点的指针。一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。但是也可以提前把一个节点的位置另外保存起来,然后直接访问。当然如果只是访问数据就没必要了,不如在链表上储存指向实际数据的指针。这样一般是为了访问链表中的下一个或者前一个节点。
相对于双向链表,这种普通的,每个节点只有一个指针的链表也叫单向链表,或者单链表,通常用在每次都只会按顺序遍历这个链表的时候(例如图的邻接表,通常都是按固定顺序访问的)。

2.程序实现:

/* c2-2.h 线性表的单链表存储结构 */
 struct LNode
 {
  ElemType data;
  struct LNode *next;
 };
 typedef struct LNode *LinkList; /* 另一种定义LinkList的方法 */
/* bo2-2.c 单链表线性表(存储结构由c2-2.h定义)的基本操作(12个) */
 Status InitList(LinkList *L)
 { /* 操作结果:构造一个空的线性表L */
  *L=(LinkList)malloc(sizeof(struct LNode)); /* 产生头结点,并使L指向此头结点 */
  if(!*L) /* 存储分配失败 */
   exit(OVERFLOW);
  (*L)->next=NULL; /* 指针域为空 */
  return OK;
 }
 Status DestroyList(LinkList *L)
 { /* 初始条件:线性表L已存在。操作结果:销毁线性表L */
  LinkList q;
  while(*L)
  {
   q=(*L)->next;
   free(*L);
   *L=q;
  }
  return OK;
 }
 Status ClearList(LinkList L) /* 不改变L */
 { /* 初始条件:线性表L已存在。操作结果:将L重置为空表 */
  LinkList p,q;
  p=L->next; /* p指向第一个结点 */
  while(p) /* 没到表尾 */
  {
   q=p->next;
   free(p);
   p=q;
  }
  L->next=NULL; /* 头结点指针域为空 */
  return OK;
 }
 Status ListEmpty(LinkList L)
 { /* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
  if(L->next) /* 非空 */
   return FALSE;
  else
   return TRUE;
 }
 int ListLength(LinkList L)
 { /* 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 */
  int i=0;
  LinkList p=L->next; /* p指向第一个结点 */
  while(p) /* 没到表尾 */
  {
   i++;
   p=p->next;
  }
  return i;
 }
 Status GetElem(LinkList L,int i,ElemType *e) /* 算法2.8 */
 { /* L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
  int j=1; /* j为计数器 */
  LinkList p=L->next; /* p指向第一个结点 */
  while(p&&j<i) /* 顺指针向后查找,直到p指向第i个元素或p为空 */
  {
   p=p->next;
   j++;
  }
  if(!p||j>i) /* 第i个元素不存在 */
   return ERROR;
  *e=p->data; /* 取第i个元素 */
  return OK;
 }
 int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType))
 { /* 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) */
  /* 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。 */
  /*      若这样的数据元素不存在,则返回值为0 */
  int i=0;
  LinkList p=L->next;
  while(p)
  {
   i++;
   if(compare(p->data,e)) /* 找到这样的数据元素 */
    return i;
   p=p->next;
  }
  return 0;
 }
 Status PriorElem(LinkList L,ElemType cur_e,ElemType *pre_e)
 { /* 初始条件: 线性表L已存在 */
  /* 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, */
  /*      返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE */
  LinkList q,p=L->next; /* p指向第一个结点 */
  while(p->next) /* p所指结点有后继 */
  {
   q=p->next; /* q为p的后继 */
   if(q->data==cur_e)
   {
    *pre_e=p->data;
    return OK;
   }
   p=q; /* p向后移 */
  }
  return INFEASIBLE;
 }
 Status NextElem(LinkList L,ElemType cur_e,ElemType *next_e)
 { /* 初始条件:线性表L已存在 */
  /* 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继, */
  /*      返回OK;否则操作失败,next_e无定义,返回INFEASIBLE */
  LinkList p=L->next; /* p指向第一个结点 */
  while(p->next) /* p所指结点有后继 */
  {
   if(p->data==cur_e)
   {
    *next_e=p->next->data;
    return OK;
   }
   p=p->next;
  }
  return INFEASIBLE;
 }
 Status ListInsert(LinkList L,int i,ElemType e) /* 算法2.9。不改变L */
 { /* 在带头结点的单链线性表L中第i个位置之前插入元素e */
  int j=0;
  LinkList p=L,s;
  while(p&&j<i-1) /* 寻找第i-1个结点 */
  {
   p=p->next;
   j++;
  }
  if(!p||j>i-1) /* i小于1或者大于表长 */
   return ERROR;
  s=(LinkList)malloc(sizeof(struct LNode)); /* 生成新结点 */
  s->data=e; /* 插入L中 */
  s->next=p->next;
  p->next=s;
  return OK;
 }
 Status ListDelete(LinkList L,int i,ElemType *e) /* 算法2.10。不改变L */
 { /* 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值 */
  int j=0;
  LinkList p=L,q;
  while(p->next&&j<i-1) /* 寻找第i个结点,并令p指向其前趋 */
  {
   p=p->next;
   j++;
  }
  if(!p->next||j>i-1) /* 删除位置不合理 */
   return ERROR;
  q=p->next; /* 删除并释放结点 */
  p->next=q->next;
  *e=q->data;
  free(q);
  return OK;
 }
 Status ListTraverse(LinkList L,void(*vi)(ElemType))
 /* vi的形参类型为ElemType,与bo2-1.c中相应函数的形参类型ElemType&不同 */
 { /* 初始条件:线性表L已存在 */
  /* 操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败 */
  LinkList p=L->next;
  while(p)
  {
   vi(p->data);
   p=p->next;
  }
  printf("\n");
  return OK;
 }
/* algo2-5.c 主程序 */
 #include"c1.h"
 typedef int ElemType;
 #include"c2-2.h"
 #include"bo2-2.c"
 void CreateList(LinkList *L,int n) /* 算法2.11 */
 { /* 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表L */
  int i;
  LinkList p;
  *L=(LinkList)malloc(sizeof(struct LNode));
  (*L)->next=NULL; /* 先建立一个带头结点的单链表 */
  printf("请输入%d个数据\n",n);
  for(i=n;i>0;--i)
  {
   p=(LinkList)malloc(sizeof(struct LNode)); /* 生成新结点 */
   scanf("%d",&p->data); /* 输入元素值 */
   p->next=(*L)->next; /* 插入到表头 */
   (*L)->next=p;
  }
 }
 void CreateList2(LinkList *L,int n)
 { /* 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表 */
  int i;
  LinkList p,q;
  *L=(LinkList)malloc(sizeof(struct LNode)); /* 生成头结点 */
  (*L)->next=NULL;
  q=*L;
  printf("请输入%d个数据\n",n);
  for(i=1;i<=n;i++)
  {
   p=(LinkList)malloc(sizeof(struct LNode));
   scanf("%d",&p->data);
   q->next=p;
   q=q->next;
  }
  p->next=NULL;
 }
 void MergeList(LinkList La,LinkList *Lb,LinkList *Lc)/* 算法2.12 */
 { /* 已知单链线性表La和Lb的元素按值非递减排列。 */
  /* 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列 */
  LinkList pa=La->next,pb=(*Lb)->next,pc;
  *Lc=pc=La; /* 用La的头结点作为Lc的头结点 */
  while(pa&&pb)
   if(pa->data<=pb->data)
   {
    pc->next=pa;
    pc=pa;
    pa=pa->next;
   }
   else
   {
    pc->next=pb;
    pc=pb;
    pb=pb->next;
   }
  pc->next=pa?pa:pb; /* 插入剩余段 */
  free(*Lb); /* 释放Lb的头结点 */
  Lb=NULL;
 }
 void visit(ElemType c) /* ListTraverse()调用的函数(类型要一致) */
 {
  printf("%d ",c);
 }
 void main()
 {
  int n=5;
  LinkList La,Lb,Lc;
  printf("按非递减顺序, ");
  CreateList2(&La,n); /* 正位序输入n个元素的值 */
  printf("La="); /* 输出链表La的内容 */
  ListTraverse(La,visit);
  printf("按非递增顺序, ");
  CreateList(&Lb,n); /* 逆位序输入n个元素的值 */
  printf("Lb="); /* 输出链表Lb的内容 */
  ListTraverse(Lb,visit);
  MergeList(La,&Lb,&Lc); /* 按非递减顺序归并La和Lb,得到新表Lc */
  printf("Lc="); /* 输出链表Lc的内容 */
  ListTraverse(Lc,visit);
 }
(0)

相关推荐

  • 用C语言实现单链表的各种操作(一)

    最近,从新复习了一下数据结构中比较重要的几个部分,现在把自己的成果记录下来,主要就是仿照严蔚敏的<数据结构>(C 语言版),中的例子和后面的习题进行改编的.首先,是单链表的各种实现,其中,包含了一些常考的知识点.例如,单链表的逆置,单链表的合并,找到单链表的中间节点等的算法实现.下面这个是单链表的结构体的定义: 复制代码 代码如下: typedef struct LNode{ ElemType data; struct LNode *next;}LinkList; 下面的基本的单链表的操作:其

  • C++如何实现广义表详解

    以下给出几种简单的广义表模型: 由上图我们可以看到,广义表的节点类型无非head.value.sub三种,这里设置枚举类型,利用枚举变量来记录每个节点的类型: enum Type { HEAD, //头节点 VALUE, //值节点 SUB, //子表节点 }; 每个节点都有自己的类型以及next指针,除此之外,如果该节点是VALUE类型还要分配空间存储该节点的有效值:但是若该节点是SUB类型,就需定义一个指针指向子表的头. 这里我们可以用联合来解决这个问题. (联合(或共同体)是一种不同数据类

  • 哈希表实验C语言版实现

    复制代码 代码如下: /* 数据结构C语言版 哈希表 */#include <stdio.h>#include <malloc.h>#define NULLKEY 0 // 0为无记录标志 #define N 10  // 数据元素个数 typedef int KeyType;// 设关键字域为整型 typedef struct{ KeyType key; int ord;}ElemType; // 数据元素类型 // 开放定址哈希表的存储结构 int hashsize[]={11

  • c语言链表基本操作(带有创建链表 删除 打印 插入)

    复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <malloc.h>#define LEN sizeof(struct Student)struct Student{    long num;    float score;    struct Student*next;};int n;int main(){    /*-----------------------------程序描述----------

  • C语言双向链表的表示与实现实例详解

    1.概述: C语言中一种更复杂的链表是"双向链表"或"双面链表".其表中的每个节点有两个连接:一个指向前一个节点,(当这个"连接"为第一个"连接"时,指向空值或者空列表):而另一个指向下一个节点,(当这个"连接"为最后一个"连接"时,指向空值或者空列表) 一个双向链表有三个整数值: 数值, 向后的节点链接, 向前的节点链接 在一些低级语言中, XOR-linking 提供一种在双向链表中

  • C语言实现带头结点的链表的创建、查找、插入、删除操作

    本文实例讲述了C语言实现带头结点的链表的创建.查找.插入.删除操作.是数据结构中链表部分的基础操作.分享给大家供大家参考.具体方法如下: #include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node* next;// 这个地方注意结构体变量的定义规则 } Node, *PNode; Node* createLinklist(int length) { int i = 0; PNo

  • C语言单循环链表的表示与实现实例详解

    1.概述: 对于一个循环链表来说,其首节点和末节点被连接在一起.这种方式在单向和双向链表中皆可实现.要转换一个循环链表,可以选择开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点.再来看另一种方法,循环链表可以被视为"无头无尾".这种列表很利于节约数据存储缓存, 假定你在一个列表中有一个对象并且希望所有其他对象迭代在一个非特殊的排列下. 指向整个列表的指针可以被称作访问指针. 用单向链表构建的循环链表 循环链表中第一个节点之前就是最后一个节点,反之亦然.循环链表的无边界使得在这

  • C语言运算符优先级列表(超详细)

    每当想找哪个运算符优先级高时,很多时候总是想找的就没有,真让人气愤!现在,终于有个我个人觉得非常全的,分享给大家,欢迎拍砖! C语言运算符优先级 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 -- () 圆括号 (表达式)/函数名(形参表) -- . 成员选择(对象) 对象.成员名 -- -> 成员选择(指针) 对象指针->成员名 -- 2 - 负号运算符 -表达式 右到左 单目运算符 ~ 按位取反运算符 ~表达式 ++ 自增运算符 +

  • C语言单向链表的表示与实现实例详解

    1.概述: C语言中的单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域.这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值. 如下图所示: 一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接 一个单向链表的节点被分成两个部分.第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址.单向链表只可向一个方向遍历. 链表最基本的结构是在每个节点

  • Java语言面向对象编程思想之类与对象实例详解

    在初学者学Java的时候,面向对象很难让人搞懂,那么今天小编就来为大家把这个思想来为大家用极为简单的方法理解吧. 首先我们来简单的阐述面向对象的思想. 面向对象: 官方的语言很抽象,我们把官方的解释和定义抛开.想想,自己有什么,对!!我们自己有手脚眼口鼻等一系列的器官.来把自己所具有的器官就可以看作我们的属性,自己是不是可以喜怒哀乐和嬉笑怒骂,这些是不是我们的行为,那么自己的具有的属性加自己有的行为就称为一个对象. 注意!!我们自己,一个个体是一个对象,因为,你是你,我是我,我们虽然有相同的,但

  • C语言实现进制转换函数的实例详解

    C语言实现进制转换函数的实例详解 前言: 写一个二进制,八进制,十六进制转换为十进制的函数 要求: 函数有两个参数,参数(1)是要转换为十进制的进制数,参数(2)是标示参数(1)是什么进制(2,8,16标示二进制,八进制,十六进制). 要有报错信息,比如参数是1012,但参数(2)是2,显然是进制数表示有错误. 系统表 pg_proc 存储关于函数的信息 内部函数在编译之前需要先定义在 pg_proc.h 中,src/include/catalog/pg_proc.h CATALOG(pg_pr

  • C/C++ 双链表之逆序的实例详解

    C/C++ 双链表之逆序的实例详解 一.结点结构 双向链表的数据结构定义如下: typedef struct node { ElemType data; struct node *prior struct node *next; }list; 其中,ElemType可以是任意数据类型如int.float或者char等,在算法中,规定其默认为int类型. 二.带头结点 本文描述的是双向链表逆序,链表逆序需要维护3个指针,分别指向前一个节点.当前节点和下一个节点,具体代码如下: list *reve

  • C语言中 值传递和指针传递实例详解

    C语言中 值传递和指针传递实例详解 在C语言中,函数的参数和返回值的传递方式有两种:值传递和指针传递. 值传递和指针传递初学者总会有一种朦胧的感觉,所以建议把指针传递的概念摸透,才能熟练应用. 值传递示例:x其实是n的一份临时拷贝,所以并不会改变n的值. #include <stdio.h> #include <windows.h> void Fun(int x) { x = 1; } int main() { int n = 2; Fun(n); printf("%d\

  • C语言单链队列的表示与实现实例详解

    1.概述: C语言的队列(queue),是指先进先出(FIFO, First-In-First-Out)的线性表.在具体应用中通常用链表或者数组来实现.队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作. 而单链队列使用链表作为基本数据结果,因此不存在伪溢出的问题,队列长度也没有限制.但插入和读取的时间代价会比较高 2.实例代码: /* 单链队列--队列的链式存储结构 */ typedef struct QNode { QElemType data; struct

  • C语言循环队列的表示与实现实例详解

    1.概述: C语言的队列(queue),是先进先出(FIFO, First-In-First-Out)的线性表数据结构.在具体应用中通常用链表或者数组来实现.队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作. 循环队列可以更简单的防止伪溢出的发生,但是队列大小是固定的. 2.实例代码: /* 队列的顺序存储结构(循环队列) */ #define MAX_QSIZE 5 /* 最大队列长度+1 */ typedef struct { QElemType *base

  • C语言中炫酷的文件操作实例详解

    目录 什么是文件 程序文件 数据文件 (本文重点) 文件名 文件的打开和关闭 文件指针 文件函数 相对路径与绝对路径 输入输出流 二进制读写 fwirte fread 总结 什么是文件 磁盘上的文件是文件 但是在程序设计中,我们一般谈的文件有两种:程序文件和数据文件(从文件功能的角度来分类). 程序文件 包括源程序文件(例如.c文件)目标文件(windows环境后缀为.obj)可执行程序(windos环境后缀为exe). 数据文件 (本文重点) 文件的内容不一定是程序,而是程序运行时读写的数据,

  • C语言main函数的三种形式实例详解

    在C语言中,main()函数有三种形式. 1.无参数 #include <stdio.h> int main(void) { printf("Hello World!\n"); return 0; } 2.有两个参数 习惯上第一个参数是整型argc,保存了外部调用命令的参数个数,第二个参数是指针数组或二级指针argv,以字符串形式保存了与argc对应的参数,如下例子: #include <stdio.h> int main(int argc, char* arg

随机推荐