सूची में सबसे छोटा पूर्णांक ज्ञात कीजिए


87

एक दिलचस्प साक्षात्कार सवाल है कि मेरा एक सहयोगी का उपयोग करता है:

मान लीजिए कि आपको 64-बिट पूर्णांक के बिना एक बहुत लंबी, अनसुलझी सूची दी गई है। आप सूची में आने वाले सबसे छोटे गैर-नकारात्मक पूर्णांक कैसे पाएंगे ?

FOLLOW-UP: अब जब छँटाई द्वारा स्पष्ट समाधान प्रस्तावित किया गया है, तो क्या आप इसे O (n log n) से अधिक तेज़ कर सकते हैं?

FOLLOW-UP: आपके एल्गोरिथ्म को 1GB मेमोरी के साथ कंप्यूटर पर चलाना है

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


6
मुझे लगता है कि आप गैर-नकारात्मक भाग को छोड़ सकते हैं, यह देखकर कि आप एक अहस्ताक्षरित पूर्णांक के बारे में कैसे बात कर रहे हैं।
केवेनडेनन

4
प्रश्न बहुत बुनियादी है, जब तक कि मैं वाया-ऑफ-बेस, आईएमओ नहीं हूं, लेकिन, जैसा कि दूसरों ने उल्लेख किया है, पूछने के लिए प्रश्न हैं, या मान्यताओं को कहा जाना चाहिए।
जेम्स ब्लैक

8
@paxdiablo: यह एक ऐसा मामला है जहां ओ (n) कहने का मतलब यह नहीं है। भले ही आप ईस्टर द्वीप पर मिट्टी की गोलियों पर अपने 2 ^ 64 बिट सरणी स्टोर करते हैं और वाहक कबूतर द्वारा इसे एक्सेस करते हैं, एल्गोरिथ्म अभी भी ओ (एन) है।
IJ कैनेडी

6
स्मृति आवश्यकताओं को आधे रास्ते में बदलने से यह एक महान साक्षात्कार प्रश्न बन जाता है ;-)
क्रिस बैलेंस

1
मुझे लगता है कि यह मनोरंजक है कि सभी उत्तर एक ही सामान्य समाधान करते हैं (सरणी को क्रमबद्ध करें और अनुक्रम को तोड़ने वाले पहले मान को ढूंढें), लेकिन वे सभी एक अलग प्रकार का उपयोग करते हैं। (संशोधित क्विकॉर्ट्स, मूलांक सॉर्ट, ...) स्वीकृत उत्तर एक गिनती के प्रकार के बराबर है जो एन के ऊपर तत्वों को
त्यागता है

जवाबों:


121

यदि डाटास्ट्रक्चर जगह में उत्परिवर्तित हो सकता है और यादृच्छिक अभिगम का समर्थन करता है तो आप इसे O (N) समय और O (1) अतिरिक्त स्थान पर कर सकते हैं। क्रमिक रूप से सरणी के माध्यम से जाओ और प्रत्येक सूचकांक के लिए मूल्य द्वारा निर्दिष्ट सूचकांक पर मूल्य लिखें, फिर से उस स्थान पर किसी भी मूल्य को अपने स्थान पर रखने और मूल्यों को दूर फेंकने के लिए> एन। फिर मौके की तलाश में सरणी के माध्यम से फिर से जाएं। जहां मूल्य सूचकांक से मेल नहीं खाता है - वह सबसे छोटा मान है जो सरणी में नहीं है। यह अधिकांश 3N तुलनाओं में परिणाम देता है और केवल कुछ मानों का उपयोग करता है जो अस्थायी स्थान के लायक हैं।

# Pass 1, move every value to the position of its value
for cursor in range(N):
    target = array[cursor]
    while target < N and target != array[target]:
        new_target = array[target]
        array[target] = target
        target = new_target

# Pass 2, find first location where the index doesn't match the value
for cursor in range(N):
    if array[cursor] != cursor:
        return cursor
return N

9
छोटी निपिक। आपने एक तुच्छ मामले को याद किया है: जब सूची {0, ..., एन -1} है। उस स्थिति में पास 1 कुछ नहीं करता है और पास 2 सरणी [कर्सर] == सूची में सभी प्रविष्टियों के लिए कर्सर है, इसलिए एल्गोरिथ्म वापस नहीं आता है। इसलिए आपको अंत में 'रिटर्न एन' स्टेटमेंट चाहिए।
एलेक्स

12
आपका समाधान डोमेन और सीमा को बताता है (लक्ष्य एक मूल्य और एक सूचकांक दोनों है)। सीमा 128M तत्वों के लिए उपलब्ध भंडारण द्वारा सीमित है, फिर भी डोमेन 2G आकार में है। यह एकल प्रविष्टि के साथ विफल हो जाएगा, जो उन प्रविष्टियों की संख्या से अधिक मूल्य के साथ है जिन्हें सरणी में आवंटित किया जा सकता है। यदि प्रश्न 'बहुत लंबा' निर्दिष्ट नहीं किया गया है, तो इसका उत्तर सुरुचिपूर्ण है, भले ही यह इनपुट को नष्ट कर दे। इस समस्या में समय-स्थान का व्यापार बहुत स्पष्ट है, और प्रदान की गई बाधाओं के तहत एक ओ (एन) समाधान संभव नहीं हो सकता है।
पेक्का

2
दूसरा पास रैखिक खोज के बजाय द्विआधारी खोज का उपयोग कर सकता है।
user448810

4
यह समाधान तभी काम करता है जब मूल्यों और सूचकांक की सीमा तुलनीय हो।
डब्बी

7
यह बड़े मूल्यों के साथ ठीक काम करेगा। बड़े मूल्यों को नजरअंदाज किया जा सकता है क्योंकि उनके पास सरणी में नहीं सबसे छोटे मूल्य के साथ कुछ भी नहीं हो सकता है। आपके उदाहरण के लिए लक्ष्य के कारण सभी मानों की अनदेखी करने वाले सरणी पर पहला पास लूप होगा <और फिर दूसरे पास के पहले पुनरावृत्ति पर 0 लौटाएगा।
चींटियों ने प्लाज्मा

