Download as pdf or txt
Download as pdf or txt
You are on page 1of 77

Ngày 3

Trong Chương 3, chúng ta đã tìm hiểu về thuật toán sắp xếp kém hiệu quả với độ phức tạp 0 (N^2).
Nhưng khi chúng ta phải đối mặt với dữ liệu đầu vào khổng lồ, những thuật toán sơ đẳng này dường
như không có giá trị thực tế. Tuy nhiên, miễn là chúng ta sử dụng các kỹ năng lập trình đệ quy và chia để
trị đã học trong chương trước, chúng ta có thể đạt được các thuật toán hiệu quả hơn.

Ví dụ trong chương này là thuật toán tốc độ cao của O (nlog N) và thuật toán sắp xếp có thể đạt tới O(N)
(độ phức tạp thời gian tuyến tính) trong những điều kiện nhất định.

Trước khi chạm vào các câu hỏi trong chương này, bạn cần nắm vững kiến thức về các thuật toán sắp
xếp sơ cấp và các kỹ năng lập trình ứng dụng đệ quy và chia và chinh phục

Hãy viết một chương trình dựa trên đoạn mã giả trên và sử dụng phương pháp sắp xếp hợp nhất để sắp
xếp dãy S chứa «số nguyên theo thứ tự tăng dần. Ngoài ra, vui lòng báo cáo có bao nhiêu thao tác so
sánh đã được thực hiện trong hợp nhất.

Nhập dòng đầu tiên để nhập trái tim và dòng thứ hai để nhập số nguyên đại diện cho S.
Đầu ra Chuỗi đã sắp xếp S được đưa ra ở hàng đầu tiên. Các phần tử liền kề trong dãy được phân cách
bằng dấu cách. Số hoạt động so sánh được xuất trên dòng thứ hai.

N<=500000

0<=Các yếu tố của S <= 10^9

giải thích

Đối mặt với một số mảng lớn, các thuật toán sắp xếp cơ bản có độ phức tạp lên đến 0 (N^2) như sắp xếp
bong bóng đã mất đi giá trị thực tế của chúng, các thuật toán nâng cao cần thiết để xử lý dữ liệu như vậy
và sắp xếp hợp nhất là một trong số đó.

Hợp nhất sắp xếp

►Execute hợp nhất với toàn bộ mảng dưới dạng một đối tượng

► hợp nhất như sau

1."Tách" một mảng cục bộ đã cho có chứa N phần tử thành hai mảng cục bộ, mỗi mảng chứa 2 phần tử.
(Chia)

2. Thực hiện hợp nhất trên hai mảng cục bộ tương ứng. (Gỡ rối)

3.Hai mảng cục bộ được sắp xếp đã được "tích hợp" thành một mảng thông qua merge. (Chinh phục)

Trong phương pháp sắp xếp hợp nhất, việc hợp nhất hai mảng đã sắp xếp là cơ sở của toàn bộ thuật
toán. Chúng ta muốn hợp nhất mảng S chứa n1 số nguyên và mảng R chứa n2 số nguyên thành nhóm A.
Bây giờ, giả sử rằng các phần tử trong S và R đã được sắp xếp theo thứ tự tăng dần, việc chúng ta cần
làm là sao chép tất cả các phần tử trong S và R sang A, đồng thời đảm bảo rằng các phần tử này được
sắp xếp theo thứ tự tăng dần.

Cần lưu ý ở đây rằng chúng ta không thể kết nối trực tiếp L và R để áp dụng các thuật toán sắp xếp thông
thường, mà sử dụng tính chất đã sắp xếp của chúng để hợp nhất chúng với sự trợ giúp của thuật toán
hợp nhất với độ phức tạp là O (nl + n2). Ví dụ, có hai mảng được sắp xếp L = {1, 5} và R = {2, 4,8}. Quá
trình hợp nhất được thể hiện trong Hình 7.1.

Để đơn giản hóa việc thực hiện hợp nhất, chúng ta có thể đặt một dấu lớn hơn tất cả các phần tử ở cuối
L và R tương ứng. Trong quá trình so sánh phần tử L và R, chắc chắn chúng ta sẽ gặp phải trường hợp
phần tử bị so sánh với dấu, chỉ cần đặt dấu đủ lớn và giới hạn số lần so sánh là nl + n2 (từ phải sang trái),
chúng ta có thể Để ngăn hai dấu được so sánh và ngăn biến vòng lặp ij vượt quá n1 và n2 tương ứng.

Hãy cùng phân tích chi tiết về mergesort bên dưới. Ba tham số của mergeSort là mảng A và các biến bên
trái và bên phải đại diện cho phạm vi của mảng cục bộ của nó. Như trong Hình 7.2, bên trái đề cập đến
phần tử bắt đầu của mảng cục bộ và bên phải đề cập đến phần tử +1 ở cuối mảng cục bộ.
Mũi tên hướng xuống thể hiện sự phân đoạn, mũi tên hướng lên thể hiện sự tích hợp và các số bên cạnh
mũi tên thể hiện thứ tự xử lý. Trong số đó, bộ phận phụ trách sáp nhập, và bộ phận hợp nhất phụ trách
sáp nhập.

Khi chỉ có một phần tử trong mảng cục bộ, hợp nhất kết thúc mà không cần xử lý. Nếu không, hãy thêm 〃
vào vị trí trung tâm của mảng cục bộ, thêm trái vào giữa (không bao gồm giữa) 1 làm nửa đầu và thêm d
vào phải (không bao gồm phải và xem như nửa sau, sau đó áp dụng mergeSorto riêng biệt.

Trong quá trình hợp nhất, vì hai mảng từng phần cần xử lý đã được sắp xếp, nên có thể sử dụng thuật
toán hợp nhất với độ phức tạp là O (n1+ n2).

Mảng {9, 6, 7, 2, 5, 1, 8, 4,2} trong Hình 7.3 chứa 9 phần tử. Nếu bạn muốn chia nó thành một mảng cục
bộ chỉ chứa một phần tử, bạn cần đi qua 9 -> 5-> 3->2->1 4, được chia thành 5 lớp tổng cộng (8 phần tử
là 4 lớp 8->4->2->). Nói chung, dữ liệu phiến được chia thành các lớp log2n. Vì tổng độ phức tạp của
việc thực hiện hợp nhất ở mỗi lớp là O(n), độ phức tạp tổng thể của sắp xếp hợp nhất là O(nlogn)

Sắp xếp hợp nhất bao gồm so sánh giữa các phần tử không liền kề, nhưng không # trao đổi trực tiếp. Khi
hợp nhất hai mảng đã sắp xếp, nếu gặp cùng một phần tử, miễn là nửa đầu của mảng có mức độ ưu tiên
hơn nửa sau của mảng, thứ tự của các phần tử giống nhau Sẽ không bị lộn ngược. Do đó, sắp xếp hợp
nhất là một thuật toán sắp xếp ổn định.
Mặc dù thuật toán sắp xếp hợp nhất hiệu quả và ổn định, nhưng nó cần tạm thời chiếm một phần không
gian bộ nhớ ngoài mảng được sử dụng để lưu dữ liệu đầu vào.

Hàm partit ion (A, p, r) có thể chia dãy A [p..r] thành A [p..q-1] và A[q+1..r]
Phần và trả về giá trị của chỉ số con q. Trong đó tất cả các phần tử trong A [p..q-1] nhỏ hơn hoặc bằng A
[q] .A [q + 1..r]] Tất cả các phần tử trong A [q]
Hãy cũng viết chương trình theo cách tạo giả sau, nhập dãy số để thực hiện phép chia.

Xin lưu ý rằng nhà máy ở đây là chỉ số con của mảng / phần tử cuối cùng và việc phân chia phải dựa trên
việc tạo ra nhà máy]. Đầu vào Dòng 1 nhập tỷ lệ các số nguyên đại diện cho độ dài lực của chuỗi. Dòng 1
nhập n số nguyên, cách nhau bởi dấu cách. Đầu ra Xuất chuỗi được chia trong vòng 1 dòng. Các phần tử
liền kề trong dãy được phân cách bằng dấu cách. Ngoài ra, các phần tử được sử dụng làm cơ sở phân
chia được đánh dấu bằng "[]".
giải thích

