एल्गोरिथ्म: एक सरणी से डुप्लिकेट पूर्णांक को हटाने का कुशल तरीका


92

मुझे यह समस्या Microsoft के साथ एक साक्षात्कार से मिली।

यादृच्छिक पूर्णांक की एक सरणी को देखते हुए, C में एक एल्गोरिथ्म लिखें जो डुप्लिकेट किए गए नंबरों को हटाता है और मूल सरणी में अद्वितीय संख्याओं को वापस करता है।

जैसे इनपुट: {4, 8, 4, 1, 1, 2, 9} आउटपुट:{4, 8, 1, 2, 9, ?, ?}

एक चेतावनी यह है कि अपेक्षित एल्गोरिथ्म को पहले सॉर्ट करने के लिए सरणी की आवश्यकता नहीं होनी चाहिए। और जब एक तत्व को हटा दिया गया है, तो निम्नलिखित तत्वों को आगे भी स्थानांतरित किया जाना चाहिए। वैसे भी, सरणी के टेल पर तत्वों का मूल्य जहां तत्वों को आगे स्थानांतरित किया गया था, वे नगण्य हैं।

अद्यतन: परिणाम मूल सरणी में वापस आ जाना चाहिए और सहायक डेटा संरचना (जैसे हैशटेबल) का उपयोग नहीं किया जाना चाहिए। हालांकि, मुझे लगता है कि आदेश संरक्षण आवश्यक नहीं है।

Update2: उन लोगों के लिए जो आश्चर्यचकित करते हैं कि ये अव्यवहारिक बाधाएं क्यों हैं, यह एक साक्षात्कार प्रश्न था और इन सभी बाधाओं पर विचार प्रक्रिया के दौरान चर्चा की जाती है कि मैं विभिन्न विचारों के साथ कैसे आ सकता हूं।


4
क्या आपको अद्वितीय संख्याओं के क्रम को संरक्षित करना है?
डगलस लीडर

1
क्या परिणाम मूल सरणी में लौटाया जाना है?
डगलस लीडर

1
मैंने सवाल अपडेट किया है। परिणाम मूल सरणी में वापस आ जाना चाहिए। हालांकि, अनुक्रम का क्रम कोई फर्क नहीं पड़ता।
ईजेल

3
यह बहुत कष्टप्रद होता है जब कोई व्यक्ति प्रश्न और अन्य उत्तरों पर अपना उत्तर देता है। बस धैर्य रखें, लोग वहां पहुंचेंगे।
GManNickG

2
हैशटेबल की अनुमति क्यों नहीं है? उस प्रतिबंध का कोई मतलब नहीं है।
RBarryYoung

जवाबों:


19

कैसा रहेगा:

void rmdup(int *array, int length)
{
    int *current , *end = array + length - 1;

    for ( current = array + 1; array < end; array++, current = array + 1 )
    {
        while ( current <= end )
        {
            if ( *current == *array )
            {
                *current = *end--;
            }
            else
            {
                current++;
            }
        }
    }
}

O (n ^ 2) या उससे कम होना चाहिए।


3
यह सरल समाधान है और संभावना से अधिक है कि साक्षात्कार प्रश्न क्या देख रहा है।
कर्क ब्रॉडहर्स्ट

7
वे यह देखने के लिए भी जाँच कर रहे होंगे कि आप समय से पहले अनुकूलन में लिप्त होने से पीड़ित नहीं हैं, जब तक कि उन्होंने आपको रनटाइम की कमी न दी हो! :-)
ट्रेवर टिपिन्स

16
योग्य, हालांकि यह निश्चित रूप से सरणी को क्रमबद्ध करने और क्रमबद्ध एक पर काम करने के लिए तेज़ है। सॉर्टिंग एक एपीआई द्वारा प्रदान की जानी चाहिए और कोई समय से पहले अनुकूलन नहीं है।
जिग्गीस्टार

2
जबकि (वर्तमान <अंत) के बजाय यह (वर्तमान <= अंत) नहीं होना चाहिए?
शैल

2
इसे सही उत्तर के रूप में क्यों स्वीकार किया गया? यदि आदेश संरक्षण आवश्यक नहीं है, तो क्या केवल मर्ज सॉर्ट O (nlogn) का उपयोग करना बेहतर नहीं है और फिर O (n) में दोहराया तत्वों को हटा दें ... कुल जटिलता - O (nlogn) जो इस समाधान से बहुत बेहतर है।
पवन

136

मेरी प्रेमिका द्वारा सुझाए गए एक समाधान में मर्ज की विविधता है। एकमात्र संशोधन यह है कि मर्ज चरण के दौरान, केवल डुप्लिकेट मानों की उपेक्षा करें। यह समाधान ओ के रूप में अच्छी तरह से (एन लॉग एन) होगा। इस दृष्टिकोण में, छँटाई / दोहराव हटाने को एक साथ जोड़ दिया जाता है। हालाँकि, मुझे यकीन नहीं है कि कोई फर्क पड़ता है, हालांकि।


8
महान सुझाव, लेकिन आपको प्रत्येक मर्ज आउटपुट के अंत पर नज़र रखने के लिए कुछ बहीखाता की आवश्यकता होगी। मैंने वास्तव में एक बार ऐसा किया था, और हाँ डुप्लिकेट को समाप्त करते हुए जैसे ही आप विलय करते हैं, यह बहुत तेज़ हो जाता है।
मार्क रैनसम

