Xóa 1 Node trong danh sách liên kết đôi

Danh sách liên kết đôi (Doubly Linked List) là một biến thể của Danh sách liên kết (Linked List), trong đó hoạt động duyệt qua các nút có thể được thực hiện theo hai chiều: về trước và về sau một cách dễ dàng khi so sánh với Danh sách liên kết đơn. Dưới đây là một số khái niệm quan trọng cần ghi nhớ về Danh sách liên kết đôi.

  • Link: mỗi link của một Danh sách liên kết có thể lưu giữ một dữ liệu và được gọi là một phần tử.

  • Next: mỗi link của một Danh sách liên kết có thể chứa một link tới next link và được gọi là Next.

  • Prev: mỗi link của một Danh sách liên kết có thể chứa một link tới previous link và được gọi là Prev.

  • First và Last: một Danh sách liên kết chứa link kết nối tới first link được gọi là First và tới last link được gọi là Last.

Biểu diễn Danh sách liên kết đôi

Xóa 1 Node trong danh sách liên kết đôi

Như hình minh họa trên, bạn cần ghi nhớ:

  • Danh sách liên kết đôi chứa một phần tử link và được gọi là First và Last.
  • Mỗi link mang một trường dữ liệu và một trường link được gọi là Next.
  • Mỗi link được liên kết với phần tử kế tiếp bởi sử dụng Next Link.
  • Mỗi link được liên kết với phần tử phía trước bởi sử dụng Prev Link.
  • Last Link mang một link trỏ tới NULL để đánh dầu phần cuối của Danh sách liên kết.

Các hoạt động cơ bản trên Danh sách liên kết đôi

  • Hoạt động chèn: thêm một phần tử vào vị trí đầu của Danh sách liên kết.

  • Hoạt động xóa: xóa một phần tử tại vị trí đầu của Danh sách liên kết.

  • Hoạt động chèn vào cuối: thêm một phần tử vào vị trí cuối của Danh sách liên kết.

  • Hoạt động xóa phần tử cuối: xóa một phần tử tại vị trí cuối của Danh sách liên kết.

  • Hoạt động chèn vào sau: thêm một phần tử vào sau một phần tử của Danh sách liên kết.

  • Hoạt động xóa (bởi key): xóa một phần tử từ Danh sách liên kết bởi sử dụng khóa đã cung cấp.

  • Hiển thị danh sách về phía trước: hiển thị toàn bộ Danh sách liên kết theo chiều về phía trước.

  • Hiển thị danh sách về phía sau: hiển thị toàn bộ Danh sách liên kết theo chiều về phía sau. 

Hoạt động chèn trong Danh sách liên kết đôi

Phần dưới đây là giải thuật minh họa cho hoạt động chèn tại vị trí đầu của một Danh sách liên kết đôi.

//Chèn link tại vị trí đầu tiên void insertFirst(int key, int data) { //tạo một link struct node *link = (struct node*) malloc(sizeof(struct node)); link->key = key; link->data = data; if(isEmpty()) { //Biến nó thành last link last = link; }else { //Cập nhật prev link đầu tiên head->prev = link; } //Trỏ nó tới first link cũ link->next = head; //Trỏ first tới first link mới head = link; }

 Chương trình danh sách liên kết đôi trong C 

Hoạt động xóa trong Danh sách liên kết đôi

Phần dưới đây là giải thuật minh họa cho hoạt động xóa phần tử tại vị trí đầu của một Danh sách liên kết đôi.
 

//xóa phần tử đầu tiên struct node* deleteFirst() { //Lưu tham chiếu tới first link struct node *tempLink = head; //Nếu chỉ có link if(head->next == NULL) { last = NULL; }else { head->next->prev = NULL; } head = head->next; //Trả về link đã bị xóa return tempLink; }

 Chương trình danh sách liên kết đôi trong C 

Hoạt động chèn tại vị trí cuối trong Danh sách liên kết đôi

Phần dưới đây là giải thuật minh họa cho hoạt động chèn tại vị trí cuối của một Danh sách liên kết đôi.