89

यहां एक सरल O(N)समाधान है जो O(N)अंतरिक्ष का उपयोग करता है। मैं मान रहा हूं कि हम इनपुट सूची को गैर-ऋणात्मक संख्याओं तक सीमित कर रहे हैं और हम पहले गैर-नकारात्मक संख्या को खोजना चाहते हैं जो सूची में नहीं है।

  1. सूची की लंबाई का पता लगाएं; यह कहना है N
  2. Nसभी के लिए प्रारंभिक, बूलियन की एक सरणी आवंटित करें false
  3. Xसूची में प्रत्येक संख्या के लिए, यदि Xइससे कम है N, X'thतो सरणी का तत्व सेट करें true
  4. सूचकांक से शुरू होने वाले सरणी को स्कैन करें 0, जो पहले तत्व की तलाश में है false। यदि आप पहली बार falseइंडेक्स पर पाते हैं I, तो Iइसका उत्तर है। अन्यथा (अर्थात जब सभी तत्व हैं true) उत्तर है N

अभ्यास में, "की सरणी Nबूलियन्स" शायद एक "बिटमैप" या "bitset" एक के रूप में प्रतिनिधित्व के रूप में एन्कोड किया जाएगा byteया intसरणी। यह आमतौर पर कम स्थान (प्रोग्रामिंग भाषा के आधार पर) का उपयोग करता है और पहले स्कैन को और falseअधिक तेज़ी से करने की अनुमति देता है ।


यह है कि एल्गोरिथ्म कैसे / क्यों काम करता है।

मान लीजिए कि Nसूची में संख्याएँ अलग नहीं हैं, या उनमें से एक या अधिक से अधिक है N। इसका मतलब यह है कि सीमा में कम से कम एक संख्या होनी चाहिए 0 .. N - 1जो सूची में नहीं है। इसलिए सबसे छोटी लापता संख्या को खोजने की समस्या को कम से कमN लापता संख्या को खोजने की समस्या को कम करना चाहिए । इसका मतलब है कि हमें उन नंबरों पर नज़र रखने की ज़रूरत नहीं है जो अधिक या बराबर हैं N... क्योंकि वे जवाब नहीं होंगे।

पिछले पैराग्राफ का विकल्प यह है कि सूची संख्याओं का क्रमपरिवर्तन है 0 .. N - 1। इस स्थिति में, चरण 3 सरणी के सभी तत्वों को सेट करता है true, और चरण 4 हमें बताता है कि पहला "लापता" नंबर है N


एल्गोरिथ्म की कम्प्यूटेशनल जटिलता O(N)आनुपातिकता के अपेक्षाकृत छोटे स्थिरांक के साथ है। यह सूची के माध्यम से दो रैखिक पास बनाता है, या सिर्फ एक पास अगर सूची की लंबाई के साथ शुरू करने के लिए जाना जाता है। मेमोरी में पूरी सूची को होल्ड करने का प्रतिनिधित्व करने की कोई आवश्यकता नहीं है, इसलिए एल्गोरिथ्म की असममित मेमोरी उपयोग बस वही है जो बूलियंस की सरणी का प्रतिनिधित्व करने के लिए आवश्यक है; यानी O(N)बिट्स।

(इसके विपरीत, एल्गोरिदम जो इन-मेमोरी सॉर्टिंग या विभाजन पर भरोसा करते हैं, यह मान लेते हैं कि आप मेमोरी में पूरी सूची का प्रतिनिधित्व कर सकते हैं। फॉर्म में सवाल पूछा गया था, इसके लिए O(N)64-बिट शब्दों की आवश्यकता होगी ।)


@ जॉर्न की टिप्पणी है कि चरण 1 से 3 तक गिनती गिनती पर भिन्नता है। एक मायने में वह सही है, लेकिन अंतर महत्वपूर्ण हैं:

  • एक प्रकार की गिनती के लिए (कम से कम) Xmax - Xminकाउंटरों की एक सूची की आवश्यकता होती है जहां Xmaxसूची में सबसे बड़ी संख्या होती है और सूची Xminमें सबसे छोटी संख्या होती है। प्रत्येक काउंटर को एन राज्यों का प्रतिनिधित्व करने में सक्षम होना चाहिए; एक द्विआधारी प्रतिनिधित्व का अर्थ है कि यह एक पूर्णांक प्रकार (कम से कम) ceiling(log2(N))बिट्स है।
  • सरणी आकार का निर्धारण करने के लिए, एक गणना प्रकार को निर्धारित करने Xmaxऔर सूची के लिए प्रारंभिक पास बनाने की आवश्यकता होती है Xmin
  • इसलिए न्यूनतम ceiling(log2(N)) * (Xmax - Xmin)बिट -केस स्थान की आवश्यकता बिट्स है।

इसके विपरीत, ऊपर प्रस्तुत एल्गोरिथ्म Nमें सबसे खराब और सबसे अच्छे मामलों में बिट्स की आवश्यकता होती है ।

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


संपादित करें: मैंने "बूलियन्स की सरणी" का उपयोग करने के लिए एल्गोरिथ्म के विवरण को बदल दिया है क्योंकि लोग स्पष्ट रूप से बिट्स और बिटमैप का उपयोग करते हुए मेरा मूल विवरण भ्रमित करते हैं।


3
@ adi92 यदि चरण 3 आपको 1 बिट सेट के साथ बिटमैप देता है, तो सूची में 0 से N-1 तक प्रत्येक मान है। इसका मतलब है कि सूची में सबसे छोटा गैर-नकारात्मक पूर्णांक N है। यदि 0 और N-1 के बीच कोई मान है जो सूची में नहीं है, तो संबंधित बिट सेट नहीं किया जाएगा। इस तरह के सबसे छोटे मूल्य इसलिए जवाब है।
डिविजेक

