मैंने इसे एक बार पहले एसओ पर पोस्ट किया है, लेकिन मैं इसे यहां फिर से तैयार करूंगा क्योंकि यह बहुत अच्छा है। यह हैशिंग का उपयोग करता है, जिसके स्थान पर हैश सेट जैसा कुछ होता है। यह एक्सिलरी स्पेस में O (1) होने की गारंटी है (रिकर्सियन एक टेल कॉल है), और आमतौर पर O (N) टाइम जटिलता है। एल्गोरिथ्म इस प्रकार है:
- सरणी का पहला तत्व लें, यह प्रहरी होगा।
- बाकी सरणी को पुन: व्यवस्थित करें, जितना संभव हो, जैसे कि प्रत्येक तत्व अपने हैश के अनुरूप स्थिति में है। जैसे ही यह कदम पूरा हो जाएगा, डुप्लिकेट की खोज की जाएगी। उन्हें प्रहरी के बराबर सेट करें।
- उन सभी तत्वों को स्थानांतरित करें जिनके लिए सूचकांक हैश के बराबर है सरणी की शुरुआत के लिए।
- सरणी के पहले तत्व को छोड़कर, प्रहरी के बराबर सभी तत्वों को स्थानांतरित करें, सरणी के अंत तक।
- ठीक से हैशेड तत्वों के बीच क्या बचा है और डुप्लिकेट तत्व ऐसे तत्व होंगे जो टकराव के कारण उनके हैश के अनुरूप सूचकांक में नहीं रखे जा सकते हैं। इन तत्वों से निपटने के लिए पुनर्खरीद करें।
यह ओ (एन) दिखाया जा सकता है बशर्ते कि हैशिंग में कोई रोग संबंधी परिदृश्य न हो: भले ही कोई डुप्लिकेट न हो, प्रत्येक पुनरावर्तन पर लगभग 2/3 तत्व समाप्त हो जाएंगे। रिकर्सन का प्रत्येक स्तर O (n) है जहाँ छोटे n तत्वों की मात्रा शेष है। केवल समस्या यह है कि, व्यवहार में, यह एक त्वरित तरह से धीमी है जब कुछ डुप्लिकेट होते हैं, यानी बहुत सारे टकराव होते हैं। हालाँकि, जब भारी मात्रा में डुप्लिकेट होते हैं, तो यह आश्चर्यजनक रूप से तेज़ होता है।
संपादित करें: D के वर्तमान कार्यान्वयन में, hash_t 32 बिट्स है। इस एल्गोरिथ्म के बारे में सबकुछ मानता है कि पूर्ण 32-बिट स्पेस में कोई भी, हैश टक्कर होने पर बहुत कम होगा। हालांकि, मापांक अंतरिक्ष में अक्सर हो सकते हैं। हालांकि, किसी भी आकार के डेटा सेट के लिए यह धारणा सभी संभावना में सही होगी। यदि कुंजी 32 बिट्स से कम या बराबर है, तो यह अपना स्वयं का हैश हो सकता है, जिसका अर्थ है कि पूर्ण 32-बिट स्थान में टक्कर असंभव है। यदि यह बड़ा है, तो आप इसके लिए 32-बिट मेमोरी एड्रेस स्पेस में पर्याप्त रूप से फिट नहीं हो सकते हैं क्योंकि यह एक समस्या है। मेरा मानना है कि D के 64-बिट कार्यान्वयन में hash_t को 64 बिट तक बढ़ा दिया जाएगा, जहां डेटासेट बड़े हो सकते हैं। इसके अलावा, अगर यह कभी भी एक समस्या साबित हुई, तो कोई भी पुनरावृत्ति के प्रत्येक स्तर पर हैश फ़ंक्शन को बदल सकता है।
यहाँ डी प्रोग्रामिंग भाषा में एक कार्यान्वयन है:
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);
}