带有头结点的单链表的实现

一、链表概述

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

二、线性表的单链表存储数据

以存储数据为整型为例

typedef int ElemType; /* 定义抽象数据类型ElemType为整型 */

三、线性表的单链表存储结构

线性表的单链表存储结构图示

代码示

//线性表的单链表存储结构
typedef struct Node
{ElemType data;      //代表数据域struct Node * next; //代表指针域,指向直接后继元素;定义指向Node类型的指针
}Node, *pNode;

四、线性表的单链表操作定义13个

//1、创建头结点返回头指针
pNode InitList();
//2、判断链表是否为空
bool ListEmpty(pNode L);
//3、向链表中追加元素
bool ListAddElem(pNode L, ElemType e);
//4、向链表pos位置前插入元素
bool ListInsert(pNode L, int pos, ElemType e);
//5、遍历链表
void ListTraverse(pNode L);
//6、删除指定位置的元素
bool ListDelete(pNode L, int i, ElemType &e);
//7、获取链表的长度
int ListLength(pNode L);
//8、获取第i个结点的值
bool GetElem(pNode L, int i, ElemType &e);
//9、若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱
bool PriorElem(pNode L, ElemType cur_e, ElemType &pre_e);
//10、若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继
bool NextElem(pNode L, ElemType cur_e, ElemType &next_e);
//11、查询满足相应条件的结点元素,并返回序位
int LocateElem(pNode L, ElemType e, bool(*compare)(ElemType, ElemType));
//11.1比较俩个元素是否相等
bool compare(ElemType e1, ElemType e2);
//12、将链表清空
void ClearList(pNode L); // 不改变L
//13、销毁链表
void DestroyList(pNode &L);

1、创建头结点返回头指针

pNode InitList()
{pNode pHead = (pNode)malloc(sizeof(Node));//申请一个结点空间作为头结点,头指针指向头结点if (!pHead) //pHead为空 存储分配失败exit(-1);//pHead->data 因为是头结点所以头结点的数据域可以不用管,是一个垃圾直值pHead->next = NULL; // 头结点指针域初始化为空return pHead;
}

2、判断链表是否为空

当头指针的指针域为空时,链表为空

bool ListEmpty(pNode L)
{ // 初始条件:线性表L已存在。//操作结果:若L为空表,则返回true,否则返回falseif (L->next) // 非空return false;elsereturn true;
}

3、向链表中追加元素

bool ListAddElem(pNode L, ElemType e)
{// 初始条件:线性表L已存在。//操作结果:在链表最后一个结点后追加一个结点pNode pEnd = L, pNew, pProEnd = NULL;while (pEnd != NULL)//让pProEnd指向链表的尾结点{pProEnd = pEnd;//保存倒数第二个结点的next值,这个值指向尾结点pEnd = pEnd->next;}pNew = (pNode)malloc(sizeof(Node));//生成新节点if (pNew == NULL)//为空则节点分配失败,程序终止{printf("分配失败,程序终止!\n");exit(-1);}pNew->data = e;          //新结点值域赋值pNew->next = NULL;       //新结点指针域赋值,由于是尾结点所以赋值为空pProEnd->next = pNew;    //新结点挂载到尾结点return true;
}

4、向链表插入结点元素

插入结点图示 pos = 2

bool ListInsert(pNode L, int pos, ElemType e)
{   // 初始条件:线性表L已存在。// 在带头结点的单链线性表L中第pos个位置之前插入元素eint i = 0;pNode p = L;while (p != NULL && i < pos - 1)//用p指向第pos-1个结点,将指针移动到插入元素位置之前。pos<=1,则不执行while{p = p->next;  //p指向头结点,此语句执行第一次后p指向首结点,第二次p指向第二个结点....i++;          //计数直到pos-1位置处的结点}if (p == NULL || i > pos - 1) //如果p指向的结点为空则p指向了尾结点或者链表没有元素 pos小于1或者大于表长return false;pNode Pnew = (pNode)malloc(sizeof(Node)); // 生成新结点Pnew->data = e; //  将值赋给要插入的结点Pnew->next = p->next;//新结点指针域指向下一个结点p->next = Pnew;//第pos-1个结点指向新节点return true;
}

5、遍历链表

void ListTraverse(pNode L)
{// 初始条件:线性表L已存在。
//操作结果:依次对L的每个结点值进行输出if (L == NULL){ printf("链表不存在\n");exit(-1);}pNode p = L->next;while (p != NULL){printf("%d  ", p->data);p = p->next;}printf("\n");
}

6、删除指定位置的元素

