Kamis, 30 April 2020

AVL TREE


BST dalam worst case bisa sampai O(n) yaitu kita melakukan pencarian sebanyak n data dalam kasus tertentu, yaitu skewed BST tree



Kalau kita ingin mencari angka 13 kiri kita harus melakukan rekursif / looping sebanyak 4 kali, padahal jumlah data ada 5. hal ini membuat boros waktu O(n)





- Jadi diperlukan cara untuk membuat BST menjadi Balanced Binary Search Tree dengan meminimalkan height / tinggi treenya , salah satunya adalah AVL Tree
- Tujuannya adalah untuk mempercepat pencarian dengan mempertahankan tree sekitar O(log n)

Ketentuan AVL Tree :
a. Balance factor
- selisih tinggi child kiri dan kanan harus paling banyak 1

Contoh AVL Tree




angka hitam adalah tinggi dari node

angka merah adalah balance factor dari setiap node

Node angka 17 memiliki left child yang memiliki tinggi 2, sedangkan tidak ada right child.
sehingga balance factornya adalah 2 - 0, 0
karena balance factornya lebih dari 1, BST ini tidak dapat disebut dengan AVL tree






Insertion pada AVL Tree















- insert key baru layaknya BST
( namun dapat membuat kerusakan pada AVL tree, seperti yang terjadi pada Node angka 3, setelah di insert 4 )
- restore / mengembalikan kondisi balance ( balance position )

Rebalancing AVL Tree
- agar AVL Tree tetap balance / seimbang, perlu dilakukan rebalancing setiap melakukan insert node

ada 4 case dalam melakukan rebalancing
anggap node yang ingin di rebalance adalah node T
1. node terdalam terletak di left sub tree dari left child T
2. node terdalam terletak di right sub tree dari right child T
3. node terdalam terletak di right sub tree dari left child T
4. node terdalam terletak di left sub tree dari right child T
karena dalam insertion, node yang di insert adalah node paling bawah

Rebalancing AVL Tree menggunakan rotation
1. Single rotation berlaku untuk kerusakan / ketidakseimbangan case 1 dan 2
2. double rotation berlaku untuk kerusakan case 3 dan 4

Insertion
- setiap kita melakukan insertion kita harus mengecek dari bawah apakah ada terjadi ketidakseimbangan, jika ada harus di rebalancing

jenis jenis rotation :
1. LL rotation, untuk menyelesaikan case 1


kita lakukan rotasi S dan T, sehingga T menjadi right childnya S. sedangkan right child S dipindahkan menjadi left childnya T.
namun apabila menghapus b dan menginsertkan lagi akan berada di left dari T, jadi sama saja

2. RR rotation, untuk menyelesaikan case 2



Kita lakukan seperti kebalikan dari case 1, node T jadi left child S. left childnya S ditempel ke rightnya T

--Contoh single rotation



Dalam kasus ini, node 30 memiliki balance factor 2, sehingga terjadi violation

Karena node terdalam ada di left sub dari child 30, makanya terjadi kasus 1

jadi kita tinggal melakukan LL (left left) rotation pada node 22 dan 30
























Untuk LR dann RL harus dengan double rotation
3. LR (left right) rotation

4. RL ( RIght left ) rotation
Merupakan kebalikan dari LR rotation

Contoh dari double rotation
























Deletion pada AVL Tree
seperti deletion pada BST, namun kita perlu melakukan checking dan rebalancing pada tree, dari bawah hingga root



Setelah mendelete node 60, node 55 akan unbalance










RR rotation pada node angka 55










lalu lakukan double rotation untuk melakukan balancing pada root









AVL Tree setelah mendelete node 60

Minggu, 05 April 2020

DATA STRUCTURE


MATERIAL REVIEW 

1. Linked list
Perbedaan mendasar antara array dan linked list adalah array melakukan alokasi / memori sesuai dengan yang di pesan. Sehingga memori digunakan semua atau tidak, tetaplah mengonsumsi memori sesuai yang dideklar.
Sedangkan linked melakukan alokasi sesuai dengan banyaknya kita memesan memori dan memori tersebut bisa dihapus atau digunakan nantinya.

