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

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

一、链表概述

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

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

以存储数据为整型为例

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,否则返回false
	if (L->next) // 非空
		return false;
	else
		return 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个位置之前插入元素e
	int 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,否则返回false
	int 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无定义,返回false
	pNode 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无定义,返回false

	pNode 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()的数据元素的位序。
  //           若这样的数据元素不存在,则返回值为0
	int 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;
	else
		return 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");
	else
		printf("链表不为空\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);
	else
		printf("获取第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);
	else
		printf("前驱获取失败\n");
	

	if (NextElem(L, cur_e, next_e))
		printf("第一个值 %d 的结点的后继结点值为 %d \n", cur_e, next_e);
	else
		printf("后继获取失败\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,否则返回false
	if (L->next) // 非空
		return false;
	else
		return 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个位置之前插入元素e
	int 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,否则返回false
	int 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无定义,返回false
	pNode 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无定义,返回false

	pNode 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;
	else
		return false;
}

//11、查询满足相应条件的结点元素,并返回序位
int LocateElem(pNode L, ElemType e, bool(*compare)(ElemType, ElemType))
{ // 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为ture,否则为false)
  // 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。
  //           若这样的数据元素不存在,则返回值为0
	int 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;
	}
}










  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值