एक श्रेणी से अधिकतम मान को गैर-सरणी में पुनर्प्राप्त करना


9

मेरे पास एक अनसुलझा सरणी है । मेरे पास प्रश्न हैं, जिसमें मैं एक सीमा देता हूं और फिर उस सीमा से अधिकतम मूल्य वापस करना होता है। उदाहरण के लिए:

array[]={23,17,9,45,78,2,4,6,90,1};
query(both inclusive): 2 6
answer: 78

मैं किस एल्गोरिथ्म या डेटा संरचना का निर्माण करता हूं, किसी भी सीमा से अधिकतम मूल्य प्राप्त करने के लिए। (बहुत सारे प्रश्न हैं)

संपादित करें: यह वास्तव में वास्तविक समस्या का एक सरल संस्करण है। मेरे पास सरणी का आकार 100000 तक और प्रश्नों की संख्या 100000 तक हो सकती है। इसलिए मुझे निश्चित रूप से कुछ प्रीप्रोसेसिंग की आवश्यकता है जो कि एक तेज क्वेरी प्रतिक्रिया की सुविधा प्रदान करेंगे।


5
यह क्यों अनसुलझा है? यदि यह हल है, तो समस्या तुच्छ है, इसलिए स्पष्ट दृष्टिकोण इसे क्रमबद्ध करना है।

1
@delnan कुछ अतिरिक्त तंत्र के बिना, आप खो देते हैं कि कौन से मूल्य मूल रूप से क्वियर होने की सीमा में थे ...
Thijs van Dien

अपनी पूरी समस्या निर्दिष्ट करें। यदि यह ज्ञान (या कोई अन्य जानकारी) मायने रखता है, तो किसी को समाधान में कारक को जानना होगा।

1
क्या मुझे कुछ याद आ रहा है, या यह सिर्फ 6 के माध्यम से आइटम 2 पर जाने और उन तत्वों का अधिकतम मूल्य खोजने का मामला है?
ब्लरफ्ल

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

जवाबों:


14

मुझे लगता है कि आप कुछ प्रकार के बाइनरी ट्री का निर्माण कर सकते हैं, जहां प्रत्येक नोड अपने बच्चों को अधिकतम मूल्य का प्रतिनिधित्व करता है:

            78           
     45            78     
  23    45     78      6  
23 17  9 45   78 2    4 6   

फिर आपको केवल यह निर्धारित करने का एक तरीका खोजने की आवश्यकता है कि आपको किन सीमाओं में अधिकतम मूल्य खोजने के लिए न्यूनतम नोड्स की जांच करनी है। इस उदाहरण में, इंडेक्स रेंज [2, 6](समावेशी) में अधिकतम मूल्य प्राप्त करने के लिए आप के max(45, 78, 4)बजाय होगा max(9, 45, 78, 2, 4)। जैसे-जैसे पेड़ बढ़ेगा, लाभ बड़ा होगा।


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

अन्यथा, +1 जैसा कि मुझे यह पता चलता है कि यह
खोजपूर्ण है

+1: यह लॉग (N) समय में किसी सूची के सबरेंज के बारे में प्रश्नों का उत्तर देने के लिए एक शक्तिशाली तकनीक है, मूल नोड पर डेटा को उपयोग करने योग्य बनाने के लिए बच्चों के डेटा से निरंतर समय में गणना की जा सकती है।
केविन क्लाइन

यह आइडिया कमाल का है। यह O (logn) क्वेरी समय देता है। मुझे लगता है कि @ इज़्काता ने भी एक अच्छी बात की। हम पेड़ के नोड को बाईं और दाईं श्रेणियों के बारे में जानकारी के साथ बढ़ा सकते हैं। तो एक सीमा को देखते हुए, यह समस्या को दो में विभाजित करना जानता है। अंतरिक्ष-वार, सभी आंकड़े पत्ती स्तर पर संग्रहीत किए जाते हैं। इसलिए इसे स्टोर करने के लिए 2 * N स्पेस की आवश्यकता होती है, जो O (N) है। मुझे नहीं पता कि एक सेगमेंट ट्री क्या है, लेकिन क्या यह सेगमेंट ट्री के पीछे का विचार है?
Kay

और प्रीप्रोसेसिंग के संदर्भ में, पेड़ के निर्माण में O (n) लगता है।
काय

2

Ngoaho91 के जवाब के पूरक के लिए।

