数据结构(四)—— 线性表的链式存储

线性表的链式存储

目录

1.链式存储结构与顺序存储结构的比较

2.单链表、双链表与循环链表

3.单链表的实现


1.链式存储结构与顺序存储结构的比较

        顺序存储的特点:已物理相邻表示逻辑关系,任一元素均可随机存取。

        链式存储的特点:用一组物理位置任意的存储单元来存放线性表的数据元素,这组存储单元可以是连续的也可以是不连续的,甚至可以分布在内存中的任意位置上,链表中元素的逻辑次序和物理次序不一定相同。

        两种存储方式可以用下图来辅助理解。

顺序存取:查找第i个位置的数据,需要通过从第一个到第i个,顺序查找,所以存取的时候都是需要按顺序进行,所以叫顺序存取。

随机存取:随便一个i位置的数据,可以通过查找数组第 (i-1)个来查找,存取的时候同样可以存第i个位置或者取第i个位置的元素,所以叫随机存取。

可以用以下图来辅助理解:

2.单链表

        单链表,指结点只有一个指针域的链表,也可以称为线性链表。每个单链表都有一个头指针,也就是链表的首地址,其结点分为两个部分,一个是指针域一个是数据域(如下图所示),其中指针域存下一个结点的地址,而数据域为该结点存放的元素。

        用单链表的方式来表示线性表,有两种方式,带头结点跟不带头结点。不带头结点,顾名思义就是链表的头指针指向的下一个即为首元素结点;而带头结点的链表则头指针指向头结点,而头结点再指向首元素结点。如下图所示:

 

         带头结点的好处:

        便于首元素结点的处理:首元素结点的地址保存在头结点的指针域中,所以在链表中的第一个位置上的操作和其他位置的一致,无需进行特殊处理。

        便于空表和非空表的统一处理:无论链表是否为空,头指针都只想头结点的非空指针,因此空表和非空表的处理也就一致了。

3.单链表的实现

        单链表定义:用结构体的方式定义,定义一个数据域跟一个指针域。这里实现的是不带头结点的单链表。

 这段代码的ElenType为data的类型(相当于结构体的自定义类型);

typedef int ElemType; //定义数据类型
typedef struct LNode  //声明结点的类型和指向结点的指针类型
{
	ElemType data;       //结点的数据域
	struct LNode* next;  //结点的指针域
}LNode,*LinkList;        //LinkList为指向结构体Lnode的指针

注:定义的结构体LNode的类型名称为LNode其指针的为LinkList。即  *LNode = LinkList ,因为后面的函数多数都是对链表的指针域进行操作,所以定义指针类型LinkList方便后面的操作。

定义函数的操作:


#define TRUE 1
#define FALSE 0
#define VETERY 1 
#define DEFEAT 0
#define NOFINE 0
typedef int Status;

Status InitLinkList(LinkList L); //初始化单链表
LinkList List_HeadInsert(LinkList L); //头插入法
LinkList List_TailInsert(LinkList L); //尾插入法
LNode* GetElem(LinkList L, int i);  //按序号查找结点值
LNode* LocateElem(LinkList L, ElemType e); //按照结点值查找结点
void Delete_Node(LinkList L, int i); //按序号删除结点
void Print_Node(LinkList L);  //打印单链表
int Linklength(LinkList L);   //计算结点个数
int ListEmpty(LinkList L);    //判断单链表是否为空
Status ClearList(LinkList L); //清空单链表
Status DestroyList(LinkList L);  //销毁单链表

 单链表的基本操作:对单链表进行初始化、建立、判断空值、取值、查找、删除、打印、求表长、清空、销毁十一个操作进行编写代码。

代码块粘贴在最后

初始化,即开辟一个空间,创建单链表的头指针;

Status InitLinkList(LinkList L) //初始化单链表
{
	L = (LinkList)malloc(sizeof(LNode));  //开辟一个指针
	L->next = NULL; //单链表没有下一个指针
	return VETERY;
}

注:(强制转换的类型)malloc(sizeof(类型空间)),malloc是C语言进行开辟空间的函数,如果还不理解的话,可以去看看官方文档的解释。

判断单链表是否为空,只要头指针指向的下一个地址不为空,则还有元素结点在。

int ListEmpty(LinkList L)    //判断单链表是否为空
{
	if (L->next)
	{
		printf("链表不为空!\n");
		return FALSE;
	}
		
	else
	{
		printf("链表为空!\n");
		return TRUE;
	}		
}