Như trong Hình 7.4, phạm vi của đối tượng phân đoạn của lực mảng là từ P đến cây (bao gồm p và r).
Đây là điểm chuẩn cho phân đoạn (tức là A [r]) )
Đối với xo. Tiếp theo, chúng tôi di chuyển các phần tử vàoA di chuyển các phần tử nhỏ hơn hoặc bằng x
đến phạm vi của p tới (bao gồm cả i, lớn hơn X
Phần tử của được chuyển đến phạm vi i+ 1 đến j(không bao gồm j). Trong đó I được khởi tạo thành p-1, j
được khởi tạo thành p.

Hình 7.4 Các chỉ số cần thiết để phân đoạn

Sau mỗi vòng tính toán, nó sẽ lùi lại một vị trí, để lần lượt xác định từng nhóm độ A[j] nên được phân
loại thành
Đối với hai tình huống sau (Hình 7.5 và Hình 7.6)

Khi A[j] lớn hơn X, không cần di chuyển phần tử, chỉ cần để j di chuyển về phía trước một vị trí và A[j]
được xếp vào "nhóm lớn hơn X".
Hình 7.5 Phân đoạn-Trường hợp 1

Nếu A[j] nhỏ hơn hoặc bằng x, trước tiên hãy di chuyển I về phía trước một vị trí, sau đó trao đổi A[j] và
A[i], theo cách này A[j]
Nhập "1, bằng hoặc bằng nhóm x và khi bạn di chuyển về phía trước một vị trí, phần tử ban đầu đứng
trong tim sẽ về "Nhóm lớn hơn x".
ví dụ

Hình 7.6 Phân đoạn-Trường hợp 2


Bây giờ chia dãy {3, 9, 8, 1, 5,6, 2, 5}, quy trình cụ thể như hình 7.7 C
Cuối cùng (8- ^ 9 trong hình), chúng ta đổi A [i + 1] với A[r] để hoàn thành phép chia.

Khảo sát

Vì bạn phải di chuyển một vị trí tại một thời điểm từ P và di chuyển đến phần xác-1, độ phức tạp của
thuật toán xử lý phân đoạn là O (n)

Quick Sort

Sắp xếp n thẻ. Mỗi thẻ bao gồm 1 bộ đồ (S, H, C, D) và 1 số. Hãy viết chương trình theo mã giả sau để
sắp xếp các thẻ này theo thứ tự tăng dần. Vui lòng nhận ra phân đoạn dựa trên mã giả
Đây A là mảng thẻ lưu trữ và thao tác so sánh trong phép chia dựa trên số lượng thẻ "
Ngoài ra, hãy báo cáo xem chương trình có ổn định đầu ra đối với dữ liệu đầu vào hay không. “Đầu ra ổn
định” ở đây có nghĩa là khi có nhiều thẻ có cùng số lượng thì trình tự đầu vào của chúng phù hợp với
trình tự đầu vào.
Nhập số lượng thẻ vào dòng 1
Nhập thẻ H vào n dòng tiếp theo. Mỗi thẻ chứa 1 chữ cái đại diện cho bộ đồ và 1 số (số nguyên) o Một
khoảng trắng ngăn cách giữa chữ cái và số.
Dòng đầu ra 1 cho biết đầu ra của chương trình ổn định (Ổn định hay Không ổn định) o
Từ dòng 2, sử dụng định dạng tương tự như đầu vào để xuất các thẻ được sắp xếp theo thứ tự (không
cần xuất n)

1<=n<=100000

1<= Các số trên thẻ <=10^9

Đầu vào không có nhiều hơn 2 thẻ có cùng bộ đồ và số

Sắp xếp nhanh chóng


► Thực thi quicksort với toàn bộ mảng dưới dạng một đối tượng
► Quá trình nhanh chóng như sau
1. Tách mảng một phần đối tượng thành hai mảng một phần trước và sau nó. (Chia)
2. Thực hiện quickSort (Giải quyết) trên nửa đầu của mảng cục bộ
3. Thực hiện quicksort (Giải quyết) trên mảng cục bộ trong nửa sau
Như trong hình 7.8, hàm quicksort hàm quicksort chia mảng cục bộ thành hai bằng cách chia, và sau đó
thực thi quicksort đệ quy trên hai nhóm trước và sau để hoàn thành việc sắp xếp mảng đã cho. Ví dụ
trong Hình 7.8 là sử dụng quy trình sắp xếp nhanh cho A {13, 19, 9, 5, 12, 8, 7, 4, 21, 2, 5, 3, 14, 6, 11}.

Khảo sát
Sắp xếp nhanh dựa trên phương pháp chia và chinh phục như sắp xếp hợp nhất, nhưng nó đã được sắp
xếp trong mảng ban đầu khi phân vùng được thực thi, vì vậy không cần xử lý đối chiếu thủ công trong
sắp xếp hợp nhất.
Quicksort trao đổi các phần tử không liền kề trong quá trình phân đoạn, vì vậy nó là một thuật toán sắp
xếp không ổn định. Mặt khác, sắp xếp hợp nhất yêu cầu O(n) (không gian lưu trữ bên ngoài chống lại và
sắp xếp nhanh không yêu cầu thêm bộ nhớ. Nói cách khác, sắp xếp nhanh là sắp xếp tại chỗ (sắp xếp nội
bộ)
Nếu sắp xếp nhanh chỉ có thể chọn giá trị giữa trong quá trình phân đoạn, toàn bộ quá trình được chia
đại khái thành các lớp log2n giống như sắp xếp hợp nhất. Độ phức tạp trung bình của sắp xếp nhanh là
O(nlogn) nói chung là hiệu quả nhất Thuật toán sắp xếp: Tuy nhiên, sắp xếp nhanh được mô tả bởi mã
giả trong câu hỏi sử dụng một phương pháp cố định để chọn điểm chuẩn, do đó, hiệu quả sẽ giảm đáng
kể khi xử lý một số thứ tự dữ liệu nhất định (chẳng hạn như dữ liệu đã sắp xếp) và độ phức tạp trong
trường hợp xấu nhất thậm chí có thể cao như O(n^2) Không chỉ vậy, một số thứ tự dữ liệu có thể làm
cho đệ quy quá sâu và cuối cùng dẫn đến tràn ngăn xếp. Trong trường hợp bình thường, chúng ta cần nỗ
lực nhiều hơn trong việc lựa chọn điểm chuẩn, chẳng hạn như chọn ngẫu nhiên, hoặc chọn một vài giá
trị rồi lấy giá trị trung bình.

Sắp xếp đếm I là một thuật toán sắp xếp ổn định, có thể sắp xếp một mảng chứa n phần tử theo
thời gian tuyến tính O (n+k), trong đó các phần tử của mảng đều lớn hơn hoặc bằng 0 và nhỏ hơn
hoặc bằng k
Khi sắp xếp từng phần tử A của mảng đầu vàoA, trước tiên chúng ta ghi lại số phần tử nhỏ hơn
hoặc bằng C trong mảng đếm C, sau đó tính vị trí của Aj trong mảng đầu ra B theo giá trị trong
C. Xem xét rằng có nhiều phần tử bằng nhau. Chúng ta cũng cần sửa đổi Aj, B để đếm sau phần
tử đầu ra C[Aj]
Hãy viết một chương trình, nhập lực của dãy số, sắp xếp A theo thứ tự tăng dần thông qua thuật toán
sắp xếp đếm và nhập nó. Thuật toán cần được thực hiện theo mã giả trên.
Đầu vào Dòng 1 nhập số nguyên đại diện cho độ dài của chuỗi A

Dòng 2 nhập các số nguyên n, được phân tách bằng dấu cách
Đầu ra Xuất trình tự đã sắp xếp thành 1 hàng. Các phần tử liền kề trong dãy được phân cách bằng dấu
cách.
giới hạn

1<= n <= 2000000

0<= Ai <= 10000

giải thích
Để thuận tiện, trong phần đếm và sắp xếp của câu hỏi này, một mảng điểm bắt đầu được sử dụng để
lưu trữ chuỗi đầu vào. Ví dụ: bây giờ chúng ta đếm và sắp xếp mảng đầu vào A {4, 5, 0,3, 1,5, 0,5} như
trong Hình 7.9. Chương trình đếm số lần xuất hiện của mỗi phần tử trong mảng A và Ghi lại nó trong
mảng C.
Lấy 5 làm ví dụ, có 3 lần 5 ở M nên C [5] bằng 3. Tiếp theo, chúng ta tìm tổng tích lũy của các phần tử
trong C và cập nhật mảng đếm C.
Đếm giá trị của phần tử C [x] trong mảng, cho biết có bao nhiêu phần tử trong mảng A nhỏ hơn hoặc
bằng X. Sau đó, như trong Hình 7.10, dựa trên mảng đếm C, chúng ta sao chép các phần tử của A vào
mảng đầu ra B theo thứ tự và chúng ta nhận được một mảng theo thứ tự tăng dần.

Hình 7.9 Sắp xếp số đếm-Khởi tạo


Hình 7.10 Sắp xếp đếm

Chúng ta bắt đầu từ cuối A và sao chép lần lượt các phần tử vào vị trí thích hợp của B: Ở bước 1, chúng
ta muốn sao chép A [8] (= 5), lúc này C [5] = 8, nghĩa là A chứa 8 nhỏ hơn hoặc bằng 5 Do đó, 5 nên được
sao chép vào vị trí của Sáng thế ký 8]: C [5] trừ đi 1 sau khi sao chép, có nghĩa là có 7 phần tử nhỏ hơn
hoặc bằng 5 .
Bước 2 là sao chép A[7]=0 Tại thời điểm này, vẫn còn hai phần tử nhỏ hơn hoặc bằng 0 trong A, do đó,
sao chép 0 thành B [2]. Ở bước 3, có 7 phần tử nhỏ hơn 5 , Vì vậy, 5 được sao chép vào B [7] và như vậy
để xác định giá trị của mỗi phần tử trong B.
Khảo sát
Miễn là việc lựa chọn bắt đầu từ phần tử cuối cùng của mảng đầu vào A, thì sắp xếp đếm là một thuật
toán sắp xếp ổn định. Trong ví dụ ở Hình 7.10, nếu chúng ta chọn từ phần tử đầu của A, số 0 và 5 lặp lại
sẽ được sao chép thành 3 theo thứ tự ngược lại:
Sắp xếp đếm được sử dụng trong câu hỏi này dựa trên tiền đề không Ai và thời gian và không gian bộ
nhớ cần thiết cho hoạt động của nó cũng tỷ lệ với giá trị lớn nhất của Ai. Tuy nhiên, sắp xếp đếm có thể
được xử lý trong thời gian tuyến tính O (n + k), là một Một thuật toán hiệu quả và ổn định.
Câu trả lời tham khảo

Sắp xếp bằng thư viện tiêu chuẩn


STL cung cấp cho người dùng nhiều thuật toán liên quan đến phần tử mảng và phần tử vùng chứa. Một
trong những chức năng linh hoạt nhất là chức năng sort To để sắp xếp các phần tử
7.5.1 sắp xếp
Trong chương trình được hiển thị trong Chương trình 7.1, chúng tôi sử dụng sắp xếp STL để sắp xếp các
phần tử của vectơ theo thứ tự tăng dần.
Chương trình 7.1 Sử dụng sắp xếp để sắp xếp vectơ
Tham số đầu tiên của sắp xếp chỉ định trình vòng lặp ở đầu đối tượng được sắp xếp và tham số thứ hai
chỉ định trình lặp ở cuối (đối tượng sắp xếp không bao gồm phần cuối).
Khi sắp xếp các phần tử mảng, bạn nên thay thế con trỏ như một tham số thực tế thành sắp xếp như
trong chương trình sau.
Sắp xếp của STL dựa trên sắp xếp nhanh, không phụ thuộc vào ảnh hưởng của bộ xử lý, đây là một
phương pháp sắp xếp hiệu quả với độ phức tạp là O(nlogn) Ngoài ra, sort bổ sung cơ chế phản hồi cho
trường hợp xấu nhất là sắp xếp nhanh X-inch, điều này khắc phục khuyết điểm về độ phức tạp cao tới 0
(N^ 2) trong trường hợp xấu nhất. Nhưng lưu ý rằng sắp xếp là một thuật toán sắp xếp không ổn định.
Khi cần giải thuật sắp xếp ổn định, chúng ta có thể chọn stable_sort dựa trên sắp xếp hợp nhất.Tuy
nhiên, mặc dù độ phức tạp của sắp xếp ổn định là O (nlogN), nó đòi hỏi nhiều bộ nhớ hơn sắp xếp và tốc
độ hơi chậm hơn.
7.6 Số thứ tự ngược lại
※ Đây là một thử thách hơi khó. Nếu thấy khó quá, tạm thời có thể bỏ qua, đợi đến khi đủ sức mới thử
thách.
Trong A{ao, a1, a.. an-1} thỏa mãn và i <j, ai>aj thì nhóm số này được gọi là dãy số nghịch, và số dãy số
ngược lại trong dãy số được gọi là dãy số nghịch. Số đảo ngược bằng số lượng trao đổi trong thuật toán
bong bóng sau đây.

Tìm số lùi của dãy đã cho A. Xin lưu ý rằng việc triển khai trực tiếp thuật toán trong mã giả ở trên sẽ gây
ra Vượt quá giới hạn = TLE

Time Limit Exceeded

Đầu vào Nhập độ dài của lực ở dòng đầu tiên và nhập a I (i=0,1,2…,n-1) ở dòng thứ hai, cách nhau bằng
dấu cách. Đầu ra Xuất ra số đảo ngược trên 1 dòng.
giải thích
Nếu sắp xếp bong bóng thực sự được chạy lại, độ phức tạp của thuật toán sẽ đạt 0 (n^ 2) và kết quả
không thể xuất ra trong một thời gian giới hạn, vì vậy ở đây chúng ta phải áp dụng phương pháp chia và
chinh phục. Trên thực tế, miễn là hàm hợp nhất của sắp xếp hợp nhất được thao tác một chút, nó có thể
được sử dụng để tìm thứ tự nghịch đảo.
Ví dụ: bây giờ chúng ta yêu cầu số thứ tự nghịch đảo của mảng A = {5, 3, 6, 2, 1, 4}. Như trong hình 7.11,
chúng ta đặt k là chỉ số con của mảng và số phần tử ở bên trái của A [k] và lớn hơn của A[k] là C [k]

C [k] là số nghịch đảo của mảng A

Số nghịch đảo trong ví dụ trên là 10. Vì giá trị của C [i] có thể được giải quyết theo bất kỳ thứ tự nào,
chúng tôi áp dụng ý tưởng sắp xếp hợp nhất ở đây, đề cập đến thuật toán thiết kế mã giả của hợp nhất
và hợp nhất trong merge sort.
Trước hết, như hình 7.12 Ta chia nhóm d thành hai điểm đồng phẳng và xét C[K] của mỗi phần].

