Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

Kopiec binarny

SPIS TREŚCI
Tematy pomocnicze
Podrozdziały

Definicje

W dotychczas omawianych przez nas drzewach nie interesowały nas zależności pomiędzy danymi zawartymi w węzłach, a jedynie struktura samego drzewa. W tym i następnych rozdziałach będziemy omawiać drzewa binarne, których węzły są powiązane ze sobą nie tylko krawędziami, lecz dodatkowymi warunkami. Pierwszą taką strukturą będzie kopiec (ang. heap).

Kopiec binarny (ang. binary heap) jest kompletnym drzewem binarnym, co oznacza, że posada zapełnione wszystkie poziomy za wyjątkiem ostatniego, a ostatni poziom jest zapełniany bez przerw, poczynając od strony lewej do prawej.

obrazek To drzewo nadaje się na kopiec,
ponieważ jest kompletne.
Ostatni poziom węzły wypełniają
bez przerw od strony lewej do prawej.
obrazek To drzewo nie nadaje się na kopiec.
Na ostatnim poziomie brakuje
pierwszego węzła od lewej strony.
obrazek To drzewo również nie nadaje się
na kopiec. Na ostatnim poziomie
mamy przerwę w ciągu węzłów.

Oprócz wymogu strukturalnego (drzewo binarne musi być kompletne) kopiec posiada dodatkowy warunek: żaden z synów węzła nie jest od niego większy, czyli nie przechowuje w swoim polu data wartości większej od zawartości pola data swojego ojca.

obrazek To jest kopiec, ponieważ drzewo jest
drzewem kompletnym, a żaden
z synów nie ma wartości większej
od swojego ojca.
obrazek To nie jest kopiec, ponieważ zaznaczone
na czerwono węzły nie spełniają
warunku kopca. Przechowują wartości
większe od wartości przechowywanych
w ich ojcach.

Ponieważ kopiec jest drzewem kompletnym, to daje się łatwo zrealizować w tablicy:

Zwróć uwagę, że w tablicy odkładamy kolejne poziomy drzewa. Na poziomie 0 mamy tylko korzeń, czyli węzeł nr 0. Na poziomie 1 są węzły 1 i 2. Na poziomie 2 mamy węzły 3...6. Ponieważ tylko ostatni poziom może nie być w całości zapełniony, w strukturze tej nie występują przerwy – jest ciągiem kolejnych węzłów. Indeksy elementów tablicy odpowiadają numerom węzłów. W drzewie kompletnym nie musimy osobno zapamiętywać krawędzi, ponieważ struktura drzewa zależy w prosty sposób od liczby węzłów i zawsze da się ją odtworzyć. Zatem do utworzenia kopca binarnego potrzebujemy odpowiednio dużej tablicy na elementy oraz dodatkowej zmiennej, która zapamiętuje liczbę elementów przechowywanych w kopcu.

Powiązania pomiędzy węzłami określają proste wzory. Niech n będzie liczbą węzłów w drzewie, a k numerem węzła. Wtedy:

numer lewego syna = 2k+1
numer prawego syna = 2k+2
numer ojca = [(k-1)/2], dla k > 0
lewy syn istnieje, jeśli 2k+1 < n
prawy syn istnieje, jeśli 2k+2 < n
węzeł k jest liściem, jeśli 2k+2 > n

Wzory te pozwalają poruszać się po strukturze kopca w górę i w dół.

Dla kopca istnieją dwie operacje standardowe: push – wstawianie nowego elementu do kopca oraz pop – usuwanie korzenia kopca. Opisujemy je poniżej.


do podrozdziału  do strony 

Wstawianie elementu do kopca – push

Dodawany element wstawiamy jako ostatni liść kopca. Następnie sprawdzamy kolejno, czy jest on mniejszy lub równy swojemu ojcu. Jeśli nie, to zamieniamy wstawiony element z jego ojcem. Operację kontynuujemy, aż znajdziemy ojca większego lub równego elementowi lub dojdziemy do korzenia drzewa. Prześledź poniższą tabelkę:

obrazek Nowy element wstawiamy jako ostatni liść kopca.
Następnie sprawdzamy, czy jest on niewiększy
od swojego ojca. Ponieważ warunek kopca nie
jest spełniony, wymieniamy ze sobą węzeł
wstawiony z jego ojcem.
obrazek Na wyższym poziomie znów sprawdzamy
warunek kopca – czy syn nie jest
większy od ojca. Warunek nie jest wciąż
spełniony, zatem wymieniamy ojca ze wstawionym węzłem i przechodzimy
na kolejny poziom drzewa.
obrazek Tutaj znów warunek kopca nie jest spełniony,
wymieniamy zatem ojca ze wstawionym
węzłem i przechodzimy na kolejny poziom
drzewa.
obrazek Tu kończymy, ponieważ doszliśmy do korzenia
drzewa, który nie ma już ojca.
Element został wstawiony do kopca.

Algorytm dodawania nowego elementu do kopca

Wejście:

