Các thao tác trên cây quản lí đoạn

Một phần của tài liệu (LUẬN văn THẠC sĩ) cây quản lí đoạn và ứng dụng (Trang 31 - 36)

2.3.2.1. Tạo nút mới

Một nút mới chứa đoạn [a, b] được tạo ra bằng các thao tác sau: function newNode(a, b: real): pnode;

var p: pnode; Begin New(p); p^.s := a; p^.f := b; p^.rightmost := b ; p^.left:=nil; p^.right:=nil; End;

2.3.2.2. Dựng cây – Chèn đoạn

Việc khởi tạo cây quản lí đoạn rỗng không khác gì cây nhị phân tìm kiếm, tức là chỉ cần cho root = nil. Ta sẽ xem xét chi tiết cách chèn một nút

chứa đoạn [a, b] vào cây quản lí đoạn gốc T sao cho sau khi chèn nút đó vào

thì vẫn đảm bảo cấu trúc cây quản lí đoạn. Thao tác này được thực hiện một cách đệ quy:

 Nếu cây rỗng thì tạo nút mới chứa đoạn [a, b] và cho nút này làm gốc của cây.

 Nếu nút T có T^.s > a thì ta tìm chỗ chèn ở cây con trái, ngược lại thì chèn đoạn vào cây con phải.

 Cập nhật T^.rightmost := b nếu T^.rightmost < b.

Procedure InsertIT(a, b: real; var T: pnode); Begin if (T = nil)then begin T := newNode(a, b); exit; end ; if (a < T^.s) then InsertIT(a, b, T^.left); else InsertIT(a, b, T^.right); if (T^.rightmost < b) then T^.rightmost := b; End;

Thời gian thực hiện giải thuật chèn một đoạn hay một nút vào cây quản lí đoạn là O(h) với h là chiều cao của cây. Trong trường hợp tồi nhất, cây

quản lí đoạn là một cây nhị phân suy biến có n nút thì h = n. Nếu sử dụng cây nhị phân tìm kiếm tự cân bằng như: cây AVL, cây đỏ đen, cây splay, treap

[2], [3],... thì phép chèn đoạn, sau đó cập nhật các trường rightmost có thời

gian O(logn), h = lgn.

2.3.2.3. Duyệt cây theo thứ tự giữa

Với cây nhị phân tìm kiếm, thông thường ta quan tâm đến phép duyệt cây theo thứ tự giữa để thăm các nút trên cây, và theo thứ tự thăm đó, ta được dãy các giá trị lưu trong nút (khóa) theo thứ tự tăng dần. Tức là, trong thao tác duyệt theo thứ tự giữa thì giá trị trong một nút bất kì sẽ được liệt kê sau tất cả các giá trị lưu ở nút con trái và được liệt kê trước tất cả các giá trị lưu ở nút con phải của nút đó theo thứ tự giữa.

Trên cây quản lí đoạn, để liệt kê các đoạn được quản lí trong các nút theo thứ tự tăng dần của đầu mút trái, ta sử dụng phép duyệt cây theo thứ tự giữa. Thao tác này có thể mô tả bằng thủ tục đệ quy:

Procedure inorder(p: pnode); Begin if p<> nil then Begin inorder(p^.left); writeln(‘[‘,p^.s,’ ‘,p^.f,’]’); inorder(p^.right); End; End;

Quá trình duyệt cây theo thứ tự giữa bắt đầu bằng lời gọi inorder(root). Nếu ta duyệt cây ở hình 2.3 theo thứ tự giữa thì quá trình duyệt sẽ liệt kê lần lượt các đoạn theo thứ tự tăng dần của đầu mút trái, ta được: [0, 3]; [5, 8]; [6, 10]; [8, 9]; [15,23]; [16, 21]; [17, 19]; [19, 20]; [25, 30]; [26, 26].

2.3.2.4. Tìm đoạn có giao với một đoạn cho trước