Chúng ta sắp xếp các mảng L và R và tìm số thứ tự nghịch đảo tương ứng của chúng, và chúng ta
biết rằng tổng các số thứ tự nghịch đảo của L và R là 2.
Tiếp theo, như trong Hình 7.13, hợp nhất các mảng đã sắp xếp và đếm các số nghịch đảo cùng
một lúc. Giả sử độ dài của L là n1 và chỉ số con hiện tại R như là tính n1-i khi hợp nhất từng
phần tử trong mảng R [j], bạn có thể biết sẽ có bao nhiêu phần tử
Phần tử di chuyển về phía sau của R [j], tức là có bao nhiêu số đảo ngược hiện có liên quan đến R [j].
7.13 Tính toán số thứ tự nghịch đảo (3)
Số thứ tự nghịch đảo liên quan đến các phần tử {1, 2, 4} trong R lần lượt là {3, 3, 2}, cộng với số thứ tự
ban đầu thu được là 2, ta có thể thấy rằng số thứ tự nghịch đảo của mảng A tổng thể là 3 + 3 + 2 + 2 =
10 .

Minimum Cost

Có n hàng hóa có trọng lượng w, (í = 0, 1,.. n-1) liên tiếp. Bây giờ chúng ta cần sử dụng cánh tay robot
để đặt những hàng hóa này

Sắp xếp. Mỗi hoạt động của cánh tay robot có thể nâng hàng hóa thứ i và hàng hóa và hoán đổi vị trí của
chúng, đồng thời phải chịu chi phí wi + wj ,. Không giới hạn số lần thao tác.

Yêu cầu tổng chi phí tối thiểu cần thiết để sắp xếp hàng hóa đã cho theo thứ tự trọng lượng tăng dần.

Ví dụ đầu vào
Để nhập một số nguyên vào dòng 1, bạn phải nhập n số nguyên vào dòng 2. Wi (i= 0, 1,.. n-1) , được
phân tách bằng dấu cách.

Đầu ra

Nhập giá trị nhỏ nhất trong 1 dòng.

giới hạn

1 <= n <= 1000

0 <= wi <=10^4

Các giá trị của wi không lặp lại nhau

giải thích

Chúng tôi lấy w = {4, 3, 2, 7, 1, 6, 5} làm ví dụ để phân tích. Mục tiêu bây giờ là tìm chi phí tối thiểu cần
thiết để sắp xếp lại W thành {1, 2, 3, 4, 5, 6, 7}. Đầu tiên hãy vẽ một bức tranh và đánh dấu vị trí cuối
cùng mỗi phần tử sẽ di chuyển (Hình 7.14)

Hình 7.14 Phân loại chi phí thấp nhất (Ví dụ 1)


Chúng ta sẽ thấy rằng một số vòng tròn khép kín xuất hiện trong hình. Có ba vòng tròn trong ví dụ này,
đó là 4-7-5-1-> 4, 3 -> 2-3, 6-6. Bây giờ chúng ta hãy phân tích chi phí tối thiểu cần thiết cho mỗi vòng
tròn.

Đường tròn có độ dài bằng 1, tức là đường tròn không cần di chuyển, có giá trị bằng 0.

Đường tròn có độ dài 2 chỉ cần đổi một lần là đủ để mỗi phần tử đến vị trí cuối cùng nên tổng của hai
phần tử là giá thành. Ví dụ, chi phí của đường tròn 3 -> 2-> 3 là 3 + 2 = 5.

Sau đó là một hình tròn có độ dài từ 3 trở lên. Như trong Hình 7.15, khi xử lý vòng tròn 4 -> 7-5-1-4, việc
di chuyển các phần tử khác theo 1 có thể đảm bảo chi phí thấp nhất.

Nhân vật

Hình 7.15 Tính toán chi phí tối thiểu

Nói cách khác, cách tốt nhất trong trường hợp này là di chuyển các phần tử khác theo giá trị nhỏ nhất
trong hình tròn.

Giả sử các phần tử trong hình tròn là Chi, và số phần tử trong hình tròn là Hồng.
Vì mỗi phần tử di chuyển ít nhất một lần nên có một vấn đề. Ngoài ra, giá trị nhỏ nhất phải di chuyển

(n-2) lần trước lần trao đổi cuối cùng, do đó có (n-2) X min (wi) Công thức trên cũng đúng khi n = 2.

Cộng chi phí của mỗi vòng tròn với nhau, chi phí tối thiểu của w = {4,3,2,7,1,6,5} là (5 + 0) + (17 + 2) = 24.

Mặc dù phương pháp này có thể tìm thấy chi phí tối thiểu của ví dụ trên, nhưng nó vẫn có các ví dụ phản
diện. Chúng ta hãy phân tích ví dụ trong hình 7.16.

Nếu thuật toán trên được áp dụng cho đầu vào này, chi phí của 1->2->1 là 3, chi phí của 8-> 10 —9-> 7->
8 là 48 và tổng chi phí là 51.

Tuy nhiên, nếu chúng ta đổi 7 và 1 trước, và thay đổi vòng tròn 8 -> 10-> 9-> 7-> 8 thành 8 -> 10-> 9-> 1
^ 8, chi phí của phần này trở thành 28+ 2 X 1 = 30. Sau đó, cộng chi phí của hai lần trao đổi là 7 và 1 và
vòng 1 1->2->1 và tổng chi phí là 49. Có thể thấy rằng ngay cả khi chúng ta cộng thêm chi phí của hai lần
trao đổi là 1 và 7 thì tổng chi phí vẫn nhỏ hơn so với thuật toán trước, nói cách khác, đôi khi việc mượn
các yếu tố từ vòng tròn để di chuyển có thể khiến chi phí thấp hơn.

Cho phần tử bên ngoài đường tròn là Xo Chi phí đi vay tăng lên là 2x (min (Wi) + x), và chi phí tiết kiệm
được là (n-1) X (min (wi) -X) Tại thời điểm này, tổng chi phí của phần này là

Có thể thấy rằng phần tử nhỏ nhất trong toàn bộ dữ liệu đầu vào nên được chọn.

Tính đến các bài toán trên, chương trình cần tính toán hai trường hợp “mượn tổng thể phần tử nhỏ nhất”
và “không mượn phần tử”, chọn trường hợp có chi phí thấp hơn.

Câu trả lời tham khảo


Cây là một cấu trúc dữ liệu được sử dụng để thể hiện cấu trúc phân cấp, trong phát triển phần mềm, cấu
trúc cây thường được sử dụng để thể hiện một cách trừu tượng cấu trúc phân cấp như tài liệu, biểu đồ
tổ chức, đồ họa và hình ảnh.

Ngoài ra, cấu trúc cây là cơ sở để hiện thực hóa các thuật toán và cấu trúc dữ liệu hiệu quả, và là một
khái niệm không thể thiếu trong xử lý thông tin và thiết kế chương trình. Nhiều thuật toán và cấu trúc
dữ liệu được cung cấp trong thư viện chuẩn có liên quan đến cấu trúc cây.

Chương này sẽ dẫn dắt bạn tìm hiểu các phương thức biểu đạt của cây và một số thuật toán cơ bản
trong cấu trúc cây thông qua các ví dụ.,

Trước khi chạm đến các câu hỏi trong chương này, bạn cần nắm vững các kỹ năng lập trình liên quan
đến mảng, xử lý vòng lặp, cấu trúc (lớp), ngoài ra bạn cũng cần nắm được kiến thức về hàm đệ quy.

Trước khi thử thách cấu trúc cây vấn đề

Cây rễ

Cấu trúc cây là một cấu trúc dữ liệu, cấu trúc này bao gồm các nút và các cạnh kết nối các nút. Như trong
hình 8.1, chúng ta sử dụng các vòng tròn để biểu diễn các nút và các đường để biểu diễn các cạnh.
Nhân vật

8.1 Ví dụ về cây

Như trong Hình 8. 2, nếu một cây có một nút đặc biệt có tên là "gốc", thì cây này được gọi là cây có gốc.

Có một mối quan hệ cha-con giữa các nút của cây gốc. Giả sử một cây gốc có tám gốc và cạnh cuối cùng
trên đường đi từ gốc đến nút U nối nút P và nút X. Lúc này, ta gọi P là cha parent của X, và gọi X Là nút
con child của P. Trong Hình 8. 2, nút cha của nút 2 là 0 (gốc), và các nút anh em là 1 và 3o

