【数据结构】四、双向链表和双向循环链表

1.双向链表的定义

双向链表是在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表就形成了有两个方向不同的链

【数据结构】四、双向链表和双向循环链表_第1张图片

//双向链表的结点结构体
struct DuLinkNode
{
    Elemtype data;  //数据域
    struct DuLNode* prior;  //前驱指针域
    struct DuLNode* next;  //后继指针域
};
typedef DuLinkNode* DuLinkList;  //DuLinkList表示双向链表,DuLinkNode* 表示单个结点

【数据结构】四、双向链表和双向循环链表_第2张图片

2.双向循环链表的定义

双向循环链表就是在双向链表的基础上,让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的后继指针指向头结点

【数据结构】四、双向链表和双向循环链表_第3张图片

3.双向循环链表的对称性

p结点前驱结点的后继也是p结点,后继结点的前驱也是p结点

【数据结构】四、双向链表和双向循环链表_第4张图片

4.双向链表的操作概述

求链表长度、找第pos个位置的数据等操作只涉及一个方向的指针,所以它们的算法与单链表相同。但在进行插入、删除等操作时,会涉及两个方向的指针。

5.双向链表的插入操作

【数据结构】四、双向链表和双向循环链表_第5张图片

//在带头结点的双向循环链表L的第i个位置插入数据域为e的结点
void ListInsert_Du(DuLinkList &L, int i, Elemtype e)
{
    //先找到第i个位置的指针p
    DuLinkNode *p = new DuLinkNode;
    p = L->next; //从首结点开始
    int k = 0;
    while((p != NULL) && (k < i))
    {
        p = p->next;
        k++;
    }
    //创建一个新结点s
    DuLinkNode *s = new DuLinkNode;
    //将s结点的数据域赋值为e
    s->date = e;
    //把s的前驱指针指向第i个位置的前一个结点
    s->prior = p->prior;
    //将第i个位置的前一个结点的后继指针指向s结点
    p->prior->next = s;
    //将s结点的后继指针指向p
    s->next = p;
    //将p结点的前驱指针指向s
    p->prior = s;
}

6.双向链表的删除操作

【数据结构】四、双向链表和双向循环链表_第6张图片

//删除带头结点的双向循环链表L的第i个位置的结点
void ListDelete_Dul(DuLinkList &L, int i)
{
    //先找到第i个结点p
    DuLinkNode *p = new DuLinkNode;
    p = L->next;
    int k = 0;
    while((p != NULL) && (k < i))
    {
        p = p->next;
        k++;
    }
    //将第i个位置的前一个结点的后继指针指向第i个位置的后一个结点
    p->prior->next = p->next;
    //将第i个位置的后一个结点的前驱指针指向第i个位置的前一个结点
    p->next->prior = p->prior;
}

7.链表和顺序表的比较

链表的优点:(1) 存储空间可以动态分配和释放。(2) 删除和修改链表时,不需要移动结点,只需修改指针。

链表的缺点:(1) 存储空间比较大,存储密度小,因为每个结点的指针都需要额外的空间来存储。(2) 链表是非随机存取结构,对任一结点的操作都要从头指针依次指到指定的结点,增加了算法的复杂度。

顺序表的优点:(1) 存储密度为1,因为只需存储结点数据本身,不用为表示结点间的逻辑关系而增加额外的存储空间。(2) 随机存取,直接通过下标来存取。

顺序表的缺点:(1) 需要预先分配空间,可能会导致空间的浪费和溢出。(2) 删除和修改顺序表时,要移动结点本身,时间开销较大。

你可能感兴趣的:(数据结构,链表,数据结构)