T : tablica zawierająca kopiec. Tablica powinna być wystarczająco duża na pomieszczenie dodawanego elementu.
n : liczba elementów w kopcu, n ∈ C.
v : wartość dodawanego elementu.

Wyjście:

T : tablica z kopcem powiększonym o dodany element v.
n : zwiększone o 1.

Elementy pomocnicze:

i, j : indeksy elementów w kopcu, i, j ∈ C

Lista kroków:

K01: in ; ustawiamy indeks i na pozycję wstawianego elementu
K02: nn+1 ; kopiec będzie zawierał o 1 element więcej
K03: j ← (i-1) div 2 ; obliczamy indeks ojca 
K04: Dopóki i > 0 obrazek T[j] < v, ; sprawdzamy warunek kopca
     wykonuj kroki K05…K07
K05:   T[i] ← T[j] ; umieszczamy ojca na miejscu syna 
K06:   ij ; przenosimy się na pozycję ojca
K07:   j ← (i-1) div 2 ; obliczamy indeks ojca
K08: T[i] ← v ; wstawiamy element do kopca
K09: Zakończ

Operacja dodawania nowego elementu do kopca posiada klasę złożoności obliczeniowej O(log n).


Przykładowe programy

Uwaga:

Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich.

Program generuje 15 liczb pseudolosowych z zakresu od 0 do 99. Następnie tworzy kopiec wprowadzając do niego kolejno wygenerowane liczby. Na końcu wyświetla zawartość kopca w postaci drzewa binarnego, wykorzystując zmodyfikowany nieco algorytm z poprzedniego rozdziału.
Pascal
// Tworzenie kopca w tablicy
// Data: 27.02.2013
// (C)2013 mgr Jerzy Wałaszek
//------------------------------

program create_heap;

// Zmienne globalne

var
  // łańcuchy do znaków ramek
  cr, cl, cp : string;
  // kopiec
  T : array [0..14] of integer;
  // liczba węzłów
  n : integer;

// Procedura wypisuje drzewo
//--------------------------
procedure print_bt(sp, sn : string;
                  v : integer);
var
  s : string;
  i : integer;
begin
  if v < n then
  begin
    s := sp;
    if sn = cr then
      s[length(s)-1] := ' ';
    print_bt(s+cp, cr, 2*v+2);

    s := Copy(sp, 1, length(sp)-2);
    for i := 1 to length(s) do
      write(char(ord(s[i])));
    for i := 1 to length(sn) do
      write(char(ord(sn[i])));
    writeln(T[v]);

    s := sp;
    if sn = cl then
      s[length(s)-1] := ' ';
    print_bt(s+cp, cl, 2*v+1);
  end;
end;

// procedura wstawia v do kopca
//-----------------------------
procedure heap_push(v : integer);
var
  i, j : integer;
begin
  i := n;
  inc(n);
  j := (i-1) div 2;

  while(i > 0) and (T[j] < v) do
  begin
    T[i] := T[j];
    i := j;
    j := (i-1) div 2;
  end;

  T[i] := v;
end;

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

var
  i, v : integer;
begin
  // ustawiamy łańcuchy znakowe, 
  // ponieważ nie wszystkie edytory
  // pozwalają wstawiać znaki konsoli
  // do tworzenia ramek.

  // cr = +--
  //      |

  // cl = |
  //      +--

  // cp = |
  //      |

  cr := #218#196;
  cl := #192#196;
  cp := #179#32;

  randomize;

  n := 0;

  for i := 1 to 15 do
  begin
    v := random(100);
    write(v, ' ');
    heap_push(v);
  end;

  writeln;
  writeln;

  print_bt('', '', 0);
end.
C++
// Tworzenie kopca w tablicy
// Data: 27.02.2013
// (C)2013 mgr Jerzy Wałaszek
//------------------------------

#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>

using namespace std;

// Zmienne globalne

// łańcuchy do znaków ramek
string cr, cl, cp;
// tablica na kopiec
int T [15];
// liczba elementów w kopcu
int n = 0;

// Procedura wypisuje drzewo
//--------------------------
void print_bt(string sp, 
             string sn, 
             int v)
{
  string s;

  if(v < n)
  {
    s = sp;
    if(sn == cr)
      s[s.length()-2] = ' ';
    print_bt(s+cp, cr, 2*v+2);

    s = s.substr(0, sp.length()-2);
    cout << s << sn << T[v]
         << endl;

    s = sp;
    if(sn == cl)
        s[s.length()-2] = ' ';
    print_bt(s+cp, cl, 2*v+1);
  }
}

// procedura wstawia v do kopca
//-----------------------------
void heap_push(int v)
{
  int i, j;

  i = n++;
  j = (i-1)/2;

  while(i > 0 && T[j] < v)
  {
    T[i] = T[j];
    i = j;
    j = (i-1)/2;
  }

  T[i] = v;
}

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

