संख्याओं की सूची में एक "छेद" खोजें


14

सबसे तेज़ तरीका पहले (छोटी) पूर्णांक है कि की दी गई सूची में मौजूद नहीं है खोजने के लिए क्या है अवर्गीकृत पूर्णांकों (और उस सूची की सबसे छोटी मूल्य से अधिक है)?

मेरा आदिम दृष्टिकोण उन्हें सुलझा रहा है और सूची के माध्यम से आगे बढ़ रहा है, क्या कोई बेहतर तरीका है?


6
मैं एक अनंत प्रगति छँटाई मुश्किल होगा ;-) लगता है @Jodrell
maple_shaft

3
@maple_shaft सहमत हुए, इसमें थोड़ा समय लग सकता है।
जॉडरेल

4
आप पहली बार एक अनसोल्ड सूची के लिए कैसे परिभाषित करते हैं?
जोडरेल

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

2
@JasonTrue FAQ से, If you have a question about… •algorithm and data structure conceptsयह IMHO विषय पर है।
maple_shaft

जवाबों:


29

यह मानते हुए कि आप "पूर्णांक" का अर्थ है जब आप "संख्या" कहते हैं, तो आप आकार 2 ^ n के बिटवेक्टर का उपयोग कर सकते हैं, जहां n तत्वों की संख्या है (कहते हैं कि आपकी श्रेणी में 1 और 256 के बीच पूर्णांक शामिल हैं, तो आप 256 का उपयोग कर सकते हैं- बिट, या 32 बाइट, बिटवेक्टर)। जब आप अपनी सीमा n की स्थिति में पूर्णांक पर आते हैं, तो nth बिट सेट करें।

जब आप पूर्णांकों के संग्रह की गणना कर रहे होते हैं, तो आप अपने बिटवेक्टर में बिट्स पर पुनरावृति करते हैं, किसी भी बिट सेट की स्थिति की तलाश में। वे अब आपके लापता पूर्णांक (एस) की स्थिति से मेल खाते हैं।

यह O (2 * N) है, इसलिए O (N) और संभवतः पूरी सूची को सॉर्ट करने की तुलना में अधिक मेमोरी कुशल है।


6
ठीक है, एक सीधी तुलना के रूप में, अगर आपके पास सभी सकारात्मक अहस्ताक्षरित 32 बिट पूर्णांक थे, लेकिन 1, तो आप लगभग आधे गीगाबाइट मेमोरी में पूर्णांक समस्या को हल कर सकते हैं। यदि आप इसके बजाय क्रमबद्ध करते हैं, तो आपको 8 गीगाबाइट मेमोरी का उपयोग करना होगा। और सॉर्टिंग, इस तरह के विशेष मामलों को छोड़कर (आपकी सूची एक बार आपके पास एक बिटवेक्टर होने के बाद क्रमबद्ध होती है) लगभग हमेशा n लॉग एन या इससे भी बदतर होती है, इसलिए उन मामलों को छोड़कर जहां निरंतर लागत में जटिलता होती है, रैखिक दृष्टिकोण जीतता है।
जेसनट्र्यूज

1
क्या होगा यदि आप रेंज को प्राथमिकता नहीं जानते हैं?
१३:१३ बजे ब्लरफ्ल

2
यदि आपके पास पूर्णांक डेटा प्रकार है, तो Blrfl, आप निश्चित रूप से सीमा के अधिकतम विस्तार को जानते हैं, भले ही आपके पास आगे संकीर्ण करने के लिए पर्याप्त जानकारी न हो। यदि आपको पता है कि यह एक छोटी सूची है, लेकिन सटीक आकार नहीं जानते हैं, तो छँटाई एक सरल उपाय हो सकता है।
जेसनट्र्यूज

1
या सबसे छोटे और सबसे बड़े तत्व को खोजने के लिए सूची के माध्यम से पहले एक और लूप करें। फिर आप मूल ऑफसेट के रूप में सबसे छोटे मूल्य के साथ सटीक आकार की एक सरणी आवंटित कर सकते हैं। अभी भी है)।
सुरक्षित