इस समस्या को हल करने का सबसे अच्छा तरीका सेगमेंट ट्री डेटा संरचना का उपयोग करना है। यह आपको O (log (n)) में ऐसे प्रश्नों का उत्तर देने की अनुमति देता है, इसका मतलब होगा कि आपके एल्गोरिथ्म की कुल जटिलता O (Q logn) होगी जहां Q प्रश्नों की संख्या है। यदि आपने भोली एल्गोरिथ्म का उपयोग किया है, तो कुल जटिलता O (Q n) होगी जो स्पष्ट रूप से धीमी है।

हालाँकि, सेगमेंट ट्री के उपयोग में कमी है। यह बहुत सारी मेमोरी लेता है, लेकिन बहुत बार आप स्पीड के बारे में मेमोरी से कम ध्यान रखते हैं।

मैं इस DS द्वारा उपयोग किए गए एल्गोरिदम का संक्षेप में वर्णन करूंगा:

सेगमेंट ट्री बाइनरी सर्च ट्री का एक विशेष मामला है, जहाँ प्रत्येक नोड उस रेंज का मान रखता है जिसे उसे सौंपा गया है। रूट नोड, को रेंज दी गई है [0, n]। बाएं बच्चे को रेंज [0, (0 + n) / 2] और दाएं बच्चे को (0 + n) / 2 + 1, n] सौंपा गया है। इस तरह से पेड़ का निर्माण होगा।

ट्री बनाएँ :

/*
    A[] -> array of original values
    tree[] -> Segment Tree Data Structure.
    node -> the node we are actually in: remember left child is 2*node, right child is 2*node+1
    a, b -> The limits of the actual array. This is used because we are dealing
                with a recursive function.
*/

int tree[SIZE];

void build_tree(vector<int> A, int node, int a, int b) {
    if (a == b) { // We get to a simple element
        tree[node] = A[a]; // This node stores the only value
    }
    else {
        int leftChild, rightChild, middle;
        leftChild = 2*node;
        rightChild = 2*node+1; // Or leftChild+1
        middle = (a+b) / 2;
        build_tree(A, leftChild, a, middle); // Recursively build the tree in the left child
        build_tree(A, rightChild, middle+1, b); // Recursively build the tree in the right child

        tree[node] = max(tree[leftChild], tree[rightChild]); // The Value of the actual node, 
                                                            //is the max of both of the children.
    }
}

क्वेरी ट्री

int query(int node, int a, int b, int p, int q) {
    if (b < p || a > q) // The actual range is outside this range
        return -INF; // Return a negative big number. Can you figure out why?
    else if (p >= a && b >= q) // Query inside the range
        return tree[node];
    int l, r, m;
    l = 2*node;
    r = l+1;
    m = (a+b) / 2;
    return max(query(l, a, m, p, q), query(r, m+1, b, p, q)); // Return the max of querying both children.
}

यदि आपको और स्पष्टीकरण की आवश्यकता है, तो मुझे बताएं।

BTW, सेगमेंट ट्री O (लॉग एन) में एकल तत्व या तत्वों की एक श्रृंखला के अद्यतन का समर्थन करता है


पेड़ को भरने की जटिलता क्या है?
पीटर बी

आपको सभी तत्वों से गुजरना होगा, और O(log(n))प्रत्येक तत्व को पेड़ में जोड़ना होगा। इसलिए, कुल जटिलता हैO(nlog(n))
एंड्रस

1

सबसे अच्छा एल्गोरिथ्म ओ (एन) समय में होगा जैसा कि नीचे शुरू होता है, अंत में सीमा की सीमा का सूचकांक हो

int findMax(int[] a, start, end) {
   max = Integer.MIN; // initialize to minimum Integer

   for(int i=start; i <= end; i++) 
      if ( a[i] > max )
         max = a[i];

   return max; 
}

4
-1 एल्गोरिथ्म को दोहराने के लिए ओपी पर सुधार करने की कोशिश कर रहा था।
केविन क्लाइन

1
+1 में बताई गई समस्या का हल पोस्ट करने के लिए। यह वास्तव में ऐसा करने का एकमात्र तरीका है यदि आपके पास एक सरणी है और पता नहीं है कि सीमा क्या एक प्राथमिकता होने जा रही है । (हालांकि मैं शुरू में और पाश को शुरू करने के maxलिए होगा ।)a[i]fori+1
ब्लरफ्ल

