双链表中插入节点(C语言实现)

(翻译文,稍加调整,加入了自己的解释)

1. 相关背景介绍
1.1 双链表概念

    相比SLL(singly linked list,单链表),DLL(Doubly Linked List,双链表)多了一个名为 previous pointer (Prev)的指针,该指针指向前一节点。SLL与DLL的相同点是都拥有需要存储的数据,和指向下一节点的指针。
在这里插入图片描述
    C语言中DLL节点的表示:

/* Node of a doubly linked list */
struct Node { 
    int data; 
    struct Node* next; // Pointer to next node in DLL 
    struct Node* prev; // Pointer to previous node in DLL 
};
1.2 双链表的优势与劣势

    双链表相对于单链表的优势:
    1) DLL可以向前和向后遍历。
    2)如果给出了要删除的节点的指针,则DLL中的删除操作会更有效。
    3)我们可以在给定节点之前快速插入一个新节点。
    在单链列表中,要删除节点,需要指向上一个节点的指针。为了获得该先前节点,有时会遍历列表。在DLL中,我们可以使用Prev指针获取先前的节点。
    双链表相对于单链表的劣势:
    1) DLL的每个节点都需要额外的空间才能存储Prev指针。
     2)所有操作都需要一个额外的指针。例如,在插入过程中,我们需要同时修改Prev指针和Next指针。例如,在以下函数中,对于不同位置的插入,我们需要1到2个额外的步骤来设置Prev指针。

1.3 双链表插入节点的位置

    可以在四个位置添加节点:
    1)在DLL的前端;
    2)在给定节点之后;
    3)在DLL的末尾;
    4)在给定节点之前。

2. 不同位置插入数据
2.1 在DLL的前端添加节点

    新节点总是添加到给定链接列表的首节点之前。新添加的节点成为DLL新的首节点。例如,如果给定的链表是10152025,我们在前面加了一个项5,那么链表就变成了510152025。我们把在列表前端添加节点的函数称为push(),push() 需要修改head指针,使head指针指向新的节点。
在这里插入图片描述
    整个过程需要5个步骤:

/* Given a reference (pointer to pointer) to the head of a list 
   and an int, inserts a new node on the front of the list. */
void push(struct Node** head_ref, int new_data) 
{ 
    /* 1. allocate node */
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); 
  
    /* 2. put in the data  */
    new_node->data = new_data; 
  
    /* 3. Make next of new node as head and previous as NULL */
    new_node->next = (*head_ref); 
    new_node->prev = NULL; 
  
    /* 4. change prev of head node to new node */
    if ((*head_ref) != NULL) 
        (*head_ref)->prev = new_node; 
  
    /* 5. move the head to point to the new node */
    (*head_ref) = new_node; 
}

    以上5个步骤中的4个步骤与单链表的最前面插入步骤相同。唯一的额外步骤是多了第4步,更改原首节点的Prev指针,将指针从NULL指向新增节点。

2.2 在给定节点之前添加节点

在这里插入图片描述
    步骤:
    给定节点为next_node,新节点为new node,其数据为new_data。
    1、检查next_node是否为NULL。如果为NULL,则从函数退出,因为在NULL之前不能添加任何新节点;
    2、为新节点分配内存,将其称为new_node;
    3、设置new_node-> data = new_data
    4、将new_node的前一个指针指向next_node的前一个节点,new_node-> prev = next_node-> prev
    5、将next_node的前一个指针设置为new_node,next_node-> prev = new_node。在图例中,就是将C节点的prev指针,从指向A节点改为指向B节点。
    6、将此new_node的下一个指针设置为next_node,new_node-> next = next_node;
    7、如果new_node的前一个节点不为NULL,则将此前一个节点的下一个指针设置为new_node,new_node-> prev-> next = new_node。在图例中,就是修改A节点的next指针,从C指向B。
    8、否则,如果new_node的prev为NULL,它将是新的头节点。因此,使(* head_ref)= new_node。

2.3 在DLL的末尾添加节点

    新节点总是添加到给定链表的最后一个节点之后。例如,如果给定的DLL是510152025,而我们在末尾添加了第30个项目,则DLL变为51015202530。
在这里插入图片描述
    共7步:

/* Given a reference (pointer to pointer) to the head 
   of a DLL and an int, appends a new node at the end  */