int main()
{
  int i, v;

  srand(time(NULL));

  // ustawiamy łańcuchy znakowe, 
  // ponieważ nie wszystkie edytory
  // pozwalają wstawiać znaki konsoli
  // do tworzenia ramek.

  // cr = +--
  //      |

  // cl = |
  //      +--

  // cp = |
  //      |

  cr = cl = cp = "  ";
  cr[0] = 218; cr[1] = 196;
  cl[0] = 192; cl[1] = 196;
  cp[0] = 179;

  for(i = 0; i < 15; i++)
  {
    v = rand() % 100;
    cout << v << " ";
    heap_push(v);
  }

  cout << endl << endl;
  print_bt("", "", 0);

  return 0;
}
Basic
' Tworzenie kopca w tablicy
' Data: 27.02.2013
' (C)2013 mgr Jerzy Wałaszek
'------------------------------

Dim Shared As Integer T(0 To 14)
Dim Shared As Integer n = 0
Dim Shared As String * 2 crx, clx, cpx

' Procedura wypisuje drzewo
'--------------------------
Sub print_bt(sp As String, _
            sn As String, _
            v As Integer)
  Dim As String s

  If v < n Then
    s = sp
    If sn = crx Then _
      Mid(s, Len(s)-1, 1) = " "
    print_bt(s+cpx, crx, 2*v+2)

    s = Mid(s, 1, Len(sp)-2)
    Print Using "&&&";s;sn;T(v)

    s = sp
    If sn = clx Then _
      Mid(s, Len(s)-1, 1) = " "
    print_bt(s+cpx, clx, 2*v+1)
  End If
End Sub

' procedura wstawia v do kopca
'-----------------------------
Sub heap_push(v As Integer)

  Dim As Integer i, j

  i = n
  n += 1
  j = (i-1)\2

  While i > 0 AndAlso T(j) < v
    T(i) = T(j)
    i = j
    j = (i-1)\2
  Wend

  T(i) = v
End Sub

' **********************
' *** PROGRAM GŁÓWNY ***
' **********************

Dim As Integer i, v

Randomize

' ustawiamy łańcuchy znakowe, 
' ponieważ nie wszystkie edytory
' pozwalają wstawiać znaki konsoli
' do tworzenia ramek.

' crx = +--
'       |

' clx = |
'       +--

' cpx = |
'       |

crx = Chr(218)+Chr(196)
clx = Chr(192)+Chr(196)
cpx = Chr(179)+" "
 
For i = 1 To 15
  v = Int(Rnd()*100)
  print v;
  heap_push(v)
Next

Print
Print

print_bt("", "", 0)
End
Python (dodatek)
# Tworzenie kopca w tablicy
# Data: 09.07.2024
# (C)2024 mgr Jerzy Wałaszek
# ---------------------------

import random

# zmienne globalne

# łańcuchy do znaków ramek
# łańcuchy do ramek
cr = "┌─"
cl = "└─"
cp = "│ "
# tablica na kopiec
t = [0] * 15
# liczba elementów w kopcu
n = 0


# Procedura wypisuje drzewo
# --------------------------
def print_bt(sp, sn, v):
    global cr, cl, cp

    if v < n:
        s = sp
        if sn == cr:
            s = s[:-2] + ' ' + s[-1]
        print_bt(s + cp, cr, 2 * v + 2)
        s = s[:-2]
        print(s, sn, t[v], sep="")
        s = sp
        if sn == cl:
            s = s[:-2] + ' ' + s[-1]
        print_bt(s + cp, cl, 2 * v + 1)


# procedura wstawia v do kopca
# -----------------------------
def heap_push(v):
    global t, n

    i = n
    n += 1
    j = (i - 1) // 2

    while i and t[j] < v:
        t[i] = t[j]
        i = j
        j = (i - 1) // 2
    t[i] = v


# **********************
# *** PROGRAM GŁÓWNY ***
# **********************

for i in range(15):
    v = random.randrange(100)
    print(v, end=" ")
    heap_push(v)
print()
print()
print_bt("", "", 0)
Wynik:
 87 59 40 41 7 40 24 49 29 83 68 94 41 20 62

    ┌─24
  ┌─62
  │ └─20
┌─87
│ │ ┌─40
│ └─41
│   └─40
94
│   ┌─59
│ ┌─68
│ │ └─7
└─83
  │ ┌─29
  └─49
    └─41

do podrozdziału  do strony 

Usuwanie elementu ze szczytu kopca – pop

Usuwamy korzeń drzewa, wstawiając na jego miejsce ostatni z liści. Następnie idziemy kolejnymi poziomami w dół drzewa, sprawdzając warunek kopca. Jeśli nie jest spełniony, to zamieniamy ojca z największym z synów. Operację kontynuujemy aż do momentu spełnienia warunku kopca lub osiągnięcia liścia. Prześledź poniższą tabelkę:

