Hàng đợi ưu tiên có cho phép trùng lặp python không?

Chương trình TopM. java là ứng dụng hàng đợi ưu tiên nhận đối số dòng lệnh M, đọc giao dịch từ đầu vào tiêu chuẩn và in ra M giao dịch lớn nhất

triển khai cơ bản

Các cấu trúc dữ liệu cơ bản mà chúng ta đã thảo luận trong Phần 1. 3 cung cấp cho chúng tôi bốn điểm bắt đầu ngay lập tức để triển khai hàng đợi ưu tiên
  • Biểu diễn mảng [không có thứ tự]. Có lẽ cách triển khai hàng đợi ưu tiên đơn giản nhất dựa trên mã của chúng tôi dành cho ngăn xếp đẩy xuống. Mã chèn vào hàng đợi ưu tiên giống như mã đẩy vào ngăn xếp. Để triển khai loại bỏ tối đa, chúng ta có thể thêm mã như vòng lặp bên trong của sắp xếp lựa chọn để hoán đổi mục tối đa với mục ở cuối và sau đó xóa mục đó, như chúng ta đã làm với pop[] cho ngăn xếp. Chương trình UnorderedArrayMaxPQ. java triển khai hàng đợi ưu tiên bằng cách sử dụng phương pháp này
  • Biểu diễn mảng [có thứ tự]. Một cách tiếp cận khác là thêm mã cho thao tác chèn để di chuyển các mục lớn hơn sang bên phải một vị trí, do đó giữ các mục trong mảng theo thứ tự [như trong sắp xếp chèn]. Do đó, mục lớn nhất luôn ở cuối và mã để loại bỏ mục tối đa trong hàng đợi ưu tiên cũng giống như đối với pop trong ngăn xếp. Chương trình OrderedArrayMaxPQ. java triển khai hàng đợi ưu tiên bằng cách sử dụng phương pháp này
  • Biểu diễn danh sách liên kết [không có thứ tự và có thứ tự đảo ngược]. Tương tự như vậy, chúng ta có thể bắt đầu với mã danh sách liên kết cho ngăn xếp đẩy xuống, sửa đổi mã cho pop[] để tìm và trả về giá trị lớn nhất hoặc mã cho push[] để giữ các mục theo thứ tự ngược lại và mã cho pop[] thành

Tất cả các triển khai cơ bản vừa được thảo luận đều có thuộc tính là thao tác chèn hoặc xóa tối đa sẽ mất thời gian tuyến tính trong trường hợp xấu nhất. Tìm một triển khai trong đó cả hai thao tác đều được đảm bảo nhanh chóng là một nhiệm vụ thú vị hơn và đó là chủ đề chính của phần này

định nghĩa đống

Heap nhị phân là một cấu trúc dữ liệu có thể hỗ trợ hiệu quả các hoạt động hàng đợi ưu tiên cơ bản. Trong một đống nhị phân, các mục được lưu trữ trong một mảng sao cho mỗi khóa được đảm bảo lớn hơn [hoặc bằng] các khóa ở hai vị trí cụ thể khác. Đổi lại, mỗi khóa đó phải lớn hơn hai khóa khác, v.v. Thứ tự này rất dễ thấy nếu chúng ta xem các khóa nằm trong cấu trúc cây nhị phân với các cạnh từ mỗi khóa đến hai khóa đã biết là nhỏ hơn

Sự định nghĩa. Cây nhị phân được sắp xếp theo thứ tự heap nếu khóa ở mỗi nút lớn hơn [hoặc bằng] khóa ở các nút đó hai nút con [nếu có]

Dự luật. Khóa lớn nhất trong cây nhị phân theo thứ tự heap được tìm thấy ở gốc

Chúng ta có thể áp đặt giới hạn thứ tự heap trên bất kỳ cây nhị phân nào. Tuy nhiên, điều đặc biệt thuận tiện là sử dụng một cây nhị phân hoàn chỉnh như cây dưới đây

