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

链式存储相关术语:

数据域:存储元素数值数据

指针域:存储直接后继结点的存储位置

  1. 结点:数据元素的存储映像。又1数据域和指针域两部分组成

  2. 链表:n个结点由指针链组成一个链表

  3. 单链表,双链表,循环链表

    • 结点只有一个指针域的链表,称为单链表或线性链表

    • 结点有两个指针域的链表,称为双链表

    • 首尾相接的链表称为循环链表

  4. 头指针、头结点、和首元结点

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

    • 首元结点:是指链表中存储第一个数据元素a1的结点

    • 头结点:是在链表的首元结点之前附设的一个结点

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

讨论一、如何判断空表

不带头结点:头指针指向为NULL

带头结点:头结点指向为空

 

讨论二、在链表中设置头结点有什么好处?

  1. 便于首元结点的处理

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

  2. 便于空表和非空表的统一处理

    • 无论链表是否为空,头指针都指是指向头结点的非空指针,因此空表和非空表的处理也就统一了。

 

讨论三、头结点的数据域内装的是什么

头结点的数据域可以为空,可以存放线性表的长度等附加信息,此时结点不能计入链表长度值。

 

链式存储的特点:

  1. 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻

  2. 访问时只能通过头指针进入链表,并通过每个节点的指针域依次向后顺序扫描其余节点,所以寻找第一个结点和最后一个结点所花费的时间不等

  3. 顺序存储法

     

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

单链表的存储结构:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 例如,存储学生学号、姓名、成绩的单链表结点类型如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

单链表的初始化(算法2.6)(带头结点的单链表)

  • 即构造一个空表

【算法步骤】

  1. 生成新结点作头结点,用头指针L指向头结点

  2. 将头结点的指针域置空

【算法描述】

// 单链表的初始化
Status InitLinkList(LinkList &L){
	L = (LinkList)malloc(sizeof(LNode));// 删除的时候用free(L)		//	或 L = new LNode;  删除的时候用 delete L; 
	L->next = NULL;
	return OK; 
} 

 单链表的销毁

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

// 单链表的销毁 
Status DistoryLinkList(LinkList &L){
	Lnode *p;  				// 或者 LinkList p;
	while(L){
		p=L;
		L=L->next;
		free(p);
	} 
	return OK;
} 

 

清空单链表

链表依然存在,但是链表中无元素,成为空链表(头指针和头结点仍然在)

【算法思路】:依次释放所有结点,并将头结点指针域设置为空

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

// 清空单链表
Status ClearLinkList(LinkList &L){		// 将链表L重置为空表 
	Lnode *p,*q;			// 或者 LinkList p,q;
	p=L->next;
	while(p){				
		q = p->next; 
		free(p);
		p = q;				
	} 
	L->next = NULL; 		// 头结点指针域为空 
	return OK;
}

单链表的取值-- 取单链表中第i个元素的内容

算发步骤:

  1. 从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p = L->next。

  2. j做计数器,累计当前扫描过的结点数,j 初始值为1

  3. 当p指向扫描到的下一结点时,计数器j加1

  4. 当j == i 时,p 所指的结点就是要找的第 i 个结点

//  获取单链表中指定位置的数据
Status GetElem(LinkList L,int i,ElemType &e){ 			// 获取单链表中指定位置数据元素的内容,通过变量 e 返回 
	Lnode *p;							// 初始化 
	int j = 1;
	p = L->next;									
	while(p&&j<i){						// 向后扫面,直到 p 指向第i个元素,或 p 为空 
		p=p->next;
		j++;
	} 
	if(!p&&j>i){						// 第i个元素不存在 
		return ERROR;
	}
	e = p->date;						// 取第i个元素 
	return OK;
} 

按值查找--根据指定数据获取该数据所在的位置(地址)

【算法步骤】:

  1. 从第一个结点起,依次和 e 相比较

  2. 如果找到一个其值与e 相等的数据元素,则返回其在链表中的位置或地址

  3. 如果查遍整个链表都没有找到与e相等的元素,则返回0或者NULL

// 1、按值查找,返回地址 
/*
在线性表L中查找值为 e 的数据元素
找到,则返回对应的数据元素的地址,查找失败则返回NULL 
*/
Lnode *LocateElem(LinkList L,ElemType e){
	Lnode *p;
	p = L->next;
	while(p && p->date != e){
		p = p->next;
	}
	return p;
} 