Qua hình vẽ có thể thấy rằng gốc là nút duy nhất không có nút cha. Chúng ta gọi một nút không có các
nút con là nút hoặc lá bên ngoài. Các nút khác với nút lá được gọi là nút bên trong (nút bên trong –
internal node)
Số lượng nút con của nút x trong cây gốc được gọi là bậc X. Ví dụ, nút 2 có ba nút con là 6, 7 và 8, vậy
bậc của nó là 3. Nếu một nút không có nút con, thì bậc của nó là 0 ',

Chiều dài của đường đi từ cây rễ đến nút được gọi là chiều sâu depth của X. Ngoài ra, chiều dài đường đi
tối đa từ nút đến nút lá được gọi là chiều cao height của nút. Chiều cao của nút gốc trong cây Chiều cao
nhất, chúng tôi còn gọi là chiều cao của cây. Ví dụ, độ sâu của nút 8 trong hình là 2 và chiều cao của cây
là 3.

Cây nhị phân

Nếu một cây có 1 nút gốc,

Và số lượng nút con của tất cả các nút không vượt quá 2,

Sau đó, cây này được gọi là rễ nhip phân

Hình 8.3 là một ví dụ về cây nhị phân.


Trong cây nhị phân, mỗi nút có không quá hai nút con và có các nút con bên trái và các nút con bên phải.
Nói cách khác, khi một nút chỉ có một nút con, cần phải phân biệt chặt chẽ xem đó là nút con trái hay
nút con phải. Trong Hình 8.3, nút 6 của (a) là nút con bên trái của nút 3 và nút 6 của (b) là nút con bên
phải của nút 3. Chúng tôi gọi loại cây có các nút con theo thứ tự cụ thể là cây có thứ tự.

Cây nhị phân có thể được định nghĩa một cách đệ quy. Cây đáp ứng một trong các điều kiện sau đây là
cây nhị phân.

►T không có bất kỳ nút nào

►T gồm ba tập đỉnh sau không chứa phần tử chung

-Nguồn gốc

Cây nhị phân được gọi là cây con bên trái left subtree

-Cây nhị phân được gọi là cây con bên phải right subtree

Hình 8.4 Cây con của cây nhị phân


Viết chương trình xuất ra thông tin của từng nút u trong cây gốc T cho trước nội dung thông tin như sau

1.Số nút của u

2.Các loại nút của u (gốc, nút bên trong , lá)

3.Số nút cha của u

4.Danh sách nút con của u

5.Độ sâu

Ở đây chúng tôi giả định rằng cây đã cho có nnút, được đánh số từ 0 đến n-1

Nhập số nút vào hàng đầu tiên Nhập thông tin của mỗi nút vào n hàng sau Mỗi nút chiếm một hàng.

id k c1 c2 .. ck

Thêm vào là số của nút, và k là độ c1 c2 .. ck là số từ nút con thứ nhất đến nút con thứ k. Đầu ra Vui lòng
xuất thông tin nút ở định dạng sau. Thông tin về nút được sắp xếp theo thứ tự tăng dần theo số.

id nút: cha = p, depth = d, type, [c1, … ck]