Malloc (pada library stdlib.h) adalah fungsi untuk me-alokasikan memori pada struktur data. Kita menggunakan malloc setiap kita membuat node baru, untuk memesan memori. Misalnya kita ingin membuat node untuk angka 1,2, dan 3, kita harus melakukan malloc sebanyak 3 kali.

Linked list adalah rantai data yang dihubungkan dengan pointer yaitu *next (untuk single linked list), lalu *next dan *prev (untuk double linked list). Lalu ada juga *head untuk penanda node awal sebagai kepala, dan *tail untuk penanda node akhir sebagai ekor. Sedangkan ada pointer *curr yang bisa berjalan – jalan, sedangkan *head dan *tail harus menempati posisi yang tepat.

struct Node{

                int value;
                Node *next;
}*head,*tail,*curr;

Karena pointer *head,*tail, dan *curr dideklarasikan secara global, dapat digunakan function / method dibawahnya dan sudah di-inisialisasi. Selain int value, bisa diisi dengan variable dari tipe data lain.
Dari contoh itu, Node memiliki nilai dan Node pointer *next untuk menunjuk alamat node setelahnya.
Operasi yang lazim digunakan dalam linked list, khususnya single linked list adalah sebagai berikut

1.pushHead = untuk menginput data melalui head, sehingga data tersebut akan menjadi head

void pushHead(int value){
                curr = (Node*)malloc(sizeof(Node));
                curr->value = value;
                if(head==NULL){
                                head = tail = curr;
                                curr->next = NULL;
                }
                else{
                                curr->next = head;
                                head = curr;
                }
}

setelah kita mengalokasikan dan memasukan nilai curr, kita melakukan validasi apakah head itu ada atau tidak. kalau tidak ada, maka kita buat curr menjadi head dan tail.
kalau head ada, makan kita buat curr->next itu head, berarti curr->next menunjuk ke alamat head.
setelah terhubung, kita jadikan curr sebagai head.

2. pushTail = untuk menginput data melalui tail dengan menggeser tail, sehingga data akan diletakan di tail

void pushTail(int value){
curr = (Node*)malloc(sizeof(Node));
curr->value = value;
curr->next = NULL;
if(head==NULL){
head = tail = curr;
}
else{
tail->next = curr;
tail = curr;
}
}

perbedaan yang ada pada pushHead dan pushTail ada pada else validasi. kita tunjuk alamat curr di tail menggunakan tail->next = curr, lalu kita jadikan curr sebagai tail.

3. pushMid = untuk menginput data di tengah - tengah rantai sesuai dengan keinginan user, biasanya hal ini butuh bantuan variabel tambahan seperti curr;

4. popHead = menghapus data pada head. kita harus memindahkan headnya terlebih dahulu sebelum dihapus / dibebaskan, karena bila head / tail hilang, data akan kacau karena tidak ada batasan.

void popHead(){
if(head==NULL) return;
if(head==tail){
free(head);
head = tail = NULL;
return;
}
curr = head;
head = curr->next;
free(curr);
}

kita harus lakukan beberapa validasi dalam menggunakan pop, yaitu
a. apakah head itu ada. bila tidak ada, maka tidak ada data yang perlu di pop, sehingga di return fungsinya
b. apakah head dan tail itu sama. kalau iya, artinya data hanya ada 1 karena hanya 1 data head dan tail bisa satu tempat.
kalau hanya satu data, maka kita bebaskan head/ tail, sama saja. dan kita inisialisasi NULL;

setelah melewati validasi, berarti rantai memiliki data lebih dari satu.
cara melakukan popHead adalah
- menandai curr di head
- menggeser head ke next, tujuannya agar head tidak di free (mempertahankan head)
- lalu kita bebaskan curr

5. popTail = menghapus data pada tail, kali ini kita harus mempertahankan tail.
dalam single linked list cara yang digunakan cukup merepotkan:
- kita mendandakan curr di head
- menggeser curr sampai sebelum tail
- menggeser tail ke curr
- bebaskan tail->next

void popTail(){
if(head==NULL) return;
if(head==tail){
free(head);
head = tail = NULL;
return;
}
curr = head;
while(curr->next!=tail){
curr = curr->next;
}
tail = curr;
free(curr->next);
tail->next = NULL;
}
lalu agar program tidak crash, tail->next kita beri NULL;

