You are on page 1of 80

BAB I STRUKTUR DATA

Dalam istilah ilmu komputer, sebuah struktur data adalah cara penyimpanan, penyusunan dan pengaturan data di dalam media penyimpanan komputer sehingga data tersebut dapat digunakan secara efisien.

Dalam teknik pemrograman, struktur data berarti tata letak data yang berisi kolom-kolom data, baik itu kolom yang tampak oleh pengguna (user) atau pun kolom yang hanya digunakan untuk keperluan pemrograman yang tidak tampak oleh pengguna. Setiap baris dari kumpulan kolom-kolom tersebut dinamakan catatan (record). Lebar kolom untuk data dapat berubah dan bervariasi. Ada kolom yang lebarnya berubah secara dinamis sesuai masukan dari pengguna, dan juga ada kolom yang lebarnya tetap. Dengan sifatnya ini, sebuah struktur data dapat diterapkan untuk pengolahan database (misalnya untuk keperluan data keuangan) atau untuk pengolah kata (word processor) yang kolomnya berubah secara dinamis. Contoh struktur data dapat dilihat pada berkas-berkas lembarsebar (spreadsheet), pangkal-data (database), pengolahan kata, citra yang dipampat (dikompres), juga pemampatan berkas dengan teknik tertentu yang memanfaatkan struktur data.

A. TAHAPAN PEMBUATAN STRUKTUR DATA 1. Tahap Spesifikasi atau pendeskripsian struktur data menyatakan apa yang dapat dilakukan struktur data, bukan cara penempatannya. Pendeskripsian ini

melibatkan level logic sehingga dapat digunakan konvensi matematika untuk menyatakan sifat-sifat struktur data yang dikehendaki.

2. Tahap Kedua Implementasi menyatakan cara penerapan struktur data dengan struktur data yang telah ada. Implementasi struktur dataadalah proses pendefinisian tipe data abstark sehingga semua operasi dapat dieksekusi computer. Implementasi berisi dekklarasi struktur penyimpanan item data serta algoritma untuk implementasi operasi, sehingga menjamin terpenuhinya karakteristik struktur data, relasi item data tau invariant pada struktur data tersebut.

3. Tahap Ketiga Pemrograman struktur data adalah penterjemahan menjadi pernyataan dalam bahasa pemrograman tertentu.struktur data dibangun menggunakan fasilitas pembentukkan atau pembuatan struktur data yang disediakan bahasa seperti array, record dan lain-lain.

Dasar Pengetahuan Struktur Data Dalam mempelajari struktur data hal-hal awal yang perlu kita ketahui adalah tentang konstanta, variable, dan tipe data. a. Konstanta Dalam membuat suatu program, kita perlu menggunakan konstanta agar program kita bisa lebih mudah diperbaiki jika ada suatu kesalahan yang kita buat. Dengan menggunakan konstanta kita bisa memberikan nama yang mudah dimengerti dan dipahami untuk bilangan numerik yang sangat

kompleks. Konstanta dideklarasikan pada awal program dan dideklarasikan dengan kata baku const. Sesuai dengan namanya konstanta, maka nilai dalam konstanta selalu konstan atau tetap dan kita tidak dapat merubah nilai dari konstanta pada saat program sedang dijalankan. b. Variabel Variabel adalah lokasi di memori yang kita siapkan dan kita beri nama khas untuk menampung suatu nilai dan atau mengambil nilai kembali tersebut. Bentuk umum dari variable adalah: Var NamaVariabel1, NamaVariabel2, . NamaVariabel1N : TipeData1; NamaVariabel1, NamaVariabel2, NamaVariabelNN : TipeDataN;

B. TIPE DATA Atribut penting yang digunakan untuk suatu tipe data terstruktur adalah sebagai berikut : 1. Jumlah Komponen Berdasarkan jumlah komponen selama eksekusi program, maka dapat dikelompokkan menjadi :

- Struktur Data Statis (Jumlah komponennya tidak berubah) - Struktur Data Dinamis (Jumlah komponennya dapat berubah)

2. Tipe untuk setiap komponennya Apabila tipe data untuk seluruh komponennya harus sama, maka disebut Struktur Data Homogen, dan bila dimungkinkan komponennya mempunyai tipe data yang berbeda-beda, maka disebut Struktur Data Heterogen.

3. Nama-nama untuk memilih komponen Hampir semua struktur data menyediakan operasi untuk mengakses komponen secara individu. Pada suatu array (kumpulan data yang mempunyai tipe sama), hal ini dilakukan dengan sebuah indeks berupa angka.

4. Jumlah maksimum komponen Tidak semua jenis struktur data harus ditentukan jumlah maksimum komponen, namun untuk sebuah tipe data dinamis mungkin perlu ditentukan dengan jelas.

5. Pengorganisasian semua komponennya Susunan yang paling umum adalah berupa barisan linier seperti pada array berdimensi 1, record, list, stack dan file. Selanjutnya ada yang dapat dikembangkan menjadi struktur non linier seperti array multi dimensi dan juga pohon/tree.

Jenis tipe data yang biasanya digunakan dalam data terstruktur adalah sebagai berikut :

a. Integer Integer adalah tipe data nilainya merupakan bilangan bulat dan teerbagi atas beberapa macam. Berikut ini adalah tabelnya: Type ShortIn Integer LongInt Byte Word Range -128127 -32768..32767 -2147483648..2147483647 0..255 0..65535 Ukuran 1 2 4 1 2 Format Signed 8-bit Signed 16-bit Signed 32-bit Unsigned 8-bit Unsigned 16-bit

C. JENIS STRUKTUR DATA 1. Struktur Data Sederhana a. Array(Larik) Larik adalah struktur data statik yang menyimpan sekumpulan elemen yang bertipe sama. Setiap elemen diakses langsung melalui indeksnya. Indeks larik harus tipe data yang menyatakan keterurutan misalnya integer atau karakter. Banyaknya elemen larik harus sudah diketahui sebelum program dieksekusi. Tipe elemen larik dapat berupa tipe sederhana, tipe terstruktur, atau tipe larik lain. Nama lain array adalah Larik, tabel, atau vektor b. Record(Catatan) ADT adalah definisi tipe dan sekumpulan primitif (operasi dasar) terhadap tipe tersebut. Tipe diterjemahkan menjadi tipe terdefinisi dalam bahasa

pemrograman yang bersangkutan.

2. Struktur Data Majemuk a. Linier - Stack(Tumpukan) Stack (tumpukan) adalah list linier yang dikenali elemen puncaknya (top), aturan penyisipan dan penghapusan elemennya tertentu (penyisipan selalu dilakukan di atas (top), penghapusan selalu dilakukan pada top). Karena aturan penyisipan dan penghapusan semacam itu, top adalah satu-satunya alamat tempat terjadi operasi. Elemen yang ditambahkan paling akhir akan

menjadi elemen yang akan dihapus. Dikatakan bahwa elemen stack akan tersusun secara LIFO (Last In First Out). - Queue(Antrian) Queue (antrian) adalah list linier yang dikenali elemen pertama (head) dan elemen terakhirnya (tail); Aturan penyisipan dan penghapusan elemennya disefinisikan sebagai penyisipan selalu dilakukan setelah elemen terakhir, penghapusan selalu dilakukan pada elemen pertama; Satu elemen dengan elemen lain dapat diakses melalui informasi next. - List dan Multi-List (Daftar) List linier adalah sekumpulan elemen bertipe sama, yang mempunyai keterurutan tertentu, yang setiap elemennya terdiri dari 2 bagian. sebuah list linier dikenali dengan (1) elemen pertamanya, biasanya melalui alamat elemen pertama yang disebut (first); (2) Alamat elemen berikutnya (suksesor), jika kita mengetahui alamat sebuah elemen, yang dapat diakses melalui field next; (3) Setiap elemen mempunyai alamat, yaitu tempat elemen disimpan dapat diacu. Untuk mengacu sebuah elemen, alamat harus terdefinisi. Dengan alamat tersebut informasi yang tersimpan pada elemen list dapat diakses; (4) Elemen terakhirnya.

b. Non-Linier - Binary Tree (Pohon Biner) Sebuah pohon biner (binary tree) adalah himpunan terbatas yang mungkin kosong atau terdiri dari sebuah simpul yang disebut sebagai akar dan dua buah himpunan lain yang disjoint yang merupakan pohon biner yang disebut

sebagai sub pohon kiri (left) dan sub pohon kanan (right) dari pohon biner tersebut. Pohon biner merupakan tipe yang sangat penting dari struktur data dan banyak dijumpai dalam berbagai terapan. Karakteristik yang dimiliki oleh pohon biner adalah bahwa setiap simpul paling banyak hanya memiliki dua buah anak, dan mungkin tidak punya anak. Istilah-istilah yang digunakan sama dengan istilah pada pohon secara umum.

- Graph (Graf) Graph merupakan struktur data yang paling umum. Jika struktur linier memungkinkan pendefinisian keterhubungan sekuensial antara entitas data, struktur data tree memungkinkan pendefinisian keterhubungan hirarkis, maka struktur graph memungkinkan pendefinisian keterhubungan tak terbatas antara entitas data. Banyak entitas-entitas data dalam masalahmasalah nyata secara alamiah memiliki keterhubungan langsung (adjacency) secara tak terbatas demikian. Contoh: informasi topologi dan jarak antar kota-kota di pulau Jawa. Dalam masalah ini kota X bisa berhubungan langsung dengan hanya satu atau lima kota lainnya. Untuk memeriksa keterhubungan dan jarak tidak langsung antara dua kota dapat diperoleh berdasarkan data keterhubungan-keterhubungan langsung dari kota-kota lainnya yang memperantarainya. Representasi data dengan struktur data linier ataupun hirarkis pada masalah ini masih bisa digunakan namun akan membutuhkan pencarian-pencarian yang kurang efisien. Struktur data graph secara eksplisit menyatakan keterhubungan ini sehingga pencariannya langsung (straightforward) dilakukan pada strukturnya sendiri.

Istilah penting dalam struktur data : 1. Data Data adalah deskripsi dari sesuatu dan kejadian yang kita hadapi(data is the description of things and events that we face). Data adalah kenyataan yang menggambarkan suatu kejadian-kejadian dan kesatuan nyata. Kejadian (event) adalah sesuatu yang terjadi pada saat tertentu. Sebagai contoh, dalam dunia bisnis kejadian-kejadian nyata yang sering terjadi adalah perubahan dari suatu nilai yang disebut dengan transaksi. Misalnya penjualan adalah transaksi perubahan nilai barang menjadi nilai uang atau nilai piutang dagang. Kesatuan nyata(fact and entity) adalah berupa suatu obyek nyata seperti tempat, benda dan orang yang betul-betul ada dan terjadi.Sumber dari informasi adalah data. Data merupakan bentuk jamak dari bentuk tunggal data-item. Data merupakan bentuk yang belum dapat memberikan manfaat yang besar bagi penerimanya, sehingga perlu suatu model yang nantinya akan dikelompokkan dan diproses untuk menghasilkan informasi.

2. Database Database bisa dikatakan sebagai suatu kumpulan dari data yang tersimpan dan diatur atau diorganisasikan sehingga data tersebut bisa diambil atau dicari dengan mudah dan efisien. Sebagai contoh sederhana dari database adalah buku telepon yang mungkin sering Anda lihat.Bagaimana halnya dengan database dengan sistem database dengan menggunakan komputer? Hal tersebut sama saja seperti database yang sifatnya manual (seperti contoh buku telepon

di atas) hanya saja dengan adanya komputer maka informasi yang ada di dalam database akan sangat mudah untuk di-update dan sangat cepat untuk dicari. Software atau aplikasi yang bertugas untuk mengatur, menyimpan,