@kevincline यह केवल आराम नहीं कर रहा है - यह भी कह रहा है "हां, आपके पास पहले से ही इस कार्य के लिए सबसे अच्छा एल्गोरिथ्म है", एक मामूली सुधार (कूदो start, पर रोक end) के साथ। और मैं इस बात से सहमत है, इस है एक बार की देखने के लिए सबसे अच्छा। @ ThijsvanDien का जवाब केवल बेहतर है यदि लुकअप कई बार होने वाला है, क्योंकि इसे शुरू में सेट होने में अधिक समय लगता है।
इज़्काता

दी गई, इस उत्तर को पोस्ट करने के समय, प्रश्न में यह पुष्टि करने वाला संपादन शामिल नहीं था कि वह एक ही डेटा पर कई प्रश्न कर रहा होगा।
इजाकाता

1

बाइनरी ट्री / सेगमेंट ट्री-आधारित समाधान वास्तव में सही दिशा में इंगित कर रहे हैं। एक आपत्ति कर सकते हैं कि उन्हें अतिरिक्त मेमोरी की आवश्यकता है। इन समस्याओं के दो समाधान हैं:

  1. बाइनरी ट्री के बजाय एक अंतर्निहित डेटा संरचना का उपयोग करें
  2. बाइनरी ट्री के बजाय M-ary ट्री का उपयोग करें

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

दूसरा बिंदु यह है कि, मूल्यांकन के दौरान थोड़ा और काम करने की कीमत पर, आप बाइनरी ट्री के बजाय एम-एरी ट्री का उपयोग कर सकते हैं। उदाहरण के लिए यदि आप 3-एरी ट्री का उपयोग करते हैं, तो आप एक बार में अधिकतम 3 तत्वों की गणना करेंगे, फिर एक समय में 9 तत्वों की, फिर 27 की, इत्यादि की आवश्यकता होगी। अतिरिक्त भंडारण तब N / (M-1) है - आप कर सकते हैं ज्यामितीय श्रृंखला सूत्र का उपयोग करके साबित करें। यदि आप M = 11 चुनते हैं, उदाहरण के लिए, आपको बाइनरी ट्री विधि के 1/10 वें भंडारण की आवश्यकता होगी।

आप सत्यापित कर सकते हैं कि पायथन में ये भोले और अनुकूलित कार्यान्वयन समान परिणाम देते हैं:

class RangeQuerier(object):
    #The naive way
    def __init__(self):
        pass

    def set_array(self,arr):
        #Set, and preprocess
        self.arr = arr

    def query(self,l,r):
        try:
            return max(self.arr[l:r])
        except ValueError:
            return None

बनाम

class RangeQuerierMultiLevel(object):
    def __init__(self):
        self.arrs = []
        self.sub_factor = 3
        self.len_ = 0

    def set_array(self,arr):
        #Set, and preprocess
        tgt = arr
        self.len_ = len(tgt)
        self.arrs.append(arr)
        while len(tgt) > 1:
            tgt = self.maxify_one_array(tgt)
            self.arrs.append(tgt)

    def maxify_one_array(self,arr):
        sub_arr = []
        themax = float('-inf')
        for i,el in enumerate(arr):
            themax = max(el,themax)
            if i % self.sub_factor == self.sub_factor - 1:
                sub_arr.append(themax)
                themax = float('-inf')
        return sub_arr

    def query(self,l,r,level=None):
        if level is None:
            level = len(self.arrs)-1

        if r <= l:
            return None

        int_size = self.sub_factor ** level 

        lhs,mid,rhs = (float('-inf'),float('-inf'),float('-inf'))

        #Check if there's an imperfect match on the left hand side
        if l % int_size != 0:
            lnew = int(ceil(l/float(int_size)))*int_size
            lhs = self.query(l,min(lnew,r),level-1)
            l = lnew
        #Check if there's an imperfect match on the right hand side
        if r % int_size != 0:
            rnew = int(floor(r/float(int_size)))*int_size
            rhs = self.query(max(rnew,l),r,level-1)
            r = rnew

        if r > l:
            #Handle the middle elements
            mid = max(self.arrs[level][l/int_size:r/int_size])
        return max(max(lhs,mid),rhs)

0

"सेगमेंट ट्री" डेटा संरचना आज़माएं
2 चरण
build_tree () O (n)
क्वेरी (int min, int max) O (nlogn) हैं