清空单链表

        清空单链表采用两个指针进行操作,通过定义p指针为L->next,则为首元素结点,然后用另外一个q指针指向p->next则为第二个元素结点,然后释放p指针free(p),在将q指针赋予给p指针,然后通过不断释放p指针从而实则释放全部的元素结点。下图辅助理解:

Status ClearList(LinkList L) //清空单链表
{
	LinkList p, q;
	p = L->next;
	while (p)
	{
		q = p->next;
		free(p);
		p = q;
	}
	L->next = NULL;
	return VETERY;
}

销毁单链表

        销毁单链表将整个单链表的空间都释放掉,因此是从头指针开始往后释放每一个结点。

Status DestroyList(LinkList L)  //销毁单链表
{
	LinkList p;
	while (L)
	{
		p = L;
		L = L->next;
		free(p);
	}
	return VETERY;
}

求单链表长度

int Linklength(LinkList L)   //求单链表长度
{
	int k = 0;
	while (L->next != NULL)
	{
		k++;
		L = L->next;	
	}
	return k;
}

打印单链表

void Print_Node(LinkList L)   //打印单链表
{
	while (L->next != NULL)
	{
		L = L->next;
		printf("%d ", L->data);

	}
	printf("\n");
}

头插法建立单链表

        通过从第一个元素结点进行插入结点,可以由下图辅助理解:

LinkList List_HeadInsert(LinkList L)//头插入法
{
	LNode *new;
	int i = 10;
	for (i = 0; i < 10; i++)
	{
		new = (LNode*)malloc(sizeof(LNode));   //开辟一个新指针空间
		new->data = i;    //给指针的数据域赋值
		new->next = L->next;   //将新结点的下一个指针指向首元素结点,则代替首元素结点
		L->next = new;  //头指针指向新结点,即新节点为首元素结点
	}
	return L;
}

 尾插法建立单链表

        顾名思义,就是从尾部插入元素,通过下图进行辅助理解:

LinkList List_TailInsert(LinkList L) //尾插入法
{
	LNode* new;
	LNode* tail = L;
	for (int i = 0; i < 10; i++)
	{
		new = (LinkList)malloc(sizeof(LNode));
		new->data = i;
		tail->next = new; //将最后一个结点下一个指向new指针
		tail = new;       //最后一个指针为new指针
	}
	tail->next = NULL;    //最后一个指针指向NULL
	return L;
}

 按序号查找结点值

        通过序号 i 来查找相对应的结点值,通过第一个到第 i 个结点,从而返回结点地址,通过结点地址可以获取到对应的结点值。

LinkList GetElem(LinkList L, int i)    //按序号查找结点值
{
	int j = 1;
	LNode* p = L->next;  //定义辅助指针p
	if (i == 0)          //如果为0,则返回头指针地址
		return L;
	if (i < 1)           //如果小于1则返回NULL空指针
		return NULL;
	while (p && j < i)   //在j>=i和p不为NULL之前,循环继续
	{
		p = p->next;     //p指下一个指针
		j++;             //j+1
	}                    //查找得到的话为j=i时返回,查找不到,则一直到p为NULL
	return p;      
}

按值查找表结点

        通过第一个结点的data开始与目标值e进行对比,直到查找到,则返回结点值。若果没有则返回NULL。

LinkList  LocateElem(LinkList L, ElemType e) //按照结点值查找结点
{
	LNode* p = L->next;
	while (p->data != e && p != NULL)
	{
		p = p->next;
	}
	return p;
}

按序号删除结点

        通过GetElem函数查找结点的前驱指针,然后将前驱指针 p 指向待删除结点 q 的后继指针,从而释放待删除结点free(q)。通过下图进行辅助理解:

void Delete_Node(LinkList L, int i) //按序号删除结点
{
	LinkList  p = GetElem(L, i - 1);  //查找前驱结点
	LinkList  q;                    
	q = p->next;                      //令q等于待删除结点
	p->next = q->next;                //将p的下一个指向q的下一个
	free(q);                          //删除q
}

代码:

 test.c

#include "LinkList.h"

