【数据结构 c语言版 】线性表的链式表示和实现

目录

一  单链表的表示和实现

1.单链表的存储结构

1.1.  头指针、头结点与首元结点

1.2.  带头结点单链表和不带头结点单链表的比较

2. 单链表的初始化

3.  单链表的长度

4.  单链表的插入

5.  单链表的删除

6.  单链表的查看

7.  单链表的撤销

完整代码

二  循环单链表

三  双向链表

1.  双向链表表的存储结构、初始化

2.  双向链表的插入、删除


结点:由数据元素域和一个或若干个指针域组成的一个结构体。
链表:链式存储结构的线性表。

一  单链表的表示和实现

1.单链表的存储结构

typedef char DataType;
typedef struct Node
{	//单链表的储存结构
	DataType data; //存放数据
	struct Node* next;  //存放指针
}SLNode;

1.  每个存储结点都包含两部分:数据域,指针域
2.  在单链表中,除了首元结点外,任一结点的存储位置由其直接前驱结点的链域的值指示。

1.1.  头指针、头结点与首元结点

 头指针是指向链表中第一个结点

头结点是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息,它不计入表长度。
首元结点是指链表中存储线性表第一个数据元素a0的结点。

1.2.  带头结点单链表和不带头结点单链表的比较

  1)  带头结点的单链表的第一个数据元素插入时,其插入在头结点后。

   2)  不带头结点的单链表的第一个数据元素插入时,其插入在头指针后。

   3) 不带头结点的单链表的其余数据元素插入时,其插入在前一节点后。

 

   4)  删除带头结点的单链表的第一个数据时,头结点指针指向第二个数据

   5) 删除不带头结点的单链表的第一个数据时,头指针指向第二个数据

 结论:插入操作时,有头结点链表方便

单链表一般构造成带头结点的单链表

2. 单链表的初始化

malloc(m)函数:向系统动态申请m字节长度的地址空间,并返回这段空间的首地址;
free(p)函数:释放指针p所指变量的存储空间,即彻底删除一个变量。
sizeof(x)运算符:计算变量x的长度(字节数);
以上有库函数/算符都在<malloc.h> 中

void ListInitiate(SLNode** hear)
{	//单链表的初始化
	*hear = (SLNode*)malloc(sizeof(SLNode));
	(* hear)->next = NULL;  //链尾标记NULL
}

3.  单链表的长度

int ListLength(SLNode* hear)
{	//单链表的长度
	SLNode* p = hear;  //p 指向头结点
	int size;
	for (size = 0; p->next != NULL; size++)
	{
		p = p->next;
	}
	return size;
}

4.  单链表的插入

实现步骤:

1.  将插入处前一节点指针指向所插入节点的指针

   (即  将所插入节点的指针指向插入处的节点,防止插入处节点丢失);

2.  将插入处前一节点指针指向所插入节点。

          x 节点先 生成再使用。

int ListInsert(SLNode* head, int i, DataType x)
{	//在顺序表的第i个位置之前插入数据值x
	//插入成功返回1,插入失败返回0
	SLNode* p, * q;
	p = head;
	int j = 0;
	for (j; p->next != NULL && j < i - 1; j++)
	{
		p = p->next;
	}
	if (j != i - 1)
	{
		printf("插入地方错误!\n");
		return 0;
	}
	q = (SLNode*)malloc(sizeof(SLNode));
	q->data = x;
	q->next = p->next;
	p->next = q;
	return 1;

}

5.  单链表的删除

实现步骤:

1.  将要删除节点前一节点的指针指向删除节点的后一节点

   (即  使得要没有指针指向删除节点);

2.  释放删除节点。

int ListDelete(SLNode* head, int i, DataType* x)
{	//删除顺序表L中位置i的数据元素值并存放到参数x中
	//删除成功返回1,失败返回0
	SLNode* p, * s;
	p = head;
	int j = 0;
	for (j; p->next != NULL && j < i - 1; j++)
	{
		p = p->next;
	}
	if (j != i - 1)
	{
		printf("删除位置错误!\n");
		return 0;
	}
	s = p->next;
	*x = s->data;
	p->next = s->next;
	free(s);
	return 1;

}

6.  单链表的查看

int ListGet(SLNode* head, int i, DataType* x)
{	//取顺序表中第i个元素存于x中
	//取出成功返回1,失败返回0
	SLNode* p;
	p = head;
	int j = 0;
	for (j; p->next != NULL && j < i; j++)
	{
		p = p->next;
	}
	if (j != i)
	{
		printf("参数i不合法!\n");
		return 0;
	}
	*x = p->data;
	return 1;

}

7.  单链表的撤销

void Destroy(SLNode **head)
{	//撤销单链表
	SLNode* p, * q;
	p = *head;
	for (p; p != NULL; free(q))
	{
		q = p;
		p = p->next;

	}
	*head = NULL;
}

完整代码

#include<stdio.h>
#include<malloc.h>

typedef char DataType;
typedef struct Node
{	//单链表的储存结构
	DataType data; //存放数据
	struct Node* next;  //存放指针
}SLNode;