1
@ जेटप्रिक: होमवर्क नहीं, व्यवसाय, मैंने सीएस साल पहले स्नातक किया है :)।
फैबियान ज़ींडल

4

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

यहां बताया गया है कि मैं इस समस्या से कैसे निपटूंगा:

  1. सूची में सबसे छोटे तत्वों पर ध्यान केंद्रित करते हुए, एक ढेर प्रकार का उपयोग करें ।
  2. प्रत्येक स्वैप के बाद, देखें कि क्या आपके पास अंतराल है।
  3. यदि आप एक अंतर पाते हैं, तो return: आपको अपना उत्तर मिल गया है।
  4. यदि आपको कोई अंतर नहीं मिलता है, तो स्वैपिंग जारी रखें।

यहाँ एक ढेर प्रकार का दृश्य है


एक प्रश्न, आप सूची के "सबसे छोटे" तत्वों की पहचान कैसे करते हैं?
जॉडरेल

4

बस गूढ़ और "चतुर" होने के लिए, केवल एक "छेद" वाले सरणी के विशेष मामले में, आप XOR- आधारित समाधान आज़मा सकते हैं:

  • अपने सरणी की सीमा निर्धारित करें; यह सरणी के पहले तत्व के लिए "अधिकतम" और "मिनट" चर सेट करके किया जाता है, और उसके बाद प्रत्येक तत्व के लिए, यदि वह तत्व न्यूनतम से कम है या अधिकतम से अधिक है, तो न्यूनतम या अधिकतम सेट करें। नया मूल्य।
  • यदि सीमा सेट की कार्डिनैलिटी से कम है, तो केवल एक "छेद" है ताकि आप XOR का उपयोग कर सकें।
  • एक पूर्णांक चर X को शून्य पर प्रारंभ करें।
  • न्यूनतम से अधिकतम तक के प्रत्येक पूर्णांक के लिए, XOR उस मान के साथ X और परिणाम को X में संग्रहीत करता है।
  • अब X के साथ एरे में प्रत्येक पूर्णांक, प्रत्येक क्रमिक परिणाम को एक्स के साथ पहले की तरह संग्रहीत करता है।
  • जब आप कर लेंगे, तो एक्स आपके "छेद" का मूल्य होगा।

यह बिटवेक्टर समाधान के समान लगभग 2N समय में चलेगा, लेकिन किसी भी एन> आकार (इंट) के लिए कम मेमोरी स्थान की आवश्यकता होती है। हालाँकि, यदि सरणी में कई "छेद" हैं, तो X सभी छेदों का XOR "योग" होगा, जिसे वास्तविक छेद मानों में अलग करना मुश्किल या असंभव होगा। उस स्थिति में आप किसी अन्य विधि पर वापस आते हैं जैसे कि "पिवट" या "बिटवेक्टर" अन्य उत्तरों से आता है।

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


यह हास्यास्पद चतुर और भयानक है! अब क्या आप छेद की एक चर संख्या के साथ ऐसा करने का तरीका चुन सकते हैं? :-D

2

आपके द्वारा सामना की जाने वाली संख्याओं की सीमा क्या है? यदि वह सीमा बहुत बड़ी नहीं है, तो आप दो स्कैन (रैखिक समय O (n)) के साथ इसे हल कर सकते हैं, जिसमें समय के लिए संख्या, व्यापारिक स्थान के साथ कई तत्वों के साथ एक सरणी का उपयोग किया जा सकता है। आप एक और स्कैन के साथ रेंज को गतिशील रूप से पा सकते हैं। अंतरिक्ष को कम करने के लिए, आप प्रत्येक नंबर पर 1 बिट असाइन कर सकते हैं, जिससे आपको प्रति बाइट 8 स्टोरेज मूल्य मिलेंगे।

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


1

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


1
क्या आप कह रहे हैं कि किसी सूची को ट्रेस करना उसी समय की जटिलता है जैसे छंटनी?
maple_shaft