obrazek Korzeń drzewa usuwamy, zastępując go przez
ostatni liść.
obrazek Sprawdzamy warunek kopca. Nie jest spełniony,
zatem wymieniamy miejscami ojca z największym
synem i przechodzimy na niższy poziom drzewa.
obrazek Sprawdzamy warunek kopca. Nie jest spełniony,
zatem wymieniamy ojca z największym synem
i przechodzimy na niższy poziom drzewa.
obrazek Sprawdzamy warunek kopca. Nie jest spełniony,
zatem wymieniamy ojca z największym synem
i przechodzimy na niższy poziom drzewa.
obrazek Ponieważ element stał się liściem i nie ma synów,
kończymy.
Kopiec został przywrócony

Operacja usuwania elementu z kopca ma klasę złożoności obliczeniowej równą O(log n). Zwróć uwagę, że usuwany element posiada zawsze największą wartość w kopcu. Ta cecha czyni kopiec idealnym materiałem dla kolejek priorytetowych. Umożliwia również wykonanie sortowania o klasie złożoności obliczeniowej równej O(n×log n) – sortowanie kopcowe (ang. heap sort).

Algorytm usuwania szczytu kopca

Wejście:

T : tablica przechowująca elementy kopca.
n : liczba elementów w kopcu, n ∈ C.

Wyjście:

T : tablica z kopcem, z którego usunięto szczyt.
n : zmniejszone o 1.

Elementy pomocnicze:

i, j : indeksy elementów, i, j ∈ C.
: przechowuje element kopca.

Lista kroków:

K01: Jeśli n = 0, ; gdy kopiec jest pusty, 
     to zakończ ; nie ma co usuwać, kończymy
K02: nn-1 ; kopiec będzie zawierał
     ; o 1 element mniej
K03: vT[n] ; w v zapamiętujemy ostatni
     ; element kopca
K04: i ← 0 ; przeszukiwanie drzewa rozpoczynamy
     ; od korzenia w dół kopca
K05: j ← 1 ; j wskazuje lewego syna 
K06: Dopóki j < n, ; idziemy w dół kopca
     wykonuj kroki K07…K11 
K07:   Jeśli (j+1 < n)obrazek(T[j+1] > T[j]), ; szukamy
       to jj+1 ; większego syna
K08:   Jeśli vT[j], ; jeśli warunek kopca
       to idź do kroku K12 ; spełniony, 
       ; to wychodzimy z pętli
K09:   T[i] ← T[j] ; inaczej kopiujemy większego
       ; syna do ojca 
K10:   ij ; przechodzimy na pozycję większego
       ; syna 
K11:   j ← 2j+1 ; j wskazuje lewego syna 
K12: T[i] ← v ; umieszczamy v w kopcu
K13: Zakończ

Operacja usuwania elementu z kopca posiada klasę złożoności obliczeniowej O(log n).


Przykładowe programy

Uwaga:

Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich.

Program tworzy kopiec z 200 liczb pseudolosowych od -99 do 99. Następnie kolejno pobiera elementy ze szczytu kopca, aż kopiec będzie pusty. Pobierane elementy są wyświetlane w oknie konsoli. Zwróć uwagę, że są one uporządkowane malejąco (ponieważ na szczycie kopca zawsze jest aktualnie największy element). Własność ta pozwala wykorzystać kopiec do szybkiego sortowania w czasie O(n×log n). Sortowanie może odbywać się w miejscu. Więcej na ten temat znajdziesz w artykule o sortowaniu przez kopcowanie.
Pascal
// Usuwanie elementu z kopca
// Data: 5.03.2013
// (C)2013 mgr Jerzy Wałaszek
//---------------------------

program heapsort;

var
  // kopiec
  T : array [0..199] of integer;
  // liczba węzłów w kopcu
  n : integer;

// Procedura wstawia v do kopca
//-----------------------------
procedure heap_push(v : integer);
var
  i, j : integer;
begin
  i := n;
  inc(n);
  j := (i-1) div 2;
  while(i > 0) and (T[j] < v) do
  begin
    T[i] := T[j];
    i := j;
    j := (i-1) div 2;
  end;
  T[i] := v;
end;

// Procedura usuwa korzeń z kopca
//-------------------------------
procedure heap_pop;
var
  i, j, v : integer;
begin
  if n > 0 then
  begin
    dec(n);
    v := T[n];
    i := 0;
    j := 1;
    while j < n do
    begin
      if(j+1 < n) and (T[j+1] > T[j]) then
        inc(j);
      if v >= T[j] then break;
      T[i] := T[j];
      i := j;
      j := 2*j+1;
    end;
    T[i] := v;
  end;
end;

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

var
  i, v : integer;
begin
  randomize;
  n := 0;

  // Tworzymy kopiec
  for i := 1 to 200 do
  begin
    v := random(199)-99;
    write(v:4);
    heap_push(v);
  end;
  writeln;
  writeln;

  // Rozbieramy kopiec
  while n > 0 do
  begin
    write(T[0]:4);
    heap_pop;
  end;
  writeln;
  writeln;
end.
C++
// Usuwanie elementu z kopca
// Data: 5.03.2013
// (C)2013 mgr Jerzy Wałaszek
//------------------------------

