Entradas populares

Cây nhị phân tìm kiếm

Cây nhị phân gồm:
- Thành phần dữ liệu
- Hai địa chỉ trỏ đến hai node con của nó
Cây nhị phân tìm kiếm là cây nhị phân mà khóa của node con trái < khóa của node cha < khóa của node con phải và các khóa trong node không được trùng nhau.


Ta xây dựng cây nhị phân tìm kiếm có dữ liệu là kiểu int.
          1. Định nghĩa node và cây:
struct Node
{
int Info;
Node *pLeft, *pRight;
};

typedef Node* Tree;
          2. Tạo node: 
Node* GetNode(int x)
{
Node *p = new Node;
if(p)
{
p->Info = x;
p->pLeft = p->pRight = NULL;
}
return p;
}
          3. Khởi tạo cây:
void Init(Tree &tree)
{
tree = NULL;
}
          4. Chèn node vào cây:
bool InsertNode(Tree &tree, int x)
{
if(tree)
{
if(x == tree->Info)
return false;
if(x < tree->Info)
return InsertNode(tree->pLeft, x);
else
return InsertNode(tree->pRight, x);
}
tree = GetNode(x);
if(tree)
return true;
else
return false;
}
          5. Duyệt cây:
              Ta thường dùng 3 kiểu duyệt: LNR (Left - Node - Right), NLR (Node - Left - Right), LRN (Left - Right - Node).
void NLR(Tree tree)
{
if(tree)
{
cout<<tree->Info<<" "<<endl;
NLR(tree->pLeft);
NLR(tree->pRight);
}
}
Ví dụ: Cho cây nhị phân tìm kiếm với kiểu dữ liệu là kiểu char

Kết quả sau khi duyệt cây theo LRN, NLR, LNR là gì?
LRN: A C D B H K I T W S P M
NLR: M I B A D C K H P S T W
LNR: A B C D I H K M P T S W

          6. Tìm kiếm:
Node* Search(Tree tree, int x)
{
if(tree == NULL)
return NULL;
if(x == tree->Info)
return tree;
if(x < tree->Info)
return Search(tree->pLeft, x);
else
return Search(tree->pRight, x);
}
          7. Xóa node:
void SearchStandFor(Tree &p, Tree &q) // tìm node trái nhất của nhánh phải để thay thế cho p
{
if(q->pLeft)
SearchStandFor(p, q->pLeft);
else
{
p->Info = q->Info;
p = q;
q = q->pRight;
}
}

bool DelNode(Tree &tree, int x)
{
if(tree == NULL)
return false;
if(x < tree->Info)
return DelNode(tree->pLeft, x);
else if(x > tree->Info)
return DelNode(tree->pRight, x);
else
{
Node *p = tree;
if(tree->pLeft == NULL)           // node không có con trái
tree = tree->pRight;
else if(tree->pRight == NULL) // node không có con phải
tree = tree->pLeft;
else                                            // node lá
SearchStandFor(p, tree->pRight);
                delete p;                                              
return true;
}
}
Ví dụ: Muốn xóa node 4 thì ta sẽ lấy node 6 để thay thế vị trí cho nó sau khi xóa

          8. Tính chiều cao cây:
               Quy ước tại gốc thì chiều cao là 0.
int Max(int a, int b)
{
if(a > b)
return a;
return b;
}

int TinhChieuCao(Tree tree)
{
if(tree == NULL)
return -1;
if(tree->pLeft == NULL && tree->pRight == NULL)
return 0;
return 1 + Max(TinhChieuCao(tree->pLeft), TinhChieuCao(tree->pRight));
}
          9. Đếm số node có đủ cả hai con:
int DemNodeFull(Tree tree)
{
int count = 0;
if(tree == NULL)
return 0;
if(tree->pLeft != NULL && tree->pRight != NULL)
count++;
return count + DemNodeFull(tree->pLeft) + DemNodeFull(tree->pRight);
}
Tương tự như vậy, ta có thể đếm được tất cả các số node trong cây, số node lá, số node chỉ có một con,.. hay node thỏa theo điều kiện nào đó.

Danh sách liên kết đơn

Danh sách liên kết đơn gồm nhiều thành phần động, mỗi thành phần được tổ chức thành 1 node, gồm:
- Thông tin dữ liệu : int, float,... hoặc là kiểu struct do ta tự định nghĩa.
- Nơi chứa địa chỉ của thành phần kế tiếp trong danh sách.