//Chèn link vào vị trí cuối cùng void insertLast(int key, int data) { //tạo một link struct node *link = (struct node*) malloc(sizeof(struct node)); link->key = key; link->data = data; if(isEmpty()) { //biến nó thành last link last = link; }else { //làm cho link trở thành last link mới last->next = link; //Đánh dấu last node là prev của new link link->prev = last; } //Trỏ last tới new last node last = link; }

 Chương trình danh sách liên kết đôi trong C 

Bài tập C này giúp bạn làm quen dần với cách tạo danh sách liên kết đôi và cách xóa một phần tử từ danh sách liên kết đôi trong C. Để giải bài tập này, mình sử dụng cấu trúc struct trong C.

#include #include struct node { int data; struct node *prev; struct node *next; }; struct node *head = NULL; struct node *last = NULL; struct node *current = NULL; //hien thi list void printList() { struct node *ptr = head; printf("\n[head] <=>"); //bat dau tu phan dau cua list while(ptr != NULL) { printf(" %d <=>",ptr->data); ptr = ptr->next; } printf(" [last]\n"); } void print_backward() { struct node *ptr = last; printf("\n[last] <=>"); //bat dau tu phan dau cua list while(ptr != NULL) { printf(" %d <=>",ptr->data); ptr = ptr->prev; } printf(" [head]\n"); } //tao danh sach lien ket void insert(int data) { // cap phat bo nho cho node moi; struct node *link = (struct node*) malloc(sizeof(struct node)); link->data = data; link->prev = NULL; link->next = NULL; // neu head la trong, tao list moi if(head==NULL) { head = link; return; } current = head; // di chuyen toi phan cuoi list while(current->next!=NULL) current = current->next; // chen link vao phan cuoi cua list current->next = link; last = link; link->prev = current; } void remove_data(int data) { int pos = 0; struct node *pre_node; if(head==NULL) { printf("Danh sach lien ket chua duoc khoi tao"); return; } if(head->data == data) { if(head->next != NULL) { head->next->prev = NULL; head = head->next; return; }else { head = NULL; printf("Bay gio List la trong"); return; } }else if(head->data != data && head->next == NULL) { printf("Khong tim thay %d trong list\n", data); return; } current = head; while(current->next != NULL && current->data != data) { pre_node = current; current = current->next; } if(current->data == data) { pre_node->next = pre_node->next->next; if(pre_node->next != NULL) { pre_node->next->prev = pre_node; }else last = pre_node; free(current); }else printf("Khong tim thay %d trong list.", data); } int main() { insert(10); insert(20); insert(30); insert(1); insert(40); insert(56); printList(); remove_data(20); printList(); print_backward(); return 0; }

Đã có app VietJack trên điện thoại, giải bài tập SGK, SBT Soạn văn, Văn mẫu, Thi online, Bài giảng....miễn phí. Tải ngay ứng dụng trên Android và iOS.

Xóa 1 Node trong danh sách liên kết đôi

Xóa 1 Node trong danh sách liên kết đôi

Theo dõi chúng tôi miễn phí trên mạng xã hội facebook và youtube:

Các bạn có thể mua thêm khóa học JAVA CORE ONLINE VÀ ỨNG DỤNG cực hay, giúp các bạn vượt qua các dự án trên trường và đi thực tập Java. Khóa học có giá chỉ 300K, nhằm ưu đãi, tạo điều kiện cho sinh viên cho thể mua khóa học.

Nội dung khóa học gồm 16 chuơng và 100 video cực hay, học trực tiếp tại https://www.udemy.com/tu-tin-di-lam-voi-kien-thuc-ve-java-core-toan-tap/ Bạn nào có nhu cầu mua, inbox trực tiếp a Tuyền, cựu sinh viên Bách Khoa K53, fb: https://www.facebook.com/tuyen.vietjack

Follow facebook cá nhân Nguyễn Thanh Tuyền https://www.facebook.com/tuyen.vietjack để tiếp tục theo dõi các loạt bài mới nhất về Java,C,C++,Javascript,HTML,Python,Database,Mobile.... mới nhất của chúng tôi.

