双向循环链表

本文详细介绍了双向循环链表的概念、实现方式及基本操作,包括初始化、清空、销毁、遍历等,并提供了完整的代码示例。此外,还讨论了双向循环链表的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/xu_ya_fei/article/details/49744699?locationNum=7&fps=1

单链表是不错的,但是呢,人无完人,玉有微瑕,单链表也不是完美的,单链表的缺点是只能往前,不能后退,虽然有循环单链表,但后退的成本还是很高的,需要跑一圈。在这个时候呢,双向链表就应运而生了,再加上循环即双向循环链表就更加不错了。所谓双向链表只不过是添加了一个指向前驱结点的指针,双向循环链表是将最后一个结点的后继指针指向头结点。下图以及以下代码为带头结点的双向循环链表的表示。然后聊的事情还按老规矩在代码里聊喽(代码可是写了很好的注释的哦,嘿嘿!)


这里写图片描述


/* 定义结点类型 */
typedef struct DuLNode
{ ElemType data;                //数据域
  DuLNode *prior;               //直接前驱指针
  DuLNode *next;               //直接后继指针
}DuLNode,*DuLinkList;

//函数声明
void InitList(DuLinkList *L);//产生双向循环链表
void ClearList(DuLinkList L);//清空双向循环链表
void DestroyList(DuLinkList &L);//销毁双向循环链表
int TestListEmpty(DuLinkList L);//判断双向循环链表是否为空
int GetListLength(DuLinkList L);//获取双向循环链表的长度
int GetElem(DuLinkList L,int i,ElemType *e);//获取双向循环链表中第i个元素的值
//返回L中第1个与e满足关系compare()的数据元素的位序
int LocateElem(DuLinkList L,ElemType e,int(*compare)(ElemType,ElemType));
int GetPriorElem(DuLinkList L,ElemType cur_e,ElemType *pre_e);//前驱判断与获取
int GetNextElem(DuLinkList L,ElemType cur_e,ElemType *next_e);//后继判断与获取
DuLinkList GetElemP(DuLinkList L,int i);//返回第i个元素的地址
int ListInsert(DuLinkList L,int i,ElemType e);//插入:在第i个位置之前插入元素e
int ListDelete(DuLinkList L,ElemType *e);//删除:删除表中第i个元素
void ListTraverse(DuLinkList L,void(*visit)(ElemType));//正序遍历双向循环链表
void ListTraverseBack(DuLinkList L,void(*visit)(ElemType));//逆序遍历双向循环链表

/* 产生双向循环链表L */
void InitList(DuLinkList *L)
{   
    *L=(DuLinkList)malloc(sizeof(DuLNode));
    if(NULL != (*L))
        (*L)->next = (*L)->prior = (*L);      //双向链表的头结点
    else
        exit(0);
}
/* 清空双向循环链表L */
void ClearList(DuLinkList L)
{   
    DuLinkList p = L->next;                   //p指向第一个结点
    while(p != L)                             //判断是否到链表尾部
    {  
        p = p->next;
        free(p->prior);
    }
    L->next = L->prior = L;                   //最后设置头结点的两个指针域指向其本身
}
/* 销毁双向循环链表L */
void DestroyList(DuLinkList *L)
{   
    DuLinkList q,p = (*L)->next;              //p指向第一个结点 
    while(p != (*L))                          //判断是否到链表尾部
    {
        q = p->next;
        free(p);
        p = q;
    }
    free((*L));                              //最后销毁头结点,并且为了避免野指针,将头结点指针置空
    (*L) = NULL;
}
/* 判断双向循环链表L是否为空:若L为空表,则返回TRUE,否则返回FALSE */
int TestListEmpty(DuLinkList L)
{ 
    if(L->next == L && L->prior == L)
        return true;
    else
        return false;
}
/* 获取双向循环链表的长度(不包括头结点) */
int GetListLength(DuLinkList L)
{    
     int len = 0;                             //len用来存储链表的长度
     DuLinkList p = L->next;                  //p指向第一个结点
     while(p != L)
     { 
        len++;
        p = p->next;
     }
     return len;
}