Ví dụ:
struct Node                                           struct Node
{                                                           {
         int Info;                                                 SINHVIEN Info; // SINHVIEN là struct đã định nghĩa
         Node *pNext;                                        Node *pNext;
};                                                           }  

Danh sách liên kết đơn có dữ liệu là kiểu số nguyên
         1. Khởi tạo danh sách:
             Ta cần định nghĩa về Node và List.

typedef int Data                  // Đặt Data là tên gọi khác của kiểu int
struct Node
{
          Data Info;
          Node *pNext;
};
struct List
{
          Node *pHead, *pTail;

};
void Init(List &list)
{
          list.pHead = list.pTail = NULL;
}
          2. Các tác vụ:
Tạo node
Node* GetNode(Data x)
{
           Node *p = new Node;
           if(p)                            // ~ if(p != NULL)
           {
                   p->Info = x;
                   p->pNext = NULL;
            }
            return p;
}
Chèn đầu
void InsertHead(List &list, Data x)
{
            Node *p = GetNode(x);
            if(!p)                           // ~ if(p == NULL)
                    return;
            if(list.pHead == NULL)
                    list.pHead = list.pTail = p;
            else
            {
                    p->pNext = list.pHead;
                    list.pHead = p;
            }
}
Chèn đuôi
void InsertTail(List &list, Data x)
{
             Node *p = GetNode(x);
             if(!p)
                     return;
             if(list.pHead == NULL)
                     list.pHead = list.pTail = p;
             else
             {
                     list.pTail->pNext = p;
                     list.pTail = p;
              }
}
Chèn vào sau 1 node
void InsertAfterNode(List &list, Node *q, Data x)
{
              Node *p = GetNode(x);
              if(!p)
                      return;
              if(q == NULL)
                      InsertHead(list, x);
              else if(q == list.pTail)
                      InsertTail(list, x);
              else
              {
                      p->pNext = q->pNext;
                      q->pNext = q;
              }
}
Tìm kiếm
Node* Search(List &list, Data x)
{
              Node *p = list.pHead;
              while(p)
              {
                       if(p->Info == x)
                                 return p;
                       else
                                 p = p->pNext;
               }
               return NULL; 
}
Xóa node
void DelNode(List &list, Data x)
{
               Node *p = list.pHead, *q = NULL;
               if(p->Info == x)
               {
                       list.pHead = p->pNext;
               }
               else
               {
                       q = p;
                       p =p->pNext;
                       while(p)
                       {
                                  if(p->Info == x)
                                  {
                                             q->pNext = p->pNext;
                                             break;
                                   }
                                   else
                                   {
                                              q = p;
                                              p = p->pNext;
                                    }
                         }
                 if(p == list.pTail)
                 list.pTail = q;
                 }
                 delete p;
}
Sắp xếp
Ta có thể sắp xếp theo nhiều cách khác nhau theo các thuật toán sắp xếp đã biết. Nhưng một trong những cách hiệu quả nhất là QuickSort.

void QuickSort(List &list)
{
             if(list.pHead == list.pTail)
                        return;
             List a, b;
             Node *p = list.pHead, *q = NULL;
             Init(a);
             Init(b);
             list.pHead = p->pNext;
             p->pNext = NULL;
             q = list.pHead;
             while(q)
             {
                          list.pHead = q->pNext;
                          q->pNext = NULL;
                          if(q->Info < p->Info)
                                     InsertTail(a, q->Info);
                          else
                                     InsertTail(b, q->Info);
                          q = list.pHead;
             }
QuickSort(a);
QuickSort(b);
if(a.pHead)
{
              list.pHead = a.pHead;
              a.pTail = p;
}
else
              list.pHead = p;
p->pNext = b.pHead;
if(b.pHead)
              list.pTail = b.pTail;
else
              list.pTail = p;
}

Thuật toán sắp xếp

Các thuật toán chúng ta sẽ đề cập đến:

  •  Interchange Sort
  •  Selection Sort
  •  Insertion Sort
  •  Binary Insertion Sort
  •  Bubble Sort
  •  Shaker Sort
  •  Shell Sort
  •  Quick Sort
  •  Merge Sort
  •  Heap Sort
  •  Radix Sort