4
@ adi92 आपके उदाहरण में, सूची में 300 तत्व होंगे। इसका मतलब है कि अगर कोई "लापता" मान है, तो यह 300 से कम होना चाहिए। एल्गोरिथ्म को चलाने के बाद, हम 300 स्लॉट्स के साथ एक बिटफील्ड बनाएंगे, फिर बार-बार बिट्स को 1, 2 और 3 में सेट करेंगे, जिससे सभी छूट जाएंगे अन्य स्लॉट्स - 0 और 4 299 के माध्यम से - स्पष्ट। बिटफील्ड को स्कैन करते समय हम स्लॉट 0 में ध्वज को स्पष्ट पाते हैं, इसलिए हमें पता होगा कि उत्तर 0 है।
डिविजेक

4
ध्यान दें कि इस एल्गोरिथ्म को बिट टिडलिंग के बिना और अधिक आसानी से समझा जा सकता है: "आकार की एक बूलियन सरणी बनाएं एन" आदि। एक बार जब आप इसे इस तरह से समझते हैं, तो एक बिटवाइज़ संस्करण पर जाना वैचारिक रूप से आसान होता है।
जॉन स्कीट

2
अमूर्त समाधान देते समय, वैचारिक रूप से सबसे सरल तरीके का उपयोग करें जो काम करता है, और अत्यधिक विशेषज्ञ नहीं है। आपका समाधान एक (सार) बूलियन सरणी के उपयोग के लिए चिल्लाता है, इसलिए इसे कॉल करें। आप इस सरणी को लागू कर सकते हैं bool[]या एक बिटमैप द्वारा सामान्य समाधान के लिए अप्रासंगिक है।
जोरेन

2
मुझे लगता है कि इस समाधान का सबसे अच्छा वर्णन किया जा सकता है "एन से ऊपर के तत्वों की अवहेलना करने वाले एक गिनती प्रकार का उपयोग करें, फिर शुरू से एक रैखिक खोज करके पहला लापता तत्व ढूंढें।"
जोरेन

13

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

1GB RAM का मतलब है कि सूची में सबसे अधिक 134,217,728 नंबर हो सकते हैं। लेकिन 2 64 हैं = 18,446,744,073,709,551,616 संभावित नंबर हैं। तो संभावना है कि सूची में शून्य 137,438,953,472 में 1 है।

इसके विपरीत, इस साल बिजली गिरने से मेरे नुकसान 700,000 में से 1 हैं। और एक उल्कापिंड की चपेट में आने की मेरी संभावना लगभग 10 ट्रिलियन में 1 है। तो मैं एक शून्य से नहीं होने की तुलना में एक खगोलीय वस्तु द्वारा मेरी असामयिक मृत्यु के कारण एक वैज्ञानिक पत्रिका में लिखे जाने की संभावना लगभग दस गुना अधिक है।


11
आपकी गणना केवल तभी मानती है जब मूल्यों को समान रूप से वितरित और यादृच्छिक पर चुना जाता है। वे केवल क्रमिक रूप से उत्पन्न हो सकते हैं।
डिविजेक

1
आप निश्चित रूप से सही हैं। लेकिन मैं सामान्य मामले के लिए अनुकूलन के बारे में हूं। :)
बैरी ब्राउन

10
तो, इस उत्तर के साथ इंटरव्यू लेने वाले को क्या पसंद है?
अमरघोष

6
प्रश्न यह नहीं कहता है कि संख्याएँ यादृच्छिक रूप से समान रूप से चुनी गई हैं। उनका चयन इस प्रश्न को सेट करने वाले व्यक्ति द्वारा किया जाता है। इसे देखते हुए, सूची में 0 होने की संभावना 137,438,953,472 में 1 से बहुत बड़ी है, शायद 2 में भी 1 से बड़ी है। :-)
श्रीवत्सआर

8
@ अमरघोष उस प्रश्न का उत्तर भी शून्य है।
पीटरअलेनवेब

10

जैसा कि अन्य उत्तरों में बताया गया है कि आप एक प्रकार कर सकते हैं, और तब तक स्कैन कर सकते हैं जब तक आप एक अंतर नहीं पाते हैं।

आप O (N) के लिए एल्गोरिथम की जटिलता में सुधार कर सकते हैं और संशोधित QuickSort का उपयोग करके O (N) स्थान रख सकते हैं जहाँ आप उन विभाजनों को समाप्त कर देते हैं जो अंतर रखने वाले संभावित उम्मीदवार नहीं हैं।

  • पहले विभाजन के चरण पर, डुप्लिकेट को हटा दें।
  • एक बार विभाजन पूरा हो जाने के बाद निचले विभाजन में मदों की संख्या पर नज़र डालें
  • क्या यह मूल्य विभाजन बनाने के लिए उपयोग किए गए मूल्य के बराबर है?
    • यदि ऐसा है तो इसका अर्थ है कि अंतर उच्च विभाजन में है।
      • निचले विभाजन को अनदेखा करते हुए क्विकॉर्ट के साथ जारी रखें
    • अन्यथा अंतर निचले विभाजन में है
      • उच्च विभाजन को अनदेखा करते हुए, क्विकॉर्ट के साथ जारी रखें

इससे बड़ी संख्या में संगणना की बचत होती है।


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

2
यदि आप सूची की लंबाई जानते हैं, तो आप लेन (सूची) की तुलना में किसी भी मान को कम कर सकते हैं। कबूतर के सिद्धांत से, किसी भी 'छेद' को लेन (सूची) से कम होना चाहिए।
डिविजेक