2
यह स्पष्ट नहीं है कि प्रश्न में ओ (एन / 2) अतिरिक्त स्थान "हेल्पर डेटा संरचना" के रूप में गिना जाता है - मुझे नहीं पता कि प्रतिबंध ओ (1) अतिरिक्त स्थान को निर्धारित करने का इरादा है, या सिर्फ यह निर्धारित करने के लिए कि उत्तर एक बड़े ol 'डेटा संरचना कार्यान्वयन पर निर्भर नहीं होना चाहिए। शायद एक मानक मर्ज ठीक है। लेकिन यदि नहीं, तो शीर्ष टिप: किसी इंटरव्यू में इन-प्लेस मर्ज सॉर्ट लिखने का प्रयास न करें, जब तक कि आप वास्तव में नहीं जानते कि आप क्या कर रहे हैं।
22:17 पर स्टीव जेसप

महान विचार। लेकिन इसके लिए आवश्यक है कि शेष डेटा मूल क्रम बनाए रखे।
हार्डी फेंग

4
एक पेपर जो बताता है कि आपकी प्रेमिका ने क्या सुझाव दिया है: dc-pubs.dbs.uni-leipzig.de/files/…
माइक बी

49

मैंने इसे एक बार पहले एसओ पर पोस्ट किया है, लेकिन मैं इसे यहां फिर से तैयार करूंगा क्योंकि यह बहुत अच्छा है। यह हैशिंग का उपयोग करता है, जिसके स्थान पर हैश सेट जैसा कुछ होता है। यह एक्सिलरी स्पेस में O (1) होने की गारंटी है (रिकर्सियन एक टेल कॉल है), और आमतौर पर O (N) टाइम जटिलता है। एल्गोरिथ्म इस प्रकार है:

  1. सरणी का पहला तत्व लें, यह प्रहरी होगा।
  2. बाकी सरणी को पुन: व्यवस्थित करें, जितना संभव हो, जैसे कि प्रत्येक तत्व अपने हैश के अनुरूप स्थिति में है। जैसे ही यह कदम पूरा हो जाएगा, डुप्लिकेट की खोज की जाएगी। उन्हें प्रहरी के बराबर सेट करें।
  3. उन सभी तत्वों को स्थानांतरित करें जिनके लिए सूचकांक हैश के बराबर है सरणी की शुरुआत के लिए।
  4. सरणी के पहले तत्व को छोड़कर, प्रहरी के बराबर सभी तत्वों को स्थानांतरित करें, सरणी के अंत तक।
  5. ठीक से हैशेड तत्वों के बीच क्या बचा है और डुप्लिकेट तत्व ऐसे तत्व होंगे जो टकराव के कारण उनके हैश के अनुरूप सूचकांक में नहीं रखे जा सकते हैं। इन तत्वों से निपटने के लिए पुनर्खरीद करें।

यह ओ (एन) दिखाया जा सकता है बशर्ते कि हैशिंग में कोई रोग संबंधी परिदृश्य न हो: भले ही कोई डुप्लिकेट न हो, प्रत्येक पुनरावर्तन पर लगभग 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);
}

1
बेहद ठंडा, अंडररेटेड जवाब! मैं एक प्रहरी मूल्य के रूप में स्थिति 1 में तत्व का उपयोग करने का विचार पसंद करता हूं। अगर मैं कुछ छोटे सुझाव दे सकता हूं, तो चरण 2 को बदलना होगा "प्रत्येक तत्व अपने हैश मोडुलो द ऐरे साइज के अनुरूप स्थिति में" शामिल करने के लिए , और शायद यह स्पष्ट करें कि डुप्लिकेट प्रहरी के लिए सेट किए जा रहे हैं ऐसे तत्व जिनका मान समान है (जैसा कि एक ही हैश, या समान हैश मोडुलो सरणी आकार के विपरीत)।
j_random_hacker 5

20

एक और कुशल कार्यान्वयन

int i, j;

/* new length of modified array */
int NewLength = 1;

for(i=1; i< Length; i++){

   for(j=0; j< NewLength ; j++)
   {

      if(array[i] == array[j])
      break;
   }

   /* if none of the values in index[0..j] of array is not same as array[i],
      then copy the current value to corresponding new position in array */

  if (j==NewLength )
      array[NewLength++] = array[i];
}

इस कार्यान्वयन में सरणी को छांटने की कोई आवश्यकता नहीं है। इसके अलावा यदि कोई डुप्लिकेट तत्व पाया जाता है, तो इसके बाद सभी तत्वों को एक स्थान पर स्थानांतरित करने की कोई आवश्यकता नहीं है।

इस कोड का आउटपुट सरणी [] आकार के साथ NewLength है

यहां हम एरे में 2 एलएमटी से शुरू कर रहे हैं और एरे के सभी तत्वों की तुलना इस एरे तक कर रहे हैं। हम इनपुट ऐरे को संशोधित करने के लिए एक अतिरिक्त इंडेक्स वेरिएबल 'न्यूलेंगल ’धारण कर रहे हैं। NewLength वैरिएबल 0 से शुरू होती है।

सरणी में तत्व [1] की तुलना सरणी [0] से की जाएगी। यदि वे अलग-अलग हैं, तो सरणी में मूल्य [NewLength] सरणी [1] और वेतन वृद्धि NewLength के साथ संशोधित किया जाएगा। यदि वे समान हैं, तो NewLength संशोधित नहीं होगी।

इसलिए अगर हमारे पास एक सरणी है [1 2 1 3 3 1], तो

'J' लूप के पहले पास में, array [1] (2) की तुलना array0 के साथ की जाएगी, फिर 2 को array [NewLength] = array [1] को लिखा जाएगा, इसलिए newLength = 2 के बाद से array [1 2] हो जाएगी।

