Serwis Edukacyjny
Nauczycieli

w I-LO w Tarnowie
obrazek Materiały głownie dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

Sortowanie stogowe
  Heap Sort

SPIS TREŚCI
Podrozdziały

Algorytm rozbioru kopca

W tym rozdziale nauczymy się rozbierać kopiec. Zasada jest następująca:

  1. Zamień miejscami korzeń z ostatnim liściem, który wyłącz ze struktury kopca. Elementem pobieranym z kopca jest zawsze jego korzeń, czyli element największy.
  2. Jeśli jest to konieczne, przywróć warunek kopca idąc od korzenia w dół.
  3. Kontynuuj od kroku 1, aż kopiec będzie pusty.

Przykład:

Dokonaj rozbioru następującego kopca:

obrazek
Operacja Opis
obrazek
9
Rozbiór kopca rozpoczynamy od korzenia, który usuwamy
ze struktury kopca. W miejsce korzenia wstawiamy ostatni
liść.
obrazek   
obrazek obrazek Poprzednia operacja zaburzyła
strukturę kopca. Idziemy zatem
od korzenia w dół struktury
przywracając warunek kopca
- przodek większy lub równy od
swoich potomków. Praktycznie
polega to na zamianie przodka
z największym potomkiem.
Operację kontynuujemy dotąd,
aż natrafimy na węzły
spełniające warunek kopca.
obrazek 
8 9
Usuwamy z kopca kolejny korzeń zastępując go ostatnim
liściem.
obrazek   obrazek obrazek W nowym kopcu przywracamy
warunek kopca.
obrazek 
7 8 9
Usuwamy z kopca kolejny korzeń zastępując go ostatnim
liściem
obrazek     obrazek W nowym kopcu przywracamy warunek kopca.
W tym przypadku już po pierwszej wymianie
węzłów warunek koca jest zachowany
w całej strukturze.
obrazek   
5 7 8 9
Usuwamy z kopca kolejny korzeń zastępując go ostatnim
liściem
obrazek obrazek Przywracamy warunek kopca w strukturze.
obrazek
4 5 7 8 9
Usuwamy z kopca kolejny korzeń zastępując go ostatnim
liściem
obrazek       obrazek

Przywracamy warunek kopca w strukturze.

     obrazek
2 4 5 7 8 9
Usuwamy z kopca kolejny korzeń zastępując go ostatnim
liściem.
             obrazek
1 2 4 5 7 8 9
Po wykonaniu poprzedniej operacji usunięcia w kopcu
pozostał tylko jeden element - usuwamy go. Zwróć uwagę,
iż usunięte z kopca elementy tworzą ciąg uporządkowany.

Operację rozbioru kopca można przeprowadzić w miejscu. Jeśli mamy zbiór d[ ] ze strukturą kopca, to idąc od końca zbioru (ostatnie liście drzewa) w kierunku początku zamieniamy elementy z pierwszym elementem zbioru (korzeń drzewa), a następnie poruszając się od korzenia w dół przywracamy warunek kopca w kolejno napotykanych węzłach.


do podrozdziału  do strony 

Opis algorytmu

Specyfikacja problemu

Dane wejściowe

d[ ] - Zbiór zawierający poprawną strukturę kopca. Elementy są numerowane począwszy od 1.
n - Ilość elementów w zbiorze, n ∈ N

Dane wyjściowe

d[ ] - Zbiór zawierający elementy pobrane z kopca ułożone w porządku rosnącym

Zmienne pomocnicze

i - zmienna licznikowa pętli pobierającej kolejne elementy z kopca, i ∈ N, i ∈ {n,n-1,...,2}
j,k - indeksy elementów leżących na ścieżce w dół od korzenia,  j,k ∈ N
m - indeks większego z dwóch elementów potomnych, m ∈ N

Lista kroków

K01: Dla i = n, n - 1, ..., 2:
    wykonuj kroki K02...K08
K02:     d[1] ↔ d[i]
K03:     j ← 1;  k ← 2
K04:     Dopóki (k  < i):
        wykonuj kroki K05...K08
K05:         Jeżeli (k  + 1 < i) ∧ (d[k + 1] > d[k]),
        to m  ← k + 1
        inaczej  m  ← k
K06:         Jeżeli d[m] ≤ d[j],
        to wyjdź z pętli K04
        i kontynuuj następny obieg pętli K01