#include <iostream>
#include <iomanip>
#include <ctime>
#include <cstdlib>

using namespace std;

int T[200]; // kopiec
int n = 0; // liczba węzłów

// Procedura wstawia v do kopca
//-----------------------------
void heap_push(int v)
{
  int i, j;

  i = n++;
  j = (i-1) / 2;
  while(i > 0 && T[j] < v)
  {
    T[i] = T[j];
    i = j;
    j = (i-1)/2;
  }
  T[i] = v;
}

// Procedura usuwa korzeń z kopca
//------------------------------
void heap_pop()
{
  int i, j, v;

  if(n--)
  {
    v = T[n];
    i = 0;
    j = 1;
    while(j < n)
    {
      if(j+1 < n && T[j+1] > T[j])
        j++;
      if(v >= T[j]) break;
      T[i] = T[j];
      i = j;
      j = 2*j+1;
    }
    T[i] = v;
  }
}

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

int main()
{
  int i, v;

  srand(time(NULL));

  // Tworzymy kopiec
  for(i = 0; i < 200; i++)
  {
    v = (rand()%199)-99;
    cout << setw(4) << v;
    heap_push(v);
  }
  cout << endl << endl;

  // Rozbieramy kopiec
  while(n)
  {
    cout << setw(4) << T[0];
    heap_pop();
  }
  cout << endl << endl;

  return 0;
}
Basic
' Usuwanie elementu z kopca
' Data: 5.03.2013
' (C)2013 mgr Jerzy Wałaszek
'---------------------------

' kopiec
Dim Shared As integer T(0 To 199)
' liczba węzłów
Dim Shared As Integer n = 0

' Procedura wstawia v do kopca
'-----------------------------
Sub heap_push(ByVal v As Integer)
  Dim As Integer i, j

  i = n
  n += 1
  j = (i-1)\2
  While(i > 0) AndAlso (T(j) < v)
    T(i) = T(j)
    i = j
    j = (i-1)\2
  Wend
  T(i) = v
End Sub

' Procedura usuwa korzeń z kopca
'------------------------------
Sub heap_pop()
  Dim As Integer i, j, v

  If n > 0 Then
    n -= 1
    v = T(n)
    i = 0
    j = 1
    While j < n
      if(j+1 < n) AndAlso _
        (T(j+1) > T(j)) Then _
        j += 1
      If v >= T (j) Then _
        Exit While
      T(i) = T(j)
      i = j
      j = 2*j+1
    Wend
    T(i) = v
  End If
End Sub

' **********************
' *** PROGRAM GŁÓWNY ***
' **********************

Dim As Integer i, v

Randomize

' Tworzymy kopiec
For i = 1 To 200
  v = Int(Rnd()*199)-99
  Print Using "####";v;
  heap_push(v)
Next
Print
Print

' Rozbieramy kopiec
While n > 0
  Print Using "####";T(0);
  heap_pop()
Wend
Print
Print
End
Python (dodatek)
# Usuwanie elementu z kopca
# Data: 10.07.2024
# (C)2024 mgr Jerzy Wałaszek
#---------------------------

import random

# kopiec
t = [0] * 200
# liczba węzłów
n = 0


# Procedura wstawia v do kopca
#-----------------------------
def heap_push(v):
    global n, t

    i = n
    n += 1
    j = (i-1)//2
    while i and t[j] < v:
        t[i] = t[j]
        i = j
        j = (i-1)//2
    t[i] = v


# Procedura usuwa korzeń z kopca
#-------------------------------
def heap_pop():
    global n, t

    if n:
        n -= 1
        v = t[n]
        i = 0
        j = 1
        while j < n:
            if (j+1 < n) and \
               (t[j+1] > t[j]):
                j += 1
            if v >= t[j]: break
            t[i] = t[j]
            i = j
            j = 2*j+1
        t[i] = v


# **********************
# *** PROGRAM GŁÓWNY ***
# **********************

# Tworzymy kopiec
for i in range(200):
    v = random.randrange(-99, 100)
    print("%4d" % v, end="")
    heap_push(v)
print()
print()
# Rozbieramy kopiec
while n:
  print("%4d" % t[0], end="")
  heap_pop()
