数据结构之线性表3:各种链表

循环链表

定义

一个首尾相接的链表,单链表最后结点的指针由NULL改为指向头结点或者第一节点。

连接两循环链表

两个带有头结点的循环链表LA,LB,将两个循环单链表合并成一个循环链表,头指针为LA

LinkList merge_1(LinkList LA,LinkList LB)
{	Node *p,*q;
	p=LA;
    q=LB;
    while(p->next!=LA){
		p=p->next;		//找到LA表尾
    }	
    while(q->next!=LB){
		q=q->next;		//找到LB表尾
    }	
    q->next=LA;		//LB表尾指向LA表头
    p->next=LB->next;	//LB表尾指向LB第一节点
    free(LB);		//释放LB的头结点
    return (LA);
}

将第一个链表的尾鱼第二个表的第一结点连起来,让第二个表的尾指向第一个表的头结点。

主体解读:分别找到表尾后,将LB的表尾指向LA的头结点(组合循环链表还是要尾连首),将LA的表尾指向LB的第一结点(去掉LB的头结点)。
最后释放LB的头结点。

双向链表

定义

每个结点都有指向前和指向后两个指针

声明

typedef struct DNode{
	ElemType  data;
    struct DNode *prior;//指向前驱
    struct DNode *next;//指向后继
}*DoubleList,DNode;

和单链表基本一致。

前插操作

在双向链表的第i个位置前插入值为e的结点

void DlinkListIns(DoubleList L,int i,ElemType e){	//在双向链表的第i个位置前插入值为e的结点
	DNode *s,*p;	//一个申请,一个定位
    int k=0;
    p=L;
    while(p!=NULL&&k<i)		//因为双向,所以直接定位在第i个位置
   		{	p=p->next;
    		k++;
    	}
    if(p==NULL)		//由于找尾巴出问题
    	return FALSE;	//位置不合法
    s=(*DNode)malloc(sizeof(DNode));
    if(s)
    	{	s->data=e;		
    		s->prior=p->prior;		//新结点s获取插入位的前驱
            p->prior->next=s;		//插入位p前一个结点的后继指向新结点s
            s->next=p;		//s的后继为插入位
            p->prior=s;		//插入位的前驱指向新结点s
            return TRUE;		//成功
    	}
    else
    	return FALSE;		//失败   
}

主体解读:由于是在双向链表中操作,因此不需要像单链表一样定位在i的前一个结点,直接让定位指针定位到i上。 之后就是分别将p的前驱赋给s的前驱,p上一个结点的后继指向s,s后继指向p,p前驱指向s。

删除操作

删除第i个结点并将该结点值保存在e中

int DlinkDel(DoubleList L,int i,ElemType *e){
	DNode *p;
    int k;
    p=L;
    while(p!=NULL&&k<i)
    {	p=p->next;
    	k++;
    }
    if(p==NULL)
    {	return FALSE;	
    }
    *e=p->data;
    p->prior->next=p->next;
    p->next->prior=p->prior;
    free(p)
    return TRUE;
}

主体解读:p前驱的后继连到p的后继,p后继的前驱连到p的前驱。

静态链表

仅在C语言情况下能使用,其实就是用数组模拟链表

声明

  • cursor域为游标指示器,指示后继结点在数组中的数组下标
  • 数组首元为表的头结点,其cursor域指示了表中第一个结点的位置
  • 以cursor值为0表示静态单链表的结束。
typedef struct{
	ElemType data;
    int cursor;
}Component,StaticList[MaxSize];

每个结点的结构是由一个数据域一个游标组成,并且按这个结构声明了一个名为“StaticList”的数组。

创建静态链表

指将静态链表初始化为一个备用的静态单链表

整个表分两部分,最初已用空间是空,备用位置后面都空闲可用

void Creat(StaticList space,int *av)		//av是空闲可用位置 
{	int k;
	space[0]->cursor=-1;	//下标为0储存的游标为-1(位置无用)
    for(k=1;k<MaxSize-1;k++)
    	{	space[k]->cursor=k+1;	
    	}
    space[MaxSize-1]=-1;	//回到最初space[0]
    *av=1;	//提供可用下标位置
}

主体解读:给下标为0位置的游标设置-1,通过循环,从下标1到倒数第二个,分别赋游标以连接成指向下一个结点的状态,最后一个结点游标指回最初-1,并将空闲可使用的位置保存。

申请新结点

不同于顺序表和链表,申请新结点本质上是在被用表中减少一个结点。

给我一个av所指的结点,返回这个结点的下标

int Getnode(StaticList space,int *av)
{	int i;
	i=*av;		//获取av所指的结点
    *av=space[*av].cur;	//av后移一步
    return i;
}

主体解读:用一个变量获取*av指向的新结点,返回这个结点并让av后移一位

回收结点

本质是备用链表中增加一个结点

void Freenode(StaticList space,int *av,int k){		//回收序号为k的结点
	space[k].cursor=*av;
    *av=k;
}

主体解读:回收序号为k的头结点,先将需要回收的结点游标指向av(其实就是备用表头指针),再让av指向k,这样原来k的地方就被av指着(回收完成),并且原来av指的地方就变成了备用链表的第二个结点

摘自: 耿国华老师 https://www.bilibili.com/video/BV1kx411h7pF?p=20

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值