/* 获取双向循环链表中第i个元素的值:当第i个元素存在时,将其值赋给e并返回OK,否则返回ERROR */
int GetElem(DuLinkList L,int i,ElemType *e)
{ 
    int j =1 ;
    if(i > GetListLength(L))                // 首先对i的合法性进行判断
        return -1;
    DuLinkList p = L->next;                 //p指向第一个结点
    while(p != L && j < i)                  //顺指针向后查找直到p指向第i个元素
    { 
        j++;
        p = p->next;
    }
    if(p == L || j > i)                    //判断查找完后是否有异常情况
        return -1;
    *e = p->data;                          //返回第i个元素的值
    return 0;
}
/* 返回双向循环链表L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回值为0。
其中compare()是数据元素判定比较函数,可根据实际情况编写,比如判断相等(就是查找了) 大于 小于等等。 */
int LocateElem(DuLinkList L,ElemType e,int(*compare)(ElemType,ElemType))
{ 
    int i = 0;
    DuLinkList p = L->next;                //p指向第一个结点
    while(p != L)                          //还没到链表末尾
    { 
        i++;
        if(compare(p->data,e))        //找到这样的元素
            return i;
        p = p->next;
    }
    return 0;                            //最后如果没有找到就返回
}
/* 若cur_e是双向循环链表L的数据元素,且不是第一个,则用pre_e指针保存返回它的前驱结点的值(数据域),如果操作成功函数返回0值,如果操作失败,pre_e无定义,函数返回-1值*/
int GetPriorElem(DuLinkList L,ElemType cur_e,ElemType *pre_e)
{ 
    DuLinkList p = L->next->next;       //p指向第2个元素
    while(p != L)
    { 
        if(p->data == cur_e)           //p指向结点的数据与cur_e值进行比较判断
        {
            *pre_e = p->prior->data;   //将p的前驱结点的值赋给*pre_e,pre_e保存返回
            return 0;                  //函数返回0值表示操作成功
        }
        p = p->next;
    }
    return -1;                         //函数返回-1值表示操作失败
}
/* 若cur_e是双向循环链表L的数据元素,且不是最后一个,则用next_e指针保存返回它的后继结点的值(数据域),如果操作成功函数返回0值,如果操作失败,next_e无定义,函数返回-1值 */
int GetNextElem(DuLinkList L,ElemType cur_e,ElemType *next_e)
{
     DuLinkList p = L->next->next;     //p指向第二个元素
     while(p != L)
     { 
        if(p->prior->data == cur_e)    //p所指结点的前驱与cur_e进行比较判断
        { 
            *next_e = p->data;         //将p所指结点的值赋给*next_e,next_e保存返回
            return 0;                  //函数返回0值表示操作成功
        }
        p = p->next;
     }
     return -1;                        //函数返回-1值表示操作失败
}
/* 在双向链表L中返回第i个元素的地址。i为0,返回头结点的地址。若第i个元素不存在,返回NULL*/
DuLinkList GetElemP(DuLinkList L,int i)
{
    int j;
    DuLinkList p = L;                  //p指向头结点
    if(i < 0 || i > GetListLength(L))  //对i的合法性进行判断
        return NULL;
    for(j = 1;j <= i;j++)              //p指向第i个结点
        p = p->next; 
    return p;                         //返回第i个元素的地址
}
/* 插入: 在双向循环链表L中第i个位置之前插入元素e,其中i的合法值为1≤i≤表长+1 */
int ListInsert(DuLinkList L,int i,ElemType e)
{ 
    DuLinkList p,s;
    if(i < 1 || i > GetListLength(L)+1)
        return -1;
    p = GetElemP(L,i-1);             //在L中确定第i个结点前驱的位置指针p即确定第i个结点的前驱位置
    if(!p) 
        return -1;
    s = (DuLinkList)malloc(sizeof(DuLNode));  //生成新结点
    if(!s) 
        return -1;
    s->data = e;                    //将e赋给新的结点
    s->prior = p;                   //新结点的前驱为第i-1个结点
    s->next = p->next;             //新结点的后继为第i个结点
    p->next->prior = s;            //第i个结点的前驱指向新结点
    p->next = s;                  //第i-1个结点的后继指向新结点
    return 0;                     //操作成功,返回0
}
/* 删除:删除双向循环链表L的第i个元素,并将要删除的元素放在指针e中保存返回,其中i的合法值为1≤i≤表长 */
int ListDelete(DuLinkList L, int i, ElemType *e)
{  
    DuLinkList p;
    if(i < 1 || i > GetListLength(L)+1)
        return -1;
    p = GetElemP(L,i);            //在L中确定第i个元素的位置指针
    if(!p) 
        return -1;
    *e = p->data;                //把第i个结点的元素的值赋给e
    p->prior->next = p->next;    //第原i-1个结点的后继指向原第i+1个结点
    p->next->prior = p->prior;   //第原i+1个结点的前驱指向原第i-1个结点
    free(p);                     //释放p指针所指向的结点
    return OK;
}
/* 正序遍历双向循环链表,遍历是调用visit函数进行 */
void ListTraverse(DuLinkList L,void(*visit)(ElemType))
{
    DuLinkList p = L->next;     //p指向第一个结点
    while(p != L)
    { 
        visit(p->data);         //对p所指结点调用函数visit()
        p = p->next;
    }
    printf("\n");
}
/* 逆序遍历双向循环链表,遍历是调用visit函数进行 */
void ListTraverseBack(DuLinkList L,void(*visit)(ElemType))
{ 
    DuLinkList p = L->prior;    //p指向尾结点
    while(p != L)
    { 
        visit(p->data);         //对p所指结点调用函数visit()
        p=p->prior;
    }
    printf("\n");
}
/* compare函数,这个函数可以根据实际情况进行编写,这里只是最简单的比较是否相等函数 
   如果比较两个元素相等就返回1,否则返回-1。 */
   int compare(ElemType datai,ElemType dataj)
   {
    if(datai == dataj)
        return 1;
    else
        return -1;
   }
   /* visit函数,这个函数可以根据实际情况进行编写,这里只是最简单的输出元素。*/
   void visit(ElemType data)
   {
    printf("%d\n", data);
   }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221

最后还是来看看双向循环链表的时间复杂度,关于查找 插入 删除操作,多出的那个前驱指针并没有起多大作用,在时间性能上仍然是和单链表一样是O(n)。添加前驱指针后是给某个结点的前后结点操作带来了方便,可以有效提高算法的时间性能,但是因为每个结点都多了一个前驱指针,会增加一些空间消耗,总的来看是空间换时间吧


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值