C语言数据结构与算法之循环,双向链表

一.循环单链表

1.初始化循环单链表(假设是带头节点和头指针的循环单链表)

typedef struct Node
{
    ElemType data;
    struct Node* next;
}Node,*LinkList;
void InitLinkList(LinkList*CL)//这里相当于二级指针,因为LinkList本身已经是Node的指针,其目的是通过二级指针改变一级指针的地址
{
    *CL=(LinkList)malloc(sizeof(Node));
    (*CL)->next=*CL;
}

2.建立循环单链表

假设线性表中节点的数据类型是字符,逐个输入这些字符,并以'$'作为结束标志符

void CreateCLinkList(LinkList CL)
{
    Node* rear,*s;
    char c;
    rear=CL;
    c=getchar();
    while(c!'$')
    {
        s=(Node*)malloc(sizeof(Node));
        s->data=c;
        rear->next=s;
        rear=s;
        c=getchar();
    }
    rear->next=CL;//再次指向头结点完成循环
}

3.循环单链表的合并方法

(1)采用头指针 (时间复杂度为O(N))

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;
    p->next=LB->next;
    free(LB);
    return LA;
}

(2)采用尾指针 (时间复杂度为O(1))

LinkList merge_2(LinkList RA,LinkList RB)//注意RA和RB是尾指针
{
   Node*p;
   p=RA->next;
   RA->next=RB->next->next;//将B的第一个结点放RA后面
   free(RB->next);//释放B的头结点
   RB->next=p;//找到A的头
   return RB;
}

二.双向链表

定义如下:

typedef struct DNode
{
    ElemType data;
    struct DNode*prior,*next;
}DNode,*DoubleList

1.双向链表初始化

void InitDoubleList(DoubleList*DL)
{
   DL=(DNode*)malloc(sizeof(DNode));
   DL->next=DL;
   DL->prior=DL;
}

2.双向链表的插入操作

在第i个结点前(算头结点)插入新的节点,成功返回1,失败返回0

int DlinkIns(DoubleList L,int i,ElemType e)
{
   if(i<=0)return FALSE;
    DoubleLink pre=L;int k=0;
    while(pre&&k<i-1)
    {
        pre=pre->next;
        k+=1;
    }
    if(pre==NULL)return FALSE;
    DNode* new=(DNode*)malloc(sizeof(Node));
    new->data=e;
    new->next=pre->next;
    new->prior=pre;
    pre->next->prior=new;
    pre->next=new;//成功插入
    return TRUE;
}

3.双向链表的删除操作

删除第i个结点

int DLinkDel(DoubleList L,int i,ElemType* e)//关键是要找到被删除的结点前面的那个结点(前驱位置),理论上不删除头结点
{
    if(i<=0)return false;
    Node* pre=L;int k=0;
    while(pre->next!=NULL&&k<i-1)
    {
        pre=pre->next;
        k+=1;
    }
    if(pre->next==NULL)return FALSE;
    DNode* cur=pre->next;//记录被删掉的结点
    *e=cur->data;
    cur->prior->next=cur->next;
    cur->next->prior=cur->prior;
    free(cur);
    return TRUE;
}

三.静态链表

#define Maxsize 10

typedef struct
{
    ElemType data;//数据位
    int cursor;//游标位
}Component,StaticList[Maxsize]
  //数组的第0个分量被设计成表的头结点,头结点的cursor指明了表中第一个结点的位置,表尾结点的cursor域为-1,表示静态单链表结束
 //其中cursor就是下一个结点的索引,尾节点的cursor为-1就象征着NULL
//问题所在:在多次进行插入和删除操作后会造成静态单链表的“假满”,即表中有很多空闲空间,但却无法再插入新元素,造成这种现象的原因是未   对已删除元素所在的空间进行回收

解决方法:将所有未被分配的结点空间以及因删除操作而回收的节点空间组成一个备用静态单链表,删除的结点可以加到备用静态单链表中,在插入时优先使用备用静态单链表的空间(备用链表的作用是回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。也就是说,静态链表使用数组申请的物理空间中,存有两个链表,一条连接数据,另一条连接数组中未使用的空间。)

通常,备用链表的表头位于数组下标为 0(a[0]) 的位置,而数据链表的表头位于数组下标为 1(a[1])的位置。

1.创建备用链表

void build_StaticList(StaticList L)
{
    for(int i=0;i<Maxsize;i++)
    {
        L[i].cursor=i+1;
        L[i].data=-1;//暂时储存无效数据
    }
    L[Maxsize-1].cursor=-1;//相当于最后一位指向NULL
}

2.初始化静态链表

//和上面的方法不同,1.中单纯的建立了备用链表,而我们这里采用第二种方法同时创建备用链表和数据链表,所以一般可以不用1.的方法

void Initial_StaticList(StaticList L,int * av)//av用于标记备用链表的头指针位置
{
    int k;
    L[0].cursor=-1;//数据位的游标暂定为-1(NULL)
    for(k=1;k<Maxsize-1;k++) L[k].cursor=k+1;//在这里数据位就不初始化了    
    *av=1;//备用头指针指向了下标1
    L[Maxsize-1].cursor=-1;//同时把最后一个的游标位置为-1;    
}

3.备用链表给数据链表分配节点空间

int GetNode(StaticList L)
{
    int i=*av;
    *av=L[*av].cursor;
    return i;
}//返回值就是备用链表头指针所在位置

4.备用链表回收结点(数据链表删除结点)

void FreeNode(StaticList L,int* av,int k)
{
    L[k].cursor=*av;
    *av=k;//直接把备用链表的头结点转换为k所在的结点
}//回收下标为k的结点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

时光诺言

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

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

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

打赏作者

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

抵扣说明:

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

余额充值