K07:         d[j] ↔ d[m]
K08:         j ← m;  k ← j + j
K09: Zakończ

Schemat blokowy

obrazek

Rozbiór kopca wykonywany jest w dwóch zagnieżdżonych pętlach. Pętla nr 1 zamienia miejscami kolejne liście ze spodu drzewa z korzeniem. Zadaniem pętli nr 2 jest przywrócenie w strukturze warunku kopca.

Pętla nr 1 przebiega wstecz indeksy elementów zbioru d[ ] poczynając od indeksu n a kończąc na indeksie 2. Element i-ty jest wymieniany z korzeniem kopca. Po tej wymianie rozpoczynamy w pętli nr 2 przeglądanie drzewa od korzenia w dół. Zmienna j przechowuje indeks przodka, zmienna k przechowuje indeks lewego potomka. Pętla nr 2 kończy się, gdy węzeł j-ty nie posiada potomków. Wewnątrz pętli wyznaczamy w zmiennej m indeks większego z dwóch węzłów potomnych. Następnie sprawdzamy, czy w węźle nadrzędnym j-tym jest zachowany warunek kopca. Jeśli tak, to następuje wyjście z pętli nr 2. W przeciwnym razie zamieniamy miejscami węzeł nadrzędny j-ty z jego największym potomkiem m-tym i za nowy węzeł nadrzędny przyjmujemy węzeł m-ty. W zmiennej k wyznaczamy indeks jego lewego potomka i kontynuujemy pętlę nr 2.

Po wyjściu z pętli nr 2 zmniejszamy zmienną i o 1 - przenosimy się do kolejnego, ostatniego liścia drzewa i kontynuujemy pętlę nr 1.

W celu wyznaczenia klasy złożoności obliczeniowej algorytmu rozbioru kopca zauważamy, iż pętla zewnętrzna nr 1 wykona się n - 1 razy, a w każdym obiegu tej pętli pętla wewnętrzna wykona się maksymalnie log2n razy. Daje to zatem górne oszacowanie O(n log n) w przypadku pesymistycznym oraz O(n) w przypadku optymistycznym, który wystąpi tylko wtedy, gdy zbiór d[ ] będzie zbudowany z ciągu tych samych elementów.


do podrozdziału  do strony 

Przykładowe programy

C++
// Rozbiór kopca
//--------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// w Tarnowie

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>

using namespace std;

int main()
{
  const int N = 15; // liczba elementów
  int d[N + 1],i,j,k,m,x;

  srand((unsigned)time(NULL));

  cout << "    Rozbior  kopca\n"
          "----------------------\n"
          "(C)2005 Jerzy Walaszek\n\n"
          "Zbior wejsciowy ze struktura kopca:\n\n";

  // W zbiorze d konstruujemy kopiec

  d[1] = 9;
  for(i = 2; i <= N; i++)
    d[i] = rand() % (d[i / 2] + 1);

  // Prezentujemy kopiec

  for(i = 1; i <= N; i++)
    cout << setw(2) << d[i];
  cout << endl << endl;
  x = (N + 1) / 2;
  k = 2;
  for(i = 1; i <= N; i++)
  {
    for(j = 1; j < x; j++)
      cout << " ";
    cout << d[i];
    for(j = 1; j <= x; j++)
      cout << " ";
    if(i + 1 == k)
    {
      k += k;
      x /= 2;
      cout << endl;
    }
  }

  // Rozbieramy kopiec

  for(i = N; i > 1; i--)
  {
    swap(d[1],d[i]);
    j = 1; k = 2;
    while(k < i)
    {
      if((k + 1 < i) &&
         (d[k + 1] > d[k]))
        m = k + 1;
      else
        m = k;
      if(d[m] <= d[j]) break;
      swap(d[j],d[m]);
      j = m;
      k = j + j;
    }
  }

  // Wyświetlamy wynik rozbioru kopca

  cout << endl
       << "Zbior wyjsciowy po rozbiorze kopca:\n\n";

  for(i = 1; i <= N; i++)
    cout << setw(2) << d[i];
  cout << endl << endl;

  // Gotowe

  system("pause");
  return 0;
}
Pascal
// Rozbiór kopca
//--------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// w Tarnowie

program kopiec;