print()
print()
Wynik:
  93  32  -5  74 -63   6 -81 -18 -88 -74  57  71  -9 -38  84  15 -48  72  88 -41
 -77 -76  92 -68 -51  12 -85   9  86 -12  61  74 -32  96  88 -94 -89  -4  20 -47
  -3   6  54 -71  22  63 -58  32  68 -19  43 -48 -62  56  11  17 -14  83 -65 -81
  68   7 -66  35 -70  -5  78  85  45  50 -88  60  97 -63 -40 -33  72  86 -79 -45
 -91 -28 -75  30 -91 -27 -92  90  94 -70  -1 -79 -39 -87 -40 -22  62  70 -32 -78
  98   7  96 -10 -95 -49  53  23  73  55  35 -17 -48  -9  27  -6 -50  80 -95  72
  65 -69  23  86 -22 -71  17 -84 -25   7 -77 -26 -89  96  -6  17  -3 -33  38 -55
 -61  41 -96  40  88  45 -55 -64 -36 -65   0 -24  29  17 -31  66  76 -16   8 -54
  16 -86  30  -7  97  83 -99  25  78 -35  33  58  79  19 -89 -27  48  89 -73 -26
   7 -75 -36 -86 -50  20 -68 -21  69  27 -59  80  41  40  29 -77 -38  -3  20  97

  98  97  97  97  96  96  96  94  93  92  90  89  88  88  88  86  86  86  85  84
  83  83  80  80  79  78  78  76  74  74  73  72  72  72  71  70  69  68  68  66
  65  63  62  61  60  58  57  56  55  54  53  50  48  45  45  43  41  41  40  40
  38  35  35  33  32  32  30  30  29  29  27  27  25  23  23  22  20  20  20  19
  17  17  17  17  16  15  12  11   9   8   7   7   7   7   6   6   0  -1  -3  -3
  -3  -4  -5  -5  -6  -6  -7  -9  -9 -10 -12 -14 -16 -17 -18 -19 -21 -22 -22 -24
 -25 -26 -26 -27 -27 -28 -31 -32 -32 -33 -33 -35 -36 -36 -38 -38 -39 -40 -40 -41
 -45 -47 -48 -48 -48 -49 -50 -50 -51 -54 -55 -55 -58 -59 -61 -62 -63 -63 -64 -65
 -65 -66 -68 -68 -69 -70 -70 -71 -71 -73 -74 -75 -75 -76 -77 -77 -77 -78 -79 -79
 -81 -81 -84 -85 -86 -86 -87 -88 -88 -89 -89 -89 -91 -91 -92 -94 -95 -95 -96 -99

do podrozdziału  do strony 

Kolejka priorytetowa z kopcem

Kopiec szczególnie dobrze nadaje się do realizacji kolejki priorytetowej. Poniżej przedstawiamy prosty przykład programu obiektowego, który tworzy taką kolejkę i wykorzystuje ją. Program jest modyfikacją programu przedstawionego w rozdziale o kolejkach priorytetowych.

Program tworzy obiekt kolejki priorytetowej o rozmiarze 10 elementów. Generuje dziesięć razy liczbę losową od 0 do 99 oraz odpowiadający jej priorytet od 0 do 9. Liczbę z priorytetem umieszcza w kolejce. Następnie pobiera z kolejki kolejne elementy i wyświetla w oknie konsoli. Zwróć uwagę, że liczby zostają z kolejki pobrane zgodnie z malejącą kolejnością ich priorytetów.

Uwaga, program nie sprawdza, czy dane mieszczą się w kolejce.

Pascal
// Kolejka priorytetowa z kopcem
// Data: 5.03.2013
// (C)2013 mgr Jerzy Wałaszek
//------------------------------

program heap_queue;

// Definicja typu obiektowego queue
//---------------------------------
  type
    Qel = record
      prio : integer;
      Data: integer;
    end;

    TQel = array of Qel;

  Queue = object
    private
      // kopiec dynamiczny
      T : TQel;
      // liczba elementów
      n : integer;

    public
      constructor init(max_n : integer);
      destructor  destroy;
      function    q_empty : boolean;
      function    q_front : integer;
      function    q_frontprio: integer;
      procedure   push(prio, v : integer);
      procedure   q_pop;
  end;

//----------------------
// Metody obiektu Queue
//----------------------

// Konstruktor-rezerwuje pamięć na kopiec
//---------------------------------------
constructor Queue.init(max_n : integer);
begin
  // tworzymy tablicę dynamiczną
  SetLength(T, max_n);
  n := 0; // kopiec jest pusty
end;

// Destruktor-usuwa tablicę z pamięci
//-----------------------------------
destructor Queue.destroy;
begin
  SetLength(T, 0);
end;

// Sprawdza, czy kolejka jest pusta
//---------------------------------
function Queue.q_empty : boolean;
begin
  q_empty := (n = 0);
end;

// Zwraca początek kolejki.
// Wartość specjalna to -MAXINT
//-----------------------------
function Queue.q_front : integer;
begin
  if n = 0 then q_front := -MAXINT
  else          q_front := T[0].data;
end;

// Zwraca priorytet pierwszego elementu
//-------------------------------------
function Queue.q_frontprio : integer;
begin
  if n = 0 then q_frontprio := -MAXINT
  else          q_frontprio := T[0].prio;
end;

// Zapisuje do kolejki wg priorytetu
//----------------------------------
procedure Queue.push(prio, v : integer);
var
  i, j : integer;
begin
  i := n;
  inc(n);
  j := (i-1) div 2;
  while(i > 0) and (T[j].prio < prio) do
  begin
    T[i] := T[j];
    i := j;
    j := (i-1) div 2;
  end;
  T[i].prio := prio;
  T[i].Data:= v;
