线性表的链式表示和实现----线性(单)链表

Posted 烽火前秦路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表的链式表示和实现----线性(单)链表相关的知识,希望对你有一定的参考价值。

头文件 head.h


#include<string.h>
#include<ctype.h>
#include<malloc.h> /* malloc()等 */
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<stdlib.h> /* atoi() */
#include<io.h> /* eof() */
#include<math.h> /* floor(),ceil(),abs() */
#include<process.h> /* exit() */

/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
/* #define OVERFLOW -2 因为在math.h中已定义OVERFLOW的值为3,故去掉此行 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

typedef int ElemType;			//通过定义不同的类型来定义不同的存储类型


typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode, *LinkList;

//typedef struct LNode *LinkList;

Status InitList_L(LinkList *L);

Status DestoryList_L(LinkList *L);

Status ClearList_L(LinkList *L);

Boolean ListEmpty_L(LinkList L);

int ListLength_L(LinkList L);

Status GetElem_L(LinkList L, int i, ElemType *e);

int LocateElem_L(LinkList L, ElemType e);

Status PriorElem_L(LinkList L, ElemType cur_e, ElemType *pre_e);

Status NextElem_L(LinkList L, ElemType cur_e, ElemType *next_e);

Status ListInsert_L(LinkList *L, int i, ElemType e);

Status ListDelet_L(LinkList *L, int i, ElemType *e);

Status ListTraverse_L(LinkList L);

void CreateList_R_L(LinkList *L, int n);
void CreateList_F_L(LinkList *L, int n);

void MergerList_L_L(LinkList A, LinkList B, LinkList *C);

void MergerList_H_L(LinkList A, LinkList *B, LinkList *C);

算法实现


#include"head.h"

//带有头结点的单链表

Status InitList_L(LinkList *L)
{
	//操作结果:构造一个空线性表L

	*L = (LinkList)malloc(sizeof(LNode));
	if (!(*L))
	{
		printf("构造线性表失败!\n");
		exit(OVERFLOW);
	}
	(*L)->next = NULL;

	return OK;
}

Status DestoryList_L(LinkList *L)
{
	//初始条件:线性表L已存在,将其中的每一个指向后继元素的指针都free掉
	//操作结果:销毁线性表L

	LinkList p;
	while (*L)
	{
		p = (*L)->next;
		free(*L);//头结点也要释放掉
		*L = p;
	}

	return OK;
}

Status ClearList_L(LinkList *L)
{
	//初始条件:线性表L已存在
	//操作结果:将L重置为空表,不改变L,即只需将该链表中的指针全部释放掉但还保留其中的形式
	//注意和DestoryList做对比

	LinkList p, q;
	p = (*L)->next;		//p指向第一个结点

	while (p)			//要保存大致结构
	{
		q = p->next;
		free(p);
		p = q;
	}

	(*L)->next = NULL;

	return OK;
}

Boolean ListEmpty_L(LinkList L)
{
	//初始条件:线性表L已存在
	//操作结果:若L为空,则返回TRUE;否则,返回FASLE

	if (L->next)
		return FALSE;
	else
		return TRUE;
}

int ListLength_L(LinkList L)
{
	//初始条件:线性表L已存在
	//操作结果:返回L中数据元素个数

	int i = 0;
	LinkList p = L->next;		//单链表的第一个数据结点

	while (p)
	{
		i++;
		p = p->next;
	}

	return i;
}

Status GetElem_L(LinkList L, int i, ElemType *e)
{
	//初始条件:线性表L已存在且1<= i <= ListLength(L) ,L为带头结点的单链表头指针
	//操作结果:用e返回L中第i个数据元素的值

	LinkList p = L->next;
	int n = 1;

	while (p && n < i)			//找到第i个元素
	{
		p = p->next;
		n++;
	}

	if (!p || n > i)			//寻找失败则退出该函数
		return ERROR;

	*e = p->data;

	return OK;
}

int LocateElem_L(LinkList L, ElemType e)
{
	//初始条件:线性表L已存在
	//操作结果:返回L中第一个与元素e相等的数据元素的位序,若这样的元素不存在则返回0

	int n = 0;
	LinkList p = L->next;	//第一个结点,非头结点

	while (p)
	{
		n++;
		if (p->data == e)
			return n;

		p = p->next;
	}

	return 0;
}

Status PriorElem_L(LinkList L, ElemType cur_e, ElemType *pre_e)
{
	//初始条件:线性表L已存在
	//操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则失败,pre_e无定义

	LinkList p = L->next;		//代表当前元素的前驱
	LinkList q = p->next;		//代表当前元素

	while (q)					//从第二个元素开始
	{
		if (q->data == cur_e)
		{
			*pre_e = p->data;
			return TRUE;
		}
		p = q;
		q = q->next;
	}

	return INFEASIBLE;
}

Status NextElem_L(LinkList L, ElemType cur_e, ElemType *next_e)
{
	//初始条件:线性表L已存在
	//操作结果:若cur_e是L的数据元素且不是最后一个,则用pre_e返回它的后继,否则操作失败,next_e无意义

	LinkList p = L->next;

	while (p)
	{
		if (p->data == cur_e)
		{
			p = p->next;
			if (p)
			{
				*next_e = p->data;
				return TRUE;
			}
			else
				return INFEASIBLE;
		}
		p = p->next;
	}

	return INFEASIBLE;
}

Status ListInsert_L(LinkList *L, int i, ElemType e)
{
	//初始条件:线性表L已存在, 1<= i <=ListLength(L)+1
	//操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1

	LinkList p = *L;
	int n = 1;
	LinkList t;

	if (i<1 || i>ListLength_L(*L) + 1)
		return ERROR;

	while (p && n<i)//找到位序i之前的结点,即i-1的结点
	{
		p = p->next;
		n++;
	}

	if (!p || n > i)
		return FALSE;

	t = (LinkList)malloc(sizeof(struct LNode));

	t->data = e;
	t->next = p->next;
	p->next = t;

	return OK;
}

Status ListDelet_L(LinkList *L, int i, ElemType *e)
{
	//初始条件:线性表L已存在
	//操作结果:在头结点的单链表L中,删除第i个元素,并有e返回其值

	LinkList p = *L, q;
	int n = 1;

	if (i<1 || i>ListLength_L(*L) + 1)
		return ERROR;

	while (p->next && n < i)		//寻找第i个结点,并令p指向其前驱
	{
		p = p->next;
		n++;
	}

	if (!(p->next) || n > i)
		return FALSE;

	q = p->next;
	p->next = q->next;
	*e = q->data;
	free(q);

	return OK;
}

Status ListTraverse_L(LinkList L)
{
	//初始条件:线性表L已存在
	//操作结果:输出线性链表中的元素

	LinkList p = L->next;
	while (p)
	{
		printf("%d  ", p->data);
		p = p->next;
	}
	printf("\n");

	return OK;

}

//算法2.11 从表尾到表头逆向建立单链表的算法
void CreateList_R_L(LinkList *L, int n)
{
	LinkList p;
	*L = (LinkList)malloc(sizeof(LNode));
	(*L)->next = NULL;

	printf("请输%d个元素值【空格键隔开】:", n);
	for (; n >= 1; n--)
	{
		p = (LinkList)malloc(sizeof(LNode));
		scanf_s("%d", &p->data);
		p->next = (*L)->next;			//插入到表头
		(*L)->next = p;
	}
}

//算法2.11-1 从表头到表尾正向建立单链表的算法
void CreateList_F_L(LinkList *L, int n)
{
	LinkList p, q;
	(*L) = (LinkList)malloc(sizeof(struct LNode));
	(*L)->next = NULL;

	q = (*L);//q指向空表的头结点(相当于尾结点)
	printf("请输入%d个数据【空格键隔开】", n);
	for (int i = 1; i <= n; i++)
	{
		p = (LinkList)malloc(sizeof(struct LNode));
		scanf_s("%d", &p->data);
		q->next = p;//将新结点插到表尾
		q = q->next;//q指向尾结点,向前进
	}
	q->next = NULL;//最后一个节点的指针域为空
}

//算法2.12合并两个有序链表为一个有序链表,借鉴了顺序列表的思想,效率低
void MergerList_L_L(LinkList A, LinkList B, LinkList *C)
{
	//已知条件:单链表A和B中的元素按非递减排序
	//输出结果:归并A和B得到新的单链表C,C的元素也按值非递减排序
	int sA = ListLength_L(A), sB = ListLength_L(B);
	int nA = 1, nB = 1, nC = 1;
	ElemType eA, eB;
	while (nA <= sA && nB <= sB)
	{
		GetElem_L(A, nA, &eA);//在单链表中效率比较低
		GetElem_L(B, nB, &eB);//在单链表中效率比较低

		if (eA < eB)
		{
			ListInsert_L(C, nC, eA);
			nA++;
			nC++;
		}
		else
		{
			ListInsert_L(C, nC, eB);
			nB++;
			nC++;
		}
	}

	while (nA <= sA)
	{
		GetElem_L(A, nA, &eA);
		ListInsert_L(C, nC, eA);
		nA++;
		nC++;
	}

	while (nB <= sB)
	{
		GetElem_L(B, nB, &eB);
		ListInsert_L(C, nC, eB);
		nB++;
		nC++;
	}
}

//算法2.12合并两个有序链表为一个有序链表,充分利用例链表,效率高
void MergerList_H_L(LinkList A, LinkList *B, LinkList *C)
{
	LinkList pa = A->next, pb = (*B)->next, pc;
	*C = pc = A;

	while (pa&&pb)
		if (pa->data <= pb->data)
		{
			pc->next = pa;//将pa所指结点归并到C中
			pc = pa;//pc指向表C的最后一个节点
			pa = pa->next;
		}
		else
		{
			pc->next = pb;
			pc = pb;
			pb = pb->next;
		}

	pc->next = pa ? pa : pb;//插入剩余段
	free(*B);				//释放B的头结点
	(*B) = NULL;
}

测试文件 test.c


#include"head.h"

void main() /* 除了几个输出语句外,主程和main2-1.c很像 */
{
	LinkList L; /* 与main2-1.c不同 */
	ElemType e, e0;
	Status i;
	int j, k;
	i = InitList_L(&L);
	for (j = 1; j <= 5; j++)
		i = ListInsert_L(&L, 1, j);
	printf("在L的表头依次插入1~5后:L=");
	ListTraverse_L(L); /* 依次对元素调用visit(),输出元素的值 */
	i = ListEmpty_L(L);
	printf("\nL是否空:i=%d(1:是 0:否)\n", i);
	i = ClearList_L(&L);
	printf("清空L后:L=");
	ListTraverse_L(L);
	i = ListEmpty_L(L);
	printf("L是否空:i=%d(1:是 0:否)\n", i);
	for (j = 1; j <= 10; j++)
		ListInsert_L(&L, j, j);
	printf("在L的表尾依次插入1~10后:L=");
	ListTraverse_L(L);
	GetElem_L(L, 5, &e);
	printf("\n第5个元素的值为:%d\n", e);

	//检测LocateElem_L
	for (j = 0; j <= 1; j++)
	{
		k = LocateElem_L(L, j);
		if (k)
			printf("第%d个元素的值为%d\n", k, j);
		else
			printf("没有值为%d的元素\n", j);
	}

	for (j = 1; j <= 2; j++) /* 测试头两个数据 */
	{
		GetElem_L(L, j, &e0); /* 把第j个数据赋给e0 */
		i = PriorElem_L(L, e0, &e); /* 求e0的前驱 */
		if (i == INFEASIBLE)
			printf("元素%d无前驱\n", e0);
		else
			printf("元素%d的前驱为:%d\n", e0, e);
	}
	for (j = ListLength_L(L) - 1; j <= ListLength_L(L); j++)/*最后两个数据 */
	{
		GetElem_L(L, j, &e0); /* 把第j个数据赋给e0 */
		i = NextElem_L(L, e0, &e); /* 求e0的后继 */
		if (i == INFEASIBLE)
			printf("元素%d无后继\n", e0);
		else
			printf("元素%d的后继为:%d\n", e0, e);
	}
	k = ListLength_L(L); /* k为表长 */
	for (j = k + 1; j >= k; j--)
	{
		i = ListDelet_L(&L, j, &e); /* 删除第j个数据 */
		if (i == ERROR)
			printf("删除第%d个数据失败\n", j);
		else
			printf("删除的元素为:%d\n", e);
	}
	printf("依次输出L的元素:");
	ListTraverse_L(L);
	DestoryList_L(&L);
	printf("\n销毁L后:L=%u\n", L);

	system("pause");
}
 

