数据结构(一)—链表

一、背景

作为机械狗转行,数据结构当然是不可缺少的,疫情假期里闲来在家无事,刚好接下给小孩教数据结构的活,所以自学了简单的数据结构用法,算是数据结构的入门吧。做个笔记记下来,其实平常喜欢用思维导图进行记录,这算复习一遍,所以再用MarkDown进行记录,顺便发个博。

话不多说,今天先来记录一下链表简单的概念、实现等。

二、链表概念及特点

概念

链表是物理存储单元上非连续的、非顺序的存储结构,它是由一个个结点,通过指针来联系起来的,其中每个结点包括数据指针

适用场合

  • 大内存空间分配

  • 元素频繁删除和插入

  • 若数据以查找为主,很少涉及增删,则选择数组

基本操作

书上主要都是利用抽象数据类型进行定义的,我这里直接用大白话,最终目的还是要理解每种数据结构的特点及实现。

  • 初始化
  • 插入
  • 删除
  • 查询
  • 取值

分类

  1. 单链表

    单链表不用多做介绍,看一眼图都懂。

    image-20200315163441065

  2. 循环链表

    循环链表(Circular Linked List),表中最后一个结点的指针域指向头结点,整个链表形成一个环。

    image-20200315163618673

  3. 双向链表

    双向链表(Double Linked List),为了克服单链表查找某结点的直接前驱结点,必须要从表头指针出发的缺点。故在双向链表的结点中有两个指针域,一个指向后继,一个指向前驱,如下图。

    image-20200315163718451

三、单链表的实现

本文只先实现单链表,利用C++编码。链表这里的代码加了一些自己的方法和书本的不完全一样。直接上码。注意主函数的调用在最后哦。

  • 单链表的存储结构初始化链表

    首先是定义单链表的存储结构及初始化链表:

#include<iostream>
using namespace std;

//单链表的存储结构
typedef char ElemType; //存储元素类型
typedef struct LNode
{
	ElemType data;//结点数据域
	struct LNode *next;//结点指针域,指向下一结点
}LNode,*LinkList;//LinkList为指向结构体LNode的指针类型

//链表初始化
void InitList(LinkList &L)
{
	L=new LNode;//生成新结点作为头结点,用头指针L指向头结点
	L->next=NULL;//头结点指针域置空
} 

​ 注意我们这里的头结点是一直不用的结点,数据域不存数据,第一个数据存到的是首元结点,不要混淆,如下图:

image-20200315165758438

​ OK,这三个概念不要搞混淆,继续下一步。

  • 创建单链表

    创建单链表,创建单链表又可以分为前插法后插法,顾名思义,就是在链表头结点后插入新结点,还是链表尾部插入新结点。

//创建单链表
//前插法
void CreateList_H(LinkList &L,int n)
{//这里输入一个n,限定了链表的长度,可以不这么做,利用其它方式停止链表插入,
 //这里为了方便展示头插法的算法逻辑,因此不必在意这点
	InitList(L);//先建立一个带头结点的空链表
	for(int i;i<n;i++)
	{
		LNode *p=new LNode;//生成新结点*p
		cin>>p->data;//输入数据
		p->next=L->next;//令新结点指针域指向原首元结点
		L->next=p;//令头结点指向新结点
	} 
}

//尾插法
void CreateList_R(LinkList &L,int n)
{
	InitList(L);//先建立一个带头结点的空链表
	LNode *r=L;//临时指针r指向头结点
	for(int i=0;i<n;i++)
	{
		LNode *p=new LNode;//生成新结点
		cin>>p->data;//输入数据,填充数据域
		p->next=NULL;//新结点指针域初始化为NULL
		r->next=p;//将新结点*p插入尾结点 r->next后
		r=p; //令r指向新的尾结点p
	} 
}
  • 插入

    下面我们来实现链表在任意位置的插入,先拿一个图来表示该插入过程,以便理解:

image-20200315171409531

​ ①查找第ai-1个结点,并将指针p指向该结点;

​ ②生成新结点*s;

​ ③将新结点*s的数据域置为e;

​ ④将新结点*s的指针域指向ai;