void append(struct Node** head_ref, int new_data) 
{ 
    /* 1. allocate node */
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); 
  
    struct Node* last = *head_ref; /* used in step 5*/
  
    /* 2. put in the data  */
    new_node->data = new_data; 
  
    /* 3. This new node is going to be the last node, so 
          make next of it as NULL*/
    new_node->next = NULL; 
  
    /* 4. If the Linked List is empty, then make the new 
          node as head */
    if (*head_ref == NULL) { 
        new_node->prev = NULL; 
        *head_ref = new_node; 
        return; 
    } 
  
    /* 5. Else traverse till the last node */
    while (last->next != NULL) 
        last = last->next; 
  
    /* 6. Change the next of last node */
    last->next = new_node; 
  
    /* 7. Make last node as previous of new node */
    new_node->prev = last; 
  
    return; 
} 

    因为链表通常是由它的head来表示的,所以我们必须遍历列表直到结束,然后将最后一个节点的下一个更改为新节点。第1步创建的last指针用于遍历链表,指向原链表的最后一个节点;第6步修改原链表最后一个节点的next指针,将指向NULL修改为指向新节点;第7步配置新节点的prev指针,为原链表的最后节点。

2.4 在给定节点之后添加节点

在这里插入图片描述
    共7步:

/* Given a node as prev_node, insert a new node after the given node */
void insertAfter(struct Node* prev_node, int new_data) 
{ 
    /*1. check if the given prev_node is NULL */
    if (prev_node == NULL) { 
        printf("the given previous node cannot be NULL"); 
        return; 
    } 
  
    /* 2. allocate new node */
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); 
  
    /* 3. put in the data  */
    new_node->data = new_data; 
  
    /* 4. Make next of new node as next of prev_node */
    new_node->next = prev_node->next; 
  
    /* 5. Make the next of prev_node as new_node */
    prev_node->next = new_node; 
  
    /* 6. Make prev_node as previous of new_node */
    new_node->prev = prev_node; 
  
    /* 7. Change previous of new_node's next node */
    if (new_node->next != NULL) 
        new_node->next->prev = new_node; 
} 

    在图例中,new_node为E节点,prev_node为B节点。第4步将E节点的next指针指向C节点;第5步将B节点的next指针指向从C节点改为E节点;第6步将E节点prev指针指向B节点;第7步将C节点prev指针指向从B节点改为E节点。

3. 综合示例

    以下是上述方法的实现:

// A complete working C program to demonstrate all  
// insertion before a given node  
#include <stdio.h>  
#include <stdlib.h>  
  
// A linked list node  
struct Node {  
    int data;  
    struct Node* next;  
    struct Node* prev;  
};  
  
/* Given a reference (pointer to pointer) to the head of a list  
and an int, inserts a new node on the front of the list. */
void push(struct Node** head_ref, int new_data)  
{  
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));  
  
    new_node->data = new_data;  
  
    new_node->next = (*head_ref);  
    new_node->prev = NULL;  
  
    if ((*head_ref) != NULL)  
        (*head_ref)->prev = new_node;  
  
    (*head_ref) = new_node;  
}  
  
/* Given a node as next_node, insert a new node before the given node */
void insertBefore(struct Node** head_ref, struct Node* next_node, int new_data)  
{  
    /*1. check if the given next_node is NULL */
    if (next_node == NULL) {  
        printf("the given next node cannot be NULL");  
        return;  
    }  
  
    /* 2. allocate new node */
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));  
  
    /* 3. put in the data */
    new_node->data = new_data;  
  
    /* 4. Make prev of new node as prev of next_node */
    new_node->prev = next_node->prev;  
  
    /* 5. Make the prev of next_node as new_node */
    next_node->prev = new_node;  
  
    /* 6. Make next_node as next of new_node */
    new_node->next = next_node;  
  
    /* 7. Change next of new_node's previous node */
    if (new_node->prev != NULL)  
        new_node->prev->next = new_node;  
    /* 8. If the prev of new_node is NULL, it will be 
       the new head node */
    else
        (*head_ref) = new_node; 
      
}  
  
// This function prints contents of linked list starting from the given node  
void printList(struct Node* node)  
{  
    struct Node* last;  
    printf("\nTraversal in forward direction \n");  
    while (node != NULL) {  
        printf(" %d ", node->data);  
        last = node;  
        node = node->next;  
    }  
  
    printf("\nTraversal in reverse direction \n");  
    while (last != NULL) {  
        printf(" %d ", last->data);  
        last = last->prev;  
    }  
}  
  
/* Driver program to test above functions*/
int main()  
{  
    /* Start with the empty list */
    struct Node* head = NULL;  
    push(&head, 7);  
  
    push(&head, 1);  
  
    push(&head, 4);  
  
    // Insert 8, before 1. So linked list becomes 4->8->1->7->NULL  
    insertBefore(&head, head->next, 8);  
  
    printf("Created DLL is: ");  
    printList(head);  
  
    getchar();  
    return 0;  
}  

    输出结果为:

Created DLL is: 
Traversal in forward direction 
 4  8  1  7 
Traversal in reverse direction 
 7  1  8  4
参考文献

[1] jatinreaper, 29AjayKumar, Murali Krishna Marimekala, rathbhupendra, Akanksha_Rai.Doubly Linked List | Set 1 (Introduction and Insertion)[EB/OL].https://www.geeksforgeeks.org/doubly-linked-list/,2020-01-01.

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值