p đại diện cho số của nút cha [khi không có nút cha, đầu ra -1. D cho biết độ sâu của cụm loại nút ° chỉ ra
loại nút, chọn một trong ba chuỗi ký tự gốc (root), nút bên trong (nút bên trong internal node ) và lá
(leaf).

Chọn một trong ba chuỗi.

C1.. ck là danh sách các nút con. Ở đây chúng tôi coi cây đã cho là cây đặt hàng, vui lòng xuất theo thứ
tự đầu vào. Thông tin liền kề được phân tách bằng dấu phẩy và dấu cách. Hãy chắc chắn chú ý đến định
dạng trong ví dụ đầu ra.

1 <= n <= 100000

Độ sâu của nút không vượt quá 20


giải thích

Đầu tiên chúng ta phải xem xét cách lưu trữ cây gốc đầu vào. Trong câu hỏi này, số lượng nút trong cây
không thay đổi sau khi nhập xong, vì vậy có thể sử dụng "biểu diễn anh chị em bên phải trái-con" (left-
child right- right sibling representation) để biểu diễn cây. 2 nút trong ký hiệu anh em bên trái con bên
phải có thông tin sau.

►Node cha của nút M

• Nút "nút con ngoài cùng bên trái

►Node anh chị em ở phía bên phải của nút X

Lấy C ++ làm ví dụ, ký hiệu con bên trái bên phải có thể được triển khai bằng cách sử dụng một mảng
cấu trúc hoặc ba mảng, như sau
Sử dụng u. Cha để biết nút cha của mỗi nút u. Gốc không có nút cha. Ngoài ra, nút không có u.left là một
lá và nút không có u.right là nút con ngoài cùng bên phải. Để chỉ ra rằng không có nút cha,

Trong trường hợp nút con bên trái và nút anh em bên phải, chúng ta sử dụng giá trị NIL làm số nút đặc
biệt. Tại thời điểm này, hãy đảm bảo rằng NIL không được sử dụng làm số nút chung '

Độ sâu của mỗi nút có thể thu được bằng thuật toán sau.

Khi tìm độ sâu của nút, cần bắt đầu từ u để lần lượt tìm các nút cha và đếm tổng số cạnh đã đi từ gốc
đến gốc. Ở đây chúng tôi đặt nút cha của gốc là NIL (= -1) để phân biệt với các nút khác.

Ngoài ra, sử dụng thuật toán đệ quy sau đây có thể nhanh chóng tìm ra độ sâu của tất cả các nút trong
cây.

Thuật toán trên sẽ tính toán đệ quy độ sâu của nút anh em bên phải và nút con ngoài cùng bên trái. Ở
đây T được nhận ra bởi ký hiệu nút con bên trái. Nếu nút hiện tại có nút con bên phải, thì lệnh gọi đệ
quy được thực hiện trực tiếp mà không làm thay đổi độ sâu P. Nếu có nút con ngoài cùng bên trái, độ
sâu được tăng lên 1 trước khi tiếp tục. Cuộc gọi đệ quy.

Danh sách nút con của nút U được nhập theo thứ tự từ nút con bên trái của W cho đến khi nút con hiện
tại không có nút anh em bên phải.
Khảo sát

Hãy để chúng tôi phân tích thuật toán tìm độ sâu của mỗi nút và xem nó phức tạp như thế nào. Giả sử
chiều cao của cây là cái vuốt, thì độ phức tạp của thuật toán tìm nút cha theo thứ tự bắt đầu từ mỗi nút
là 0 (hầm, vì vậy khi tìm độ sâu của tất cả các nút, độ phức tạp của thuật toán là 0 (h). Câu hỏi này là về
độ sâu của mỗi nút Do đó, có thể áp dụng thuật toán đơn giản này.

Ngược lại, thuật toán độ sâu tính toán đệ quy chỉ cần duyệt qua mỗi nút một lần nên độ phức tạp của
thuật toán là O(n)

Câu trả lời tham khảo

Cho một cây nhị phân gốc T, hãy viết một chương trình để xuất ra các thông tin
sau của mỗi nút U.

►số nút của u

►Độ sâu của u

►Nút cha của U

►Chiều cao của u


►U's brother node

►Loại nút (gốc, nút bên trong, lá)

► Số nút con của u

Giả sử một cây nhị phân đã cho có n nút, được đánh số từ 0 đến n-1.

Nhập số nút vào dòng đầu tiên. Dòng tiếp theo là nhập thông tin của từng nút
theo định dạng sau, mỗi

Nút chiếm 1 hàng.

Id left right

id là số nút, left là số nút con bên trái và ng được thêm vào số nút con bên phải.
Khi không có nút con, trái (phải) là -1.

Đầu ra Thông tin nút xuất theo k định dạng sau.

id nút: parent = p, sibling = s, degree = độ, depth = dep, độ cao = h, type

Trong đó p đại diện cho kết cấu w của nút cha và điểm của chim cha được ghi là -1
khi 2 không được lưu trữ. Số của nút anh em được hiển thị trong GAO, được ghi
lại là -1 khi nút anh chị em không tồn tại.

deg, dep và h lần lượt thể hiện số lượng, độ sâu và chiều cao của các nút con. Ví
dụ, type đại diện cho loại nút, hãy chọn một trong ba chuỗi: gốc, nút bên trong
(internal node) và lá.

Vui lòng đọc kỹ ví dụ đầu ra và chú ý đến định dạng đầu ra như dấu cách
1 <= n <=25

giải thích

Trong câu hỏi này, số lượng nút của cây nhị phân là cố định, vì vậy nó có thể được
thực hiện bằng cách sử dụng một mảng cấu trúc, như sau.

Struct Node{

Int parent, left , right;

};

Như trong Hình 8 6, khi nút trong phần triển khai trên không có nút con trái / phải,
chúng ta đặt trái / phải thành NIL. NIL đóng vai trò là một điểm đánh dấu ở đây và
giá trị của nó không nằm trong phạm vi của số nút, chẳng hạn như- 1.

Thuật toán đệ quy sau có thể tìm chiều cao của nút u

Khảo sát

Khi tính toán chiều cao của nút hiện tại, trước tiên bạn chỉ cần tính "cao +1 của
nút con bên trái" và "chiều cao của nút con bên phải +1", sau đó chọn cái lớn hơn.
Thuật toán để tìm một nút cao trong câu hỏi này là thực hiện xử lý trên một cách
đệ quy. Thuật toán này sẽ truy cập vào mỗi nút một lần, do đó, độ phức tạp là O(n)
Tree Walk

Hãy viết chương trình theo thuật toán sau để truy cập một cách có hệ thống cây
nhị phân cho trước

1. Xuất ra số nút theo thứ tự của nút gốc , cây con bên trái và cây con bên
phải. Đây được gọi là truyền thứ tự trước của cây (Preorder Tree Walk)
2. Xuất ra số nút theo thứ tự của cây con bên trái , nút gốc và cây con bên
phải. Đây được gọi là trình duyệt cây (Inorder Tree Walk)
3. Xuất ra số nút theo thứ tự của cây con bên trái, cây con và nút gốc . Đây
được gọi là truyền thứ tự của cây (Postorder Tree Walk)

Input n Nhập số nút ở dòng đầu tiên Tiếp theo nhập thông tin của từng nút theo
định dạng sau, mỗi nút chiếm một dòng.

id le ft right

Thêm nó vào số nút 2, không đánh số nút con bên trái và vào số nút con bên phải.
Khi không có nút con trái (phải) là -1.

Dòng đầu ra in ra Preorder . Dòng 2 xuất ra số nút theo thứ tự truyền tải trước.

Dòng thứ ba xuất ra "Inorder" và dòng thứ tư xuất ra các số nút theo thứ tự của
trình duyệt thứ tự giữa.
Dòng thứ năm xuất ra "Postorder" và dòng thứ sáu xuất ra các số nút theo thứ tự
truyền tải thứ tự sau.

Đầu ra 1 trống trước số nút

1 <= n <= 25
Lấy preParse (u) làm ví dụ, chương trình truy cập đầu tiên u (ở đây, print U được
sử dụng để xuất ra số nút), sau đó thực thi preParse (T [u] .left) để truy cập vào
cây con bên trái của U và sau đó thực thi preParse (T [ u] .right) Ghé thăm cây con
bên phải của U. Khi U bằng NIL, nghĩa là không có nút tiếp theo và hàm kết thúc.

Tương tự, miễn là chúng ta thay đổi vị trí của chữ in U, chúng ta có thể triển khai
các thuật toán duyệt khác nhau. Inorder traversal đặt chữ in U ở giữa việc đi
ngang qua cây con bên trái và đi qua cây con bên phải, trong khi phương thức
Postorder traversal là thực hiện in U sau khi đi qua cây con bên trái và cây con bên
phải.

Khảo sát

Việc duyệt qua cây nhị phân sẽ đến thăm mỗi nút của cây một lần, do đó độ phức
tạp của thuật toán là 0 (n). Tuy nhiên, khi sử dụng đệ quy để thực hiện thuật toán
duyệt, hãy lưu ý rằng một khi số lượng nút trong cây lớn và phân bố không đều,
nó có thể làm cho độ sâu của đệ quy quá sâu.

Câu trả lời tham khảo

Ứng dụng của việc tái tạo cây đi ngang qua cây

※ Đây là một thử thách hơi khó. Nếu cảm thấy quá khó, bạn có thể bỏ qua và
quay lại thử thách khi còn sức.
Có hai trình tự nút, là kết quả của trình tự duyệt ngang và trình tự giữa của cùng
một cây nhị phân. Hãy viết chương trình xuất chuỗi nút khi cây nhị phân được
duyệt theo thứ tự sau.

Đầu vào Dòng 1 nhập số nút trong cây nhị phân

Trên dòng thứ hai, nhập chuỗi số nút của đường truyền trước đó và các số liền kề
được phân tách bằng dấu cách "

Nhập chuỗi số nút cho truyền lệnh giữa ở dòng 3 và phân tách các số liền kề bằng
dấu cách.

Số nút là một số nguyên từ 1 đến n. Xin lưu ý rằng 1 không nhất thiết phải là nút
gốc.

Đầu ra Xuất ra chuỗi số nút khi duyệt theo thứ tự bài trên 1 dòng. Sử dụng 1 dấu
cách để phân tách các số nút liền kề.

Giới hạn 1 W số nút W 100

giải thích

Đặt trước duyệt đệ quy theo thứ tự gốc T ■ cây con bên trái 1 ► cây con bên
phải, và Inorder duyệt đệ quy theo thứ tự cây con bên trái 1> gốc 1 ► cây con
bên phải. Ví dụ: đầu vào của Đặt hàng trước là trước {1, 2, 3, 4, 5, 6, 7, 8, 9} và
đầu vào của Inorder là = {3, 2, 5, 4, 6, 1, 8 , 7, 9}, cây nhị phân được hiển thị trong
Hình 8 11 có thể được tạo.

Đầu tiên hãy truy cập từng nút theo trình tự theo thứ tự Truyền tải trước. Trong
quá trình truy cập, chúng ta có thể tìm hiểu thứ tự của phép duyệt Inorder trong
mỗi cây con bằng cách thêm, để tạo lại cây con bên trái và cây con bên phải bắt
nguồn từ nút c hiện tại.

Nói cách khác, giả sử nút hiện tại được Preorder duyệt qua là c, vị trí của C trong
phép cộng là m, phía bên trái của m là cây con bên trái của C và phía bên phải là
cây con bên phải, sau đó đệ quy theo cùng một cách.

Ví dụ, nút hiện tại là 1 và vị trí của nó trong là 3 2 5 4 6 [1] 8 7 9, thì gốc của cây
hiện tại là 1, và các cây con trái và phải là 3 2 5 4 6 và 87 9. Tiếp theo, trong cây
gồm 3 2 5 4 6, nút tiếp theo 2 của đường truyền Đặt hàng trước là gốc (3 [2] 5 4 6),
3 và 5 4 6 là hai cây con

Nếu các chỉ số con l và r của ìn được sử dụng để chỉ ra phạm vi của cây con hiện
đang được truy cập bởi Đặt hàng trước (không bao gồm r), thì hàm đệ quy này có
thể được thực hiện theo cách sau.
Khảo sát

Thuật toán này cần thực hiện tìm kiếm tuyến tính với độ phức tạp 0 (n) cho mỗi
lớp đệ quy, do đó, độ phức tạp trong trường hợp xấu nhất là O(n^2)

Danh sách liên kết cho phép chúng ta thêm, xóa và tìm kiếm dữ liệu. Cấu trúc dữ
liệu này sẽ áp dụng cho không gian bộ nhớ cần thiết trong quá trình thực thi để
tạo điều kiện quản lý các tập hợp động. Tuy nhiên, độ phức tạp của thuật toán khi
tìm kiếm các phần tử trong danh sách liên kết là 0 (N)). Ngược lại, sử dụng cấu
trúc cây động có thể thêm, xóa và tìm kiếm dữ liệu hiệu quả hơn.

Trong chương này bạn sẽ trả lời cấu trúc dữ liệu của việc quản lý các tập hợp
động cùng nhau . Chủ đề có liên quan đến cây tìm kiếm dọc

Cây tam thức

Cây tìm kiếm sinh ba một


Tìm kiếm lại cây

TÌm kiếm chéo

Trước khi tiếp xúc với các câu hỏi trong chương này, bạn cần nắm vững cấu trúc
cây và các kiến thức liên quan về cây nhị phân, đồng thời cần có kỹ năng lập trình
để thực hiện danh sách liên kết

Trước khi thử thách cây tìm kiếm nhị phân vấn đề

Cây tìm kiếm là một cấu trúc dữ liệu có thể được chèn, tìm kiếm, xóa, ... Nó có thể
được sử dụng như một từ điển hoặc hàng đợi ưu tiên Cây tìm kiếm nhị phân
thuộc cây tìm kiếm cơ bản nhất.

Mỗi nút của cây tìm kiếm nhị phân có một giá trị khóa và cây thường thỏa mãn
thuộc tính cây tìm kiếm nhị phân sau (Thuộc tính cây tìm kiếm nhị phân) 5

A gọi X là nút của cây tìm kiếm nhị phân. Nếu F là một nút trong cây con bên trái,
thì giá trị khóa của y là giá trị khóa của x. Ngoài ra, nếu Z là một nút trong cây con
bên phải, thì giá trị khóa của X là giá trị khóa của x <=z

Hình 9.1 là một ví dụ về cây tìm kiếm nhị phân.


Lấy nút có giá trị khóa là 80 làm ví dụ, giá trị khóa của tất cả các nút trong cây con
bên trái của nó không lớn hơn 80 và giá trị khóa của tất cả các nút trong cây con
bên phải không nhỏ hơn 80. Nếu cây tìm kiếm nhị phân đang được thực thi Duyệt
đơn hàng, chúng ta sẽ nhận được một chuỗi khóa-giá trị theo thứ tự tăng dần.

Khi thực hiện cây tìm kiếm nhị phân, cần đảm bảo rằng sau khi dữ liệu được chèn
hoặc xóa, tất cả các nút vẫn duy trì các thuộc tính trên: giống như một bảng,
chúng ta kết nối các nút thành một cây thông qua các con trỏ, và mỗi nút chứa
một giá trị (giá trị khóa) Và con trỏ đến nút cha, nút con bên trái và nút con bên
phải.

Binary Search Tree 1

Phần chèn hiển thị trong mã giả sau đây được sử dụng để chèn giá trị mới V chèn
vào cây tìm kiếm nhị phân T. Giá trị khóa là V, cây con bên trái là NIL và cây con
bên phải là NIL. Điểm Z được sử dụng làm tham số thực. Chèn T là thích hợp Chức
vụ.
Hãy viết chương trình thực hiện các lệnh sau trên cây tìm kiếm nhị phân.

►insert k: chèn giá trị khóa trong T

►print: sử dụng thuật toán duyệt bậc giữa và thuật toán duyệt bậc trước của cây
để xuất ra các giá trị khóa tương ứng

Vui lòng làm theo mã giả ở trên để triển khai thuật toán chèn.

Nhập liệu Nhập số lượng lệnh trong dòng đầu tiên Tiếp theo, thêm một dòng để
nhập lệnh theo định dạng chèn k hoặc in. Mỗi lệnh chiếm một dòng.
Đầu ra Mỗi khi lệnh in được thực hiện, các chuỗi khóa-giá trị thu được bởi thuật
toán duyệt bậc giữa và thuật toán duyệt bậc trước tương ứng được xuất ra và mỗi
chuỗi chiếm một dòng. Xuất ra 1 khoảng trắng trước mỗi giá trị khóa.

Giới hạn Số lượng lệnh không vượt quá 500 000O Ngoài ra, số lượng lệnh in
không vượt quá 10.

-2 000 000 000 <=k <= 2 000 000 000

Khi sử dụng thuật toán hiển thị trong đoạn mã giả trên, chiều cao cây không được
vượt quá 100.

Giá trị khóa của mỗi nút trong cây tìm kiếm nhị phân không được lặp lại.

Lấy C ++ làm ví dụ, chúng tôi xác định các cấu trúc sau đây là các nút và kết nối
chúng thông qua các con trỏ để tạo thành một cây tìm kiếm nhị phân.

Cấu trúc Node chứa 4 thành viên, đó là giá trị khóa, con trỏ tới nút cha ★ cha,
con trỏ tới nút con bên trái * left. Con trỏ tới nút con bên phải * right

Thao tác chèn cần chèn dữ liệu đã cho vào vị trí thích hợp theo tiền đề đảm bảo
chất lượng của cây tìm kiếm nhị phân. Ví dụ, chúng ta lần lượt chèn các nút có giá
trị khóa {30, 88 12, 1, 20, 17, 25} vào một cây trống và quá trình tạo cây tìm kiếm
nhị phân được thể hiện trong Hình 9.2.

9.2 Chèn các phần tử vào cây tìm kiếm nhị phân

Chèn lấy gốc làm điểm bắt đầu để tìm vị trí mà nút Z cần được chèn. Giả sử nút
hiện tại là x, nếu giá trị khóa của Z nhỏ hơn x, nút con bên trái của nút hiện tại
được lấy làm hai x tiếp theo, ngược lại, nút con bên phải được lấy làm x tiếp theo,
và cứ thế, nút lá được tìm kiếm liên tục. Trong quá trình này, chương trình lưu nút
trước đó trong y và sử dụng nó làm nút cha ứng cử viên của Z. Khi X đến NIL, quá
trình tìm kiếm kết thúc và Y bây giờ là nút cha của Z.

Nếu Y vẫn là giá trị ban đầu NIL khi kết thúc tìm kiếm, điều đó có nghĩa là cây nhị
phân trước khi chèn là trống và Z trở thành nút gốc. Nếu cây nhị phân trước khi
chèn không trống, thì nút Z được chèn vào sẽ trở thành nút con bên trái hoặc nút
con bên phải của y theo giá trị khóa.

Khảo sát
Nếu chiều cao của cây là hình vuông thì độ phức tạp của thao tác chèn các phần
tử vào cây tìm kiếm nhị phân là 0 (h). Nói cách khác, nếu số lượng nút dài đến
mức đầu vào đủ cân bằng, thì độ phức tạp của thuật toán là O (log n). Nói chung,
giá trị khóa và thứ tự chèn các nút có thể làm cho chiều cao cây tăng lên và trong
trường hợp xấu nhất, cây Cao có thể gần bằng số điểm Độ phức tạp thuật toán lúc
này là O(n). Để giải quyết vấn đề này, cây tìm kiếm nhị phân cần được cân bằng
tốt, nhưng cuốn sách này chỉ giới thiệu các phương pháp thực hiện đơn giản.

Binary Search Tree 2

Hãy viết một chương trình, thêm lệnh tìm trên cơ sở A: Cây tìm kiếm nhị phân I và
thực hiện lệnh sau trên cây tìm kiếm nhị phân T.

►insert k: chèn phímk vào T

►tìm k: Báo cáo xem giá trị khóa k có được đưa vào T hay không

►print: sử dụng thuật toán duyệt qua bậc giữa và thuật toán duyệt bậc trước của
cây để xuất ra các giá trị chính

Đầu vào Dòng 1 số lệnh nhập cộng. Tiếp theo, thêm một dòng để nhập lệnh theo
định dạng insert k, find k ,print, mỗi lệnh chiếm một dòng.

Kết quả Sau khi lệnh find k được thực hiện một lần, yes được xuất ra khi T chứa k,
ngược lại no là output Mỗi lệnh chiếm một dòng.
Ngoài ra, mỗi khi lệnh in được thực thi, các chuỗi khóa-giá trị thu được bởi thuật
toán duyệt bậc giữa và thuật toán duyệt bậc trước sẽ được nhập tương ứng và
mỗi chuỗi chiếm một dòng. Xuất ra 1 khoảng trắng trước mỗi giá trị khóa.

Giới hạn Số lượng lệnh không vượt quá 500 000O Ngoài ra, số lượng lệnh in
không vượt quá 10.

-2 000 000 000 <=k <= 2 000 000 000

Khi sử dụng thuật toán hiển thị trong đoạn mã giả trên, chiều cao cây không được
vượt quá 100.

Giá trị khóa của mỗi nút trong cây tìm kiếm nhị phân không được lặp lại.

Thao tác tìm là find nút có khóa k được chỉ định trong cây tìm kiếm nhị phân.
Thuật toán như sau
Chúng tôi gọi tìm kiếm với gốc là điểm bắt đầu và find các nút từ gốc đến lá. Nếu
giá trị khóa đã cho nhỏ hơn giá trị khóa của nút hiện tại X, thì mục tiêu tìm kiếm
sẽ di chuyển đến nút con bên trái để tiếp tục tìm kiếm, ngược lại nó sẽ di chuyển
đến nút con bên phải. NIL được trả về khi giá trị khóa không tồn tại.

Khảo sát

Giả sử chiều cao của cây là h, và độ phức tạp của thuật toán của phép toán tìm
cũng giống như của insert O(h)

Binary Search Tree 3

Hãy viết chương trình thêm lệnh xóa trên cơ sở B: Cây tìm kiếm nhị phân II và
thực hiện các lệnh sau trên nhà máy cây tìm kiếm nhị phân.

►insert k: chèn giá trị khóa k vào T

►tìm k: Giá trị khóa k có được đưa vào T hay không?

►xóa k: xóa nút chứa khóa k

►print: sử dụng thuật toán duyệt bậc giữa và thuật toán duyệt bậc trước của cây
để xuất ra các giá trị khóa tương ứng

Lệnh delete k được sử dụng để xóa nút z chứa khóa k đã cho khỏi cây tìm kiếm
nhị phân T. Khi xóa z, cần thảo luận ba trường hợp theo thuật toán sau để đảm
bảo cây vẫn giữ được bản chất của cây tìm kiếm nhị phân sau khi liên kết (con trỏ)
được cập nhật.

1.Khi Z không có nút con, hãy xóa các nút con của nút cha P của nó (nghĩa là Z).
2.Khi Z có nút con thì đổi nút con của nút cha của Z thành nút con, đồng thời đổi
nút cha của nút con thành nút cha của Z, rồi xóa Z khỏi cây.

3.Khi Z có hai nút con, hãy sao chép giá trị khóa của nút tiếp theo y của Z sang Z,
sau đó xóa bước 1 và 2 cho thao tác xóa của y. Ở đây "nút tiếp theo của Z" đề cập
đến nút đầu tiên z sau Z trong trình tự giữa

Đầu vào Dòng 1 số lệnh nhập cộng. Tiếp theo, nhập các lệnh theo định dạng

insert k ,f ind k , delete k , print, mỗi lệnh chiếm một dòng.

Kết quả Sau khi lệnh find k được thực hiện một lần, khiT chứa k, nó sẽ xuất ra yes,
ngược lại, nó sẽ xuất ra no, mỗi lệnh chiếm một dòng.

Ngoài ra, mỗi khi lệnh print được thực thi, thuật toán duyệt bậc giữa và thuật
toán duyệt bậc trước lần lượt được xuất

Chuỗi khóa-giá trị kết quả, mỗi chuỗi chiếm 1 hàng. Xuất ra 1 khoảng trắng trước
mỗi giá trị khóa. Giới hạn Số lượng lệnh không vượt quá 500 000. Ngoài ra, số
lượng lệnh in không vượt quá 10.

-2 000 000 000 <= k <= 2 000 000 000

Khi sử dụng thuật toán được hiển thị trong mã giả ở trên, chiều cao cây không
được vượt quá 100,

Giá trị khóa của mỗi nút trong cây tìm kiếm nhị phân không được lặp lại
giải thích

Hàm deleteNode được sử dụng để xóa nút z khỏi cây tìm kiếm nhị phân T. Thuật
toán như sau.

Chương trình 9.3 xóa nút của cây tìm kiếm nhị phân

1deleteNode (T, z)

2// Cho đối tượng xóa là nút y

3if z.left == NIL I I z.right == NIL

4y = z // 2 Khi không có hoặc chỉ có một nút con, đối tượng bị xóa là z

5khác

6y = getSuccessor (z) // Khi z có hai nút con, hãy xóa nút tiếp theo của z

7số

8// Xác định nút con X của y


9nếu y.left! = NIL

10X = y. Left // Nếu y có nút con bên trái thì X là nút con bên trái của y

11khác

X = y.right

nếu X! = NIL

X.parent

// Nếu y không có nút con bên trái thì X là nút con bên phải của y

y.parent //

Đặt nút cha của X

nếu y.parent == NIL

'root of T' = X // Nếu y là nút gốc thì X trở thành nút gốc của cây

khác nếu y == y.parent.left

y .parent. left = X // Female [] Nếu y là nút con bên trái của nút cha p của nó, thì X
trở thành nút con bên trái của P else

y .parent .right = X // Nếu y là nút con bên phải của nút cha p thì X trở thành nút
con bên phải của P

nếu y! = Z

z.key = y.key

// Khi nút tiếp theo của z bị xóa

// Sao chép dữ liệu của y sang Z


Trong thuật toán trên, mặc dù tham số Z đại diện cho nút bị xóa, nhưng nó cần
được thảo luận trong các tình huống khác nhau như Hình 9.3 trong quá trình thực
thi. Đầu tiên, xác định nút ứng viên sẽ bị xóa
Xóa các phần tử khỏi cây tìm kiếm nhị phân

Khi Z không có nút con (trường hợp 1) hoặc có 1 nút con (trường hợp 2), y là Z.

Ngược lại, khi Z có 2 nút con (trường hợp 3) thì Xi là nút kế tiếp của Z. Ở đây "nút
tiếp theo của Z" đề cập đến nút đầu tiên đến sau Z trong quá trình truyền theo
thứ tự.

Tiếp theo, xác định một nút con của nút y cần xóa. trường hợp 1 là nút con bên
phải

(NIL), trường hợp 2 là Z

Nút con (một trong các nút con trái và phải), trong trường hợp 3 là nút con bên
phải của nút sau Z (nút sau không có nút con bên trái)

Tiếp theo, thay đổi con trỏ của nút cha / con của y và xóa con. Đầu tiên, để con
trỏ của nút cha của x trỏ đến nút cha của y.
Sau đó đổi con trỏ của nút cha thành y để X trở thành nút con của nút cha. Ở đây,
trước khi thay đổi con trỏ, trước tiên hãy kiểm tra xem nút x là nút gốc hay nút
con trái / phải của nút cha của nó.

Cuối cùng, nếu bạn đang xử lý trường hợp 3, bạn cũng cần gán giá trị khóa củay
cho Z

getSuccessor (X) được sử dụng để tìm nút tiếp theo của X, thuật toán là

Đầu tiên, khi X có nút con bên phải, nút có giá trị khóa nhỏ nhất trong cây con bên
phải là nút tiếp theo của U, vì vậy getMinimum (x. Right) được trả về. Khi không
có nút con bên phải, truy vấn hướng lên Nút cha, "nút cha đầu tiên xuất hiện dưới
dạng nút con bên trái" cũng là nút sau. Nếu nút tiếp theo của X không tồn tại
trong cây tìm kiếm nhị phân (X có giá trị khóa lớn nhất trong cây), NIL được trả về.

Như hình dưới đây, getMinimum (x) sẽ tìm kiếm và trả về nút có giá trị khóa nhỏ
nhất trong cây con có gốc tại X.

Khảo sát
Giả sử chiều cao của cây là cái vuốt. Khi xóa một phần tử cụ thể trong cây tìm
kiếm nhị phân, đầu tiên phải tốn 0(h) để tìm nút có giá trị khóa đã cho, sau đó cần
0 (h), vì vậy Độ phức tạp của toàn bộ thuật toán bằng 0(h)

Nói chung, xử lý chèn, tìm và xóa cây tìm kiếm nhị phân sẽ chọn một thuật toán
có độ phức tạp là O (Iogn) để nhận ra rằng S là số nút). Do đó, chúng ta thường
cần nén chiều cao của cây càng nhiều càng tốt. Cây tìm kiếm nhị phân có phân bố
đồng đều được gọi là cây tìm kiếm nhị phân cân bằng.