Tôi chỉ cài đặt mảng số nguyên theo sắp xếp tăng dần, còn các kiểu mảng dữ liệu khác hay sắp xếp giảm dần, bạn hãy tự vận dụng nha!

      1. Interchange Sort:
          Đây là thuật toán rất quen thuộc khi chúng ta mới làm quen với lập trình.
          Ý tưởng: Lấy một phần tử làm mốc, duyệt tất cả các phần tử đứng sau nó, phần tử nào có giá trị                          nhỏ hơn giá trị mốc thì đổi chỗ
       
 void InterchageSort(int *a, int n)
{
int i, j, t;
for(i = 0; i < n - 1; i++)
{
for(j = i + 1; j < n; j++)
{
if(a[i] > a[j])
{
t = a[i]; a[i] = a[j]; a[j] = t;
}
}
}
}
      2. Selection Sort:
          Ý tưởng: Lấy một phần tử làm mốc, tìm phần tử có giá trị nhỏ nhất (nhỏ hơn giá trị mốc) đứng                          sau nó, rồi đổi chỗ.

void SelectionSort(int *a, int n)
{
int i, j, min, t;
for(i = 0; i < n - 1; i++)
{
min = i;
for(j = i + 1; j < n; j++)
if(a[j] < a[min])
min = j;
t = a[min]; a[min] = a[i]; a[i] = t;
}
}

       3. Insertion Sort:
           Ý tưởng: Duyệt các phần tử từ vị trí thứ 1 đến n - 1, nếu giá trị  tại phần tử i nhỏ hơn giá trị                                  phần tử i - 1 thì tiến hành chèn (giá trị của i lấy giá trị của i - 1).

void InsertionSort(int *a, int n)
{
int i, j, x;
for(i = 1; i < n; i++)
{
x = a[i];
for(j = i - 1; j >= 0  && a[j] > x; j--)
a[j + 1] = a[j];
a[j + 1] = x;
}
}
          4. Binary Insertion Sort:
              Ý tưởng: Giống như Insertion Sort nhưng việc chèn không còn tuyến tính nữa mà là nhị                                       phân (thời gian sắp xếp sẽ nhanh hơn).

void BinaryInsertionSort(int *a, int n)
{
int i, j, x, left, right, mid;
for(i = 1; i < n; i++)
{
x = a[i];
left = 0;
right = i - 1;
while(left <= right)
{
mid = (left + right) / 2;
if(x > a[mid])
left = mid + 1;
else
right = mid - 1;
}
for(j = i - 1; j > left; j--)
a[j + 1] = a[j];
a[j + 1] = x;
}
}

            5. Bubble Sort:
                Ý tưởng: Đẩy phần tử lớn nhất của mảng hiện hành về vị trí cuối.

void BubbleSort(int *a, int n)
{
bool check = true;
int i, j, t;
for(i = 0; i < n - 1 && check == true; i++)
{
check = false;
for(j = 0; j < n - 1 - i; j++)
if(a[j] > a[j + 1])
{
t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
check = true;
}
}
}

             6. Shaker Sort:
                 Ý tưởng: Giống như Bubble Sort nhưng thay vì chỉ duyệt từ một đầu, nay ta duyệt từ cả                                       hai đầu.

void ShakerSort(int *a, int n)
{
bool check = true;
int i, j, k, t;
for(k = 0; k < n / 2 && check == true; k++)
{
i = k;
j = n - 1 - k;
check = false;
while(j > k)
{
if(a[i] > a[i + 1])
{
t = a[i]; a[i] = a[i + 1]; a[i + 1] = t;
check = true;
}
if(a[j] < a[j - 1])
{
t = a[j]; a[j] = a[j - 1]; a[j - 1] = t;
check = true;
}
i++;
j--;
}
}
}
             7. Shell Sort:
                  Ý tưởng: Giống như Insertion Sort nhưng thay vì chèn tuyến tính từng phần tử một, ta                                         chọn phần tử chèn theo số bước (step).
                   Hiệu quả của thuật toán này phụ thuộc nhiều vào việc chọn step, đó có thể là                                    dãy số nguyên tố, fibonaxi,...
void ShellSort(int *a, int n)
{
int i, j, x, step;
step = n / 2;
while(step > 0)
{
for(i = step; i < n; i++)
{
x = a[i];
for(j = i - step;j >= 0 && a[j] > x; j = j - step)
a[j + step] = a[j];
a[j + step] = x;
}
step = step / 2;
}
}

             8. Quick Sort:
                 Ý tưởng: Lấy một phần tử làm trục, sắp xếp sao cho các phần tử đứng trước trục đều có giá                                  trị nhỏ hơn giá trị trục, các phần tử đứng sau trục đều có giá trị lớn hơn giá trị                                        trục.
                  Sắp xếp sẽ có hiệu quả hơn nếu chọn phần tử ở chính giữa làm trục.