1
मुझे नहीं लगता कि वह ओ (एन) है ... एक के लिए, मुझे यकीन नहीं है कि आप डुप्लिकेट को हटा सकते हैं जब तक कि एक सूची पूरी तरह से छंटनी न हो। दूसरी बात यह है कि जब तक आप प्रत्येक खोज के आधे भाग को फेंकने की गारंटी दे सकते हैं (क्योंकि आप मिडपॉइंट में और उससे अधिक में विभाजित हो चुके हैं), तब भी आपके पास n पर निर्भर कई डेटा (n पर निर्भर) है।
paxdiablo

1
paxdiablo: आप एक अद्वितीय सूची के साथ एक नई सूची का निर्माण कर सकते हैं, जैसे कि स्टीफन सी ने जो प्रस्तावित किया है, उसमें एक बिटमैप विधि का उपयोग करके। यह O (n) समय और स्थान पर चलता है। मुझे यकीन नहीं है कि यह उससे बेहतर किया जा सकता है।
निक

9

O(N)सोच के नुकसान के बारे में बताने के लिए , यहां एक O(N)एल्गोरिथ्म है जो O(1)अंतरिक्ष का उपयोग करता है।

for i in [0..2^64):
  if i not in list: return i

print "no 64-bit integers are missing"

1
इच्छाशक्ति सही है। यह O (n) नहीं है क्योंकि आपके पास वास्तव में यहां दो लूप हैं, लेकिन एक निहित है। यह निर्धारित करना कि मान किसी सूची में है O (n) ऑपरेशन, और आप उस n को लूप के लिए कर रहे हैं। जो इसे O (n ^ 2) बनाता है।
निक

6
निक, विल, यह ओ (एन * एन) है जहां एन सूची का आकार है और एन डोमेन (64 बिट पूर्णांक) का आकार है। जबकि एन एक बहुत बड़ी संख्या है, यह अभी भी एक निरंतर है तो औपचारिक रूप से समस्या के लिए जटिलता है जैसा कि ओ (एन) है।
चींटियाँ प्लाज्मा

1
चींटियों, मैं मानता हूं कि यह ओ (एन एन) है, लेकिन एन निरंतर नहीं है। क्योंकि एल्गोरिथ्म समाप्त हो जाता है जब वह उत्तर पाता है, बाहरी लूप के माध्यम से पूर्ण पुनरावृत्तियों की संख्या उत्तर के बराबर होती है, जो स्वयं सूची के आकार से बंधी होती है। तो, इस मामले में O (N n) O (n ^ 2) है।
विल हैरिस

12
N तत्वों की सूची में एक संख्या की तलाश में स्पष्ट रूप से O (N) है। हम ऐसा 2 ^ 64 बार करते हैं। जबकि बड़ा, 2 ^ 64 एक ठोस है। इसलिए एल्गोरिथ्म C * O (N) है, जो अभी भी O (N) है।
IJ कैनेडी

3
मुझे अपने पिछले कथन को याद रखना चाहिए; सबसे कठिन परिभाषा के अनुसार, यह ऑपरेशन वास्तव में O (n) है।
निक

8

चूंकि संख्याएं सभी 64 बिट लंबी हैं, इसलिए हम उन पर मूलांक सॉर्ट का उपयोग कर सकते हैं, जो ओ (एन) है। उन्हें सॉर्ट करें, तब तक उन्हें स्कैन करें जब तक आप नहीं ढूंढते कि आपको क्या मिल रहा है।

यदि सबसे छोटी संख्या शून्य है, तब तक आगे स्कैन करें जब तक कि आप एक अंतर नहीं पाते हैं। यदि सबसे छोटी संख्या शून्य नहीं है, तो उत्तर शून्य है।


यह सच है, लेकिन मूलांक के लिए मेमोरी आवश्यकताएं बहुत तीव्र हो सकती हैं।
पीटरअलेनवेब

1
बहुत बड़े डेटा सेट के लिए मूलांक सॉर्ट अभ्यस्त काम करता है। लेकिन विभाजन और मूलांक क्रमांक काम कर सकते हैं।
डार्थवेडर

5

एक अंतरिक्ष कुशल विधि के लिए और सभी मूल्य अलग हैं आप इसे अंतरिक्ष O( k )और समय में कर सकते हैं O( k*log(N)*N )। यह अंतरिक्ष कुशल है और कोई डेटा नहीं चल रहा है और सभी ऑपरेशन प्राथमिक (घटाना जोड़कर) हैं।

  1. सेट U = N; L=0
  2. पहले kक्षेत्रों में संख्या स्थान का विभाजन करें । इस कदर:
    • 0->(1/k)*(U-L) + L, 0->(2/k)*(U-L) + L, 0->(3/k)*(U-L) + L...0->(U-L) + L
  3. count{i}प्रत्येक क्षेत्र में कितनी संख्या ( ) हैं। ( N*kचरण)
  4. पहला क्षेत्र खोजें ( h) जो भरा नहीं है। इसका मतलब है count{h} < upper_limit{h}। ( kचरण)
  5. अगर h - count{h-1} = 1आपको अपना जवाब मिल गया है
  6. सेट U = count{h}; L = count{h-1}
  7. गोटो २

इसे हैशिंग (निक इस विचार के लिए धन्यवाद) का उपयोग करके सुधार किया जा सकता है।

  1. वही
  2. पहले kक्षेत्रों में संख्या स्थान का विभाजन करें । इस कदर:
    • L + (i/k)->L + (i+1/k)*(U-L)
  3. inc count{j} का उपयोग करते हुए j = (number - L)/k (if L < number < U)
  4. पहला क्षेत्र खोजें ( h) जिसमें k तत्व न हों
  5. अगर count{h} = 1h आपका जवाब है
  6. सेट U = maximum value in region h L = minimum value in region h

इसमें चलेगा O(log(N)*N)


मुझे वास्तव में यह उत्तर पसंद है। यह पढ़ना थोड़ा कठिन था, लेकिन जब मैंने प्रश्न पढ़ा तो यह मेरे सिर के समान था।
निक