'J' लूप के दूसरे पास में, array [2] (1) को array0 और array1 के साथ तुलना की जाएगी। यहाँ के बाद से सरणी [2] (1) और array0 एक ही लूप हैं जो यहाँ टूट जाएगा। इसलिए सरणी NewLength = 2 के बाद से [1 2] होगी

और इसी तरह


3
अच्छा है। मुझे सुधार करने का सुझाव है। दूसरा नेस्टेड लूप के लिए बदला जा सकता है (जे = 0; j <NewLength; J ++) और पिछले है, तो जाँच बदला जा सकता है, तो (जे == NewLength) के लिए
Vadakkumpadath

यह एक महान शर्करा था। मैंने उर टिप्‍पणी के आधार पर कोड को अपडेट किया है
बायजू

कम से कम यदि हम सरणी {1,1,1,1,1,1} में समान मान रखते हैं, तो विफल। बेकार कोड।
यूरी चेर्नशोव

वैसे इस की जटिलता क्या है, क्या यह O (n ^ 2) भी नहीं है?
JavaSa

1
इतने सारे अपवोट, लेकिन यह कुशल नहीं है: यह O (n ^ 2) है जब कुछ डुप्लिकेट हैं।
पॉल हैंकिन

19

यदि आप बेहतर O- नोटेशन की तलाश कर रहे हैं, तो O (n log n) सॉर्ट के साथ ऐरे को सॉर्ट करना है तो O (n) ट्रैवर्सल करना सबसे अच्छा रूट हो सकता है। सॉर्ट किए बिना, आप O (n ^ 2) देख रहे हैं।

संपादित करें: यदि आप केवल पूर्णांक कर रहे हैं, तो आप O (n) प्राप्त करने के लिए मूलांक भी कर सकते हैं।


जेफ बी का जवाब ओ (एन) है। हैश-सेट और हैश-शब्दकोश मधुमक्खियों के घुटने हैं।
17W पर क्रिस ऑक्ट

3
क्रिस: हैश सेट / शब्दकोश केवल ओ (1) हैं यदि आप कोई टकराव मानते हैं। (मैं यह नहीं कह रहा हूं कि मैं उन्हें इस समस्या के लिए उपयोग नहीं करूंगा - मैं शायद यह कहूंगा - यह दावा करने के लिए कि वे वास्तव में ओ (1) हैं।)
लॉरेंस गोंसाल्वेस

2
दरअसल, जब से आपको हाथ से पहले सरणी का आकार पता है, तो आप O (1) की गारंटी दे सकते हैं। फिर आप टकरावों का व्यापार कर सकते हैं। आप कितनी अतिरिक्त मेमोरी का उपयोग करते हैं।
विटाली

आप उस डाउनवोट को पुनर्विचार करना चाहते हैं - समस्या के लिए नई पोस्ट की गई शर्तें जेफ बी के समाधान को अमान्य बना देती हैं।
मार्क रैनसम

3
आप "ट्रैवर्सल" के बारे में विस्तार से जानना चाहते हैं, क्योंकि एक भोली मिटाने की विधि के परिणामस्वरूप बड़ी संख्या में डुप्लिकेट के लिए ओ (एन ^ 2) हो सकता है।
मार्क रैनसम

11

1. O (n लॉग एन) समय में O (1) अतिरिक्त स्थान का उपयोग करना

यह संभव है, उदाहरण के लिए:

  • पहले एक जगह O (n लॉग एन) सॉर्ट करें
  • फिर सूची के माध्यम से एक बार चलें, सूची की शुरुआत में हर पीठ का पहला उदाहरण लिखें

मेरा मानना ​​है कि ईजेल का पार्टनर सही है कि ऐसा करने का सबसे अच्छा तरीका एक सरल मर्ज के साथ एक इन-प्लेस मर्ज सॉर्ट होगा, और यदि आप उदाहरण के लिए, तो शायद यह सवाल का आशय है। इनपुट्स को बेहतर बनाने की क्षमता के साथ इसे यथासंभव कुशलतापूर्वक करने के लिए एक नया लाइब्रेरी फ़ंक्शन लिखना, और ऐसे मामले होंगे जो इनपुट के प्रकार के आधार पर हैश-टेबल के बिना ऐसा करना उपयोगी होगा। लेकिन मैंने वास्तव में इसकी जाँच नहीं की है।

2. O (n) समय में O (बहुत) अतिरिक्त स्थान का उपयोग करना

  • सभी पूर्णांकों को धारण करने के लिए शून्य शून्य सरणी को बड़ा घोषित करें
  • एक बार सरणी के माध्यम से चलो
  • प्रत्येक पूर्णांक के लिए संबंधित सरणी तत्व को 1 पर सेट करें।
  • यदि यह पहले से ही 1 था, तो उस पूर्णांक को छोड़ दें।

यह केवल तभी काम करता है जब कई प्रश्न मान्य होते हैं:

  • स्मृति को सस्ते में शून्य करना संभव है, या उन की संख्या की तुलना में चींटियों का आकार छोटा है
  • आप अपने OS को 256 ^ sizepof (int) मेमोरी के लिए पूछकर खुश हैं
  • और यह आपके लिए यह वास्तव में कुशलता से कैश करेगा यदि यह विशाल है

यह एक बुरा जवाब है, लेकिन अगर आपके पास इनपुट तत्वों के बहुत सारे हैं, लेकिन वे सभी 8-बिट पूर्णांक हैं (या शायद 16-बिट पूर्णांक) तो यह सबसे अच्छा तरीका हो सकता है।

3. O (थोड़ा) -ish अतिरिक्त स्थान, O (n) -ish समय

# 2 के रूप में, लेकिन एक हैश तालिका का उपयोग करें।