kalau dengan double linked list, kita tinggal memindahkan tail melewati *prev, sedangkan single linked list harus melakukan looping. Dan pada head->prev harus menunjuk pada NULL sehingga awal dan akhir data diapit oleh NULL.

6. popMid = melakukan pop pada data bisa di head, tail, ataupun diantara mereka. biasanya popMid mengacu pada value yang dipass pada parameter atau index data yang akan dihapus.

2. Double Linked List

1. Circular Single Linked List
- sama seperti single linked list, namun node paling akhir menunjuk pada pointer awal
- circular tidak hanya dapat dilakukan pada single linked list, tapi juga double linked list
- tidak ada memasukan nilai NULL pada list ini
















2. Double Linked List
adalah list struktur data yang memiliki 2 penunjuk, yang satu menunjuk pada data selanjutnya, dan yang satu menujuk pada data sebelumnya;








struct Node{
int value;
struct Node *next;
struct Node *prev;
};
struct Node *head=0, *tail=0;

a .Insert
adalah langkah untuk menyisipkan data pada list, biasanya dengan menggeser head (pushHead), menggeser tail (pushTail), ataupun menyisipkan pada tengah list (pushMid)

contoh :





contoh menambah list setelah tail / push tail
struct Node *curr = (Node*)malloc(sizeof(Node));
curr->value = x;
// menghubungkan tail dan curr
curr->next = NULL;
curr->prev = tail;
tail->next = curr;
// menggeser curr menjadi tail
tail = curr;

kita juga bisa meletakan data baru diantara data lain, tinggal dihubungkan kedua panah nya

b. Delete
yang bisa didelete adalah
- data hanya satu di list
- data adalah head
- data adalah tail
- data bukan head juga bukan tail

jika data yang mau dihapus hanya satu satunya data =
free(head);
head = NULL:
tail = NULL;

jika data yang mau dihapus head =
head = head->next; //membuat data selanjutnya menjadi head
free(head->prev); //memutus panah dari pointer head->prev
head->prev = NULL; //menghapus nilai head sebelumnya

jika data yang mau dihapus tail =
tail = tail->prev; //membuat data sebelumnya tail
free(tail->next); //memutus tail->next baru
tail->next = NULL: //menghapus nilai tail lama

jika data yang mau dihapus bukan head / tail =
struct Node *curr = head; //curr dimulai dari head
while(curr->next->value != x) curr = curr->next; // selama nilai curr->next bukan target, maka curr =                                                                                    curr->next
struct Node *a = curr; //karena curr berhenti sebelum data yang akan dihapus
struct Node *delete = curr->next atau a->next; //data yang akan dihapus
struct Node *b = curr->next->next atau  delete->next //data setelah data yang akan dihapus
a->next = b; //menunjuk b sebagai next a
b->prev = a; // menunjuk a sebagai prev b
                    // dengan kata lain, data delete, sudah tidak terhubung dengan list (node)
free(del);

3. Hashing, HashTable and Binary Tree

A.HASHING and HASHTABLE
- Hashing adalah suatu teknik untuk menyimpan atau menerima suatu key dalam suatu pengaturan tertentu.
- Dalam hashing, biasanya string karakter biasanya diubah menjadi nilai karakter atau key yang lebih kecil yang mewakili string tersebut, bisa dibilang menyederhanakan.
- Hashing digunakan untuk mempercepat pencarian dari key yang sudah di Hashed daripada harus mencari original value / nilai sebenarnya dari suatu data.
- Jadi Hashing juga bisa dibilang konsep untuk mendistribusi / menyalurkan key kedalam suatu array Hash table dengan fungsi yang telah ditentukan (yang disebut hash function).

Hash Table
Hash Table adalah sebuah array yang menyetor string (original string). Index dari array adalah nilai dari key yang sudah di Hashed dan nilai dari string tersebut tetap.
Biasanya ukuran array bisa kurang dari banyaknya string yang ada, jadi beberapa string bisa saja punya Hashed key yang sama.

Contoh: misalnya kita punya 5 string; “define”, ”exp”, ”float”, ”atan” ,dan ”char”. Kita mau menyetor 5 string tersebut ke HashTable yang berindeks 26 dan kita memiliki ketentuan “ubah karakter pertama string menjadi angka diantara 0 sampai 25”. (a jadi 0, b jadi 1, c jadi 2,… z menjadi 25).