danh-sach-lien-ket-trong-c.jsp

This entry is part 5 of 15 in the series Cấu trúc dữ liệu

Ở bài viết trước, tôi đã hướng dẫn bạn cách cài đặt danh sách liên kết đơn và các kiến thức về danh sách liên kết. Bài viết này, mình sẽ hướng dẫn bạn cài đặt danh sách liên kết đôi nhé. Danh sách liên kết đôi có sự liên kết 2 chiều giữa 2 phần tử liền kề nhau trong khi danh sách liên kết đơn chỉ có liên kết 1 chiều.

1. Lý thuyết về Danh sách liên kết đôi

Một Node của danh sách liên kết đôi sẽ có dạng như sau:

    ---------------------------------------------

    |              |             |              |

    |     PREV     |     DATA    |    NEXT      |

    |              |             |              |

    ---------------------------------------------

Và hình dưới đây so sanh sự khác nhau giữ dslk đôi và dslk đơn:

Xóa 1 Node trong danh sách liên kết đôi
Khác biệt giữa danh sách liên kết đơn với danh sách liên kết đôi

2. Cài đặt danh sách liên kết đôi

2.1. Khai báo & khởi tạo

Khác một chút với dslk đơn, ngoài con trỏ next, chúng ta sẽ có thêm con trỏ prev để liên kết với Node trước nó. Chúng ta vẫn sẽ để data có kiểu int để đơn giản và dễ hiểu nhé.

Ngay sau khai báo, chúng ta sẽ khởi tạo Node đầu tiên của danh sách liên kết đôi.

struct Node  {

    int data;

    struct Node* next;

    struct Node* prev;

};

struct Node* head; // Khởi tạo Node head global của dslk đôi.

2.2. Tạo mới 1 Node

Thay vì chỉ cho next = NULL và gán giá trị cho Node mới, chúng ta cũng phải cho con trỏ prev = NULL.

struct Node* GetNewNode(int x) {

    struct Node* newNode

        = (struct Node*)malloc(sizeof(struct Node));

    newNode->data = x;

    newNode->prev = NULL;

    newNode->next = NULL;

    return newNode;

}

2.3. Thêm Node

Thêm vào đầu

  • Nếu head = NULL, ta sẽ cho cả head và tail = newNode.
  • Nếu head != NULL, ta sẽ cập nhật lại head mới là newNode. Ta cần tạo liên kết giữa head hiện tại với newNode trước khi cho newNode làm head mới.

void InsertAtHead(int x) {

    struct Node* newNode = GetNewNode(x);

    if(head == NULL) {

        head = newNode;

        tail = newNode;

        return;

    }

    head->prev = newNode;

    newNode->next = head;

    head = newNode;

}

Về cơ bản, ý tưởng thêm Node vào đầu/ cuối vẫn giống với thêm vào đầu trong danh sách liên kết đơn.

Thêm vào cuối

  • Nếu head = NULL, newNode sẽ là head và tail luôn
  • Nếu head != NULL, cập nhật lại tail mới là newNode. Ta cần tạo liên kết thằng tail hiện tại với newNode trước khi để newNode làm tail mới.

void InsertAtTail(int x) {

    struct Node* newNode = GetNewNode(x);

    if(head == NULL) {

        head = newNode;

        tail = newNode;

        return;

    }

    tail->next = newNode;

    newNode->prev = tail;

    tail = newNode;

}

2.4. Xóa Node

Xóa ở đầu

  • Nếu head = NULL, chẳng có gì để xóa
  • Nếu head != NULL, cho head mới bằng phần tử tiếp theo và sửa prev của nó = NULL(ngắt liên kết với thằng head cũ).

void DeleteAtHead() {

    if(head == NULL) {

        return;

    }

    head = head->next;

    head->prev = NULL;

}

Xóa ở cuối

  • Nếu head = NULL, chẳng có gì để xóa
  • Nếu head != NULL, cho tail mới bằng phần tử trước nó và sửa next của nó = NULL(ngắt liên kết với thằng tail cũ).

