数据结构与算法(青岛大学-王卓老师)——笔记概要(第3周)

数据结构与算法(青岛大学-王卓老师)——学习笔记(第3周)

线性表的链式表示和实现

1. 知识点回顾

  • 顺序表的特点:以物理位置相邻表示逻辑关系。
  • 顺序表的优点:任一元素均可随机存取。
  • 顺序表的缺点:进行插入和删除操作时,需移动大量的元素。存储空间不灵活。

2. 线性表链式表示的定义

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

在这里插入图片描述

2.1 知识点

链表=节点+指针链

节点=数据域+指针域

数据域:存储元素数值数据
指针域:存储直接后继结点的存储位置

单链表是由头指针唯一确定,因此单链表可以用头指针的名字来命名。

在这里插入图片描述

3. 链表的分类

单链表、双链表、循环链表

  • 结点只有一个指针域的链表,称为单链表或线性链表;
  • 结点有两个指针域的链表,称为双链表;
  • 首尾相接的链表称为循环链表;

在这里插入图片描述

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

  • 头指针:是指向链表中第一个结点的指针 ;
  • 首元结点:是指链表中存储第一个数据元素a1的结点;
  • 头结点:是在链表的首元结点之前附设的一个结点;
    在这里插入图片描述

4.1 知识点

单链表一般有两种形式:带头结点和不带头结点。

其中带头结点是我们经常使用的。

讨论1:如何表示空表?
  • 无头结点时,头指针为空时表示空表;
  • 有头结点时,当头结点的指针域为空时表示空表。

在这里插入图片描述

讨论2:在链表中设置头结点有什么好处?
  • 便于首元结点的处理首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理。
  • 便于空表和非空表的统一处理无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
    在这里插入图片描述
讨论3:头结点的数据域内装的是什么?
  • 头结点的数据域可以为空也可存放线性表长度等附加信息,但此结点不能计入链表长度值。
    在这里插入图片描述

5.链表(链式存储结构)的特点

  • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
  • 访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。

6.单链表的定义

在这里插入图片描述
在这里插入图片描述

typedef struct Lnode//声明结点的类型和指向结点的指针类型
{
	Elemtype     data;//结点的数据域
	struct Lnode *next;//结点的指针域,此处为嵌套定义
}Lnode, *Linklist// Linklist为指向结构体 Lnode的指针类型

注意:结构体中next的类型为strcuct Lnode指针类型,与定义的结构体类型struct Lnode 加上*后一致。表明指针指向struct Lnode类型的结构体。

Lnode *p;//一般定义结点指针
Linklist L;//一般定义链表

注意:上述两种定义本质无区别,只是因为“约定俗称”,才会出现“人造”区别。

拓展:指针类型引用和一般类型的引用

  • 指针类型的引用
Lnode* p // 定义为指针类型
p->data = 2;
p->next = 0x12213;

LinkList  L;  // 相当于Lnode* p
L->data = xx;
l->next = xx;
  • 一般类型的引用
Lnode a; // 定义为普通类型
a.data = 2;
a.next = 0x12213;

6.1单链表定义实例

存储学生学号、姓名、成绩的单链表结点类型定义

typedef Struct student
{
char num[8];//数据域
char name[8];//数据域
int score;//数据域
struct student*next;//指针域
}Lnode. *Linklist.

但是通常情况下我们一般不这样定义,通常将要存储的结构类型定义为一个结构类型,然后用这个结构类型定义数据域,实现了一致性

typedef Struct{
char num [8];//数据域
char name[8];//数据域
int  score;//数据域
}Elemtype;

typedef struct Lnode{
Elemtype data;      //数据域
struct Lnode *next;//指针域
}Lnode,*Linklist

7. 单链表的基本操作

7.1单链表的初始化

【算法步骤】

  1. 生成新结点作头结点,用头指针指向头结点。
  2. 将头结点的指针域置空**
// 初始化链表,参数为引用类型,直接对链表进行操作
status InitList_L(LinkList& L)//这里为引用类型,需要对链表做更改
{
	L = new LNode;//或者(Linklist)mallod(sizeof(Lnode));
	L->next = NULL;// 头节点指针域为空
	return OK;
}

7.2 判断链表是否为空

【算法思路】判断头结点指针域是否为空

// 判断链表是否为空,参数为指针类型,不对链表做出更改
int IsEmpty_L(LinkList L)//这里没有加&,因为不需要对链表做出更改
{
	if(L->next == NULL)
		return 0;
	else:
		return 1;
}

7.3单链表的销毁

【算法思路】从头指针开始,依次释放所有结点
在这里插入图片描述

//销毁单链表 参数为引用类型,直接对链表进行操作
status Destory_L(LinkList& L)
{
	Lnode* p;
	while(L)
	{
		p = L;
		L = L->next;
		delete p ;//C语言用free(p)
	}
	return ok;
}

7.4 单链表的清空