memodifikasi data disebut dengan software database engine dan lebih resminya disebut dengan DBMS (Database Management System). Ada banyak sekali aplikasi DBMS ini mulai yang berjalan di komputer personal (PC) sampai ke komputer skala mainframe. Contoh-contoh dari aplikasi database engine misalnya seperti: SQL Server, dibuat oleh Microsoft. MS Access, dibuat oleh Microsoft. Oracle Database, dibuat oleh Oracle. MySQL, dibuat oleh MySQL AB. Firebird, dibuat oleh komunitas open source berdasarkan dari kode Interbase. PostgreSQL, dibuat oleh komunitas open source. DB2, dibuat oleh IBM.

3. Data Set Istilah untuk sekelompok record data yang sama dan saling terhubung dalam memori computer.

4. Data bit Sekelompok bit yang digunakan untuk menggambarkan satu karakter:character data untuk transmisi:trans...

- Enkapsulasi (Pembungkusan)

Variabel dan method yang dipunyai suatu obyek, bisa ditentukan hak aksesnya. Definisi enkapsulasi: Pembungkusan variabel dan method dalam sebuah obyek yang terlindungi. cara kerja dan sistem. Definisi enkapsulasi: menyembunyikan

Dalam OOP, konsep enkapsulasi sebenarnya

merupakan perluasan dari struktur dalam bahasa C.

BAB II ARRAY

Array adalah suatu struktur yang terdiri dari sejumlah elemen yang memiliki tipe data yang sama. Elemen-elemen array tersusun secara sekuensial dalam memori komputer. Array dapat berupa satu dimensi, dua dimensi, tiga dimensi ataupun banyak dimensi (multi dimensi).

A. ARRAY SATU DIMENSI

Array Satu dimensi tidak lain adalah kumpulan elemen-elemen identik yang tersusun dalam satu baris. Elemen-elemen tersebut memiliki tipe data yang sama, tetapi isi dari elemen tersebut boleh berbeda. Elemen KeNilai 0 23 1 34 2 32 3 12 4 25 5 14 6 23 7 12 8 11 9 10

Bentuk umum: <tipe data> NamaArray[n] = {elemen0, elemen1, elemen2,.....,n}; n = jumlah elemen

1. Inisialisasi Array 1 Dimensi Inisialisasi dapat dilakukan bersama dengan deklarasi atau tersendiri.

Inisialisasi suatu array adalah dengan meletakkan elemen array di antara tanda kurung kurawal {}, antara elemen yang satu dengan lainnya dipisahkan koma. int bil [2] = {4,1,8} bil [0] = 4 bil [1] = 1 bil [2] = 8 AUTOMATIC ARRAY adalah Inisialisasi array dilakukan di dalam fungsi tertentu. Hanya compiler C yang berstandar ANSI C yang dapat

menginisialisasikan automatic array.

Cara menginisialisasikan ANSI C:

array dari compiler yg tidak mengikuti standar

a. Diinisialisasikan di luar fungsi sebagai variabel GLOBAL/EXTERNAL ARRAY. int bil[2]={0,0,0}; main()