कुछ बिंदु पर यह स्टीफन सी द्वारा उस बिटमैप समाधान पर स्विच करने के लिए स्मार्ट होगा, शायद जबU-L < k
एगॉन

यह O (लॉग (N) * N) में नहीं बल्कि O (N) में चलता है। आपका उत्तर @cdiggins उत्तर का एक सामान्यीकरण है और यह O (N) में चलता है क्योंकि सम (1 / k ** i के लिए मैं रेंज में (ceil (log_k (n)))) <= 2.
Lapinot

प्रत्येक पुनरावृति पर आप O (N) संख्याओं से गुजरते हैं, यह O (log_k (N)) कुल पुनरावृत्तियों को लेता है। इसलिए O (log_k (N) * N) == O (लॉग (N) * N)। मूल संख्याओं को क्रमबद्ध / बकेट नहीं किया गया है और आपको उन सभी के माध्यम से जाने की आवश्यकता है।
इगुन

लेकिन अगर आपने k क्षेत्रों (मूल n / k) में मूल सूची को विभाजित किया है तो आप पहले क्षेत्र का चयन करते हैं जो पूर्ण नहीं है। इसलिए अगली यात्रा में आपको केवल चयनित क्षेत्र पर विचार करने और इसे नए क्षेत्रों में विभाजित करने की आवश्यकता है (आकार n / k ** 2) आदि। वास्तव में आप हर बार पूरी सूची पर पुनरावृति नहीं करते हैं (अन्यथा विभाजन का क्या मतलब है? ?)।
लैपिनोट

3

मैं उन्हें तब क्रमबद्ध करूँगा, जब तक कि मैं एक अंतराल (शून्य और पहली संख्या के बीच के अंतराल पर अंतराल सहित) क्रम से न चलूँ।

एक एल्गोरिथ्म के संदर्भ में, ऐसा कुछ ऐसा करेगा:

def smallest_not_in_list(list):
    sort(list)
    if list[0] != 0:
        return 0
    for i = 1 to list.last:
        if list[i] != list[i-1] + 1:
            return list[i-1] + 1
    if list[list.last] == 2^64 - 1:
        assert ("No gaps")
    return list[list.last] + 1

बेशक, यदि आपके पास सीपीयू ग्रंट की तुलना में बहुत अधिक मेमोरी है, तो आप सभी संभावित 64-बिट मानों का एक बिटमैप बना सकते हैं और बस सूची में प्रत्येक संख्या के लिए बिट्स सेट कर सकते हैं। फिर उस बिटमास्क में पहले 0-बिट देखें। यह समय के संदर्भ में एक ओ (एन) ऑपरेशन में बदल जाता है लेकिन स्मृति आवश्यकताओं के संदर्भ में बहुत महंगा है :-)

मुझे संदेह है कि आप O (n) में सुधार कर सकते हैं क्योंकि मैं ऐसा करने का एक तरीका नहीं देख सकता जिसमें प्रत्येक संख्या को कम से कम एक बार देखना शामिल नहीं है।

उस के लिए एल्गोरिथ्म निम्नलिखित के साथ होगा:

def smallest_not_in_list(list):
    bitmask = mask_make(2^64) // might take a while :-)
    mask_clear_all (bitmask)
    for i = 1 to list.last:
        mask_set (bitmask, list[i])
    for i = 0 to 2^64 - 1:
        if mask_is_clear (bitmask, i):
            return i
    assert ("No gaps")

विवरण से ऐसा लगता है कि यह पहले तत्व में सबसे छोटा है, क्योंकि यह सूची में सबसे छोटा नहीं है। लेकिन, यह एक धारणा है जिसे मैंने बनाया है, मैं गलत हो सकता हूं।
जेम्स ब्लैक

मेरे विचार थे कि यदि क्रमबद्ध अनुक्रम 4,5,6 था, तो 0 सूची में सबसे छोटा नहीं होगा।
पैक्सडीब्लो

मुझे उम्मीद है कि 2, 3, 5, उत्तर 4 होना चाहिए, लेकिन, मैं गलत हो सकता है।
जेम्स ब्लैक

एक सवाल जिसका जवाब ओपी को देना चाहिए। क्या खोज स्थान "सभी 64-बिट अहस्ताक्षरित पूर्णांक" या "सूची में सबसे कम और उच्चतम के बीच सभी संख्याएं" हैं?
पैक्सिडाब्लो

मैं मानता हूं कि सबसे खराब स्थिति में आपको कम से कम एक बार देखना होगा, जब तक कि यह पहले से ही एक बाइनरी ट्री में सॉर्ट नहीं किया गया था।
जेम्स ब्लैक

2

सूची को क्रमबद्ध करें, पहले और दूसरे तत्वों को देखें, और तब तक ऊपर जाना शुरू करें जब तक कि अंतर न हो।


निर्भर करता है कि आप कैसे परिभाषित करते हैं, सूची में नहीं।
जेम्स ब्लैक

@PeterAllenWebb - वहाँ होंगे, लेकिन यादृच्छिक क्रम में संख्याएँ हैं, या क्रमबद्ध हैं?
जेम्स ब्लैक

1

आप इसे ओ (एन) समय और ओ (1) अतिरिक्त स्थान पर कर सकते हैं, हालांकि छिपा हुआ कारक काफी बड़ा है। यह समस्या को हल करने का एक व्यावहारिक तरीका नहीं है, लेकिन फिर भी यह दिलचस्प हो सकता है।

हर अहस्ताक्षरित 64-बिट पूर्णांक के लिए (आरोही क्रम में) सूची पर पुनरावृति करें जब तक कि आप लक्ष्य पूर्णांक नहीं पाते हैं या आप सूची के अंत तक नहीं पहुंचते हैं। यदि आप सूची के अंत तक पहुँचते हैं, तो लक्ष्य पूर्णांक सबसे छोटा पूर्णांक है जो सूची में नहीं है। यदि आप 64-बिट पूर्णांकों के अंत तक पहुँचते हैं, तो प्रत्येक 64-बिट पूर्णांक सूची में है।