4. स्पष्ट रास्ता

यदि तत्वों की संख्या छोटी है, तो एक उपयुक्त एल्गोरिथ्म लिखना उपयोगी नहीं है यदि अन्य कोड लिखने के लिए तेज है और पढ़ने के लिए तेज है।

उदाहरण के लिए। प्रत्येक अद्वितीय तत्वों को हटाने वाले प्रत्येक अद्वितीय तत्वों (यानी पहला तत्व, दूसरा तत्व (पहले वाले को हटा दिया गया है आदि)) के लिए सरणी के माध्यम से चलो। O (1) अतिरिक्त स्थान, O (n ^ 2) समय।

उदाहरण के लिए। ऐसा करने वाले पुस्तकालय कार्यों का उपयोग करें। दक्षता निर्भर करती है जो आपने आसानी से उपलब्ध कर ली है।


7

खैर, यह बुनियादी कार्यान्वयन काफी सरल है। सभी तत्वों के माध्यम से जाएं, जांचें कि क्या शेष लोगों में डुप्लिकेट हैं और बाकी को उन पर स्थानांतरित कर दें।

यह भयानक अक्षम्य है और आप इसे आउटपुट या सॉर्टिंग / बाइनरी पेड़ों के लिए सहायक-सरणी द्वारा गति दे सकते हैं, लेकिन इसकी अनुमति नहीं लगती है।


1
OTOH, एक सॉर्टिंग ट्री को लागू करने के लिए आवश्यक अतिरिक्त कोड सरल समाधान की तुलना में कम (मेमोरी) कुशल हो सकता है, और शायद छोटे (100 से कम तत्वों) सरणियों के लिए रन-टाइम पर कम कुशल है।
टीएमएन

6

यदि आपको C ++ का उपयोग करने की अनुमति है, तो std::sortकॉल करने के std::uniqueलिए आपको जवाब देने के लिए कॉल किया जाएगा। समय जटिलता ओ और (एन) एन के लिए सॉर्ट और ओ (एन) अद्वितीय ट्रैवर्सल के लिए है।

और अगर C ++ टेबल से दूर है, तो ऐसा कुछ भी नहीं है जो इन समान एल्गोरिदम को C में लिखे जाने से बचाए रखता है।


"एक चेतावनी यह है कि अपेक्षित एल्गोरिथ्म को पहले सॉर्ट करने के लिए सरणी की आवश्यकता नहीं होनी चाहिए।"
sbi

2
यह नहीं है कि आप इसे प्राप्त करने के बाद सरणी को सॉर्ट नहीं कर सकते ... ओ (एन) का उपयोग किए बिना बाहरी मेमोरी सॉर्टिंग इसे ओ (एन लॉग एन) या बेहतर करने का एकमात्र तरीका है।
ग्रेग रोजर्स

समस्या के उद्देश्य के लिए, मानक पुस्तकालय के बर्तनों का उपयोग नहीं किया जाना चाहिए। सॉर्टिंग के बारे में, हालांकि, जितना अधिक मैं इसके बारे में सोचता हूं, उतना ही अनिश्चित होता हूं कि यह ठीक है या नहीं।
ईजेल

1
मुझे लगता है कि C ++ और C ++ मानक कार्यों के संदर्भ में उत्तर उपयोगी होते हैं, भले ही वे मूल प्रश्न का उत्तर न दें, क्योंकि वे उन लोगों को अधिक गोल उत्तर प्रदान करते हैं जो इस प्रश्न को बाद में पाते हैं।
डगलस लीडर

6

यदि आप स्मृति का त्याग करने के लिए तैयार हैं, तो आप इसे एकल ट्रावेल में कर सकते हैं। आप बस मिलान कर सकते हैं कि आपने हैश / एसोसिएटिव ऐरे में पूर्णांक देखा है या नहीं। यदि आपने पहले ही कोई संख्या देख ली है, तो जाते समय इसे हटा दें, या बेहतर अभी तक, उन संख्याओं को स्थानांतरित करें जिन्हें आपने नए सरणी में नहीं देखा है, मूल सरणी में किसी भी स्थानांतरण से बचें।

पर्ल में:

foreach $i (@myary) {
    if(!defined $seen{$i}) {
        $seen{$i} = 1;
        push @newary, $i;
    }
}

यह स्पष्ट नहीं है कि उत्तर मूल सरणी में होना है।
डगलस लीडर

एक नए सरणी की आवश्यकता के बिना ऐसा करने के लिए, आप बस डुप्लिकेट को एक तत्व के साथ बदल सकते हैं, जो सरणी के अंत में पॉपअप होता है, और वर्तमान लूप को फिर से करता है, क्योंकि समस्या उस आदेश को निर्दिष्ट नहीं करती है। इसके लिए कुछ अतिरिक्त सीमा की जाँच की आवश्यकता होती है, लेकिन यह बहुत सक्षम है।
जेफ बी

6
यह एक अच्छा विचार था, जब तक प्रश्न को संपादित नहीं किया गया था। आपका हैशटेबल विचार स्पष्ट रूप से नियमों के विरुद्ध है।
WCWedin

14
मुझे यह नहीं सूझा कि इस जवाब को सबसे ज्यादा वोट क्यों मिले। यह पर्ल में लिखा गया है और सी में उपलब्ध महत्वपूर्ण विशेषताओं का उपयोग नहीं करता है, जैसा कि सवाल पूछता है।
लीरनूना

