- Từ 4 -- 7 - cách rút tiền fun88 wtf

Trong quá trình nghiên cứu về cơ sở dữ liệu vectơ, tôi nhận thấy rằng thuật toán HNSW là một khái niệm quan trọng và cốt lõi. Tuy nhiên, việc tiếp cận trực tiếp có thể gây khó khăn cho người mới bắt đầu. Do đó, chúng ta sẽ cùng khám phá trước cấu trúc skip list – một cấu trúc phổ biến được xây dựng dựa trên danh sách liên kết (linked list).

Cấu trúc danh sách liên kết Link to heading

Danh sách liên kết là một trong những cấu trúc dữ liệu đơn giản nhất nhưng rất hiệu quả khi thêm hoặc xóa phần tử. Tuy nhiên, nhược điểm lớn của nó là phải duyệt qua từng phần tử để tìm kiếm mục tiêu, dẫn đến độ phức tạp tuyến tính O(n). Ví dụ như trong Java, từ phiên bản JDK 1.8 trở đi, cấu trúc hash map đã được cải tiến bằng cách sử dụng sự kết hợp giữa danh sách liên kết và cây đỏ đen (Red-Black Tree) để tăng tốc độ tìm kiếm khi kích thước danh sách dài hơn.

Để cải thiện vấn đề này, các nhà khoa học đã phát triển skip list - một cấu trúc tối ưu hóa từ danh sách liên kết thông thường.

Lý thuyết cơ bản về skip list Link to heading

Skip list hoạt động theo nguyên tắc phân tầng. Mỗi tầng cao hơn chỉ chứa một số phần tử thưa thớt so với tầng dưới. Điều này giúp giảm thiểu số lần duyệt cần thiết để tìm kiếm phần tử mong muốn. Để dễ hình dung, hãy tưởng tượng bạn có một danh sách đơn giản như sau:

1 --> 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 --> 9 

Nếu bạn muốn tìm số 8, bạn sẽ phải duyệt qua tất cả các phần tử từ đầu đến cuối. Nhưng nếu chúng ta chuyển đổi cấu trúc thành dạng skip list như sau:

1 --------------> 4 --------------> 7 ---------> null 
|         |         |
1 --> 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 --> 9 --> null

Bây giờ, bạn có thể nhanh chóng bỏ qua các phần tử không cần thiết ở tầng cao hơn. Ví dụ, khi tìm số 8, bạn chỉ cần đi qua các bước sau:

  • Từ 1 –> 4.
  • Từ 4 –> 7.
  • Sau đó xuống tầng thấp hơn và di chuyển từ 7 –> 8.

Như vậy, thay vì duyệt qua tất cả các phần tử, bạn chỉ cần thực hiện vài bước, tiết kiệm đáng kể thời gian.

Thực hiện skip list trong mã win123 club vua bài đổi thưởng nguồn Link to heading

Định nghĩa lớp Node Link to heading

Mỗi nút trong skip list sẽ bao gồm giá trị dữ liệu và mảng con trỏ tới các nút khác ở các tầng khác nhau:

public class Node {
    public int data = -1;
    public Node[] forwards;

    public Node(int level) {
        forwards = new Node[level];
    }

    public void setData(int value) {
        this.data = value;
    }
}

Xây dựng cấu trúc skip list Link to heading

Cấu trúc skip list bao gồm mức tối đa (MAX_LEVEL), mức hiện tại (currentLevel), nút đầu tiên (head), và bộ tạo số ngẫu nhiên (random):

public class SkipList {
    private static final int MAX_LEVEL = 16;
    private int currentLevel = 0;
    private Node head = new Node(MAX_LEVEL);
    private Random random = new Random();
}

Sinh ngẫu nhiên mức độ của nút mới Link to heading

Chúng ta sử dụng phương pháp sinh ngẫu nhiên dựa trên xác suất (giống như Redis đã làm):

private int generateRandomLevel() {
    int level = 1;
    while ((random.nextInt() & 0xFFFF) < (0.25 * 0xFFFF)) {
        level += 1;
    }
    return Math.min(level, MAX_LEVEL);
}

Chèn phần tử vào skip list Link to heading

Khi chèn phần tử, chúng ta cần xác định vị trí phù hợp trong từng tầng và cập nhật các con trỏ tương ứng:

public void insert(int value) {
    int level = generateRandomLevel();
    if (level >= currentLevel) {
        currentLevel = level;
    }

    Node newNode = new Node(level);
    newNode.setData(value);

    Node[] update = new Node[level];
    Arrays.fill(update, head);

    Node p = head;
    for (int i = level - 1; i >= 0; --i) {
        while (p.forwards[i] != null && p.forwards[i].data < value) {
            p = p.forwards[i];
        }
        update[i] = p;
    }

    for (int i = 0; i [xèng hoa quả đổi thưởng](/blog/6d366a11d68fc231/)  < level; i++) {
        newNode.forwards[i] = update[i].forwards[i];
        update[i].forwards[i] = newNode;
    }
}

Tìm kiếm phần tử trong skip list Link to heading

Để tìm kiếm phần tử, chúng ta bắt đầu từ tầng cao nhất và dần dần di chuyển xuống các tầng thấp hơn:

public Node search(int value) {
    Node p = head;
    for (int i = currentLevel - 1; i >= 0; i--) {
        while (p.forwards[i] != null && p.forwards[i].data < value) {
            p = p.forwards[i];
        }
    }

    if (p.forwards[0] != null && p.forwards[0].data == value) {
        return p.forwards[0];
    } else {
        return null;
    }
}

Xóa phần tử khỏi skip list Link to heading

Quá trình xóa tương tự như chèn, chỉ cần cập nhật lại các con trỏ để loại bỏ phần tử:

public void delete(int value) {
    Node[] update = new Node[currentLevel];
    Node p = head;

    for (int i = currentLevel - 1; i >= 0; i--) {
        while (p.forwards[i] != null && p.forwards[i].data < value) {
            p = p.forwards[i];
        }
        update[i] = p;
    }

    if (p.forwards[0] != null && p.forwards[0].data == value) {
        for (int i = currentLevel - 1; i >= 0; i--) {
            if (update[i].forwards[i] != null && update[i].forwards[i].data == value) {
                update[i].forwards[i] = update[i].forwards[i].forwards[i];
            }
        }
    }
}

Kết luận Link to heading

Skip list là một giải pháp tuyệt vời để tăng cường hiệu suất tìm kiếm trên danh sách liên kết. Mặc dù nó yêu cầu thêm không gian lưu trữ cho các tầng bổ sung, nhưng lợi ích về tốc độ tìm kiếm thường vượt xa nhược điểm này. Hiểu rõ skip list sẽ giúp bạn dễ dàng tiếp cận các thuật toán nâng cao hơn như HNSW trong lĩnh vực cơ sở dữ liệu vectơ.