b. Diinisialisasikan didlm fungsi sebagai variabel LOKAL/STATIC ARRAY. main() { static int bil[2]={0,0,0}; ......... Pada automatic array yang tidak diinisialisasikan , elemen array akan memiliki nilai yang tidak beraturan. Bila global & static array tidak diinisialisasi maka semua elemen array secara otomatis akan diberi nilai nol(0).

Contoh : main() { int y; int hitung=0; int x[0]; for(y=0;y<5;y++) { hitung+=y; x[y]=hitung; printf("%3d - %3d\n",y,x[y]); } } OUTPUT: 0- 0

1- 1 2- 3 3- 6 4- 10

2. Array Dua Dimensi Array dua dimensi sering digambarkan sebagai sebuah matriks, merupakan perluasan dari array satu dimensi. Jika array satu dimensi hanya terdiri dari sebuah baris dan beberapa kolom elemen, maka array dua dimensi terdiri dari beberapa baris dan beberapa kolom elemen yang bertipe sama. Bentuk umum: <tipe data> NamaArray [m][n]; Atau <tipe data> NamaArray [m][n] = { {a,b,..z},{1,2,...,n-1} }; Contoh: double matrix[4][4]; bool papan[2][2] = { {true,false},{true,false} }; Pendeklarasian array dua dimensi hampir sama dengan pendeklarasian array satu dimensi, kecuali bahwa array dua dimensi terdapat dua jumlah elemen yang terdapat didalam kurung siku dan keduanya boleh tidak sama. Elemen array dua dimensi diakses dengan menuliskan kedua indeks elemennya dalam kurung siku seperti pada contoh berikut: //papan nama memiliki 2 baris dan 5 kolom bool papan[2][5];

papan[0][0] = true; papan[0][4] = false; papan[1][2] = true; papan[1][4] = false;

B. MENDEFINISIKAN JUMLAH ELEMEN ARRAY DALAM VARIABEL Besarnya variabel indeks dapat ditentukan dengan menggunakan preprocessor directives #define #define N 40 main() { int no[N],gaji[N],gol[N],status[N],juman[N];

Bila besari indeks akan diubah menjadi 50, cukup diganti dengan #define N 50 1. Pada string terdapat karakter null(\0) di akhir string 2. String sudah pasti array, array belum tentu string

Contoh - contoh : 1. array dengan pengisian input melalui keyboard baca_input() { float nilai[10]; for(i=0;i<10;i++)

scanf("%d",&nilai[i]); } 2. Fungsi yang mencetak isi array dari akhir ke awal cetak_array() { float nilai[10]; for(i=9;i>=0;i--) scanf("%3f",nilai[i]); } 3. Menghitung rata - rata isi array nilai rata_rata() { float nilai[10],jum*rata; for(i=0,jum=0;i<=9;i++) jum+=nilai[i]; rata=jum/i; } 4. Mencari nilai terbesar besar() float temp,nilai[10]; { for(temp=nilai[0],i=1;i<=9;i++) if(nilai[i] > temp)

temp=nilai[i]; } return(temp)

C. ARRAY DINAMIS Posted Sab, 03/21/2009 - 01:39 by belajarprogram 1. Versi Ramah Cetak Panjang suatu array tidak bisa diubah setelah dibuat. Akan tetapi, sering kali jumlah data yang disimpan dalam array bisa berubah setiap saat. Misalnya dalam contoh berikut : Suatu array menyimpan baris teks dalam program pengolah kata. Atau suatu array yang menyimpan daftar komputer yang sedang mengakses halaman suatu website. Atau array yang berisi gambar yang ditambahkan user oleh program gambar. Di sini jelas, bahwa kita butuh sesuatu yang bisa menyimpan data di mana jumlahnya tidak tetap. 2. Array Setengah Penuh Bayangkan suatu aplikasi di mana sejumlah item yang ingin kita simpan di dalam array akan berubah-ubah sepanjang program tersebut berjalan. Karena ukuran array tidak bisa diubah, maka variabel terpisah digunakan untuk menyimpan berapa banyak sisa tempat kosong yang masih bisa diisi di dalam array.

Bayangkan misalnya, suatu program yang membaca bilangan bulat positif dari user, kemudian menyimpannya untuk diproses kemudian. Program akan

berhenti membaca input apabila input yang diterima bernilai 0 atau kurang dari nol. Bilangan input n tersebut kita simpa di dalam array bilangan dengan tipe int[]. Katakan banyaknya bilangan yang bisa disimpan tidak lebih dari 100 buah. Maka ukuran array bisa dibuat 100.

Akan tetapi program tersebut harus melacak berapa banyak bilangan yang sudah diambil dari user. Kita gunakan variabel terpisah bernama jmlBilangan. Setiap kali suatu bilangan disimpan di dalam array, nilai jmlBilangan akan bertambah satu.

Sebagai contoh sederhana, masi kita buat program yang mengambil bilangan yang diinput dari user, kemudian mencetak bilangan-bilangan tersebut dalam urutan terbalik. (Ini adalah contoh pengolahan yang membutuhkan array, karena semua bilangan harus disimpan pada suatu tempat. Banyak lagi contoh program misalnya, mencari jumlah atau rata-rata atau nilai maksimum beberapa bilangan, bisa dilakukan tanpa menyimpan bilangan tersebut satu per satu) public class BalikBilanganInput {

public static void main(String[] args) {

int[] bilangan; // Array untuk menyimpan nilai input dari user int jmlBilangan; int bil; // Banyaknya bilangan yang sudah disimpan dalam array

// Bilangan yang diambil dari user

bilangan = new int[100]; // Buat array dengan 100 bilangan int jmlBilangan = 0; // Belum ada bilangan yang disimpan

System.out.println("Masukkan bilangan bulat positif (paling banyak 100 bilangan)" + ", masukkan nol untuk mengakhiri.");

while (true) { System.out.print("? "); bil = KonsolIO.ambilInt(); if (bil <= 0) break; bilangan[jmlBilangan] = bil; jmlBilangan++; } System.out.println("\nBilangan yang Anda masukkan dalam urutan terbalik adalah :\n"); for (int i = jmlBilangan - 1; i >= 0; i--) { System.out.println( bilangan[i] ); } } // akhir main(); } // akhir kelas BalikBilanganInput

Penting untuk diingat bahwa jml Bilangan memiliki dua peran. Yang pertama, adalah melacak banyaknya bilangan yang sudah dimasukkan di dalam array. Yang kedua adalah menentukan di mana indeks kosong berikutnya di dalam array. Misalnya, jika 4 bilangan sudah dimasukkan ke dalam array, maka bilanganbilangan tersebut diisi pada array di posisi 0, 1, 2, dan 3. Maka posisi kosong berikutnya adalah posisi 4.

Ketika kita akan mencetak angka di dalam array, maka poisisi penuh berikutnya adalah di lokasi jmlBilangan - 1, sehingga perulangan for mencetak bilangan dari jmlBilangan - 1 hingga 0.

Mari kita lihat contoh lain yang lebih realistis. Misalkan kita ingin menulis program game, di mana pemain bisa masuk ke dalam game dan keluar dari game setiap saat. Sebagai programmer berorientasi objek yang baik, mungkin kita memiliki kelas bernama Pemain sebagai lambang pemain di dalam game. Daftar pemain yang sedang ada di dalam game, bisa disimpan di dalam array ArrayPemain dengan tipe Pemain[].

Karena jumlah pemain bisa berubah-ubah maka kita bisa menggunakan variabel bantu, misalnya jumlahPemainAktif untuk mencatat banyaknya pemain yang sedang aktif berada di dalam game. Misalnya jumlah maksimum pemain di dalam game adalah 10 orang, maka kita bisa mendeklarasikan variabelnya sebagai : Pemain[] ArrayPemain = new Pemain[10]; // Hingga 10 pemain. int jumlahPemainAktif = 0; // Di awal game, tidak ada pemain yang aktif

Setelah beberapa pemain masuk ke dalam game, variabel jumlahPemainAktif akan lebih dari 0, dan objek pemailn akan disimpan dalam array, misalnya ArrayPemain[0], ArrayPemain[1], ArrayPemain[2], dan seterusnya. Ingat bahwa pemain ArrayPemain[jumlahPemainAktif] tidak ada. Prosedur untuk menambah pemain baru, secara sederhana : // Tambah pemain di tempat kosong ArrayPemain[jumlahPemainAktif] = pemainBaru; // And increment playerCt to count the new player. jumlahPemainAktif++;

Untuk menghapus seorang pemain mungkin sedikit lebih sulit karena kita tidak ingin meninggalkan lubang di tengah-tengah array. Misalnya kita ingin menghapus pemain pada indeks k pada ArrayPemain. Jika kita tidak peduli urutan pemainnya, maka salah satu caranya adalah memindahkan posisi pemain terakhir ke posisi pemain yang meninggalkan game, misalnya : ArrayPemain[k] = ArrayPemain[jumlahPemainAktif - 1]; jumlahPemainAktif--;

Pemain yang sebelumnya ada di posisi k, tidak lagi ada di dalam array. Pemain yang sebelumnya ada di posisi jumlahPemainAktif -1 sekarang ada di array sebanyak 2 kali. Akan tetapi sekarang ia berada di bagian yang valid, karena nilai jumlahPemainAktif kita kurangi dengan satu. Ingat bahwa setiap elemen di dalam array harus menyimpan satu nilai, akan tetapi satu-satunya nilai dari posisi 0 hingga jumlahPemainAktif - 1 akan tetap diproses seperti biasa.

Misalnya kita ingin menghapus pemain di posisi k, akan tetapi kita ingin agar urutan pemain tetap sama. Untuk melakukannya, semua pemain di posisi k+1 ke atas harus dipindahkan satu posisi ke bawah. Pemain k+ mengganti pemain k yang keluar dari game. Pemain k+2 mengganti pemain yang pindah sebelumnya, dan berikutnya. Kodenya bisa dibuat seperti for (int i = k+1; i < jumlahPemainAktif; i++) { ArrayPemain[i-1] = ArrayPemain[i]; } jumlahPemainAktif--;

Perlu ditekankan bahwa contoh Pemain di atas memiliki tipe dasar suatu kelas. Elemennya bisa bernilai null atau referensi ke suatu objek yang bertipe Pemain, Objek Pemain sendiri tidak disimpan di dalam array, hanya referensinya saja yang disimpan di sana. Karena aturan pemberian nilai pada Java, objek tersebut bisa saja berupa kelas turunan dari Pemain, sehingga mungkin juga array tersebut menyimpan beberapa jenis Pemain misalnya pemain komputer, pemain manusia, atau pemain lainnya, yang semuanya adalah kelas turunan dari Pemain.

Contoh lainnya, misalnya kelas BentukGeometri menggambarkan suatu bentuk geometri yang bisa digambar pada layar, dan ia memiliki kelas-kelas turunan yang merupakan bentuk-bentuk khusus, seperti garis, kotak, kotak bertepi bulat, oval, atau oval berisi warna, dan sebagainya. (BentukGeometri sendiri bisa berbentuk kelas abstrak, seperti didiskusikan sebelumnya). Kemudian array bertipe

BentukGeometri[] bisa menyimpan referensi objek yang bertipe kelas turunan dari BentukGeometri. Misalnya, perhatikan contoh pernyataan berikut BentukGeometri[] gambar = new BentukGeometri[100]; // Array untuk menyimpan 100 gambar. gambar[0] = new Kotak(); gambar[1] = new Garis(); // Letakkan beberapa objek di dalam array. // (Program betulan akan menggunakan beberapa // parameter di sini

gambar[2] = new OvalBerwarna();

int jmlGambar = 3; // Lacak jumlah objek di dalam array bisa diilustrasikan sebagai berikut.

Array tersebut bisa digunakan dalam program gambar. Array bisa digunakan untuk menampung gambar-gambar yang akan ditampilkan. Jika BentukGeometri memiliki metode "void gambarUlang(Graphics g)" untuk menggambar pada grafik g, maka semua grafik dalam array bisa digambar dengan perulangan sederhana for (int i = 0; i < jmlGambar; i++) gambar[i].gambarUlang(g);

Pernyataan "gambar[i].gambarUlang(g);" memanggil metode gambarUlang() yang dimiliki oleh masing-masing gambar pada indeks i di array tersebut. Setiap objek tahu bagaimana menggambar dirinya sendiri, sehingga perintah dalam perulangan tersebut sebetulnya melakukan tugas yang berbeda-beda tergantung pada objeknya. Ini adalah contoh dari polimorfisme dan pengolahan array.

3. Array Dinamis Dalam contoh-contoh di atas, ada batas tententu dalam jumlah elemennya, yaitu 100 int, 100 Pemain, dan 100 BentukGeometris. Karena ukuran array tidak bisa berubah, array tersebut hanya bisa menampung maksimum sebanyak elemen yang didefinisikan pada pembuatan array. Dalam banyak kasus, adanya batas maksimum tersebut tidak diinginkan. Kenapa harus bekerja dengan hanya 100 bilangan bulat saja, bukan 101?

Alternatif yang umum adalah membuat array yang sangat besar sehingga bisa digunakan untuk dalam kehidupan sehari-hari. Akan tetapi cara ini tidak baik, karena akan sangat banyak memori komputer yang terbuang karena tidak digunakan. Memori itu mungkin lebih baik digunakan untuk yang lain. Apalagi jika komputer yang akan digunakan tidak memiliki memori yang cukup untuk menjalankan program tersebut.

Tentu saja, cara yang lebih baik adalah apabila kita bisa mengubah ukuran array sesuka kita kapan saja. Ingat bahwa sebenarnya variabel array tidak menyimpan array yang sesungguhnya. Variabel ini hanya menyimpan referensi

ke objek tersebut. Kita tidak bisa membuat array tersebut lebih besar, akan tetapi kita bisa membuat array baru yang lebih besar, kemudian mengubah isi variabel array tersebut ke array baru itu.

Tentunya kita harus mengkopi semua isi di array yang lama ke array baru. Array lama akan diambil oleh pemulung memori, karena ia tidak lagi digunakan.

Mari kita lihat kembali contoh game di atas, di mana ArrayPemain adalah array dengan tipe Pemain[] dan jumlahPemainAktif[/code] adalah jumlah pemain yang sudah digunakan array tersebut. Misalnya kita tidak ingin membuat limit banyaknya pemainnya yang bisa ikut main. Jika pemain baru masuk dan array tersebut sudah penuh, kita akan membuat array baru yang lebih besar.

Variabel ArrayPemain akan merujuk pada array baru. Ingat bahwa setelah ini dilakukan, ArrayPemain[0] akan menunjuk pada lokasi memori yang berbeda, akan tetapi nilai ArrayPemain[0] sama dengan sebelumnya. Berikut ini adalah kode untuk melakukan hal di atas: // Tambah pemain baru, meskipun array sudah penuh if (jumlahPemainAktif == ArrayPemain.length) { // Array sudah penuh. Buat array baru yang lebih besar, // kemudian kopi isi array lama ke array baru lalu ubah // ArrayPemain ke array baru. int ukuranBaru = 2 * ArrayPemain.length; // Ukuran array baru Pemain[] temp = new Pemain[ukuranBaru]; // Array baru

System.arraycopy(ArrayPemain, 0, temp, 0, ArrayPemain.length); ArrayPemain = temp; // Ubah referensi ArrayPemain ke array baru. } // Di sini kita sudah tahu bahwa pasti ada tempat di array baru. ArrayPemain[jumlahPemainAktif] = pemainBaru; // Tambah pemain baru... jumlahPemainAktif++; // ... dan tambah satu jumlahPemainAktif nya

Jika kita akan melakukan hal ini terus menerus, akan lebih indah jika kita membuat kelas untuk menangani hal ini. Objek mirip array yang bisa berubah ukuran untuk mengakomodasi jumlah data yang bisa ia tampung disebut array dinamis. Array dinamis memiliki jenis operasi yang sama dengan array : mengisi nilai pada posisi tertentu dan mengambil nilai di posisi tertentu. Akan tetapi tidak ada batas maksimum dari jumlah array (hanya tergantung pada jumlah memori komputer yang tersedia). Dalam kelas array dinamis, metode put dan get akan diimplementasikan sebagai metode instansi.

Di sini misalnya, adalah kelas yang mengimplementasikan array dinamis int : public class ArrayDinamisInt { private int[] data; // Array untuk menyimpan data public DynamicArrayOfInt() { // Konstruktor. data = new int[1]; // Array akan bertambah besar jika diperlukan } public int get(int posisi) {

// Ambil nilai dari posisi tertentu di dalam array. // Karena semua posisi di dalam array adalah nol, maka // jika posisi tertentu di luar data array, nilai 0 akan dikembalikan if (posisi >= data.length) return 0; else return data[posisi]; } public void put(int posisi, int nilai) { // Simpan nilai ke posisi yang ditentukan di dalam array // Data array tersebut akan bertambah besar jika diperlukan if (posisi >= data.length) { // Posisi yang ditentukan berada di luar array data // Besarkan ukuran array 2x lipat. Atau jika ukurannya masih // terlalu kecil, buat ukurannya sebesar 2*posisi

int ukuranBaru = 2 * data.length; if (posisi >= ukuranBaru) ukuranBaru = 2 * posisi; int[] dataBaru = new int[ukuranBaru]; System.arraycopy(data, 0, dataBaru, 0, data.length); data = dataBaru;

// Perintah berikut hanya untuk demonstrasi System.out.println("Ukuran array dinamis diperbesar menjadi " + ukuranBaru); } data[posisi] = nilai; } } // akhir kelas ArrayDinamisInt

Data pada objek ArrayDinamisInt disimpan dalam array biasa, akan tetapi arraynya akan dibuang dan diganti dengan array baru yang lebih besar apabila diperlukan. Jika bilangan adalah variable bertipe ArrayDinamisInt, maka perintah bilangan.put(pos,nilai) akan menyimpan bilangan pada posisi pos di array dinamis tersebut. Fungsi bilangan.get(pos) mengambil nilai yang disimpan pada posisi pos.

Pada contoh pertama, kita menggunakan array untuk menyimpan bilangan bulat positif yang dimasukkan oleh user. Kita bisa menulis ulang program tersebut dengan menggunakan ArrayDinamisInt. Referensi ke bilangan[i] diganti dengan bilangan.get[i]. Perintah "bilangan[jmlBilangan] = bil;" kita ganti dengan "bilangan.put(jmlBilangan,bil);". Berikut ini adalah programnya: public class BalikBilanganInput {

public static void main(String[] args) {

ArrayDinamisInt bilangan; // Array untuk menyimpan nilai input dari user

int jmlBilangan; int bil;

// Banyaknya bilangan yang sudah disimpan dalam array

// Bilangan yang diambil dari user

bilangan = new ArrayDinamisInt(); jmlBilangan = 0; // Belum ada bilangan yang disimpan

System.out.println("Masukkan bilangan bulat positif, masukkan nol untuk mengakhiri."); while (true) { System.out.print("? "); bil = KonsolIO.ambilInt(); if (bil <= 0) break; bilangan.put(jmlBilangan,bil); jmlBilangan++; } System.out.println("\nBilangan yang Anda masukkan dalam urutan terbalik adalah :\n"); for (int i = jmlBilangan - 1; i >= 0; i--) { System.out.println( bilangan.get(i) ); } } // akhir main(); } // akhir kelas BalikBilanganInput

4. Array Multi Dimensi

Posted Sab, 03/21/2009 - 01:45 by belajarprogram a. Versi ramah cetak Tipe apapun bisa digunakan sebagai tipe dasar suatu array. Kita bisa membuat array int, array String, array Object dan seterusnya. Terutama, karena array adalah tipe Java kelas satu, kita bisa membuat array yang bertipe array.

Misalnya suatu array bertipe int[], juga otomatis memiliki array bertipe int[][], yaitu "array bertipe array int". Array tersebut disebut array 2 dimensi. Tentunya, dengan tipe int[][], kita juag bisa membuat arraynya dengan tipe int[][][], yang merupakan array 3 dimensi, dan seterusnya. Tidak ada batasan berapa dimensi array yang kita buat, akan tetapi bukan sesuatu yang biasa dilakukan untuk membuat array lebih dari 3 dimensi. Pembahasan kita akan lebih dikhususkan pada array 2 dimensi. Tipe TipeDasar[][] biasanya dibaca "array 2 dimensi bertipe TipeDasar" atau "array dari array TipeDasar".

Deklarasi pernyataan "int[][] A;" adalah membuat variabel bernama A dengan tipe int[][]. Variabel ini berisi objek yang bertipe int[][]. Pernyataan pemberian nilai "A = new int[3][4];" akan membuat objek array 2 dimensi dan mengisi A ke objek yang baru dibuat tersebut.

Seperti biasa, deklarasi dan pemberian nilai bisa digabung menjadi satu pernyataan, seperti "int[][] A = new int[3][4];". Objek yang baru dibuat adalah objek yang merupakan array dari array int. Bagian int[3][4] menyatakan bahwa ada 3 array int di dalam array A, dan di setiap array int tersebut terdapat 4 int.

Cara seperti itu mungkin sedikit membingungkan, akan tetapi akan lebih mudah apabila kita bayangkan array tersebut seperti matriks. Istilah "int[3][4]" bisa disebut sebagai matriks dengan 3 baris dan 4 kolom, seperti pada ilustrasi berikut ini :

Untuk banyak hal, kita bisa mengabaikan kenyataan di atas, dan membayangkan bentuk matriks seperti di atas. Kadang-kadang kita juga harus ingat bahwa setiap baris sebenarnya juga merupakan suatu array. Array-array ini bisa dirujuk dengan A[0], A[1], dan A[2]. Setiap baris bertipe int[].

Pernyataan A[1] merujuk pada salah satu baris pada array A. Karena A[1] itu sendiri sebenarnya adalah array int, kita bisa menambah indeks lain untuk merujuk pada posisi pada baris tersebut. Misalnya A[1][3] adalah elemen nomor 3

pada baris 1. Seperti biasa, ingat bahwa posisi baris dan kolom dimulai dari 0. Jadi pada contoh di atas, A[1][3] bernilai 5. Lebih umum lagi, A[i][j] adalah posisi pada baris i dan kolom j. Seluruh elemen pada A bisa dinamakan seperti berikut : A[0][0] A[1][0] A[2][0] A[0][1] A[1][1] A[2][1] A[0][2] A[1][2] A[2][2] A[0][3] A[1][3] A[2][3]

A[i][j]

adalah

variabel

bertipe

int. Kita

bisa

mengisi

nilainya

atau

menggunakannya seperti variabel bertipe int biasa.

Perlu juga diketahui bahwa A.length akan memberikan jumlah baris pada A. Untuk mendapatkan jumlah kolom pada A, kita harus mencari jumlah int dalam setiap baris, yaitu yang disimpan pada A[0]. Jumlah kolom ini bisa didapatkan dengan menggunakan A[0].length, atau A[1].length atau A[2].length. (Tidak ada aturan baku yang menyatakan bahwa pada setiap baris suatu array harus memiliki panjang yang sama, dan sebenarnya pada beberapa aplikasi, juga digunakan array dengan panjang yang berbeda-beda pada setiap barisnya. Akan tetapi apabila kita membuat array dengan perintah seperti di atas, maka kita akan selalu mendapatkan array dengan panjang array yang sama.)

Array 3 dimensi juga dibuat dan diolah dengan cara yang sama. Misalnya, array 3 dimensi bertipe int bisa dibuat dengan pernyataan "int[][][] B = new int [7][5][11];". Kita juga bisa mengilustrasikannya sebagai kubus 3-dimensi. Masing-masing bloknya bertipe int yang bisa dipanggil dalam bentuk B[i][j][k].

Array dimensi lain yang lebih tinggi juga mengikuti pola yang sama, akan tetapi akan sangat sulit untuk membuat visualisasi struktur arraynya.

Kita bisa mengisi array multi dimensi sekaligus pada saat dideklarasikan. Ingat sebelumnya bagaimana array 1 dimensi biasa dideklarasikan, dan bagaimana isinya diinisialisasikan, yaitu seperti daftar nilai-nilainya yang dipisahkan dengan koma, dan diletakkan di dalam tanda kurung kurawal { dan }.

Inisialisasi array bisa juga digunakan untuk array multi dimensi, yang terdiri dari beberapa inisialisasi array 1 dimensi, masing-masing untuk setiap barisnya. Misalnya, array A pada gambar di atas dapat dibuat dengan perintah : int[][] A = { { 1, 0, 12, -1 }, { 7, -3, 2, 5 }, { -5, -2, 2, 9 } }; Jika tidak ada inisialisasi yang diberikan untuk suatu array, maka nilainya akan diisi dengan nilai awal tergantung pada tipenya : nol untuk bilangan, false untuk boolean dan null untuk objek.

Seperti halnya array 1 dimensi, array 2 dimensi juga sering diolah dengan menggunakan perulangan for. UNtuk mengolah semua elemen pada array 2 dimensi, kita bisa menggunakan pernyataan for bertingkat. Jika array A dideklarasikan seperti int[][] A = new int[3][4];

maka kita bisa mengisi 0 untuk semua elemen pada A dengan menggunakan for (int baris = 0; baris < 3; baris++) { for (int kolom = 0; kolom < 4; kolom++) { A[baris][kolom] = 0; } }

Pertama kali perulangan for bagian luar akan memproses dengan baris = 0. Bagian dalamnya akan mengisi keempat kolom pada baris pertama, yaitu A[0][0] = 0, A[0][1] = 0, A[0][2] = 0, dan A[0][3] = 0. Kemudian perulangan for bagian luar akan mengisi baris kedua, dan seterusnya.

Dan juga, kita bisa menjumlah semua elemen pada A dengan int jml = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 4; i++) jml = jml + A[i][j];

Untuk mengolah array 3 dimensi, tentunya kita harus menggunakan perulangan for bertingkat 3.

Suatu array 2 dimensi bisa digunakan untuk menyimpan data yang secara alami memang tersusun sebagai baris dan kolom. Misalnya papan catur terdiri dari 8 baris dan 8 kolom. Jika suatu kelas dinamakan PapanCatur untuk

merepresentasikan papan catur, maka kita bisa deklarasikan dengan perintah

PapanCatur[][] papan = new PapanCatur[8][8];

Kadang-kadang array 2 dimensi juga digunakan untuk masalah yang tidak terlalu jelas matriksnya. Misalnya perusahaan yang memiliki 25 toko. Anggap masingmasing toko memiliki keuntungan yang didapat pada masing-masing toko tersebut setiap bulan pada tahun 2009. Jika toko-toko tersebut memiliki nomor 0 hingga 24, dan 12 bulan dari Januari 09 hingga Desember 09 dinomori 0 hingga 11, maka data keuntungan dapat disimpan dalam array untung yang dideklarasikan seperti : double[][] untung = new double[25][12];

untung[3][2] adalah keuntungan yang dibuat oleh toko nomor 3 di bulan Maret. Atau secara umum, untung[noToko][noBulan] adalah keuntungan toko noToko pada bulan noBulan. Dalam contoh ini array 1 dimensi untung[noToko] memiliki arti : Data keuntungan satu toko selama satu tahun.

Anggap array untung telah diisi dengan data. Data ini bisa diolah lebih lanjut. Misalnya, total keuntungan seluruh perusahaan -- sepanjang tahun dari seluruh toko -- dapat dihitung dengan menjumlahkan semua elemen pada array : double totalUntung; // Total keuntungan perusahaan tahun 2009

total Untung = 0; for (int toko = 0; toko < 25; toko++) { for (int bulan = 0; bulan < 12; bulan++) totalUntung += untung[toko][bulan]; }

Kadang-kadang kita juga perlu menghitung hanya satu baris atau satu kolom saja, bukan keseluruhan array. Misalnya, kita ingin menghitung keuntungan total perusahaan pada bulan Desember, yaitu bulan nomor 11, maka kita bisa gunakan perulangan : double untungDesember = 0.0; for (noToko = 0; noToko < 25; noToko++) untungDesember += untung[noToko][11];

Sekarang mari kita buat array 1 dimensi yang berisi total keuntungan seluruh toko setiap bulan : double[] untungBulanan; // Keuntungan setiap bulan untungBulanan = new double[12]; for (int bulan = 0; bulan < 12; bulan++) { // hitung total keuntungan semua toko bulan ini untungBulanan[bulan] = 0.0; for (int toko = 0; toko < 25; toko++) { untungBulanan[bulan] += profit[toko][bulan]; } }

Sebagai contoh terakhir untuk mengolah array keuntungan, misalnya kita ingin tahu toko mana yang menghasilkan keuntungan terbesar sepanjang tahun. Untuk menghitungnya, kita harus menjumlahkan keuntungan setiap toko sepanjang tahun. Dalam istilah array, ini berarti kita ingin mengetahui jumlah setiap baris

pada array. Kita perlu mencatat hasil perhitungannya untuk mencari mana toko dengan keuntungan terbesar. double untungMaks; // Keuntungan terbesar suatu toko int tokoTerbaik; // Nomor toko yang memiliki keuntungan terbesar double total = 0.0; // Total keuntungan suatu toko

// Pertama-tama hitung keuntungan dari toko nomo 0 for (int bulan = 0; bulan < 12; bulan++) total += untung[0][bulan]; tokoTerbaik = 0; // Mulai dengan anggapan toko nomor 0 untungMaks = total; // adalah toko paling menguntungkan

// Sekarang kita lihat seluruh toko, dan setiap kali // kita melihat toko dengan keuntungan lebih besar dari // untungMaks, kita ganti untungMaks dan tokoTerbaik // dengan toko tersebut for (toko = 1; toko < 25; toko++) { // Hitung keuntungan toko tersebut sepanjang tahun total = 0.0; for (bulan = 0; bulan < 12; bulan++) total += untung[toko][bulan]; // Bandingkan keuntungan toko ini dengan untungMaks if (total > untungMaks) { untungMaks = total; tokoTerbaik = toko; // keuntungan terbesar saat ini // datang dari toko ini

} } // akhir for // Di sini, untungMaks adalah keuntungan terbesar dari 25 toko // dan tokoTerbaik adalah toko dengan keuntung tersebut // (Mungkin juga ada toko yang menghasilkan keuntungan // yang persis sama.)

BAB III STACK

A. DEFINISI STACK Stack adalah suatu tumpukan dari benda. Konsep utamanya adalah LIFO (Last In First Out), benda yang terakhir masuk dalam stack akan menjadi benda pertama yang dikeluarkan dari stack.
COMPO COMPO

TV COMPO VCD VCD TV TV TV

TV

TV

Pada gambar di atas, jika kita ingin mengambil sesuatu dari tumpukan maka kita harus mengambil benda paling atas dahulu, yakni compo. Misalnya jika VCD langsung

diambil, compo akan jatuh. Prinsip stack ini bisa diterapkan dalam pemrograman. Di C++, ada dua cara penerapan prinsip stack, yakni dengan array dan linked list. Setidaknya stack haruslah memiliki operasi-operasi sebagai berikut: Push = Untuk menambahkan item pada tumpukan paling atas Pop = Untuk mengambil item teratas Clear = Untuk mengosongkan stack IsEmpty = Untuk memeriksa apakah stack kosong IsFull = Untuk memeriksa apakah stack sudah penuh Retreive = Untuk mendapatkan nilai dari item teratas 1. Stack dengan Array Sesuai dengan sifat stack, pengambilan / penghapusan di elemen dalam stack harus dimulai dari elemen teratas.

2. Operasi-operasi pada Stack dengan Array - IsFull Fungsi ini memeriksa apakah stack yang ada sudah penuh. Stack penuh jika puncak stack terdapat tepat di bawah jumlah maksimum yang dapat ditampung stack atau dengan kata lain Top =MAX_STACK -1. - Push

Fungsi ini menambahkan sebuah elemen ke dalam stack dan tidak bisa dilakukan lagi jika stack sudah penuh. - Is Empty Fungsi menentukan apakah stack kosong atau tidak. Tanda bahwa stack kosong adalah Top bernilai kurang dari nol. - Pop Fungsi ini mengambil elemen teratas dari stack dengan syarat stack tidak boleh kosong. - Clear Fungsi ini mengosongkan stack dengan cara mengeset Top dengan -1. Jika Top bernilai kurang dari nol maka stack dianggap kosong. - Retreive Fungsi ini untuk melihat nilai yang berada pada posisi tumpukan teratas.

3. Double Stack dengan Array Metode ini adalah teknik khusus yang dikembangkan untuk menghemat pemakaian memori dalam pembuatan dua stack dengan array. Intinya adalah penggunaan hanya sebuah array untuk menampung dua stack. Tampak jelas bahwa sebuah array dapat dibagi untuk dua stack, stack 1 bergerak ke atas dan stack 2 bergerak ke bawah. Jika Top1 (elemen teratas dari Stack 1) bertemu dengan Top 2 (elemen teratas dari Stack 2) maka double stack telah penuh. Implementasi double stack dengan array adalah dengan memanfaatkan operasioperasi yang tidak berbeda jauh dengan operasi single stack dengan array.

4. Operasi-operasi Double Stack Array - IsFull Fungsi ini memeriksa apakah double stack sudah penuh. Stack dianggap penuh jika Top[0] dan Top[1] bersentuhan sehingga stack tida memiliki ruang kosong. Dengan kata lain, (Top[0] + 1) > Top[1]. - Push Fungsi ini memasukkan sebuah elemen ke salah satu stack. - Is Empty Fungsi memeriksa apakah stack pertama atau stack kedua kosong. Stack pertama dianggap kosong jika puncak stack bernilai kurang dari nol, sedangkan stack kedua dianggap kosong jika puncak stack sama atau melebihiMAX_STACK. - Pop Fungsi ini mengeluarkan elemen teratas dari salah satu stack - Clear Fungsi ini mengosongkan salah satu stack.

5. Stack Dengan Single Linked List Selain implementasi stack dengan array seperti telah dijelasnkan sebelumnya, ada cara lain untuk mengimplementasi stack dalam C++, yakni dengan single linked list. Keunggulannya dibandingkan array tebtu saja adalah penggunaan alokasi memori yang dinamis sehingga menghindari pemborosan memori. Misalnya saja pada stack dengan array disediakan tempat untuk stack berisi

150 elemen, sementara ketika dipakai oleh user stack hanya diisi 50 elemen, maka telah terjadi pemborosan memori untuk sisa 100 elemen, yang tak terpakai. Dengan penggunaan linked list maka tempat yang disediakan akan sesuai dengan banyaknya elemen yang mengisi stack. Oleh karena itu pula dalam stack dengan linked list tidak ada istilah full, sebab biasanya program tidak menentukan jumlah elemen stack yang mungkin ada (kecuali jika sudah dibatasi oleh pembuatnya). Namun demikian sebenarnya stack ini pun memiliki batas kapasitas, yakni dibatasi oleh jumlah memori yang tersedia.

6. Operasi-operasi untuk Stack dengan Linked List - IsEmpty Fungsi memeriksa apakah stack yang adamasih kosong. - Push Fungsi memasukkan elemen baru ke dalam stack. Push di sini mirip dengan insert dalam single linked list biasa. - Pop Fungsi ini mengeluarkan elemen teratas dari stack. - Clear Fungsi ini akan menghapus stack yang ada.

List berantai adalah salah satu jenis struktur data, yang tersusun dari objek yang terkait satu sama lain oleh pointer. Pada bagian sebelumnya, kita menggunakan list berantai untuk menyimpan String terurut, dan kita juga mengimplementasikan operasi sisip, hapus dan cari pada list tersebut.

Akan tetapi kita juga bisa menyimpan list String pada array atau ArrayList. Kita bisa juga mengimplementasikan operasi sisip, hapus, dan cari. Implementasi operasi tersebut akan berbeda, akan tetapi antar muka dan perilakunya akan tetap sama.

Istilah tipe data abstrak (abstract data type, atau ADT) adalah kumpulan nilai dan operasi yang bisa dilakukan pada nilai tersebut, tanpa perlu mengetahui bagaimana nilai tersebut disimpan dan bagaimana operasi tersebut

diimplementasikan.

Suatu "list terurut yang berisi string" adalah contoh tipe data abstrak. Ada banyak cara untuk mengimplementasikan tipe data abstrak yang sama, misalnya seperti disebut di atas, list terurut berisi string bisa diimplementasikan dalam bentuk list berantai atau array. Program yang menggunakan tipe data ini bisa menggunakannya tanpa mengetahui dengan detail tentang implementasinya. Lebih jauh, implementasi TDA bisa diganti tanpa mempengaruhi jalannya program secara keseluruhan. Dengan cara ini, program bisa lebih mudah untuk dipelihara dan didebug. TDA adalah alat penting dalam rekayasa perancang lunak.

Pada bagian ini dan yang akan datang, kita akan lihat TDA lain, yaitu tumpukan dan antrian. Tumpukan dan antrian sering diimplementasikan dalam bentuk list berantai, akan tetapi ini bukan satu-satunya cara implementasi. Mari kita anggap bagian ini sebagai studi kasus dari TDA.

Tumpukan (stack) terdiri dari kumpulan item yang disusun sedemikian rupa sehingga satu item ditumpuk di atas item lainnya, mirip seperti tumpukan boks. Hanya item paling atas yang bisa diakses pada suatu saat.

Item tersebut bisa diambil dari tumpukan dengan operasi yang disebut "ambil" (atau dalam bahasa Inggris, istilah untuk mengeluarkan item dari tumpukan disebut "pop"). Item di bawah hanya bisa diambil jika semua item di atasnya telah dikeluarkan dari tumpukan. Suatu item hanya bisa ditambahkan di atas tumpukan dengan perintah "dorong" (atau "push").

Kita bisa membuat tumpukan dari berbagai macam data. Misalnya, jika itemnya bertipe int, maka operasi dorong dan keluar bisa diimplementasikan dalam metode instansi

void dorong(int itemBaru) // Tambah itemBaru di atas tumpukan int ambil() // Mengambil int paling atas pada tumpukan.

Kita tidak bisa mengambil item dari tumpukan yang kosong, jadi kita juga perlu memberi tahu apakah suatu tumpukan kosong atau tidak. Kita perlu operasi lain untuk mengujinya, yang diimplementasikan dalam metode instansi boolean isKosong() // Kembalikan true jika tumpukan kosong

Ilustrasi berikut menggambarkan tumpukan int, sebagai tipe data abstrak. TDA ini bisa diimplementasikan dengan berbagai cara, akan tetapi gambar tumpukan di dalam imaginasi kita akan tetap sama.

Pada implementasi dengan list berantai, tumpukan atas adalah simpul kepala dari list. Kita bisa menambah dan mengurangi simpul pada kepala list berantai -- jauh lebih mudah daripada menyisipkan atau menghapus simpul dari tengah-tengah list. Berikut ini adalah kelas "tumpukan int" yang mengimplementasikan TDA menggunakan list berantai. (Kelas ini menggunakan kelas bertingkat sebagai kelas simpul dari list berantai. Lihat bagian sebelumnya tentang kelas bertingkat. Jika Anda masih belum paham atau merasa tidak nyaman menggunakan kelas bertingkat, Anda bisa juga memisahkannya sebagai kelas terpisah.) public class TumpukanInt { private static class Simpul { // Objek dengan tipe Simpul menyimpan // satu item di dalam list berantai

int item; Simpul berikut; } // Referensi ke simpul paling atas dari tumpukan // jika atas == null, maka tumpukan tidak berisi // apa-apa (kosong) private Simpul atas; public void dorong( int N ) { // Letakkan N di tumpukan paling atas Simpul atasBaru; // Simpul baru untuk menyimpan item baru

atasBaru = new Simpul(); atasBaru.item = N; // Simpan N di simpul baru

atasBaru.berikut = atas; // Simpul baru menunjuk pada atas lama atas = atasBaru; } public int ambil() { // Ambil item paling atas dari dalam tumpukan // dan kembalikan nilainya. Catatan bahwa rutin ini akan // melempar NullPointerException jika kita mencoba untuk // mengambil item dari tumpukan kosong int itemAtas = atas.item; // Item yang akan diambil atas = atas.berikut; return itemAtas; // Item yang dulunya di posisi kedua sekarang di atas // Simpul baru sekarang menjadi atas

} public boolean isKosong() { // Kembalikan true jika tumpukan kosong. // Kembalikan false jika ada satu atau lebih item di dalam tumpukan return (atas == null); } } // akhir kelas TumpukanInt

Anda perlu memahami bagaimana operasi dorong dan ambil dilaksanakan. Mungkin sedikit oret-oretan akan membantu. Lihat bahwa list berantai merupakan bagian private dari kelas TumpukanInt. Program yang menggunakan kelas ini tidak perlu tahu bahwa list berantai digunakan dalam implementasinya.

Sekarang, kita bisa juga mengimplementasikan tumpukan dalam bentuk array, bukan hanya list berantai. Karena banyaknya item di dalam tumpukan bervariasi dan tidak bisa ditentukan, maka kita perlu memiliki variabel lain yang melacak berapa banyak tempat dalam array yang sudah digunakan.

Jika variabel ini kita namakan atas, maka item akan disimpan dalam tumpukan pada posisi 0, 1, ..., atas - 1. Item pada posisi 0 adalah item paling bawah dalam tumpukan, sedangkan item atas - 1 adalah item paling atas.

Untuk mendorong item baru ke dalam tumpukan, kita bisa meletakkan item di posisi atas kemudian nilai atas ditambahkan 1. Jika kita tidak ingin memberi limit

banyaknya item di dalam tumpukan, kita bisa menggunakan array dinamis yang telah dibahas sebelumnya.

Berikut ini adalah implementasi kelas TumpukanInt dengan menggunakan array dinamis: public class TumpukanInt { private int[] item = new int[10]; // Menyimpan item dalam tumpukan private int atas = 0; // Banyaknya item dalam tumpukan public void dorong( int N ) { // Tambahkan N ke dalam tumpukan if (atas == item.length) { // Array sudah penuh, jadi buat array yang lebih besar dan // kopi isi tumpukan sekarang ke dalam array baru int[] arrayBary = new int[ 2*item.length ]; System.arraycopy(item, 0, arrayBary, 0, item.length); item = arrayBaru; } item[atas] = N; // Letakkan N di tempat kosong berikutnya atas++; } public int ambil() { // Mengambil item teratas dari tumpukan dan mengembalikannya // Ingat bahwa rutin ini akan melempar pengecualian // ArrayIndexOutOfBoundsException jika mencoba mengambil // Jumlah item bertambah 1

// item dari tumpukan kosong int itemAtas = item[top - 1] // Item teratas di dalam tumpukan atas--; // Jumlah item dalam tumpukan berkurang 1

return itemAtas; } public boolean isKosong() { // Kembalikan true jika tumpukan kosong // Kembalikan false jika ada satu atau lebih item di dalam tumpukan return (atas == 0); } } // akhir kelas TumpukanInt

Sekali lagi, implementasi tumpukan (dalam bentuk array) bersifat private. Kedua versi kelas TumpukanInt bisa digunakan dalam suatu program. Jika suatu program menggunakan versi pertama, maka kita bisa menggantinya dengan versi kedua tanpa mengubah isi program lain. Akan tetapi, ada satu kasus di mana kedua kelas akan berbeda, yaitu jika mencoba mengambil item dari tumpukan kosong, maka versi pertama akan melemparkan NullPointerException sedangkan versi kedua akan melemparkan ArrayIndexOutOfBoundsException.

Mungkin akan lebih baik untuk mendefinisikan jenis pengecualian baru, misalnya EmptyStackException di kedua versi. Dan sebetulnya TDA harusnya memberikan spesifikasi apa yang harus dilakukan jika program mencoba mengambil item dari tumpukan kosong. Ini yang sering dilupakan ketika seseorang membuat

spesifikasi untuk interface, yang akhirnya masalah lain akan muncul di kemudian hari.

Apa contoh kegunaan tumpukan dalam keadaan sehari-hari? Misalnya, lihat apa yang terjadi jika suatu rutin memanggil subrutin. Rutin pertama akan dihentikan sementara ketika subrutin yang dipanggil dieksekusi, dan akan diteruskan apabila subrutin yang dipanggil selesai dijalankan.

Sekarang, misalnya subrutin tersebut memanggil subrutin kedua, dan subrutin kedua memanggil subrutin ketiga, dan seterusnya. Setiap subrutin akan berhenti untuk sementara ketika subrutin berikutnya dipanggil. Komputer harus bisa melacak subrutin yang dihentikan. Bagaimana caranya? Yaitu menggunakan tumpukan. dengan

Ketika subrutin dipanggil, catatan aktivasi (activation record) dibuat untuk subrutin tersebut. Catatan aktivasi ini berisi informasi yang relevan tentang eksekusi dari subruti tersebut, misalnya parameter dan variabel lokalnya. Catatan aktivasi ini ditempatkan dalam tunpukan. Catatan ini akan dibuang ketika subrutin selesai dipanggil.

Jika subrutin ini memanggil subrutin lain, maka catatan aktivasi dari subrutin kedua akan didorong ke dalam tumpukan, di atas catatan aktivasi subrutin pertama. Tumpukan akan semakin besar jika semakin banyak subrutin yang dipanggil, dan semakin kecil jika subrutin-subrutin itu selesai dijalankan.

Contoh lainnya, tumpukan digunakan untuk mengevaluasi ekspresi postfix (postfix expresssion). Ekspresi matematika biasa seperti 2+(15-12)*17 disebut ekspresi infix. Dalam ekspresi infix, operator berada di tengah nilai yang akan dihitung, misalnya "2 + 2". Dalam ekspresi postfix, operator ada di akhir nilai, misalnya "2 2 +".

Ekspresi 2+(15-12)*17" dapat ditulis dalam bentuk postfix menjadi "2 15 12 - 17 * +". Di sini operator "-" dilakukan pada dua nilai sebelumnya, yaitu "15" dan "12". Tanda * dilakukan pada dua nilai sebelumnya, yaitu "15 12 -" dan "17". Dan operator "+" dilakukan pada 2 dan "15 12 - 17 *". Hasilnya akan persis sama dengan ekspresi infix awalnya.

Meskipun lebih mudah bagi manusia untuk melakukan perhitungan dengan ekspresi infix, ekspresi postfix memiliki beberapa keuntungan. Satu hal, ekspresi postfix tidak memerlukan tanda kurung atau aturan perhitungan (tanda kali harus dilakukan lebih dulu sebelum tambah misalnya). Aturan penghitungan hanya ditentukan berdasarkan urutannya saja. Sehingga algoritma yang menghitung ekspresi postfix dapat menjalankannya dengan lebih cepat dan tepat.

Sekarang misalnya kita akan menghitung ekspresi "2 15 12 - 17 * +" dari kiri ke kanan. Nilai yang kita temui pertama adalah 2, tapi apa yang bisa kita lakukan dengan 2? Di sini kita belum tahu operatornya, dan selain itu kita juga belum tahu nilai lainnya. Kita akan ingat nilai 2 ini untuk sementara, yaitu dengan mendorongnya ke dalam tumpukan.

Berikutnya, kita akan melihat nilai 15, yang kita juga masukkan ke dalam tumpukan di atas nilai 2. Kemudian nilai 12 juga dimasukkan ke dalam tumpukan di atas 15.

Sekarang kita sampai pada operator "-". Operasi ini dilakukan pada dua nilai sebelumnya. Kita telah menyimpan 2 nilai sebelumnya ke dalam tumpukan, yaitu 15 dan 12. Sekarang kita ambil 2 nilai tersebut dari dalam tumpukan, dan kita lakukan perhitungan 15 - 12 yaitu 3.

Nilai 3 ini kita simpan lagi ke dalam tumpukan. Ingat bahwa 15 dan 12 sudah diambil dari dalam tumpukan, sehingga hanya nilai 2 yang ada di dalam tumpukan. Setelah nilai 3 dimasukkan ke dalam tumpukan, maka 3 ada di atas 2 di dalam tumpukan.

Item berikutnya adalah 17, yang juga dimasukkan ke dalam tumpukan di atas nilai 3. Untuk menghitung item berikutnya "*", kita ambil 2 nilai dari dalam tumpukan, yaitu 3 dan 17. Hasil dari 3 * 17, yaitu 51 dimasukkan kembali ke dalam tumpukan (di atas 2 yang masih ada di dalam tumpukan).

Item berikutnya adalah "+", yang akan mengambil 51 dan 2 dari dalam tumpukan, menghitung hasilnya, yaitu 53, kemudian menyimpannya lagi ke dalam tumpukan. Sekarang kita sampai pada akhir ekspresi. Nilai pada tumpukan itu adalah hasil perhitungan keseluruhan ekspresi. Kita cukup mengambil nilainya dan melaporkan hasilnya, yaitu 53.

Algoritma untuk melakukan perhitungan ekspresi postfix adalah sebagai berikut :

Mulai dengan tumpukan kosong untuk setiap item di dalam ekspresi: jika item berupa bilangan: Dorong item ke dalam tumpukan jika item berupa operator Ambil dua nilai dari tumpukan // bisa terjadi kesalahan Lakukan perhitungan dua nilai dengan operator tersebut Dorong hasil perhitungan ke dalam tumpukan else Ada kesalahan dalam ekspresi Ambil nilai dari tumpukan Jika tumpukan tidak kosong: Ada kesalahan dalam ekspresi else: Nilai terakhir adalah hasil perhitungan

Kesalahan ekspresi dapat dideteksi dengan mudah. Misalnya, dalam ekspresi "2 3 + *", tidak cukup nilai untuk menghitung operasi "*". Ini akan dideteksi oleh algoritma ketika mencoba mengambil nilai kedua dari dalam tumpukan, karena pada saat itu tumpukan sudah kosong.

Kesalahan lain bisa juga muncul ketika menghitung ekspresi "2 3 4 +", di mana tidak cukup operator untuk menghitung semua nilai. Ini akan dideteksi ketika 2 masih ada di dalam tumpukan di akhir algoritma.

Ekspresi postfix sering digunakan secara internal oleh komputer. Dan sebenarnya, mesin virtual Java adalah "mesin tumpukan", yang menggunakan tumpukan untuk melakukan perhitungan yang telah kita diskusikan. Algoritma ini bisa diperluas untuk menangani variabel dan konstanta. Ketika variabel ditemukan di dalam ekspresi, isi variabel ini didorong ke dalam tumpukan.

Algoritma ini juga bisa dikembangkan untuk operator yang membutuhkan kurang atau lebih dari dua operator. Banyaknya nilai yang diambil dari dalam tumpukan bisa disesuaikan dengan berapa banyak nilai yang dibutuhkan. Misalnya, operator "-" sebagai operator negasi, misalnya "-x" hanya membutuhkan satu nilai.

BAB IV LINGKED LIST

Linked adalah koleksi obyek heterogen dengan sifat setiap obyek (kecuali obyek terakhir) mempunyai penerus dan setiap obyek (kecuali obyek pertama) mempunyai pendahulu. Salah satu penggunaan pointer adalah untuk membuat linked list atau senarai berantai. Linked list sendiri dapat diartikan sebagai sekumpulan komponen yang saling berhubungan (berantai) dengan bantuan pointer. Perhatikan ilustrasi berikut untuk lebih jelasnya.

Single Linked List merupakan jenis Linked List yang hanya menggunakan satu variabel pointer untuk menyimpan banyak data.Double Linked List Merupakan linked list yang tiap-tiap nodenya menyimpan node sebelumnya dan node yang selanjutnya, atau biasa disebut dengan linked list berpointer ganda Circular Linked adalah linked list yang node awalnya menunjuk ke node akhir dan node akhirnya menunjuk ke node awal. Sehingga membentuk link list tersebut membentuk suatu siklus seperti lingkaran.

BAB V QUEUE

A. DEFINISI QUEUE Jika diartikan secara harafiah, queue berarti antrian, queue merupakan salah satu contoh aplikasi dari pembuatan double linked list yang cukup sering kita temui dalam kehiduypan sehari-hari, misalnya saat Anda mengantri di loket untuk membeli tiket. Istilah yang cukup sering dipakai seseorang masuk dalam sebuah antrian adalah enqueue. Dalam suatu antrian, yang dating terlebih dahulu akan

dilayani lebih dahulu. Istilah yang sering dipakai bila seseorang keluar dari antrian adalah dequeue. Walaupun berbeda implementasi, struktur data queue setidaknya harus memiliki operasi-operasi sebagai berikut : EnQueue : Memasukkan data ke dalam antrian DeQueue : Mengeluarkan data terdepan dari antrian Clear : Menghapus seluruh antrian IsEmpty : Memeriksa apakah antrian kosong IsFull : Memeriksa apakah antrian penuh 1. Implementasi Queue dengan Linear Array Linear array adalah suatu array yang dibuat seakan-akan merupakan suatu garis lurus dengan satu pintu masuk dan satu pintu keluar. Berikut ini diberikan deklarasi kelas Queue Linear sebagai implementasi dari Queue menggunakan linear array. Dalam prakteknya, anda dapat menggantinya sesuai dengan kebutuhan Anda. Data diakses dengan field data, sedangkan indeks item pertama dan terakhir disimpan dalam field Head dan Tail. Konstruktor akan menginisialisasikan nilai Head dan Tail dengan -1 untuk menunjukkan bahwa antrian masih kosong dan mengalokasikan data sebanyak MAX_QUEUE yang ditunjuk oleh Data. Destruktor akan mengosongkan antrian kembali dan mendealokasikan memori yang digunakan oleh antrian.

Operasi-Operasi Queue dengan Linear Array a. IsEmpty Fungsi IsEmpty berguna untuk mengecek apakah queue masih kosong atau sudah berisi data. hal ini dilakukan dengan mengecek apakah tail bernilai -1 atau tidak. Nilai -1 menandakan bahwa queue masih kosong.

b. Is Full Fungsi IsFull berguna untuk mengecek apakah queue sudah penuh atau masih bisa menampung data dengan cara mengecek apakah nilai tail sudah sama dengan jumlah maksimal queue. Jika nilai keduanya sama, berarti queue sudah penuh. c. EnQueue Fungsi EnQueue berguna untuk memasukkan sebuah elemen dalam queue. d. DeQueue Fungsi DeQueue berguna untuk mengambil sebuah elemen dari queue. Operasi ini sering disebut juga serve. Hal ini dilakukan dengan cara memindahkan sejauh satu langkah ke posisi di depannya sehingga otomatis elemen yang paling depan akan tertimpa dengan elemen yang terletak di belakangnya. e. Clear Fungsi Clear berguna untuk menghapus semua lemen dalam queue dengan jalan mengeluarkan semua elemen tersebut satu per satu hingga queue kosong dengan memanfaatkan fungsi DEQueue.

2. Implementasi Queue dengan Circular Array Circular array adalah suatu array yang dibuat seakan-akan merupakan sebuah lingkaran dengan titik awal (head) dan titik akhir (tail) saling bersebelahan jika array tersebut masih kosong. Posisi head dan tail pada gambar diatas adalah bebas asalkan saling bersebelahan. Berikut ini diberikan deklarasi kelas Queue Circular sebagai implementasi circular array. Dalam prakteknya, Anda dapat

menggantikanny sesuai dengan kebutuhan Anda. Data diakses dengan field data, sedangkan indeks itemn pertama dan terakhir disimpan dalam field Head dan Tail. Konstruktor akan menginisialisasi nilai Head dan Tail dengan 0 dan MAX-QUEUE-1 untuk menunjukkan bahwa antrian masih kosong dan mengalokasikan data sebanyak MAX-QUEUE yang ditunjuk oleh Data. destruktor akan mengosongkan antrian kembali dan mendealokasikan memori yang digunakan oleh antrian.

Operasi-Operasi Queue dengan Circular Array a. Is Empty Fungsi IsEmpty berguna untuk mengecek apakah Queue masih kosong atau sudah berisi. Hal ini dilakukan dengan mengecek apakah tail masih terletak bersebelahan dengan head dan tail lebih besar dari head atau tidak. Jika benar, maka queue masih kosong. b. IsFull Fungsi IsFull berguna untuk mengecek apakah queue sudah penuh atau masih bisa menampung data dengan cara mengecek apakah tempat yang masih kosong tinggal satu atau tidak (untuk membedakan dengan empty dimana semua tempat kosong). Jika benar berarti queue penuh. c. EnQueue Fungsi EnQueue berguna untuk memasukkan sebuah elemen ke dalam queue tail dan head mula-mula bernilai nol (0). d. DeQueue

DeQueue berguna untuk mengambil sebuah elemen dari queue. Hal ini dilakukan dengan cara memindahkan posisi head satu langkah ke belakang.

3. Implementasi Queue dengan Double Linked List Selain menggunakan array, queue juga dapat dibuat dengan linked list. Metode linked list yang digunakan adalah double linked list.

Operasi-operasi Queue dengan Double Linked List a. IsEmpty Fungsi IsEmpty berguna untuk mengecek apakah queue masih kosong atau sudah berisi data. Hal ini dilakukan dengan mengecek apakah head masih menunjukkan pada Null atau tidak. Jika benar berarti queue masih kosong. b. IsFull Fungsi IsFull berguna untuk mengecek apakah queue sudah penuh atau masih bias menampung data dengan cara mengecek apakah Jumlah Queue sudah sama dengan MAX_QUEUE atau belum. Jika benar maka queue sudah penuh. c. EnQueue Fungsi EnQueue berguna untuk memasukkan sebuah elemen ke dalam queue (head dan tail mula-mula meunjukkan ke NULL). d. DeQueue Procedure DeQueue berguna untuk mengambil sebuah elemen dari queue. Hal ini dilakukan dengan cara menghapus satu simpul yang terletak paling depan (head).

Antrian Posted Sab, 03/28/2009 - 23:23 by belajarprogram Versi ramah cetak

Antrian (queue) adalah struktur data mirip dengan tumpukan, yaitu terdiri dari item dalam urutan tertentu. Antrian memiliki dua ujung, yang disebut ujung depan dan ujung belakang. Item selalu dimasukkan dari belakang antrian, dan selalu dikeluarkan dari depan antrian. Operasi memasukkan dan mengeluarkan item dari dalam antrian disebut "masuk" dan "keluar" (dalam bahasa Inggris disebut enqueue dan dequeue).

Suatu item yang ditambahkan di belakang antrian tidak bisa dihapus sebelum item di depannya dihapus. Mirip seperti antrian pada kasir atau pada customer service di bank misalnya. Customer akan dilayani dalam urutan ketika mereka datang.

Antrian bisa menampung item dengan jenis apa saja. Untuk antrian int, operasi masuk dan keluar dapat diimplementasikan sebagai metode instansi dalam kelas "AntrianInt". Kita juga membutuhkan metode instansi untuk menguji apakah antrian kosong atau tidak: void masul(int N) // Tambah N di belakang antrian int keluar() // Keluarkan antrian dari depan dan kembalikan isinya boolean isKosong() // Kembalikan true jika antrian kosong Antrian bisa diimplementasikan dalam bentuk list berantai atau array. Akan tetapi implementasi yang efisien dalam bentuk array sedikit lebih sulit, sehingga akan kita abaikan dalam diskusi kita. Dalam implementas dalam bentuk list berantai, item bertama adalah item di depan antrian. Untuk mengeluarkan item dari depan antrian, kita dapat lakukan seperti mengambil item dari atas tumpukan.

Belakang antrian adalah akhir dari list. Unutk memasukkan item ke dalam antrian, kita harus mengeset pointer di akhir simpul untuk menunjuk ke simpul baru yang berisi item yang akan kita tambahkan. Kita bisa menggunakan perintah seperti "buntut.berikut = simpulBaru;", di mana buntut adalah pointer ke simpul terakhir dari list.

Jika kepala adalah pointer ke simpul pertama, maka kita bisa mendapat pointer ke simpul terakhir dengan menggunakan :

Simpul buntut;

// Pointer ini akan menunjuk ke simpul terakhir

buntut = kepala; // Mulai dari simpul pertama while (buntut.berikut != null) { buntut = buntut.berikut; } // Di sini, buntut.berikut berisi null, artinya buntut adalah // simpul terakhir di dalam list

Akan tetapi, tentunya akan sangat tidak efisien jika kita harus melakukan perulangan terus menerus setiap kali kita memasukkan item baru ke dalam antrian. Dengan alasan efisiensi, kita akan menyimpan pointer ke akhir simpul sebagai variabel instansi. Kita harus selalu ingat untuk terus mengupdate isi variabel ini setiap kali simpul baru ditambahkan ke akhir list. Dengan pengetahuan di atas, kita bisa membuat kelas "AntrianInt" dengan mudah sebagai berikut : public class AntrianInt { private static class Simpul { // Objek bertipe Simpul berisi item dalam bentuk // list berantai int item; Simpul berikut; } // Menunjuk ke simpul pertama pada antrian // Jika antrian kosong, maka kepala berisi null

private Simpul kepala = null; private Simpul buntut = null; // Menunjuk ke simpul akhir pada antrian void masuk( int N ) { // Menambahkan N ke akhir antrian Simpul buntutBaru = new Simpul(); // Simpul baru untuk menampung item baru buntutBaru.item = N; if (kepala == null) { // Antrian kosong, sehingga simpulBaru menjadi // satu-satunya simpul di dalam list. Sehingga // kepala dan buntut sama-sama menunjuk ke simpulBaru kepala = buntutBaru; buntut = buntutBaru; } else { // Simpul baru menjadi buntut antrian // (kepala tidak berpengaruh apa-apa) buntut.berikut = buntutBaru; buntut = buntutBaru; } } int keluar() { // Keluarkan item dari kepala antrian

// Bisa melempar NullPointerException. int itemPertama = kepala.item; kepala = kepala.berikut; // Sekarang item kedua menjadi kepala if (kepala == null) { // Sekarang antrian kosong. Simpul yang telah dihapus adalah // kepala sekaligus buntut, karena simpul ini adalah satu-satunya // yang ada di dalam antrian. Isi buntut dengan null. buntut = null; } return itemPertama; } boolean isKosong() { // Kembalikan true jika antrian kosong return (kepala == null); } } // akhir kelas AntrianInt

Antrian digunakan dalam komputer (dan juga dalam kehidupan nyata) ketika hanya satu item yang bisa diproses pada suatu saat, akan tetapi banyak item bisa menunggu untuk diproses, misalnya :

Dalam Java program yang memiliki banyak threads, thread yang ingin diolah dalam CPU disimpan di dalam antrian. Ketika thread baru dimulai, thread ini ditambahkan ke dalam antrian. Thread akan dihapus dari depan antrian, diolah

oleh CPU, dan kemudian -- jika belum selesai -- diletakkan kembali di belakang antrian untuk menunggu giliran berikutnya.

Event seperti ketikan tombol dan klik mouse disimpan dalam antrian yang disebut "antrian event". Program akan menghapus item dari antrian item dan mengolahnya satu per satu. Mungkin saja terjadi beberapa event diterima ketika satu event sedang diproses, akan tetapi event akan diolah berdasarkan urutan kedatangannya

ServerSocket, memiliki antrian di dalamnya yang berisi permintaan sambungan yang diterima akan tetapi belum dilayani. Metode accept() pada kelas ServerSocket menerima permintaan sambungan berikutnya dari depan antrian ini.

Antrian disebut mengikuti kebijakan FIFO (First In First Out, atau Pertama Masuk Pertama Keluar). Atau dalam bahasa awam, pertama datang dan pertama dilayani. Di lain pihak, tumpukan mengikuti prinsip LIFO (Last In First Out, Terakhir Masuk Pertama Keluar). Item yang bisa keluar dari tumpukan adalah item terkakhir yang dimasukkan. Seperti antrian, tumpukan juga bisa digunakan untuk menampung item yang sedang menunggu untuk diproses (walaupun dalam aplikasi di mana antrian biasa digunakan, tumpukan adalah antrian yang tidak adil).

BAB VI Binary Tree


Binary Tree adalah tree dengan syarat bahwa tiap node hanya boleh memiliki maksimal dua subtree dan kedua subtree tersebut harus terpisah. Sesuai dengan definisi tersebut tiap node dalam binary tree hanya boleh memiliki paling banyak dua child.

A. Jenis- Jenis Binary Tree 1. Full Binary Tree Jenis binary tree ini tiap nodenya (kecuali leaf) memiliki dua child dan tiap subtree harus mempunyai panjang path yang sama.

2. Complete Binary Tree Jenis ini mirip dengan Full Binary Tree, namun tiap subtree boleh memiliki panjang path yang berbeda dan setiap node kecuali leaf hanya boleh memiliki 2 child. 3. Skewed Binary Tree Skewed Binary Tree adalah Binary Tree yang semua nodenya (kecuali leaf) hanya memiliki satu child. 4. Implementasi Binary Tree Binary tree dapat diimplementasikan dalam C++ dengan menggunakan double linkedlist.

Operasi-Operasi pada Binary Tree Create : Membentuk binary tree baru yang masih kosong Clear : Mengosongkan binary tree yang sudah ada Empty : Function untuk memeriksa apakah binary tree masih kosong Insert : Memasukkan sebuah node ke dalam tree. Ada tiga pilihan insert : sebagai root, left child, atau right child. Khusus insert sebagai root, tree harus dalam keadaan kosong Find : Mencari root, parent, left child, atau right child dari suatu node. (tree tidak boleh kosong). Update : Mengubah isi dari node yang ditunjuk oleh pointer curret (Tree tidak boleh kosong)

Retrieve : Mengetahui isi dari node yang ditunjuk oleh pointer current (Tree tidak boleh kosong) DeleteSub : Menghapus sebuah subtree (node beserta seluruh descendantnya) yang ditunjuk current. Tree tidak boleh kosong. Setelah itu, pointer current dakan berpindah ke parent dari node yang dihapus. Characteristic Mengetahui karakteristik dari suatu tree, yakni: size, height, serta average length. Tree tidak boleh kosong. Traverse Mengunjungi seluruh node-node pada tree, masingmasing sekali. Hasilnya adalah urutan informasi secara linear yang tersimpan dalam tree. Ada tiga cara traverse,yaitu PreOrder, InOrder, dan PostOrder. Langkah-langkah Tranverse : PreOrder : cetak isi node yang dikunjungi, kunjungi Left Child, kunjungi Right Child InOrder : kunjungi Left Child, cetak isi node yang dikunjungi, kunjungi Right Child PostOrder : kunjungi Left Child, kunjungi Right Child cetak isi node yang dikunjungi.

5. Binary Search Tree Binary Tree ini memiliki sifat dimana semua left child harus lebih kecil dari pada right child dan parentnya. Semua right child juga harus lebih besar dari left child serta parentnya. Binary search tree dibuat untuk mengatasi kelemahan pada binary tree biasa, yaitu kesulitan dalam searching / pendarian node

tertentu dalam binary tree. Pada dasarnya operasi dalam Binary Search Tree sama dengan Binary Tree biasa, kecuali pada operasi insert, update, dan delete. 6. Insert Pada Binary Search Tree insert dilakukan setelah lokasi yang tepat ditemukan (lokasi tidak ditentukan oleh user sendiri ). 7. Update Update ini seperti yang ada pada Binary Tree biasa, namun di sini update akan berpengaruh pada posisi node tersebut selanjutnya. Bila update mengakibatkan tree tersebut bukan Binary Search Tree lagi, harus dilakukan perubahan pada tree dengan melakukan rotasi supaya tetap menjadi Binary Search Tree. 8. Delete Seperti halnya update, delete dalam Binary Search Tree juga turut mempengaruhi struktur dari tree tersebut. B. AVL TREE

AVL Tree adalah Binary Search Tree yang memiliki perbedaan tinggi/ level maksimal 1antara subtree kiri dan subtree kanan. AVL Tree muncul untuk menyeimbangkan Binary Search Tree. Dengan AVL Tree, waktu pencarian dan bentuk tree dapat dipersingkat dan disederhanakan.

Selain AVL Tree, terdapat pula Height Balanced n Tree, yakni Binary Search Tree yang memiliki perbedaan level antara subtree kiri dan subtree kanan maksimal adalah sehingga dengan kata lain AVL Tree adalah Height Balanced 1

Tree. Untuk memudahkan dalam menyeimbangkan tree, digunakan simbol-simbol Bantu : - (tanda minus) : digunakan apabila Subtree kiri lebih panjang dari Subtree kanan. + (tanda plus) : digunakan apabila Subtree kanan lebih panjang dari Subtree kiri. 0 (nol) : digunakan apabila Subtree kiri dan Subtree kanan mempunyai height yang sama.

Kita telah melihat di beberapa bagian sebelumnya bagaimana objek bisa dikaitkan satu sama lain menjadi list berantai. Ketika suatu objek memiliki 2 pointer ke objek dengan tipe yang sama, kita bisa membuat struktur data yang lebih kompleks dari list berantai. Dalam bagian ini kita akan melihat salah satu struktur dasar dan paling berguna: pohon biner (binary tree).

Setiap objek dalam pohon biner memiliki dua pointer, yang biasa disebut kiri dan kanan. Selain itu, tentunya simpul pada pohon memiliki data yang bisa bertipe apa saja. Misalnya, pohon biner integer bisa dibuat dalam bentuk :

class SimpulPohon { int item; // Data pada simpul ini

SimpulPohon kiri; // Pointer ke cabang sebelah kiri SimpulPohon kanan; // Pointer ke cabang sebelah kanan }

Pointer kiri dan kanan dalam SimpulPohon bisa beriis null atau menunjuk pada objek lain dengan tipe SimpulPohon. Simpul yang menunjuk pada simul lain disebut induk (parent), sedangkan yang ditunjuk disebut anak (child). Dalam gambar di atas, simpul 3 adalah induk dari simpul 6, dan simpul 4 dan 5 adalah anak dari simpul 2. Tidak semua struktur yang terdiri dari simpul pohon merupakan pohon biner. Pohon biner harus memiliki sifat :

Harus ada satu simpul di dalam pohon yang tidak memiliki induk. Simpul ini disebut simpul akar (root). Simpul lain harus memiliki hanya satu induk Tidak boleh ada perulangan dalam pohon biner, artinya tidak boleh ada rantai pointer yang dimulai dari satu simpul dan berakhir di simpul yang sama.

Simpul yang tidak memiliki anak disebut simpul daun (leaf). Simpul daun dapat dikenali karena kedua pointer kiri dan kanannya berisi null. Dalam ilustrasi suatu pohon biner, biasanya simpul akar terletak di atas dan simpul daun terletak di

bawah -- tidak sama seperti pohon sungguhan. Akan tetapi, kita bisa melihat cabang-cabang, seperti pohon, yang merupakan cikal bakal nama pohon biner ini.

Misalnya, mari kita lihat suatu simpul pada pohon biner. Lihat simpul tersebut beserta seluruh simpul turunannya (yaitu anak, anak dari anaknya, dan seterusnya). Simpul-simpul ini juga membentuk pohon biner, yang disebut pohon cabang (subtree) dari pohon awalnya. Misalnya, pada gambar di atas, simpul 2, 4, dan 5 membentuk pohon cabang. Pohon cabang ini disebut pohon cabang sebelah kiri dari simpul akarnya. Hal yang sama, simpul 3 dan 6 adalah pohon cabang sebelah kanan dari simpul akarnya. Salah satu atau kedua pohon cabang bisa kosong. Ini adalah definisi rekursif. Jadi tidak heran apabila kita menerapkan subrutin rekursif untuk mengolah struktur data pohon.

Mari kita lihat bagaimana caranya menghitung banyaknya simpul di dalam pohon biner. Sebagai latihan, kita mungkin bisa membuat algoritma untuk menghitung simpul. Inti permasalahannya adalah, bagaimana melacak simpul mana yang belum dihitung. Ini bukan hal sepele. Dan mungkin kita tidak mungkin menyelesaikannya tanpa menggunakan tumpukan atau antrian.

Dengan rekursi, algoritmanya akan jauh lebih mudah. Suatu pohon bisa kosong, atau bisa terdiri dari akar dan dua pohon cabang. Jika pohon kosong, maka banyaknya simpul adalah nol. (Ini merupakan kasus dasar dari rekursi). Kemudian kita bisa menggunakan rekursi untuk menghitung jumlah simpul pada masing-

masing pohon cabang. Jumlahkan hasil dari kedua cabang, kemudian tambah satu simpul akarnya. Ini adalah jumlah simpul di dalam pohon.

Dalam Java bisa kita tuliskan sebagai : static int hitungSimpul( SimpulPohon akar ) { // Hitung berapa simpul yang dimiliki suatu pohon // biner, termasuk akarnya if ( akar == null ) return 0; // Pohon kosong, tidak ada simpul di dalamnya else { int jumlah = 1; // Mulai dengan menghitung akarnya // Tambahkan dengan simpul dari pohon cabang sebelah kiri jumlah += hitungSimpul(akar.kiri);

// Tambahkan dengan simpul dari pohon cabang sebelah kanan jumlah += hitungSimpul(akar.kanan); return hitung; // Kembalikan jumlahnya } } // akhir hitungSimpul()

Juga, mari kita lihat bagaimana mencetak isi item di dalam pohon biner. Jika pohon kosong, kita tidak melakukan apa-apa. Jika pohon tidak kosong, maka mungkin ia berisi akar dan dua pohon cabangnya. Cetak item pada akarnya, dan

gunakan rekursi untuk mencetak item di dalam pohon cabangnya. Berikut ini adalah subrutin untuk mencetak semua item dalam satu baris cetakan. static void preorderCetak( SimpulPohon akar ) { // Cetak semua item di dalam pohon yang ditunjuk oleh akar. // Item pada akar dicetak dahulu, kemudian item di sebelah kiri // dan baru item di pohon cabang sebelah kanan if ( akar != null ) { // (Jika null, kita tidak melakukan apa-apa.) System.out.print( akar.item + " " ); // Print item akar preorderCetak( akar.kiri ); // Print item di pohon cabang kiri preorderCetak( akar.kanan ); // Print items di pohon cabang kanan } } // akhir preorderCetak()

Rutin ini disebut "preorderCetak" karena ia menelusuri pohon secara preorder. Dalam penelusuran preorder, simpul akan diproses terlebih dahulu, kemudian pohon cabang sebelah kiri ditelusuri, dan kemudian cabang sebelah kanan. Dalam penelusuran postorder, cabang kiri ditelusuri, kemudian cabang kanan, baru kemudian simpul akar. Dan dalam penelusuran inorder, cabang kiri dikunjungi dahulu, kemudian akar, dan kemudian cabang kanan.

Subrutin yang mencetak postorder dan inorder berbeda dengan preorder dalam urutan pencetakannya di layar : static void postorderCetak( SimpulPohon akar ) { // Cetak semua item di dalam pohon yang ditunjuk oleh akar.

// Cabang pohon sebelah kiri dicetak dahulu, kemudian kanan, // dan baru simpul akarnya if ( akar != null ) { // (Jika null, kita tidak melakukan apa-apa.) postorderCetak( akar.kiri ); // Print item di pohon cabang kiri postorderCetak( akar.kanan ); // Print items di pohon cabang kanan System.out.print( akar.item + " " ); // Print item akar } } // akhir postorderCetak()

static void inorderCetak( SimpulPohon akar ) { // Cetak semua item di dalam pohon yang ditunjuk oleh akar. // Cabang pohon cabang sebelah kiri dicetak dahulu, // kemudian simpul akarnya, baru pohon cabang sebelah kanan if ( akar != null ) { // (Jika null, kita tidak melakukan apa-apa.) inorderCetak( akar.kiri ); // Print item di pohon cabang kiri System.out.print( akar.item + " " ); // Print item akar inorderCetak( akar.kanan ); // Print items di pohon cabang kanan } } // akhir inorderCetak()

Subrutin ini bisa dijalankan pada pohon biner yang diilustrasikan di awal bagian ini. Urutan item ketika dicetak akan berbeda, di mana : preorderCetak mencetak: 1 2 4 5 3 6 postorderCetak mencetak: 4 5 2 6 3 1

inorderCetak mencetak: 4 2 5 1 3 6

Dalam preorderCetak misalnya, itam pada akar pohon, yaitu 1, dicetak paling awal. Akan tetapi urutan preorder juga dilakukan untuk setiap pohon cabangnya. Simpul akar pada pohon cabang sebelah kiri, yaitu 2, dicetak terlebih dahulu sebelum pohon cabangnya 4 dan 5. Sedangkan untuk cabang sebelah kanan, akarnya 3 akan dicetak sebelum 6. Penelusuran preorder dilakukan pada semua cabang pohon. Urutan penelusuran yang lainnya bisa dianalisis dengan cara yang sama.

BAB VII SORTING

Pengurutan data dalam struktur data sangat penting untuk data yang beripe data numerik ataupun karakter. Pengurutan dapat dilakukan secara ascending (urut naik) dan descending (urut turun) pengurutan (Sorting) adalah proses menyusun kembali data yang sebelumnya telah disusun dengan suatu pola tertentu, sehingga tersusun secara teratur menurut aturan tertentu. Contoh:

Data Acak Ascending Descending

: 5 6 8 1 3 25 10 : 1 3 5 6 8 10 25 : 25 10 8 6 5 3 1

A. BUBBLE SORT Metode sorting termudah Diberi nama Bubble karena proses pengurutan secara berangsur-angsur bergerak/berpindah ke posisinya yang tepat, seperti gelembung yang keluar dari sebuah gelas bersoda. Bubble Sort mengurutkan data dengan cara membandingkan elemen sekarang dengan elemen berikutnya.

B. EXCHANGE SORT Sangat mirip dengan Bubble Sort Banyak yang mengatakan Bubble Sort sama dengan Exchange Sort Pebedaan : dalam hal bagaimana membandingkan antar elemen-elemennya. Exchange sort membandingkan suatu elemen dengan elemen-elemen lainnya dalam array tersebut, dan melakukan pertukaran elemen jika perlu. Jadi ada elemen yang selalu menjadi elemen pusat (pivot). sedangkan Bubble sort akan membandingkan elemen pertama/terakhir dengan elemen

sebelumnya/sesudahnya, kemudian elemen tersebut itu akan menjadi pusat \ (pivot) untuk dibandingkan dengan elemen sebelumnya/sesudahnya lagi, begitu seterusnya.

C. INSERTION SORT

Mirip dengan cara orang mengurutkan kartu, selembar demi selembar kartu diambil dan disisipkan (insert) ke tempat yang seharusnya. Pengurutan dimulai dari data ke-2 sampai dengan data terakhir, jika ditemukan data yang lebih kecil, maka akan ditempatkan (diinsert) diposisi yang seharusnya. Pada penyisipan elemen, maka elemen-elemen lain akan bergeser ke belakang

D. SELECTION SORT Merupakan kombinasi antara sorting dan searchingUntuk setiap proses, akan dicari elemen-elemen yang belum diurutkan yang memiliki nilai terkecil atau terbesar akan dipertukarkan ke posisi yang tepat di dalam array.Misalnya untuk putaran pertama, akan dicari data dengan nilai terkecil dan data ini akan ditempatkan di indeks terkecil (data[0]), pada putaran kedua akan dicari data kedua terkecil, dan akan ditempatkan di indeks kedua (data[1]). Selama proses, pembandingan dan pengubahan hanya dilakukan pada indeks pembanding saja, pertukaran data secara fisik terjadi pada akhir proses. Perbandingan Tabel Perbandingan Kecepatan Metode Pengurutan Data Untuk data sejumlah 10.000 data pada komputer Pentium II 450 MHz

E. MASIH BANYAK LAGI

Merge Sort

Heap Sort

Quick Sort

You might also like