Running result:

在L的表头依次插入1~5后:L=5  4  3  2  1

L是否空:i=0(1:是 0:否)
清空L后:L=
L是否空:i=1(1:是 0:否)
在L的表尾依次插入1~10后:L=1  2  3  4  5  6  7  8  9  10

第5个元素的值为:5
没有值为0的元素
第1个元素的值为1
元素1无前驱
元素2的前驱为:1
元素9的后继为:10
元素10无后继
删除第11个数据失败
删除的元素为:10
依次输出L的元素:1  2  3  4  5  6  7  8  9

销毁L后:L=0
请按任意键继续. . .

测试文件 test2.c

#include"head.h"

void main()
{
	LinkList A, B, C;
	LinkList D, E, F;
	int n = 0;
	InitList_L(&A);
	InitList_L(&B);
	InitList_L(&C);

	for (int i = 1; i <= 5; i++)
		ListInsert_L(&A, i, (i + 3));
	for (int i = 1; i <= 10; i++)
		ListInsert_L(&B, i, i);

	printf("单链表A中的元素有:");
	ListTraverse_L(A);
	printf("\n单链表B中的元素有:");
	ListTraverse_L(B);

	MergerList_L_L(A, B, &C);
	printf("\n单链表C中的元素有:");
	ListTraverse_L(C);
	printf("\n");

	printf("请输入单链表D中的元素个数值n:");
	scanf_s("%d", &n);
	CreateList_F_L(&D, n);

	printf("请输入单链表E中的元素个数值n:");
	scanf_s("%d", &n);
	CreateList_F_L(&E, n);

	MergerList_H_L(D, &E, &F);
	printf("\n单链表F中的元素有:");
	ListTraverse_L(F);
	printf("\n");

	system("pause");
}

Running Result:

单链表A中的元素有:4  5  6  7  8

单链表B中的元素有:1  2  3  4  5  6  7  8  9  10

单链表C中的元素有:1  2  3  4  4  5  5  6  6  7  7  8  8  9  10

请输入单链表D中的元素个数值n:3
请输入3个数据【空格键隔开】1 2 3
请输入单链表E中的元素个数值n:3
请输入3个数据【空格键隔开】4 5 6

单链表F中的元素有:1  2  3  4  5  6

请按任意键继续. . .



以上是关于线性表的链式表示和实现----线性(单)链表的主要内容,如果未能解决你的问题,请参考以下文章

线性表之链表C语言

线性表的链式表示和实现

数据结构与算法线性表的链式表示和实现,超详细C语言版

C语言基础知识中:线性表的顺序、链式存储结构分别是:随机存取和顺序存取结构对吗?

数据结构2:链表

用C语言编写链式存储结构下实现线性表的创建,插入,删除,按值查找