`xÅt{tÅ| STRUKTUR DATA

advertisement
`xÅt{tÅ|
STRUKTUR DATA
Dengan Bahasa C
EKO SEDIYONO
FAKULTAS TEKNOLOGI INFORMASI
UNIVERSITAS KRISTEN SATYA WACANA
SALATIGA
2007
DAFTAR ISI
I.
PENDAHULUAN
II.
TIPE DATA TERSTRUKTUR
III.
POINTER
IV.
LINKED LISTS
V.
TUMPUKAN (STACK)
VI.
REKURSI
VII. ANTRIAN (QUEUE)
VIII. POHON (TREE)
DAFTAR PUSTAKA
1
I. PENDAHULUAN
Diktat ini tidak membahas lagi bahasa pemrograman C, karena pembaca
dianggap sudah menguasai bahasa pemrograman C yang dipelajari pada matakuliah
Dasar-dasar pemrograman. Oleh karena itu sebelum tertinggal jauh dengan
pembahasan materi ini, maka diharapkan pembaca segera mempelajari dan menguasai
bahasa pemrograman C baik dengan menggunakan Turbo C, Borland C, maupun
Microsoft Visual C.
Menurut Wikipedia, struktur data adalah cara untuk menyimpan data di
komputer sehingga data tersebut dapat digunakan secara efisien. Dengan memilih
struktur data yang tepat akan berpengaruh pada penggunaan algoritma yang efisien
pula. Struktur data yang dirancang dengan baik juga akan memberikan peluang untuk
melakukan komputasi-komputasi yang rumit dengan menggunakan sumberdaya yang
sesedikit mungkin, sehingga menghemat waktu eksekusi dan memori. Contoh dari
masalah ini akan dibahas pada pembahasan tiap topik. Struktur data
diimplementasikan dengan menggunakan tipe data, referensi dan operasi-operasi yang
tersedia dalam bahasa pemrograman, yang dalam hal ini adalah bahasa C.
Tipe data dalam bahasa C terdiri dari tipe data dasar (int, char, float, double,
long int, dan lain-lain), dan tipe data terstruktur (struct, union, enum, static) yang akan
dibahas pada bab 2. Setiap variabel yang diberi tipe data tertentu memberikan
pengertian acuan (referensi) pada suatu tempat (alamat) di dalam memori. Dengan
model referensi tersebut maka user atau programmer tidak perlu repot menentukan
alamat memori untuk menempatkan data. Dengan menggunakan tipe data tertentu
pada tiap variabel maka pada saat kompilasi akan ditentukan alamat memori relatif
untuk tiap variabel tersebut (detil dari pemetaan alamat memori dipelajari pada
organisasi sistem komputer). Setiap operasi (fungsi) yang ada dalam bahasa
pemrograman juga memiliki tipe tertentu, seperti misalnya fungsi printf() memiliki
tipe void, div() memiliki tipe float, dan lain-lain. Setiap struktur data hanya cocok
digunakan untuk aplikasi tertentu, bahkan beberap struktur data khusus dibuat untuk
aplikasi tertentu. Contohnya struktur data B-tree hanya cocok digunakan untuk
basisdata, struktur data graph digunakan untuk merepresentasikan jaringan, dan lainlain.
Dalam merancang suatu aplikasi yang besar, struktur data menjadi bagian
utama yang harus diperhatikan. Kesalahan rancangan struktur data menyebabkan
kesulitan dalam merealisasikan rancangan dan menyebabkan kualitas dan unjuk kerja
aplikasi menjadi jelek. Tahap berikutnya, setelah rancangan struktur data adalah
algoritmanya. Dari penjelasan di atas dapat disimpulkan bahwa menguasai struktur
data adalah hal pokok supaya dapat menciptakan aplikasi yang baik dan efisien.
Dalam bahasa-bahasa pemrograman modern seperti C++, Java, dan .NET,
struktur data telah dikemas dalam bentuk pustaka standar (standard libraries). Dalam
bahasa C++ tersedia Standard Template Library, dalam Java tersedia Java API, dan
dalam Microsoft .NET tersedia .NET Framework.
1
II. TIPE DATA TERSTRUKTUR
Pada bab ini akan dibahas tipe-tipe data lanjut yang belum dibahas pada
matakuliah dasar-dasar pemrograman. Tipe-tipe berikut ini dapat digunakan pada
compiler C atau C++.
2.1. Structure (struct)
Tipe data struct adalah tipe data untuk menggabung tipe data – tipe data yang
kita inginkan. Misalnya kita ingin menyimpan data tentang senapan (magazines).
Informasi2 yang akan disimpan untuk tiap senapan adalah nama (name), ukuran
(magazinesize), dan kaliber (calibre). Maka cara membuatnya adalah sebagai berikut :
struct gun
{
char name[50];
int magazinesize;
float calibre;
};
struct gun koleksiku;
Kata-kata yang tercetak tebal adalah identifier yang kita buat sendiri. Dengan
menggunakan struct maka kita dapat menggabungkan beberapa variabel yang berbeda
tipe menjadi satu kesatuan (instant). Sekarang gun menjadi shortcut untuk
menyatakan beberapa variabel secara bersama-sama. Pada contoh ini variabel-variabel
yang digabung adalah nama, ukuran dan kaliber. Selanjutnya untuk mendefinisikan
variabel dengan gabungan tiga tipe tersebut maka dibuat perintah
struct gun koleksiku;
Supaya tidak mengulang definisi variabel koleksiku, maka dapat dibuat seperti contoh
di bawah ini.
struct gun
{
char name[50];
int magazinesize;
float calibre;
} koleksiku ;
Variabel dalam struct juga dapat langsung diinisialisasi, misalnya :
struct gun koleksiku={"Pistol",30,7};
menyatakan koleksiku sudah ada satu yaitu Pistol kaliber 7 dengan ukuran 30 mm.
Untuk mengakses anggota (atau field) dari struct, C menggunakan operator “.”
Misalnya ukuran diameter pelurunya yang semula 30 akan diganti dengan 100 maka
dapat dilakukan assignment hanya untuk magazinesize sebagai berikut :
koleksiku.magazineSize=100;
2
Mendefinisikan Tipe Data Baru
Untuk mendefinisikan tipe data baru dengan struct dapat digunakan typedef .
Dengan cara berikut ini maka tipesenapan dapat menjadi tipe data baru
typedef struct gun
{
char name[50];
int magazinesize;
float calibre;
} tipesenapan;
tipesenapan senapanku ={"Pistol",30,7};
Dalam hal ini gun hanya berfungsi sebagai shortcut (bukan tipe) dan bersifat opsional.
Setelah mendefinisikan tipe data baru tipesenapan, maka identifier gun boleh tidak
digunakan. Variabel senapanku adalah variabel yang memiliki tipe data tipesenapan
dan terdiri dari 3 field nama, ukuran dan kaliber.
Selanjutnya apabila koleksinya banyak, maka type struct dapat dikombinasi
dengan array. Misalnya kita akan menyimpan koleksi sampai dengan 1000 senapan,
maka struktur datanya dapat dibuat seperti berikut ini.
typedef struct gun
{
char name[50];
int magazinesize;
float calibre;
} tipesenapan;
tipesenapan senapanku[1000];
Untuk mengakses koleksi ke 50 dan menetapkan kalibernya dengan nilai 100, maka
caranya adalah :
senapanku [50].calibre =100;
Untuk mencatat (mengcopy) kaliber dari senapan pertama ke dalam suatu variabel
baru maka perlu diperhatikan tipe dari kaliber. Karena calibre bertipe float, maka
variabel baru misalnya kaliber1 harus bertipe float, sebagai berikut.
float kaliber1;
kaliber1 = senapanku[0].calibre;
Contoh berikut ini adalah perbandingan penggunaan struktur data yang tepat dan tidak
tepat. Dari contoh tersebut dapat dilihat kesulitan-kesulitan yang dihadapi pada saat
harus memanipulasi data.
3
Soal :
Ingin dibuat laporan penjualan secara berkelompok menurut kode barang. Informasi
yang diketahui untuk setiap item barang adalah kode barang (KodeBar), Nama barang
(NamaBar) dan jumlah barang yang terjual (Terjual).
Karena yang menjadi kelompok adalah kode barang yang bertipe string maka harus
dilakukan pengurutan (sorting) string. Kita tidak terlalu memperhatikan algoritma
sorting, oleh karena itu dipilih saja metode sorting yang sederhana yaitu selection sort.
Jawaban pertama menggunakan struktur data array of string, yaitu untuk kode barang
dan nama barang disediakan array 50 elemen dan masing-masing elemen bertipe
string 5 karakter dan string 10 karakter, dan variabel Terjual disediakan array 50
elemen masing-masing bertipe integer. Jawaban ke dua menggunakan struktur data
struct. Untuk data dengan beberapa field yang berbeda seperti ini lebih mudah
digunakan struktur data struct.
Jawaban model 1 (struktur data array of string)
Nama Program : jklp_arr.c
#include <stdio.h>
#include <conio.h>
#include <string.h>
char
char
int
int
KodeBar[50][5];
NamaBar[50][10];
Terjual[50];
n;
// Jumlah barang
char TukarKode[5];
char TukarNama[10];
int TukarJual;
//variabel pembantu untuk menukar KodeBarang
//variabel pembantu untuk menukar NamaBarang
//variabel pembantu untuk menukar Terjual
char Batas;
int SubTotal,Total;
//variabel pembatas sub total
void main()
{
int i,j;
clrscr;
printf("\nJumlah Barang
: "); scanf("%d", &n);
// Input n barang
for(i=0;i<n; i++)
{
printf("\nKode Barang
scanf("%s",&KodeBar[i]);
printf("\nNama Barang
scanf("%s",&NamaBar[i]);
printf("\nTerjual
scanf("%d",&Terjual[i]);
}
: ");
: ");
: ");
4
// Sorting
for (i=0;i< n-1;i++)
for(j=i+1;j<n; j++)
if (strcmp(KodeBar[i],KodeBar[j]) > 0) {
strcpy(TukarKode,KodeBar[i]);
strcpy(KodeBar[i],KodeBar[j]);
strcpy(KodeBar[j],TukarKode);
strcpy(TukarNama,NamaBar[i]);
strcpy(NamaBar[i],NamaBar[j]);
strcpy(NamaBar[j],TukarNama);
TukarJual = Terjual[i];
Terjual[i]=Terjual[j];
Terjual[j]=TukarJual;
}
;
printf("\nData yang urut");
for(i=0;i<n;i++)
printf("\n %4s %10s
%3d
",&KodeBar[i],&NamaBar[i],Terjual[i]);
printf("\nTekan Sembarang tombol");
getch();
// Laporan dengan subtotal kelompok
Batas=KodeBar[0][0];
// ambil huruf A atau B saja
SubTotal=Total=0;
printf("\n LAPORAN PER KELOMPOK");
printf("\nKodeBar
NamaBar
Terjual");
for (i=0;i<n;i++)
if (Batas != KodeBar[i][0]) {
printf("\n Sub Total --->
%3d",SubTotal);
SubTotal = 0;
Batas=KodeBar[i][0];
SubTotal += Terjual[i];
Total += Terjual[i];
printf("\n %4s
%10s
%3d",
&KodeBar[i],&NamaBar[i],Terjual[i]);
}else {
SubTotal += Terjual[i];
Total += Terjual[i];
printf("\n %4s
%10s
%3d",
&KodeBar[i],&NamaBar[i],Terjual[i]);
}
printf("\n Sub Total --->
%3d",SubTotal);
printf("\n Total ------->
%3d",Total);
}
Jika inputnya ada 5 barang berikut ini
B001
semen
30
A002
benih
20
A001
pupuk
50
B002
cat
40
A003
insekt
30
5
Maka hasilnya adalah:
Data yang urut
A001
pupuk
50
A002
benih
20
A003
insekt
30
B001
semen
30
B002
cat
40
Tekan sembarang tombol
LAPORAN PER KELOMPOK
KodeBar
NamaBar
Terjual
A001
pupuk
50
A002
benih
20
A003
insekt
30
Sub Total --->
100
B001
semen
30
B002
cat
40
Sub Total --->
70
Total ------->
170
Jawaban model 2 (struktur data struct)
Nama Program : jklp_str.c
#include <stdio.h>
#include <conio.h>
#include <string.h>
typedef struct item
{
char KodeBar[5];
char NamaBar[10];
int Terjual;
} itembarang;
//ini struktur datanya
itembarang Barang[50];
int n;
//ini variabel barang
//Jumlah barang
itembarang TukarBarang;
//variabel pembantu untuk menukar Barang
char Batas;
int SubTotal,Total;
//Variabel pembatas sub total
void main()
{
int i,j;
clrscr;
printf("\nJumlah Barang
: "); scanf("%d", &n);
// Input n barang
for(i=0;i<n; i++)
{
printf("\nKode Barang
: ");
scanf("%s",&Barang[i].KodeBar);
printf("\nNama Barang
: ");
6
scanf("%s",&Barang[i].NamaBar);
printf("\nTerjual
: ");
scanf("%d",&Barang[i].Terjual);
}
// Sorting
for (i=0;i< n-1;i++)
for(j=i+1;j<n; j++)
if (strcmp(Barang[i].KodeBar,Barang[j].KodeBar) >
0) {
TukarBarang = Barang[i];
Barang[i] = Barang[j];
Barang[j] = TukarBarang;
};
printf("\nData yang urut");
for(i=0;i<n;i++)
printf("\n %4s %10s
%3d
",
&Barang[i].KodeBar,&Barang[i].NamaBar,
Barang[i].Terjual);
printf("\nTekan Sembarang tombol");
getch();
// Laporan dengan subtotal kelompok
Batas=Barang[0].KodeBar[0]; //ambil karakter pertama dari kodebar
SubTotal=Total=0;
printf("\n LAPORAN PER KELOMPOK");
printf("\nKodeBar
NamaBar
Terjual");
for (i=0;i<n;i++)
if (Batas != Barang[i].KodeBar[0]) {
printf("\n Sub Total --->
%3d",
SubTotal);
SubTotal = 0;
Batas=Barang[i].KodeBar[0];
SubTotal += Barang[i].Terjual;
Total += Barang[i].Terjual;
printf("\n %4s
%10s
%3d",
&Barang[i].KodeBar,&Barang[i].NamaBar,
Barang[i].Terjual);
} else {
SubTotal += Barang[i].Terjual;
Total += Barang[i].Terjual;
printf("\n %4s
%10s
%3d",
&Barang[i].KodeBar,&Barang[i].NamaBar,
Barang[i].Terjual);
}
printf("\n Sub Total --->
%3d",SubTotal);
printf("\n Total ------->
%3d",Total);
}
Dengan menggunakan struktur data struct maka pada bagian sorting lebih sederhana
(lihat bagian yang tercetak tebal), tidak perlu menukarkan nilai untuk tiap field.
7
2.2. Unions
Union adalah tipe data untuk variabel yang dapat menyimpan obyek yang
berbeda ukuran dan tipe. Bahasa C menggunakan union untuk menggabung beberapa
tipe yang berbeda.
union number
{
short shortnumber;
long longnumber;
double floatnumber;
} anumber ;
Kata-kata yang tercetak tebal adalah identifier / variabel yang kita ciptakan
sendiri. number adalah shortcut untuk menggabungkan (nunion) shortnumber,
longnumber, dan floatnumber. anumber adalah obyek yang berisi tiga bilangan tadi.
Setiap anggota dari obyek anumber dapat diakses dengan operator “.” Misalnya nilai
salah satu anggota obyek ingin dicetak, maka perintahnya adalah sebagai berikut :
printf("%ld
\n",anumber.longnumber);
Union juga dapat digunakan untuk menggabungkan tipe struct buatan kita
sendiri. Contoh berikut ini adalah struktur data untuk menyatakan pesawat terbang
yang terdiri dari beberapa macam pesawat, dan untuk tiap pesawat memiliki satuan
ukuran yang berbeda. Untuk jenis pesawat jet ukuran satuannya adalah jumlah
penumpang. Untuk helikopter ukuran satuannya adalah kapasitas angkutnya, dan
untuk pesawat kargo ukuran satuannya adalah maksimum biaya angkutnya. Untuk
menyatakan hal tersebut dapat dibuat struktur data berikut ini.
typedef struct {
int maxpassengers;
} jet;
typedef struct {
int liftcapacity;
} helicopter;
typedef struct {
int maxpayload;
} cargoplane;
typedef
union {
jet jetu;
helicopter helicopteru;
cargoplane cargoplaneu;
} aircraft;
Perbedaan dan Persamaan Union dan Structure
Dari forum diskusi yang ada di internet diperoleh beberapa hal tentang
perbedaan dan persamaan antara Union dan Structure. Beberapa perbedaan tersebut
adalah :
8
1. union mengalokasikan memori sebesar memori maksimum yang diminta oleh field
anggotanya. Contohnya untuk struktur number di atas, memori yang dibutuhkan
adalah 8 byte karena field dengan alokasi memori terbesar adalah floadnumber
dengan tipe double. Structure mengalokasikan memori sebanyak jumlah byte dari
seluruh field anggotanya.
2. Dalam union, satu blok memori digunakan bersama oleh seluruh anggota dalam
union, sedangkan dalam structure masing-masing field anggota memiliki ruang
memori sendiri.
3. union menyimpan satu nilai aktual saja. Jika ada elemen baru disimpan sebelum
elemen pertama diambil, maka elemen pertama tersebut akan hilang tertimpa oleh
elemen baru. Oleh karena itu programmer perlu hati-hati dalam mengelola struktur
union. Dalam structure, karena masing-masing elemen anggota dialokasikan di
ruang memori yang berbeda, maka perubahan pada satu elemen tidak
mempengaruhi elemen lainnya.
Beberapa persamaan dari union dan structure adalah :
1. Keduanya menggunakan operator dot (.) untuk mengakses field anggotanya.
2. Keduanya menggunakan tanda { } untuk menyatakan obyek datanya. Keduanya
juga menggunakan nama shortcut dan inisialisasi eksplisit.
3. Keduanya dapat diketahui alokasi memori maksimumnya dalam byte dengan
menggunakan operator sizeof().
Berikut ini adalah contoh perbedaan dan persamaannya di dalam program.
Nama Program : unionstr.c (Jalan di Visual C++ 6.0)
#include <stdio.h>
#include <stdlib.h>
/* Deklarasi union */
union one{
char satu;
int dua;
float tiga;
};
/* Deklarasi structure */
struct two{
char satu;
int dua;
float tiga;
};
int main(void)
{
/* Menggunakan identifier shortcut untuk membuat struktur S dan union U. */
struct two S;
union one U;
/* Output ukuran obyek ke layar */
9
printf("%d adalah jumlah byte dari S, sebagai structure.\n",
sizeof(S));
printf("%d adalah jumlah byte dari U, sebagai union.\n\n",
sizeof(U));
/* Mengambil nilai untuk S dan U */
S.satu = 'A';
S.dua = 3645;
S.tiga = 678.32;
U.satu = 'A';
U.dua = 3645;
U.tiga = 678.32;
/* Masukannya ditampilkan di layar supaya mudah membandingkan*/
printf("Nilai 'A', 3645, dan 678.32 telah dimasukkan "
"ke anggota struktur S dan\n anggota union U \n\n");
/* Nilai yang diterima untuk setiap anggota S */
printf("%c sama dengan nilai S.satu\n", S.satu);
printf("%d sama dengan nilai S.dua\n", S.dua);
printf("%3.2f sama dengan nilai S.tiga\n\n", S.tiga);
/* Nilai yang diterima untuk setiap anggota U (Terkorupsi !!). */
printf("%c sama dengan nilai U.satu\n", U.satu);
puts("Oops! Tidak sama dengan nilai 'A' yang
dimasukkan!\n");
printf("%d sama dengan nilai U.dua\n", U.dua);
puts("Oops! Tidak sama dengan nilai 3645 yang
dimasukkan!\n");
printf("%3.2f sama dengan nilai U.tiga\n\n", U.tiga);
system("PAUSE");
return 0;
}
Outputnya adalah :
12 adalah jumlah byte dari S, sebagai structure.
4 adalah jumlah byte dari U, sebagai union.
Nilai ‘A’, 3645, dan 678.32 telah dimasukkan ke anggota S
Dan anggota union U
A sama dengan nilai S.satu
3645 sama dengan nilai S.dua
678.32 sama dengan nilai S.tiga
{ sama dengan nilai U.satu
Oops! Tidak sama dengan nilai ’A’ yang dimasukkan!
1143575675 sama dengan nilai U.dua
Oops! Tidak sama dengan nilai 3645 yang dimasukkan!
678.32 sama dengan nilai U.tiga
10
Pembahasan :
Struktur data struct mengalokasikan memori untuk setiap field anggotanya.
Nilai 12 diperoleh dari 1 untuk char, 4 untuk int, 4 untuk float, dan 3 sisanya untuk
menyimpan struktur yang beranggotakan 3 field.
Dalam struktur data union hanya dinyatakan 4, karena field terbesar adalah int atau
float dengan 4 byte .
Struktur data struct dapat menerima setiap masukan nilai anggotanya secara
sekuensial, tetapi union tidak. Setiap masukan nilai akan mengubah semua nilai
anggotanya, karena union hanya mengalokasikan satu ruang memori saja.
2.3. Coercion atau Type-Casting
Bahasa C adalah sedikit dari bahasa pemrograman yang menyediakan fasilitas
coersion, yaitu fasilitas untuk memaksa suatu variabel dengan tipe tertentu berubah
menjadi tipe lain pada saat program berjalan. Operator yang digunakan adalah ( ) di
depan variabel yang akan diubah tipenya.
Contoh :
int integernumber;
float floatnumber=9.87;
integernumber=(int)floatnumber;
Memaksa 9.87 menjadi bilangan bulat, yaitu dengan membuang .87 sehingga hasilnya
integernumber menyimpan nilai 9.
int integernumber=10;
float floatnumber;
floatnumber=(float)integernumber;
Memaksa bilangan 10 menjadi bilangan pecahan (float) sehingga floatnumber bernilai
10.0.
Coersion dapat juga digunakan untuk semua tipe data dasar termasuk char.
Sehingga apabila ada char di integerkan akan menjadi seperti berikut ini.
int integernumber;
char letter='A';
integernumber=(int)letter;
Huruf A memiliki kode ASCII 65, sehingga integernumber bernilai 65.
Beberapa type casting dilakukan secara otomatis, terutama untuk integer. Contohnya
dalam pembagian (/), secara otomatis bilangan-bilangan pembagi dan yang dibagi
diubah menjadi float. Tetapi ada aturan yang menganjurkan : “Bila ragu, maka
lakukan cast”.
Contoh :
floatnumber = (float) internumber/(float) anotherint;
11
Memastikan pembagian secara floating point. Hal ini dilakukan untuk membuat hasil
pembagian menjadi lebih teliti.
2.4. Tipe Enumerasi
Tipe enumerasi adalah daftar bilangan-bilangan konstan yang dapat diberi
alamat sebagai bilangan bulat (integer). Cara mendeklarasikannya adalah sebagai
berikut :
enum hari {senin, selasa, rabu, kamis, jum’at, sabtu,
minggu} seminggu;
enum hari minggu1, minggu2;
Kata-kata yang tercetak tebal adalah identifier atau nilai yang kita buat sendiri. Kata
hari adalah tag (shortcut) untuk menyatakan hari-hari dalam satu minggu. Kata-kata
seminggu, minggu1 dan minggu2 adalah variabel yang bernilai seperti array yang
dimulai dari 0. Jadi senin adalah 0, selasa 1, dan seterusnya.
Contoh lainnya, enum dapat digunakan untuk menyatakan karakter-karakter
control, seperti berikut ini.
enum escapes {bell = `\a',backspace =`\b',tab =`\t',
newline = `\n',vtab = `\v',return = `\r'};
Nilai default yang dimulai dengan 0 dapat diganti dengan cara seperti contoh berikut
ini.
enum months {jan = 1, feb, mar, ......, dec};
Hal ini berarti januari bernilai 1, februari bernilai 2 dan seterusnya.
2.5. Variabel Statik (Static Variables )
Variabel statik adalah variabel yang bersifat lokal untuk suatu fungsi, nilainya
akan diinisialisasi satu kali saja pada saat dipanggil pertama kali. Selain itu, pada saat
meninggalkan fungsi variabel tersebut mendapatkan nilai yang terakhir. Pada saat
pemanggilan fungsi berikutnya, maka nilai variabel statik tersebut menggunakan nilai
yang terbaru, bukan dimulai dari nilai awal.
Untuk mendefinisikan variabel statik tinggal menambahkan kata-kata static di
depan variabel yang akan kita buat sebagai variabel statik. Contohnya adalah sebagai
berikut :
Nama Program : static.c
#include <stdio.h>
void stat();
/* prototype fungsi */
void main()
{ int i;
for (i=0;i<5;++i)
12
stat();
}
void stat()
{
int auto_var = 0;
static int static_var = 0;
printf("auto = %d, static = %d \n", auto_var,
static_var);
++auto_var;
++static_var;
}
Output dari program di atas adalah :
auto_var
auto_var
auto_var
auto_var
auto_var
=
=
=
=
=
0,
0,
0,
0,
0,
static_var= 0
static_var = 1
static_var = 2
static_var = 3
static_var = 4
Jadi variabel auto_var dibuat setiap kali fungsi dipanggil, sedangkan variabel
static_var dibuat hanya sekali saja.
2.6. Soal Latihan
Latihan 1.
Tulislah program untuk menebak nama hari pada tahun 2007, apabila dalam program
diinputkan tanggal, bulan dan tahun. Diketahui Tanggal 1 Januari 2007 adalah hari
senin.
Latihan 2
Buatlah program untuk menyimpan database sederhana yang berisi nama, umur,
tanggal lahir, alamat, dan nomor telpon.
13
III. POINTER
Pointer digunakan di hampir semua aplikasi dengan menggunakan bahasa C.
Jika pembaca sudah mengenal pointer, maka tidak akan menghadapi masalah dalam
membangun aplikasi dengan bahasa C, tetapi kalau belum menguasai pointer maka
harus lebih banyak membaca buku tentang pointer, atau bahkan bertanya kepada
dosen atau orang lain untuk lebih memahaminya. Pada dasarnya pointer dalam bahasa
C hampir sama dengan pointer dalam bahasa Pascal, bahakan penggunaan pointer
dalam bahasa C lebih bebas dari pada dalam Pascal.
Bahasa C menggunakan pointer untuk tiga hal, yaitu : pertama untuk
membangun struktur data dinamik, yaitu struktur data yang dibangun pada saat
program berjalan dengan mengalokasikan satu blok memori. Pointer dalam Pascal
hanya digunakan untuk keperluan ini. Ke dua, pointer dalam bahasa C digunakan
untuk menangani parameter variabel yang dilewatkan pada fungsi. Ke tiga, pointer
dalam bahasa C digunakan untuk mengakses informasi yang disimpan dalam bentuk
array. Hal ke tiga ini terutama berguna pada saat menangani string. Dalam bahasa C
terdapat hubungan yang sangat erat antara pointer dan array.
Dalam banyak hal, penggunaan pointer membuat program menjadi lebih
efisien, tetapi disisi lain, penggunaan pointer dalam program juga menyebabkan
program tersebut sulit dipelajari. Dalam hal ini, kita tidak boleh tanggung-tanggung
menguasai pointer dari ketiga hal tersebut di atas, karena bahasa C digunakan secara
luas untuk keperluan praktis.
3.1. Dasar-dasar Penggunaan Pointer
Variabel biasa dalam bahasa C mengandung arti lokasi memori yang
digunakan untuk menyimpan suatu nilai. Misalnya dideklarasikan variabel i bertipe
integer, maka di memori disediakan ruang sebanyak 4 byte. Untuk menuju lokasi
tersebut dalam program harus menggunakan variabel i. Pada level bahasa mesin,
lokasi memori tersebut dinyatakan dengan alamat dari lokasi memori sebesar 4 byte
untuk integer.
Pointer adalah variabel yang menunjuk ke variabel lain. Hal ini berarti
variabel pointer memegang alamat memori dari variabel lain. Dengan kata lain,
variabel pointer tidak memegang nilai, tetapi memegang alamat dari variabel lain.
Dapat diumpamakan anda memegang uang kontan Rp. 100.000,- adalah variabel
biasa. Apabila anda membutuhkan nilai (uangnya) langsung ada. Sebaliknya pointer
dapat diumpamakan dengan anda memiliki uang Rp. 100.000,- tetapi disimpan oleh
Bank dalam bentuk tabungan. Anda mempunyai hak atas uang tersebut, tetapi yang
dipegang adalah nomor rekeningnya. Kalau ingin mengambil uang anda harus pergi
ke bank. Dalam hal ini anda adalah variabel pointer, dan Bank adalah variabel yang
alamatnya (nomor rekeningnya) anda pegang.
Karena pointer tidak memegang nilai tetapi memegang alamat, maka dalam
pointer terdapat dua bagian, yaitu pointer itu sendiri yang memegang alamat dan
alamat tersebut menunjuk ke suatu nilai. Jadi perlu dicatat mulai sekarang, supaya
tidak bingung, dalam pointer ada alamat dan nilai yang ditunjuk.
Berikut ini adalah contoh penggunaan pointer yang sederhana.
14
Nama Program : pointer1.c
#include <stdio.h>
void main()
{
int i,j;
int *p;
/* pointer ke integer */
p = &i;
*p=5;
j=i;
printf("%d %d %d\n",i,j,*p);
}
Baris int *p adalah mendeklarasikan sebuat variabel pointer yang bernama p.
Tanda (*) digunakan untuk menyatakan variabel pointer. Variabel pointer dapat
dibuat menunjuk ke tipe apa saja, baik tipe data dasar maupun tipe data buatan seperti
yang dibahas pada Bab 2.
Baris p = &i; adalah pemberian nilai (assignment) alamat variabel i kepada
variabel p. Dalam bahasa C , & disebut operator alamat. Pernyataan &i berarti adalah
“alamat memori dari variabel i”. Dengan demikian baris p = &i berarti p menunjuk ke
variabel i. Sebelum baris tersebut dieksekusi maka p bernilai acak yang tidak
diketahui alamatnya. Oleh karena itu kalau pointer (p) tidak diberi nilai akan
menyebabkan kesalahan segmentasi (segmentation fault).
Pernyataan p = &i menyebabkan satu lokasi memiliki dua nama yaitu i itu
sendiri dan *p. Dengan demikian jelas bahwa pointer p terdiri dari dua bagian
yaitu p adalah lokasi yang menyimpan alamat dan *p adalah lokasi yang
menyimpan nilai yang ditunjuk oleh p. Pernyataan *p = 5 berarti bahwa lokasi yang
ditunjuk oleh p diberi nilai 5. Karena lokasi tersebut juga bernama i, maka pernyataan
tersebut juga menyebabkan nilai menjadi 5. Dan selanjutnya j=i berarti j diberi nilai 5
juga. dan akhirnya perintah printf menghasilkan 5 5 5.
Nama Program : pointer2.c
#include <stdio.h>
void main()
{
int i,j;
int *p;
/* pointer ke integer */
printf("%d %d\n",p,&i);
p = &i;
printf("%d %d\n",p,&i);
}
Program tersebut memberitahukan kepada compiler untuk mencetak alamat
yang dipegang oleh p dan alamat dari i. Variabel p akan memberikan sembarang nilai
atau 0 dan &i (alamat i) bernilai besar sekali. Dengan demikian outputnya tidak tentu,
tetapi sebagai salah satu contohnya adalah sebagai berikut :
0 2147478276
2147478276 2147478276
15
Sekarang perhatikan program berikut ini. Apa yang akan dihasilkannya ?
Nama Program : pointer3.c
#include <stdio.h>
void main()
{
int *p;
/* a pointer to an integer */
printf("%d\n",*p);
}
Program tersebut memberitahukan kompiler untuk mencetak nilai yang ditunjuk oleh
p. Tetapi karena p belum diberi nilai awal, maka nilainya adalah alamat 0 dan
menghasilkan segmentation fault, karena menunjuk ke alamat yang tidak ada.
3.2. Penggunaan Pointer Sebagai Parameter
Kebanyakan programmer C menggunakan pointer untuk parameter fungsi.
Misalnya diketahui prosedur dalam Pascal untuk menukarkan dua nilai integer berikut
ini.
program samp;
var
a,b:integer;
procedure swap(var i,j:integer);
var t:integer;
begin
t:=i;
i:=j;
j:=t;
end;
begin
a:=5;
b:=10;
writeln(a,b);
swap(a,b);
writeln(a,b);
end.
Program Pascal menggunakan parameter variabel, sehingga pertukaran nilai
a dan b dapat dilakukan dengan tepat di dalam prosedur swap. Dalam bahasa C tidak
ada mekanisme yang formal untuk melewatkan nilai ke dalam fungsi. Semua
dilewatkan ke dalam fungsi melalui nilainya (passing by value). Hal ini dapat diuji
coba melalui program di bawah ini.
Nama program : param.c
#include <stdio.h>
16
void swap(int i, int j)
{
int t;
t=i;
i=j;
j=t;
}
void main()
{
int a,b;
a=5;
b=10;
printf("%d %d\n",a,b);
swap(a,b);
printf("%d %d\n",a,b);
}
Kalau program tersebut dijalankan maka tidak ada pertukaran nilai antara a
dan b. Nilai a dan b dibawa masuk ke dalam fungsi swap, tetapi tidak ada nilai yang
dikembalikan (return), maka di luar fungsi tidak terjadi perubahan apapun. Supaya
fungsi tersebut berjalan dengan benar maka harus digunakan pointer. Bandingkan
program di atas dengan program di bawah ini.
Nama program : paramptr.c
#include <stdio.h>
void swap(int *i, int *j)
{
int t;
t = *i;
*i = *j;
*j = t;
}
void main()
{
int a,b;
a=5;
b=10;
printf("%d %d\n",a,b);
swap(&a,&b);
printf("%d %d\n",a,b);
}
Kalau program tersebut dijalankan maka, perintah printf yang pertama akan
menghasilkan 5 10, karena a diberi nilai 5 dan b diberi nilai 10. Selanjutnya perintah
printf yang ke dua dieksekusi setelah fungsi swap. Fungsi swap menggunakan
parameter pointer *i dan *j yang merupakan nama lain dari a dan b. Di dalam fungsi
swap nilai dari *i ditukar dengan nilai dari *j, maka diluar dari fungsi swap
menyebabkan nilai a dan b tertukar, sehingga printf yang ke dua menghasilkan output
10 5.
17
Dari contoh ini kita menjadi tahu mengapa perintah scanf menjadi error bila
pada variabel parameternya tidak diawali dengan &. Dalam hal ini scanf
menggunakan pointer untuk variabel yang menyimpan hasil input dari keyboard.
Tanpa menggunakan tanda &, maka scanf akan mengembalikan alamat asal-asalan
sehingga program crash.
3.3. Penggunaan Pointer untuk Struktur Data Dinamik
Struktur data dinamik adalah struktur data yang dapat bertambah atau
berkurang sesuai dengan kebutuhan pada saat program berjalan. Struktur data dinamik
sangat penting dalam pemrograman C.
Struktur data dinamik dapat mengalokasikan blok-blok memori sesuai dengan
yang diinginkan dalam program. Masing-masing blok dirangkai (link) dengan
menggunakan struktur pointer. Bila suatu blok memori tidak digunakan lagi dalam
program maka blok memori tersebut dilepas supaya dapat digunakan untuk keperluan
lain. Dua contoh program berikut ini menunjukkan perbedaan pengelolaan memori
(heap) dari Pascal dan C.
Contoh pertama adalah mengalokasikan satu blok memori untuk integer,
mengisi nilai, menulis output di layar dan menghilangkan blok memori tersebut yang
dibuat dalam Pascal dan C. Program Pascalnya adalah sebagai berikut :
program samp;
var
p:^integer;
begin
new(p);
p^:=10;
writeln(p^);
dispose(p);
end.
Kode yang sama dalam bahasa C adalah sebagai berikut :
Nama program : dinamik.c
#include <stdio.h>
#include <stdlib.h>
void main()
{
int *p;
p=(int *) malloc (sizeof(int));
*p=10;
printf("%d\n",*p);
free(p);
}
Kedua program tersebut di atas mendemonstrasikan alokasi, dealokasi dan
menggunakan blok memori. Perintah malloc dalam C sama dengan perintah new
dalam Pascal, yaitu mengalokasikan blok memori sebesar yang diinginkan yaitu
sebesar sizeof(int) byte. Karena int bernilai 4 byte maka perintah tersebut
mengalokasikan 4 byte.
18
Fungsi malloc mengembalikan pointer ke blok yang dialokasikan. Karena yang
digunakan adalah pointer generik, maka menggunakan pointer tanpa type casting akan
memunculkan peringatan kesalahan tipe (type warning) dari compiler. Type cast (int
*) mengubah pointer generik yang dihasilkan oleh malloc menjadi pointer ke integer,
seperti yang diharapkan dalam p. Perintah dispose dalam Pascal sama dengan perintah
free dalam C, yaitu melepaskan blok memori yang tidak digunakan.
Contoh ke dua menggambarkan fungsi yang sama dengan contoh pertama, tetapi
dalam contoh ke dua menggunakan record. Dalam Pascal direalisasikan sebagai
berikut :
program samp;
type
rec=record
i:integer;
f:real;
c:char;
end;
var
p:^rec;
begin
new(p);
p^.i:=10;
p^.f:=3.14;
p^.c='a';
writeln(p^.i,p^.f,p^.c);
dispose(p);
end.
Dalam bahasa C programnya adalah sebagai berikut :
Nama program : dinamik1.c
#include <stdio.h>
#include <stdlib.h>
struct rec
{
int i;
float f;
char c;
};
void main()
{
struct rec *p;
p=(struct rec *) malloc (sizeof(struct rec));
(*p).i=10;
(*p).f=3.14;
(*p).c='a';
printf("%d %f %c\n",(*p).i,(*p).f,(*p).c);
free(p);
}
Perhatikan baris (*p).i=10; mengapa tidak menggunakan *p.i=10; saja ?
19
Seperti dalam operator (tanda operasi) pada umumnya, masing-masing memiliki
derajat (precedence). Dalam bahasa C, tanda “.” memiliki derajat lebih tinggi dari
pada tanda “*”. Oleh karena itu maka *p harus berada di dalam tanda kurung.
Banyak orang yang malas menulis (*p).i, maka dalam C notasi tersebut dapat
diganti dengan p-> = 10.
(*p).i=10;
p->i=10;
Keduanya adalah sama, tetapi baris ke dua lebih mudah mengetiknya.
Penggunaan struktur data dinamis yang lebih kompleks akan dibahas di bab 5
mengenai stack (tumpukan).
3.4. Penggunaan Pointer Bersama dengan Array
Array dan pointer memiliki hubungan yang sangat erat dalam bahasa C.
Supaya dapat mengunakan array secara efektif maka anda perlu mengerti penggunaan
pointer bersama array. Penjelasan dimulai dengan mengenal array dalam Pascal atau
bahasa lainnya. Dalam bahasa C konsep array sangat berbeda, oleh karena itu contoh
di bawah ini sangat membantu pemahaman.
Berikut ini adalah contoh penggunaan array dalam Pascal.
program samp;
const
max=9;
var
a,b:array[0..max] of integer;
i:integer;
begin
for i:=0 to max do
a[i]:=i;
b:=a;
end.
Elemen-elemen dalam array a diberi nilai dari 0 sampai dengan max=9, kemudian
semua isi elemen a dikopi ke b, sehingga a dan b sama. Sekarang bandingkan dengan
konsep array dalam C melalui contoh berikut ini.
#define MAX 10
void main()
{
int a[MAX];
int b[MAX];
int i;
for(i=0; i<MAX; i++)
a[i]=i;
b=a;
}
20
Jika program C di atas dikompilasi maka akan terjadi kesalahan, karena dalam C tidak
dikenal array assignment b = a; seperti dalam program di atas. Jika ingin mengkopi isi
array a ke b maka harus dilakukan dengan cara berikut ini.
for (i=0; i<MAX; i++)
a[i]=b[i];
atau cara yang lebih ringkas adalah seperti berikut ini
for (i=0; i<MAX; a[i]=b[i], i++);
Array dalam bahasa C bukanlah array seperti dalam Pascal atau bahasa lainnya,
tetapi pointer permanen ke array. Dengan kata lain array dalam C adalah pointer ke
blok memori yang menyimpan elemen-elemen array. Variabel array menyimpan
alamat dari array yang sesungguhnya, tetapi karena pointernya permanen maka kita
tidak bisa mengubah alamatnya. Dengan demikian pernyataan a = b; tidak benar.
Beberapa keuntungan lain dalam penggunaan array dapat dilihat pada contoh
berikut ini :
Nama program : pointarr.c
#include <stdio.h>
#define MAX 10
void main()
{
int a[MAX];
int b[MAX];
int i;
int *p,*q;
for(i=0; i<MAX; i++);
a[i]=i;
p=a;
printf("%d\n",*p);
}
Perintah p = a; dalam program di atas dapat berjalan karena a adalah pointer.
Secara teknis a menunjuk ke elemen ke 0 dari array yang sesungguhnya. Elemen ke
0adalah integer, maka a adalah pointer ke integer tunggal. Oleh karena itu dengan
mendeklarasikan p sebagai pointer ke integer dan mengisinya dengan alamat a ( p = a)
program berjalan dengan baik. Cara yang sama untuk menyatakan p = a; adalah
dengan p = &a[0]; karena a berisi alamat a[0] yang dapat dinyatakan dengan &a[0].
Sekarang p menunjuk ke elemen ke 0 dari array a, demikian juga dengan a.
Perbedaannya adalah a adalah pointer permanen, sedangkan p bukan. Dengan
demikian kita dapat melakukan manipulasi terhadap pointer p. Contohnya: dapat
dibuat perintah p++; Compiler C tahu bahwa p menunjuk ke integer, maka operasi
menambah p dengan 1 (increment) artinya adalah alamat yang dipegang oleh p
ditambah dengan sejumlah byte yang sesuai untuk menunjuk ke elemen berikutnya.
Dalam hal ini karena p menunjuk integer (4 byte) maka dengan p++; maka p akan
menunjuk ke alamat 4 byte berikutnya atau ke elemen berikutnya. (Ingat ! yang diincrement bukan nilainya, tetapi alamat yang dipegang oleh pointer p).
21
Kita juga dapat mengkopi array a ke array b dengan menggunakan pointer.
Program berikut ini dapat menggantikan perintah (for i=0; i<MAX; a[i]=b[i], i++); :
p=a;
q=b;
for (i=0; i<MAX; i++)
{
*q = *p;
q++;
p++;
}
atau dengan meringkas kode menjadi seperti berikut ini.
p=a;
q=b;
for (i=0; i<MAX; i++)
*q++ = *p++;
atau dapat diringkas lagi menjadi
for (p=a,q=b,i=0; i<MAX; *q++ = *p++, i++);
Apa yang terjadi kalau kita melewati batas atas/bawah dari array a atau b dengan
menggunakan pointer p atau q ? Bahasa C tidak memperdulikannya, kalau p dan q
ditambah terus maka apa saja yang ada di alamat tersebut akan dikopi.
Kita dapat melewatkan array ke dalam fungsi dengan dua cara. Contoh berikut
ini adalah fungsi dump yang menerima array integer sebagai parameter dan
menampilkan isinya dengan perintah printf. Cara pertama adalah sebagai berikut :
void dump(int a[],int nia)
{
int i;
for (i=0; i<nia; i++)
printf("%d\n",a[i]);
}
atau dengan cara ke dua :
void dump(int *p,int nia)
{
int i;
for (i=0; i<nia; i++)
printf("%d\n",*p++);
}
Variabel nia digunakan untuk menyatakan jumlah elemen array. Jadi yang dilewatkan
sebagai parameter adalah pointer ke array, bukan isinya. Demikian juga fungsi C
dapat menerima ukuran elemen array lewat parameter. Hal ini tidak mungkin
dilakukan dalam Pascal.
22
IV. LINKED LIST
Linked list hampir sama dengan array, yaitu sama-sama menyimpan
sekumpulan data. Perbedaannya adalah dalam mengalokasikan memori. Array
mengalokasikan memori untuk semua elemen dalam satu blok memori, sedangkan
linked list mengalokasikan memori elemen per elemen pada saat program berjalan,
sehingga tempatnya di memori juga acak. Dalam linked list, tiap elemen disebut
“node”. Walaupun tempatnya terpencar tetapi tiap node dirangkai dengan
menggunakan pointer.
Dalam tiap node terdiri dari dua bagian yaitu : field-field “data” untuk
menyimpan informasi apa saja dengan tipe data yang diketahui, dan bagian ke dua
adalah satu field “next” yang berisi pointer ke node berikutnya. Setiap node
dialokasikan dalam memori (heap) dengan memanggil fungsi malloc(), dan
didealokasi (dibuang dari memori) dengan memanggil fungsi free(). Kedua fungsi
tersebut terdapat pada header <stdlib.h>. Kepala dari list tersebut adalah pointer ke
node pertama. Untuk lebih jelasnya lihat Gambar 4.1 yang menggambarkan linked list
dengan 3 node masing-masing berisi integer 1,2, dan 3.
Heap
Stack
BuildStack()
head
3
2
1
Gambar 4.1. Gambaran Linked List dari Sisi Pengguna (Stack) dan di Sisi Memori
(Heap)
Gambar 4.1. bagian kiri menggambarkan variabel dari sisi aplikasi. Variabel
head adalah variabel adalah variabel lokal di dalam fungsi BuildStack() yang bertipe
linked list yang menunjuk ke node pertama. Gambar 4.1. bagian kanan
menggambarkan susunannya di dalam memori (heap). Node pertama terdiri dari field
data yang berisi nilai 1 dan field next yang berisi pointer ke node berikutnya. Node
terakhir berisi field data yang berisi nilai 3 dan field next diisi NULL, yang perarti
tidak menunjuk kemanapun. Programnya dapat dilihat di bawah ini.
Nama program : llist1.c
#include <stdio.h>
#include <stdlib.h>
struct node {
int
data;
struct node* next;
};
struct node* BuildStack() {
struct node* head = NULL;
struct node* second = NULL;
struct node* third = NULL;
23
head= (struct node*) malloc(sizeof(struct node));
second= (struct node*) malloc(sizeof(struct node));
third= (struct node*) malloc(sizeof(struct node));
head->data = 1;
head->next=second;
second->data = 2;
second->next=third;
third->data = 3;
third->next = NULL;
return head;
}
void main()
{
struct node* gelang;
gelang=BuildStack();
printf("\nData node 1 = %d",gelang->data);
printf("\nData node 2 = %d",gelang->next->data);
printf("\nData node 3 = %d",gelang->next->next->data);
free(gelang);
}
Pada program di atas struktur datanya bernama node dengan 2 field yaitu data
bertipe integer. Informasi-informasi lain dapat dimasukkan di sini dengan field data
dengan tipe-tipe yang diketahui. Field berikutnya adalah next bertipe node itu sendiri.
Jadi dalam hal ini next menunjuk node berikutnya yang bertipe node juga.
BuildStack() adalah fungsi untuk membangun stack seperti yang digambarkan
pada Gambar 4.1., yang terdiri dari node 1 (head), node 2 (second), dan node 3 (third).
Masing-masing node dialokasikan memori sebesar byte yang dibutuhkan oleh struct
node, dengan menggunakan fungsi malloc(). Di depan fungsi malloc dibuat type
casting (struct node*) supaya nilai kembaliannya bertipe sama dengan penerimanya.
Kalau tidakdi casting maka nilai kembalian dari malloc adalah void * (pointer ke
void). Selanjutnya setiap node diisi nilai. Untuk node pertama field data diisi 1 dan
field next diisi node berikutnya (second), node ke dua field datanya diisi 2 dan field
next nya diisi third, dan node ke tiga field data diisi 3 dan field next nya diisi NULL,
karena node terakhir tidak menunjuk kemanapun. Nilai kembalian dari fungsi
BuildStack() adalah head yang bertipe (struct node*).
Di dalam program utama berisi pemanggilan fungsi BuildStack(), yang nilai
kembaliannya diterima oleh variabel gelang. Selanjutnya untuk mencetak nilai data
tiap node, secara sederhana dilakukan dengan menuliskan gelang->next->... sebagai
parameter fungsi printf().
Program tersebut di atas dapat dikembangkan, misalnya dengan membuat
fungsi untuk menghitung jumlah node yang ada. Fungsinya adalah sebagai berikut.
int length(struct node* head)
{
struct node* current = head;
24
int count = 0;
while (current != NULL) {
count++;
current =current->next;
}
return count;
}
Dalam main program dapat ditambahkan perintah untuk mencetak hasil pemanggilan
fungsi length() tersebut di atas sebagai berikut.
printf("\nJumlah nodenya = %d",length(gelang));
length(gelang) adalah memanggil fungsi length dengan melewatkan variabel gelang
sebagai parameter. Variabel gelang, tidak lain adalah variabel yang telah berisi linked
list 3 node tadi. Nilai kembalian dari pemanggilan fungsi length() diterima sebagai
parameter fungsi printf(), maka hasilnya akan tercetak
Jumlah nodenya = 3
Dari fungsi length() kita dapat belajar dua hal yaitu :
1. Melewatkan list dengan menggunakan pointer head.
Linked list dilewatkan ke dalam fungsi length() dengan menggunakan satu pointer
head. Pointernya dikopi dari pemanggil (gelang) ke head. Pengkopian ini tidak
menyebabkan seluruh node terkopi tetapi pointer gelang dan pointer head
menunjuk ke alamat yang sama.
2. Pengulangan (iterasi) list dengan pointer lokal.
Dengan perintah current = current->next yang diulang-ulang dalam while-loop
maka variabel current akan menunjuk ke setiap node dari awal sampai akhir.
Kembali ke fungsi BuildStack() di atas. Cara pengisian node yang ada di
dalam fungsi tersebut tidak baik. Cara yang baik adalah dengan menggunakan fungsi,
misalnya fungsi push() yang di dalamnya berisi perintah-perintah menciptakan node
baru, memasukkan data yang ada di parameter, dan menyambung node ke dalam node
yang sudah ada. Program di atas dimodifikasi dengan menambahkan fungsi push().
Nama program : llist2.c
#include <stdio.h>
#include <stdlib.h>
struct node {
int
data;
struct node* next;
};
void push(struct node** headRef, int data) {
struct node*NewNode=(struct node*)malloc(sizeof(
struct node));
NewNode->data = data;
25
NewNode->next = *headRef;//'*' adalah ref ulang ke head
*headRef = NewNode;
}
struct node* BuildStack() {
struct node* head = NULL;
struct node* second = NULL;
struct node* third = NULL;
int i;
head=
(struct node*) malloc(sizeof(struct node));
head->data = 0;
head->next=second;
for (i=1; i<10; i++)
push(&head, i);
return head;
}
int length(struct node* head)
{
struct node* current = head;
int count = 0;
while (current != NULL) {
count++;
current =current->next;
}
return count;
}
void main()
{
struct node* gelang;
int i=1;
gelang=BuildStack();
printf("\nJumlah nodenya = %d",length(gelang));
while(gelang != NULL) {
printf("\nData node %d = %d",i,gelang->data);
gelang=gelang->next;
i++;
}
free(gelang);
}
Output program ini adalah :
Jumlah nodenya = 10
Data node 1 = 9
Data node 2 = 8
Data node 3 = 7
Data node 4 = 6
Data node 5 = 5
26
Data
Data
Data
Data
Data
node
node
node
node
node
6 = 4
7 = 3
8 = 2
9 = 1
10 = 0
Soal latihan :
Program BuildStack() di atas adalah program untuk membangun linked list dengan
menambahkan node di depan. Sekarang buatlah program untuk membangun list
{1,2,3,4,5} dengan menambah node di belakang. Skema linked list nya adalah
sebagai berikut .
Heap
Stack
head
1
2
tail
NewNode
3
27
V. TUMPUKAN (STACK)
5.1. Dasar-Dasar Stack
Stack adalah struktur data yang mengikuti aturan Last In First Out (LIFO),
yaitu data yang masuknya terakhir harus keluar terlebih dahulu. Mirip seperti bola
tenis yang berada di dalam tempatnya. Apabila ingin mengambil bola yang paling
dalam harus mengeluarkan beberapa bola di atasnya. Prinsip ini digunakan di hampir
semua sistem komputer modern. Contohnya adalah dalam pemanggilan beberapa
fungsi dalam program. Program memanggil fungsi satu, di dalam fungsi satu
memanggil fungsi dua, di dalam fungsi dua memanggil fungsi tiga. Di dalam memori,
fungsi satu akan disimpan dulu dalam stack, fungsi dua juga disimpan di dalam stack.
Setelah fungsi tiga selesai dikerjakan, maka proses akan kembali ke fungsi dua dulu,
setelah fungsi dua selesai baru kembali ke fungsi satu, dan akhirnya kembali ke
program utama. Mekanisme tersebut digambarkan pada Gambar 5.1.
main()
{
…..
call func1()
…..
}
func1()
{
……
call func2()
…..
}
stack
top
func2()
func2()
{
……
call func3()
…..
}
func3 ()
{
……
……
…..
}
push pop
func1()
bottom
main()
Gambar 5.1. Mekanisme Pemanggilan Fungsi dan Stack
Pada saat fungsi func1() dijalankan maka main() disimpan di stack yang paling
bawah. Demikian juga pada saat fungsi func1() memanggil func2(), maka func1()
disimpan di stack, dan pada saat func2() memanggil func3(), func2() disimpan di
stack. Tahap penyimpanan di dalam stack disebut PUSH. Setelah func3() selesai
mengerjakan proses, maka kembalinya sesuai dengan posisinya dalam stack, yaitu
func2() diselesaikan, kembali ke func1(). Fungsi func1() selesai, kembali ke main().
Proses pengambilan isi stack disebut POP.
28
Struktur data stack dapat diimplementasikan menggunakan array maupun
linked list. Dalam array, apabila proses PUSH menggunakan indek dari kecil ke besar,
maka proses POP nya dari indek besar ke kecil, atau sebaliknya. Dalam linked list,
bila PUSH nya dengan menambah node di depan (dekat dengan head), maka proses
POP nya membuang node dari depan ke belakang.
Berikut ini adalah contoh program membuat stack dengan menggunakan
linked list yang memasukkan karakter ‘I’, ‘D’, ‘E’ dan tercetak ‘E’, ‘D’, ‘I’, sesuai
dengan prinsip LIFO.
Nama program : stack.c
/*Tumpukan (Stack).Uji tumpukan kosong,
Cipta tumpukan, push, pop dan cetak tumpukan */
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
typedef enum { FALSE = 0, TRUE = 1} BOOL;
// Deklarasi Node
struct nod {
char data;
struct nod *next;
};
typedef struct nod NOD;
// Uji Tumpukan Kosong
BOOL TumpukanKosong(NOD *T)
{
return ((BOOL)(T == NULL));
}
//Cipta Node Baru
NOD *NodBaru (char item)
{
NOD *n;
n = (NOD*) malloc(sizeof(NOD));
if(n != NULL) {
n->data = item;
n->next = NULL;
}
return n;
}
void CiptaTumpukan (NOD **T)
{
*T = NULL; // Buat Tumpukan Kosong
}
29
// Push
void Push(NOD **T, char item)
{
NOD *n;
n = NodBaru(item);
n->next = *T;
*T = n;
}
// Pop
char Pop(NOD **T)
{
NOD *P; char item;
if ( ! TumpukanKosong(*T)) {
P = *T;
*T = (*T)->next;
item = P->data;
free(P);
}
return item;
}
void CetakTumpukan (NOD *T)
{
NOD *p;
printf("T --> ");
for (p = T; p != NULL; p = p->next) {
printf("[%c] --> ", p->data); }
printf("NULL\n");
}
int main()
{
NOD *T;
CiptaTumpukan(&T);
Push(&T, 'I');
Push(&T, 'D');
Push(&T, 'E');
CetakTumpukan(T);
return 0;
}
Output program tersebut di atas adalah :
T --> [E] --> [D] --> [I] --> NULL
30
5.2. Aplikasi Stack
Evaluasi dan Konversi Infix – Prefix – Postfix
Dalam aritmatika sehari-hari operasi tambah (+), kurang (-), kali (x) dan bagi
(:) ditulis dengan operator terletak di tengah diantara operan-operan. Contohnya :
A + B, A – B, A x B, atau A : B
Dalam bahasa pemrograman operator tersebut dinotasikan secara berturut-turut : +, - ,
*, dan /. Masing-masing operator memiliki tingkat kepentingan (precedence) sendiri.
Operator * dan / memiliki tingkat yang sama, tetapi lebih tinggi dari pada + dan /. Hal
ini berarti, dalam operasi aritmatika yang melibatkan banyak operator, maka operatoroperator * atau / akan didahulukan evaluasinya dari pada + atau -. Contohnya :
3+4*2
hasilnya bukan 24, tetapi 11, karena operator * dievaluasi pertama, baru kemudian
operator + dievaluasi. Operasi aritmatika yang operatornya terletak di tengah disebut
infix.
Bentuk operasi lain yang banyak digunakan dalam sistem komputer adalah
prefix, yaitu operatornya terletak di depan, dan postfix yang operatornya terletak di
belakang. Beberapa contoh konversi infix, prefix dan postfix dapat dilihat pada tabel
di bawah ini.
Tabel 5.1. Konversi Infix, Prefix, Postfix
infix
A+B
A+B-C
(A + B) * (C – D)
B * C – D + E/F/(G+H)
A-B/(C*D)
prefix
+AB
- + ABC
*+AB-CD
-*BC+D//EF+GH
-A/B*CD
postfix
AB+
AB+C AB+CD-*
BC*D-EFGH+//+
ABCD*/-
Agar tahap-tahap konversinya tidak salah, maka dapat digunakan bantuan
tanda ( ). Contohnya : ingin mengubah operasi (A+B) * (C-D) menjadi operasi prefix.
Operasi yang akan dievaluasi pertama kali adalah (A+B), dan bentuk prefixnya adalah
(+AB). Langkah berikutnya adalah mengevaluasi (C-D), dan bentuk prefixnya adalah
(-CD). Terakhir adalah mengevaluasi (+AB) * (-CD), dan bentuk prefixnya adalah
*(+AB)(-CD). Kalau tanda kurung dibuang akan menjadi *+AB-CD. Demikian juga
cara mengubah nya menjadi operasi postfix. Pertama kali yang akan dievaluasi adalah
(A+B), dan bentuk postfixnya adalah (AB+). Kemudian operasi (C-D), bentuk
postfixnya adalah (CD-). Langkah selanjutnya adalah mengevaluasi (AB+) * (CD-),
yang bentuk postfixnya adalah (AB+)(CD-)*. Kalau dibuang tanda kurungnya akan
menjadi AB+CD-*.
Proses mengevaluasi (menghitung nilai) dan mengkonversi prefix dan postfix
menggunakan stack. Pada bagian ini akan dibahas tentang mengevaluasi dan
mengkonversi operasi postfix, sedangkan untuk operasi prefix dapat digunakan
sebagai latihan. Proses evaluasi dilakukan dengan cara membaca satu-persatu karakter
input. jika karakter tersebut bilangan maka dianggap sebagai operand dan dimasukkan
ke dalam stack (push), tetapi kalau karakter input yang dibaca adalah tanda atau
operator, maka ambil dua operand teratas (pop dua kali) dan lakukan evaluasi.
31
Hasilnya dikembalikan ke dalam stack (push) untuk dievaluasi dengan operator
berikutnya. Algoritmanya dapat dilihat di bawah ini.
opndstk = stack kosong;
/* baca input string karakter demi karakter
dan disimpan di variabel symb */
while (not end of input) {
symb = karakter input berikutnya;
if (symb adalah operand)
push(opndstk,symb);
else {
/* symb adalah operator */
opnd2 = pop(opndstk);
opnd1 = pop(opndstk);
value = hasil operasi opnd1, opnd2 dengan symb;
push(opndstk, value);
} /* end else */
} /* end while */
return(pop(opndstk));
Perhatikan contoh evaluasi postfix dari string berikut ini
623+-382/+*
Langkah demi langkah prosesnya dapat dilihat pada tabel di bawah ini
Tabel 5.2. Proses Evaluasi Postfix
symb
6
2
3
+
3
8
2
/
+
*
opnd1
opnd2
value
2
6
3
5
5
1
8
3
1
2
4
7
4
7
7
opndstk
6
6,2
6,2,3
6,5
1
1,3
1,3,8
1,3,8,2
1,3,4
1,7
7
Setiap operand (bilangan) yang terbaca dimasukkan ke stack (opndstk). Dalam hal ini
stack nya tumbuh ke kanan, maka top stack adalah karakter yang paling kanan pada
kolom opndstk. Isi stack yang terakhir adalah 7. Inilah hasil evaluasi akhir.
Program untuk mengevaluasi operasi postfix adalah sebagai berikut.
Nama program : postfix.c
/*Evaluasi operasi postfix menggunakan Stack */
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <ctype.h>
32
#define MAXCOLS 80
typedef enum { FALSE = 0, TRUE = 1} BOOL;
// Deklarasi Node
struct nod {
float data;
struct nod *next;
};
typedef struct nod NOD;
// Uji Tumpukan Kosong
BOOL TumpukanKosong(NOD *T)
{
return ((BOOL)(T == NULL));
}
//Cipta Node Baru
NOD *NodBaru (float item)
{
NOD *n;
n = (NOD*) malloc(sizeof(NOD));
if(n != NULL) {
n->data = item;
n->next = NULL;
}
return n;
}
void CiptaTumpukan (NOD **T)
{
*T = NULL; // Buat Tumpukan Kosong
}
// Push
void Push(NOD **T, float item)
{
NOD *n;
n = NodBaru(item);
n->next = *T;
*T = n;
}
// Pop
float Pop(NOD **T)
{
NOD *P; float item;
if ( ! TumpukanKosong(*T)) {
P = *T;
*T = (*T)->next;
33
item = P->data;
free(P);
}
return item;
}
void CetakTumpukan (NOD *T)
{
NOD *p;
printf("T --> ");
for (p = T; p != NULL; p = p->next) {
printf("[%c] --> ", p->data); }
printf("NULL\n");
}
/* fungsi evaluasi operasi */
float oper(symb,op1,op2)
int symb;
float op1,op2;
{
switch (symb) {
case '+' : return (float)(op1
case '-' : return (float)(op1
case '*' : return (float)(op1
case '/' : return (float)(op1
default : printf("\n Operasi
} /* end switch */
} /* end oper */
+ op2);
- op2);
* op2);
/ op2);
salah"); exit(1);
/* Fungsi evaluasi Postfix */
float eval(expr)
char expr[];
{
int c, position;
float opnd1,opnd2, value;
NOD *opndstk;
for(position=0; (c = expr[position]) != '\0'; position++)
if (isdigit(c))
/* operand -- koonversi ke float dan push */
Push(&opndstk, (float) (c-'0'));
else {
/* operator */
opnd2 = (float) Pop (&opndstk);
opnd1 = (float) Pop (&opndstk);
value = oper(c,opnd1,opnd2);
Push (&opndstk, value);
} /* end else */
return (float) (Pop(&opndstk));
} /* end eval */
int main()
{
34
char expr[MAXCOLS]; /* string input */
int position = 0;
/*baca string input */
while((expr[position++]=getchar())!='\n');
expr[--position] = '\0'; /* beri pembatas string */
printf("\nString yang akan dievaluasi adalah: %s", expr);
printf("\nHasilnya : %f\n",eval(expr));
return 0;
}
Dengan memasukkan string 623+-382/+* pada program di atas, maka output yang
dihasilkan adalah
String yang akan dievaluasi adalah: 623+-382/+*
Hasilnya : 7.00000
Proses konversi operasi infix ke postfix menggunakan algoritma berikut ini.
opstk = stack kosong;
while (not end of input) {
symb = input karakter berikutnya;
if (symb adalah operand)
tambahkan symb ke string postfix
else {
while (!empty(opstk) && prcd(stacktop(opstk),symb)){
topsymb=pop(opstk);
tambahkan topsymb ke string postfix;
} /* end while */
} /* end else */
} /* end while */
/* tampilkan oprator sisanya */
while (!empty(opstk)) {
topsymb = pop(opstk);
tambahkan topsymb ke string postfix;
} /* end while */
Contoh 1
Diketahui string operasi infix A+B*C, maka hasil konversi langkah-demi langkahnya pada
variabel symb, opstk dan string postfixnya adalah sebagai berikut.
Tabel 5.3. Langkah-langkah Konversi Infix – Postfix
No.
1
2
3
4
5
6
7
symb
A
+
B
*
C
String postfix
A
A
AB
AB
ABC
ABC*
ABC*+
opstk
+
+
+*
+*
+
Contoh 2
Sekarang jika yang diketahui adalah string infix (A+B)*C, maka hasil konversinya akan
berbeda. Langkah demi langkah prosesnya dapat dilihat pada tabel di bawah ini.
35
Tabel 5.3. Langkah-langkah Konversi Infix – Postfix
No.
1
2
3
4
5
6
7
8
symb
(
A
+
B
)
*
C
String postfix
A
A
AB
AB+
AB+
AB+C
AB+C*
opstk
(
(
(+
(+
*
*
Untuk menyelesaikan masalah tersebut di atas dapat dibuat program seperti di
bawah ini.
Nama program : in2pos.c
/*Konversi Infix ke postfix menggunakan Stack */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXCOLS 80
typedef enum { FALSE = 0, TRUE = 1} BOOL;
// Deklarasi Node
struct nod {
char data;
struct nod *next;
};
typedef struct nod NOD;
// Uji Tumpukan Kosong
BOOL TumpukanKosong(NOD *T)
{
return ((BOOL)(T == NULL));
}
//Cipta Node Baru
NOD *NodBaru (char item)
{
NOD *n;
n = (NOD*) malloc(sizeof(NOD));
if(n != NULL) {
n->data = item;
n->next = NULL;
}
return n;
36
}
void CiptaTumpukan (NOD **T)
{
*T = NULL; // Buat Tumpukan Kosong
}
// Push
void Push(NOD **T, char item)
{
NOD *n;
n = NodBaru(item);
n->next = *T;
*T = n;
}
// Pop
char Pop(NOD **T)
{
NOD *P; char item;
if ( ! TumpukanKosong(*T)) {
P = *T;
*T = (*T)->next;
item = P->data;
free(P);
}
return item;
}
/* Fungsi menentukan Tingkat Kepentingan Operator Precedence */
BOOL prcd(char topsymb,char symb)
{
if ((topsymb=='+') || (topsymb=='-'))
switch (symb) {
case '+' : return TRUE;
case '-' : return TRUE;
case '*' : return FALSE;
case '/' : return FALSE;
case '(' : return FALSE;
case ')' : return TRUE;
}
if ((topsymb=='*')||(topsymb=='/'))
switch (symb) {
case '+' : return TRUE;
case '-' : return TRUE;
case '*' : return TRUE;
case '/' : return TRUE;
case '(' : return FALSE;
case ')' : return TRUE;
}
37
if (topsymb=='(')
return FALSE;
return FALSE;
}
/* Fungsi Konversi Infix-Postfix */
void postfix(infix,postr)
char infix[];
char postr[];
{
int position;
int outpos = 0;
char topsymb = '+';
char symb;
NOD *opstk=NULL;
for(position=0;(symb=infix[position])!='\0';position++){
if((symb=='+')||(symb=='-')||(symb=='*')||
(symb=='/')||(symb=='(')||(symb==')')){
while((!TumpukanKosong(opstk))&&
prcd(opstk->data,symb)) {
topsymb=Pop(&opstk);
postr[outpos++]=topsymb;
} /* end while */
Push(&opstk,symb);
} /* end if */
else
postr[outpos++]=symb;
}; /* end for */
while(!TumpukanKosong(opstk)){
topsymb=Pop(&opstk);
if (!( topsymb=='(' || topsymb ==')'))
postr[outpos++]= topsymb;
}
postr[outpos]='\0';
free(opstk);
} /* end of postfix */
int main()
{
char infix[MAXCOLS];
char postr[MAXCOLS];
int pos = 0;
/*baca string input */
while((infix[pos++]=getchar())!='\n');
infix[--pos] = '\0'; /* beri pembatas string */
printf("\nString infix yang asli: %s", infix);
postfix(infix,postr);
printf("\nHasilnya : %s\n",postr);
return 0;
}
38
Latihan :
Dengan menggunakan contoh program yang telah dipelajari di atas, maka
1. Buat program untuk mengevaluasi nilai prefix
2. Buat program untuk mengkonversi infix ke prefix
3. Buat program untuk mengkonversi postfix ke infix
39
VI. REKURSI
Rekursif merupakan konsep dasar dalam matematika maupun ilmu komputer.
Banyak masalah-masalah komputasi praktis yang lebih mudah diselesaikan dengan
menggunakan pola rekursif. Definisi secara umum, fungsi (program atau algoritma)
rekursif adalah fungsi yang memanggil dirinya sendiri di dalam tubuh fungsi.
Secara matematis suatu fungsi rekursif dinyatakan sebagai :
jika n = 0 atau n = 1
⎧ 0
f ( n) = ⎨
⎩ f (n − 1) + 1 untuk n lainnya
(6.1)
dalam hal ini fungsi dipanggil secara berulang-ulang dengan menggunakan argumen
yang nilainya makin kecil. Pada fungsi 6.1 berkurangnya nilai argumen dinyatakan
dengan n-1. Sebelum fungsi selesai dikerjakan, dia sudah memanggil dirinya dengan
argumen yang semakin kecil. Dengan pemanggilan berulang-ulang tersebut, maka
nilai n akan mencapai 0 atau 1. Kalau n sudah bernilai 0 atau 1, maka fungsi tersebut
bernilai 0. Kalau fungsi yang dipanggil terakhir bernilai 0, maka fungsi pemanggil
sebelumnya dapat dievaluasi yaitu 0+1 atau 1, fungsi pemanggil sebelumnya lagi
dapat diselesaikan dengan 1+1 atau 2, fungsi sebelumnya lagi dapat diselesaikan
dengan 2+1 atau 3 dan seterusnya sampai dapat diselesaikan fungsi yang pertama
memanggil.
Dari uraian di atas dapat diketahui bahwa ciri-ciri suatu fungsi rekursif adalah:
1. Terdapat nilai fungsi setelah argumennya mencapai batas bawah yang
ditentukan. (disebut juga base case). Dalam hal ini fungsinya sudah cukup
sederhana sehingga nilainya dapat langsung diketahui.
2. Dalam tubuh fungsi ada pernyataan yang memanggil dirinya sendiri dengan
argumen fungsi yang semakin kecil. (disebut juga recursive case). Dalam hal
ini terdapat tiga langkah yaitu: masalahnya dibagi menjadi masalah-masalah
yang lebih kecil, kemudian panggil fungsi secara rekursif untuk tiap masalah,
dan terakhir menggabung hasilnya menjadi hasil akhir
Contoh penggunaan fungsi rekursif adalah untuk menghitung faktorial, yang
memakai simbol (!). Misalnya : 7 ! = 7 * 6 * 5 * 4 * 3 * 2 * 1. Secara rekursif 7! dapat
dinyatakan dengan 7 * 6!, sedangkan 6! dapat dinyatakan sebagai 6 * 5!, dan
seterusnya. Untuk 0! didefinisikan bernilai 1, demikian juga 1! didefinisikan bernilai
1. Jadi fungsi faktorial secara matematis dapat dinyatakan sebagai :
⎧ 1 jika n = 0 atau n = 1
fakt (n) = ⎨
⎩n * f (n − 1) untuk n lainnya
(6.2)
Dalam bahasa C fungsi faktorial didefinisikan sebagai berikut.
long int fakt(long int n)
{
if(n<2) return (1);
else
return (n* fakt(n-1));
}
40
Dalam tubuh fungsi fakt, terdapat perintah memanggil fungsi fakt lagi dengan
argumen (n-1). Batas bawah fungsinya adalah n < 2, yaitu untuk n=1 atau n=0.
Misalnya, akan dihitung fakt(7), maka skema penyelesaian fungsi faktorial
tersebut dapat digambarkan seperti gambar 5.1 berikut ini.
fakt(7)
7*fakt(6)
= 5040
6*fakt(5)
= 720
5*fakt(4)
= 120
= 24
4*fakt(3)
3*fakt(2)
=6
2*fakt(1)
=2
1
Gambar 5.1. Skema Penyelesaian Rekursif
Contoh program di bawah ini terdapat tiga fungsi yaitu : faktorial, fibonacci,
dan penggandaan (perkalian) rekursif. Fibonacci adalah bilangan yang merupakan
hasil penjumlahan dari dua bilangan fibonaci sebelumnya, dimana fibonacci(0) = 0
dan fibonacci (1) = 1. Sedangkan penggandaan n dengan a, ditulis dengan ganda(n,a)
adalah n + ganda(a-1), dimana ganda(n,1) = n. Pelajarilah program di bawah ini
dengan baik.
Nama program : rekursif.c (jalan di Visual C++ 6.0)
/* Fungsi rekursif untuk menghitung
faktorial, Fibonacci, dan Penggandaan*/
#include <stdio.h>
/* Fungsi Faktorial */
long int fakt(long int n)
{
if(n<2)
return (1);
else
return (n* fakt(n-1));
}
41
/* Bilangan Fibonacci = Jumlah dari
dua bilangan fibonacci sebelumnya */
long int fibo(long int n)
{
if ((n==0) || (n==1))
return(n);
else
return(fibo(n-2) + fibo(n-1));
}
/* Penggandaan (perkalian) bilangan a dan b */
long int ganda(long int a, long int b)
{
if (b==1)
return(a);
else
return(ganda(a, b-1) + a);
}
int main()
{
long int a, b, c, d, iFaktorial, iFibonacci, iGanda;
printf("Masukkan bilangan yang akan diFaktorialkan: ");
scanf("%d", &a);
iFaktorial = fakt(a);
printf("Faktorial dari bilangan %d ialah %d\n\n",
a, iFaktorial);
printf("Masukkan bilangan yang akan diFibonaccikan: ");
scanf("%d", &b);
iFibonacci = fibo(b);
printf("Fibonacci dari bilangan %d ialah %d\n\n",
b, iFibonacci);
printf("Masukkan bilangan: ");
scanf("%d", &c);
printf("Masukkan bilangan pengali: ");
scanf("%d", &d);
iGanda = ganda(c, d);
printf("Hasil penggandaan bilangan %d dengan %d ialah
%d\n\n", c,d, iGanda);
return 0;
}
Latihan :
1. Buatlah fungsi untuk menghitung pangkat secara rekursif. Dengan pengertian: n
pangkat a, yang dinyatakan dengan pangkat(n,a) = n * pangkat(a-1), dimana
pangkat(n,0)=1.
42
2. Buatlah fungsi untuk menghitung logaritma n dengan bilangan dasar a. Dengan
pengertian : log(n,a) = 1+log((n div a),a), dan log(1,a) = 0. Dengan syarat n = (a
pangkat k), dimana k = 1,2,3, ...
3. Buatlah fungsi rekursif untuk membalik string. Contoh
43
VII. ANTRIAN (QUEUE)
Antrian (Queue , baca : kiu) adalah struktur data dengan aturan "First In First
Out" (FIFO). Hal ini dapat digambarkan dengan deretan orang yang sedang mengantri
karcis. Orang yang pertama kali datang akan dilayani dulu. Orang yang baru datang
harus mengantri di belakang. Struktur data antrian biasanya digunakan untuk
pencarian model BFS (Breadth First Search).
Operasi-operasi dalam Antrian
Operasi-operasi dalam antrian adalah :
1. enqueue (enq) – Memasukkan item baru di belakang (rear) antrian Q
2. dequeue (deq)- Mengambil (dan menghapus) item paling depan (front) dari antrian
Q
3. init (clear) – Mengintialisasi antrian Q, dan mereset semua variabel.
4. Empty – Mengetahui apakah antrian Q kosong atau tidak. Antrian kosong ditandai
dengan item belakang = item depan atau front = rear. Operasi ini digunakan untuk
syarat bagi operasi dequeue.
5. Full – Mengetahui apakah antrian Q penuh. Operasi ini hanya digunakan bila
menggunakan array.
Ilustrasi dari struktur data antrian dapat dilihat pada Gambar 7.1 berikut ini.
Gambar 7.1. Ilustrasi Antrian (QUEUE) [2]
Program untuk mengimplementasikan struktur data antrian adalah sebagai berikut :
Nama program : antrian.c
# include<stdio.h>
# define MAX 100
typedef int itemtype;
typedef struct queue{
itemtype item[MAX];
int f;
int r;
}Queue;
void clear(Queue *q)
{
q->r=0;
44
q->f=0;
}
void enq(Queue *q,itemtype x)
{
q->r=(q->r+1)%MAX;
q->item[q->r]=x;
}
void deq(Queue *q,itemtype *x)
{
q->f=(q->f+1)%MAX;
*x=q->item[q->f];
}
int empty(Queue *q)
{
return (q->r==q->f);
}
int full(Queue *q)
{
return ((q->r+1)%MAX==q->f);
}
main()
{
int i;
Queue q1;
clear(&q1);
for(i=0;i<10;i++)
if (!full(&q1)) enq(&q1,i);
while(!empty(&q1))
{
deq(&q1,&i);
printf("%d\n",i);
}
}
45
VIII. POHON (TREES)
Sturktur data pohon adalah salah satu struktur data buatan yang banyak
digunakan dalam aplikasi. Salah satu kegunaannya adalah digunakan untuk
mempercepat proses pencarian. Disebut struktur pohon karena konsepnya menyerupai
pohon, ada yang disebut akar (root) yaitu bagian puncak dari struktur pohon, dan
cabang (branch). Setiap titik disebut node, berisi informasi (data), dan cabangcabangnya. Suatu cabang juga dapat berupa struktur pohon, (memiliki pengertian
rekursif). Node paling bawah, yang tidak memiliki cabang, disebut leaf. Suatu pohon
yang setiap cabangnya terdiri dari maksimum dua cabang disebut struktur pohon
biner (binary tree). Dalam struktur data, yang banyak digunakan adalah struktur
pohon biner.
Gambar 8.1. menunjukkan struktur pohon biner, dengan masing-masing node
berisi informasi angka (bilangan bulat). Pohon tersebut terdiri dari 6 node, dengan
root berisi nilai 5, cabang kiri 3 dan cabang kanan 9. Node 3 dan node 9, masingmasing dapat dianggap sebagai tree bagi cabang-cabang di bawahnya. Node 1,4 dan 6
adalah leaf bagi seluruh pohon.
Gambar 8.1. Struktur Pohon Biner
Node paling atas (root) disebut sebagai level 0, node-node cabangnya disebut
level 1, node-node dibawahnya lagi disebut level 2, dan seterusnya. Kalau struktur
pohonnya biner maka banyaknya level dalam pohon biner, disebut juga ketinggian
pohon (height), adalah ⎣log2 ( n) ⎦, dimana n adalah banyaknya node, dan tanda ⎣⎦
adalah tanda pembulatan ke bawah. Seperti contoh pada Gambar 9.1 terdapat 6 node,
maka banyaknya level adalah : ⎣log2 ( 6) ⎦ = ⎣2.5850⎦ = 2, yaitu : level 0 terdiri dari
node 5, level 1 terdiri dari node-node 3, 9, dan level 2 terdiri dari node-node 1,4, 6.
8.1. Aplikasi pada Pohon Biner
Struktur data pohon biner sangat diperlukan pada saat membuat keputusan
dalam suatu proses. Misalnya ingin dicari bilangan yang sama pada sekelompok
bilangan-bilangan. Kalau menggunakan cara biasa, yaitu dengan membandingkan satu
46
sama lain bilangan-bilangan yang diketahui, maka akan terjadi jumlah perbandingan
yang sangat banyak.
Jumlah perbandingan dalam mencari bilangan-bilangan yang sama dapat
dikurangi dengan menggunakan pohon biner. Caranya sebagai berikut : bilangan
pertama kita pasang sebagai root. Bilangan ke dua dibandingkan dengan root, jika
sama maka ada diplikasi, tetapi jika tidak sama maka jika bilangan ini lebih kecil dari
root, maka bilangan ini diletakkan sebagai cabang kiri, jika lebih besar, maka
diletakkan di cabang kanan. Selanjutnya bilangan ke tiga dibandingkan dengan root.
Jika bilangannya sama dengan root maka ada duplikasi, tetapi jika tidak sama maka
jika lebih kecil dari root, perbandingan dilanjutkan ke cabang kiri, tetapi jika lebih
besar dari root dilanjukan perbandingan ke cabang kanan. Dan seterusnya sampai
semua bilangan dibandingkan. Agar lebih jelas, maka diberikan contoh sekelompok
bilangan : 14, 15, 4, 9, 7, 18, 3, 5, 16, 4, 20. Skemanya dapat dilihat pada gambar 8.2
di bawah ini.
14
14
14
15
14
4
15
4
15
9
14
14
4
15
4
9
7
14
15
9
4
18
3
15
9
18
7
Gambar 8.2. Skema Pembangunan Pohon Biner
47
14
14
4
3
15
4
3
18
9
7
15
9
16
18
7
5
5
Selanjutnya bilangan 4 duplikasi, maka informasinya dicatat di tempat lain dan 4 tidak
dimasukkan dalam struktur pohon. Kemudian dilanjutkan dengan bilangan berikutnya
lagi.
14
4
3
15
9
16
7
18
20
5
Gambar 8.2. (Lanjutan)
Bentuk akhir dari pohon biner di atas tampak tidak seimbang, dan tidak
memenuhi syarat log2 (n). Dalam hal ini masalahnya bukan membuat pohon biner
yang seimbang tetapi mencari data duplikasi. Dengan struktur data pohon biner, jika
terdapat n data maka jumlah perbandingannya hanya log2 (n). Bandingkan dengan
pencarian duplikasi dengan cara biasa (linear) yang membutuhkan pembandingan
sebanyak n2. Program aplikasinya dalam bahasa C, dibuat sebagai bahan latihan.
Struktur data dasar untuk struktur pohon biner adalah sebagai berikut :
48
struct nod {
struct nod *left;
char data;
struct nod *right;
};
Setiap node terdiri dari node kiri yang bertipe node juga (struct nod
*left;), informasi untuk tiap node (char data; ), dan node kanan yang bertipe
node juga (struct nod *right; ). Dalam hal ini informasinya berupa satu data
karakter. Dalam aplikasi yang sesungguhnya, informasi bisa bermacam-macam dan
lebih dari satu informasi.
8.2. Penelusuran Pohon Biner
Ada tiga macam penelusuran pohon biner, yaitu : preorder (atau disebut
depth-first order), inorder (atau disebut symetric order), dan postorder. Penelusuran
preorder menggunakan algoritma berikut ini.
1. Kunjungi root dan catat
2. Telusuri sub pohon kiri secara preorder
3. Telusuri sub pohon kanan secara preorder
Penelusuran secara inorder menggunakan algoritma berikut ini.
1. Telusuri sub pohon kiri secara inorder
2. Kunjungi root dan catat
3. Telusuri sub pohon kanan secara inorder
Dan penelusuran secara postorder menggunakan algoritma berikut ini.
1. Telusuri sub pohon kiri secara post order
2. Telusuri sub pohon kanan secara post order
3. Kunjungi root dan catat
Sebagai contoh untuk memperjelas prosedur penelusuran tersebut, berikut ini
diketahui sebuah pohon biner berikut ini.
A
B
D
E
C
F
Gambar 8.3. Contoh Pohon Biner
49
Penelusuran secara pre order adalah : catat root A, catat root sub pohon kiri B, catat
root sub sub pohon kiri (kosong). Sub pohon yang paling kiri dan paling dalam sudah
habis, maka dilanjutkan dengan sub pohon bagian kanan yang terdalam. Catat root C.
Sub pohon kiri sudah habis, maka dilanjutkan dengan sub pohon kanan. Catat root D,
catat root dari sub pohon kiri (kosong), dan lanjutkan ke sub sub pohon kanan. Catat
root E, dan catat root sub sub pohon kiri F. Semua node sudah dikunjungi dan dicatat,
maka hasil akhirnya adalah : A – B – C – D – E – F. Hasil penelusuran secara inorder
adalah : B – C – A – D – F – E. Sedangkan hasil penelusuran secara postorder adalah :
C – B – F – E – D – A.
Program untuk ketiga proses penelusuran tersebut dapat dilihat di bawah ini.
Nama program : pohon.c
/* Aplikasi Pohon (tree) */
#include <stdio.h>
#include <malloc.h>
struct nod {
struct nod *left;
char data;
struct nod *right;
};
typedef struct nod NOD;
typedef NOD POHON;
NOD *NodBaru(char item)
{
NOD *n;
n = (NOD*) malloc(sizeof(NOD));
if(n != NULL) {
n->data = item;
n->left = NULL;
n->right = NULL;
}
return n;
}
void CiptaPohon(POHON **T)
{
*T = NULL;
}
typedef enum { FALSE = 0, TRUE = 1} BOOL;
BOOL PohonKosong(POHON *T)
{
return((BOOL)(T == NULL));
}
50
void TambahNod(NOD **p, char item)
{
NOD *n;
n = NodBaru(item);
*p = n;
}
void preOrder(POHON *T)
{
if(!PohonKosong(T)) {
printf("%c ", T->data);
preOrder(T->left);
preOrder(T->right);
}
}
void inOrder(POHON *T)
{
if(!PohonKosong(T)) {
inOrder(T->left);
printf("%c ", T->data);
inOrder(T->right);
}
}
void postOrder(POHON *T)
{
if(!PohonKosong(T)) {
postOrder(T->left);
postOrder(T->right);
printf("%c ", T->data);
}
}
int main()
{
POHON *kelapa;
char buah;
CiptaPohon(&kelapa);
TambahNod(&kelapa, buah = 'A');
TambahNod(&kelapa->left, buah = 'B');
TambahNod(&kelapa->left->right, buah = 'C');
TambahNod(&kelapa->right, buah = 'D');
TambahNod(&kelapa->right->right, buah = 'E');
TambahNod(&kelapa->right->right->left, buah = 'F');
printf("Urutan secara PreOrder: ");
preOrder(kelapa);
printf("\nUrutan secara InOrder: ");
inOrder(kelapa);
51
printf("\nUrutan secara PostOrder: ");
postOrder(kelapa);
printf("\n\n");
return 0;
}
Latihan :
Realisasikan program untuk menyusun pohon biner seperti pada gambar 8.2. Cetak
data yang ada duplikasinya.
52
DAFTAR PUSTAKA
1. Anonim, “Queue”, http://www.harvestsoft.net/
2.
3.
Embedded Artists, “ Reference Documentation Queue Data Structure v 1.0.1”, ESICTM –
Embedded Systems Infrastructure Component, Sweden, 2003.
Jenkins Thomas, 1998, Recursion, http://engr.nmsu.edu/~etti/winter98/computers jenkins/
jenkins.html
4. Marshall Dave , “Programing In C, New Edition” , LEMU , 1999.
5. Parlante Nick, “ Linked List Basic”, http://cslibrary.stanford.edu/
6. Tenenbaum Aaron M, Yedidyah Langsam, Moshe J. Augenstein, “ Data
Structures Using C”, Prentice Hall, New Jersey, 1996.
53
Download