http://en.wikipedia.org/wiki/Segment_tree

संपादित करें:

आप लोग मेरे द्वारा भेजे गए विकी को न पढ़ें!

यह एल्गोरिथम है:
- आप ट्री बनाने के लिए 1 बार सरणी को पार करते हैं। O (n)
- अगले 100000000+ बार आप सरणी के किसी भी हिस्से का अधिकतम जानना चाहते हैं, बस क्वेरी फ़ंक्शन को कॉल करें। हर क्वेरी के लिए O (logn)
- c ++ यहां लागू करें geeksforgeeks.org/segment-tree-set-1-range-minimum-query/
पुराना एल्गोरिथ्म है:
प्रत्येक क्वेरी, बस चयनित क्षेत्र का पता लगाएं और खोजें।

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

लाइन 1 के लिए: 0-1000000 से 50000 यादृच्छिक संख्या, '(स्पेस)' (यह एरे)
लाइन से विभाजित होती है 2: 2 यादृच्छिक संख्या 1 से 50000 तक, '(स्थान)' (यह क्वेरी है) द्वारा विभाजित
...
पंक्ति 200000: पंक्ति 2 पसंद करता है, यह यादृच्छिक क्वेरी भी है

यह उदाहरण की समस्या है, क्षमा करें, लेकिन यह
HTTP http://vn.spoj.com/problems/NKLINEUP/ में है
यदि आप इसे पुराने तरीके से हल करते हैं, तो आप कभी भी पास नहीं होते हैं।


3
मुझे नहीं लगता कि यह प्रासंगिक है। एक अंतराल का पेड़ अंतराल रखता है, पूर्णांक नहीं, और वे संचालन की अनुमति देते हैं जो ओपी के लिए पूछते हैं जैसे कुछ भी नहीं दिखता है। आप निश्चित रूप से, सभी संभव अंतराल उत्पन्न कर सकते हैं और उन्हें एक अंतराल के पेड़ में संग्रहीत कर सकते हैं, लेकिन (1) उनमें से कई तेजी से हैं, इसलिए यह पैमाने पर नहीं है, और (2) संचालन अभी भी ओपी की तरह नहीं दिखते हैं पूछने के लिए।

मेरी गलती, मेरा मतलब है सेगमेंट ट्री, इंटरवल ट्री नहीं।
ngoaho91

दिलचस्प है, मुझे लगता है कि मैं इस पेड़ पर कभी नहीं आया हूँ! IIUC को अभी भी सभी संभावित अंतरालों को संग्रहीत करने की आवश्यकता है, हालांकि। मुझे लगता है कि उनमें से O (n ^ 2) है, जो महंगा है। (इसके अलावा, कश्मीर ओ परिणाम के लिए O (log n + k) नहीं होना चाहिए ?

हाँ, शून्य build_tree () को सरणी को पार करना चाहिए। और हर नोड्स के लिए अधिकतम (या मिनट) मान स्टोर करें। लेकिन कई मामलों में, मेमोरी लागत गति से महत्वपूर्ण नहीं है।
ngoaho91

2
मैं इस बात की कल्पना नहीं कर सकता कि यह O(n)अरुण के सादे खोज से कहीं ज्यादा तेज है , जैसा कि tarun_telang के उत्तर में वर्णित है। पहली वृत्ति है कि O(log n + k)की तुलना में तेज है O(n), लेकिन O(log n + k)उप-सरणी की सिर्फ पुनर्प्राप्ति है - O(1)शुरुआत और अंत बिंदुओं को देखते हुए सरणी पहुंच के बराबर । आपको अधिकतम खोजने के लिए अभी भी इसे पार करने की आवश्यकता होगी।
इजाकाता

0

आप विरल तालिका नामक डेटा संरचना का उपयोग करके ओ (1) प्रति क्वेरी (ओ (एन लॉग एन) निर्माण के साथ) प्राप्त कर सकते हैं। 2 की प्रत्येक शक्ति के लिए आइए इस लंबाई के प्रत्येक खंड के लिए अधिकतम बचत करें। अब दिए गए सेगमेंट [l, r) से आपको अधिकतम k [l + 2 ^ k) और [r-2 ^ k, r) पर अधिकतम k प्राप्त होता है। वे ओवरलैप करते हैं लेकिन यह ठीक है

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