// 2、按值查找,返回指定位置序号
// 查找	对应的值所在的位置,并返回位置序号 
int LocateElem_L(LinkList L,ElemType e){
	Lnode *p;
	int j=1;
	p = L->next;
	while(p&&p->date!=e){
		p = p->next;
		j++;
	}
	if(p){
		return j;
	}
	else{
		return 0;
	}
} 

插入 --在第i 个结点前插入值为e 的新结点

【算法步骤】:

  1. 首先找到ai-1的存储位置p

  2. 生成一个数据域为e的新节点s

  3. 插入新结点:

    • 新结点的指针域指向ai

    • 新结点ai-1的指针域指向新结点

// 插入 --在第i 个结点前插入值为e 的新结点
Status InsertLinkList(LinkList &L,int i,ElemType e){
	Lnode *p,*s;
	int j = 0;
	p = L;
	while(p&&j<i-1){   				// 寻找i-1个结点 
		p = p->next;
		j++;
	}
	if(!p||j>i-1){					// i大于表长加1或者小于1,插入位置非法 
		return ERROR; 
	}
	s = new Lnode;
	s->date = e;					// 生成新结点s ,将结点的数据域置为e 
	s->next=p->next;				// 将结点s插入L中 
	p->next=s;
	return OK;
}

 

删除---删除第i个节点

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

// 删除-- 将链表中第i个元素删除
Status DeleteLinkList(LinkList &L,int i,ElemType &e){
	Lnode *p,*q;
	int j=0;
	p = L;
	while(j<i-1&&p->next){				// 寻找第i个结点,并令p指向其前趋 
		p = p->next;
		j++;
	}
	if(j>i-1||p->next){					// 删除位置不合理 
		return ERROR;
	}
	q = p->next;						// 临时保存被删除结点的地址以备释放 
	p->next = q->next;					// 改变删除结点前驱结点的指针域 
	e = q->date;						// 保存删除结点的数据域 
	free(q);							// 释放删除结点的空间 
	return OK;
} 

 

建立单链表:头插法-元素插入在链表的头部也叫前插法

  1. 从一个空表开始,重复读入数据

  2. 生成新结点吗,将读入数据存放到新结点的数据域中

  3. 从最后一个结点开始,依次将各结点插入到链表的前端

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

// 头插法
void CreateLinkListHead(LinkList &L,int n){
	Lnode *p;
	L = (LinkList)malloc(sizeof(Lnode));
	L->next = NULL;						// 先建立一个带头结点的单链表
	for(int i = n;i>0;i--){
		p = (LinkList)malloc(sizeof(Lnode));	// 生成新结点 
		scanf(&p->date);				// 输入元素值
		p->next = L->next;				// 插到表头
		L->next = p; 
	} 
} 

尾插法--元素插入在链表的尾部,也叫后插法

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWFVOfk1MRg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

//尾插法
void CreateLinkList_R(LinkList &L,int n){
	Lnode *p,*r;
	L = new Lnode;
	L->next = NULL;
	r = L;				// 尾指针指向头结点 
	for(int i = 0;i<n;i++){
		p = new Lnode;		// 生成新结点 
		scanf(&p->date);	// 输入元素值
		p->next = NULL;
		r->next = p;		// 插到表尾 
		r = p;				// r 指向新的尾结点 
	}
} 

 

汇总

#include<stdio.h>

# define OK 1
# define ERROR 0

typedef char ElemType;
typedef int  Status;

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

// 1、单链表的初始化
Status InitLinkList(LinkList &L){
	L = new LNode;
	L->next = NULL;
	return OK;
} 

// 2、判断链表是否为空
void IsEmpty(LinkList &L){
	if(L->next){
		printf("单链表非空\n");
	}else{
		printf("单链表为空\n");
	}
} 

// 3、单链表的销毁
void DistoryLinkList(LinkList &L){
	LNode *p;
	while(L){
		p=L;
		L=L->next;
		delete p;
	}
	printf("单链表已经销毁\n"); 
} 

