06. 双向链表

一、什么是双向链表

  双向链表在数据结构上附加一个域,使它包含指向前一个单元的指针即可。其开销是增加了一个附加的链,它增加了空间的需求,同时也使得插入和删除的开销增加一倍。

双向链表

typedef int ElementType;

typedef struct LNode
{
    ElementType Data;
    struct LNode * Prior;
    struct LNode * Next;
} LNode, * List;

二、求表长

/**
 * @brief 求双向链表的表长
 * 
 * @param PtrL 双向链表
 * @return int 双向链表的长度
 */
int Length(List PtrL)
{
    List p = PtrL;                                                              // p指向表的第一个节点
    int j = 0;

    while (p != NULL)
    {
        p = p->Next;
        j++;                                                                    // 当前p指向的是第j个节点
    }
    return j;
}

三、查找元素

/**
 * @brief 按序号查找
 * 
 * @param PtrL 双向链表
 * @param K 要查找的序号
 * @return List 如果找到返回指向第K个的指针,否则返回NULL
 */
List FindKth(List PtrL, int K)
{
    List p = PtrL;                                                              // p指向表的第一个节点
    int i = 1;

    if (PtrL == NULL)
    {
        printf("链表为空!\n");
        return NULL;
    }

    if (i < 1 || i > Length(PtrL) + 1)
    {
        printf("查找的位置不合法\n");
        return PtrL;
    }

    while (p != NULL && i < K)
    {
        p = p->Next;
        i++;                                                                    // 当前p指向的是第j个节点
    }

    return (i == K) ? p : NULL;
}
/**
 * @brief 按值查找
 * 
 * @param PtrL 双向链表
 * @param X 要查找的元素
 * @return List 如果找到返回指向X的指针,否则返回NULL
 */
List FindElement(List PtrL, ElementType X)
{
    List p = PtrL;                                                              // p指向表的第一个节点

    if (PtrL == NULL)
    {
        printf("链表为空!\n");
        return NULL;
    }

    while (p != NULL && p->Data != X)
    {
        p = p->Next;
    }

    return p;
}

四、插入元素

  1. 先构造一个新节点,用 s 执行。
  2. 再找到链表的第 i-1 个结点,用 p 指向。
  3. 然后修改指针,插入结点(p 之后插入新结点是 s)。

双向链表的插入

双向链表的表头插入

/**
 * @brief 插入一个元素
 * 
 * @param PtrL 双向链表
 * @param i 要插入的位置
 * @param X 要插入的元素
 * 
 * @return List 指向添加元素的双向链表的头指针
 */
List Insert(List PtrL, int i, ElementType X)
{
    List p = NULL, s = NULL;

    if (i < 1 || i > Length(PtrL) + 1)
    {
        printf("插入的位置不合法\n");
        return PtrL;
    }

    if (PtrL == NULL)
    {
        PtrL = (List)malloc(sizeof(LNode));
        PtrL->Data = X;
        PtrL->Prior = PtrL->Next = NULL;
        return PtrL;
    }
  
    if (i == 1)                                                                 // 新节点插入在表头
    {
        s = (List)malloc(sizeof(LNode));                                        // 申请新节点
        s->Data = X;                                                            // 新节点的数据域为表头
        s->Prior = NULL;                                                        // 新节点的前驱为NULL
        s->Next = PtrL;                                                         // 新节点指向原来的表头
        PtrL->Prior = s;                                                        // 原来表头节点的前驱为新节点
        return s;
    }

    p = FindKth(PtrL, i - 1);                                                   // 找到第i-1个节点
    if (p == NULL)                                                              // 插入的位置不合法
    {
        printf("插入的位置不合法\n");
        return PtrL;
    }

    s = (List)malloc(sizeof(LNode));                                            // 申请新节点
    s->Data = X;
    s->Next = p->Next;                                                          // 新节点指向p的下一个节点
    s->Prior = p;                                                               // 新节点的前驱为p
    if (p->Next != NULL)                                                        // 如果p的后继不为NULL
    {                                                                           // p的后继的前驱指向新节点
        p->Next->Prior = s;
    }
    p->Next = s;                                                                // 节点的后继指向新节点

    return PtrL;
}

五、删除元素

  1. 先找到链表的第 i 个节点,用 p 指向。
  2. 然后修改指针,删除 p 所指节点。
  3. 最后释放 p 所指节点的空间。

双向链表的删除

双向链表的表头删除

/**
 * @brief 删除一个节点
 * 
 * @param PtrL 双向链表
 * @param node 要删除的节点
 * @return List 指向删除后的双向链表的头指针
 */
List DeleteNode(List PtrL, LNode * node)
{
    List p = PtrL, s = NULL;

    if (PtrL == NULL)
    {
        printf("链表为空!\n");
        return NULL;
    }
  
    if (node == PtrL)                                                           // 如果是头节点
    {
        if (node->Next == NULL)                                                 // 如果只有一个节点
        {
            free(PtrL);
            return NULL;
        }
      
        s = PtrL;                                                               // s指向表头
        p->Next->Prior = NULL;
        p = p->Next;                                                            // 删除表头,表头指向下一个节点
        free(s);                                                                // 释放s
        return p;
    }
  
    if (node->Next == NULL)                                                     // 如果是最后一个节点
    {
        node->Prior->Next = NULL;
    }
    else
    {
        node->Prior->Next = node->Next;
        node->Next->Prior = node->Prior;
    }
    free(node);

    return PtrL;
}
/**
 * @brief 删除一个元素
 * 
 * @param PtrL 双向循环链表
 * @param i 要删除元素的位置
 * @return List 指向删除后的双向循环链表的头指针
 */
List DeleteByPositon(List PtrL, int i)
{
    List p = NULL, s = NULL;

    if (PtrL == NULL)
    {
        printf("链表为空!\n");
        return NULL;
    }

    if (i < 1 || i > Length(PtrL))
    {
        printf("删除的位置不合法\n");
        return PtrL;
    }

    p = FindKth(PtrL, i);                                                       // 找到第i个节点
    if (p == NULL)
    {
        printf("第%d个节点不存在!\n", i);
    }
    else
    {
        PtrL = DeleteNode(PtrL, p);
    }

    return PtrL;
}
/**
 * @brief 按元素删除
 * 
 * @param PtrL 双向循环链表
 * @param X 要删除的元素
 * @return List 指向删除后的双向循环链表的头指针
 */
List DeleteByElement(List PtrL, ElementType X)
{
    List p = PtrL;
    LNode * node = FindElement(PtrL, X);

    if (p == NULL)
    {
        printf("链表为空!\n");
        return NULL;
    }

    if (node == NULL)
    {
        printf("要删除的元素不存在!\n");
    }
    else
    {
        PtrL = DeleteNode(PtrL, node);
    }

    return PtrL;
}

六、遍历链表

/**
 * @brief 遍历双向循环链表
 * 
 * @param PtrL 双向循环链表
 */
void PrintDoubleCircularLinkedList(List PtrL)
{
    List p = PtrL;
  
    while (1)
    {
        printf("%d ", p->Data);
        if (p->Next == PtrL)
        {
            break;
        }
        p = p->Next;
    }
    printf("\n");
}
posted @ 2023-06-25 20:49  星光樱梦  阅读(20)  评论(0编辑  收藏  举报