5
सी कोड के लिए पूछा गया प्रश्न, पर्ल नहीं। पर्ल का उपयोग करने पर आपको हैशटेबल्स और "पुश" मुफ्त में मिलते हैं। अगर मैं इसे scala में कर सकता हूं, तो आप बस input.removeDuplicates कहेंगे, लेकिन मुझे संदेह है कि साक्षात्कारकर्ताओं के लिए स्वीकार्य होगा :)
पीटर रेकोर

5

फ़ंक्शन का रिटर्न मान अद्वितीय तत्वों की संख्या होना चाहिए और वे सभी सरणी के सामने संग्रहीत हैं। इस अतिरिक्त जानकारी के बिना, आपको यह भी पता नहीं चलेगा कि क्या कोई डुप्लिकेट था।

बाहरी लूप का प्रत्येक पुनरावृत्ति सरणी के एक तत्व को संसाधित करता है। यदि यह अद्वितीय है, तो यह सरणी के सामने रहता है और यदि यह एक डुप्लिकेट है, तो यह सरणी में अंतिम असंसाधित तत्व द्वारा अधिलेखित है। यह समाधान O (n ^ 2) समय में चलता है।

#include <stdio.h>
#include <stdlib.h>

size_t rmdup(int *arr, size_t len)
{
  size_t prev = 0;
  size_t curr = 1;
  size_t last = len - 1;
  while (curr <= last) {
    for (prev = 0; prev < curr && arr[curr] != arr[prev]; ++prev);
    if (prev == curr) {
      ++curr;
    } else {
      arr[curr] = arr[last];
      --last;
    }
  }
  return curr;
}

void print_array(int *arr, size_t len)
{
  printf("{");
  size_t curr = 0;
  for (curr = 0; curr < len; ++curr) {
    if (curr > 0) printf(", ");
    printf("%d", arr[curr]);
  }
  printf("}");
}

int main()
{
  int arr[] = {4, 8, 4, 1, 1, 2, 9};
  printf("Before: ");
  size_t len = sizeof (arr) / sizeof (arr[0]);
  print_array(arr, len);
  len = rmdup(arr, len);
  printf("\nAfter: ");
  print_array(arr, len);
  printf("\n");
  return 0;
}

4

यहाँ एक जावा संस्करण है।

int[] removeDuplicate(int[] input){

        int arrayLen = input.length;
        for(int i=0;i<arrayLen;i++){
            for(int j = i+1; j< arrayLen ; j++){
                if(((input[i]^input[j]) == 0)){
                    input[j] = 0;
                }
                if((input[j]==0) && j<arrayLen-1){
                        input[j] = input[j+1];
                        input[j+1] = 0;
                    }               
            }
        }       
        return input;       
    }

कम से कम अगले इनपुट के साथ: {1,1,1,1,1,1,1} {0,0,0,0,0,1,1,1,1,1,1}
यूरी

3

यहाँ मेरा समाधान है।

///// find duplicates in an array and remove them

void unique(int* input, int n)
{
     merge_sort(input, 0, n) ;

     int prev = 0  ;

     for(int i = 1 ; i < n ; i++)
     {
          if(input[i] != input[prev])
               if(prev < i-1)
                   input[prev++] = input[i] ;                         
     }
}

2

एक सरणी को स्पष्ट रूप से "ट्रैवर्सेड" दाएं-से-बाएं होना चाहिए ताकि आगे और पीछे के मूल्यों की अनावश्यक नकल से बचा जा सके।

यदि आपके पास असीमित मेमोरी है, तो आप sizeof(type-of-element-in-array) / 8बाइट्स के लिए एक बिट ऐरे को आबंटित कर सकते हैं, प्रत्येक बिट को सूचित करने के लिए कि क्या आप पहले से ही संबंधित मूल्य का सामना कर चुके हैं या नहीं।

यदि आप नहीं करते हैं, तो मैं किसी सरणी को ट्रेस करने और मानों के साथ प्रत्येक मान की तुलना करने से बेहतर कुछ भी नहीं सोच सकता हूं और फिर यदि डुप्लिकेट पाया जाता है, तो इन मानों को पूरी तरह से हटा दें। यह कहीं O (n ^ 2) (या O ((n ^ 2-n) / 2) ) के पास है।

आईबीएम का थोड़े करीबी विषय पर एक लेख है


वास्तव में - सबसे बड़ा तत्व खोजने के लिए एक O (n) पास समग्र O () लागत में वृद्धि नहीं करेगा।
डगलस लीडर

2

चलो देखते हैं:

  • O (N) मिनट / अधिकतम आवंटित करने के लिए पास करें
  • पाया के लिए बिट सरणी
  • O (N) डुप्लिकेट को समाप्त करने के लिए स्वैपिंग पास।

यह देखते हुए कि वे केवल पूर्णांक हैं, सादगी के लिए आप 32 बिट मान सकते हैं और न्यूनतम / अधिकतम की तलाश में परेशान नहीं हो सकते हैं: 2 ^ 32 बिट्स "केवल" 512MB है, इसलिए सीमा ढूंढना केवल एक स्मृति-उपयोग है और O (1) समय अनुकूलन (दी गई, दिए गए उदाहरण के मामले में भारी अनुकूलन)। और अगर वे 64 बिट हैं, तो यह अप्रासंगिक है क्योंकि आप नहीं जानते कि स्मृति और बिट्स की संख्या के अलावा मिनट और अधिकतम आगे नहीं होंगे।
स्टीव जेसप

एक तरफ थ्योरी, 512MB को आवंटित करने में मिनट / अधिकतम खोजने में अधिक समय नहीं लगेगा?
LiraNuna