const N = 15; // liczba elementów
var
  d         : array[1..N] of integer;
  i,j,k,m,x : integer;

begin
  randomize;
  writeln('    Rozbior  kopca');
  writeln('----------------------');
  writeln('(C)2005 Jerzy Walaszek');
  writeln;

  // W zbiorze d konstruujemy kopiec

   d[1] := 9;
   for i := 2 to N do
     d[i] := random(d[i div 2] + 1);

  // Prezentujemy kopiec

  writeln('Zbior wejsciowy ze struktura kopca:');
  writeln;
  for i := 1 to N do
    write(d[i]:2);
  writeln; writeln;
  x := (N + 1) div 2;
  k := 2;
  for i := 1 to N do
  begin
    for j := 1 to x - 1 do
      write(' ');
    write(d[i]);
    for j := 1 to x do
      write(' ');
    if i + 1 = k then
    begin
      k := k + k;
      x := x div 2;
      writeln;
    end;
  end;

  // Rozbieramy kopiec

  for i := N downto 2 do
  begin
    x := d[1];
    d[1] := d[i];
    d[i] := x;
    j := 1; k := 2;
    while k < i do
    begin
      if (k + 1 < i) and
         (d[k + 1] > d[k]) then
        m := k + 1
      else
        m := k;
      if d[m] <= d[j] then break;
      x := d[j];
      d[j] := d[m];
      d[m] := x;
      j := m;
      k := j + j;
    end;
  end;

// Wyświetlamy wynik rozbioru kopca

  writeln;
  writeln('Zbior wyjsciowy po rozbiorze kopca:');
  writeln;
  for i := 1 to N do
    write(d[i]:2);

// Gotowe

  writeln;
  writeln;
  write('Nacisnij klawisz Enter...');
  readln;

end.
Basic
' Rozbiór kopca
'--------------
' (C)2012 mgr Jerzy Wałaszek
' I Liceum Ogólnokształcące
' w Tarnowie

CONST N = 15 ' liczba elementów

DIM AS INTEGER d(N),i,j,k,m,x

RANDOMIZE

PRINT "    Rozbior  kopca"
PRINT "----------------------"
PRINT "(C)2005 Jerzy Walaszek"
PRINT

' W zbiorze d konstruujemy kopiec

d(1) = 9
FOR i = 2 TO N:
  d(i) = INT(RND * (d(i \ 2) + 1))
NEXT

' Prezentujemy kopiec

PRINT "Zbior wejsciowy ze struktura kopca:"
PRINT
FOR i = 1 TO N
  PRINT USING "##";d(i);
NEXT
PRINT
PRINT
x = (N + 1) \ 2
k = 2
FOR i = 1 TO N
  FOR j = 1 TO x - 1
    PRINT " ";
  NEXT
  PRINT USING "#";d(i);
  FOR j = 1 TO x
    PRINT " ";
  NEXT
  IF i + 1 = k THEN
    k = k + k
    x = x \ 2
    PRINT
  END IF
NEXT

' Rozbieramy kopiec

FOR i = N TO 2 STEP -1
  SWAP d(1),d(i)
  j = 1
  k = 2
  WHILE k < i
    IF (k + 1 < i) AND _
       (d(k + 1) > d(k)) THEN
      m = k + 1
    ELSE
      m = k
    END IF
    IF d(m) <= d(j) THEN EXIT WHILE
    SWAP d(j),d(m)
    j = m
    k = j + j
  WEND
NEXT

' Wyświetlamy wynik rozbioru kopca

PRINT
PRINT "Zbior wyjsciowy po rozbiorze kopca:"
PRINT
FOR i = 1 TO N
  PRINT USING "##";d(i);
NEXT
PRINT
PRINT

' Gotowe

PRINT "Nacisnij klawisz Enter..."
SLEEP
END
Python (dodatek)
# Rozbiór kopca
#--------------
# (C)2026 mgr Jerzy Wałaszek
# I Liceum Ogólnokształcące
# w Tarnowie

import random

n = 15 # liczba elementów

print("    Rozbiór  kopca")
print("----------------------")
print("(C)2026 Jerzy Walaszek")
print()

# W zbiorze d konstruujemy kopiec