यहाँ यह पायथन फ़ंक्शन के रूप में है:

def smallest_missing_uint64(source_list):
    the_answer = None

    target = 0L
    while target < 2L**64:

        target_found = False
        for item in source_list:
            if item == target:
                target_found = True

        if not target_found and the_answer is None:
            the_answer = target

        target += 1L

    return the_answer

यह फ़ंक्शन इसे ओ (एन) रखने के लिए जानबूझकर अक्षम है। विशेष रूप से ध्यान दें कि उत्तर मिलने के बाद भी फ़ंक्शन लक्ष्य पूर्णांक की जाँच करता रहता है। यदि उत्तर मिलते ही फ़ंक्शन वापस आ गया, तो बाहरी लूप चलाने की संख्या, उत्तर के आकार से बंधी होगी, जो n द्वारा बाध्य है। यह परिवर्तन रन टाइम O (n ^ 2) बना देगा, भले ही यह बहुत तेज़ होगा।


सच। यह मनोरंजक है कि ओ (1) स्पेस और ओ (एन) समय के कुछ एल्गोरिदम कितने भयावह हैं, इस प्रश्न के साथ अभ्यास में विफल हो जाते हैं।
पीटरअलेनवेब

1

मेरी प्रेरणा के लिए एगॉन, स्विल्डन और स्टीफन सी का धन्यवाद। सबसे पहले, हम लक्ष्य मूल्य की सीमा जानते हैं क्योंकि यह सूची के आकार से अधिक नहीं हो सकता है। इसके अलावा, 1GB सूची में अधिकतम 134217728 (128 * 2 ^ 20) 64-बिट पूर्णांक हो सकते हैं।

हैशिंग भाग
I ने हैशिंग का उपयोग करते हुए नाटकीय रूप से हमारे खोज स्थान को कम करने का प्रस्ताव दिया। सबसे पहले, सूची के आकार को वर्गाकार करें। 1GB सूची के लिए, यह N = 11,586 है। सूची के माध्यम से आकार N. Iterate के पूर्णांक सरणी को सेट करें, और प्रत्येक संख्या का वर्गमूल * लें जिसे आप अपने हैश के रूप में पाते हैं। अपनी हैश तालिका में, उस हैश के लिए काउंटर बढ़ाएँ। अगला, अपने हैश तालिका के माध्यम से पुनरावृति। पहली बाल्टी जो आप पाते हैं कि यह अधिकतम आकार के बराबर नहीं है, आपके नए खोज स्थान को परिभाषित करती है।

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

इसे O (n) समय और O (sqrt (n)) स्थान में पूरा किया जाएगा।

(* आप इसे और अधिक कुशलता से करने के लिए बिट शिफ्टिंग जैसे कुछ का उपयोग कर सकते हैं, और बस तदनुसार बाल्टी की संख्या और आकार भिन्न हो सकते हैं।)


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

आप सही हैं, मैंने डुप्लिकेट प्रविष्टियों पर विचार करने की उपेक्षा की है। मुझे यकीन नहीं है कि चारों ओर काम किया जा सकता है।
निक

1

ठीक है अगर संख्याओं की सूची में केवल एक लापता संख्या है, तो लापता संख्या को खोजने का सबसे आसान तरीका श्रृंखला को जोड़ना और सूची में प्रत्येक मूल्य को घटाना है। अंतिम मान लापता संख्या है।


हाँ। यह एक और क्लासिक साक्षात्कार प्रश्न है।
पीटरअलेनवेब

1
इससे भी आसान है कि सूची में संख्याओं को एक साथ XOR करें, एक साथ सीमा में संख्याओं को XOR, और XOR एक साथ परिणामों को।
जॉन कुर्लाक

1
 int i = 0;
            while ( i < Array.Length)
            {

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

                if (i < Array.Length)
                {
                    if (Array[i] <= Array.Length)
                    {//SWap

                        int temp = Array[i];
                        int AnoTemp = Array[temp - 1];
                        Array[temp - 1] = temp;
                        Array[i] = AnoTemp;

                    }
                    else
                       i++;



                }
            }

            for (int j = 0; j < Array.Length; j++)
            {
                if (Array[j] > Array.Length)
                {
                    Console.WriteLine(j + 1);
                    j = Array.Length;
                }
                else
                    if (j == Array.Length - 1)
                        Console.WriteLine("Not Found !!");

            }
        }

1

हम संख्याओं को रखने के लिए हैश टेबल का उपयोग कर सकते हैं। एक बार सभी संख्याएँ पूरी हो जाने के बाद, 0 से एक काउंटर चलाएं जब तक कि हम सबसे कम न मिलें। एक बहुत अच्छा हैश लगातार समय में हैश और स्टोर करेगा, और निरंतर समय में पुनर्प्राप्त करेगा।

for every i in X         // One scan Θ(1)
   hashtable.put(i, i);  // O(1)

low = 0;

while (hashtable.get(i) <> null)   // at most n+1 times
   low++;

print low;

सबसे खराब स्थिति अगर nसरणी में तत्व हैं , और हैं {0, 1, ... n-1}, तो किस स्थिति में, इसका जवाब प्राप्त किया जाएगा n, फिर भी इसे बनाए रखना O(n)


1

यहाँ मेरा उत्तर जावा में लिखा है:

बेसिक आइडिया: 1- डुप्लिकेट पॉजिटिव, जीरो और नेगेटिव नंबर्स को फेंकने वाले ऐरे के माध्यम से लूप करें जबकि बाकी को समेटें, साथ ही अधिकतम पॉजिटिव नंबर प्राप्त करें और मैप में यूनिक पॉजिटिव नंबर रखें।

