2.3.1 单链表的基本操作+代码

前言:

顺序表需要连续的空间且插入和删除操作需要移动大量元素,根据这些顺序表的缺点,我们引入了链表。
​ 链式存储线性表时,不需要使用地址连续的存储单元,即不要求逻辑上相邻的元素在物理位置上也相邻
它通过“链”建立元素之间的逻辑关系,因此插入和删除操作不需要移动元素,而只需修改指针,但也会失去顺序表可随机存取的优点。

2.3.1 单链表的定义

​ 线性表的链式存储又称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据元素之间的线性关系,对每个链表结点,除存放元素自身的信息之外,还需要存放一个指向其后继的指针

你可以理解成链表就是把顺序表全部“切”成一段一段,再利用指针将这些“碎片”连接起来,这些“碎片”就是结点

在这里插入图片描述

typedef struct LNode{//定义单链表节点类型
	ElemType data;//数据域
    struct LNode *next;//指针域
}LNode,*LinkList;//两者是一样的,都是别名

单链表解决了顺序表需要大量连续存储空间的缺点,但引入了指针域增加了一定空间上的开销

​ 单链表的元素离散地分布在存储空间中,因此是非随机存取的存储结构,查找特定结点时,需要从头开始遍历,依次查找

在创建链表时,可以选择带头结点和不带头结点这两种形式

通常用*头指针L(或head等)*来标识一个单链表,指出链表的起始地址,头指针为NULL,表示空表。

在单链表第一个数据结点之前附加一个结点,这就是头结点,头结点可以不带任何的信息,也可以记录表长信息,引入头结点是为了操作上的方便

单链表带头结点时,头指针L指向头结点,不带头结点时,头指针L指向第一个数据结点,表尾指针域为NULL,如下图所示
在这里插入图片描述
在这里插入图片描述

引入头结点后,可以带来两个优点:

  1. 由于第一个数据结点的位置被存放在头结点的指针域中,因此在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。

  2. 无论链表是否为空,其头指针都是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就得到了统一。

2.3.2 单链表上基本操作的实现

带头结点单链表的操作代码书写较为方便,如无特殊说明,本节均默认链表带头结点。

1.单链表的初始化

带头结点和不带头结点的单链表的初始化操作是不同的。

带头结点的单链表初始化时,需要创建一个头结点,并让头指针指向头结点,头结点的next域初始化为NULL。

bool InitList(LinkList &L){//带头结点的单链表的初始化
    L=(LNode*)malloc(sizeof(LNode));//之前定义了类型LNode,创建头结点
    L->next=NULL;//头结点之后暂时还没有元素结点
    return true;
}

不带头结点的单链表初始化时,只需将头指针L初始化为NULL。

    bool InitList(LinkList &L){//不带头结点的单链表的初始化
    L=NULL;
    return true; 
   }

注意

  1. 设p为指向链表结点的结构体指针,则*p表示结点本身,因此可用p->data或(*p).data访问*p这个结点的数据域,个人推荐前者,二者完全等价。
  2. 成员运算符(.)左边是一个普通的结构体变量,而指向运算符(->)左边是一个结构体指针。
  3. 通过(*p).next可以得到指向下一个结点的指针,直接用p->next->data,表示下一个结点的指针

2.求表长操作

就是记录数据结点的个数,主要的思想是遍历,从第一个结点开始访问下一个结点,设置一个变量(如len),len初始化为0,每访问一次,len+1

int Length(Linklist L){//因为要计算长度,故返回int整形len
	int len=0;
	LNode *p=L;
	while(p->next!=NULL)
    //如果下一个结点不为空,则继续
	p=p->next;//访问下一个结点
    len++;//注意顺序,访问后再++
    //不要颠倒顺序!
}
return len;
}

求表长的时间复杂度是O(n),注意单链表的长度是不包含头结点的

3.按序号查找结点

思想:主要的思想是遍历,从单链表的第一个结点开始,沿着next域从前往后依次搜索,直到找到第i个结点为止,则返回该结点的指针;

若i小于单链表的表长,则返回NULL

LNode *GetElem(LinkList L,int i){ 
//LNode强调返回的是一个结点,形参Linklist强调这是个单链表
	LNode *p=L;	//	指针p指向当前扫描到的结点
    int j=0;	//	记录当前结点的位序,头结点是第0个结点
    while(p!=NULL&&j<i){	//	循环找到第i个结点
     	p=p->next;
		j++; 
    }
return p;	//	返回第i个结点的指针或NULL
}

按序号查找操作的时间复杂度为O(n)

4.按值查找表结点

主要的思想还是遍历,从单链表的第一个结点开始,从前往后依次比较表中各结点的data

若某结点的data域等于给定值e,则返回该结点的指针

若整个单链表中没有这样的结点,则返回NULL。

LNode*LocateElem(LinkList L,ElemType e){ 
    LNode *p=L->next;
while(p!=NULL&&p—>data!=e)
	//代码3的意思是在遍历完之前找到data=e的结点
    p=p->next;
return p;
    //找到后返回该结点指针,否则返回NULL
}

按值查找操作的时间复杂度为O(n)=

5.插入操作

插入结点操作将值为x的新结点插入到单链表的第i个位置,插入之前要检查值和位置是否合法

首先查找第i-1个结点,假设第i-1个结点为*p,然后令新结点 s的指针域指向 *p的后继,再令结点 *p的指针域指向新插入的结点 *s

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

bool ListInsert(LinkList &L,int i,Elemtype e){
    LNode *p=L;//指针p指向当前扫描到的结点
    int j=0;
//记录当前结点的位序,头结点是第0个结点//循环找到第i—1个结点
p=p->next; j++; {
if(p==NULL)	//i值不合法 
    return false;
LNode *s=(LNode*)malloc(sizeof(LNode)); //建立新的结点
s->data=e;//赋值
s->next=p->next;//关键
p->next=s;//关键
return true;
}

代码中的s->next=p->next和p->next=s顺序不能颠倒!,否则,先执行p->next=s后,指向其原后继的指针就不存在了,再执行s->next=p->next时,相当于执行了s->next=s,显然不对

需注意的是,当链表不带头结点时,若是,则要做特殊处理,将头指针L指向新的首结点。当链表带头结点时,插入位置i为1时不用做特殊处理。

本算法的时间开销在于查找第i-1个元素,时间复杂度为O(n),如果在指定结点后面插入新结点,则时间复杂度O(1)

6.删除结点的操作

在这里插入图片描述

思想:删除结点的操作是将单链表的第i个结点删除。先检查删除位置的合法性,然后查找表中第i-1个结点,就是被删结点的前驱,再删除第i个结点

bool ListDelete(LinkList &L,int i,ElemType &e){ 
    LNode *p=L;//指针p指向当前扫描到的结点
    int j=0;//记录当前结点的位序,头结点是第0个结点
while(p!=NULL&&j<i-1){//循环找到第i—1个结点
    p=p->next;
	j++; 
}
if(p==NULLIIp->next==NULL)//i值不合法 
    return false;
LNode *q=p->next;//令q指向被删除结点
    e=q->data;//用e返回元素的值
    p->next=q->next;//将*q结点从链中“断开”
    free(q);
//释放结点的存储空间
    return true;
}

同插入算法一样,该算法的主要时间也耗费在查找操作上,时间复杂度为O(n)

当链表不带头结点时,需要判断被删结点是否为首结点,若是,则要做特殊处理,将头指针L指向新的首结点

当链表带头结点时,删除首结点和删除其他结点的操作是相同的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值