H[]
Value
0
atan
1

2
char
3
define
4
exp
5
float
6


25

Kita lihat saja dari huruf pertama string lalu disetor sesuai dengan ketentuan alamat, seperti atan yang huruf paling depannya a dimasukan ke index pertama, begitupula seterusnya

HashFunction
Ada beberapa cara untuk melakukan hash dari string yaitu

1. Mid square
Kuadratkan string atau nilai (key)  lalu gunakan beberapa angka dari tengah “rentetan bilangan biner” untuk mendapatkan Hashed key. Jika key berupa string, diubah dulu jadi angka (konversikan).
key
kuadrat
Nilai tengah
3121
9740641
406
3122
9746884
468
3123
9753129
531

Misalnya kita mempunyai ukuran table 1024, maka representasi biner 3121 ialah 1001010-0101000010-1100001 . 1024 adalah 2 pangkat 10, jadi kita pilih 10 bilangan biner tengah. Lalu dikonversikan menjadi 406.

2. Division
Membagi nilai string dengan menggunakan modulus (menyederhanakan). Cara hashing ini paling sering digunakan karena paling simple.
Fungsi H(x) = x mod M
X adalah key
M adalah nilai yang digunakan untuk membagi key, biasanya menggunakan angka prima / ukuran table / banyaknya penggunaan memori.

Misalnya kita ingin memasukan string. Fungsi hash akan menambahkan nilai ascii dari semua karakter string lalu dimodulo dengan ukuran table, misalnya 97
“COBB” akan disetor di ( 64+3 + 64+15 + 64+2 + 64+2) % 97 = 88
Huruf ‘C’ harus dikonversikan asci terlebih dahulu yaitu 67
“HIKE” akan distor di ( 64+8 + 64+9 + 64+11 + 64+5) % 97 = 2
88 dan 2 merupakan hashed key dari kedua string tersebut


3. Folding
Adalah membagi string ke beberapa bagian, lalu tambahkan bagian – bagian tersebut untuk mendapatkan nilai HashKey. Jika melebihi batas angka, dapat dihilangkan / dibiarkan.
Contoh : diberikan hashtabel 100 index, hitung nilai hash dari angka 5678, 321, 34567. Karena hanya terdapat 100 tempat alamat, jadi kita bisa memecahkan key jadi beberapa bagian yang satu bagiannya menyimpan 2 digit.

key
5678
321
34567
Bagian
56, 78
32, 1
34, 56, 7
jumlah
134
33
97
Nilai hash
34
(hilangin angka sisa)
33
97


4. Digit Extraction
Digit yang telah ditentukan adalah sumber refrensi dari HashKey
Contoh: angka 14,568
Kalau kita menentukan dari index ke 1, 3, dan 5 maka nilai dari Hashed key ialah 158

5. Rotating Hash
Pertama lakukan satu fungsi hash, seperti division atau mid-square method
Setelah itu, lakukan rotasi, rotasi dilakukan dengan membalikan angka dari belakang ke depan
Contoh : hash address = 20021;
Hasil Rotasi = 12002;


B. Collision
Misalnya kita ingin menyetor string dengan fungsi hash sebelumnya, yaitu define, float, exp, char, atan, ceil, acos, floor.
Ada string yang memiliki nilai key yang sama yakni atan dan acos (hash key 0), char dan ceil (hash key 2)
Ada 2 cara untuk menanggulangi collision :

1. Linear Probing
Cari tempat kosong berikutnya lalu taruh string disana
Cth : diberikan beberapa string ; define, float, exp, char, atan, ceil, floor, acos.
H[]
value
setep
0
atan
1
1
acos
2
2
char
1
3
define
1
4
Exp
1
5
float
1
6
ceil
5
7
foor
3



Misalnya kita mau memasukan acos, H[0] sudah terisi, sehingga dicari tempat lain, yaitu h[1].
Linear probling punya kompleksitas yang buruk jika terdapat banyak collisions. Baris step menjelaskan berapa banyak loop/ step yang dilakukan untuk menemukan string. Misalnya kita mau mencari ceil, kita menghitung hash key mendapatkan 2, tapi ceil tidak disana, sehingga kita harus mencari lagi sampai mendapatkan ceil.