Bài toán đặt ra là cho một tập S gồm n đoạn. Cho một đoạn [ , ]a b , hãy chỉ ra một đoạn của S có giao với (hay gối lên) đoạn [ , ]a b . Một dạng truy vấn cụ thể hơn là hãy chỉ ra một đoạn của S chứa một điểm x cho trước. Bài toán này có thể quy về bài toán tổng quát với [ , ] [ , ]a bx x

Tất nhiên, ta có thể trả lời truy vấn này trong thời gian O(n) bằng cách duyệt tất cả các đoạn của S và sử dụng hàm Overlapped dưới đây để tìm cũng như liệt kê các đoạn gối lên đoạn [ , ]a b . Hàm này nhận vào hai đoạn [ 1, 1],[ 2, 2]s f s f và trả ra True nếu hai đoạn gối nhau:

Function overlapped(s1, f1, s2, f2: real): boolean; Begin

Return (s1<=f2) and (s2<=f1); End;

Tuy nhiên, nếu S liên tục có sự cập nhật thêm, bớt các đoạn thì cách trên là không hiệu quả. Thay vào đó, sử dụng cấu trúc dữ liệu cây quản lí đoạn sẽ thực hiện cập nhật và trả lời truy vấn một cách hiệu quả.

Xây dựng cây quản lí đoạn chứa tất cả các đoạn của tập S. Bắt đầu từ nút

p = root, nếu đoạn trong p có giao với [a, b] thì xong.

Nếu p^.left^.rightmost >= a, ta quy về tìm trong nhánh con trái của p;

ngược lại thì tìm trong nhánh con phải của p.

Function IntervalSearch(a, b: real): pnode; Var p: pnode;

Begin

p := root;

While (p<>nil) and not overlapped(p^.s,p^.f,a,b) do If p^.left^.rightmost >= a then

P := p^.left Else p := p^.right;

Return p; End;

Tính đúng đắn của thuật toán được chỉ ra trong hai nhận xét sau:

 Nếu p^.left^.rightmost >= a thì chỉ cần tìm trong nhánh con trái của p là đủ, bởi nếu tìm trong nhánh con trái của p không thấy thì chắc chắn tìm trong nhánh con phải của p cũng không thấy.

 Nếu p^.left^.rightmost < a thì nhánh con trái của p chắc chắn không chứa đoạn nào có giao với [a, b].

Thời gian thực hiện giải thuật tìm kiếm trên là O(h) với h là chiều cao

cây quản lí đoạn, h = lgn nếu sử dụng cây nhị phân tìm kiếm tự cân bằng để cài đặt cây quản lí đoạn.

2.3.2.5. Liệt kê các đoạn có giao với một đoạn cho trước

Bài toán đặt ra là cho tập S gồm n đoạn, hãy liệt kê các đoạn có giao với đoạn [a, b] cho trước. Thuật toán duyệt toàn bộ theo cách: duyệt tất cả các

đoạn của S và dùng hàm overlapped để liệt kê các đoạn thỏa mãn trong thời gian O(n). Thực tế không có thuật toán nào tốt hơn để liệt kê trong trường hợp tồi nhất là tất cả các đoạn của S đều có giao với [a,b].

Tuy nhiên, ta có thể tìm một thuật toán khác mà thời gian thực hiện giải thuật phụ thuộc vào số đoạn được liệt kê và ít phụ thuộc vào giá trị của n.

Trước hết ta xây dựng cây quản lí đoạn chứa các đoạn của S. Sau đó sử

dụng thủ tục ListIntervals(p, a, b) để liệt kê các đoạn có giao với [a, b] trong nhánh cây gốc p^.

Procedure listIntervals(p: pnode; a, b: real); Begin

If p = nil then return;

If p^.left^.rightmost >= a then listIntervals(p^.left, a, b);

Output [p^.s, p^.f]; // Liệt kê If p^.s <=b then

listIntervals(p^.right, a, b); End;

Thời gian thực hiện giải thuật là O(logn + m) với m là số nút được liệt kê.

Một phần của tài liệu (LUẬN văn THẠC sĩ) cây quản lí đoạn và ứng dụng (Trang 31 - 36)

Tải bản đầy đủ (PDF)

(65 trang)