int main()
{
	LNode L;           //定义链表L
	InitLinkList(&L);  //初始化链表
	//List_HeadInsert(&L);   //头插法创建链表
	//Print_Node(&L);        //打印链表
	List_TailInsert(&L);     //尾插法创建链表
	Print_Node(&L);          //打印链表 
	LNode* p = GetElem(&L, 3);    //查找i = 3 位置的指针
	printf("查找成功3的数据为%d \n", p->data);    //打印3位置的元素数据
	LNode* q = LocateElem(&L, 5);                      //查找值为5的元素的指针
	printf("查找成功5的位置为%p \n", q);          //打印查找的指针值

	int l = LinkLength(&L);                             //链表长度
	printf("%d\n", l);                            //打印链表长度
	 
	Delete_Node(&L, 4);								//删除i = 4的结点
	printf("删除成功!\n");                        
	Print_Node(&L);                                     //打印链表

	int i = LinkLength(&L);           
	printf("%d\n", i);                           //查看链表长度

	ClearList(&L);                                      //清空链表
	if(!ListEmpty(&L))                                  //判断链表是否为空
		Print_Node(&L);                                 //不为空则打印链表
	return 0;
}

LinkList.,h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define VETERY 1 
#define DEFEAT 0
#define NOFINE 0

typedef int ElemType; //定义数据类型
typedef int Status;
typedef struct LNode
{
	ElemType data;       //结点的数据域
	struct LNode* next;  //结点的指针域
}LNode,*LinkList;        //LinkList为指向结构体Lnode的指针


Status InitLinkList(LinkList L); //初始化单链表
LinkList List_HeadInsert(LinkList L); //头插入法
LinkList List_TailInsert(LinkList L); //尾插入法
LinkList GetElem(LinkList L, int i);  //按序号查找结点值
LinkList LocateElem(LinkList L, ElemType e); //按照结点值查找结点
void Delete_Node(LinkList L, int i); //按序号删除结点
void Print_Node(LinkList L);  //打印单链表
int LinkLength(LinkList L);   //计算结点个数
int ListEmpty(LinkList L);    //判断单链表是否为空
Status ClearList(LinkList L); //清空单链表
Status DestroyList(LinkList L);  //销毁单链表

LinkList.c

#include "LinkList.h"

Status InitLinkList(LinkList L) //初始化单链表
{
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;
	return VETERY;
}
LinkList List_HeadInsert(LinkList L)//头插入法
{
	LNode *new;
	int i = 10;
	for (i = 0; i < 10; i++)
	{
		new = (LNode*)malloc(sizeof(LNode));
		new->data = i;
		new->next = L->next;
		L->next = new;
	}
	return L;
}
LinkList List_TailInsert(LinkList L) //尾插入法
{
	LNode* new;
	LNode* tail = L;
	for (int i = 0; i < 10; i++)
	{
		new = (LinkList)malloc(sizeof(LNode));
		new->data = i;
		tail->next = new;
		tail = new;
	}
	tail->next = NULL;
	return L;
}
LinkList GetElem(LinkList L, int i)    //按序号查找结点值
{
	int j = 1;
	LNode* p = L->next;
	if (i == 0)
		return L;
	if (i < 1)
		return NULL;
	while (p && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}
LinkList LocateElem(LinkList L, ElemType e) //按照结点值查找结点
{
	LNode* p = L->next;
	while (p->data != e && p != NULL)
	{
		p = p->next;
	}
	return p;
}
void Delete_Node(LinkList L, int i) //按序号删除结点
{
	LNode* p = GetElem(L, i - 1);
	LNode* q;
	q = p->next;
	p->next = q->next;
	free(q);
}
void Print_Node(LinkList L)   //打印单链表
{
	while (L->next != NULL)
	{
		L = L->next;
		printf("%d ", L->data);

	}
	printf("\n");
}
int LinkLength(LinkList L)   //求单链表长度
{
	int k = 0;
	while (L->next != NULL)
	{
		k++;
		L = L->next;	
	}
	return k;
}
int ListEmpty(LinkList L)    //判断单链表是否为空
{
	if (L->next)
	{
		printf("链表不为空!\n");
		return FALSE;
	}
		
	else
	{
		printf("链表为空!\n");
		return TRUE;
	}		
}
Status ClearList(LinkList L) //清空单链表
{
	LinkList p, q;
	p = L->next;
	while (p)
	{
		q = p->next;
		free(p);
		p = q;
	}
	L->next = NULL;
	return VETERY;
}
Status DestroyList(LinkList L)  //销毁单链表
{
	LinkList p;
	while (L)
	{
		p = L;
		L = L->next;
		free(p);
	}
	return VETERY;
}

运行截图:

注:当前为单链表的相关操作以及代码展示,链表除了单链表,还有双链表和循环链表,后续学习完再进行学习笔记的编写!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值