Thứ Năm, 24 tháng 12, 2015

Chúng ta cùng tìm hiểu một cấu trúc dữ liệu cũng khá hữu ích là Danh sách liên kết vòng (Circular Linked List). Nó biểu diễn một cách tự nhiên các cấu trúc dạng tròn như các góc của đa giác, v.v... DSLK vòng có hai dạng thường thấy là dạng vòng đơnvà vòng kép.

Dạng vòng đơn thực chất là một danh sách liên kết đơn có phần tử cuối trỏ về phần tử đầu tiên. Nó cũng có nhược điểm là chỉ duyệt từ một chiều. Dạng vòng kép cũng là một danh sách liên kết kép có phần tử cuối trỏ về đầu và đầu trỏ ngược về cuối.

Với DSLK vòng ta cần biết một vài thao tác cơ bản đủ dùng và các thao tác này sẽ được minh họa bằng C++. Bài này chỉ nói về danh sách liên kết vòng kép và bạn cũng nên sử dụng vòng kép để việc code lại đơn giản hơn.

Tổ chức dữ liệu
Một danh sách gồm có các phần tử gọi là node, mỗi node gồm 1 biến chứa dữ liệu và một hoặc nhiều biến con trỏ để liên kết với các node khác. Dưới đây là khai báo cấu trúc node:

1
2
3
4
5
struct DoublyNode
{
   <datatype> info;
   DoublyNode* prev, *next;
};

Do cấu trúc này ở dạng vòng nên một danh sách ta chỉ cần chọn một phần tử đầu thôi.
1
2
3
4
struct DoublyList
{
   DoublyNode* head;
};


Các thao tác cơ bản
Tạo danh sách rỗng
Do đặc điểm của cách cài đặt hướng cấu trúc và dùng con trỏ trong C++ nên cần thiết phải tạo danh sách rỗng bằng cách gán NULL cho phần tử đầu.
1
2
3
4
void CreateList(DoublyList &list)
{
   list.head = NULL;
}

Đưa dữ liệu vào node
Đơn giản là đưa dữ liệu của bạn vào một node để có thể thêm vào danh sách.
1
2
3
4
5
6
7
8
9
10
DoublyNode* CreateNode(<datatype> data)
{
   DoublyNode* node = new DoublyNode;
   if (node)
   {
      node->info = data;
      node->next = node->prev = NULL;
   }
   return node;
}

Thêm node vào danh sách
Ở đây ta chỉ cần 2 trường hợp: thêm trước 1 node và thêm sau 1 node (mà thực ra cũng chỉ cần thêm trước 1 node là đủ dùng), nhưng mình cũng code thêm hàm thêm vào cuối (có thể hiểu như thêm trước phần tử đầu).
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
void AddTail(DoublyList &list, DoublyNode* node)
{
   if (!list.head)
   {
      list.head = node;
      node->next = node->prev = list.head;
   }
   else
   {
      node->prev = list.head->prev;
      node->next = list.head;
      list.head->prev->next = node;
      list.head->prev = node;
   }
}
void AddBefore(DoublyList &list, DoublyNode* node, DoublyNode* before)
{
   if (!before)
   {
      list.head = node;
      node->next = node->prev = list.head;
   }
   else
   {
      node->prev = before->prev;
      node->next = before;
      before->prev->next = node;
      before->prev = node;
   }
}
void AddAfter(DoublyList &list, DoublyNode* node, DoublyNode* after)
{
   if (!after)
   {
      list.head = node;
      node->next = node->prev = list.head;
   }
   else
   {
      node->prev = after;
      node->next = after->next;
      after->next->prev = node;
      after->next = node;
   }
}

Duyệt danh sách
Duyệt là đến từng phần tử để thực hiện thao tác nào đó. Trong DSLK vòng phải có một điều kiện dừng nào đó để dừng duyệt (nếu không nó cứ đi lòng vòng). Cái này mình không nói cụ thể ở đây mà tùy trường hợp cụ thể điều kiện dừng sẽ khác nhau.
1
2
3
4
5
6
7
void Browse(DoublyList list)
{
   for (DoublyNode* i = list.head; <condition>; i=i->next)
   {
      ///
   }
}

Xóa phần tử và danh sách
Ở đây ta chỉ quan tâm việc xóa một phần tử cụ thể và xóa danh sách.
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
void RemoveKey(DoublyList &list, int key)
{
   DoublyNode *i = list.head;
   do
   {
      if (i->info == key)
      {
         i->prev = i->next;
         i->next = i->prev;
         if (i == list.head)
            list.head = NULL;
         delete i;
         break;
      }
      i = i->next;
   } while (i != list.head);
}
void RemoveList(DoublyList &list)
{
   DoublyNode *i = NULL;
   do
   {
      i = list.head;
      list.head->prev->next = list.head->next;
      list.head->next->prev = list.head->next;
      list.head = list.head->next;
      delete i;
      if (!i) list.head = NULL;
   } while (!list.head)
}

Các thao tác cơ bản này đã đủ dùng với danh sách liên kết vòng. Tuy nhiên các cài đặt trên chỉ là cài đặt mẫu, bạn cần phải cài đặt linh động hơn trong một số thao tác để giải quyết bài toán nhanh hơn.

Related Posts:

  • 1.9. Thao tác với các tệpCác chương trình chúng ta viết từ trước đến giờ vẫn còn khá cơ bản. Đấy là bình thường, các bạn vừa mới bắt đầu học C++ thôi mà. Chỉ cần thêm 1 chút cố gắng với rèn luyện thì sớm sẽ viết được các ứng dụng thật sự thôi.Những k… Read More
  • 1.11. Con trỏChúng ta đã bước đến bài học cuối cùng trong chương đầu tiên nói về các khái niệm cơ bản trong C++. Hãy chú ý, kiến thức trong bài học này tương đối khó hơn nếu so sánh với những gì đã học từ trước tới giờ.Chủ đề về con trỏ l… Read More
  • Những lỗi cơ bản trong lập trình CNhững lỗi cơ bản trong lập trình C… Read More
  • 1.10. [Thực hành] Từ khóa bí ẩnừ đầu của giáo trình này, các bạn đã được làm quen với khá nhiều khái niệm : trình biên dịch, IDE, các biến, các hàm, các lệnh điều kiện, vòng lặp. Các bạn đã được chỉ cho xem rất nhiều ví dụ xuyên suốt các bài học nhưng đã c… Read More
  • 1 số bài tập mẫuPhần làm game: 1.Need for Speed phiên bản Console1 số hàm hỗ trợ làm gameKỹ thuật phát sinh ngẫu nhiên: Ví dụ 1 ,Ví dụ 2, Chương trình oẳn tù tìTrò chơi từ khóa bí ẩnRắn săn mồi (util.h, Source.cpp)Bài toán lớp 3 gây sốt-----… Read More

0 nhận xét:

Đăng nhận xét

Categories

Sample Text

Được tạo bởi Blogger.

Must Read

Biểu mẫu liên hệ

Tên

Email *

Thông báo *

Popular Posts

Video

Popular Posts

Our Facebook Page