2. Chaining
Taruh string di slots ebagai rantai (Linked list).
H[]
value
0
atan->acos
1
NULL
2
char->ceil
3
define
4
exp
5
float->floor
6
NULL
7
NULL


Dalam chaining, kita masukan setiap string dalam suatu rantai (linked list). Sehingga jika terjadi collision, kita tinggal menambahkan, menghubungkan rantai


C. Trees / binary tree
Three adalah struktur data non linear yang mereprentasikan relasi hirarkis diantara objek data.
Konsep trees:
- Node yang paling atas disebut root
- Garis yang menghubungkan parent dengan anak nya adalah edge
- Node yang tidak punya anak disebut leaf
- Node yang punya parent yang sama disebut sibling
- Degree dari node adalah berapa cabang node itu
- height/depth adalah degree maksimum dari semua node tree
- jika ada garis yang menghubungkan p ke q. p dikatakan leluhur / ancestor dar q, sedangkan q adalah     descendant/ keturunan dari p

Konsep binary tree:
- Binary tree adalah struktur data akar pohon yang memiliki anak node paling banyak 2, yaitu left dan right
- node yang tidak memiliki anak biasa disebut leaf

contoh

banyak node = 9, berakar dari node yang memiliki nilai 18
leaves adalah node yang berisikan value 9,12,10 dan 23

Representasi binary tree dan Array




Representasi binary tree
struct node {
                int data;
                struct node *left;
                struct node *right;
                struct node *parent;
};
struct node *root = NULL;



pointer *left, *right untuk menunjuk pada child, sedangkan *parent untuk menunjuk parent diatas


D. Hashing dalam block chain


hashing berarti mengambil string input dengan panjang berapa pun dan memberikan output dengan panjang tetap .Dalam konteks cryptocurrency seperti Bitcoin, transaksi diambil sebagai input dan dijalankan melalui algoritma hashing ( Bitcoin menggunakan SHA-256 ) yang memberikan output dengan panjang tetap.



Seperti yang Anda lihat, dalam kasus SHA-256 , tidak peduli seberapa besar atau kecil input Anda, output akan selalu memiliki panjang 256-bit tetap. Ini menjadi penting ketika Anda berurusan dengan sejumlah besar data dan transaksi. Jadi pada dasarnya, alih-alih mengingat input data yang bisa jadi besar, Anda bisa mengingat hash dan terus melacak. Sebelum kita melangkah lebih jauh, kita perlu terlebih dahulu melihat berbagai properti fungsi hashing dan bagaimana mereka diimplementasikan di blockchain

Fungsi hash kriptografis
Fungsi hash kriptografi adalah kelas khusus fungsi hash yang memiliki berbagai properti sehingga ideal untuk kriptografi. Ada sifat-sifat tertentu yang harus dimiliki oleh fungsi hash kriptografis agar dianggap aman. Mari kita jalankan satu per satu.

Properti 1: Deterministik
Ini berarti bahwa tidak peduli berapa kali Anda mem-parsing melalui input tertentu melalui fungsi hash Anda akan selalu mendapatkan hasil yang sama. Ini sangat penting karena jika Anda mendapatkan hash yang berbeda setiap kali tidak mungkin untuk melacak input.

Properti 2: Komputasi Cepat
Fungsi hash harus mampu mengembalikan hash input dengan cepat. Jika prosesnya tidak cukup cepat maka sistem tidak akan efisien.

Properti 3: Resistensi Pra-Gambar
Apa pra-gambar resistensi menyatakan bahwa diberikan H (A) itu tidak layak untuk menentukan A, di mana A adalah input dan H (A) adalah hash output. Perhatikan penggunaan kata “tidak layak” alih-alih “tidak mungkin”. Kita sudah tahu bahwa bukan tidak mungkin untuk menentukan input asli dari nilai hash-nya . Mari kita ambil contoh.

Misalkan Anda melempar dadu dan hasilnya adalah hash dari angka yang muncul dari dadu. Bagaimana Anda bisa menentukan nomor aslinya? Sederhana saja yang harus Anda lakukan adalah mengetahui hash semua angka dari 1-6 dan membandingkan. Karena fungsi hash bersifat deterministik, hash dari input tertentu akan selalu sama, sehingga Anda dapat dengan mudah membandingkan hash dan mencari tahu input asli.