@maple_shaft: नहीं, मैं यादृच्छिक डेटा से एक द्विआधारी पेड़ का निर्माण कर रहा हूं और फिर इसे दाएं से बाएं ट्रैवर्स करना छंटाई के बराबर है और फिर छोटे से बड़े तक ट्रैवर्सिंग है।
पिलमंचर

1

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

मेरे विचार में, ढेर संरचना का उपयोग करना संभवतः सबसे सामान्य समाधान है (एक ढेर मूल रूप से आपको एक पूर्ण प्रकार के बिना कुशलता से सबसे छोटे तत्व देता है)।

आप उन दृष्टिकोणों का भी विश्लेषण कर सकते हैं जो सबसे छोटी संख्याओं को पहले पाते हैं और फिर उससे बड़े प्रत्येक पूर्णांक के लिए स्कैन करते हैं। या आपको लगता है कि वसीयत में अंतर होगा 5 सबसे छोटी संख्याएं मिलेंगी।

इन सभी एल्गोरिदम में इनपुट विशेषताओं और कार्यक्रम की आवश्यकताओं के आधार पर अपनी ताकत है।


0

एक समाधान जो अतिरिक्त भंडारण का उपयोग नहीं करता है या पूर्णांकों की चौड़ाई (32 बिट्स) को ग्रहण करता है।

  1. एक रैखिक पास में सबसे छोटी संख्या पाते हैं। चलो इस "मिनट" कहते हैं। ओ (एन) समय जटिलता।

  2. एक यादृच्छिक पिवट तत्व चुनें और एक क्विकॉर्ट स्टाइल विभाजन करें।

  3. यदि पोजिशन स्थिति में समाप्त हो गई है = ("पिवट" - "मिनट"), तो विभाजन के दाईं ओर पुनरावृत्ति करें, अन्यथा विभाजन के बाईं ओर पुनरावृत्ति करें। यहां विचार यह है कि यदि शुरुआत से कोई छेद नहीं हैं, तो धुरी ("पिवट" - "मिनट") वें स्थान पर होगी, इसलिए पहला छेद विभाजन के दाईं ओर स्थित होना चाहिए और इसके विपरीत।

  4. बेस केस 1 तत्व की एक सरणी है और छेद इस तत्व और अगले एक के बीच स्थित है।

अपेक्षित कुल चलने का समय जटिलता हे (n) (स्थिरांक के साथ 8 * n) और सबसे खराब स्थिति O (n ^ 2) है। इसी तरह की समस्या के लिए समय जटिलता विश्लेषण यहां पाया जा सकता है


0

मेरा मानना ​​है कि मैं कुछ ऐसी चीज़ों के साथ आया हूं जो आम तौर पर और कुशलता से काम करना चाहिए यदि आपको डुप्लिकेट न होने की गारंटी दी जाती है * (हालांकि, यह किसी भी संख्या में छेद और पूर्णांक की किसी भी सीमा तक होना चाहिए)।

इस पद्धति के पीछे का विचार क्विकर की तरह है, जिसमें हम एक धुरी और उसके चारों ओर विभाजन को ढूंढते हैं, फिर एक छेद के साथ पक्ष (ओं) पर पुनरावृत्ति करते हैं। यह देखने के लिए कि किन पक्षों में छेद है, हम सबसे कम और उच्चतम संख्या पाते हैं, और उनकी तुलना उस तरफ की धुरी और मूल्यों से करते हैं। कहते हैं कि धुरी 17 है और न्यूनतम संख्या 11. है। यदि कोई छेद नहीं है, तो 6 संख्याएं (11, 12, 13, 14, 15, 16, 17) होनी चाहिए। यदि 5 हैं, तो हम जानते हैं कि उस तरफ एक छेद है और हम इसे खोजने के लिए उस तरफ बस फिर से उठ सकते हैं। मुझे इससे अधिक स्पष्ट रूप से समझाने में परेशानी हो रही है, तो चलिए एक उदाहरण लेते हैं।

