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

头文件 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

请按任意键继续. . .
时间: 2024-07-30 10:17:26

线性表的链式表示和实现----线性(单)链表的相关文章

数据结构算法C语言实现(二)---2.3线性表的链式表示和实现之单链表

一.简述 [暂无] 二.头文件 1 #ifndef _2_3_part1_H_ 2 #define _2_3_part1_H_ 3 //2_3_part1.h 4 /** 5 author:zhaoyu 6 email:[email protected] 7 date:2016-6-4 8 note:realize my textbook <<数据结构(C语言版)>> 9 */ 10 //----线性表的单链表存储结构---- 11 /** 12 My Code 13 to mak

【数据结构】-线性表的链式存储结构

引言:由于线性表的顺序存储结构在插入和删除时需要大量移动数据元素,从而引入线性表的链式存储结构. 线性表的链式存储结构:用一组任意的存储单元(可以连续也可以不连续)存储线性表的数据元素. 为了表示数据元素ai和其直接后继ai+1之间的逻辑关系,对ai来说,除了存储其本身的数据信息外,还需要存储其直接后继的存储位置.这两部分信息组成数据元素ai的存储映像(结点).它包含两个域:其中存储数据元素信息的域称为数据域:存储直接后继存储位置的域称为指针域. n个结点链接成一个链表,称为线性链表,由于此链表

线性表的链式存储结构

1 n个结点链结成一个链表,即为线性表的链式存储结构,由于每一个结点只包含一个指针域,因此称为单链表. 链表中第一个结点的存储位置成为头指针,那么整个链表的存取就必须是从头指针开始了. 有时候会在单链表的第一个结点前附设一个结点,称为头结点. 头指针与头结点的区别: 头指针: (1)头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针. (2)头指针具有标识作用,所以常用头指针冠以链表的名字. (3)无论链表是否为空,头指针都不为空.头指针是链表的必要元素. 头结点: (1)

线性表的链式存储之单链表的尾插法

对单链表进行遍历.查找.插入.删除等操作,最终效果如下: 相关C代码如下: /*线性表的链式存储之单链表的尾插法*/ #include <stdio.h> #include <stdlib.h> #include <malloc.h> /*定义变量*/ typedef int DataType; typedef struct node{     //定义链表结点数据结构 DataType data; struct node * pNext; }NODE; typedef

线性表的链式存储

线性表的链式存储 线性表的链式存储 基本概念 设计与实现 实现代码 优缺点 1. 基本概念 链式存储定义 为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息. 表头结点 链表中的第一个结点,包含指向第一个数据元素的指针以及链表自身的一些信息 数据结点 链表中代表数据元素的结点,包含指向下一个数据元素的指针和数据元素的信息 尾结点 链表中的最后一个数据结点,其下一元素指针为空,表示无后继. 2.设计与实现 在C语言中可以用结构体来定义链

线性表的链式表示和实现

本节内容讨论的线性表的另一种表示方法----链式存储结构,由于它不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序存储结构所具有弱点,但同时也失去了顺序表可随机存取的优点. 1.线性链表 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的).因此,为了表示每个数据元素ai与其直接后续数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示直接后继的信息(即直接后继的存储位置).这两部分信息组

数据结构之线性表(链式存储结构)

线性表的实现分顺序存储结构和链式存储结构 上一节我们主要介绍了顺序存储结构,在最后我们还分别总结了顺序存储结构的优缺点, 对于顺序结构的缺点,我们有没有什么好的解决方法呢? 我们今天要介绍的线性表的链式存储结构就可以很好的解决顺序结构的缺点,一起来看. 顺序结构最大的缺点就是在进行插入和删除操作的时候,如果插入位置不理想,那么我们需要移动大量的元素,那产生这一问题的原因是什么呢? 仔细分析后,我们可以发现在顺序存储结构中,他们相邻的元素的存储位置也是相邻的,我们在申请内存的的时候,是一次性申请一

数据结构------线性表的链式表示与实现

不多说直接上代码: #include"stdafx.h" #include <stdio.h> #include <stdlib.h> /****************************************************************************** /* 数据类型和常量定义 /***********************************************************************

数据结构-线性表的链式结构

线性表的链式结构,与之前说过的线性表的顺序结构差不多,区别在于存储结构和方式的不一样. 在链式中,来一个数据就去寻找一个空间存储一个结点有多少数据占多大的地方,是动态的存储方式.另外顺序表如果存储1MB就只占1MB存储空间,但是链式表不一样,它会有额外的空间去存储对应结点的指针. 这样一分析结构就出来了,链式结构为两部分:1.结点域.2.指针域.实现方法用动态存储. 1 #include "stdio.h" 2 #include "stdlib.h" 3 4 typ