Chúng ta biểu diễn các cây nhị phân đầy đủ theo trình tự trong một mảng bằng cách đặt các nút theo thứ tự cấp độ, với gốc ở vị trí 1, con của nó ở vị trí 2 và 3, con của chúng ở vị trí 4, 5, 6 và 7, v.v.

Sự định nghĩa. Một đống nhị phân là một tập hợp các nút có khóa được sắp xếp trong một cây nhị phân hoàn chỉnh theo thứ tự đống, được biểu diễn theo thứ tự cấp độ trong một mảng [không sử dụng mục nhập đầu tiên]

Trong một đống, nút cha của nút ở vị trí k nằm ở vị trí k/2; . Chúng ta có thể di chuyển lên và xuống bằng cách thực hiện phép tính đơn giản trên các chỉ số mảng. để di chuyển lên trên cây từ a[k] chúng ta đặt k thành k/2;

Thuật toán trên đống

Chúng tôi đại diện cho một đống có kích thước n trong mảng riêng tư pq[] có độ dài n + 1, với pq[0] không được sử dụng và đống trong pq[1] đến pq[n]. Chúng tôi chỉ truy cập các khóa thông qua các hàm trợ giúp riêng less[] và exch[]. Các thao tác trên heap mà chúng tôi coi là hoạt động bằng cách trước tiên thực hiện một sửa đổi đơn giản có thể vi phạm điều kiện của heap, sau đó di chuyển qua heap, sửa đổi heap theo yêu cầu để đảm bảo rằng điều kiện của heap được thỏa mãn ở mọi nơi. Chúng tôi gọi quá trình này là reheapifying hoặc khôi phục thứ tự heap
  • Khởi động lại từ dưới lên [bơi]. Nếu thứ tự heap bị vi phạm vì khóa của một nút trở nên lớn hơn khóa cha của nút đó, thì chúng ta có thể tiến hành khắc phục vi phạm bằng cách trao đổi nút với cha của nó. Sau khi trao đổi, nút lớn hơn cả hai nút con của nó [một nút là cha cũ và nút kia nhỏ hơn nút cha cũ vì nó là con của nút đó] nhưng nút này vẫn có thể lớn hơn nút cha của nó. Chúng ta có thể khắc phục vi phạm đó theo cách tương tự, v.v., di chuyển lên trên đống cho đến khi chúng ta đến một nút có khóa lớn hơn hoặc nút gốc.
    ______0_______


  • heapify từ trên xuống [chìm]. Nếu thứ tự heap bị vi phạm vì khóa của một nút trở nên nhỏ hơn một hoặc cả hai khóa con của nút đó, thì chúng ta có thể tiến hành khắc phục vi phạm bằng cách trao đổi nút với khóa con lớn hơn của nó. Công tắc này có thể gây ra vi phạm ở trẻ; .
    ______1_______

Hàng đợi ưu tiên dựa trên heap

Các hoạt động sink[] và swim[] này cung cấp cơ sở để triển khai hiệu quả API hàng đợi ưu tiên, như sơ đồ bên dưới và được triển khai trong MaxPQ. java và MinPQ. java
  • Chèn. Chúng tôi thêm mục mới vào cuối mảng, tăng kích thước của heap, sau đó di chuyển qua heap với mục đó để khôi phục tình trạng của heap
  • Loại bỏ tối đa. Chúng tôi lấy mục lớn nhất ra khỏi đầu, đặt mục từ cuối đống lên trên cùng, giảm kích thước của đống, sau đó chìm xuống trong đống với mục đó để khôi phục tình trạng của đống


Dự luật. Trong hàng đợi ưu tiên n mục, thuật toán heap yêu cầu không quá 1 + lg n phép so sánh để chèn và không quá 2 lg n phép so sánh để loại bỏ tối đa

Cân nhắc thực tế