15 21 10 13 18 16 22 23 24 20 17 11 25 12 14

धुरी:

10 13 11 12 14 |15| 21 18 16 22 23 24 20 17 25

15 धुरी है, जिसे पाइपों द्वारा दर्शाया गया है ( ||)। धुरी के बाईं ओर 5 नंबर हैं, जैसे कि (15 - 10) होना चाहिए, और दाईं ओर 9, जहां 10 (25 - 15) होना चाहिए। इसलिए हम दाईं ओर पुनरावृत्ति करते हैं; हम ध्यान देंगे कि पिछली बाउंड 15 थी यदि छेद इसके साथ (16) है।

[15] 18 16 17 20 |21| 22 23 24 25

अब बाईं ओर 4 नंबर हैं लेकिन 5 (21 - 16) होना चाहिए। इसलिए हम वहां फिर से आते हैं, और फिर से हम पिछले बाउंड (कोष्ठक में) पर ध्यान देंगे।

[15] 16 17 |18| 20 [21]

बाईं ओर की दाईं ओर 2 संख्याएं (18 - 16) हैं, लेकिन दाईं ओर 2 (20 - 18) के बजाय 1 है। हमारी समाप्ति स्थितियों के आधार पर, हम 1 नंबर की तुलना दोनों पक्षों (18, 20) से कर सकते हैं और देख सकते हैं कि 19 गायब है या एक बार फिर से जीवित हो सकता है:

[18] |20| [21]

बाईं ओर का आकार शून्य है, जिसमें धुरी (20) और पिछली बाउंड (18) के बीच का अंतर है, इसलिए 19 छेद है।

*: यदि डुप्लिकेट हैं, तो आप संभवतः ओ (एन) समय में उन्हें हटाने के लिए एक हैश सेट का उपयोग कर सकते हैं, समग्र विधि ओ (एन) को रखते हुए, लेकिन हो सकता है कि किसी अन्य विधि का उपयोग करने से अधिक समय लगे।


1
मुझे विश्वास नहीं हो रहा है कि ओपी ने कहा कि केवल एक छेद होने के बारे में कुछ भी कहा गया है। इनपुट संख्याओं की एक अनसुलझी सूची है - वे कुछ भी हो सकते हैं। यह आपके विवरण से स्पष्ट नहीं है कि आप यह निर्धारित करेंगे कि "कितने नंबर" होने चाहिए।
कालेब

@caleb इससे कोई फर्क नहीं पड़ता कि कितने छेद हैं, बस कोई डुप्लिकेट (जिसे हे (एन) में हैश सेट के साथ हटाया जा सकता है, हालांकि व्यवहार में जो अन्य तरीकों से अधिक ओवरहेड हो सकता है)। मैंने वर्णन को बेहतर बनाने की कोशिश की है, देखें कि क्या यह बेहतर है।
केविन

यह रैखिक नहीं है, IMO है। यह अधिक (logN) ^ 2 की तरह है। प्रत्येक चरण में, आप उस संग्रह के सबसेट की धुरी करते हैं, जिसकी आप परवाह करते हैं (पिछले सबरे के आधे हिस्से को, जिन्हें आपने "पहले छेद" के रूप में पहचाना है), फिर "छेद" होने पर, बाईं ओर या तो पुनरावृत्ति करें; या दाईं ओर यदि बाईं ओर नहीं है। (logN) ^ 2 अभी भी रैखिक से बेहतर है; यदि N दस गुना बढ़ जाता है तो आप केवल 2 (लॉग (N) -1) + 1 और चरणों के आदेश पर लेते हैं।
कीथएस

@ कीथ - दुर्भाग्य से, आपको उन्हें पिवट करने के लिए प्रत्येक स्तर पर सभी नंबरों को देखना होगा, इसलिए यह n + n / 2 + n / 4 + ... = 2n (तकनीकी रूप से, 2 (nm)) तुलना के बारे में लेगा ।
केविन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.