bool ListDelete(pNode L, int i, ElemType &e) // 不改变L
{ // 初始条件:线性表L已存在。//操作结果:在带头结点的单链线性表L中,删除第i个元素,并由e返回其值int j = 0;pNode p = L, q;while (p != NULL && j < i - 1) // 链表不为空,用p指向第i-1个结点{p = p->next;j++;}if (p == NULL || j > i - 1) // p初始指针指向为空或者i为链表长度+1 删除位置不合理return false;q = p->next; //保存要删除结点指针p->next = q->next;//要删除结点的指针域的值赋值给要删除结点前的结点的指针域e = q->data;//保存要删除结点的值free(q);//释放删除结点的占用的内存空间return true;
}

7、获取链表的长度

int ListLength(pNode L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数int i = 0;pNode p = L->next; // p指向第一个结点while (p != NULL) // 没到表尾{i++;p = p->next;}return i;
}

8、获取第i个结点的值

bool GetElem(pNode L, int i, ElemType &e)
{ // L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回true,否则返回falseint j = 1; // j为计数器pNode p = L->next; // p指向第一个结点while (p != NULL && j < i) // 顺指针向后查找,直到p指向第i个元素或p为空{p = p->next;j++;}if (p == NULL || j > i) // 第i个元素不存在return false;e = p->data; // 取第i个元素return true;
}

9、若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱

找到第一个结点值为cur_e的结点,返回其前一个节点值

bool PriorElem(pNode L, ElemType cur_e, ElemType &pre_e)
{// 初始条件: 线性表L已存在// 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,//           返回true;否则操作失败,pre_e无定义,返回falsepNode q = NULL, p = L->next;// p指向第一个结点if (p->data == cur_e){printf("第一个结点没有前驱\n");return false;}while (p != NULL){q = p;//q用来保存p的前驱p = p->next;if (p != NULL && p->data == cur_e)//找到与cur_e相等的结点,当p ==NULL时 p是最后一个结点的指针域的值。则p->data是非法的break;//终止循环}if (p == NULL)//防止链表为空{ return false;}pre_e = q->data;//保存前驱结点值return true;
}

10、若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继

bool NextElem(pNode L, ElemType cur_e, ElemType &next_e)
{ // 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,//           返回true;否则操作失败,next_e无定义,返回falsepNode p = L, q = L;if (q == NULL)return false;int i = 0;while (p != NULL && i<ListLength(L))//将指针指向最后一个结点{p = p->next;i++;}while (q != NULL && q != p) // p所指结点有后继,即q最多只会指向最后一个结点之前的结点{if (q->data == cur_e){next_e = q->next->data;//保存后驱结点值return true;}q = q->next;}return false;
}

11、查询满足相应条件的结点元素,并返回序位

int LocateElem(pNode L, ElemType e, bool(*compare)(ElemType, ElemType))
{ // 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为ture,否则为false)// 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。//           若这样的数据元素不存在,则返回值为0int i = 0;pNode p = L->next;//p指向首节点while (p){i++;if (compare(p->data, e)) // 找到这样的数据元素return i;p = p->next;}return 0;
}

11.1比较俩个元素是否相等

bool compare(ElemType e1, ElemType e2)
{if (e1 == e2)return true;elsereturn false;
}

12、将链表清空

void ClearList(pNode L) // 不改变L
{ // 初始条件:线性表L已存在。//操作结果:将L重置为空表,从第首节点开始释放pNode p = NULL, q = NULL;p = L->next;//p指向首节点while (p){q = p->next;//q保存下一个结点的地址free(p);//释放当前结点内存p = q;//将下一节点的地址给p,保证循环}L->next = NULL; //头结点指针域重置为空
}

13、销毁链表

void DestroyList(pNode &L)
{ // 初始条件:线性表L已存在。//操作结果:销毁线性表L,从头结点开始释放(free(L)释放L指向的内存地址)pNode q;while (L){q = L->next;free(L);L = q;}
}

最后给出完整代码