Chúng tôi kết thúc nghiên cứu của mình về API hàng đợi ưu tiên heap với một vài cân nhắc thực tế
  • đống đa chiều. Không khó để sửa đổi mã của chúng tôi để tạo heap dựa trên biểu diễn mảng của các cây bậc ba hoặc d-ary theo thứ tự heap hoàn chỉnh. Có sự đánh đổi giữa chi phí thấp hơn từ việc giảm chiều cao cây và chi phí cao hơn khi tìm kiếm con lớn nhất trong ba hoặc d con tại mỗi nút
  • Thay đổi kích thước mảng. Chúng ta có thể thêm một hàm tạo không đối số, mã để nhân đôi mảng trong insert[] và mã để chia đôi mảng trong delMax[], giống như chúng ta đã làm với ngăn xếp trong Phần 1. 3. Giới hạn thời gian logarit được khấu hao khi kích thước của hàng đợi ưu tiên là tùy ý và các mảng được thay đổi kích thước
  • Tính bất biến của khóa. Hàng đợi ưu tiên chứa các đối tượng được tạo bởi máy khách nhưng giả định rằng mã máy khách không thay đổi khóa [điều này có thể làm mất hiệu lực các bất biến của heap]
  • Hàng đợi ưu tiên lập chỉ mục. Trong nhiều ứng dụng, việc cho phép khách hàng tham khảo các mục đã có trên hàng đợi ưu tiên là điều hợp lý. Một cách dễ dàng để làm như vậy là liên kết một chỉ mục số nguyên duy nhất với mỗi mục.

    IndexMinPQ. java là một triển khai dựa trên heap của API này; . java cũng tương tự nhưng dành cho hàng đợi ưu tiên định hướng tối đa. đa chiều. java là một ứng dụng khách hợp nhất một số luồng đầu vào được sắp xếp thành một luồng đầu ra được sắp xếp.

sắp xếp đống

Chúng ta có thể sử dụng bất kỳ hàng đợi ưu tiên nào để phát triển phương pháp sắp xếp. Chúng tôi chèn tất cả các khóa cần sắp xếp vào hàng đợi ưu tiên theo định hướng tối thiểu, sau đó lặp lại việc xóa khóa tối thiểu để xóa tất cả theo thứ tự. Khi sử dụng heap cho hàng đợi ưu tiên, chúng ta thu được heapsort

Tập trung vào nhiệm vụ sắp xếp, chúng tôi từ bỏ khái niệm ẩn biểu diễn heap của hàng đợi ưu tiên và sử dụng trực tiếp bơi[] và chìm[]. Làm như vậy cho phép chúng ta sắp xếp một mảng mà không cần thêm bất kỳ khoảng trống nào, bằng cách duy trì đống trong mảng được sắp xếp. Heapsort chia thành hai giai đoạn. xây dựng heap, trong đó chúng tôi sắp xếp lại mảng ban đầu thành một đống và sắp xếp, trong đó chúng tôi kéo các mục ra khỏi đống theo thứ tự giảm dần để tạo kết quả được sắp xếp

  • xây dựng đống. Chúng ta có thể hoàn thành nhiệm vụ này trong thời gian tỷ lệ với n lg n, bằng cách tiến hành từ trái sang phải qua mảng, sử dụng hàm swim[] để đảm bảo rằng các mục ở bên trái của con trỏ quét tạo thành một cây hoàn chỉnh theo thứ tự đống, giống như liên tiếp . Một phương pháp thông minh và hiệu quả hơn nhiều là tiến hành từ phải sang trái, sử dụng hàm sink[] để tạo các nhóm con khi chúng ta di chuyển. Mọi vị trí trong mảng là gốc của một subheap nhỏ; . Nếu hai nút con của một nút là đống, thì việc gọi hàm sink[] trên nút đó sẽ làm cho cây con bắt nguồn từ đó thành một đống
  • sắp xếp. Hầu hết công việc trong quá trình sắp xếp heapsort được thực hiện trong giai đoạn thứ hai, trong đó chúng tôi loại bỏ các phần tử lớn nhất còn lại khỏi heap và đặt nó vào vị trí mảng bị bỏ trống khi heap co lại


