Modul Praktikum Struktur Data (Dengan Python)
Modul Praktikum Struktur Data (Dengan Python)
Struktur Data
Versi 16.03
Daftar Isi
Daftar Isi ........................................................................................................................................ 2
Kata Pengantar .............................................................................................................................. 4
Lab 1: Python: List, String, Tuple, Set, Dictionary dan Class ......................................................... 5
1.0 Obyektif ............................................................................................................................... 5
1.1 List ....................................................................................................................................... 5
1.2 String ................................................................................................................................... 7
1.3 Tuple .................................................................................................................................... 8
1.4 Set........................................................................................................................................ 8
1.5 Dictionary ............................................................................................................................ 9
1.6 Struktur Data Kelas: Mengenal Pendekatan Berorientasi Obyek ..................................... 11
1.8 Rangkuman........................................................................................................................ 12
1.9 Tugas Pendahuluan ........................................................................................................... 12
1.10 Tugas Lanjutan ................................................................................................................ 12
1.11 Latihan Mandiri ............................................................................................................... 12
Lab 2: Stack & Queue .................................................................................................................. 14
2.0 Obyektif ............................................................................................................................. 14
2.1 Stack .................................................................................................................................. 14
2.2 Queue ................................................................................................................................ 20
2.3 Simulasi: Antrian Mencetak dalam Jaringan ..................................................................... 22
2.4 Rangkuman........................................................................................................................ 26
2.5 Tugas Pendahuluan ........................................................................................................... 27
2.6 Tugas Lanjutan .................................................................................................................. 27
2.7 Latihan Mandiri ................................................................................................................. 28
Lab 3: Deque & Linked-List .......................................................................................................... 29
3.0 Obyektif ............................................................................................................................. 29
3.1 Deque ................................................................................................................................ 29
3.2 List Lanjutan ...................................................................................................................... 31
3.3 Rangkuman........................................................................................................................ 37
3.4 Tugas Pendahuluan ........................................................................................................... 37
3.5 Tugas Lanjutan .................................................................................................................. 37
3.6 Latihan Mandiri ................................................................................................................. 37
4.7
Kata Pengantar
Alhamdulillah, modul praktikum Struktur Data edisi baru 16.03 ini dapat diselesaikan sesuai
dengan jadwal pelaksanaan praktikum yang ditetapkan. Modul ini berbeda 100% dengan
modul praktikum yang digunakan tahun sebelumnya (2015). Pada tahun ini, praktikum
Struktur Data menggunakan bahasa Pemrograman Python, begitu pula pembahasan dalam
modul ini, tepatnya Python versi 3 (terakhir 3.5.1).
Modul ini dibagi ke dalam 5 aktifitas lab yang direncanakan dapat dituntaskan dalam satu
semester Praktikum. Walaupun jumlah pertemuannya sedikit, namun kandungannya sudah
sangat mewakili konsep Struktur Data yang dibahas di kuliah, termasuk praktik tentang
struktur data linier, Stack, Antrian (Queue), Deque (antrian dua pintu), List, Linked-list (list
bersambungan), Tree (pohon biner), fungsi rekursif, Searching (pencarian) dan Sorting
(pengurutan). Satu-satunya jenis struktur data yang tidak dijelaskan dalam modul ini adalah
Graf (Graph). Graf merupakan materi terakhir dalam kuliah Struktur Data dan dapat dianggap
sebagai topik paling berat. Dua alasan tersebut menyebabkan graf tidak dimasukkan dalam
modul ini, mungkin dapat berubah di tahun depan.
Referensi utama dalam pembuatan modul ini adalah Problem Solving with Algorithms and
Data Structures yang ditulis oleh Brad Miller dan David Ranum (dapat didownload di
interactivepython.org/runestone/static/pythonds/index.html). Referensi lain yang sangat
mendukung adalah buku Data Structures and Algorithms in Python karya Michael T. Goodrich,
Roberto Tamassia dan Michael H. Goldwasser yang merupakan rujukan utama kuliah Struktur
Data saat ini. Beberapa tulisan di Wikipedia juga turut melengkapi modul ini.
Kami mengucapkan terimakasih kepada Program Studi Teknik Informatika Universitas
Trunojoyo Madura yang telah mempercayakan kami menata kembali materi Praktikum
Struktur Data mengikuti tren bahasa pemrograman terkini. Terimakasih pula untuk para
Asisten dan Laboran yang selalu berkoordinasi dalam penyelesaian modul ini. Kami menunggu
dan berterimakasih atas semua feedback dari siapapun yang membaca modul ini, untuk
menghasilkan modul yang lebih baik nantinya.
Semoga modul ini dapat memandu para Asisten Praktikum dan Praktikan dalam melaksanakan
kegiatan Praktikum Struktur Data menggunakan Python.
1.1 List
Selain kelas numerik dan boolean, Python mempunyai sejumlah kelas koleksi built-in yang
sangat powerful. Lists, strings dan tuples adalah koleksi terurut (ordered) yang sangat mirip
secara struktur tetapi mempunyai perbedaan spesifik yang harus dipahami agar dapat
digunakan dengan tepat. Sets dan dictionaries adalah koleksi tak-terurut (unordered).
List (daftar) adalah suatu koleksi berurut beranggotakan nol atau lebih rujukan ke obyek data
Python. List ditulis sebagai nilai-nilai terpisahkan koma di dalam kurung siku. List kosong
diwakili oleh [ ]. List bersifat heterogen, artinya obyek-obyek data di dalamnya tidak harus
berasal dari kelas yang sama dan koleksi tersebut dapat berikan ke suatu variabel.
Coba tuliskan 3 baris instruksi ini di dalam modus interaktif (menggunakan software IDLE) dan
perhatikan hasilnya:
>>>[1,3,True,6.5]
>>>myList = [1,3,True,6.5]
>>>myList
Tabel 1.1 Operasi terhadap Sequence di dalam Python
Nama Operasi
Operator
Penjelasan
Indexing
[]
Concatenation
Repetition
Membership
In
Length
Len
Slicing
[:]
>>>print(A)
>>>myList[2]=45
>>>print(A)
Tabel 1.2 Metode-metode yang disediakan oleh List di dalam Python
Nama Metode
Cara Penggunaan
Penjelasan
append
alist.append(item)
insert
alist.insert(i,item)
pop
alist.pop()
pop
alist.pop(i)
sort
alist.sort()
alist.reverse()
del alist[i]
index
alist.index(item)
count
alist.count(item)
alist.remove(item)
reverse
del
remove
1.2 String
String adalah koleksi sequential dari nol atau lebih huruf, bilangan dan simbol-simbol lain. Hurufhuruf, bilangan dan simbol-simbol lain tersebut dinamakan sebagai karakter. Nilai string literal
dibedakan dari pengenal (identifiers) dengan menggunakan tanda quotation (single atau double).
Cobalah 5 baris di bawah ini dan perhatikan hasilnya:
>>>"Susantoso"
>>>myName="Susantoso"
>>>myName[3]
>>>myName*2
>>>len(myName)
Berikut ini adalah contoh pemanfaatan metode yang tersedia dalam kelas Strings:
>>>myName
>>>myName.upper()
>>>myName.center(10)
>>>myName.find('v')
>>>myName.split('v')
Tabel 1.3 Metode bagi Strings di dalam Python
Nama Metode
center
count
ljust
lower
rjust
find
split
Cara Penggunaan
astring.center(w)
Penjelasan
Mengembalikan string di tengah dalam field
berukuran w
astring.count(item) Mengembalikan jumlah kehadiran item
dalam string
astring.ljust(w)
Mengembalikan string left-justified dalam
field berukuran w
astring.lower()
Mengembalikan string dalam huruf kecil
astring.rjust(w)
Mengembalikan string right-justified dalam
field ukuran w
astring.find(item) Mengembalikan index dari kemuculan
pertama dari item
astring.split(schar) Memecah string menjadi substring-substring
pada schar
Perbedaan besar antara lists dan strings adalah bahwa lists dapat dimodifikasi sedangkan
string tidak. Ini dikenal sebagai mutability. Lists bersifat mutable; strings dikatakan immutable.
Kita dapat mengubah item dalam list menggunakan indexing dan assignment, tetapi hal
tersebut tidak berlakukan untuk string.
>>>myList
>>>myList[0]=2**10
>>>myList
>>>myName
>>>myName[0]='X'
1.3 Tuple
Tuples sangat mirip dengan list dalam hal keberagaman rangkaian data. Perbedaannya adalah
tuple bersifat immutable, seperti string. Tuples ditulis sebagai nilai-nilai dipisahan koma di
dalam kurung. Karena berbentuk sequences maka operasi-operasi di atas dapat diterapkan
terhadap tuples.
>>>myTuple=(2,True,4.96)
>>>myTuple
>>>len(myTuple)
>>>myTuple[0]
>>>myTuple*3
>>>myTuple[0:2]
1.4 Set
Suatu set adalah koleksi tak berurut dari nol atau lebih obyek data Python yang immutable.
Sets tidak membolehkan duplikasi dan nilai-nilai dipisahkan koma di dalam kurung kurawal.
Himpunan kosong diwakili oleh set(). Set bersifat heterogen, dan koleksi ini dapat diberikan ke
suatu variabel.
>>>{3,6,"cat",4.5,False}
>>>mySet={3,6,"cat",4.5,False}
>>>mySet
Set tidak bersifat sequential, tetapi masih dapat menggunanakan operasi-operasi di atas. Tabel
4 merangkum operasi-operasi tersebut.
Tabel 1.4 Operasi terhadap Set di dalam Python
Nama Operasi
Membership
Length
|
Operator
in
len
aset | otherset
&
aset otherset
<=
Penjelasan
Keanggotaan Himpunan (set)
Mengembalikan kardinalitas dari himpunan
Mengembalikan himpunan baru dengan semua
elemen dari kedua set
Mengembalikan set baru dengan hanya elemenelemen yang hadir di kedua set
Mengembalikan himpunan baru dengan semua item
dari set pertama yang tidak ada di set kedua
Menanyakan apakah semua elemen dari set pertama
ada dalam set kedua?
>>>mySet
>>>len(mySet)
>>>False in mySet
>>>"dog" in mySet
>>>mySet
>>>yourSet={99,3,100}
>>>mySet.union(yourSet)
>>>mySet|yourSet
>>>mySet.intersection(yourSet)
>>>mySet&yourSet
>>>mySet.difference(yourSet)
>>>mySet-yourSet
>>>{3,100}.issubset(yourSet)
>>>{3,100}<=yourSet
>>>mySet.add("house")
>>>mySet
>>>mySet.remove(4.5)
>>>mySet
>>>mySet.pop()
>>>mySet
>>>mySet.clear()
>>>mySet
Tabel 1.5 Metode yang disediakan oleh Set di dalam Python
Nama Metode
union
Cara Penggunaan
aset.union(otherset)
intersection
aset.intersection(otherset)
difference
aset.difference(otherset)
issubset
aset.issubset(otherset)
add
remove
pop
clear
aset.add(item)
aset.remove(item)
aset.pop()
aset.clear()
Penjelasan
Mengembalikan himpunan baru dengan
semua elemen dari kedua set
Mengembalikan himpunan baru dengan
hanya elemen yang hadir di kedua set
Mengembalikan himpunan baru dengan
semua item yang hadir dalam set pertama
tetapi tidak hadir di dalam set kedua
Menanyakan apakah semua elemen dari
set tertentu ada dalam set yang lain
Menambahkan item ke suatu set
Menghapus item dari suatu set
Menghapus elemen tertentu dari suatu set
Menghapus semua elemen dari suatu set
1.5 Dictionary
Koleksi Python terakhir dari struktur tak-berurut adalah dictionary. Dictionaries adalah koleksi
pasangan item-item berasosiasi dimana setiap pasangan terdiri dari suatu key dan value.
Pasangan key-value ini ditulis seabagai key:value. Dictionaries ditulis dipisahkan koma dalam
kurung kurawal.
>>>capitals={'Iowa':'DesMoines','Wisconsin':'Madison'}
>>>capitals
Manipulasi terhadap dictionary dilakukan dengan mengakses nilai melalui key-nya atau dengan
menambahkan pasangan key-value berikutnya. Mirip dengan mengaksesan sequence, tetapi
tidak menggunakan inex, melainkan harus menggunakan key.
Listing 1.1 Program Python yang memanfaatkan dictionary
capitals = {'Iowa':'DesMoines','Wisconsin':'Madison'}
print(capitals['Iowa'])
capitals['Utah']='SaltLakeCity'
print(capitals)
capitals['California']='Sacramento'
print(len(capitals))
for k in capitals:
print(capitals[k]," is the capital of ", k)
Tabel 1.6 Operator yang disediakan oleh Dictionaries di dalam Python
Operator
[]
Penggunaan
mydict[k]
in
key in adict
del
del adict[key]
Penjelasan
Mengembalikan nilai berasosiasi dengan k,
jika tidak maka muncul error
Mengembalikan True jika key ada dalam
Dictionary, jika tidak False
Menghapus entry dari Dictionary
Penggunaan
adict.keys()
values
adict.values()
items
adict.items()
get
adict.get(k)
get
adict.get(k, alt)
Penjelasan
Mengembalikan keys dari dictionary dalam
obyek dict_keys
Mengembalikan values dari dictionary dalam
obyek dict_values
Mengembalikan pasangan key-value pairs
dalam obyek dict_items
Mengembalikan value yang berasosiasi dengan
k, jika tidak None
Mengembalikan nilai yang berasosiasi dengan
k, jika tidak alt
>>>phoneext={'saalimah':1410, 'hidayah':1137}
>>>phoneext
>>>phoneext.keys()
>>>list(phoneext.keys())
>>>phoneext.values()
>>>list(phoneext.values())
>>>phoneext.items()
10
>>>list(phoneext.items())
>>>phoneext.get("kent")
>>>phoneext.get("kent","NO ENTRY")
Listing 1.2 Fungsi Menghitung Akar Kuadrat. Tambahkan exception handling agar root atau n
dapat menerima nilai nol.
def squareroot(n):
root = n / 2
#tebakan awal akan berupa 1/2 of n
for k in range(20):
root = (1 / 2) * (root + (n / root))
return root
>>>squareroot(9)
>>>squareroot(4563)
11
1.8 Rangkuman
12
6.
7.
8.
9.
10.
umum, ini merupakan konstrain yang tidak penting. Lakukan perubahan terhadap
konstruktor agar memungkinkan pengguna melewatkan suatu penyebut negatif
sehingga semua operator tetap berlanjut bekerja dengan benar.
Telitilah metode __radd__ . Bagaimana perbedaannya dengan __add__? Kapan
digunakan? Implementasikan metode__radd__.
Ulangi pertanyaan terakhir tetapi kali ini untuk metode __iadd__.
Telitilah metode __repr__ . Apa bedanya dengan __str__? Kapan digunakan?
Implementasikan __repr__.
Rancanglah suatu kelas untuk merepresentasikan suatu permainan kartu. Rancanglah
kelas unutk mereprentasikan kumpulan kartu. Menggunakan dua kelas ini,
implementasikan suatu game kartu favorit anda.
Temukan permainan sudoku di majalah atau situs web. Tulislah program untuk
menyelesaikan masalah puzzle tersebut.
13
Memahami tipe data abstrak (Abstract Data Type, ADT) stack dan queue.
Mampu mengimplementasikan ADTs stack dan queue menggunakan list dari Python.
Memahami format ekspresi prefix, infix dan postfix dan menggunakan stacks untuk
mengevaluasi ekspresi postfix, serta untuk mengkonversi ekspresi infix kepada postfix.
Menggunakan queues untuk simulasi timing (pengaturan waktu) sederhana.
Mampu mengenali properti-properti masalah dimana stacks dan queues merupakan
struktur data yang tepat untuk digunakan.
2.1 Stack
Stacks, queues, deques dan lists merupakan contoh koleksi data yang item-itemmnya terurut
bergantung pada bagaimana item tersebut ditambahkan atau dihapus. Begitu suatu item
ditambahkan, ia mendiami posisi relatif terhadap elemen lain yang hadir sebelum dan
setelahnya. Koleksi demikian dinamakan struktu data linier. Struktur linier mempunyai dua
ujung, kadang dinamakan kiri dan kanan, depan dan belakang, juga top dan base.
Stack (disebut pula push-down stack) adalah koleksi berurut dari item-item dimana
penambahan item baru dan penghapusan item yang telah ada selalu terjadi di ujung yang
sama. Ujung ini dinamakan sebagai top. Ujung berlawanan dari top dikenal sebagai base.
Stack diurutkan mengikuti konsep LIFO (last in first out). Berikut ini adalah beberapa operasi
terhadap stack:
stack() membuat suatu stack baru yang kosong. Tidak memerlukan parameter dan
mengembalikan suatu stack kosong.
push(item) menambahkan suatu item baru ke atas (top) dari stack. Perlu item dan
tidak mengembalikan apapun.
pop() menghapus item teratas dari stack. Tidak perlu parameter dan mengembalikan
item. Stack berubah.
peek() mengembalikan top item dari stack tetapi tidak menghapusnya. Tidak
memerlukan parameter dan stack tidak berubah.
isEmpty() memeriksa apakah stack dalam keadaan kosong. Tidak memerlukan
parameter dan mengembalikan nilai boolean.
size() mengembalikan jumlah item di dalam stack. Tidak memerlukan parameter dan
mengembalikan suatu integer.
Sebagai contoh, jika s adalah suatu stack yang baru dibuat, tabel 2.1 memperlihatkan hasil
serangkaian operasi terhadap stack.
Listing 2.1 Implementasi kelas stack dengan list. Ujung dari list dianggap memegang elemen
teratas dari stack. Operasi push akan menambahkan item baru ke ujung list, operasi pop akan
mengubah ujung yang sama tersebut.
14
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
Tabel 2.1 Contoh Operasi pada Stack
Operasi Stack
s.isEmpty()
s.push(4)
s.push('dog')
s.peek()
s.push(True)
s.size()
s.isEmpty()
s.push(8.4)
s.pop()
s.pop()
s.size()
Isi Stack
[]
[4]
[4,'dog']
[4,'dog']
[4,'dog',True]
[4,'dog',True]
[4,'dog',True]
[4,'dog',True,8.4]
[4,'dog',True]
[4,'dog']
[4,'dog']
Nilai Kembalian
True
'dog'
3
False
8.4
True
2
Listing 2.2 Contoh pemanfaatan kelas Stack yang ada di dalam modul pythonds:
from pythonds.basic.stack import Stack
s=Stack()
print(s.isEmpty())
s.push(4)
s.push('dog')
print(s.peek())
s.push(True)
print(s.size())
print(s.isEmpty())
15
s.push(8.4)
print(s.pop())
print(s.pop())
print(s.size())
Listing 2.3 Implementasi stack dengan list dimana top menunjukkan awal dari list, bukan
ujungnya. Metode pop dan append sebelumnya tidak dapat digunakan lagi. Posisi indeks 0
(item pertama dalam list) dikenakan operasi pop dan push.
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
return self.items == []
def push(self, item):
self.items.insert(0, item)
def pop(self):
return self.items.pop(0)
def peek(self):
return self.items[0]
def size(self):
return len(self.items)
s = Stack()
s.push('hello')
s.push('true')
print(s.pop())
Listing 2.4 Memeriksa keseimbangan jumlah tanda kurung. Algoritma dimulai dengan suatu
stack kosong, memroses string berisi kurung dari kiri ke kanan. Jika suatu simbol adalah kurung
buka, push ke stack sebagai simbol bahwa simbol tutup yang terkait harus muncul nantinya.
Jika, pada sisi lain, suatu simbol adalah kurung tutup, pop stacknya. Selama masih mungkin
melakukan pop terhadap stack untuk mencocokkan setiap simbol tutup, kurung tetap
seimbang. Jika pada suatu waktu tidak ada simbol buka pada stack yang cocok dengan simbol
tutup, string tersebut dikatakan tidak seimbang. Pada akhir string, ketika semua simbol telah
diproses, stack akan kosong.
from pythonds.basic.stack import Stack
def parChecker(symbolString):
s = Stack()
balanced = True
index = 0
16
17
Ekspresi Prefix
+AB
+A*BC
*+ABC
++A*BCD
*+AB+CD
+*AB*CD
+++ABCD
18
Ekspresi Postfix
AB+
ABC*+
AB+C*
ABC*+D+
AB+CD+*
AB*CD*+
AB+C+D+
2.2 Queue
Queue atau antrian adalah suatu koleksi item berurut dimana penambahan item baru terjadi
di satu ujung bernama ekor (rear) dan penghapusan terjadi pada ujung lainnya yang
dinamakan kepala (front). Queues menggunakan pengurutan FIFO. Berikut ini adalah
beberapa operasi Queue:
queue() membuat suatu antrian baru yang kosong. Tidak memerlukan parameter dan
mengembalikan suatu antrian kosong.
enqueue(item) menambahkan suatu item baru ke ujung saru antrian. Perlu item dan
tidak mengembalikan sesuatu.
dequeue() menghapus item depan dari antrian. Tidak memerlukan parameter dan
mengembalikan itemnya. Antrian termodifikasi.
isEmpty() menguji untuk melihat apakah antrian dalam keadaan kosong. Tidak
memerlukan parameter dan mengembalian nilai boolean.
size() mengembalikan jumlah item yang ada di dalam antrian. Tidak memerlukan
parameter dan mengembalikan suatu integer.
Jika q adalah suatu queue yang baru dibuat (kosong) maka tabel 2.3 memperlihatkan
serangkaian operasi terhadap queue tersebut.
Tabel 2.3 Contoh Operasi terhadap Queue
Operasi Antrian
q.isEmpty()
q.enqueue(4)
q.enqueue('dog')
q.enqueue(True)
q.size()
q.isEmpty()
q.enqueue(8.4)
q.dequeue()
q.dequeue()
q.size()
Isi Antrian
[]
[4]
['dog',4]
[True,'dog',4]
[True,'dog',4]
[True,'dog',4]
[8.4,True,'dog',4]
[8.4,True,'dog']
[8.4,True]
[8.4,True]
Nilai Kembalian
True
3
False
4
'dog'
2
Listing 2.9: Implementasi Queue (antrian) dimana ekor adalah pada posisi 0 dari list. Ini
memungkinkan kita menggunakan fungsi insert untuk menambahkan elemen baru ke ekor dari
antrian. Operasi pop dapat digunakan untuk menghapus elemen di depan (elemen terakhir
dari list).
20
class Queue:
def __init__(self):
self.items=[]
def isEmpty(self):
return self.items==[]
def enqueue(self,item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
q=Queue()
q.enqueue(4)
q.enqueue('dog')
q.enqueue(True)
print(q.size())
Listing 2.10 Game untuk anak-anak Kentang Panas mensimulasikan antrian FIFO. Program
menerima input suatu list nama dan konstanta bernama num yang digunakan sebagai counting
(penghitungan). Program akan mengembalikan nama orang terakhir yang terisi setelah
counting berulang sebanyak num.
from pythonds.basic.queue import Queue
def hotPotato(namelist, num):
simqueue = Queue()
for name in namelist:
simqueue.enqueue(name)
while simqueue.size() > 1:
for i in range(num):
simqueue.enqueue(simqueue.dequeue())
simqueue.dequeue()
return simqueue.dequeue()
print(hotPotato(["Bill","David","Susan","Jane","Kent","Brad"],7))
21
Untuk setiap waktu (currentSecond): Apakah suatu tugas cetak akan dibuat? Jika Iya,
maka tambahkan itu ke queue dengan currentSecond sebagai timestampnya.
Jika printer tidak sibuk dan jika suatu task sedang menunggu, hapus task berikutnya
dari antrian cetak (print queue) dan serahkan task tersebut ke printer.
Kurangkan timestamp dari currentSecond untuk menghitung waktu tunggu (waiting
time) untuk task tersebut.
Tambahkan waiting time untuk task tersebut ke suatu list untuk pemrosesan akan
datang.
Berdasarkan pada jumlah halaman dalam print task, tetapkan berapa waktu yang
diperlukannya.
Printer sekarang melakukan satu detik pencetakan jika diperlukan. Ia juga
mengurangkan satu detik dari waktu yang diperlukan untuk tugas tersebut.
Jika task tersebut telah selesai (completed), dengan kata lain waktu yang diperlukan
telah mencapai nol, maka printer tidak sibuk lagi.
Setelah simulasi selesai, hitung waktu tunggu rata-rata (average waiting time) dari
daftar waktu tunggu yang dibangkitkan.
Simulasi ini dirancang dengan membuat kelas-kelas untuk tiga obyek nyata, yaitu: Printer, Task
dan PrintQueue. Kelas Printer diperlukan untuk mencatat adanya kehadiran task (mencetak).
Jika Ya, maka status printer menjadi busy dan waktu yang diperlukan dihitung dari jumlah
halaman dalam task tersebut. Konstruktor memungkinkan perubahan terhadap pages-perminute. Metode tick menurunkan internal timer dan mengatur printer ke status idle jika task
sudah diselesaikan.
Listing 2.11 Kelas Printer
class Printer:
def __init__(self,ppm):
self.pagerate = ppm
self.currentTask = None
self.timeRemaining = 0
def tick(self):
if self.currentTask != None:
self.timeRemaining = self.timeRemaining-1
if self.timeRemaining <= 0:
self.currentTask = None
def busy(self):
if self.currentTask!=None:
return True
22
else:
return False
defstartNext(self,newtask):
self.currentTask = newtask
self.timeRemaining = newtask.getPages()*60/self.pagerate
Kelas Task akan merepresentasikan suatu tugas printing. Saat task tersebut dibuat, suatu
pembangkit bilangan acak menyediakan halaman dengan panjang 1 s.d 20. Kita memilih untuk
menggunakan fungsi randrange dari modul random.
>>>importrandom
>>>random.randrange(1,21)
>>>random.randrange(1,21)
Setiap task perlu memelihara suatu timestamp yang digunakan untuk menghitung waiting
time. Timestamp ini akan merepresentasikan waktu yang task yang dibuat dan diletakkan di
dalam printer queue. Metode waitTime dapat digunakan kemudian untuk meretrieve jumlah
waktu yang dihabiskan di dalam antrian semulai printing dimulai.
Listing 2.12 Kelas Task
import random
class Task:
def __init__(self, time):
self.timestamp = time
self.pages = random.randrange(1,21)
def getStamp(self):
return self.timestamp
def getPages(self):
return self.pages
def waitTime(self, currenttime):
return currenttime - self.timestamp
Simulasi utama mengimplementasikan algoritma di atas. Obyek printQueue merupakan
instance dari ADT queue yang ada. Fungsi boolean, newPrintTask, memutuskan apakah suatu
printing task baru dibuatkan. Fungsi randrange digunakan untuk mengacak nilai 1 s.d 180.
Print tasks tiba sekali setiap 180 detik. Fungsi simulation memungkinkan kita mengatur waktu
total dan halaman per menit untuk printer tersebut.
Listing 2.13 Simulasi antrian pencetakan dokumen
from pythonds.basic.queue import Queue
import random
23
def simulation(numSeconds,pagesPerMinute):
labprinter = Printer(pagesPerMinute)
printQueue = Queue()
waitingtimes = []
for currentSecond in range (numSeconds):
if newPrintTask():
task = Task(currentSecond)
printQueue.enqueue(task)
if (not labprinter.busy()) and (not printQueue.isEmpty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labprinter.startNext(nexttask)
labprinter.tick()
averageWait=sum(waitingtimes)/len(waitingtimes)
print("Average Wait %6.2f secs %3d tasks
printQueue.size()))
remaining."%(averageWait,
def newPrintTask():
num=random.randrange(1,181)
if num==180:
returnTrue
else:
return False
for i in range(10):
simulation(3600,5)
Pada saat kita menjalankan simulasi, kita tidak perhatian dengan hasil yang berbeda setiap
waktu. Ini karena sifat probabilistik dari bilangan random. Kita tertarik pada tren itu terjadi
karena parameter simulation yang ditentukan.
Pertama, kita menjalankan simulation untuk periode 60 menit (3600 detik) menggunakan
suatu page rate 5 halaman per menit. Sebagai tambahan, kita menjalankan 10 percobaan
independen. Ingat bahwa karena simulation bekerja dengan bilangan random maka setiap run
akan mengembalikan hasil berbeda.
>>>for i in range(10):
simulation(3600,5)
Setelah menjalankan 10 trial kita dapat melihat bahwa waktu tunggu (wait time) rata-rata
adalah 122.155 seconds. Dapat pula dilihat bahwa ada variasi besar dalam waktu bobot ratarata dengan suatu rata-rata minimum 17.27 seconds dam maksimum 239.61 seconds. Juga
terlihat bahwa hanya dalam dua kasus semua task tuntas lengkap.
24
Sekarang, kita akan mengatur page rate menjadi 10 pages per minute dan menjalankan 10
trials lagi, dengan suatu page rate lebih cepat diharapkan lebih banyak tugas akan diselesaikan
dalam frame waktu satu jam.
>>>for i in range(10):
simulation(3600,10)
Listing 2.14 Kode program lengkap dari proses yang dijelaskan di atas (3 kelas dan metode di
dalamnya)
from pythonds.basic.queue import Queue
import random
class Printer:
def __init__(self, ppm):
self.pagerate = ppm
self.currentTask = None
self.timeRemaining = 0
def tick(self):
if self.currentTask != None:
self.timeRemaining = self.timeRemaining - 1
if self.timeRemaining <= 0:
self.currentTask = None
def busy(self):
if self.currentTask != None:
return True
else:
return False
def startNext(self,newtask):
self.currentTask = newtask
self.timeRemaining = newtask.getPages() * 60/self.pagerate
class Task:
def __init__(self,time):
self.timestamp = time
self.pages = random.randrange(1,21)
def getStamp(self):
return self.timestamp
def getPages(self):
return self.pages
def waitTime(self, currenttime):
return currenttime - self.timestamp
25
2.4 Rangkuman
26
Simulasi adalah pembangkit bilangan random (acak) untuk membuat situasi kehidupan
nyata (real-life) dan memungkinkan kita untuk menjawab pertanya berjenis what if
(bagaimana jika?)
27
Posisi antrian mobil pada suatu tempat pencucian mobil (car wash)
Antrian pelanggan pada kasir supermarket (grocery store)
Take-off dan landing pesawat terbang pada suatu run way (di bandara)
Seorang teller di bank
Pastikan untuk menyatakan suatu asumsi bahwa anda membuat dan menyediakan
suatu data probabilistik yang harus dipertimbangkan sebagai bagian dari scenario.
11. Lakukan perubahan terhadap simulasi permainan Hot Potato agar memungkinkan
nilai counting terpilih secara acak sehingga setiap pass tidak dapat diprediksi.
12. Implementasikan suatu mesin pengurutan radix. Pengurutan radix untuk Interger
basis 10 adalah suatu teknik pengurutan mekanis yang menggunakan sederetan
keranjang, satu keranjang utama (main) dan 10 keranjang digit (0 s.d 9) . Setiap
keranjang bertindak seperti suatu antrian dan memegang nilai-nilainya (terurut)
sesuai dengan kedatangannnya. Algortima bermula dengan penempatan setiap
bilangan dalam keranjang utama. Kemudian melihat setiap nilai digit demi digit. Nilai
pertama dihapus dan ditempatkan ke dalam keranjang digit yang berkaitan dengan
digit yang dilihat. Sebagai contoh, jika suatu digit yang dipertimbangkan/sedang
dilihat, 534 ditempatkan dalam keranjang digit 4 dan 667 diletakkan di keranjang digit
7. Setelah semua nilai diletakkan di dalam keranjang dikit yang bersesuaian, nilai-nilai
dikoleksi dari keranjang 0 s.d 9 dan diletakkan kembali ke dalam keranjang utama.
Proses tersebut berlanjut dengan digit puluhan, ratusan, dan seterusnya. Setelah digit
terakhir diproses, keranjang utama mengandung nilai-nilai yang terurut.
28
3.1 Deque
Deque (Deantrian), dikenal juga sebagai antrian berujung dua (double-ended), adalah suatu
koleksi item terurut serupa dengan queue. Perbedaannya? Sifat tidak terikat untuk
penambahan dan penghapusan item-item dari antrian. Item baru dapat ditambahkan ke depan
atau belakang. Karena itu, item yang ada dapat dihapuskan dari salah satu ujung. Struktur
linier hibrida ini menyediakan semua kapabilutas stack dan antrian dalam satu struktur data.
deque() membuat suatu deque baru yang kosong. Tidak perlu parameter dan
mengembalikan suatu deque kosong.
addFront(item) menambahkan suatu item baru ke depan dari deque. Perlu item dan
tidak mengembalikan apapun.
addRear(item) menambahkan suatu item baru ke ekor dari deque. Perlu item dam
tidak mengembalikan sesuatu.
removeFront() menghapus item depan dari deque. Tidak perlu parameter dan
mengembalikan item. Deque termodifikasi.
removeRear() menghapus item ujung (ekor) dari deque. Tidak perlu parameter dan
mengembalikan item. Deque berubah.
isEmpty() menguji apakah deque dalam kondisi kosong. Tidak perlu parameter dan
mengembalikan suatu nilai boolean.
29
size() mengembalikan jumlah item dalam deque. Tidak perlu parameter dan
mengembalikan suatu integer.
Jika d adalah deque yang baru dibuat dan kosong, tabel 3.1 memperlihatkan contoh operasi
terhadap d. Isi dari ujung depan (front) diletakkan di kanan. Penting sekali untuk menjaga track
dari depan dan belakang (rear) karena item-item dapat dipindahkan (keluar masuk koleksi)
secara bebas yang mungkin sedikit membingungkan.
Tabel 3.1 Contoh operasi terhadap deque
Operasi Deque
d.isEmpty()
d.addRear(4)
d.addRear('dog')
d.addFront('cat')
d.addFront(True)
d.size()
d.isEmpty()
d.addRear(8.4)
d.removeRear()
d.removeFront()
Isi Deque
[]
[4]
['dog',4,]
['dog',4,'cat']
['dog',4,'cat',True]
['dog',4,'cat',True]
['dog',4,'cat',True]
[8.4,'dog',4,'cat',True]
['dog',4,'cat',True]
['dog',4,'cat']
Nilai Kembalian
True
4
False
8.4
True
Listing 3.1 Implementasi ADT deque, ekor dari deque pada posisi 0 dari list
class Deque
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
d = Deque()
print(d.isEmpty())
30
d.addRear(4)
d.addRear('dog')
d.addFront('cat')
d.addFront(True)
print(d.size())
print(d.isEmpty())
d.addRear(8.4)
print(d.removeRear())
print(d.removeFront())
Dalam removeFront digunakan metode pop untuk menghapus elemen terakhir dari list.
Namun, dalam removeRear, metode pop(0) harus menghapus elemen pertama dari list.
Metode insert digunakan dalam addRear karena metode append menganggap penambahan
elemen baru ke ujung (akhir) list.
Listing 3.2 Palindrome-Checker menggunakan deque
from pythonds.basic.deque import Deque
def palchecker(aString):
chardeque = Deque()
for ch in aString:
chardeque.addRear(ch)
stillEqual = True
while chardeque.size() > 1 and stillEqual:
first = chardeque.removeFront()
last = chardeque.removeRear()
if first != last:
stillEqual = False
return stillEqual
print(palchecker("lsdkjfskf"))
print(palchecker("radar"))
31
List adalah koleksi item dimana setiap item berposisi relatif terhadap yang lain. Ini disebut pula
unordered list. List punya item pertama, kedua, kedtiga dan seterusnya. Dapat pula dirujuk
awal list (item pertama) atau ujung list (item terakhir). Untuk penyederhanaan, anggap list
tidak dapat mengandung item-item terduplikat (ganda).
Beberapa operasi list tak-berurut (unordered) adalah
List() membuat suatu list baru yang kosong. Tidak memerlukan parameter dan
mengembalikan suatu list kosong.
add(item) menambahkan suatu item baru ke dalam list. Diperlukan item yang akan
ditambahkan dan tidak mengembalikan apapun. Anggapan: item tersebut belum ada
dalam list.
remove(item) menghapus item dari dalam list. Diperlukan item dan akan mengubah
list. Anggapan: item tersebut tersedia di dalam list.
search(item) mencari item di dalam list. Perlu item dan mengembalikan suatu nilai
boolean.
isEmpty() menguji untuk melihat apakah list dalam keadaan kosong (empty). Tidak
memerlukan parameter dan mengembalikan suatu nilai boolean.
size() mengembalikan jumlah item di dalam list. Tidak memerlukan parametyer dan
mengembalikan suatu integer.
append(item) menambahkan item baru ke akhir (ujung) list dan menjadikannya
sebagai item terakhir di dalam koleksi. Diperlukan item dan tidak mengembalikan
apapun. Anggapan: item tersebut belum ada di dalam list.
index(item) mengembalikan posisi dari item di dalam list. Diperlukan item dan
mengembalikan index dari item tersebut. Anggapan: item terdapat di dalam list.
insert(pos, item) menambahkan suatu item baru ke dalam list pada posisi pos.
Diperlukan item dan tidak mengembalikan apapun. Anggapan: item belum terdapat di
dalam list dan ada cukup item existing (telah ada di dalam list) sehingga dapat
diketahui posisi pos.
pop() menghapus dan mengembalikan item terakhir di dalam list. Tidak memerlukan
parameter dan mengembalikan suatu item. Anggapan: list tidak kosong, setidaknya
ada satu item.
pop(pos) menghapus dan mengembalikan item pada posisi pos. Diperlukan posisi
sebagai parameter dan mengembalikan suatu item. Anggapan: item ada dalam list.
Listing 3.2 Kelas Node dan UnorderedList yang mengimplementasikan suatu linked-list (list
berkait atau bersambungan). Perlu ditentukan nilai data awal untuk node. Juga ada metode
untuk mengakses dan mengubah datanya dan referensi berikutnya. Perhatikan konstruktor,
metode add, search, remove dan bagaimana itu semua digunakan.
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
32
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class UnorderedList:
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def add(self,item):
temp = Node(item)
temp.setNext(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.getNext()
return count
def search(self,item):
current = self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
def remove(self,item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
33
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
mylist = UnorderedList()
mylist.add(31)
mylist.add(77)
mylist.add(17)
mylist.add(93)
mylist.add(26)
mylist.add(54)
print(mylist.size())
print(mylist.search(93))
print(mylist.search(100))
mylist.add(100)
print(mylist.search(100))
print(mylist.size())
mylist.remove(54)
print(mylist.size())
mylist.remove(93)
print(mylist.size())
mylist.remove(31)
print(mylist.size())
print(mylist.search(93))
Metode-metode lain diperlukan dalam mengelola list di atas, misalnya append, insert, index,
dan pop. Jadikan itu sebagai latihan (dikerjakan secara mandiri).
Bagaimana dengan ADT List berurut (ordered)? Jika list integer di atas dibuat ordered list (urut
ascending) maka dapat ditulis sebagai 17, 26, 31, 54, 77 dan 93. Karena 17 adalah item
terkecil, maka ia menempati posisi pertama dalam list. Demikian juga, karena 93 adalah yang
terbesar, ia menempati posisi terakhir.
Berikut ini adalah beberapa operasi yang dapat diberlakukan terhadap list berurut:
OrderedList() membuat suatu ordered list baru yang masih kosong. Tidak memerlukan
parameter dan mengembalikan suatu list kosong.
add(item) menambahkan suatu item baru ke dalam list, urutan dipersiapkan.
Memerlukan item sebagai parameter dan tidak mengembalikan apapun. Anggapan:
item tidak ada di dalam list.
34
remove(item) menghapus item dari list. Memerlukan item sebagai parameter dab
akan memodifikasi list. Anggapan: item telah ada di dalam list.
search(item) mencari item di dalam list. Perlu item sebagai parameter dan
mengembalikan suatu nilai boolean.
isEmpty() menguji apakah list dalam kondisi kosong. Tidak memerlukan parameter dan
mengembalikan suatu nilai boolean.
size() mengembalikan jumlah item dalam list. Tidak memerlukan parameter dan
mengembalikan suatu integer.
index(item) mengembalikan posisi dari item di dalam list. Memerlukan item sebagai
parameter dan mengembalikan index. Anggapan: item ada di dalam list.
pop() menghapus dan mengembalikan item terakhir dalam list. Tidak memerlukan
parameter dan mengembalikan suatu item. Anggapan: list mempunyai setidaknya satu
item.
pop(pos) menghapus dan mengembalikan item pada posisi pos. Perlu posisi sebagai
parameter dan mengembalikan item. Anggapan: item ada di dalam list.
35
else:
if current.getData() > item:
stop = True
else:
current = current.getNext()
return found
def add(self, item):
current = self.head
previous = None
stop = False
while current != None and not stop:
if current.getData() > item:
stop = True
else:
previous = current
current = current.getNext()
temp = Node(item)
if previous == None:
temp.setNext(self.head)
self.head = temp
else:
temp.setNext(current)
previous.setNext(temp)
def isEmpty(self):
return self.head == None
def size(self):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.getNext()
return count
mylist = OrderedList()
mylist.add(31)
mylist.add(77)
mylist.add(17)
mylist.add(93)
mylist.add(26)
mylist.add(54)
print(mylist.size())
print(mylist.search(93))
print(mylist.search(100))
36
3.3 Rangkuman
Deques adalah struktur data yang memungkinkan perilaku hibrid seperti stacks dan
queues.
Operasi fundamental bagi suatu deque adalah addFront, addRear, removeFront,
removeRear dan isEmpty.
Lists merupakan koleksi item-item dimana setiap item memegang suatu posisi relatif.
Implementasi linked list memelihara logical order tanpa memerlukan persyaratan
physical storage.
Modifikasi terhadap head dari linked list merupakan kasus khusus.
37
38
Listing 4.1 Fungsi iteratif untuk menghitung hasil penjumlahan bilangan di dalam suatu list
def listsum(numList):
theSum = 0
for i in numList:
theSum = theSum + i
return theSum
print(listsum([1,3,5,7,9]))
Listing 4.2 Penjumlahan dalam bentuk rekursi.
def listsum(numList):
if len(numList) == 1:
return numList[0]
else:
return numList[0] + listsum(numList[1:])
print(listsum([1,3,5,7,9]))
39
Bagaimana jika kita akan mengkonversi suatu integer ke string pada suatu basis: biner dan
hexadecimal. Misalnya mengubah integer 10 ke tipe stringnya menjadi "10" atau ke bentuk
binernya menjadi "1010". Ada banyak algoritma untuk menyelesaikan masalah ini, termasuk
stack, namun formulasi rekursif dianggap sangat elegan.
Listing 4.3 Implementasi Python untuk mengkonversi integer ke string pada basis 2 atau 16.
def toStr(n,base):
convertString = "0123456789ABCDEF"
if n < base:
return convertString[n]
else:
return toStr(n//base,base) + convertString[n%base]
print(toStr(1453,16))
Listing 4.4 Implementasi rekursi menggunakan stack
import Stack # sebagaimana didefinisikan sebelumnya
r_stack = Stack()
def to_str(n, base):
convert_string = "0123456789ABCDEF"
while n > 0:
if n < base:
r_stack.push(convert_string[n])
else:
r_stack.push(convert_string[n % base])
n = n // base
res = ""
while not r_stack.is_empty():
res = res + str(r_stack.pop())
return res
print(to_str(1453, 16))
40
last = midpoint-1
else:
first = midpoint+1
return found
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binarySearch(testlist, 3))
print(binarySearch(testlist, 13))
Listing 4.8 Fungsi pencarian biner dalam bentuk rekursif
def binarySearch(alist, item):
if len(alist) == 0:
return False
else:
midpoint = len(alist) // 2
if alist[midpoint] == item:
return True
else:
if item < alist[midpoint]:
return binarySearch(alist[ : midpoint], item)
else:
return binarySearch(alist[midpoint+1 : ], item)
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binarySearch(testlist, 3))
print(binarySearch(testlist, 13))
4.3 Pengurutan
Listing 4.9 Fungsi pengurutan item-item dalam list menggunakan pendekatan bubble.
def bubbleSort(alist):
for passnum in range(len(alist)-1,0,-1):
for i in range(passnum):
if alist[i]>alist[i+1]:
temp = alist[i]
alist[i] = alist[i+1]
alist[i+1] = temp
alist = [54,26,93,17,77,31,44,55,20]
bubbleSort(alist)
print(alist)
42
Listing 4.10 Fungsi pengurutan buble yang lebih cepat (Short Bubble Sort)
def shortBubbleSort(alist):
exchanges = True
passnum = len(alist)-1
while passnum > 0 and exchanges:
exchanges = False
for i in range(passnum):
if alist[i]>alist[i+1]:
exchanges = True
temp = alist[i]
alist[i] = alist[i+1]
alist[i+1] = temp
passnum = passnum-1
alist=[20,30,40,90,50,60,70,80,100,110]
shortBubbleSort(alist)
print(alist)
Listing 4.11 Fungsi quicksort untuk mengurutkan item-item di dalam List
def quickSort(alist):
quickSortHelper(alist,0,len(alist)-1)
def quickSortHelper(alist,first,last):
if first<last:
splitpoint = partition(alist,first,last)
quickSortHelper(alist,first,splitpoint-1)
quickSortHelper(alist,splitpoint+1,last)
def partition(alist,first,last):
pivotvalue = alist[first]
leftmark = first+1
rightmark = last
done = False
while not done:
while leftmark <= rightmark and alist[leftmark] <= pivotvalue:
leftmark = leftmark + 1
while alist[rightmark] >= pivotvalue and rightmark >= leftmark:
rightmark = rightmark -1
if rightmark < leftmark:
done = True
43
else:
temp = alist[leftmark]
alist[leftmark] = alist[rightmark]
alist[rightmark] = temp
temp = alist[first]
alist[first] = alist[rightmark]
alist[rightmark] = temp
return rightmark
alist = [54,26,93,17,77,31,44,55,20]
quickSort(alist)
print(alist)
4.4 Rangkuman
Kita sudah melihat contoh dari beberapa algoritma rekursif. Algoritma-algoritma ini dipilih
untuk mengekspos beberapa masalah berbeda dimana rekursi adalah teknik penyelesaian
masalah yang efektif. Poin kunci untuk diingat dari lab 4 ini adalah
Semua algoritma rekursif harus mempunyai suatu kasus basis (base case).
Suatu algoritma rekursif harus mengubah statusnya dan membuat progress menuju
base case.
Suatu algoritma rekursif harus memanggil dirinya sendiri (secara rekursif).
Rekursi menggantikan iterasi dalam beberapa kasus.
Algoritma rekursif sering diformalkan ke ekspresi formal dari masalah yang coba
diselesaikan.
Rekursi bukan selalu jawaban. Kadang solusi rekursi komputasinya lebih mahal
daripada algoritma alternatif.
44
45
Tuliskan suatu program yang mencetak segitiga Pascal. Program sebaiknya dapat
menerima suatu parameter yang memberitahukan berapa banyak baris yang akan
dicetak.
12. Siapkan suatu eksperimen acak (random experiment) untuk menguji perbedaan antara
sequential search dan binary search pada suatu list integer.
13. Gunakan fungsi pencarian biner yang telah diberikan (pendekatan rekursif dan iteratif)
sebelumnya. Bangkitkan suatu ordered list yang berisi integer acak dan lakukan suatu
analisa benchmark untuk setiap pendekatan. Apa hasilnya, jelaskan!
14. Implementasikan suatu binary search menggunakan rekursi tanpa operator slice. Ingat
kembali bahwa anda perlu melewatkan list bersama dengan nilai index starting dan
ending untuk sublist tersebut. Bangkitkan suatu ordered list yang berisi integer acak
dan lakukan suatu analisa benchmark.
15. Implementasikan metode len (__len__) untuk mewujudkan ADT Map tabel hash.
16. Implementasikan metode in (__contains__) untuk ADT Map tabel hash.
17. Bagaimana anda dapat menghapus item-item dari suatu hash table yang menggunakan
chaining untuk resolusi kolisinya? Bagaimana jika digunakan open addressing?
Keadaan khusus apa yang harus ditangani? Implementasikan metode del untuk kelas
HashTable.
18. Dalam implementasi map hash table, ukuran hash table dipilih 101. Jika tabel penuh,
ini perlu ditingkatkan. Tuliskan ulang dari metode put sehingga tabel akan berubah
ukuran (resize) secara otomatis ketika loading factor mencapai suatu nilai yang telah
ditentukan (anda dapat menentukan nilai ini berdasarkan pada taksiran anda tentang
beban vs. kinerja).
19. Implementasikan bubble sort menggunakan simultaneous assignment.
20. Implementasikan selection sort menggunakan simultaneous assignment.
21. Satu cara meningkatkan quick sort adalah menggunakan insertion sort pada lists yang
panjangnya kecil (disebut partition limit). Tulis ulang kode quick sort dan gunakan
pendekatan ini untuk mengurutkan list yang berisi integer random.
46
myTree = ['a',
['b',
['d', [], [] ],
['e', [], [] ] ],
['c',
['f', [], [] ],
[] ]
]
#root
# subtree kiri
# subtree kanan
47
Definisi dari struktur data Tree tersebut dapat diformalkan dengan menyediakan beberapa
fungsi yang membuatnya lebih mudah digunakan. Metode BinaryTree membuat suatu list yang
menyerupai binary tree kosong:
def BinaryTree(r):
return[r, [], [] ]
Fungsi BinaryTree di atas membangun suatu list dengan node root dan dua sublist kosong
untuk node anak. Untuk menambahkan suatu subtree kiri ke root dari tree, dilakukan
penyisipan list baru ke dalam posisi kedua dari list root.
Listing 5.1 Kode Python untuk melakukan penyisipan anak kiri (left child)
def insertLeft(root, newBranch):
t = root.pop(1)
if len(t) > 1:
root.insert(1, [newBranch, t, [] ])
else:
root.insert(1, [newBranch, [], [] ])
return root
Perhatikan, bahwa untuk menyisipkan suatu anak kiri, kita harus mendapatkan list (mungkin
kosong) yang berkaitan dengan anak kiri sekarang. Kita kemudian menambahkan anak kiri
baru, menginstal anak kiri lama sebagai anak kiri dari yang baru. Ini memungkinkan kita untuk
menyambung suatu node baru ke dalam tree pada posisi tertentu. Kode insertRigt() sama
dengan insertLeft(), diperlihatkan pada Listing 5.2.
Listing 5.2 Kode Python untuk menambahkan anak kanan baru (right child)
def insertRight(root, newBranch):
t = root.pop(2)
if len (t) > 1:
root.insert(2, [newBranch, [], t])
else:
root.insert(2, [newBranch, [], [] ])
return root
Listing 5.3 Fungsi untuk mengambil dan mengubah nilai root serta mengambil nilai anak kiri
dan kanan
def getRootVal(root):
return root[0]
def setRootVal(root, newVal):
root[0] = newVal
def getLeftChild(root):
return root[1]
48
def getRightChild(root):
return root[2]
Listing 5.4 Contoh Pembuatan BynaryTree dan pemanfaatannya
def BinaryTree(r):
return [r, [], []]
def insertLeft(root,newBranch):
t = root.pop(1)
if len(t) > 1:
root.insert(1,[newBranch,t,[]])
else:
root.insert(1,[newBranch, [], []])
return root
def insertRight(root,newBranch):
t = root.pop(2)
if len(t) > 1:
root.insert(2, [newBranch,[],t])
else:
root.insert(2, [newBranch,[],[]])
return root
def getRootVal(root):
return root[0]
def setRootVal(root,newVal):
root[0] = newVal
def getLeftChild(root):
return root[1]
def getRightChild(root):
return root[2]
r = BinaryTree(3)
insertLeft(r,4)
insertLeft(r,5)
insertRight(r,6)
insertRight(r,7)
l = getLeftChild(r)
print(l)
setRootVal(l,9)
print(r)
insertLeft(l,11)
print(r)
print(getRightChild(getRightChild(r)))
49
50
def getLeftChild(self):
return self.leftChild
def setRootVal(self,obj):
self.key = obj
def getRootVal(self):
return self.key
r = BinaryTree('a')
print(r.getRootVal())
print(r.getLeftChild())
r.insertLeft('b')
print(r.getLeftChild())
print(r.getLeftChild().getRootVal())
r.insertRight('c')
print(r.getRightChild())
Gambar 5.3 Pohon Uraian untuk menganalisa suatu kalimat dan ekspresi matematika
Langkah pertama dalam pembangunan suatu parse tree Adalah memecah string ekspresi ke
dalam daftar tokens. Ada 4 jenis token berbeda yang perlu diperhatikan: kurung left, kurung
kanan, operator dan operan. Kita tahun bahwa kapanpun kita membaca kurung kiri kita
52
memulai suatu ekspresi baru, dan karena itu kita membuat suatu tree baru untuk
menyesuaikan ekspresi tersebut. Sebaliknya, kapanpun kita membaca suatu kurung kanan, kita
telah menyelesaikan suatu ekspresi. Kita juda mengetahui bahwa operan akan menjadi node
daun (leave) dan anak-anak (children) dari operator-operatornya. Terakhir, kita mengetahui
bahwa setiap operator akan mempunyai anak kiri dan kanan.
Berdasarkan informasi di atas kita dapat mendefinisikan 4 rule berikut:
1. Jika current token adalah '(', tambahkan node baru sebagai anak kiri (left child) dari
current node, dan turunkan ke anak kiri tersebut.
2. Jika current token adalah operator dalam list ['+','-','/','*'], set nilai root dari current
node ke operator yang direpresentasikan oleh current token. Tambahkan node baru
sebagai anak nakan dari current node dan turunkan ke anak kanan tersebut.
3. Jika current token adalah suatu bilangan, set nilai root dari current node ke bilangan
tersebut dan kembali ke induknya.
4. Jika current token adalah ')', pindahkan ke induk dari current node.
Sekarang mari kita manfaatkan 4 aturan di atas untuk menguji ekspresi matematika (3 + (4 *
5)). Ekspresi ini akan diurai ke dalam daftar token ['(', '3', '+', '(', '4', '*', '5' ,')',')']. Awalnya
dimulai dengan suatu parse tree yang mengandung node root kosong. Gambar 5.4
mengilustrasikan struktur dan isi dari parse tree saat setiap token baru diproses.
53
currentTree = pStack.pop()
else:
raise ValueError
return eTree
pt = buildParseTree("( ( 10 + 5 ) * 3 )")
pt.postorder() #sudah terdefinisi dan dijelaskan kemudian
Listing 5.11 Kode untuk mengevaluasi suatu Parse Tree
def evaluate(parseTree):
opers={'+':operator.add,'-':operator.sub,'*':operator.mul,'/':operator.truediv}
leftC=parseTree.getLeftChild()
rightC=parseTree.getRightChild()
if leftC and rightC:
fn=opers[parseTree.getRootVal()]
returnfn(evaluate(leftC),evaluate(rightC))
else:
returnparseTree.getRootVal()
55
Listing 5.13 Implementasi preorder sebagai metode dari kelas BinaryTree (metode internal di
dalam kelas). Tree diganti dengan self.
def preorder(self):
print(self.key)
if self.leftChild:
self.leftChild.preorder()
if self.rightChild:
self.rightChild.preorder()
Listing 5.14 Algoritma postorder traversal, mirip sekali dengan preorder kecuali pemanggilan
print di akhir fungsi
def postorder(tree):
if tree!=None:
postorder(tree.getLeftChild())
postorder(tree.getRightChild())
print(tree.getRootVal())
Pemanfaatan umum dari postorder traversal adalah mengevaluasi suatu parse tree. Apa yang
dilakukan adalah mengevaluasi subtree kiri, subtree kanan dan menggabungkan keduanya
dalam root melalui function call ke suatu operator. Anggapan: binary tree hanya akan
menyimpan data tree ekspresi.
Listing 5.15 Evaluasi dengan postOrder
def postordereval(tree):
opers={'+':operator.add,'-':operator.sub,'*':operator.mul,'/':operator.truediv}
res1=None
res2=None
if tree:
res1=postordereval(tree.getLeftChild())
res2=postordereval(tree.getRightChild())
if res1 and res2:
return opers[tree.getRootVal()](res1,res2)
else:
return tree.getRootVal()
Listing 5.16 Inorder traversal
def inorder(tree):
if tree!=None:
inorder(tree.getLeftChild())
print(tree.getRootVal())
inorder(tree.getRightChild())
Jika kita mengerjakan suatu inorder traversal sederhana dari suatu parse tree maka diperoleh
(kembali) ekspresi originalnya tanpa tanda kurung. Lakukan perubahan terhadap algortima
inorder sederhana agar kita mampu menangani versi ekspresi bertanda kurung penuh.
56
Modifikasinya terhadap template dasar: print suatu kurung kiri sebelum recursive call ke
subtree kiri dan print kurung kanan setelah recursive call terhadap subtree kanan.
Listing 5.17 Perubahan terhadap inorder traversal
def printexp(tree):
sVal=""
if tree:
sVal='('+printexp(tree.getLeftChild())
sVal=sVal+str(tree.getRootVal())
sVal=sVal+printexp(tree.getRightChild())+')'
return sVal
57
tmp = self.heapList[i // 2]
self.heapList[i // 2] = self.heapList[i]
self.heapList[i] = tmp
i = i // 2
def insert(self,k):
self.heapList.append(k)
self.currentSize = self.currentSize + 1
self.percUp(self.currentSize)
def percDown(self,i):
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
tmp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = tmp
i = mc
def minChild(self,i):
if i * 2 + 1 > self.currentSize:
return i * 2
else:
if self.heapList[i*2] < self.heapList[i*2+1]:
return i * 2
else:
return i * 2 + 1
def delMin(self):
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize = self.currentSize - 1
self.heapList.pop()
self.percDown(1)
return retval
def buildHeap(self,alist):
i = len(alist) // 2
self.currentSize = len(alist)
self.heapList = [0] + alist[:]
while (i > 0):
self.percDown(i)
i=i-1
bh = BinHeap()
bh.buildHeap([9,5,6,2,3])
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())
58
print(bh.delMin())
print(bh.delMin())
59
def hasAnyChildren(self):
return self.rightChild or self.leftChild
def hasBothChildren(self):
return self.rightChild and self.leftChild
def replaceNodeData(self,key,value,lc,rc):
self.key = key
self.payload = value
self.leftChild = lc
self.rightChild = rc
if self.hasLeftChild():
self.leftChild.parent = self
if self.hasRightChild():
self.rightChild.parent = self
class BinarySearchTree:
def __init__(self):
self.root = None
self.size = 0
def length(self):
return self.size
def __len__(self):
return self.size
def put(self,key,val):
if self.root:
self._put(key,val,self.root)
else:
self.root = TreeNode(key,val)
self.size = self.size + 1
def _put(self,key,val,currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self._put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
else:
if currentNode.hasRightChild():
self._put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
def __setitem__(self,k,v):
self.put(k,v)
60
def get(self,key):
if self.root:
res = self._get(key,self.root)
if res:
return res.payload
else:
return None
else:
return None
def _get(self,key,currentNode):
if not currentNode:
return None
elif currentNode.key == key:
return currentNode
elif key < currentNode.key:
return self._get(key,currentNode.leftChild)
else:
return self._get(key,currentNode.rightChild)
def __getitem__(self,key):
return self.get(key)
def __contains__(self,key):
if self._get(key,self.root):
return True
else:
return False
def delete(self,key):
if self.size > 1:
nodeToRemove = self._get(key,self.root)
if nodeToRemove:
self.remove(nodeToRemove)
self.size = self.size-1
else:
raise KeyError('Error, key not in tree')
elif self.size == 1 and self.root.key == key:
self.root = None
self.size = self.size - 1
else:
raise KeyError('Error, key not in tree')
def __delitem__(self,key):
self.delete(key)
def spliceOut(self):
if self.isLeaf():
if self.isLeftChild():
self.parent.leftChild = None
61
else:
self.parent.rightChild = None
elif self.hasAnyChildren():
if self.hasLeftChild():
if self.isLeftChild():
self.parent.leftChild = self.leftChild
else:
self.parent.rightChild = self.leftChild
self.leftChild.parent = self.parent
else:
if self.isLeftChild():
self.parent.leftChild = self.rightChild
else:
self.parent.rightChild = self.rightChild
self.rightChild.parent = self.parent
def findSuccessor(self):
succ = None
if self.hasRightChild():
succ = self.rightChild.findMin()
else:
if self.parent:
if self.isLeftChild():
succ = self.parent
else:
self.parent.rightChild = None
succ = self.parent.findSuccessor()
self.parent.rightChild = self
return succ
def findMin(self):
current = self
while current.hasLeftChild():
current = current.leftChild
return current
def remove(self,currentNode):
if currentNode.isLeaf(): #leaf
if currentNode == currentNode.parent.leftChild:
currentNode.parent.leftChild = None
else:
currentNode.parent.rightChild = None
elif currentNode.hasBothChildren(): #interior
succ = currentNode.findSuccessor()
succ.spliceOut()
currentNode.key = succ.key
currentNode.payload = succ.payload
else: # this node has one child
if currentNode.hasLeftChild():
62
if currentNode.isLeftChild():
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.leftChild
elif currentNode.isRightChild():
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.leftChild
else:
currentNode.replaceNodeData(currentNode.leftChild.key,
currentNode.leftChild.payload,
currentNode.leftChild.leftChild,
currentNode.leftChild.rightChild)
else:
if currentNode.isLeftChild():
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.rightChild
elif currentNode.isRightChild():
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.rightChild
else:
currentNode.replaceNodeData(currentNode.rightChild.key,
currentNode.rightChild.payload,
currentNode.rightChild.leftChild,
currentNode.rightChild.rightChild)
mytree = BinarySearchTree()
mytree[3]="red"
mytree[4]="blue"
mytree[6]="yellow"
mytree[2]="at"
print(mytree[6])
print(mytree[2])
5.7 Rangkuman
Struktur data tree memungkinkan kita menulis banyak algoritma menarik. Kita telah
melihat algoritma yang menggunakan tree untuk melakukan hal berikut:
Suatu pohon biner (binary tree) untuk mem-parsing dan mengevaluasi ekspresi.
Binary tree untuk mengimplementasikan ADT map.
Pohon biner berimbang (AVL tree) untuk implementasi ADT map.
Binary tree untuk mengimplementasikan suatu min heap.
Min heap digunakan untuk implementasi suatu priority queue.
63
64