void DeleteAtTail() {

    if(head == NULL) {

        return;

    }

    tail = tail->prev;

    tail->next = NULL;

}

2.5. Duyệt danh sách liên kết

Duyệt từ đầu tới cuối

Ta sẽ duyệt bắt đầu từ Node head cho tới trước khi gặp Node NULL bằng cách dùng con trỏ next.

void Print() {

    struct Node* temp = head;

    printf("\nForward: ");

    while(temp != NULL) {

        printf("%d ",temp->data);

        temp = temp->next;

    }

    printf("\n");

}

Duyệt từ cuối về đầu

Lần này, ta sẽ duyệt bắt đầu từ Node tailcho tới trước khi gặp Node NULL bằng cách dùng con trỏ prev.

void ReversePrint() {

    struct Node* temp = tail;

    if(temp == NULL) return; // empty list, exit

    // Traversing backward using prev pointer

    printf("\nReverse: ");

    while(temp != NULL) {

        printf("%d ",temp->data);

        temp = temp->prev;

    }

    printf("\n");

}

3. Full code danh sách liên kết đôi

0

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

/* Doubly Linked List implementation */

#include

#include

struct Node  {

    int data;

    struct Node* next;

    struct Node* prev;

};

struct Node *head, *tail; // Khởi tạo Node head global của dslk đôi.

//Creates a new Node and returns pointer to it.

struct Node* GetNewNode(int x) {

    struct Node* newNode

        = (struct Node*)malloc(sizeof(struct Node));

    newNode->data = x;

    newNode->prev = NULL;

    newNode->next = NULL;

    return newNode;

}

//Inserts a Node at head of doubly linked list

void InsertAtHead(int x) {

    struct Node* newNode = GetNewNode(x);

    if(head == NULL) {

        head = newNode;

        tail = newNode;

        return;

    }

    head->prev = newNode;

    newNode->next = head;

    head = newNode;

}

//Inserts a Node at tail of Doubly linked list

void InsertAtTail(int x) {

    struct Node* newNode = GetNewNode(x);

    if(head == NULL) {

        head = newNode;

        tail = newNode;

        return;

    }

    tail->next = newNode;

    newNode->prev = tail;

    tail = newNode;

}

void DeleteAtHead() {

    if(head == NULL) {

        return;

    }

    head = head->next;

    head->prev = NULL;

}

//Inserts a Node at tail of Doubly linked list

void DeleteAtTail() {

    if(head == NULL) {

        return;

    }

    tail = tail->prev;

    tail->next = NULL;

}

//Prints all the elements in linked list in forward traversal order

void Print() {

    struct Node* temp = head;

    printf("\nForward: ");

    while(temp != NULL) {

        printf("%d ",temp->data);

        temp = temp->next;

    }

    printf("\n");

}

//Prints all elements in linked list in reverse traversal order.

void ReversePrint() {

    struct Node* temp = tail;

    if(temp == NULL) return; // empty list, exit

    // Traversing backward using prev pointer

    printf("\nReverse: ");

    while(temp != NULL) {

        printf("%d ",temp->data);

        temp = temp->prev;

    }

    printf("\n");

}

int main() {

    /*Driver code to test the implementation*/

    head = NULL; // empty list. set head as NULL.

    // Calling an Insert and printing list both in forward as well as reverse direction.

    InsertAtTail(2);

    Print(); ReversePrint();

    InsertAtTail(4);

    Print(); ReversePrint();

    InsertAtHead(6);

    Print(); ReversePrint();

    InsertAtTail(8);

    Print(); ReversePrint();

    DeleteAtHead();

    Print(); ReversePrint();

    DeleteAtTail();

    Print(); ReversePrint();

}

Kết quả chạy:

0

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

Forward: 2

Reverse: 2

Forward: 2 4

Reverse: 4 2

Forward: 6 2 4

Reverse: 4 2 6

Forward: 6 2 4 8

Reverse: 8 4 2 6

Forward: 2 4 8

Reverse: 8 4 2

Forward: 2 4

Reverse: 4 2

Process returned 0 (0x0)   execution time : 0.040 s

Press any key to continue.