Quản lý bộ sưu tập thông qua thư viện tiêu chuẩn

Vùng chứa STL quản lý tập hợp các phần tử được chia thành hai loại. Một loại là
tập hợp có thứ tự, được gọi là vùng chứa tuần tự; loại kia là tập hợp được sắp xếp,
được gọi là vùng chứa kết hợp.

Vùng chứa trình tự sẽ đặt phần tử mới được thêm vào một vị trí cụ thể, được xác
định bởi thời gian và địa điểm chèn và khác với phần tử ban đầu

Giá trị của phần nội dung không liên quan. Vectơ và danh sách được giới thiệu
trước đó là các vùng chứa tuần tự đại diện.

Ngược lại, các vùng chứa kết hợp xác định vị trí của các phần tử sẽ được thêm vào
dựa trên các tiêu chí sắp xếp cụ thể. STL cung cấp cho người dùng các vùng chứa
set, map. Multiset, multimap. Sau đây chúng tôi sẽ giới thiệu cách sử dụng set và
map.

Vùng chứa kết hợp tự động sắp xếp các phần tử trong quá trình quản lý dữ liệu.
Mặc dù các vùng chứa tuần tự cũng có thể được sắp xếp, nhưng ưu điểm của
vùng chứa kết hợp là phương pháp tìm kiếm nhị phân có thể được sử dụng bất cứ
lúc nào và hiệu quả của việc tìm kiếm các phần tử là rất cao.