​ ⑤将结点p的指针域指向新结点s。

//单链表插入
//在带头结点的单链表L中第i个位置插入值为e的新结点
void ListInsert(LinkList &L,int i,ElemType e)
{
	LNode *p=L;
	int j=0;
	while(p!=NULL&&(j<i-1))
	{//查找第i-1个结点,令p指向该结点
		p=p->next;
		j++;
	}	
	if(p==NULL||j>i-1)
	{//这里判断i值是否存在或合法,当i>n+1或者i<1时,i值非法
		cout<<"i值非法!"<<endl;
		return;
	}
	
	LNode *s=new LNode;//生成新结点*s
	s->data=e;//新结点数据域填充
	s->next=p->next;//将新结点的指针域指向p->next
	p->next=s;//p->next指向s
} 
  • 删除

    删除元素,老样子,先来个图。

    image-20200315173115999

    代码如下:

//删除
//在单链表L中,删除第i个元素
void ListDelete(LinkList &L,int i)
{
	LNode *p=L;
	int j=0;
	while((p->next!=NULL)&&(j<i-1))
	{//查找第i-1个结点,p指向该结点
		p=p->next;
		j++;
	}
	if(p->next==NULL||(j>i-1))
	{//判断i值是否合理,当i>n或i<1时,删除位置不合理
		cout<<"i值非法,删除失败!"<<endl;
		return;
	}
	LNode *q=p->next;//临时保存被删结点,以备释放
	p->next=q->next;//改变删除结点前驱结点的指针域
	delete q; //释放删除结点的空间
} 
  • 取值查找

    取值和查找的就简单了,来一起整。

//取值//在单链表L中根据序号i获取元素的值
ElemType GetElem(LinkList &L,int i)
{
	int j=1;
	LNode *p=L->next;//初始化,p指向链表L的头结点,计数器j初值赋为1
	while((p!=NULL)&&j<i)
	{//顺着链表向后扫描,直到p为空或p指向第i个元素为止
		p=p->next;//p指向下一个结点
		j++;//计数器j++
	} 
	if(p==NULL||j>i)
	{//判断i值是否合法,i>n或i<=0时,不合法
		cout<<"i值不合法!"<<endl;
		return NULL;
	}
	return p->data;//返回元素
} 

//查找
//在单链表L中查找值为e的结点
LNode* LocateElem(LinkList &L,ElemType e)
{
	LNode *p=L->next;//初始化,令p指向头结点
	while(p!=NULL&&p->data!=e)
	{//向后扫描,直到p为空或p指向结点的数据域为e
		p=p->next;
	} 
	return p;//查找成功返回值为e的结点地址p,查找失败则p为NULL
} 
  • 调用及链表打印

    这里我们进行调用以上方法,进行测试,同时打印穿件的链表。

//打印链表
void PrintList(LinkList &L)
{
	LNode *p=L->next;
	while(p!=NULL)
	{
		cout<<p->data<<"\t";
		p=p->next;
	}
	cout<<"\n";
}

int main()
{
	LinkList L1;
	int n;
	cout<<"请输入链表元素数量"<<endl; 
	cin>>n;
	CreateList_R(L1,n);//后插法创建链表
	PrintList(L1);//打印链表
	ListDelete(L1,2);//链表删除第2个元素
	PrintList(L1);//打印链表
	ListInsert(L1,3,'z');
	PrintList(L1);
	return 0;
}

以上代码测试结果如下:

image-20200315174853167

OK,以上查找和查询功能大家可以自己去测试,应该都是没问题的。

欢迎大家留言讨论,转载请注明原文地址就好哟yo~

参考引用:

《数据结构(C语言版 第2版)》,好书啊,推荐入门小白去看~例如我,哈哈哈。

写文不易~因此做以下申明:

1.博客中标注原创的文章,版权归原作者 煦阳(本博博主) 所有;

2.未经原作者允许不得转载本文内容,否则将视为侵权;

3.转载或者引用本文内容请注明来源及原作者;

4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。

posted @ 2020-03-15 18:02  煦阳  阅读(774)  评论(0编辑  收藏  举报