end;

// Usuwa z kolejki
//----------------
procedure Queue.q_pop;
var
  i, j, v, p : integer;
begin
  if n > 0 then
  begin
    dec(n);
    p := T[n].prio;
    v := T[n].data;
    i := 0;
    j := 1;
    while j < n do
    begin
      if(j+1 < n) and
        (T[j+1].prio > T[j].prio) then
        inc(j);
      if p >= T[j].prio then break;
      T[i] := T[j];
      i := j;
      j := 2*j+1;
    end;
    T[i].prio := p;
    T[i].Data:= v;
  end;
end;

//---------------
// Program główny
//---------------

var
  // kolejka 10-cio elementowa
  Q : Queue;
  i, p, v : integer;

begin
  Randomize;
  Q.init(10);
  for i := 1 to 10 do
  begin
    v := random(100);
    p := random(10);
    writeln(v:2, ':', p);
    Q.push(p, v);
  end;

  writeln('----');

  while not Q.q_empty do
  begin
    writeln(Q.q_front:2, ':', Q.q_frontprio);
    Q.q_pop;
  end;

  Q.destroy;
end.
C++
// Kolejka priorytetowa z kopcem
// Data: 5.03.2013
// (C)2013 mgr Jerzy Wałaszek
//------------------------------

#include <iostream>
#include <iomanip>
#include <ctime>
#include <cstdlib>

using namespace std;

const int MAXINT = -2147483647;

// Definicja typu obiektowego Queue
//----------------------------------

 struct Qel
 {
   int prio, data;
 };

class Queue
{
  private:
    // kopiec dynamiczny
    Qel * T;
    // liczba elementów
    int n;

  public:
    Queue(int max_n);
    ~Queue();
    bool empty();
    int  front();
    int  frontprio();
    void push(int prio, int v);
    void pop();
};

//----------------------
// Metody obiektu Queue
//----------------------

// Konstruktor-rezerwuje pamięć na kopiec
//---------------------------------------
Queue::Queue(int max_n)
{
  // tworzymy tablicę dynamiczną
  T = new Qel[max_n];
  n = 0; // kopiec jest pusty
}

// Destruktor-usuwa tablicę z pamięci
//-----------------------------------
Queue::~Queue()
{
  delete [] T;
}

// Sprawdza, czy kolejka jest pusta
//---------------------------------
bool Queue::empty()
{
  return !n;
}

// Zwraca początek kolejki.
// Wartość specjalna to MAXINT
//----------------------------
int Queue::front()
{
  return n ? T[0].Data: MAXINT;
}

// Zwraca priorytet pierwszego elementu
//-------------------------------------
int Queue::frontprio()
{
  return n ? T[0].prio : MAXINT;
}

// Zapisuje do kolejki wg priorytetu
//----------------------------------
void Queue::push(int prio, int v)
{
  int i, j;

  i = n++;
  j = (i-1)/2;
  while(i > 0 && T[j].prio < prio)
  {
    T[i] = T[j];
    i = j;
    j = (i-1)/2;
  }
  T[i].prio = prio;
  T[i].data = v;
}

// Usuwa z kolejki
//----------------
void Queue::pop()
{
  int i, j, v, p;

  if(n--)
  {
    p = T[n].prio;
    v = T[n].data;
    i = 0;
    j = 1;
    while(j < n)
    {
      if(j+1 < n &&
         T[j+1].prio > T[j].prio)
        j++;
      if(p >= T[j].prio) break;
      T[i] = T[j];
      i = j;
      j = 2*j+1;
    }
    T[i].prio = p;
    T[i].data = v;
  }
}

//---------------
// Program główny
//---------------

int main()
{
  // kolejka 10-cio elementowa
  Queue Q(10);
  int i, p, v;

  srand(time(NULL));

  for(i = 0; i < 10; i++)
  {
     v = rand()%100;
     p = rand()%10;
     cout << setw(2) << v << ":" << p
          << endl;
     Q.push(p, v);
  }

  cout << "----\n";

  while(!Q.empty())
  {
    cout << setw(2) << Q.front()
         << ":" << Q.frontprio()
         << endl;
    Q.pop();
  }
  return 0;
}
Basic
' Kolejka priorytetowa z kopcem
' Data: 5.03.2013
' (C)2020 mgr Jerzy Wałaszek
'------------------------------

Const MAXINT = 2147483647

' Definicja typu obiektowego Queue
'----------------------------------
Type Qel
  prio As Integer
  Data As Integer
End Type

Type Queue
  Private:
    T As Qel Ptr
    n As Integer

  Public:
    Declare Constructor(max_n As integer)
    Declare Destructor
    Declare Function q_empty As Integer
    Declare Function q_front As Integer
    Declare Function q_frontprio As Integer
    Declare Sub push(ByVal prio As Integer, _
                       ByVal v As Integer)
    Declare Sub q_pop
End Type

'---------------
' Program główny
'---------------