// 4、清空单链表
void ClearLinkList(LinkList &L){
	LNode *p,*q;
	p = L->next;
	while(p){
		q = p->next;
		delete p;
		p = q;
	}
	L->next=NULL;
	printf("链表已经清空\n"); 
} 

// 5、求单链表的表长
int LinkListLength(LinkList &L){
	LNode *p;
	int i = 0;
	p = L->next;
	while(p){
		i++;
		p = p->next;
	} 
	return i;
} 



// 6、获取单链表中指定位置的数据
Status GetElem(LinkList L,int i,ElemType &e){ 			// 获取单链表中指定位置数据元素的内容,通过变量 e 返回 
	Lnode *p;							// 初始化 
	int j = 1;
	p = L->next;									
	while(p&&j<i){						// 向后扫面,直到 p 指向第i个元素,或 p 为空 
		p=p->next;
		j++;
	} 
	if(!p&&j>i){						// 第i个元素不存在 
		return ERROR;
	}
	e = p->date;						// 取第i个元素 
	return OK;
} 

// 7-1、按值查找,返回地址 
/*
在线性表L中查找值为 e 的数据元素
找到,则返回对应的数据元素的地址,查找失败则返回NULL 
*/
Lnode *LocateElem(LinkList L,ElemType e){
	Lnode *p;
	p = L->next;
	while(p && p->date != e){
		p = p->next;
	}
	return p;
} 

// 7-2、按值查找,返回指定位置序号
// 查找	对应的值所在的位置,并返回位置序号 
int LocateElem_L(LinkList L,ElemType e){
	Lnode *p;
	int j=1;
	p = L->next;
	while(p&&p->date!=e){
		p = p->next;
		j++;
	}
	if(p){
		return j;
	}
	else{
		return 0;
	}
} 

// 8、插入 --在第i 个结点前插入值为e 的新结点
void InsertLinkList(LinkList &L,int i,ElemType e){
	LNode *p,*q;
	int j = 0;
	p = L;
	while(p&&j<i-1){
		p = p->next;
		j++;
	}
	if(!p||j>i-1){
		printf("结点插入失败\n");
	}
	q = new LNode;
	q->data = e;
	q->next=p->next;
	p->next = q;
	printf("结点插入成功\n");
}

// 9、删除-- 将链表中第i个元素删除 
void DeleteList(LinkList &L,int i){
	LNode *p,*q;
	int j = 0;
	p=L;
	while(p&&j<i-1){
		p = p->next;
		j++;
	}
	if(!p||j>i-1){
		printf("删除失败\n");
	}
	q= p->next;
	p->next = q->next;
	delete q;
	printf("结点删除成功!\n");
}

// 10、头插法
void InsertList_H(LinkList &L,int n){
	LNode *p;
	L = new LNode;
	L->next = NULL;
	for(int i = n;i>0;i--){
		p = new LNode;
		scanf(&p->data);
		p->next=L->next;
		L->next = p;
	}
	printf("数据插入头成功\n"); 
}

// 11、尾插法
void InsertList_R(LinkList &L,int n){
	LNode *p,*r;
	L = new LNode;
	L->next = NULL;
	r = L;
	for(int i=0;i<n;i++){
		p = new LNode;
		scanf(&p->data);
		p->next=NULL;
		r->next = p;
		r = p;
	}
	printf("尾插法插入成功!\n");
}

int main(){
	// 1、单链表的初始化
	LinkList L;
	int init = InitLinkList(L); 
	printf("单链表的初始化是否成功:%d\n",init);
	
	// 2、判断链表是否为空
	IsEmpty(L);
	
	// 3、单链表的销毁
//	DistoryLinkList(L); 
	
	// 4、链表清空
	ClearLinkList(L); 
	
	// 8、节点的插入
	InsertLinkList(L,1,'A');
	InsertLinkList(L,2,'A');
	
	// 10、头插法
	InsertList_H(L,3); 
	
	// 11、尾插法
	InsertList_R(L,3);
	
	// 5、求链表的表长
	int length = LinkListLength(L);
	printf("链表的表长:%d\n",length); 
	
	// 9、结点删除
	DeleteList(L,1); 
	
	// 5、求链表的表长
	int length1 = LinkListLength(L);
	printf("链表的表长:%d\n",length1); 
	return 0;
}

 

 

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XUN~MLF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值