Saya telah memposting ini sekali sebelumnya di SO, tetapi saya akan mereproduksinya di sini karena itu cukup keren. Ini menggunakan hashing, membangun sesuatu seperti set hash di tempat. Ini dijamin menjadi O (1) di ruang ketiak (rekursi adalah panggilan ekor), dan biasanya kompleksitas waktu O (N). Algoritmanya adalah sebagai berikut:
- Ambil elemen pertama dari array, ini akan menjadi sentinelnya.
- Susun ulang sisa larik, sebanyak mungkin, sedemikian rupa sehingga setiap elemen berada pada posisi yang sesuai dengan hashnya. Saat langkah ini selesai, duplikat akan ditemukan. Atur mereka sama dengan sentinel.
- Pindahkan semua elemen yang indeksnya sama dengan hash ke awal larik.
- Pindahkan semua elemen yang sama dengan sentinel, kecuali elemen pertama dari larik, ke akhir larik.
- Apa yang tersisa antara elemen yang di-hash dengan benar dan elemen duplikat adalah elemen yang tidak dapat ditempatkan di indeks yang sesuai dengan hashnya karena benturan. Berulang kali untuk menangani elemen-elemen ini.
Ini dapat diperlihatkan sebagai O (N) asalkan tidak ada skenario patologis dalam hashing: Bahkan jika tidak ada duplikat, kira-kira 2/3 dari elemen akan dihilangkan pada setiap rekursi. Setiap level rekursi adalah O (n) dimana n kecil adalah jumlah elemen yang tersisa. Satu-satunya masalah adalah, dalam praktiknya, ini lebih lambat daripada pengurutan cepat ketika hanya ada sedikit duplikat, yaitu banyak tabrakan. Namun, bila ada banyak duplikat, itu luar biasa cepat.
Sunting: Dalam implementasi D saat ini, hash_t adalah 32 bit. Segala sesuatu tentang algoritma ini mengasumsikan bahwa akan ada sangat sedikit, jika ada, benturan hash dalam ruang 32-bit penuh. Tabrakan, bagaimanapun, dapat sering terjadi di ruang modulus. Namun, asumsi ini kemungkinan besar akan benar untuk kumpulan data yang berukuran wajar. Jika kunci kurang dari atau sama dengan 32 bit, itu bisa menjadi hashnya sendiri, yang berarti tabrakan di ruang 32-bit penuh tidak mungkin terjadi. Jika lebih besar, Anda tidak bisa memasukkan cukup banyak ke dalam ruang alamat memori 32-bit karena itu menjadi masalah. Saya berasumsi hash_t akan ditingkatkan menjadi 64 bit dalam implementasi 64-bit D, di mana kumpulan data bisa lebih besar. Selain itu, jika ini terbukti menjadi masalah, seseorang dapat mengubah fungsi hash di setiap tingkat rekursi.
Berikut implementasi dalam bahasa pemrograman D:
void uniqueInPlace(T)(ref T[] dataIn) {
uniqueInPlaceImpl(dataIn, 0);
}
void uniqueInPlaceImpl(T)(ref T[] dataIn, size_t start) {
if(dataIn.length - start < 2)
return;
invariant T sentinel = dataIn[start];
T[] data = dataIn[start + 1..$];
static hash_t getHash(T elem) {
static if(is(T == uint) || is(T == int)) {
return cast(hash_t) elem;
} else static if(__traits(compiles, elem.toHash)) {
return elem.toHash;
} else {
static auto ti = typeid(typeof(elem));
return ti.getHash(&elem);
}
}
for(size_t index = 0; index < data.length;) {
if(data[index] == sentinel) {
index++;
continue;
}
auto hash = getHash(data[index]) % data.length;
if(index == hash) {
index++;
continue;
}
if(data[index] == data[hash]) {
data[index] = sentinel;
index++;
continue;
}
if(data[hash] == sentinel) {
swap(data[hash], data[index]);
index++;
continue;
}
auto hashHash = getHash(data[hash]) % data.length;
if(hashHash != hash) {
swap(data[index], data[hash]);
if(hash < index)
index++;
} else {
index++;
}
}
size_t swapPos = 0;
foreach(i; 0..data.length) {
if(data[i] != sentinel && i == getHash(data[i]) % data.length) {
swap(data[i], data[swapPos++]);
}
}
size_t sentinelPos = data.length;
for(size_t i = swapPos; i < sentinelPos;) {
if(data[i] == sentinel) {
swap(data[i], data[--sentinelPos]);
} else {
i++;
}
}
dataIn = dataIn[0..sentinelPos + start + 1];
uniqueInPlaceImpl(dataIn, start + swapPos + 1);
}