Singly linked list adalah bentuk paling sederhana dari linked list: setiap node memuat data dan satu pointer next yang menunjuk ke node berikutnya. Struktur ini cukup untuk operasi dasar — insert di depan/belakang, hapus berdasarkan nilai, dan traverse dari head ke tail. Pada Chapter ini kita akan membangun singly linked list lengkap menggunakan template C++, dengan penanganan head dan tail agar push_back tetap O(1). Setiap operasi akan kita bedah baris-per-baris agar mekanisme pointer terlihat jelas.
Struktur Node dan Class #
Node adalah blok dasar linked list. Kita gunakan template <typename T> agar node bisa menampung tipe data apa pun — int, std::string, atau tipe user-defined.
Mekanisme: Node sebagai Pasangan Data dan Pointer #
Saat kita menulis Node<int> n(42), compiler memesan blok memori yang cukup untuk int (4 byte) plus pointer (8 byte pada sistem 64-bit). Constructor mengisi nilai dengan 42 dan berikutnya dengan nullptr — artinya node ini belum terhubung ke node lain.
template <typename T>
struct Node {
T nilai;
Node* berikutnya;
Node(const T& v) : nilai(v), berikutnya(nullptr) {}
};Class SinglyLinkedList menyimpan tiga member privat:
head_— pointer ke node pertama;nullptrbila list kosong.tail_— pointer ke node terakhir; memungkinkanpush_backO(1).size_— jumlah node; menghindari traverse hanya untuk menghitung ukuran.
Operasi push_front: Sisip di Depala #
Aturan #
Tambah node baru di posisi paling depan sehingga node baru menjadi head. Kompleksitas O(1) — tidak ada traverse.
Mekanisme: Sambung Baru ke Head Lama #
- Alokasikan node baru
n. - Set
n->berikutnya = head_— node baru menunjuk ke head lama. - Set
head_ = n— head sekarang menunjuk ke node baru. - Bila list sebelumnya kosong (
tail_ == nullptr), maka tail juga harus di-update ke node baru tersebut.
Operasi push_back: Sisip di Belakang #
Mekanisme: Pakai Tail Pointer #
Bila kita hanya punya head, push_back memerlukan traverse O(n) untuk mencari node terakhir. Dengan tail_, kita langsung sambungkan ke node terakhir dalam O(1).
- Alokasikan node baru
n. - Bila list kosong:
head_ = tail_ = n. - Bila tidak:
tail_->berikutnya = n, lalutail_ = n.
Operasi insert_after: Sisip Setelah Node Tertentu #
Mekanisme: Sambung Dua Arah #
- Alokasikan
n. n->berikutnya = pos->berikutnya— node baru menunjuk ke node setelah posisi.pos->berikutnya = n— posisi sekarang menunjuk ke node baru.- Bila
pos == tail_, updatetail_ = n.
Operasi hapus_nilai: Hapus Node Berdasarkan Nilai #
Mekanisme: Simpan Pointer Sebelumnya #
Karena singly linked list tidak punya pointer prev, kita harus menjaga pointer prev selama traverse agar dapat menyambung ulang node sebelumnya.
- Iterasi
cur = head_,prev = nullptr. - Bila
cur->nilai == v: sambungprev->berikutnya = cur->berikutnya(atauhead_ = cur->berikutnyabilaprev == nullptr), laludelete cur. - Update
tail_bila node yang dihapus adalah tail.
Kode Lengkap Singly Linked List #
#include <iostream>
template <typename T>
struct Node {
T nilai;
Node* berikutnya;
Node(const T& v) : nilai(v), berikutnya(nullptr) {}
};
template <typename T>
class SinglyLinkedList {
public:
SinglyLinkedList() : head_(nullptr), tail_(nullptr), size_(0) {}
~SinglyLinkedList() { clear(); }
void push_front(const T& v) {
Node<T>* n = new Node<T>(v);
n->berikutnya = head_;
head_ = n;
if (tail_ == nullptr) tail_ = n;
++size_;
}
void push_back(const T& v) {
Node<T>* n = new Node<T>(v);
if (tail_ == nullptr) {
head_ = tail_ = n;
} else {
tail_->berikutnya = n;
tail_ = n;
}
++size_;
}
void insert_after(Node<T>* pos, const T& v) {
if (pos == nullptr) { push_front(v); return; }
Node<T>* n = new Node<T>(v);
n->berikutnya = pos->berikutnya;
pos->berikutnya = n;
if (pos == tail_) tail_ = n;
++size_;
}
bool hapus_nilai(const T& v) {
Node<T>* cur = head_;
Node<T>* prev = nullptr;
while (cur != nullptr) {
if (cur->nilai == v) {
if (prev == nullptr) head_ = cur->berikutnya;
else prev->berikutnya = cur->berikutnya;
if (cur == tail_) tail_ = prev;
delete cur;
--size_;
return true;
}
prev = cur;
cur = cur->berikutnya;
}
return false;
}
void traverse() const {
Node<T>* cur = head_;
std::cout << "[";
while (cur != nullptr) {
std::cout << cur->nilai;
if (cur->berikutnya != nullptr) std::cout << " -> ";
cur = cur->berikutnya;
}
std::cout << "] (size=" << size_ << ")\n";
}
void clear() {
Node<T>* cur = head_;
while (cur != nullptr) {
Node<T>* next = cur->berikutnya;
delete cur;
cur = next;
}
head_ = tail_ = nullptr;
size_ = 0;
}
std::size_t size() const { return size_; }
private:
Node<T>* head_;
Node<T>* tail_;
std::size_t size_;
};
int main() {
SinglyLinkedList<int> list;
list.push_back(10);
list.push_back(20);
list.push_back(30);
list.traverse();
list.push_front(5);
list.traverse();
list.hapus_nilai(20);
list.traverse();
list.hapus_nilai(99);
std::cout << "hapus 99? " << (list.hapus_nilai(99) ? "ya" : "tidak") << "\n";
list.traverse();
return 0;
}Output yang Dihasilkan:
[10 -> 20 -> 30] (size=3)
[5 -> 10 -> 20 -> 30] (size=4)
[5 -> 10 -> 30] (size=3)
hapus 99? tidak
[5 -> 10 -> 30] (size=3)- Baris
template <typename T> struct Node: struct generik.Tbisaint,std::string, atau tipe user-defined. Setiap instanceNode<T>adalah tipe berbeda —Node<int>danNode<double>tidak dapat dipertukarkan. - Baris
Node(const T& v) : nilai(v), berikutnya(nullptr) {}: constructor menerima konstanta reference agar tidak terjadi copy mahal bilaTberukuran besar (mis.std::string). Member initializer list langsung mengisinilaidanberikutnya. - Baris
n->berikutnya = head_; head_ = n;(dipush_front): urutan krusial. Kita sambungkan dulu node baru ke head lama, baru kemudian head diperbarui. Bila urutan dibalik, kita kehilangan referensi ke list lama. - Baris
if (tail_ == nullptr) tail_ = n;(dipush_front): kasus list kosong — node baru sekaligus menjadi head dan tail. - Baris
tail_->berikutnya = n; tail_ = n;(dipush_back): tail lama menunjuk ke node baru, lalu tail diperbarui. Tetap O(1). - Baris
n->berikutnya = pos->berikutnya; pos->berikutnya = n;(diinsert_after): urutan penting. Node baru harus dulu menunjuk ke node setelah posisi, baru posisi menunjuk ke node baru — kalau tidak, kita memutus rantai. - Baris
prev->berikutnya = cur->berikutnya;(dihapus_nilai): simpul sebelumcurdilompati ke simpul setelahcur. Inilah inti operasi hapus pada linked list — sambung ulang, barudelete. - Baris
if (cur == tail_) tail_ = prev;(dihapus_nilai): bila node yang dihapus adalah tail, tail munduk keprev. Bilaprev == nullptr, list jadi kosong — tail akan di-setnullptrsaatprev == nullptrkasus. - Method
clear(): traverse dandeletesetiap node. Destructor memanggilclear()agar tidak ada memory leak bila objek keluar scope.
Kompleksitas Operasi #
| Operasi | Kompleksitas | Catatan |
|---|---|---|
push_front | O(1) | Cukup ubah head |
push_back | O(1) | Pakai tail pointer |
insert_after | O(1) | Bila pointer pos diketahui |
hapus_nilai | O(n) | Harus traverse cari nilai |
traverse | O(n) | Kunjungi setiap node |
| Akses indeks ke-i | O(n) | Tak ada random access |
Iterasi dan Traverse #
Traverse linked list dilakukan dengan loop while mengikuti pointer berikutnya hingga nullptr. Pola ini berulang di banyak operasi (cari, cetak, hapus, hitung panjang):
Node<T>* cur = head_;
while (cur != nullptr) {
// proses cur->nilai
cur = cur->berikutnya;
}Pola ini disebut pointer chasing — kita tidak tahu di mana node berikutnya secara fisik, kita hanya mengikuti alamat yang tersimpan di pointer.
Ringkasan #
- Singly linked list terdiri dari node dengan data + pointer
berikutnya; head sebagai pintu masuk. - Template
Node<T>memungkinkan tipe data apa pun;Tdipass sebagai const reference untuk efisiensi. push_frontdanpush_backkeduanya O(1) berkat kehadiran pointer head dan tail.hapus_nilaiO(n) karena traverse untuk mencari nilai; perlu menjaga pointerprev.- Selalu
deletenode yang dihapus dan panggilclear()di destructor untuk mencegah memory leak. - Chapter berikutnya akan membahas doubly linked list yang menambah pointer
prevuntuk traverse dua arah.