void QuickSort(int *a, int left, int right)
{
int i, j, pivot, t;
pivot = a[(left + right) / 2];
i = left;
j = right;
while(i <= j)
{
while(a[i] < pivot)
i++;
while(a[j] > pivot)
j--;
if(i <= j)
{
t = a[i]; a[i] = a[j]; a[j] = t;
i++;
j--;
}
}
if(left < j)
QuickSort(a, left, j);
if(i < right)
QuickSort(a, i, right);
}

             9. Merge Sort:
                 Ý tưởng: Từ hai mảng bị phân đôi đã có thứ tự, ta tiến hành trộn hai mảng đó thành một                                      mảng duy nhất có thứ tự.

void Merge(int *a, int start1, int end1, int end2)
{
int *b, i, j, k;
b = new int [end2 - start1 + 1];
if(b == NULL)
return;
i = start1;
j = end1 + 1;
k = 0;
while(i <= end1 && j <= end2)
{
if(a[i] > a[j])
b[k++] = a[j++];
else
b[k++] = a[i++];
}
while(i <= end1)
b[k++] = a[i++];
while(j <= end2)
b[k++] = a[j++];
for(i = end2; i >= start1; i--)
a[i] = b[--k];
}

void MergeSort(int *a, int left, int right)
{
int mid = (left + right) / 2;
if(left < mid)
MergeSort(a, left, mid);
if(mid + 1 < right)
MergeSort(a, mid + 1, right);
Merge(a, left, mid, right);
}
             10. Heap Sort:
                 Ý tưởng: Tổ chức mảng như cây nhị phân gần đầy, mỗi nút có nhiều nhất là hai nút con sao                                  cho giá trị nút cha lớn hơn giá trị của hai nút con. Xóa nút gốc, đưa nút cuối cùng                                  lên làm gốc rồi tạo lại cây.

void ShiftDown(int *a, int left, int right)
{
int k, t;
while(left * 2 + 1 <= right)
{
k = left * 2 + 1;
if(k + 1 <= right && a[k] < a[k + 1])
k = k + 1;
if(a[k] < a[left])
return;
else
{
t = a[k]; a[k] = a[left]; a[left] = t;
left = k;
}
}
}

void HeapSort(int *a, int n)
{
int i, t;
for(i = (n - 2) / 2; i >= 0; i--)
ShiftDown(a, i, n - 1);
i = n - 1;
while(i > 0)
{
t = a[0]; a[0] = a[i]; a[i] = t;
  í--;
ShiftDown(a, 0, i);
}
}

                11. Radix Sort:
                    Ý tưởng: Sắp xếp các số lần lượt theo chữ số ở hàng đơn vị, chục, trăm,...

void RadixSort(int *a, int n)
{
int i, max, k;
int *b = new int [n];
if(b == NULL)
return;
max = a[0];
k = 1;
for(i = 1; i < n; i++)
{
if(a[i] > max)
max = a[i];
}
while(max / k > 0)
{
int c[10] = {0};
for(i = 0; i < n; i++)
c[a[i] / k % 10]++;
for(i = 1; i < 10; i++)
c[i] += c[i - 1];
for(i = n - 1; i >= 0; i--)
b[--c[a[i] / k % 10]] = a[i];
for(i = 0; i < n; i++)
a[i] = b[i];
k = k * 10;
}
}

  • Từ những điều trên, ta có thể tính thời gian sắp xếp của mỗi thuật toán để tìm ra cách hiệu quả và phù hợp nhất. Hãy xây dựng hàm phát sinh mảng ngẫu nhiên và tính thời gian.
  • Gợi ý:
      #include <time.h>
   
      int main()
      {
              int *a, n;
              //Tạo mảng ngẫu nhiên
              clock_ t  start, end;
              start = clock();
              //Gọi thuật toán
              end = clock();
              cout<<"Time = "<< (float)(end - start) / CLOCKS_PER_SEC<<" s"<<endl;
      }

Buscar

 

About

Something in IT Copyright © 2011 | Tema diseñado por: compartidisimo | Con la tecnología de: Blogger