9,5,1 bộ

set là một tập hợp được sắp xếp theo giá trị phần tử, phần tử được chèn là duy
nhất trong tập hợp và không có phần tử trùng lặp
Chương trình 9.6 thực hiện thao tác chèn trên tập hợp, sau đó nhập phần tử.
Giống như vùng chứa tuần tự, vùng chứa kết hợp cũng có thể truy cập tuần tự
từng phần tử thông qua trình lặp
#include <set> được sử dụng để đưa tập hợp STL vào chương trình.

set <int> s; là một khai báo được sử dụng để tạo ra một tập hợp các phần tử kiểu
int. Chúng tôi có thể thực hiện các thao tác và truy vấn khác nhau trên tập hợp
này. Ví dụ về các hàm thành viên được xác định trong tập hợp được trình bày
trong Bảng 9.1.

Bảng 9.1 Các ví dụ về các hàm thành viên của tập hợp

Tập hợp được thực hiện bởi cây tìm kiếm nhị phân và cây được cân bằng, để các
phần tử được phân bố đồng đều hơn trong cây, do đó độ phức tạp của các thao
tác tìm kiếm, chèn và xóa có thể được giữ ở mức O (logn).
Bản đồ 9.5.2

Bộ sưu tập map lấy sự kết hợp của khóa và giá trị làm phần tử, mỗi phần tử có 1
khóa và 1 giá trị và bộ sưu tập lấy giá trị làm tiêu chí sắp xếp. Khóa của mỗi phần
tử trong tập hợp là duy nhất và không có sự trùng lặp. Bản đồ có thể được coi là
một vùng chứa liên kết có thể sử dụng bất kỳ loại chỉ số con nào. Ví dụ, chúng ta
có thể sử dụng nó để thực hiện các chức năng từ điển như "xóa chuỗi khỏi chuỗi".

Chương trình 9.7 sau đây thực hiện thao tác chèn trên map, sau đó nhập các phần
tử.
#include <map> được sử dụng để đưa bản đồ STL vào chương trình.

map <string, int> T; là một khai báo được sử dụng để tạo một mảng kết hợp,
mảng này quản lý các phần tử kiểu int với chuỗi là khóa. Ở đây bạn cần chỉ định
một tập hợp (một cặp) các loại khóa và giá trị trong <>.

Vùng chứa bản đồ có thể truy cập (đọc và ghi) giá trị tương ứng bằng cách chỉ
định khóa trong toán tử "[]" hoặc truy cập tuần tự từng cặp khóa và giá trị thông
qua một trình lặp. Đây cặp là mẫu cấu trúc do STL cung cấp. Phần tử đầu tiên có
thể được truy cập bởi thứ nhất và phần tử thứ hai có thể được truy cập bởi thứ
hai.

Ví dụ về các chức năng thành viên được xác định trong bản đồ được trình bày
trong Bảng 9.2.
Map và set được thực hiện bởi cây tìm kiếm nhị phân cân bằng, do đó độ phức
tạp của các phép toán chèn, xóa, tìm kiếm và "[]" phần tử đều là O (logn).

Bây giờ chúng ta sử dụng bản đồ STL để giải quyết ví dụ trước. Dictionary : Từ
điển có thể được thực hiện bằng các phương pháp sau.
Một loạt trước thử thách

Cây nhị phân hoàn chỉnh

Như thể hiện trong (a) trong Hình 10.1, một cây nhị phân với tất cả các nút lá có
cùng độ sâu và tất cả các nút bên trong có hai nút con được gọi là cây nhị phân
hoàn chỉnh. Ngoài ra, như thể hiện trong ⑹, cây nhị phân Sự khác biệt lớn nhất về
độ sâu của các nút lá là 1 và các nút lá thấp nhất tập trung ở các vị trí ngoài cùng
bên trái của lớp.] Cây nhị phân này cũng (gần đúng) là một cây nhị phân hoàn
chỉnh.

Giả sử số nút là "thì chiều cao của cây nhị phân hoàn chỉnh là log2n.. Sử dụng
thuộc tính này, chúng ta có thể quản lý dữ liệu ở tốc độ cao.

Đống nhị phân

Nếu giá trị khóa của mỗi nút của cây nhị phân hoàn chỉnh và mỗi phần tử của
mảng có mối quan hệ tương ứng như trong Hình 10.2, thì cây nhị phân hoàn
chỉnh là một đống nhị phân

10.2 đống nhị phân


Mặc dù cấu trúc lôgic của một đống nhị phân là một cây nhị phân hoàn chỉnh, nó
thực sự được biểu diễn bằng một mảng một chiều với điểm bắt đầu là 1. Giả sử
rằng kích thước của A đống nhị phân (số phần tử) là H, thì các phần tử của đống
nhị phân được lưu trữ trong A[1,...,H], trong đó chỉ số con của gốc là 1. Khi chỉ số
con của một nút được cung cấp, bạn có thể dễ dàng vượt qua L i/2, 2xi , 2x i+1

Tính toán nút cha của nó là phụ). Nút con bên trái left(i) và nút con bên phải rìght
(i) trong đó Lx ”là phép toán làm tròn, biểu thị số nguyên lớn nhất nhỏ hơn hoặc
bằng số thực X.

Giá trị khóa của mỗi nút được lưu trữ trong heap nhị phân phải đảm bảo rằng
heap có một trong các thuộc tính sau.

► Bản chất đống tối đa: giá trị khóa của nút nhỏ hơn hoặc bằng giá trị khóa của
nút cha của nó

►Tính chất đống tối thiểu: giá trị khóa của nút lớn hơn hoặc bằng giá trị khóa của
nút cha của nó

Heap nhị phân thỏa mãn các thuộc tính của heap tối đa được gọi là heap tối đa và
heap nhị phân được thể hiện trong Hình 10.2 là heap tối đa G

Gốc của đống lớn nhất lưu trữ phần tử lớn nhất. Trong bất kỳ cây con nào, giá trị
khóa của tất cả các nút nhỏ hơn hoặc bằng giá trị khóa của nút gốc của cây con.
Xin lưu ý rằng chỉ các nút cha-con có mối quan hệ kích thước và không có giới hạn
giữa các nút anh chị em.

Sử dụng cấu trúc dữ liệu đống nhị phân, chúng ta có thể loại bỏ (xóa) một cách
hiệu quả phần tử có mức ưu tiên cao nhất hoặc thêm phần tử mới trong khi vẫn
duy trì mối quan hệ kích thước dữ liệu

Complete Binary Tree


Hãy viết một chương trình để đọc đống nhị phân được biểu diễn dưới dạng một
cây nhị phân hoàn chỉnh và xuất ra dữ liệu của từng nút của đống nhị phân ở định
dạng sau.

Node id: key = k , parent key = pk, left key = lk , right key = rk

Trong đó plus là số nút (chỉ số con), k là giá trị của nút, pk là giá trị của nút cha, H
là giá trị của nút con bên trái và phải là giá trị của nút con bên phải. Vui lòng xuất
thông tin theo thứ tự. Ngoài ra, khi nút không tồn tại, không có đầu ra nào được
thực hiện.

Dữ liệu vào 1 Dòng đầu tiên của kích thước đống đầu vào và dòng tiếp theo, nhập
số nguyên H đại diện cho các giá trị nút trong đống nhị phân (giá trị khóa) theo
thứ tự số nút. Các số nguyên liền kề được phân tách bằng dấu cách.

Đầu ra Theo định dạng trên, xuất thông tin của mỗi nút của ngăn xếp nhị phân
theo thứ tự từ 1 đến H. Xin lưu ý rằng hầu hết

Có một khoảng trống sau nó.

giới hạn

H <= 250

2000 000 000 <= Giá trị chính của nút <= 2000 000 000
giải thích

Heap nhị phân được thực hiện bởi một cây nhị phân hoàn chỉnh, do đó, một
mảng một chiều có điểm bắt đầu là 1 có thể được sử dụng để nhập chuỗi giá trị
khóa.