/*带有头结点的单链表*/
# include<stdio.h>
# include<stdlib.h>
# include<malloc.h>typedef int ElemType; /* 定义抽象数据类型ElemType为整型 *///线性表的单链表存储结构
typedef struct Node
{ElemType data;      //代表数据域struct Node * next; //代表指针域,指向直接后继元素;定义指向Node类型的指针
}Node, *pNode;//1、创建头结点返回头指针
pNode InitList();
//2、判断链表是否为空
bool ListEmpty(pNode L);
//3、向链表中追加元素
bool ListAddElem(pNode L, ElemType e);
//4、向链表pos位置前插入元素
bool ListInsert(pNode L, int pos, ElemType e);
//5、遍历链表
void ListTraverse(pNode L);
//6、删除指定位置的元素
bool ListDelete(pNode L, int i, ElemType &e);
//7、获取链表的长度
int ListLength(pNode L);
//8、获取第i个结点的值
bool GetElem(pNode L, int i, ElemType &e);
//9、若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱
bool PriorElem(pNode L, ElemType cur_e, ElemType &pre_e);
//10、若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继
bool NextElem(pNode L, ElemType cur_e, ElemType &next_e);
//11、查询满足相应条件的结点元素,并返回序位
int LocateElem(pNode L, ElemType e, bool(*compare)(ElemType, ElemType));
//11.1比较俩个元素是否相等
bool compare(ElemType e1, ElemType e2);
//12、将链表清空
void ClearList(pNode L); // 不改变L
//13、销毁链表
void DestroyList(pNode &L);int main(void)
{pNode L = NULL;   //创建头指针L,空指向L = InitList();//创建一个链表头结点,L指向头结点if (ListEmpty(L))printf("链表为空\n");elseprintf("链表不为空\n");if (ListAddElem(L, 22))printf("追加成功\n");int i = 0;while (i < 5){ListAddElem(L, 2 * i);i++;}ListTraverse(L);ListInsert(L, 1, 100);ListTraverse(L);ElemType e = 0;ListDelete(L, 5, e);ListTraverse(L);printf("删除的元素是  %d\n", e);printf("链表的长度为 %d\n", ListLength(L));if (GetElem(L, 5, e))printf("第5个结点值为 %d\n", e);elseprintf("获取第5个结点失败\n");ElemType pre_e, cur_e = 2, next_e;if (PriorElem(L, cur_e, pre_e))printf("第一个值 %d 的结点的前驱结点值为 %d \n", cur_e, pre_e);elseprintf("前驱获取失败\n");if (NextElem(L, cur_e, next_e))printf("第一个值 %d 的结点的后继结点值为 %d \n", cur_e, next_e);elseprintf("后继获取失败\n");printf("链表中与值%d 相等的值的序位为 %d\n", cur_e, LocateElem(L, cur_e, compare));ClearList(L);ListTraverse(L);DestroyList(L);ListTraverse(L);return 0;}//1、创建头结点返回头指针
pNode InitList()
{pNode pHead = (pNode)malloc(sizeof(Node));//申请一个结点空间作为头结点,头指针指向头结点if (!pHead) //pHead为空 存储分配失败exit(-1);//pHead->data 因为是头结点所以头结点的数据域可以不用管,是一个垃圾直值pHead->next = NULL; // 头结点指针域初始化为空return pHead;
}//2、判断链表是否为空
//当头指针的指针域为空时,链表为空
bool ListEmpty(pNode L)
{ // 初始条件:线性表L已存在。//操作结果:若L为空表,则返回true,否则返回falseif (L->next) // 非空return false;elsereturn true;
}// 3、向链表中追加元素
bool ListAddElem(pNode L, ElemType e)
{pNode pEnd = L, pNew, pProEnd = NULL;while (pEnd != NULL)//让pProEnd指向链表的尾结点{pProEnd = pEnd;//保存倒数第二个结点的next值,这个值指向尾结点pEnd = pEnd->next;}pNew = (pNode)malloc(sizeof(Node));//生成新节点if (pNew == NULL)//为空则节点分配失败,程序终止{printf("分配失败,程序终止!\n");exit(-1);}pNew->data = e;          //新结点值域赋值pNew->next = NULL;       //新结点指针域赋值,由于是尾结点所以赋值为空pProEnd->next = pNew;    //新结点挂载到尾结点return true;
}//4、向链表插入元素
bool ListInsert(pNode L, int pos, ElemType e)
{// 在带头结点的单链线性表L中第pos个位置之前插入元素eint i = 0;pNode p = L;while (p != NULL && i < pos - 1)//用p指向第pos-1个结点,将指针移动到插入元素位置之前。pos<=1,则不执行while{p = p->next;  //p指向头结点,此语句执行第一次后p指向首结点,第二次p指向第二个结点....i++;          //计数直到pos-1位置处的结点}if (p == NULL || i > pos - 1) //如果p指向的结点为空则p指向了尾结点或者链表没有元素 pos小于1或者大于表长return false;pNode Pnew = (pNode)malloc(sizeof(Node)); // 生成新结点Pnew->data = e; //  将值赋给要插入的结点Pnew->next = p->next;//新结点指针域指向下一个结点p->next = Pnew;//第pos-1个结点指向新节点return true;
}//5、遍历链表
void ListTraverse(pNode L)
{// 初始条件:线性表L已存在。
//操作结果:依次对L的每个结点值进行输出if (L == NULL){ printf("链表不存在\n");exit(-1);}pNode p = L->next;while (p != NULL){printf("%d  ", p->data);p = p->next;}printf("\n");
}//6、删除指定位置的元素
bool ListDelete(pNode L, int i, ElemType &e) // 不改变L
{ // 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值int j = 0;pNode p = L, q;while (p != NULL && j < i - 1) // 链表不为空,用p指向第i-1个结点{p = p->next;j++;}if (p == NULL || j > i - 1) // p初始指针指向为空或者i为链表长度+1 删除位置不合理return false;q = p->next; //保存要删除结点指针p->next = q->next;//要删除结点的指针域的值赋值给要删除结点前的结点的指针域e = q->data;//保存要删除结点的值free(q);//释放删除结点的占用的内存空间return true;
}//7、获取链表的长度
int ListLength(pNode L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数int i = 0;pNode p = L->next; // p指向第一个结点while (p != NULL) // 没到表尾{i++;p = p->next;}return i;
}//8、获取第i个结点的值
bool GetElem(pNode L, int i, ElemType &e) // 算法2.8
{ // L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回true,否则返回falseint j = 1; // j为计数器pNode p = L->next; // p指向第一个结点while (p != NULL && j < i) // 顺指针向后查找,直到p指向第i个元素或p为空{p = p->next;j++;}if (p == NULL || j > i) // 第i个元素不存在return false;e = p->data; // 取第i个元素return true;
}//9、若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱
bool PriorElem(pNode L, ElemType cur_e, ElemType &pre_e)
{// 初始条件: 线性表L已存在// 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,//           返回true;否则操作失败,pre_e无定义,返回falsepNode q = NULL, p = L->next;// p指向第一个结点if (p->data == cur_e){printf("第一个结点没有前驱\n");return false;}while (p != NULL){q = p;//q用来保存p的前驱p = p->next;if (p != NULL && p->data == cur_e)//找到与cur_e相等的结点,当p ==NULL时 p是最后一个结点的指针域的值。则p->data是非法的break;//终止循环}if (p == NULL)//防止链表为空{ return false;}pre_e = q->data;//保存前驱结点值return true;
}
//10、若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继
bool NextElem(pNode L, ElemType cur_e, ElemType &next_e)
{ // 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,//           返回true;否则操作失败,next_e无定义,返回falsepNode p = L, q = L;if (q == NULL)return false;int i = 0;while (p != NULL && i<ListLength(L))//将指针指向最后一个结点{p = p->next;i++;}while (q != NULL && q != p) // p所指结点有后继,即q最多只会指向最后一个结点之前的结点{if (q->data == cur_e){next_e = q->next->data;//保存后驱结点值return true;}q = q->next;}return false;
}
//11.1比较俩个元素是否相等
bool compare(ElemType e1, ElemType e2)
{if (e1 == e2)return true;elsereturn false;
}//11、查询满足相应条件的结点元素,并返回序位
int LocateElem(pNode L, ElemType e, bool(*compare)(ElemType, ElemType))
{ // 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为ture,否则为false)// 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。//           若这样的数据元素不存在,则返回值为0int i = 0;pNode p = L->next;//p指向首节点while (p){i++;if (compare(p->data, e)) // 找到这样的数据元素return i;p = p->next;}return 0;
}//12、将链表清空
void ClearList(pNode L) // 不改变L
{ // 初始条件:线性表L已存在。//操作结果:将L重置为空表,从第首节点开始释放pNode p = NULL, q = NULL;p = L->next;//p指向首节点while (p){q = p->next;//q保存下一个结点的地址free(p);//释放当前结点内存p = q;//将下一节点的地址给p,保证循环}L->next = NULL; //头结点指针域重置为空
}//13、销毁链表
void DestroyList(pNode &L)
{ // 初始条件:线性表L已存在。//操作结果:销毁线性表L,从头结点开始释放(free(L)释放L指向的内存地址)pNode q;while (L){q = L->next;free(L);L = q;}
}

数据结构笔记(三)-- 链式实现顺序表相关推荐

  1. 数据结构 链式哈希表(Hash Table)的接口定义与实现分析(完整代码)

    链式哈希表的接口定义 关于哈希表与链式哈希表的描述可以参阅:http://www.cnblogs.com/idreamo/p/7990860.html 链式哈希表的操作与属性有:初始化.销毁.插入元素 ...

  2. 《数据结构》实验报告二:顺序表 链表

    一.实验目的 1.掌握线性表中元素的前驱.后续的概念. 2.掌握顺序表与链表的建立.插入元素.删除表中某元素的算法. 3.对线性表相应算法的时间复杂度进行分析. 4.理解顺序表.链表数据结构的特点(优 ...

  3. 单链表原地逆转 c语言,链式结构线性表的实现(二)

    链式结构线性表的实现(二) 一.目的: 掌握链表的表示方法,存储结构及其基本操作的实现,灵活使用链表. 二.要求: 修改实验三的程序,完成 (1)实现带头接点的单链表的原地逆转(结果链表仍使用原链表的 ...

  4. 【数据结构和算法笔记】c语言实现顺序表和链表

    线性表的定义: 线性表中元素关系是一对一的,元素个数是有限的 序列补充: 存在唯一开始元素和终端元素,除此之外,每个元素只有唯一的前驱元素和后继元素 线性表的长度: 线性表中所含元素的个数(n),n= ...

  5. 数据结构初阶——链式二叉树

    目录 树概念及结构 树的概念 树的表示 二叉树概念及结构 概念 特殊二叉树 二叉树的性质 二叉树链式结构及实现 二叉树的简单创建 二叉树的前序遍历 二叉树中序遍历与二叉树后序遍历 求二叉树节点个数 求 ...

  6. c语言输出单链表最大值与最小值,数据结构(C语言版)---顺序表与链表的比较...

    1.存取方式 1)顺序表:可以顺序存取,也可以随机存取. 2)链表:只能从表头顺序存取. 2.逻辑结构与物理结构 1)顺序存储:逻辑上相邻,物理位置相邻. 2)链式存储:逻辑上相邻,物理位置不一定相邻 ...

  7. (PTA)数据结构(作业)2、顺序表

    判断题 1.数据的逻辑结构是指数据的各数据项之间的逻辑关系.  F 数据的逻辑结构是指数据的各数据元素之间的逻辑关系. 2.顺序表中逻辑上相邻的元素,其物理位置也一定相邻.  T 线性表的顺序表示指的 ...

  8. 数据结构c语言版严蔚敏 顺序表

    说来惭愧由于贪玩,数据结构挂科了,现在重新学一遍数据结构,用博客督促一下自己,希望各位同学引以为戒,贪玩一时爽,痛苦永留存. 本文主要以严老师的数据结构书为主. 结构类型 listsize代表这个顺序 ...

  9. 数据结构与算法(8-1)顺序表查找及优化

    目录 一.顺序表查找 二.顺序表查找优化(重点) 总代码 一.顺序表查找 从头到尾或从尾到头查找. //顺序表查找(需要判断两次) int ListSearch(char ch) {for (int ...

