Xóa node trong DSLK đôi
Trong bài viết này, codehow sẽ hướng dẫn các bạn cách xóa node trong DSLK đôi. Đây là một trong các thao tác cơ bản khi sử dụng danh sách liên kết đôi, vì vậy hãy tìm hiểu nó thật kĩ nhé.
Tương tự như việc thêm node vào DSLK đôi ở bài trước mình đã hướng dẫn. Việc xóa node trong DSLK đôi cũng có hai cách xóa đó là xóa node đầu tiên và xóa node cuối cùng của danh sách.
Vậy làm thế nào để thực hiện chương trình xóa node trong C / C++ thì hãy bắt đầu cùng mình ngay thôi nhé.
Xóa node đầu trong DSLK đôi
Như các bạn đã biết thì trong DSLK đôi node head luôn luôn quản lý node đầu tiên của danh sách. Vậy nếu chúng ta muốn xóa node đầu thì có nghĩa là xóa node head, như vậy thì sẽ không còn node nào quản lý node đầu nữa.
Vì vậy để node head vẫn tồn tại và quản lý node đầu tiên của danh sách, chúng ta cần phải chuyển node head đến node tiếp theo. Sau khi xóa node đầu tiên thì node thứ hai chính là node head.
head = head -> next
Thêm một điều nữa là node head sẽ có con trỏ prev trỏ về NULL, vậy nên sau khi xóa chúng ta cần cập nhật lại con trỏ prev của head trỏ về NULL nữa nhé.
head -> prev = NULL
Lưu ý rằng chúng ta cần kiểm tra xem danh sách có rỗng không nhé, nếu rỗng thì chúng ta sẽ không thực hiện xóa node mà chỉ thông báo cho người dùng.
Hàm xóa node đầu trong DSLK đôi.
/* Xóa node đầu trong danh sách */ void DelFirst() { //Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; } //ngược lại nếu danh sách có phần tử thì head = head->next;//thực hiện dịch chuyển node head đến node kế tiếp head->prev = NULL;//sau đó cho con trỏ prev của head = null }
Xóa node cuối trong DSLK đôi
Đối với trường hợp chúng ta muốn xóa node cuối cũng sẽ thực hiện tương tự như việc xóa node đầu.
Để đảm bảo rằng trong danh sách đã tồn tại phần tử, chúng ta cần kiểm tra xem danh sách có rỗng không nhé. Nếu rỗng thì chỉ thông báo cho người dùng rồi thoát khỏi hàm. Trường hợp danh sách đã tồn tại phần tử thì chúng ta mới thực hiện xóa node cuối.
//Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; }
Để xóa node cuối thì việc đầu tiên ta cần dịch chuyển node tail về node trước đó đã nhé. Bởi vì node tail luôn quản lý node cuối, nếu chúng ta không dịch chuyển mà xóa luôn thì sẽ không còn node tail để quản lý ndoe cuối nữa.
tail = tail -> prev
Sau khi dịch chuyển ta cần thiết lập lại mối liên kết của con trỏ next == NULL, vì con trỏ next của node tail luôn luôn trỏ về NULL nhé.
tail->next = NULL;
Hàm xóa node cuối trong DSLK đôi.
/* Xóa node cuối trong danh sách */ void DelLast() { //Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; } //Ngược lại nếu danh sách có phần tử thì tail = tail->prev;//thực hiện dịch chuyển tail về node trước đó tail->next = NULL;//sau đó cho con trỏ next của tail == null }
Ví dụ xóa node trong DSLK đôi
Trong phần này mình sẽ thực hiện một chương trình để xóa node trong DSLK đôi. Chương trình sẽ sử dụng dữ liệu từ các bài trước, điều này giúp các bạn vừa ôn lại kiến thức cũ và vừa áp dụng kiến thức mới. Cụ thể như sau:
- Xây dụng cấu trúc dữ liệu của DSLK đôi.
- Tạo mới node.
- Chèn node vào đầu, vào cuối dánh sách.
- Duyệt danh sách từ đầu về cuối và từ cuối về đầu.
- Xóa node đầu và xóa node cuối trong DSLK đôi.
Bây giờ chúng ta sẽ bắt đầu thực hiện từng yêu cầu mà đề bài đưa ra nhé.
Bước 1: Xây dụng cấu trúc dữ liệu của DSLK đôi.
/* tạo cấu trúc node */ 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.
Bước 2: Tạo mới node.
/* tạo node mới */ struct Node* CreateNode(int x) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = x; newNode->prev = NULL; newNode->next = NULL; return newNode; }
Bước 3: Chèn node vào đầu, vào cuối danh sách.
/* thêm node vào đầu danh sách */ void InsertFirst(int x) { //sử dụng hàm tạo Node tạo một Node mới struct Node* newNode = CreateNode(x); //nếu danh sách rỗng thì node chèn vào vừa là node đầu vừa là node cuối if(head == NULL) { head = newNode; tail = newNode; return; } //Nếu danh sách không rỗng thì, dịch chuyển Node head về node mới chèn, và cho con trỏ của newNode trỏ đến Node head head->prev = newNode; newNode->next = head; head = newNode; } /* thêm node vào cuối danh sách */ void InsertLast(int x) { //sử dụng hàm tạo Node để tạo node mới newNode struct Node* newNode = CreateNode(x); //Nếu danh sách rỗng thì newNode vừa là node đầu vừa là node cuối if(head == NULL) { head = newNode; tail = newNode; return; } //Nếu danh sách có phần tử thì, tail->next = newNode;// con trỏ next của tail trỏ đến newNode newNode->prev = tail;// con trỏ prev của newNode trỏ đến tail tail = newNode;//dịch chuyển tail về newNode, vì tail luôn luôn quản lý phần tử cuối cùng trong danh sách }
Bước 4: Duyệt danh sách từ đầu về cuối và từ cuối về đầu.
/* hiển thị từ đầu đến cuối */ void Print() { struct Node* temp = head; printf("\nĐầu đến cuối: "); while(temp != NULL) { printf("%d ",temp->data); temp = temp->next; } printf("\n"); } /* hiển thị từ cuối về đầu */ void ReversePrint() { struct Node* temp = tail; if(temp == NULL) return; // empty list, exit // Traversing backward using prev pointer printf("\nCuối về đầu: "); while(temp != NULL) { printf("%d ",temp->data); temp = temp->prev; } printf("\n"); }
Bước 5: Xóa node đầu và xóa node cuối trong DSLK đôi.
/* Xóa node đầu trong danh sách */ void DelFirst() { //Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; } //ngược lại nếu danh sách có phần tử thì head = head->next;//thực hiện dịch chuyển node head đến node kế tiếp head->prev = NULL;//sau đó cho con trỏ prev của head = null } /* Xóa node cuối trong danh sách */ void DelLast() { //Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; } //Ngược lại nếu danh sách có phần tử thì tail = tail->prev;//thực hiện dịch chuyển tail về node trước đó tail->next = NULL;//sau đó cho con trỏ next của tail == null }
Dưới đây là chương trình mình đã viết sẵn bằng ngôn ngữ C++, các bạn có thể tham khảo nhé.
Full code:
#include <iostream> using namespace std; /* tạo cấu trúc node */ 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. /* tạo node mới */ struct Node* CreateNode(int x) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = x; newNode->prev = NULL; newNode->next = NULL; return newNode; } /* hiển thị từ đầu đến cuối */ void Print() { struct Node* temp = head; printf("\nĐầu đến cuối: "); while(temp != NULL) { printf("%d ",temp->data); temp = temp->next; } printf("\n"); } /* hiển thị từ cuối về đầu */ void ReversePrint() { struct Node* temp = tail; if(temp == NULL) return; // empty list, exit // Traversing backward using prev pointer printf("\nCuối về đầu: "); while(temp != NULL) { printf("%d ",temp->data); temp = temp->prev; } printf("\n"); } /* thêm node vào đầu danh sách */ void InsertFirst(int x) { //sử dụng hàm tạo Node tạo một Node mới struct Node* newNode = CreateNode(x); //nếu danh sách rỗng thì node chèn vào vừa là node đầu vừa là node cuối if(head == NULL) { head = newNode; tail = newNode; return; } //Nếu danh sách không rỗng thì, dịch chuyển Node head về node mới chèn, và cho con trỏ của newNode trỏ đến Node head head->prev = newNode; newNode->next = head; head = newNode; } /* thêm node vào cuối danh sách */ void InsertLast(int x) { //sử dụng hàm tạo Node để tạo node mới newNode struct Node* newNode = CreateNode(x); //Nếu danh sách rỗng thì newNode vừa là node đầu vừa là node cuối if(head == NULL) { head = newNode; tail = newNode; return; } //Nếu danh sách có phần tử thì, tail->next = newNode;// con trỏ next của tail trỏ đến newNode newNode->prev = tail;// con trỏ prev của newNode trỏ đến tail tail = newNode;//dịch chuyển tail về newNode, vì tail luôn luôn quản lý phần tử cuối cùng trong danh sách } /* Xóa node đầu trong danh sách */ void DelFirst() { //Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; } //ngược lại nếu danh sách có phần tử thì head = head->next;//thực hiện dịch chuyển node head đến node kế tiếp head->prev = NULL;//sau đó cho con trỏ prev của head = null } /* Xóa node cuối trong danh sách */ void DelLast() { //Nếu danh sách rỗng thì return và thoát khỏi hàm if(head == NULL) { return; } //Ngược lại nếu danh sách có phần tử thì tail = tail->prev;//thực hiện dịch chuyển tail về node trước đó tail->next = NULL;//sau đó cho con trỏ next của tail == null } int main() { head = NULL; //gọi hàm thêm node vào đầu danh sách InsertFirst(2); InsertFirst(3); InsertFirst(4); cout<<"Danh sách sau khi thêm node vào đầu: \n"; Print(); ReversePrint(); cout<<"\n----------end--------------\n"; //gọi hàm thêm node vào cuối danh sách InsertLast(5); InsertLast(6); InsertLast(7); cout<<"Danh sách sau khi thêm node vào cuối: \n"; Print(); ReversePrint(); cout<<"\n----------end--------------\n"; //gọi hàm xóa đầu danh sách DelFirst(); cout<<"Danh sách sau khi xóa đầu : \n"; Print(); ReversePrint(); cout<<"\n----------end--------------\n"; //gọi hàm xóa cuối danh sách DelLast(); cout<<"Danh sách sau khi xóa cuối : \n"; Print(); ReversePrint(); cout<<"\n---------------------------------------\n"; cout<<"Chương trình này được đăng tại codehow.net"; }
Như vậy là chúng ta đã cùng nhau tìm hiểu về cách xóa node trong DSLK đôi. Ở bài tiếp theo, mình sẽ hướng dẫn các bạn cách tìm kiềm phần tử k trong danh sách liên kết đôi. Các bạn chú ý theo dõi nhé, cảm ơn các bạn rất nhiều !!!!