Mengurutkan array dan mencari elemen secara efisien adalah tugas yang muncul hampir di setiap program. Daripada menulis sendiri algoritma quicksort dan binary search, C menyediakan dua fungsi generik di <stdlib.h>: qsort untuk mengurutkan dan bsearch untuk pencarian biner. Kedua fungsi menerima array void *, ukuran elemen, dan sebuah comparator function pointer. Pada chapter ini kita akan menguasai pola comparator, mengurutkan array sederhana dan struct, lalu memakai bsearch untuk pencarian O(log n).
qsort dengan Comparator Integer dan String #
Signature qsort adalah void qsort(void *base, size_t n, size_t sz, int (*cmp)(const void *, const void *)). Parameter base adalah array, n adalah jumlah elemen, sz adalah ukuran tiap elemen dalam byte, dan cmp adalah function pointer yang membandingkan dua elemen. Comparator harus mengembalikan nilai negatif, nol, atau positif bergantung pada apakah elemen pertama kurang dari, sama dengan, atau lebih besar dari elemen kedua.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Comparator integer menaik */
static int cmp_int(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
/* Comparator string menurun (pakai strcmp) */
static int cmp_str_desc(const void *a, const void *b) {
const char *sa = *(const char *const *)a;
const char *sb = *(const char *const *)b;
return -strcmp(sa, sb); /* negatifkan untuk urutan menurun */
}
int main(void) {
int arr[] = {42, 17, 9, 31, 24, 8, 55};
size_t n = sizeof(arr) / sizeof(arr[0]);
printf("sebelum sort: ");
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
qsort(arr, n, sizeof(int), cmp_int);
printf("setelah sort: ");
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
/* Pencarian dengan bsearch */
int key = 31;
int *found = (int *)bsearch(&key, arr, n, sizeof(int), cmp_int);
if (found) {
printf("ditemukan: %d di indeks %zu\n", *found, (size_t)(found - arr));
} else {
printf("tidak ditemukan: %d\n", key);
}
int key2 = 100;
int *found2 = (int *)bsearch(&key2, arr, n, sizeof(int), cmp_int);
printf("pencarian %d: %s\n", key2, found2 ? "ada" : "tidak ada");
/* Sortir string menurun */
const char *buah[] = {"mangga", "apel", "jeruk", "pisang"};
size_t nb = sizeof(buah) / sizeof(buah[0]);
qsort(buah, nb, sizeof(const char *), cmp_str_desc);
printf("buah (desc): ");
for (size_t i = 0; i < nb; i++) printf("%s ", buah[i]);
printf("\n");
return 0;
}Output yang Dihasilkan:
sebelum sort: 42 17 9 31 24 8 55
setelah sort: 8 9 17 24 31 42 55
ditemukan: 31 di indeks 4
pencarian 100: tidak ada
buah (desc): pisang mangga jeruk apel Mari kita telaah baris-baris kode di atas:
- Baris 6-10 (
cmp_int): comparator generik. Parameter bertipeconst void *, jadi harus di-cast dulu keconst int *sebelum di-dereferensi. Ekspresi(ia > ib) - (ia < ib)adalah idiom yang aman dari overflow—berbeda dengania - ibyang bisa meluap bila selisih sangat besar. - Baris 13-17 (
cmp_str_desc): comparator string menurun. Karena elemen array adalahconst char *, parameterconst void *sebenarnya menunjuk keconst char *. Cast keconst char *const *diperlukan untuk mengambil string yang ditunjuk.strcmpmengembalikan-1/0/1; negatifkan hasilnya untuk urutan menurun. - Baris 26 (
qsort(arr, n, sizeof(int), cmp_int);):qsortmengurutkan array secara in-place. Tidak ada nilai kembalian—jika gagal (jarang terjadi karena alokasi internal), perilaku tidak terdefinisi. - Baris 30 (
int *found = (int *)bsearch(...)):bsearchmengembalikanvoid *yang menunjuk ke elemen yang cocok, atauNULLbila tidak ada. Indeks elemen dapat dihitung denganfound - arr(pointer arithmetic). - Baris 42 (
qsort(buah, nb, sizeof(const char *), cmp_str_desc);):sizeof(const char *)adalah 8 pada sistem 64-bit.qsortmemakai ukuran ini untuk melangkah dari elemen ke elemen.
Mengurutkan Struct dengan Multi-Key #
Pada praktik nyata, kita sering mengurutkan array struct berdasarkan beberapa field sekaligus: primary key, lalu secondary key bila primary sama. Comparator harus mengembalikan perbedaan primary key terlebih dahulu, baru membandingkan secondary key bila primary sama.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char *nama;
int skor;
} Siswa;
/* Comparator: skor menurun, lalu nama menaik */
static int cmp_siswa(const void *a, const void *b) {
const Siswa *sa = (const Siswa *)a;
const Siswa *sb = (const Siswa *)b;
if (sa->skor != sb->skor) {
return (sb->skor > sa->skor) - (sb->skor < sa->skor);
}
return strcmp(sa->nama, sb->nama);
}
int main(void) {
Siswa kelas[] = {
{"budi", 85},
{"siti", 92},
{"agus", 85},
{"dina", 92},
{"rina", 78},
};
size_t n = sizeof(kelas) / sizeof(kelas[0]);
qsort(kelas, n, sizeof(Siswa), cmp_siswa);
printf("peringkat kelas:\n");
for (size_t i = 0; i < n; i++) {
printf("%2zu. %-6s %d\n", i + 1, kelas[i].nama, kelas[i].skor);
}
/* bsearch siswa dengan skor 85, nama "budi" */
Siswa key = {"budi", 85};
Siswa *found = (Siswa *)bsearch(&key, kelas, n, sizeof(Siswa), cmp_siswa);
if (found) {
printf("\nditemukan: %s (%d) di indeks %zu\n",
found->nama, found->skor, (size_t)(found - kelas));
}
return 0;
}Output yang Dihasilkan:
peringkat kelas:
1. dina 92
2. siti 92
3. agus 85
4. budi 85
5. rina 78
ditemukan: budi (85) di indeks 3Comparator cmp_siswa membandingkan skor terlebih dahulu. Bila skor sama, comparator melanjutkan ke strcmp untuk nama. Perhatikan arah perbandingan: skor menurun (sb->skor > sa->skor) berarti siswa dengan skor lebih tinggi muncul lebih dulu, sedangkan nama menaik (strcmp(sa->nama, sb->nama)) berarti urutan alfabetis biasa. bsearch memerlukan key lengkap (skor dan nama) untuk menemukan entri spesifik.