最新文章

  1. 大一c语言大作业课题大全,昆明理工大学大一C语言大作业题目.doc
  2. 区块链基础:理论和术语
  3. 从零开始学习 webservice第一集,java webservice简单实例入门教程
  4. UVa1335 Beijing Guards(二分查找)
  5. Matlab Robotic Toolbox V9.10工具箱(四):常用函数
  6. 售楼小姐真情自白:揭穿卖房二十骗局 (我转载的--他NND房地产商真黑!)
  7. ReactJS入门之Model分层的概念
  8. 微信小程序之阻止冒泡事件
  9. IDEA自动生成 构造方法 get set方法
  10. JavaScript 弹出窗口总结
  11. mysql 触发器 insert new_mysql触发器实例 插入前更新数据
  12. 小米手机每次安装应用都需要属于小米账号密码
  13. 红帽Linux自带的光盘刻录,linux下mkisofs制作光盘映像cdrecord刻录光盘
  14. 骑士CMSgetshell复现
  15. Vue.js项目实战——Day(1)
  16. 电子邮箱地址是什么?如何找回电子邮箱的地址呢?
  17. 深度学习(二、全连接网络FNN)
  18. Java并发编程实战(学习笔记十 第十一章 性能与可伸缩性)
  19. linux查找服务器大文件,Linux查找大文件命令
  20. idea自定义背景图片

热门文章

  1. 基环树DP(bzoj 1040: [ZJOI2008]骑士)
  2. LCA--最近公共祖先
  3. matlab 提取图像轮廓(图像边缘提取)
  4. 指定范围内每个数的所有真约数
  5. phabricator安装配置和使用(docker安装和独立部署)
  6. 简化document.createElement(div)动态生成层方法
  7. java.lang.NumberFormatException: For input string:
  8. 进程和线程(4)-进程 vs. 线程
  9. 给员工授予svn相关权限
  10. HTML+CSS 整站 步骤