Tapi ini hanya berfungsi ketika jumlah data yang diberikan sangat kurang. Apa yang terjadi ketika Anda memiliki sejumlah besar data? Misalkan Anda berurusan dengan hash 128-bit. Satu-satunya metode yang Anda harus menemukan input asli adalah dengan menggunakan ” metode brute-force “. Metode brute-force pada dasarnya berarti bahwa Anda harus mengambil input acak, hash dan kemudian membandingkan output dengan hash target dan ulangi sampai Anda menemukan kecocokan.
Jadi, apa yang akan terjadi jika Anda menggunakan metode ini (brute-force)?

 1.  Skenario kasus terbaik: Anda mendapatkan jawaban pada percobaan pertama itu sendiri. Anda harus benar-benar menjadi orang paling beruntung di dunia untuk ini terjadi. Peluang terjadinya ini adalah astronomi.
 2. Skenario kasus terburuk: Anda mendapatkan jawaban setelah 2 ^ 128 – 1 kali. Pada dasarnya, itu berarti bahwa Anda akan menemukan jawaban Anda di akhir semua data
 3.   Skenario rata-rata: Anda akan menemukannya di suatu tempat di tengah jadi pada dasarnya setelah 2 ^ 128/2 = 2 ^ 127 kali. Untuk memasukkannya ke dalam perspektif, 2 ^ 127 = 1,7 X 10 ^ 38. Dengan kata lain, ini adalah jumlah yang sangat besar.

Jadi, walaupun dimungkinkan untuk memecah resistansi pra-gambar melalui metode brute force, butuh waktu lama sehingga tidak masalah.

Properti 4: Perubahan Kecil Pada Input Mengubah Hash.
Bahkan jika Anda membuat perubahan kecil pada input Anda, perubahan yang akan tercermin dalam hash akan sangat besar. Mari kita mengujinya menggunakan SHA-256:
Kamu melihatnya? Meskipun Anda baru saja mengubah huruf alfabet pertama input, lihat seberapa besar yang memengaruhi hash output. Ini adalah fungsi kritis karena sifat hashing ini mengarah ke salah satu kualitas terhebat blockchain, ketidakberubahannya (lebih lanjut tentang itu nanti.)



Properti 5: Tahan Tabrakan
Mengingat dua input A dan B yang berbeda di mana H (A) dan H (B) adalah hash masing-masing, maka tidak mungkin H (A) sama dengan H (B). Apa artinya itu adalah bahwa sebagian besar, setiap input akan memiliki hash uniknya sendiri. Mengapa kami mengatakan “sebagian besar”? Mari kita bicara tentang konsep yang menarik yang disebut “The Birthday Paradox”.

Properti 6: Puzzle Friendly
Sekarang, ini adalah properti yang menarik, dan aplikasi serta dampak yang dimiliki properti ini pada cryptocurrency sangat besar (lebih banyak tentang itu nanti ketika kita membahas teka-teki mining dan crypto). Pertama mari kita mendefinisikan properti, setelah itu kita akan membahas setiap istilah secara rinci.
Untuk setiap output “Y”, jika k dipilih dari distribusi dengan entropi min tinggi, tidak mungkin menemukan input x sedemikian rupa sehingga H (k | x) = Y.
Itu mungkin melintas di kepala Anda! Tapi tidak apa-apa, mari kita sekarang mengerti apa arti definisi itu.

Jadi hashing memiliki manfaat yang besar dalam block chain, sampai hari ini pun blockchain masih menggunakan hashing. implementasi Hashing dalam blockchain, akan lebih terlihat dalam pohon merkel.

4. Hashing (II) and Binary Search Tree (BST)

A. Hashing
Hashing adalah metode yang digunakan untuk mempermudah penyimpanan dengan cara melakukan ekripsi nilai tertentu dengan kaidah yang telah ditentukan sebagai “key” dari suatu data.

Hash Table
Adalah table ( dalam bentuk array ) dimana kita menyetor suatu data, dimana indexnya adalah key dan nilainya (value) adalah data sebenarnya.