Đầu tiên chúng ta áp dụng ba công thức i i/2, 2*I , 2*i + 1 cho mỗi nút số 2 của
cây nhị phân hoàn chỉnh để tìm số nút cha, nút con trái và phải của nó, sau đó
xuất thông tin về nút theo thứ tự. Xin lưu ý rằng ở đây chúng ta cần kiểm tra xem
số có được theo công thức trên có nằm trong khoảng 1 đến H. không.

Max Heap

Vui lòng viết một chương trình dựa trên mã giả sau để tạo đống tối đa bằng cách
sử dụng mảng đã cho.

maxHeapify (A , i) được sử dụng để tìm vị trí thích hợp của giá trị A[i] từ nút gốc i
đến nút lá, sao cho cây con có Ỉ là nút gốc trở thành đống lớn nhất. Ở đây chúng
tôi đặt kích thước của đống là H.
Sau đây buildMaxHeap (A) áp dụng maxHeapify từ dưới lên để biến đổi mảng
Athành heap tối đa.

1 buildMaxHeap (A)

2 cho ỉ = H / 2 xuống 1

3 maxHeapify (A, i)

Đầu vào 1 Độ dài của mảng đầu vào ở dòng đầu tiên , nhập số nguyên H ở
dòng tiếp theo, lần lượt đại diện cho giá trị của các nút từ 1 đến H trong đống.
Hai số nguyên liền kề được phân cách bằng dấu cách.

Đầu ra Giá trị của các nút từ 1 đến H trong đống lớn nhất được xuất theo thứ
tự và toàn bộ đầu ra chiếm một dòng. Vui lòng nhập 1 khoảng trắng trước mỗi
giá trị.

giới hạn

1 <= H <= 500000


-2000 000 000 <= Giá trị nút <= 2000 000 000

giải thích

maxHeapif y (A, ỉ) sẽ di chuyển giá trị của A[i] đến nút lá cho đến khi nó đáp
ứng thuộc tính đống lớn nhất. Ví dụ: bây giờ chúng ta cần chuyển đổi nhị phân
heap A = {5, 86, 37, 12, 25, 32, 11, 7, 1, 2, 4, 19} thành heap lớn nhất và thực
thi maxHeapify (A, 1) Tình huống được thể hiện trong Hình 10.3

Hình 10.3 Xếp chồng

maxHeapif y (A,i) trước tiên hãy chọn cái lớn hơn trong số các nút con bên trái
và bên phải của i:, nếu giá trị khóa của nó lớn hơn giá trị khóa của nút hiện tại,
hãy trao đổi nó, rồi thực hiện xử lý theo một vòng lặp đi xuống.

buildMaxHeap được sử dụng để chuyển đổi một mảng nhất định thành heap
lớn nhất, vì vậy hãy bắt đầu từ nút không phải lá S với chỉ số con lớn nhất và
thực thi maxHeapify (A, i) từ dưới lên. Chỉ số con của s trong câu hỏi này là H/2.
Lấy đống nhị phân A = {4, 1, 3, 2, 16, 9, 10, 14, & 7} làm ví dụ và việc thực thi
buildMaxHeaP trên đó được thể hiện trong Hình 10.4.

Chúng tôi cho phép tôi lấy H / 2 làm điểm bắt đầu và 1 làm điểm kết thúc, và
thực thi maxHeapify (A, i) tuần tự trên cây con có gốc tại i. Mỗi lần
maxHeapifytA, i) được thực thi, các cây con bên trái và bên phải của I đã được
Cả hai đều là đống lớn nhất, vì vậy chỉ cần di chuyển giá trị của gốc i qual
maxHeapify (A, Ỉ) đến vị trí thích hợp.

Khảo sát

Giả sử kích thước của đống nhị phân là maxHeapify, độ phức tạp tỷ lệ với chiều
cao của cây nhị phân hoàn chỉnh nên nó là O(logH) Sau đó, chúng ta xem xét
độ phức tạp của buildMaxHeap. Đặt số phần tử là chương trình đầu tiên thực
thi maxHeapify trên H / 2 cây con của chiều cao 1, sau đó thực thi maxHeapify
trên H / 4 cây con có chiều cao 2, và cuối cùng

Thực hiện apify maxH cho cây con H (cả cây), do đó, độ phức tạp là

Ngoài ra, thêm các phần tử mới theo thứ tự ở cuối heap cũng là một cách để
tạo ra heap lớn nhất. Trong câu hỏi tiếp theo, chúng ta sẽ tìm hiểu về thuật
toán chèn các phần tử vào heap
Priority Queue

Hàng đợi ưu tiên (Priority Queue) là một cấu trúc dữ liệu, trong tập dữ liệu S
được lưu trữ trong đó, mỗi phần tử chứa một giá trị khóa. Hàng đợi ưu tiên
chủ yếu thực hiện các hoạt động sau.

►insert (s, k): chèn phần tử k vào tập S

►extractMax (S): Loại bỏ phần tử có giá trị khóa lớn nhất khỏi S và trả về giá
trị khóa

Hãy viết chương trình để thực hiện insert (s, k) và extractMax (S) trên hàng đợi
ưu tiên S. Trong câu hỏi này, các phần tử của hàng đợi là số nguyên và giá trị
khóa là chính nó.

Đầu vào Nhập nhiều lệnh vào hàng đợi ưu tiên S. Các lệnh được đưa ra dưới
dạng chèn k, giải nén, kết thúc và mỗi lệnh chiếm một dòng. Lực ở đây đại diện
cho số nguyên được chèn vào và end biểu thị sự hoàn thành của đầu vào lệnh.

Đầu ra Mỗi khi lệnh giải nén được thực hiện, một giá trị lấy từ hàng đợi ưu tiên
S sẽ được xuất ra và mỗi giá trị chiếm một dòng.

Giới hạn số lượng lệnh không vượt quá 2 000 000

0 <= k <= 2000 000 000


giải thích

Hàng đợi nhận giá trị khóa lớn nhất đầu tiên được gọi là hàng đợi ưu tiên tối
đa và có thể được thực hiện bởi đống tối đa. Ở đây chúng tôi sử dụng một
mảng / để triển khai một đống nhị phân có kích thước H.

insert (key) dùng để thêm khóa vào hàng đợi ưu tiên tối đa S, thuật toán như
sau.

Program10.1 chèn các phần tử vào hàng đợi ưu tiên (heap)


Phép toán được sử dụng để tăng phần tử của đống nhị phân, giá trị khóa,
thuật toán như sau

Trước hết, để đảm bảo rằng heap chỉ được thay đổi khi giá trị khóa mới lớn
hơn hoặc bằng giá trị khóa hiện tại, trước tiên chúng ta phải kiểm tra giá trị
khóa hiện có, sau đó cập nhật giá trị khóa A [i] o do A[i] tăng có thể phá hủy
các thuộc tính tối đa của khóa Vì vậy, hãy di chuyển giá trị khóa đã cập nhật về
phía gốc và đặt nó vào vị trí đá. Quá trình xử lý rất đơn giản, chỉ cần so sánh
phần tử hiện tại với nút cha của nó, nếu giá trị của phần tử hiện tại lớn hơn,
trao đổi hai phần tử, sau đó gọi nó một cách đệ quy.

Ví dụ: chúng tôi thêm 25 vào heap nhị phân A {86, 14,37, 12,5,32, 11,7, 1,2},
sau đó thực thi heapIncreaseKey trên vị trí của phần tử được thêm vào. Quá
trình xử lý được hiển thị trong hình Như hình 10.5.

Giá trị lớn nhất trong hàng đợi ưu tiên tối đa S có thể nhận được bởi nút gốc
của đống nhị phân. Thuật toán xóa và trích xuất phần tử lớn nhất từ hàng đợi
ưu tiên lớn nhất S như sau.

Sau khi lấy giá trị gốc 86, hãy di chuyển 3 ở cuối đến giá trị gốc và giảm kích
thước đống 1. Tiếp theo, thực thi maxHeapify từ gốc để đảm bảo tính chất của
heap. Cuối cùng trở lại 86

Khảo sát

Số lần trao đổi phần tử được thực hiện trong heapIncreaseKey và maxHeapif y
tỷ lệ với chiều cao của cây. Giả sử số phần tử trong hàng ưu tiên là n thì độ
phức tạp của thuật toán chèn và xóa phần tử vào hàng ưu tiên là O(logn)

Triển khai hàng đợi ưu tiên thông qua thư viện chuẩn

STL không chỉ cung cấp cho người dùng các vùng chứa cơ bản như vector mà
còn cung cấp các bộ điều hợp vùng chứa với các giao diện cho các nhu cầu đặc
biệt. Ngăn xếp và hàng đợi được mô tả trước đó trong cuốn sách này đều là bộ
điều hợp vùng chứa. Chúng là các vùng chứa quản lý dữ liệu dựa trên lần lượt
là LIFO (vào cuối, ra trước) và FIFO (vào trước, ra trước). "Phần này sẽ giới
thiệu các phần tử có thể được cung cấp trong STL. Thêm vùng chứa ưu tiên
priority_queue0

10.5.1 priority_queue

priority_queue là một hàng đợi có thể được chèn, tham chiếu và xóa theo mức
độ ưu tiên của phần tử. Giao diện (hàm) để thực hiện các thao tác này giống
như queue, push () được sử dụng để chèn một phần tử vào hàng đợi, top ()
được sử dụng để truy cập (đọc) phần tử đầu và pop () được sử dụng để xóa
phần tử bắt đầu. Trong priority_queue, phần tử bắt đầu luôn là phần tử có
mức ưu tiên cao nhất. Mặc dù cơ sở ưu tiên có thể được chỉ định bởi lập trình
viên, "nhưng ở đây chúng tôi không đưa ra bất kỳ đặc tả nào, hãy xem cách
hàng đợi ưu tiên quản lý dữ liệu thông qua cơ sở mặc định.

Chương trình 10.4 sau đây thực hiện các thao tác chèn và xóa số nguyên trên
hàng đợi ưu tiên.

Khi kiểu phần tử là int, phần tử có mức ưu tiên lớn nhất mặc định có mức ưu
tiên cao nhất và nó được lấy ra trước.
Bây giờ chúng ta sử dụng hàng đợi ưu tiên của STL để giải quyết ví dụ trước.
Hàng đợi ưu tiên có thể được thực hiện bằng các phương pháp sau.

You might also like