đống. java là một triển khai đầy đủ của heapsort. Dưới đây là dấu vết nội dung của mảng sau mỗi lần chìm


Dự luật. Xây dựng heap dựa trên chìm là thời gian tuyến tính


Dự luật. Người dùng heapsort ít hơn 2 n lg n so sánh và trao đổi để sắp xếp n mục

Hầu hết các mục được chèn lại vào đống trong quá trình sắp xếp đều đi xuống dưới cùng. Do đó, chúng ta có thể tiết kiệm thời gian bằng cách tránh kiểm tra xem vật phẩm đã đến vị trí của nó hay chưa, chỉ cần đẩy vật lớn hơn trong hai đứa trẻ cho đến khi chạm đến đáy, sau đó di chuyển trở lại đống đến vị trí thích hợp. Ý tưởng này cắt giảm số lượng so sánh xuống 2 lần với chi phí ghi sổ kế toán bổ sung

bài tập

  1. Giả sử rằng dãy
    P R I O * R * * I * T * Y * * * Q U E * * * U * E
    
    [trong đó một chữ cái có nghĩa là chèn và dấu hoa thị có nghĩa là loại bỏ tối đa] được áp dụng cho hàng đợi ưu tiên trống ban đầu. Đưa ra chuỗi các giá trị được trả về bằng cách loại bỏ các hoạt động tối đa

    Giải pháp. R R P O T Y I I U Q E U [E trái trên PQ]

  2. Phê bình ý kiến ​​sau. để triển khai tìm giá trị lớn nhất trong thời gian không đổi, tại sao không theo dõi giá trị lớn nhất đã chèn cho đến nay, sau đó trả về giá trị đó để tìm giá trị lớn nhất?

    Giải pháp. Sẽ cần cập nhật giá trị tối đa từ đầu sau thao tác loại bỏ giá trị tối đa

  3. Cung cấp các triển khai hàng đợi ưu tiên hỗ trợ chèn và xóa tối đa, một cho mỗi cấu trúc dữ liệu cơ bản sau. mảng không có thứ tự, mảng có thứ tự, danh sách liên kết không có thứ tự và danh sách liên kết có thứ tự. Đưa ra một bảng giới hạn trường hợp xấu nhất cho mỗi thao tác đối với mỗi trong số bốn cách triển khai của bạn từ bài tập trước

    giải pháp một phần. OrderedArrayMaxPQ. java và UnorderedArrayMaxPQ. java

  4. Là một mảng được sắp xếp theo thứ tự giảm dần một đống định hướng tối đa

    Câu trả lời. Đúng

  5. Giả sử rằng ứng dụng của bạn sẽ có một số lượng lớn thao tác chèn, nhưng chỉ một số thao tác loại bỏ tối đa. Bạn nghĩ triển khai hàng đợi ưu tiên nào sẽ hiệu quả nhất. đống, mảng không có thứ tự, mảng có thứ tự?

    Câu trả lời. mảng không có thứ tự. Chèn là thời gian không đổi

  6. Giả sử rằng ứng dụng của bạn sẽ có một số lượng lớn các thao tác tìm tối đa, nhưng một số lượng tương đối nhỏ các thao tác chèn và xóa tối đa. Bạn nghĩ triển khai hàng đợi ưu tiên nào sẽ hiệu quả nhất. đống, mảng không có thứ tự, mảng có thứ tự?

    Câu trả lời. Sắp xếp mảng. Tìm thời gian tối đa không đổi

  7. Số mục tối thiểu phải được trao đổi trong quá trình loại bỏ hoạt động tối đa trong một đống kích thước n không có khóa trùng lặp là bao nhiêu? . Trả lời cùng một câu hỏi cho hai và ba lần liên tiếp loại bỏ các hoạt động tối đa

    câu trả lời một phần. [một] 2

  8. Thiết kế thuật toán chứng nhận thời gian tuyến tính để kiểm tra xem một mảng pq[] có phải là một đống định hướng tối thiểu hay không

    Giải pháp. Xem phương thức isMinHeap[] trong MinPQ. java

  9. Chứng minh rằng việc xây dựng heap dựa trên phần chìm sử dụng nhiều nhất 2n so sánh và nhiều nhất n trao đổi

    Giải pháp. Đủ để chứng minh rằng việc xây dựng heap dựa trên phần chìm sử dụng ít hơn n trao đổi vì số lần so sánh nhiều nhất gấp đôi số lần trao đổi. Để đơn giản, giả sử rằng đống nhị phân là hoàn hảo [i. e. , một cây nhị phân trong đó mọi mức được lấp đầy hoàn toàn] và có chiều cao h

    Chúng tôi xác định chiều cao của một nút trong cây là chiều cao của cây con bắt nguồn từ nút đó. Một chìa khóa ở độ cao k có thể được trao đổi với tối đa k chìa khóa bên dưới nó khi nó bị chìm xuống. Vì có 2h−k nút ở độ cao k nên tổng số lần trao đổi nhiều nhất là. $$ \begin{eqnarray*} h + 2[h-1] + 4[h-2] + 8[h-3] + \ldots + 2^h [0] & = & 2^{h+1}

    Đẳng thức đầu tiên dành cho một tổng không theo tiêu chuẩn, nhưng thật dễ dàng để xác minh rằng công thức đúng thông qua quy nạp toán học. Đẳng thức thứ hai đúng vì một cây nhị phân hoàn hảo có chiều cao h có 2h+1 − 1 nút

    Chứng minh rằng kết quả đúng khi cây nhị phân không hoàn hảo cần cẩn thận hơn một chút. Bạn có thể làm như vậy bằng cách sử dụng thực tế là số nút ở độ cao k trong một đống nhị phân trên n nút tối đa là trần[n / 2k+1]

    giải pháp thay thế. Chúng tôi xác định chiều cao của một nút trong cây là chiều cao của cây con bắt nguồn từ nút đó

    • Đầu tiên, quan sát rằng một đống nhị phân trên n nút có n - 1 liên kết [vì mỗi liên kết là cha của một nút và mọi nút đều có liên kết cha ngoại trừ gốc]
    • Đánh chìm một nút có độ cao k yêu cầu tối đa k lần trao đổi
    • Chúng tôi sẽ tính phí k liên kết đến mỗi nút ở độ cao k, nhưng không nhất thiết là các liên kết trên đường dẫn được thực hiện khi đánh chìm nút. Thay vào đó, chúng tôi tính phí nút bằng k liên kết dọc theo đường dẫn từ nút đi sang trái–phải–phải–phải–. Ví dụ: trong sơ đồ bên dưới, nút gốc được tính phí cho 4 liên kết màu đỏ;
    • Lưu ý rằng không có liên kết nào được tính phí cho nhiều hơn một nút. [Các liên kết có được bằng cách chỉ theo các liên kết bên phải từ gốc không bị tính phí cho bất kỳ nút nào. ]
    • Như vậy, tổng số lần trao đổi nhiều nhất là n. Vì có nhiều nhất 2 lần so sánh trên mỗi lần trao đổi nên số lần so sánh nhiều nhất là 2n

vấn đề sáng tạo

  1. Lý thuyết số tính toán. Viết chương trình CubeSum. java in ra tất cả các số nguyên có dạng \[a^3 + b^3\] trong đó \[a\] và \[b\] là các số nguyên trong khoảng từ 0 đến \[n

Chủ Đề