Sebagai contoh, kita akan menyetor beberapa nilai integer kedalam array yang memiliki index sebanyak 20. Karena index hanya 20, maka key yang kita buat harus 0 – 20. Nilai key akan ditentukan dengan modulus 20;
Contoh :
Input:
- Value = 20, key =  20 % 20 = 0;
- Value = 5,  key = 5%20 = 5;
Hasil
index array
Key
value
0
20
20
1..4


5
5
5
6…19




Tetapi ada beberapa kondisi yang dapat muncul dalam melakukan Hashing ini
Contoh :
Input :
- value = 20, key = 0
- value = 1, key = 1;
- value = 21, key = 21 % 20 = 1;
Value 1 dan 21 memiliki key yang sama, kalua dibiarkan maka value 1 akan di override / ditimpa nilainya oleh 21. Hal ini disebut collision
Untuk menangani collision, ada 2 cara yang dapat diterapkan yaitu probing dan chaining
1.  probing
Berarti jika tempat key sudah diisi, maka data akan diisi ditempat yang kosong selanjutnya. Hal ini akan memperlambat pencarian jika sudah banyak tempat yang terisi.
void insert(int key,int value){
               int hash_index = hashCode(key); // untuk dapatin key yang udah di hash
               struct Data *item= (Data*)malloc(sizeof(Data)); //setor nilai dalam node item
               item->key = key;
               item->value = value;
               while(tabel[hash_index]!=NULL){ //selama tempat itu ga kosong
                              hash_index++;                                                // digeser trus sampai t4 kosong
               }
               hash_index %= size;
               tabel[hash_index] = item; // sampai t4 kosong baru bisa masukin
}

Data* search(int keyz){
               int hash_index = hashCode(keyz); //mencari nilai key
               while(tabel[hash_index]!=NULL){ //masih harus cari karena bisa aja datanya ud tergeser dari
                                                                            index yang seharusnya                           
                              if(tabel[hash_index]->key==keyz){ //klu misalnya ktemu kosong, nanti stop
                                             return tabel[hash_index];
                              }
                              hash_index++;  //menambah nilai key sampai ketemu tempat kosong
                              hash_index %= size;
               }
               // warning : klu semua kotak terisi dan tidak ada yang dicari, bisa looping forever -> crash
               return NULL;
}
void deletion(int key){
               Data *del = search(key); //cari datanya pkai function search
               if(del==NULL){
                              printf("No data to be deleted\n");
                              return;
               }
               else{
                              int code = hashCode(del->key);
                              while(tabel[code]!=NULL){
                                             if(tabel[code]->key == key){
printf("(%d,%d) has been deleted\n",tabel[code]->key,tabel[code]->value);
                                                            tabel[code] = NULL;
                                             }
                                             code++;
                                             code %= size;
                              }
                              // warning : klu semua kotak terisi dan dkd yang dicari, bisa looping forever -> crash
               }
               return;
}

Kelemahan probingakan terlihat dari contoh berikut
Insert(1,20) , insert(21,21), insert (41,22), insert(2,50)
Maka akan menghasilkan
idx array
Key
value
0


1
1
20
2
21
21
3
41
22
4
2
50
5..19



Bila kita menghapus key 1, maka array index 1 akan memiliki value null. Lalu Ketika kita ingin menghapus key 21 ataupun 22, maka akan dicek index 1, yaitu null. Karena null tersebut, maka deletion akan berhenti sehingga key 21 dan 41 tidak bisa dihapus.

2. Chaining
Bila ada key yang sama, maka akan ditempatkan di next. Cara kerjanya sama seperti linked list.
void insert(int key,int value){
               int hash_idx = hashCode(key);
               Data *item = (Data*)malloc(sizeof(Data));
               item->key = key;
               item->value = value;
               item->next = NULL;
               if(kotak[hash_idx]==NULL){
                              kotak[hash_idx] = item;
               }
               else{
                              Data *temp = kotak[hash_idx];
                              while(temp->next){      //kalau setelah head(index 1) ada sesuatu, dia looping sampai ga
                                                                           ada lagi (ujung)                    
                                             temp = temp->next;
                              }
                              temp->next = item; //baru ujungnya ditempelin item
               }
}