' kolejka 10-cio elementowa
Dim Q As Queue = 10
Dim As Integer i, p, v

Randomize

For i = 1 To 10
  v = Int(Rnd*100)
  p = Int(Rnd*10)
  Print Using "##:#";v;p
  Q.push(p, v)
Next

Print "----"

While Q.q_empty = 0
  Print Using "##:#";Q.q_front;Q.q_frontprio
  Q.q_pop
Wend
End

'----------------------
' Metody obiektu Queue
'----------------------

' Konstruktor-tworzy pustą listę
'---------------------------------
Constructor Queue(max_n As Integer)
  T = New Qel[max_n]
  n = 0
End Constructor

' Destruktor-usuwa listę z pamięci
'-----------------------------------
Destructor Queue
  Delete [] T
End Destructor

' Sprawdza, czy kolejka jest pusta
'---------------------------------
Function Queue.q_empty As Integer
  If n = 0 Then Return 1
  Return 0
End Function

' Zwraca początek kolejki.
' Wartość specjalna to -MAXINT
'-----------------------------
Function Queue.q_front As Integer
  If n = 0 then
    q_front = -MAXINT
  Else
    q_front = T[0].data
  End If
End Function

' Zwraca priorytet pierwszego elementu
'-------------------------------------
Function Queue.q_frontprio As Integer
  If n = 0 then
    q_frontprio = -MAXINT
  Else
    q_frontprio = T[0].prio
  End If
End Function

' Zapisuje do kolejki
'--------------------
Sub Queue.push(ByVal prio As Integer, _
                  ByVal v As Integer)
  Dim As Integer i, j

  i = n
  n += 1
  j = (i-1)\2
  While i > 0 AndAlso T[j].prio < prio
    T[i] = T[j]
    i = j
    j = (i-1)\2
  Wend
  T[i].prio = prio
  T[i].data = v
End Sub

' Usuwa z kolejki
'----------------
Sub Queue.q_pop
  Dim As Integer i, j, v, p

  If n > 0 Then
    n -= 1
    p = T[n].prio
    v = T[n].data
    i = 0
    j = 1
    While j < n
      if(j+1 < n) AndAlso _
        (T[j+1].prio > T[j].prio) Then _
        j += 1
      If p >= T[j].prio Then Exit While
      T[i] = T[j]
      i = j
      j = 2*j+1
    Wend
    T[i].prio = p
    T[i].data = v
  End If
End Sub
Python (dodatek)
# Kolejka priorytetowa z kopcem
# Data: 11.07.2024
# (C)2024 mgr Jerzy Wałaszek
#------------------------------

import random

# wartość specjalna
MAXINT = 2147483647


# Element tablicy
#----------------
class Qel:


    def __init__(self):
        self.prio = 0
        self.data = 0


# Definicja typu obiektowego Queue
#----------------------------------
class Queue:


    # Konstruktor
    def __init__(self, max_n):
        self.t = []
        self.n = 0
        for i in range(max_n):
            self.t.append(Qel())

    # destruktor
    def __del__(self):
        self.t = None

    # kolejka jest pusta?
    def empty(self):
        return not self.n

    # zwraca początek kolejki
    # wartość specjalna to -MAXINT
    def front(self):
        if self.n:
            return self.t[0].data
        else:
            return -MAXINT

    # priorytet pierwszego elementu
    def frontprio(self):
        if self.n:
            return self.t[0].prio
        else:
            return -MAXINT

    # zapis do kolejki wg priorytetu
    def push(self, p, v):
        i = self.n
        self.n += 1
        j = (i-1)//2
        while i and (self.t[j].prio < p):
            self.t[i].prio = self.t[j].prio
            self.t[i].data = self.t[j].data
            i = j
            j = (i-1)//2
        self.t[i].prio = p
        self.t[i].data = v

    # Usuwa z kolejki
    def pop(self):
        if self.n:
            self.n -= 1
            p = self.t[self.n].prio
            v = self.t[self.n].data
            i, j = 0, 1
            while j < self.n:
                if (j+1 < self.n) and \
                   (self.t[j+1].prio > self.t[j].prio):
                    j += 1
                if p >= self.t[j].prio: break
                self.t[i].prio = self.t[j].prio
                self.t[i].data = self.t[j].data
                i = j
                j = 2*j+1
            self.t[i].prio = p
            self.t[i].data = v


#---------------
# Program główny
#---------------

# kolejka 10-cio elementowa
q = Queue(10)
for i in range(10):
    v = random.randrange(100)
    p = random.randrange(10)
    print("%2d:%d" %(v, p))
    q.push(p, v)
print("----")
while not q.empty():
    print("%2d:%d" %(q.front(),
                     q.frontprio()))
    q.pop()
Wynik:
 3:1
61:6
82:8
77:1
 5:7
60:4
99:2
53:7
91:0
12:9
----
12:9
82:8
53:7
 5:7
61:6
60:4
99:2
 3:1
77:1
91:0

do podrozdziału  do strony 

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.