2- योग की गणना अधिकतम * (अधिकतम + 1) / 2 के रूप में करें।

3- चरण 1 और 2 में गणना की गई राशि के बीच का अंतर ज्ञात करें

4- फिर से लूप को 1 से न्यूनतम [sums का अंतर, अधिकतम] पर लाएं और पहला नंबर लौटाएं जो कि चरण 1 में आबादी वाले नक्शे में नहीं है।

public static int solution(int[] A) {
    if (A == null || A.length == 0) {
        throw new IllegalArgumentException();
    }

    int sum = 0;
    Map<Integer, Boolean> uniqueNumbers = new HashMap<Integer, Boolean>();
    int max = A[0];
    for (int i = 0; i < A.length; i++) {
        if(A[i] < 0) {
            continue;
        }
        if(uniqueNumbers.get(A[i]) != null) {
            continue;
        }
        if (A[i] > max) {
            max = A[i];
        }
        uniqueNumbers.put(A[i], true);
        sum += A[i];
    }
    int completeSum = (max * (max + 1)) /  2;
    for(int j = 1; j <= Math.min((completeSum - sum), max); j++) {
        if(uniqueNumbers.get(j) == null) { //O(1)
            return j;
        }
    }
    //All negative case
    if(uniqueNumbers.isEmpty()) {
        return 1;
    }
    return 0;
}

0

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

बाइनरी खोज का उपयोग करने का तरीका उस संख्या को जोड़ना है जिसे आप सरणी के प्रत्येक तत्व से देख रहे हैं, और नकारात्मक परिणामों की जांच करें।


0

मुझे "अनुमान शून्य" पसंद है। यदि संख्याएं यादृच्छिक थीं, तो शून्य अत्यधिक संभावित है। यदि "परीक्षक" एक गैर-यादृच्छिक सूची सेट करता है, तो एक जोड़ें और फिर से अनुमान लगाएं:

LowNum=0
i=0
do forever {
  if i == N then leave /* Processed entire array */
  if array[i] == LowNum {
     LowNum++
     i=0
     }
   else {
     i++
   }
}
display LowNum

सबसे खराब स्थिति एन = एन के साथ एन = एन है, लेकिन व्यवहार में एन एक छोटी संख्या (उदाहरण 1) होने की अत्यधिक संभावना है।


0

मुझे यकीन नहीं है कि मुझे सवाल मिला। लेकिन अगर सूची के लिए 1,2,3,5,6 और लापता संख्या 4 है, तो लापता संख्या O (n) में पाई जा सकती है: (n + 2) (n + 1) / 2- (n +) 1) n / 2

संपादित करें: क्षमा करें, मुझे लगता है कि मैं कल रात बहुत तेजी से सोच रहा था। वैसे भी, दूसरे भाग को वास्तव में योग (सूची) द्वारा प्रतिस्थापित किया जाना चाहिए, जहां ओ (एन) आता है। सूत्र इसके पीछे के विचार को प्रकट करता है: n अनुक्रमिक पूर्णांकों के लिए, योग (n + 1) * n / 2 होना चाहिए। यदि कोई अनुपलब्ध संख्या है, तो योग सम संख्या (n + 1) के समतुल्य पूर्णांक के लापता संख्या के बराबर होगा।

इस तथ्य को इंगित करने के लिए धन्यवाद कि मैं अपने दिमाग में कुछ मध्य टुकड़े डाल रहा था।


1
मैं नहीं देखता, पहली नज़र में यह कैसे काम करेगा। आपके मामले में n = 5 और फॉर्मूला तय किया जाएगा, चाहे उसमें कोई भी संख्या क्यों न हो।
सिसवे

साइमन: क्या अब आप कृपया मेरे संपादन के अनुसार डाउन वोट निकाल सकते हैं?
कोडिज्म

0

अच्छी तरह से किया चींटियों का प्लाज्मा! मैंने लगभग 15 मिनट के लिए उत्तर के बारे में सोचा और स्वतंत्र रूप से आपकी सोच की एक समान नस में उत्तर के साथ आया:

#define SWAP(x,y) { numerictype_t tmp = x; x = y; y = tmp; }
int minNonNegativeNotInArr (numerictype_t * a, size_t n) {
    int m = n;
    for (int i = 0; i < m;) {
        if (a[i] >= m || a[i] < i || a[i] == a[a[i]]) {
            m--;
            SWAP (a[i], a[m]);
            continue;
        }
        if (a[i] > i) {
            SWAP (a[i], a[a[i]]);
            continue;
        }
        i++;
    }
    return m;
}

मीटर "वर्तमान अधिकतम संभव आउटपुट देता है जो मुझे पहले i इनपुट के बारे में पता है और मानों के बारे में और कुछ नहीं मान रहा है जब तक कि एम -1 में प्रवेश नहीं हुआ है"।