निर्भर करता है कि कितना डेटा है, और न्यूनतम / अधिकतम क्या है। यदि आप 512MB से अधिक इनपुट देख रहे हैं, तो संभवतः यह अतिरिक्त O (N) पास से बचने के लिए तेज़ है। बेशक, यदि आप उस बहुत इनपुट को देख रहे हैं, तो इसकी संभावना कम है कि आपके पास 512MB अतिरिक्त है। ऐसे मामलों में जहां न्यूनतम / अधिकतम 0 / INT_MAX के करीब है, तो अनुकूलन भी मदद नहीं करता है। मैं सिर्फ इतना कह रहा हूं कि हालांकि पहला कदम स्पष्ट रूप से छोटी संख्या के लिए मदद करता है, यह इस तथ्य से नहीं बच सकता है कि यह एल्गोरिथ्म सबसे खराब स्थिति में UINT_MAX बिट्स का उपयोग करता है, इसलिए आपको उस सीमा के लिए योजना बनाने की आवश्यकता है।
स्टीव जेसोप

आप अच्छी तरह से सही हो सकते हैं - किसी भी मामले में प्रश्न के स्पष्टीकरण का मतलब है कि बिट-सरणी का उपयोग करना बाहर है। यदि कोई व्यक्ति बाद में बाधाओं के बिना आता है और सभी संभावित उत्तरों को देखना चाहता है, तो मैं इस उत्तर को छोड़ दूंगा।
डगलस लीडर

2

यह एक पास में ओ (एन लॉग एन) एल्गोरिथ्म और कोई अतिरिक्त भंडारण के साथ किया जा सकता है।

तत्व से आगे बढ़ें a[1]करने के लिए a[N]। प्रत्येक स्तर पर i, तत्वों के बाईं ओर सभी तत्वों के a[i]एक प्रकार के ढेर को समाहित a[0]करते हैं a[j]। इस बीच, एक दूसरा सूचकांक j, शुरू में 0, ढेर के आकार का ट्रैक रखता है।

जांच करें a[i]और इसे ढेर में डालें, जो अब तत्वों a[0]को रखता है a[j+1]। जैसा कि तत्व डाला गया है, अगर एक डुप्लिकेट तत्व a[k]समान मूल्य का सामना करना पड़ रहा है, तो a[i]ढेर में सम्मिलित न करें (यानी, इसे छोड़ दें); अन्यथा इसे ढेर में डालें, जो अब एक तत्व से बढ़ता है और अब इसमें शामिल a[0]है a[j+1], और वृद्धि j

इस प्रकार से जारी रखें, iतब तक वृद्धि करें जब तक कि सभी एलीमेंट तत्वों की जांच न कर ली जाए और उन्हें ढेर में न डाल दिया जाए, जो कब्जे में समाप्त हो a[0]जाते हैं a[j]jढेर के अंतिम तत्व का सूचकांक है, और हीप में केवल अद्वितीय तत्व मूल्य शामिल हैं।

int algorithm(int[] a, int n)
{
    int   i, j;  

    for (j = 0, i = 1;  i < n;  i++)
    {
        // Insert a[i] into the heap a[0...j]
        if (heapInsert(a, j, a[i]))
            j++;
    }
    return j;
}  

bool heapInsert(a[], int n, int val)
{
    // Insert val into heap a[0...n]
    ...code omitted for brevity...
    if (duplicate element a[k] == val)
        return false;
    a[k] = val;
    return true;
}

उदाहरण को देखते हुए, यह बिल्कुल वैसा नहीं है जैसा कि पूछा गया था क्योंकि परिणामी सरणी मूल तत्व क्रम को बरकरार रखती है। लेकिन अगर यह आवश्यकता शिथिल है, तो ऊपर दिए गए एल्गोरिदम को चाल चलनी चाहिए।


1

जावा में मैं इसे इस तरह से हल करूंगा। पता नहीं कैसे सी में यह लिखने के लिए।

   int length = array.length;
   for (int i = 0; i < length; i++) 
   {
      for (int j = i + 1; j < length; j++) 
      {
         if (array[i] == array[j]) 
         {
            int k, j;
            for (k = j + 1, l = j; k < length; k++, l++) 
            {
               if (array[k] != array[i]) 
               {
                  array[l] = array[k];
               }
               else
               {
                  l--;
               }
            }
            length = l;
         }
      }
   }

यदि आप उस डुप्लिकेट को अधिलेखित कर लेते हैं जो आपको सरणी के अंत में मूल्य के साथ मिलता है तो आप अपने () लूप के लिए अपने आंतरिक में पूरे सरणी के स्थानांतरण से बच सकते हैं। जो आपको O (n ^ 3) से O (n ^ 2) लाएगा। मेरा C क्रियान्वयन कहीं इधर-उधर हो रहा है ...
mocj

मैंने सोचा था, स्थानांतरण आवश्यकता का हिस्सा था, लेकिन आप निश्चित रूप से सही हैं।
डोमिनिक