Data* search(int key){
               int hash_idx = hashCode(key);
               while(kotak[hash_idx]!=NULL){
                              if(kotak[hash_idx]->key==key){
                                             return kotak[hash_idx];
                              }
                              kotak[hash_idx] = kotak[hash_idx]->next;
               }
               return NULL;
}

void deletion(int key){ //deletion v2
               int hash_idx = hashCode(key);
               if(!kotak[hash_idx]) return;
               Data *temp = kotak[hash_idx];
               Data *stab;
               if(temp->key==key){ // jika yang mau dihapus data pertama / head
                              kotak[hash_idx] = temp->next;   //headnya digeser
                              temp = NULL;
                              free(temp);
                              return;
               }
               while(temp->next!=NULL){
                              if(temp->next->key==key){
                                             stab = temp->next;
                                             temp->next = stab->next;
                                             stab = NULL;
                                             free(stab);
                                             return;
                              }
                              temp = temp->next;
               }
               printf("no data to be deleted\n");
               return;
}

Contoh: Insert(1,20) , insert(21,21), insert (41,22), insert(2,50)

Index array
key
value
0


1
1, 21, 41
20 -> 21 -> 22
2
2
5
3..19



Jadi cara terbaik dalam hash table adalah chaining karena lebih efektif, efisien dan aman dalam insertion, searching, dan deletion.

B. Binary Search Table
Adalah struktur data yang setiap nodenya memiliki cabang dua yaitu left dan right, dan diawali oleh root. Node yang tidak ada cabang disebut leaf.

Metode dalam bst ada 3 yaitu insert, search, and delete.

struct Node{
               int value;
               Node *left,*right;
}*curr;

Node *create(int nilai){
               curr = (Node*)malloc(sizeof(Node));
               curr->value = nilai;
               curr->left = curr->right = NULL;
               return curr;
}

Node *insert(Node *root, int nilai){
               if(root==NULL) return create(nilai);
               else if(nilai > root->value){
                              root->right = insert(root->right,nilai);
               }
               else if(nilai < root->value){
                              root->left = insert(root->left,nilai);
               }
               return root;
}

Kita mengoper root, dan nilai baru untuk node yang kita acari. Ketika root nya tidak ada atau null, maka nilai itu akan menjadi root. Jika tidak, maka akan dilakukan pengecekan nilai itu lebih besar atau lebih kecil dari root.
Jika lebih besar dari root, maka akan diinsert ke root right. Sebaliknya jika lebih kecil maka akan diinsert ke left, hingga ketemu NULL dan di create

Node *del(Node *root, int nilai){
               if(root==NULL) return NULL;
               else{
                              if(nilai == root->value){
                                             if(root->left==NULL && root->right==NULL){ //ga ada left right
                                                            free(root);
                                                            root = NULL;
                                             }
                                             else if(root->left==NULL){ //ada right
                                                            curr = root;
                                                            root = root->right;
                                                            free(curr);
                                                            curr = NULL;
                                             }
                                             else if(root->right==NULL){ /ada left
                                                            curr = root;
                                                            root = root->left;
                                                            free(curr);
                                                            curr = NULL;                                                   
                                             }
                                             else{ //kalau 2 anak,
                                                            curr = root->left; //dalam hal ini, dicari anak terkanan dari left                                                                                              root
                                                            while(curr->right){
                                                                           curr = curr->right;
                                                            }
                                                            root->value = curr->value; //dilakukan penukaran nilai
                                                            root->left = del(root->left,curr->value);
                                             }
                              }
                              else if(nilai > root->value){
                                             root->right = del(root->right,nilai);
                              }
                              else if(nilai < root->value){
                                             root->left = del(root->left,nilai);
                              }             
               }
               return root;
}

Deletion memiliki 3 kondisi
11.    Node merupakan leaf / tidak ada left dan right
Dalam hal ini, node tinggal di free dan dijadikan NULL
22.    Node punya 1 anak , left atau right
Maka node digeser , dihubungkan dengan anak dan node yang lama di free
33.   Node punya dua anak, left dan right
      Biasanya, ada kesepakatan mengenai pengganti dari node, yaitu node paling kanan dari left atau node paling kiri dari right, tergantung kesepakatan.

      Sekian review dari saya, terima kasih :)