void ListInitiate(SLNode** hear)
{	//单链表的初始化
	*hear = (SLNode*)malloc(sizeof(SLNode));
	(* hear)->next = NULL;  //链尾标记NULL
}

int ListLength(SLNode* hear)
{	//单链表的长度
	SLNode* p = hear;  //p 指向头结点
	int size;
	for (size = 0; p->next != NULL; size++)
	{
		p = p->next;
	}
	return size;
}

int ListInsert(SLNode* head, int i, DataType x)
{	//在顺序表的第i个位置之前插入数据值x
	//插入成功返回1,插入失败返回0
	SLNode* p, * q;
	p = head;
	int j = 0;
	for (j; p->next != NULL && j < i - 1; j++)
	{
		p = p->next;
	}
	if (j != i - 1)
	{
		printf("插入地方错误!\n");
		return 0;
	}
	q = (SLNode*)malloc(sizeof(SLNode));
	q->data = x;
	q->next = p->next;
	p->next = q;
	return 1;

}

int ListDelete(SLNode* head, int i, DataType* x)
{	//删除顺序表L中位置i的数据元素值并存放到参数x中
	//删除成功返回1,失败返回0
	SLNode* p, * s;
	p = head;
	int j = 0;
	for (j; p->next != NULL && j < i - 1; j++)
	{
		p = p->next;
	}
	if (j != i - 1)
	{
		printf("删除位置错误!\n");
		return 0;
	}
	s = p->next;
	*x = s->data;
	p->next = s->next;
	free(s);
	return 1;

}

int ListGet(SLNode* head, int i, DataType* x)
{	//查看
    //取顺序表中第i个元素存于x中
	//取出成功返回1,失败返回0
	SLNode* p;
	p = head;
	int j = 0;
	for (j; p->next != NULL && j < i; j++)
	{
		p = p->next;
	}
	if (j != i)
	{
		printf("参数i不合法!\n");
		return 0;
	}
	*x = p->data;
	return 1;

}

void Destroy(SLNode **head)
{	//撤销单链表
	SLNode* p, * q;
	p = *head;
	for (p; p != NULL; free(q))
	{
		q = p;
		p = p->next;

	}
	*head = NULL;
}

void main(void)
{
	SLNode* head;
	DataType x;
	ListInitiate(&head);
	ListInsert(head, 1, 'a');
	ListInsert(head, 2, 'b');
	ListInsert(head, 3, 'c');
	printf("%d\n", ListLength(head));
	ListGet(head, 3, &x);
	printf("%c\n", x);
	ListDelete(head, 2, &x);
	printf("%d\n", ListLength(head));
	ListGet(head, 2, &x);
	printf("%c\n", x);
	Destroy(&head);

}

二  循环单链表

在初始化函数中,将语句(*head)->next = NULL改为(*head)->next =head。

void ListInitiate(SLNode** hear)
{	//单链表的初始化
	*hear = (SLNode*)malloc(sizeof(SLNode));
	(* hear)->next = hear;  //链尾标记hear
}

 在删除与查看函数中,将更改其判断语句。

int ListDelete(SLNode* hear, int i, DataType* x)
{	//删除
	SLNode* p, * q;
	p = hear;
	int j = 0;
	for (j; j < i - 1; j++)
	{
		p = p->next;
		if (p->next == hear)
		{
			p = p->next;
		}
	}
	
	if (j != i - 1)
	{
		printf("删除位置错误!\n");
		return 0;
	}
	q = p->next;
	*x = q->data;
	p->next = q->next;
	free(q);
	return 1;

}

int ListGet(SLNode* head, int i, DataType* x)
{	//查看
	SLNode* p;
	p = head;
	int j = 0;
	for (j; j < i - 1; j++)
	{
		p = p->next;
		if (p->next == head)
		{
			p = p->next;
		}
	}
	
	if (j != i - 1)
	{
		printf("参数!不合法!\n");
		return 0;
	}
	*x = (p->next)->data;
	return 1;
}

在撤销函数中, 增加一标志点以判断循环链表已清空。

void Destroy(SLNode** head)
{	//撤销
	SLNode* p, * q;
	p = *head;
	int i = 0;
	for (p; i != 0 && p->next != *head; free(q))
	{
		q = p;
		p = p->next;
		i++;

	}
	*head = NULL;
}

完整代码,输出卡牌游戏,卡牌第一个位置输出 1 ,第二个位置输出 2 ,以此类推。

#include<stdio.h>
#include<malloc.h>

typedef char DataType;
typedef struct Node
{	
	DataType data; //存放数据
	struct Node* next;  //存放指针
}SLNode;

void ListInitiate(SLNode** head)
{	//初始化
	*head = (SLNode*)malloc(sizeof(SLNode));
	(*head)->next = *head;  //链尾标记hear
}

int ListLength(SLNode* hear)
{	//长度
	SLNode* p = hear;
	int size;
	for (size = 0; p->next != hear; size++)
		p = p->next;
	return size;
}