M का यह मान तभी लौटाया जाएगा जब ([i], ..., [[m-1]) मानों का क्रमपरिवर्तन (i, ..., m-1) हो। इस प्रकार यदि एक [i]> = m या यदि एक [i] <i या यदि एक [i] == [a [a]] तो हम जानते हैं कि m गलत आउटपुट है और कम से कम एक तत्व कम होना चाहिए। अतः m को घटाना और एक [i] [m] के साथ अदला-बदली करना हम पुनरावृत्ति कर सकते हैं।

अगर यह सच नहीं है, लेकिन एक [i]> तो मैं जानता हूँ कि एक [i]! = [A [i]] हम जानते हैं कि [[i] के साथ [a] को स्वैप करने से तत्वों की संख्या बढ़ जाएगी। अपनी जगह पर।

अन्यथा [i] मेरे बराबर होना चाहिए जिस स्थिति में हम यह जानकर मुझे बढ़ा सकते हैं कि इस सूचकांक के सभी मान और सम्‍मिलित हैं जो उनके सूचकांक के बराबर हैं।

यह सबूत कि यह एक अनंत लूप में प्रवेश नहीं कर सकता है, पाठक को एक अभ्यास के रूप में छोड़ दिया जाता है। :)


0

Dafny चींटियों 'जवाब शो से टुकड़ा क्यों यथा-स्थान एल्गोरिथ्म विफल हो सकता है। requiresपूर्व शर्त का वर्णन करता है कि प्रत्येक आइटम के मूल्यों सरणी की सीमा से परे जाना नहीं होना चाहिए।

method AntsAasma(A: array<int>) returns (M: int)
  requires A != null && forall N :: 0 <= N < A.Length ==> 0 <= A[N] < A.Length;
  modifies A; 
{
  // Pass 1, move every value to the position of its value
  var N := A.Length;
  var cursor := 0;
  while (cursor < N)
  {
    var target := A[cursor];
    while (0 <= target < N && target != A[target])
    {
        var new_target := A[target];
        A[target] := target;
        target := new_target;
    }
    cursor := cursor + 1;
  }

  // Pass 2, find first location where the index doesn't match the value
  cursor := 0;
  while (cursor < N)
  {
    if (A[cursor] != cursor)
    {
      return cursor;
    }
    cursor := cursor + 1;
  }
  return N;
}

forall ...सत्यापन त्रुटि देखने के लिए कोड को सत्यापनकर्ता के साथ और उसके बिना सत्यापनकर्ता में पेस्ट करें । दूसरी त्रुटि वेरीफायर का परिणाम है जो पास 1 लूप के लिए समाप्ति की स्थिति स्थापित करने में सक्षम नहीं है। यह साबित करने के लिए किसी को छोड़ दिया जाता है जो टूल को बेहतर समझता है।


0

यहां जावा में एक उत्तर दिया गया है जो इनपुट को संशोधित नहीं करता है और O (N) समय और N बिट्स का उपयोग करता है और मेमोरी का एक छोटा स्थिर ओवरहेड (जहां N सूची का आकार है):

int smallestMissingValue(List<Integer> values) {
    BitSet bitset = new BitSet(values.size() + 1);
    for (int i : values) {
        if (i >= 0 && i <= values.size()) {
            bitset.set(i);
        }
    }
    return bitset.nextClearBit(0);
}

0
def solution(A):

index = 0
target = []
A = [x for x in A if x >=0]

if len(A) ==0:
    return 1

maxi = max(A)
if maxi <= len(A):
    maxi = len(A)

target = ['X' for x in range(maxi+1)]
for number in A:
    target[number]= number

count = 1
while count < maxi+1:
    if target[count] == 'X':
        return count
    count +=1
return target[count-1] + 1

उपरोक्त समाधान के लिए 100% मिला।


0

1) फ़िल्टर नकारात्मक और शून्य

2) क्रमबद्ध करें / विशिष्ट

3) ऐरे जाएँ

जटिलता : ओ (एन) या ओ (एन * लॉग (एन))

Java8 का उपयोग करना

public int solution(int[] A) {
            int result = 1;
    boolean found = false;
    A = Arrays.stream(A).filter(x -> x > 0).sorted().distinct().toArray();
    //System.out.println(Arrays.toString(A));
    for (int i = 0; i < A.length; i++) {
        result = i + 1;
        if (result != A[i]) {
            found = true;
            break;
        }
    }
    if (!found && result == A.length) {
        //result is larger than max element in array
        result++;
    }
    return result;
}

0

सभी सकारात्मक संख्याओं को संग्रहीत करने के लिए एक unordered_set का उपयोग किया जा सकता है, और फिर हम 1 से लंबाई तक unordered_set को पुनरावृत्त कर सकते हैं, और पहली संख्या को देखें जो घटित नहीं होती है।

int firstMissingPositive(vector<int>& nums) {

    unordered_set<int> fre;
    // storing each positive number in a hash.
    for(int i = 0; i < nums.size(); i +=1)
    {
        if(nums[i] > 0)
            fre.insert(nums[i]);
     }

    int i = 1;
    // Iterating from 1 to size of the set and checking 
    // for the occurrence of 'i'

    for(auto it = fre.begin(); it != fre.end(); ++it)
    {
        if(fre.find(i) == fre.end())
            return i;
        i +=1;
    }

    return i;
}

0

मूल जावास्क्रिप्ट के माध्यम से समाधान

var a = [1, 3, 6, 4, 1, 2];

function findSmallest(a) {
var m = 0;
  for(i=1;i<=a.length;i++) {
    j=0;m=1;
    while(j < a.length) {
      if(i === a[j]) {
        m++;
      }
      j++;
    }
    if(m === 1) {
      return i;
    }
  }
}

console.log(findSmallest(a))

आशा है कि यह किसी के लिए मदद करता है।


0

अजगर के साथ यह सबसे कुशल नहीं है, लेकिन सही है

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import datetime

# write your code in Python 3.6

def solution(A):
    MIN = 0
    MAX = 1000000
    possible_results = range(MIN, MAX)

    for i in possible_results:
        next_value = (i + 1)
        if next_value not in A:
            return next_value
    return 1

test_case_0 = [2, 2, 2]
test_case_1 = [1, 3, 44, 55, 6, 0, 3, 8]
test_case_2 = [-1, -22]
test_case_3 = [x for x in range(-10000, 10000)]
test_case_4 = [x for x in range(0, 100)] + [x for x in range(102, 200)]
test_case_5 = [4, 5, 6]
print("---")
a = datetime.datetime.now()
print(solution(test_case_0))
print(solution(test_case_1))
print(solution(test_case_2))
print(solution(test_case_3))
print(solution(test_case_4))
print(solution(test_case_5))

0
def solution(A):
    A.sort()
    j = 1
    for i, elem in enumerate(A):
        if j < elem:
            break
        elif j == elem:
            j += 1
            continue
        else:
            continue
    return j

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.