链表仍存在,但链表中无元素,成为空链表(头指针和头结点仍然在)
【算法思路】年所有结点、井将头结指针域没置为空。

在这里插入图片描述

// 清空链表(保留头节点和头指针)
status Clear_L(LinkList& L)
{
	Lnode* p;
	Lnode* q;
	p = L->next;
	while(p)
	{
		q = p->next;
		delete p;
		p = q;
	}
	L->next = NULL;
	return OK;
}

总结归纳

从首元节点开始操作时, p = L->next;
从头结点开始操作时, p = L;

当删除到最后一个节点时,q先指向最后一个节点的指针域^(null),下一次循环p也指向NULL。循环终止的条件就是p=NULL。

7.5 求链表表长

在这里插入图片描述

// 求链表表长
int Get_len_L(LinkList L)
{
	Lnode* p ;
	int i = 0;
	p = L->next;
	while(p)
	{
		i++;
		p = p->next;
	}
}

8. 单链表的进阶操作

在这里插入图片描述

8.1 单链表取值

【思路】从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构。

  • 特殊情况1:要找的第i个元素超过链表长度
  • 特殊情况2:要找的第i个元素小于1
    在这里插入图片描述
    【算法步骤】
  • 从第1个结点(L->next)顺链扫描,用指针p指向当前扫描到的结 点,p初值p=L->next。
  • j做计数器,累计当前扫描过的结点数,j初值为1。
  • 当p指向扫描到的下ー结点时,计数器j加1。
  • 当j==i时,p所指的结点就是要找的第i个结点。
status Get_elem_data(LinkList L,int i,Elementtype& e)  // 找的的结果返回给e
{
	Lnode* p;
	p = L->next;
	int j =1;
	while(p&&j<i)
	{
		p = p->next;
		j++;
	}
	if(!p||j>i) return ERROR;
	e = p->data;
	return OK;
}

8.2 单链表查找

8.2.1 返回指针

在这里插入图片描述

// 根据值查找链表中的节点,返回节点的地址
//找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
Lnode* LocateElem(LinkList L,ElementType e)
{
	Lnode* p;
	p = L->next;
	while(p&&p->data!=e)  // p不为^或者未找到这个节点
		p = p->next;
	return p;
}
8.2.2 返回序号

在这里插入图片描述

int LocateElem(LinkList L, ElementType e)
{
	Lnode *p;
	p=L->next;
	int count=0;
	whlie(p->date!=e&&p)
	{	
		count++;
		p=p->next;
	}
	if(p) retunrn count;
	else retunrn error;	
}

注意:如果要返回查找节点的序号,需要多做一次判断后返回。

- 如果没找到,则p指向^,序号返回0;
- 如果找到,则直接返回序号。

8.3 单链表的插入

在这里插入图片描述
在这里插入图片描述

status insert_L(Linklist L,int i,Elemtpe e)
{
	Lnode* p;
	p = L->next;
	int j =1;
	while(p&&j<i-1)
	{
		p = p->next;
		j++;
	}
	if(!p||j>i) return ERROR;
	s=new Lnode;
	s->date=e;
	s->next=p->next;
	p->next=s;
	reruen OK;
}

8.4 单链表的删除

在这里插入图片描述

status delete_L(Linklist& L,int i,Elemtype &e)
{
 Lnode *p;
 Lnode *s;
 p=L->next;
 int j=1;
 while(p&&j<i-1)
 {
 	p=p->next;
 	j++;
 }
 if(j>i||!p) return error;
 s=p->next;
 p-next=s->next;
 e=s->date;
 delete s;
 retunrn OK;
}

8.5 单链表的查找、插入和删除时间复杂度

1.查找
因线性链表只能顺序存取,即在査找时要从头指针找起,査找的时间
复杂度为O(n)

2.插入和删除
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度
为O(1)

但是,如果要在单链表中进行前插或删除操作,由于要从头看找前驱
结点,所耗时间复杂度为(n)
在这里插入图片描述

8.4 单链表的建立

8.4.1 头插法

在这里插入图片描述

// 建立单链表,长度为n
void CreateList_L(LinkList& L , int n)
{
	L = new Lnode;//C语言为 L=(Linklist)malloc(sizeof(Lnode));
	L->next = NULL; // 初始化一个头结点
	for(int i = n;i>0;i--)
	{
		p = new Lnode;
		cin>>p->data;//c语言为scanf("%Elemtype",&(p->date);
		p-next = L->next;
		L->next = p;
	}
}

时间复杂度:O(n);

8.4.1 尾插法

在这里插入图片描述
在这里插入图片描述

// 建立单链表(尾插法),长度为n
void CreateList_wL(LinkList& L,int n)
{	
	Lnode *r;
	L = new Lnode;
	L->next = NULL;
	r = L;
	for(int i = 0;i<n;i++)
	{
		p = new Lnode;
		cin>>p->data;
		p->next = NULL;
		r->next = p;
		r = p;
	}
}

时间复杂度:O(n);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值