Hotline: 024.62511017

024.62511081

  Trang chủ   Sản phẩm   Phần mềm Dành cho nhà trường   Phần mềm Hỗ trợ học tập   Kho phần mềm   Liên hệ   Đăng nhập | Đăng ký

Tìm kiếm

School@net
 
Xem bài viết theo các chủ đề hiện có
  • Hoạt động của công ty (726 bài viết)
  • Hỗ trợ khách hàng (498 bài viết)
  • Thông tin tuyển dụng (57 bài viết)
  • Thông tin khuyến mại (80 bài viết)
  • Sản phẩm mới (216 bài viết)
  • Dành cho Giáo viên (549 bài viết)
  • Lập trình Scratch (3 bài viết)
  • Mô hình & Giải pháp (156 bài viết)
  • IQB và mô hình Ngân hàng đề kiểm tra (127 bài viết)
  • TKB và bài toán xếp Thời khóa biểu (242 bài viết)
  • Học tiếng Việt (183 bài viết)
  • Download - Archive- Update (289 bài viết)
  • Các Website hữu ích (70 bài viết)
  • Cùng học (92 bài viết)
  • Learning Math: Tin học hỗ trợ học Toán trong nhà trường (78 bài viết)
  • School@net 15 năm (154 bài viết)
  • Mỗi ngày một phần mềm (7 bài viết)
  • Dành cho cha mẹ học sinh (124 bài viết)
  • Khám phá phần mềm (122 bài viết)
  • GeoMath: Giải pháp hỗ trợ học dạy môn Toán trong trường phổ thông (36 bài viết)
  • Phần mềm cho em (13 bài viết)
  • ĐỐ VUI - THƯ GIÃN (363 bài viết)
  • Các vấn đề giáo dục (1210 bài viết)
  • Bài học trực tuyến (1037 bài viết)
  • Hoàng Sa - Trường Sa (17 bài viết)
  • Vui học đường (275 bài viết)
  • Tin học và Toán học (220 bài viết)
  • Truyện cổ tích - Truyện thiếu nhi (180 bài viết)
  • Việt Nam - 4000 năm lịch sử (97 bài viết)
  • Xem toàn bộ bài viết (8223 bài viết)
  •  
    Đăng nhập/Đăng ký
    Bí danh
    Mật khẩu
    Mã kiểm traMã kiểm tra
    Lặp lại mã kiểm tra
    Ghi nhớ
     
    Quên mật khẩu | Đăng ký mới
     
    Thành viên có mặt
    Khách: 11
    Thành viên: 0
    Tổng cộng: 11
     
    Số người truy cập
    Hiện đã có 89585574 lượt người đến thăm trang Web của chúng tôi.

    Tìm kiếm với cấu trúc dữ liệu Skip List

    Ngày gửi bài: 05/10/2007
    Số lượt đọc: 3318

    Skip List là một cấu trúc dữ liệu trừu tượng có xác suất, được phát minh bởi giáo sư William Pugh thuộc trường đại học MaryLand. Skip List lần đầu tiên được giới thiệu vào năm 1989 trong bài báo cáo “Skip Lists: A Probabilistic Alternative to Balanced Trees”, tham dự hội nghị về thuật toán và cấu trúc dữ liệu tại Ottawa Canada.

    Trong báo cáo của mình, tác giả khẳng định: Skip List là sự thay thế hiệu quả cho cấu trúc cây cân bằng (Balanced Trees). Một Cấu trúc dữ liệu nổi tiếng và cổ điển nhất trong các cây cân bằng, được đưa ra bởi Adelson Velsky và E.M.Landis là cây AVL. Nó cho phép thực hiện thuật toán tìm kiếm với độ phức tạp trung bình là O(log2n ). Tuy nhiên để duy trì tính “cân bằng” của cấu trúc cây, các phép toán bổ sung và loại bỏ trên Balanced Trees được thực hiện khá phức tạp, sự phân tích một cách toán học của chúng cho đến nay vẫn còn là một bài toán mở. Skip List đã giải quyết vấn đề này một cách hiệu quả mà không làm ảnh hưởng đến tốc độ của phép tìm kiếm. Cấu trúc này thể hiện tính cân bằng một cách tự nhiên theo xác suất hơn là tạo nên sự cân bằng hoàn toàn. Kết quả là các thuật toán chèn và loại bỏ phần tử trở nên đơn giản, và quan trọng hơn là chúng được thực hiện nhanh hơn so với các thuật toán tương đương trên Balanced Trees.

    Skip List là một danh sách liên kết đơn mở rộng với mục đích tăng tốc độ của phép toán tìm kiếm. Để tìm kiếm một phần tử trên danh sách liên kết đơn đã được sắp xếp (hình1), ta phải duyệt từ đầu danh sách cho đến khi gặp được nút có khoá cần tìm hoặc có khoá lớn hơn khoá cần tìm. Trong trường hợp tồi nhất ta phải duyệt qua toàn bộ các phần tử của danh sách, do dó chi phí cho phép toán tìm kiếm này là O(n).

    (hình 1 - danh sách liên kết đơn)

    Để tăng tốc độ của phép tìm kiếm trên danh sách liên kết, William Pugh thêm vào các con trỏ ở một số nút cho phép trỏ đến nững nút nằm ở xa hơn. Chẳng hạn ta có thể thêm vào các con trỏ phụ ở các nút có thứ tự chẵn, các con trỏ này trỏ đến các nút kế tiếp thứ hai (hình 2). Với việc thêm vào các con trỏ phụ, danh sách bây giờ giống như có hai tầng (level). Tầng một là danh sách ban đầu, tầng hai là một danh sách với các phần tử là các nút có thứ tự chẵn trong danh sách ban đầu. Tương tự, ta có thể thêm vào các con trỏ để tạo ra Skip List có nhiều tầng (hình 3).

    (hình 2 - Skip List 2 tầng)

    (hình 3 - Skip List 3 tầng)

    Trong Skip List, một nút có k con trỏ được gọi là nút có cấp k. Theo các nhà nghiên cứu thì số cấp lớn nhất của một nút hay số tầng tối đa (MaxLevel) của Skip List nên là Log2n, với n là số nút của danh sách. Trên thực tế MaxLevel=32 được dùng cho danh sách có 232 (~ 4 tỷ) nút là đủ dùng cho hầu hết các trường hợp. Khác với Balanced Trees, thao tác bổ xung và loai bỏ trên Skip List được thực hiện rất dễ dàng. Để bổ xung (Insert) một phần tử, ta phải phát sinh ngẫu nhiên cấp của nút đó, tìm vị trí của nút cần chèn, cập nhật các con trỏ cho nút mới. Hình 4, 5 mô tả trực quan các thao tác này. Thao tác xoá (Remove) phần tử được thực hiện bằng cách tìm nút cần xoá, ghi nhận các con trỏ trỏ đến nút này, loại bỏ nút, cập nhật lại các con trỏ và MaxLevel.

    (hình 4 – Tìm vị trí cần chèn một nút mới trong Skip List)

    (hình 5- Skip List sau khi chèn phần tử mới)

    Skip List là cấu trúc dữ liệu có xác suất. Hiệu suất tìm kiếm trung bình được tăng lên là do chúng ta giảm số nút ở mỗi tầng để tìm kiếm bằng cách “nhảy”. Để tìm kiếm một phần tử, ta xuất phát từ tầng cao nhất, duyệt qua các nút ở tầng hiện tại cho đến khi nút kế tiếp có khoá lơn hơn khoá cần tìm. Lúc đó ta giảm đi một tầng và tiếp tục thực hiện quá trình tìm kiếm. Nếu ở tầng đáy (tầng 1) mà nút kế tiếp có khoá lớn hơn khoá cần tìm, thì nút cần tìm không có trong Skip List. Hình 6 minh hoạ tác tìm kiếm phần tử có khoá bằng 21. Khi các nút của danh sách càng lớn, hiệu quả trung bình của Skip List càng tiệm cận với Balanced Trees.

    (hình 6 - minh họa quá trình tìm kiếm trên Skip List)

    Nếu ai đã từng cài đặt các thuật toán cây cân bằng, chắc hẳn đều có chung một nhận định: đó là việc “nói dễ hơn làm” [3]. Với Skip List, việc cài đặt trở nên rất đơn giản. Để dễ dàng sử dụng trong nhiều chương trình, dưới đây ta cài đặt Skip List như một thư viện trong ngôn ngữ C++. Với Borland C++, hay Microsoft Visual C++ bạn soạn thảo tệp SkipList.h với nội dung như sau.

    #include "stdio.h"
    #include "conio.h"
    #ifdef _WIN32 // Microsoft Visual C++
    #include "BASETSD.H"
    #define MAX_OF_KEY MAXINT_PTR
    #else // Borland C++
    #include "values.h"
    #define MAX_OF_KEY MAXINT
    #endif
    #include "stdlib.h"
    #define MAXLEVEL 15
    #define Less(a,b) (a < b)
    #define Equal(a,b) (a == b)
    typedef int KeyType; /*kieu cua truong khoa */
    typedef int RecType; /* kieu cua truong info*/
    typedef struct NodeType
    {
    KeyType key; /* truong khoa */
    RecType info; /* truong thong tin */
    struct NodeType *forward[1];
    };
    typedef struct SkipList
    {
    NodeType *head; // nut dau danh sach
    NodeType *tail; // nut cuoi danh sach
    int ListLevel;
    ;
    SkipList list;
    /*** ham khoi tao ******************/
    void init( SkipList &list)
    {
    if ((list.head =(NodeType *) malloc(sizeof(NodeType) + MAXLEVEL*sizeof(NodeType *))) == NULL)
    {
    printf(" loi cap phat!");
    exit(EXIT_FAILURE);
    }
    if ((list.tail =(NodeType *) malloc(sizeof(NodeType) + sizeof(NodeType *))) == NULL)
    {
    printf(" loi cap phat!");
    exit(EXIT_FAILURE);
    }
    list.tail->key=MAX_OF_KEY; //nut cuoi phai co gia tri khoa lon nhat
    list.ListLevel=1;
    for (int i=1;i<=MAXLEVEL;i++)
    list.head->forward[i]=list.tail;
    }
    /*** ham phat sinh ngau nhien cap cua mot nut ***/
    int generate_level(void)
    {
    int level=1;
    while ((rand()< RAND_MAX/2) && (level level=level+1;
    return level;
    }
    /*** ham chen mot nut vao Skip List *******/
    void insert(SkipList &list, int key, RecType newvalue)
    {
    int i, newLevel;
    NodeType *update[MAXLEVEL+1];
    NodeType *x;
    x=list.head;
    for (i = list.ListLevel; i >0; i--)
    {
    while (Less(x->forward[i]->key, key))
    x = x->forward[i];
    update[i] = x;
    }
    x = x->forward[1];
    if (Equal(x->key, key))
    x->info=newvalue;
    else
    {
    newLevel = generate_level();
    if (newLevel>list.ListLevel)
    {
    for (i=list.ListLevel+1;i<=newLevel;i++)
    update[i]=list.head;
    list.ListLevel=newLevel;
    }
    // tao nut
    if ((x =(NodeType *) malloc(sizeof(NodeType) + newLevel*sizeof(NodeType *))) == NULL)
    {
    printf(" loi cap phat!");
    exit(EXIT_FAILURE);
    }
    x->key=key; x->info=newvalue;
    // cap nhat cac con tro
    for (i=1;i<=newLevel;i++)
    {
    x->forward[i]=update[i]->forward[i];
    update[i]->forward[i]=x;
    }
    }
    }
    /*** ham xoa mot nut trong Skip List *********/
    void remove(SkipList &list, int key)
    {
    int i;
    NodeType *update[MAXLEVEL+1], *x;
    x = list.head;
    for (i = list.ListLevel; i >= 1; i--)
    {
    while Less(x->forward[i]->key, key)
    x = x->forward[i];
    update[i] = x;
    }
    x = x->forward[1];
    if (Equal(x->key, key)) // nut can xoa
    {
    for (i = 1; i <= list.ListLevel; i++)
    {
    if (update[i]->forward[i] != x) break;
    update[i]->forward[i] = x->forward[i];
    }
    free (x);
    while ((list.ListLevel > 1)&&(list.head->forward[list.ListLevel] == list.tail))
    list.ListLevel--;
    }
    }
    /*** ham tim kiem mot nut co gia tri khoa bang key trong Skip List ****/
    NodeType * search(SkipList list, int key)
    {
    int i;
    NodeType *x = list.head;
    for (i = list.ListLevel; i > 1; i--)
    {
    while Equal(x->forward[i]->key, key)
    x = x->forward[i];
    }
    x = x->forward[1];
    if Equal(x->key, key)
    return x ;
    else
    return NULL;
    }

    Nhằm mục đích khảo sát, xo sánh và khẳng định sức mạnh của Skip List. Các nhà nghiên cứu đã tiến hành thực nghiệm với các kỹ thuật tìm kiếm khác nhau như Skip List, AVL Trees, 2-3 Trees, self-adjusting trees. Chương trình được cài đặt bằng ngôn ngữ C, thao tác tìm kiếm được thực hiện trên cấu trúc dữ liệu chứa 216 phần tử, trường khoá của các phần tử có kiểu Integer. Kết quả thực nghiệm thu được như bảng sau:

    (bảng 1- khảo sát thời gian thực hiện các thao tác với các kỹ thuật tìm kiếm khác nhau) Trong cài đặt trên, hàm generate_level() sẽ phát sinh ngẫu nhiên cấp của một nút. Vì hàm rand() trả lại giá trị từ 0 đến RAND_MAX, nên số lượng nút ở cấp i+1 sẽ bằng khảng 50% ở cấp i. Gọi p là tỷ lệ giữa số nút ở cấp i+1và số nút cấp i, ta có thể thay đổi tỷ lệ này theo từng trường hợp cụ thể. William Pugh đề nghị nên sử dụng p=25% trong đa số trường hợp. Nếu ứng dụng yêu cầu tính ổn định cao thì nên sử dụng p=50%. Điều rất thú vị là với p=25%, một nút của Skip List trung bình chỉ có 1.33 con trỏ, trong khi đó Balanced Trees cần tới 2 con trỏ cho mỗi nút. Rõ ràng trong trường hợp này, Skip List tiết kiệm bộ nhớ hơn so với Balanced Trees và diều này một lần nữa chứng minh Skip List là một sự lựa chọn tốt cho các ứng dụng tìm kiếm. Xin được trích dẫn khẳng định của William Pugh để thay cho lời kết: “Whatever you might want to do using Balanced Trees, you can do it faster and more simply using Skip Lists”.

    Tài liệu tham khảo

    [1] William Pugh, Skip Lists: A Probabilistic Alternative to Balanced Trees, Proceedings of the Workshop on Algorithms and Data Structures, Ottawa Canada, August 1989 (to appear in Comm. ACM).

    [2] Pugh, W., Whatever you might want to do using Balanced Trees, you can do it faster and more simply using Skip Lists, Tech Report CS-TR-2286, Dept. of Computer Science, University of Maryland, College Park, July 1989.

    [3] Robert Sedgewick. Cẩm nang thuật toán (bản dịch). NXB Khoa học và Kỹ thuật, Hà Nội, 1994.

    School@net



     Bản để in  Lưu dạng file  Gửi tin qua email


    Những bài viết khác:



    Lên đầu trang

     
    CÔNG TY CÔNG NGHỆ TIN HỌC NHÀ TRƯỜNG
     
    Phòng 804 - Nhà 17T1 - Khu Trung Hoà Nhân Chính - Quận Cầu Giấy - Hà Nội
    Phone: 024.62511017 - 024.62511081
    Email: kinhdoanh@schoolnet.vn


    Bản quyền thông tin trên trang điện tử này thuộc về công ty School@net
    Ghi rõ nguồn www.vnschool.net khi bạn phát hành lại thông tin từ website này
    Site xây dựng trên cơ sở hệ thống NukeViet - phát triển từ PHP-Nuke, lưu hành theo giấy phép của GNU/GPL.