d = [9] * 2
for i in range(2,n +1):
    d.append(random.randrange(d[i // 2] +1))

# Prezentujemy kopiec

print("Zbiór wejściowy ze strukturą kopca:")
print()
for i in range(1,n + 1):
    print("%2d" % (d[i]),end="")
print()
print()
x = (n + 1) // 2
k = 2
for i in range(1,n + 1):
    for j in range(1,x):
        print(" ",end="")
    print("%1d" % (d[i]),end="")
    for j in range(1,x+1):
        print(" ",end="")
    if i + 1 == k:
        k += k
        x //= 2
        print()

# Rozbieramy kopiec

for i in reversed(range(2,n + 1)):
    d[1],d[i] = d[i],d[1]
    j = 1
    k = 2
    while k < i:
        if (k + 1 < i) and \
           (d[k + 1] > d[k]):
            m = k + 1
        else:
            m = k
        if d[m] <= d[j]: break
        d[j],d[m] = d[m],d[j]
        j = m
        k = j + j

# Wyświetlamy wynik rozbioru kopca

print()
print("Zbior wyjściowy po rozbiorze kopca:")
print()
for i in range(1,n + 1):
    print("%2d" % (d[i]),end="")
print()
print()

# Gotowe

input("Naciśnij Enter...")
Wynik:
    Rozbiór  kopca
----------------------
(C)2026 Jerzy Walaszek

Zbiór wejściowy ze strukturą kopca:

 9 4 9 0 2 7 8 0 0 1 0 5 1 2 2

       9        
   4       9    
 0   2   7   8  
0 0 1 0 5 1 2 2 

Zbiór wyjściowy po rozbiorze kopca:

 0 0 0 0 1 1 2 2 2 4 5 7 8 9 9

Naciśnij Enter...
JavaScript
<html>
  <head>
  </head>
  <body>
    <form style="BORDER-RIGHT: #ff9933 1px outset;
                 PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
                 PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
                 BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
                 BORDER-BOTTOM: #ff9933 1px outset;
                 BACKGROUND-COLOR: #ffcc66" name="frmheap">
      <h3 style="text-align: center">Rozbiór kopca</h3>
      <p style="TEXT-ALIGN: center">
        (C)2012 mgr Jerzy Wałaszek - I LO w Tarnowie
      </p>
      <hr>
      <p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Rozbierz Kopiec" name="B1">
      </p>
      <p style="TEXT-ALIGN: center">
        <font face="Courier New"><span id="t_out">.</span></font>
      </p>
    </form>

<script language=javascript>

// Rozbiór kopca
//--------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// w Tarnowie

function main()
{
  var N = 15; // liczba elementów
  var d = new Array(N + 1);
  var i,j,k,x,t;

// W zbiorze d konstruujemy kopiec

  d[1] = 9;
  for(i = 2; i <= N; i++)
    d[i] = Math.floor(Math.random() * (d[Math.floor(i / 2)] + 1));

// Prezentujemy kopiec

  t = "";
  for(i = 1; i <= N; i++) t += d[i] + " ";
  t += "<BR><BR>";
  x = Math.floor((N + 1) / 2); k = 2;
  for(i = 1; i <= N; i++)
  {
    for(j = 1; j < x; j++) t += "&nbsp;";
    t += d[i];
    for(j = 1; j <= x; j++) t += "&nbsp;";
    if(i + 1 == k)
    {
      k += k; x = Math.floor(x / 2); t += "<BR>";
    }
  }

// Rozbieramy kopiec

  for(i = N; i > 1; i--)
  {
    x = d[1]; d[1] = d[i]; d[i] = x;
    j = 1; k = 2;
    while(k < i)
    {
      if((k + 1 < i) && (d[k + 1] > d[k]))
        m = k + 1;
      else
        m = k;
      if(d[m] <= d[j]) break;
      x = d[j]; d[j] = d[m]; d[m] = x;
      j = m; k = j + j;
    }
  }

// Wyświetlamy wynik rozbioru kopca

  t += "<BR>";
  for(i = 1; i <= N; i++) t += d[i] + " ";

// Gotowe

  document.getElementById("t_out").innerHTML = t;
}

</script> 

  </body>
</html>

Rozbiór kopca

(C)2012 mgr Jerzy Wałaszek - I LO w Tarnowie


.


do podrozdziału  do strony 

Zobacz również na: Drzewo binarne | Tworzenie kopca | Sortowanie przez kopcowanie

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2026 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.