int ListInsert(SLNode* head, int i, DataType x)
{	//插入
	SLNode* p, * q;
	p = head;
	int j = 0;
	for (j; p->next != head && j < i - 1; j++)
	{
		p = p->next;
	}
	if (j != i - 1)
	{
		printf("插入地方错误!\n");
		return 0;
	}
	q = (SLNode*)malloc(sizeof(SLNode));
	q->data = x;
	q->next = p->next;
	p->next = q;
	return 1;

}

int ListDelete(SLNode* hear, int i, DataType* x)
{	//删除
	SLNode* p, * q;
	p = hear;
	int j = 0;
	for (j; j < i - 1; j++)
	{
		p = p->next;
		if (p->next == hear)
		{
			p = p->next;
		}
	}
	
	if (j != i - 1)
	{
		printf("删除位置错误!\n");
		return 0;
	}
	q = p->next;
	*x = q->data;
	p->next = q->next;
	free(q);
	return 1;

}

int ListGet(SLNode* head, int i, DataType* x)
{	//查看
	SLNode* p;
	p = head;
	int j = 0;
	for (j; j < i - 1; j++)
	{
		p = p->next;
		if (p->next == head)
		{
			p = p->next;
		}
	}
	
	if (j != i - 1)
	{
		printf("参数!不合法!\n");
		return 0;
	}
	*x = (p->next)->data;
	return 1;
}

void Destroy(SLNode** head)
{	//撤销
	SLNode* p, * q;
	p = *head;
	int i = 0;
	for (p; i != 0 && p->next != *head; free(q))
	{
		q = p;
		p = p->next;
		i++;

	}
	*head = NULL;
}


void main(void)
{
	SLNode* hear;
	DataType x;
	ListInitiate(&hear);
	ListInsert(hear, 1, '1');
	ListInsert(hear, 2, 'K');
	ListInsert(hear, 3, '2');
	ListInsert(hear, 4, '8');
	ListInsert(hear, 5, '3');
	ListInsert(hear, 6, '0');
	ListInsert(hear, 7, '4');
	ListInsert(hear, 8, 'j');
	ListInsert(hear, 9, '5');
	ListInsert(hear, 10, '9');
	ListInsert(hear, 11, '6');
	ListInsert(hear, 12, 'q');
	ListInsert(hear, 13, '7');

	ListDelete(hear, 1, &x);
	printf("%c", x);
	ListDelete(hear, 2, &x);
	printf("%c", x);
	ListDelete(hear, 3, &x);
	printf("%c", x);
	ListDelete(hear, 4, &x);
	printf("%c", x);
	ListDelete(hear, 5, &x);
	printf("%c", x);
	ListDelete(hear, 6, &x);
	printf("%c", x);
	ListDelete(hear, 7, &x);
	printf("%c", x);
	ListDelete(hear, 8, &x);
	printf("%c", x);
	ListDelete(hear, 9, &x);
	printf("%c", x);
	ListDelete(hear, 10, &x);
	printf("%c", x);
	ListDelete(hear, 11, &x);
	printf("%c", x);
	ListDelete(hear, 12, &x);
	printf("%c", x);
	ListDelete(hear, 13, &x);
	printf("%c", x);
	Destroy(&hear);



}

三  双向链表

双向链表:链表中每个结点除后继指针域和数据域外还有一个前驱指针域。
它有带头结点和不带头结点,循环和非循环结构。双向链表是解决查找前驱结点问题的有效途径。

1.  双向链表表的存储结构、初始化

 初始化时,前驱指针和后继指针各自构成自己的循环单链表。

2.  双向链表的插入、删除

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 线性表的定义和基本操作 - 线性表是一种数据结构,由零个或多个数据元素组成,数据元素之间是有序的,且每个数据元素只有一个前驱和一个后继。 - 基本操作包括插入、删除、查找、遍历等。 2. 线性表的顺序存储结构 - 线性表的顺序存储结构是指将线性表中的数据元素存储在一段连续的存储单元中。 - 顺序存储结构的优点是随机存取,操作简单高效。 - 顺序存储结构的缺点是插入、删除操作需要移动大量元素。 3. 线性表链式存储结构 - 线性表链式存储结构是指将线性表中的数据元素存储在一些不连续的存储单元中,这些存储单元可以是任意的物理位置。 - 链式存储结构的优点是插入、删除操作方便,不需要移动元素。 - 链式存储结构的缺点是不能随机存取,需要遍历整个链表才能访问某个元素。 4. 线性表的单链表和双向链表 - 单链表是指每个节点只有一个指针指向下一个节点。 - 双向链表是指每个节点有两个指针,一个指向前一个节点,一个指向后一个节点。 5. 线性表的循环链表 - 循环链表是指链表的最后一个节点的指针指向链表的第一个节点,形成一个环。 - 循环链表可以解决单链表遍历到最后一个节点时需要特殊处理的问题。 6. 线性表的静态链表 - 静态链表是指使用数组来实现链表的存储结构。 - 静态链表可以解决动态分配内存的问题,但是插入、删除操作仍然需要移动元素。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值