c 语言数据结构,双链表及循环链表的相关操作

双链表

单链表是无法逆向检索的,有时候不太方便,有时候我们需要用到双链表。双链表结点的定义:

typedef struct DNode{	//定义结点类型
    ElemType data;	//数据域
    struct DNode *prior,*next;	//前驱和后继指针
}DNode,*DLinkList;

双链表的初始化

以下的代码片段是带头结点的双链表的初始化:

bool InitDLinkList(DLinkList &L){
    L=(DNode *)malloc(sizeof(DNode));	//分配一个头结点
    if(L==NULL)	//内存不足,分配失败
        return false;
    L->prior=NULL;	//头结点的prior 永远指向 NULL
    L->next=NULL;	//头结点之后暂时还没有结点
    return true;
}
void testDLinkList(){
    //初始化双链表
    DLinkList L;
    InitDLinkList(L);
    //.....
}

双链表结点的样子:

image-20220219113054893

双链表的插入

这种操作也相当于在p 结点之后插入s 结点,部分代码片段如下:

bool InsertNextDNode(DNode *p,DNode *s){
    if(p==NULL||s==NULL)	//非法参数
        return false;
    s->next=p->next;
    if(p->next!=NULL)	//如果p 结点有后继结点
        p->next->prior=s;
    s->prior=p;
    p->next=s;
    return true;
}

下面结合动图理解一下上面的代码:

插入双

双链表的删除

这不就是相当于删除p 结点的后继结点吗?看如下代码实现:

bool DeteleNextDNode(DNode *p){
    if(p==NULL)
        return false;
    DNode *q=p->next;	//找到p 的后继结点q
    if(q==NULL)
        return false;	//p没有后继
    p->next=q->next;
    if(q->next!=NULL)	//q 结点不是最后一个结点
        q->next->prior=p;
    free(q);	//释放结点空间
   return true;
}

结合动图理解一下下面的代码:

删除

看到这里是不是我们也可以对一个双链表进行销毁呢?看如下代码片段:

void DestoryList(DLinkList &L){
    //循环释放各个数据结点
    while(L->next!=NULL)
        DeleteNextDNode(L);
    free(L);	//释放头结点
    L=NULL;	//头指针指向NULL
}

双链表的遍历

  • 前向遍历

    while(p!=NULL){
        //对p 结点作相应的处理
        p=p->prior;
    }
    
  • 后向遍历

    while(p!=NULL){
        p=p->next;
    }
    
  • 前向遍历(跳过头结点)

    while(p->prior!=NULL){
        p=p->prior;
    }
    

双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现。时间复杂度 O(n)。

循环链表

循环单链表

循环单链表也就是将尾结点的next 指针指向头结点:

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,* LinkList;

//初始化一个循环单链表
bool InitList(LinkList &L){
    L=(LNode *)malloc(sizeof(LNode));	//分配一个头结点
    if(L==NULL)
        return false;
    L->next=L;	//头结点next 指向头结点
    return true;
}

//判断循环单链表是否为空的方法
bool Empty(LinkList L){
    if(L->next==L)
        return true;
    else
        return false;
}

那如何判断结点p 是否为循环单链表的表尾结点呢?就是判断p 的下一个结点是头结点即可:

bool isTail(LinkList L,LNode *p){
    if(p->next==L)
        return true;
    else
        return false;
}

循环双链表

表头结点的prior 指向表尾结点;表尾结点的next 指向头结点:

image-20220219140059728

各项实现逻辑比较简单,这里就不在赘述了。

除了以上的几种线性表外,还有一种用数组的方式实现的链表 —— 静态链表,静态链表的增、删操作不需要大量移动元素,但容量固定不可变,不能随机存取,只能从头结点开始依次往后查找。 要做到心里有数哦~

posted @ 2022-02-21 17:10  Charmchin  阅读(139)  评论(0编辑  收藏  举报