1
@ मोकज: मुझे आपका समाधान पसंद है, बहुत सुंदर लग रहा है। लेकिन मुझे लगता है कि यदि अंतिम दो तत्व बराबर हैं, तो यह काम नहीं करता है, क्योंकि आप अंतिम से पहले समानता की जांच करना बंद कर देते हैं। (यहाँ पर आकर, क्योंकि कहीं और भी टिप्पणी करने के लिए प्रतिष्ठा देखी गई है :()
डोमिनिकन

आप सही हैं कि मूल समस्या यह बताती है कि सरणी के अंत में मान नगण्य हैं। चूँकि आप संशोधित मान की लंबाई को अंतिम मान और दूसरे मान के बीच का अंतर नहीं लौटा रहे हैं, जब दोनों मान समान हैं। कॉलर ने लौटे सरणी के अंत की व्याख्या कहां की है
mocj

1

निम्नलिखित के बारे में कैसे?

int* temp = malloc(sizeof(int)*len);
int count = 0;
int x =0;
int y =0;
for(x=0;x<len;x++)
{
    for(y=0;y<count;y++)
    {
        if(*(temp+y)==*(array+x))
        {
            break;
        }
    }
    if(y==count)
    {
        *(temp+count) = *(array+x);
        count++;
    }
}
memcpy(array, temp, sizeof(int)*len);

मैं एक अस्थायी सरणी घोषित करने और मूल सरणी में वापस सब कुछ कॉपी करने से पहले तत्वों को उसमें डालने की कोशिश करता हूं।


1

समस्या की समीक्षा करने के बाद, यहाँ मेरा डेल्फी तरीका है, जो मदद कर सकता है

var
A: Array of Integer;
I,J,C,K, P: Integer;
begin
C:=10;
SetLength(A,10);
A[0]:=1; A[1]:=4; A[2]:=2; A[3]:=6; A[4]:=3; A[5]:=4;
A[6]:=3; A[7]:=4; A[8]:=2; A[9]:=5;

for I := 0 to C-1 do
begin
  for J := I+1 to C-1 do
    if A[I]=A[J] then
    begin
      for K := C-1 Downto J do
        if A[J]<>A[k] then
        begin
          P:=A[K];
          A[K]:=0;
          A[J]:=P;
          C:=K;
          break;
        end
        else
        begin
          A[K]:=0;
          C:=K;
        end;
    end;
end;

//tructate array
setlength(A,C);
end;

1

निम्नलिखित उदाहरण से आपकी समस्या का समाधान होना चाहिए:

def check_dump(x):
   if not x in t:
      t.append(x)
      return True

t=[]

output = filter(check_dump, input)

print(output)
True

1
import java.util.ArrayList;


public class C {

    public static void main(String[] args) {

        int arr[] = {2,5,5,5,9,11,11,23,34,34,34,45,45};

        ArrayList<Integer> arr1 = new ArrayList<Integer>();

        for(int i=0;i<arr.length-1;i++){

            if(arr[i] == arr[i+1]){
                arr[i] = 99999;
            }
        }

        for(int i=0;i<arr.length;i++){
            if(arr[i] != 99999){

                arr1.add(arr[i]);
            }
        }

        System.out.println(arr1);
}
    }

गिरफ्तारी [i + 1] अंतिम तत्व के लिए ArrayIndexOutOfBoundsException फेंकना चाहिए?
शतेश

@ सथेश नं। की वजह से "<arr.length -1"
गेब्रियलबी

1

यह भोला (एन * (एन -1) / 2) समाधान है। यह निरंतर अतिरिक्त स्थान का उपयोग करता है और मूल क्रम को बनाए रखता है। यह @ बायजू द्वारा समाधान के समान है, लेकिन कोई if(){}ब्लॉक का उपयोग नहीं करता है । यह अपने आप में एक तत्व की नकल करने से भी बचता है।

#include <stdio.h>
#include <stdlib.h>

int numbers[] = {4, 8, 4, 1, 1, 2, 9};
#define COUNT (sizeof numbers / sizeof numbers[0])

size_t undup_it(int array[], size_t len)
{
size_t src,dst;

  /* an array of size=1 cannot contain duplicate values */
if (len <2) return len; 
  /* an array of size>1 will cannot at least one unique value */
for (src=dst=1; src < len; src++) {
        size_t cur;
        for (cur=0; cur < dst; cur++ ) {
                if (array[cur] == array[src]) break;
                }
        if (cur != dst) continue; /* found a duplicate */

                /* array[src] must be new: add it to the list of non-duplicates */
        if (dst < src) array[dst] = array[src]; /* avoid copy-to-self */
        dst++;
        }
return dst; /* number of valid alements in new array */
}

void print_it(int array[], size_t len)
{
size_t idx;

for (idx=0; idx < len; idx++)  {
        printf("%c %d", (idx) ? ',' :'{' , array[idx] );
        }
printf("}\n" );
}

int main(void) {    
    size_t cnt = COUNT;

    printf("Before undup:" );    
    print_it(numbers, cnt);    

    cnt = undup_it(numbers,cnt);

    printf("After undup:" );    
    print_it(numbers, cnt);

    return 0;
}

0

यह इनपुट पास सूची में पूर्णांकों की संख्या में O (N) समय में, एक और पास में, और विशिष्ट पूर्णांकों की संख्या में O (N) भंडारण में किया जा सकता है।

दो बिंदुओं "dst" और "src" के साथ सामने से पीछे की सूची के माध्यम से चलो, पहले आइटम पर आरंभीकृत। "पूर्णांक देखे गए" की खाली हैश तालिका से प्रारंभ करें। अगर src में पूर्णांक हैश में मौजूद नहीं है, तो इसे dst और increment dst पर स्लॉट पर लिखें। पूर्णांक को src पर हैश में जोड़ें, फिर वृद्धि src। दोहराएँ जब तक src इनपुट सूची के अंत में गुजरता है।


2
मूल प्रश्न के संशोधन में, हैश टेबल की अनुमति नहीं है। हालाँकि, डुप्लिकेट की पहचान करने के बाद आपका दो पॉइंटर अप्रोच आउटपुट को कॉम्पैक्ट करने का एक अच्छा तरीका है।
मार्क रैनसम

0

सभी तत्वों को एक binary tree the disregards duplicates- में डालें O(nlog(n))। फिर उन सभी को एक ट्रावेलर करके सरणी में वापस निकालें - O(n)। मैं मान रहा हूँ कि आपको आदेश संरक्षण की आवश्यकता नहीं है।


0

हैशिंग के लिए ब्लूम फ़िल्टर का उपयोग करें। यह मेमोरी ओवरहेड को काफी कम कर देगा।


विस्तृत या एक संदर्भ प्रदान करने के लिए देखभाल?
dldnh

0

जावा में,

    Integer[] arrayInteger = {1,2,3,4,3,2,4,6,7,8,9,9,10};

    String value ="";

    for(Integer i:arrayInteger)
    {
        if(!value.contains(Integer.toString(i))){
            value +=Integer.toString(i)+",";
        }

    }

    String[] arraySplitToString = value.split(",");
    Integer[] arrayIntResult = new Integer[arraySplitToString.length];
    for(int i = 0 ; i < arraySplitToString.length ; i++){
        arrayIntResult[i] = Integer.parseInt(arraySplitToString[i]);
    }

आउटपुट: {१, २, ३, ४, ६, 2, 2, ९, १०}

आशा है कि यह मदद करेगा


1
इनपुट के साथ यह परीक्षण करेंarrayInteger = {100,10,1};
ब्लास्टफर्नस


0

सबसे पहले, आपको एक सरणी बनाना चाहिए check[n]जहाँ n उस सरणी के तत्वों की संख्या है जिसे आप डुप्लिकेट-मुक्त बनाना चाहते हैं और प्रत्येक तत्व का मान सेट करें (चेक सरणी का) 1 के बराबर। एक लूप का उपयोग करके सरणी के साथ सरणी का उपयोग करें डुप्लिकेट, कहते हैं कि इसका नाम है arr, और फॉर-लूप में यह लिखें:

{
    if (check[arr[i]] != 1) {
        arr[i] = 0;
    }
    else {
        check[arr[i]] = 0;
    }
}

इसके साथ, आप प्रत्येक डुप्लिकेट को शून्य के बराबर सेट करते हैं। तो केवल एक ही चीज़ बची है वह है arrसरणी को पीछे छोड़ना और वह सब कुछ छापना जो शून्य के बराबर नहीं है। आदेश रहता है और यह रैखिक समय (3 * n) लेता है।


प्रश्न अतिरिक्त डेटा संरचना का उपयोग करने की अनुमति नहीं देता है।
ejel

0

N तत्वों की एक सरणी को देखते हुए, O (nlogn) समय में सरणी से सभी डुप्लिकेट को निकालने के लिए एक एल्गोरिथ्म लिखें

Algorithm delete_duplicates (a[1....n])
//Remove duplicates from the given array 
//input parameters :a[1:n], an array of n elements.

{

temp[1:n]; //an array of n elements. 

temp[i]=a[i];for i=1 to n

 temp[i].value=a[i]

temp[i].key=i

 //based on 'value' sort the array temp.

//based on 'value' delete duplicate elements from temp.

//based on 'key' sort the array temp.//construct an array p using temp.

 p[i]=temp[i]value

  return p.

अन्य तत्वों में आउटपुट कुंजी में 'कुंजी' का उपयोग करके बनाए रखा जाता है। विचार करें कि लंबाई O (n) की है, कुंजी और मान पर सॉर्ट करने के लिए लिया गया समय O (nlogn) है। इसलिए सरणी से सभी डुप्लिकेट को हटाने का समय हे (nlogn) है।


सभी बोल्ड ग्लिफ़ के लिए, आपने क्या बनाया helper data structure (e.g. hashtable) should not be used?
ग्रेबियर

जरूरी नहीं। मैंने सिर्फ समझने के उद्देश्य से उन पर प्रकाश डाला।
शारजी मुज़म्मिल

0

यह वही है जो मुझे मिला है, हालांकि यह उस आदेश को गलत करता है जिसे हम आरोही या इसे ठीक करने के लिए नीचे ला सकते हैं।

#include <stdio.h>
int main(void){
int x,n,myvar=0;
printf("Enter a number: \t");
scanf("%d",&n);
int arr[n],changedarr[n];

for(x=0;x<n;x++){
    printf("Enter a number for array[%d]: ",x);
    scanf("%d",&arr[x]);
}
printf("\nOriginal Number in an array\n");
for(x=0;x<n;x++){
    printf("%d\t",arr[x]);
}

int i=0,j=0;
// printf("i\tj\tarr\tchanged\n");

for (int i = 0; i < n; i++)
{
    // printf("%d\t%d\t%d\t%d\n",i,j,arr[i],changedarr[i] );
    for (int j = 0; j <n; j++)
    {   
        if (i==j)
        {
            continue;

        }
        else if(arr[i]==arr[j]){
            changedarr[j]=0;

        }
        else{
            changedarr[i]=arr[i];

        }
    // printf("%d\t%d\t%d\t%d\n",i,j,arr[i],changedarr[i] );
    }
    myvar+=1;
}
// printf("\n\nmyvar=%d\n",myvar);
int count=0;
printf("\nThe unique items:\n");
for (int i = 0; i < myvar; i++)
{
        if(changedarr[i]!=0){
            count+=1;
            printf("%d\t",changedarr[i]);   
        }
}
    printf("\n");
}

-1

यदि आपके पास एक अच्छा DataStructure है जो जल्दी से बता सकता है कि क्या इसमें पूर्णांक है तो यह कितना अच्छा होगा। शायद किसी प्रकार का एक पेड़।

DataStructure elementsSeen = new DataStructure();
int elementsRemoved = 0;
for(int i=0;i<array.Length;i++){
  if(elementsSeen.Contains(array[i])
    elementsRemoved++;
  else
    array[i-elementsRemoved] = array[i];
}
array.Length = array.Length - elementsRemoved;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.