हम अन्य डेटा संरचनाओं के बजाय सरणियों का उपयोग क्यों करते हैं?


195

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

तो, मूल रूप से, सरणियों का उपयोग करने का क्या मतलब है?

यह इतना नहीं है कि हम एक कंप्यूटर दृष्टिकोण से सरणियों का उपयोग क्यों करते हैं, बल्कि हम एक प्रोग्रामिंग दृष्टिकोण (एक सूक्ष्म अंतर) से सरणियों का उपयोग क्यों करेंगे। सरणी के साथ कंप्यूटर क्या करता है यह सवाल का बिंदु नहीं था।


2
क्यों नहीं माना जाता है कि कंप्यूटर सरणी के साथ क्या करता है? क्योंकि हमारे पास हम एक घर नंबर प्रणाली है सीधे सड़कों। तो क्या यह सरणियों के लिए है।
lcn

" अन्य डेटा संरचनाएं " या " एक और रूप " क्या आपका मतलब है? और किस उद्देश्य से?
तेवमादर

जवाबों:


770

सबक के लिए समय पर वापस जाने का समय। जबकि हम आज अपनी फैंसी प्रबंधित भाषाओं में इन चीजों के बारे में ज्यादा नहीं सोचते हैं, वे एक ही आधार पर बनाए गए हैं, तो आइए देखें कि सी में मेमोरी कैसे प्रबंधित की जाती है।

इससे पहले कि मैं गोता लगाऊं, शब्द " पॉइंटर " का एक त्वरित स्पष्टीकरण का मतलब है। एक पॉइंटर बस एक वैरिएबल है जो मेमोरी में किसी लोकेशन को "पॉइंट" करता है। यह स्मृति के इस क्षेत्र में वास्तविक मूल्य नहीं रखता है, इसमें इसके लिए स्मृति पता है। एक मेलबॉक्स के रूप में स्मृति के एक ब्लॉक के बारे में सोचो। पॉइंटर उस मेलबॉक्स का पता होगा।

सी में, एक सरणी एक ऑफसेट के साथ बस एक संकेतक है, ऑफसेट निर्दिष्ट करता है कि स्मृति में कितनी दूर देखना है। यह O (1) पहुंच का समय प्रदान करता है।

  MyArray   [5]
     ^       ^
  Pointer  Offset

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

उदाहरण के लिए, मान लें कि हमारे पास 6 संख्याओं (6,4,2,3,1,5) के साथ एक सरणी है, स्मृति में यह इस तरह दिखेगा:

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================

एक सरणी में, हम जानते हैं कि प्रत्येक तत्व स्मृति में एक दूसरे के बगल में है। एसी एरे ( MyArrayयहाँ कहा जाता है) केवल पहले तत्व के लिए एक संकेतक है:

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^
MyArray

अगर हम ऊपर देखना चाहते हैं MyArray[4], तो आंतरिक रूप से इसे इस तरह एक्सेस किया जाएगा:

   0     1     2     3     4 
=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
                           ^
MyArray + 4 ---------------/
(Pointer + Offset)

क्योंकि हम सूचक में ऑफसेट को जोड़कर सरणी में किसी भी तत्व को सीधे एक्सेस कर सकते हैं, हम सरणी के आकार की परवाह किए बिना किसी भी तत्व को उसी समय में देख सकते हैं। इसका मतलब है कि मिलने MyArray[1000]में उतना ही समय लगेगा जितना कि मिलने में MyArray[5]

एक वैकल्पिक डेटा संरचना एक लिंक की गई सूची है। यह बिंदुओं की एक रैखिक सूची है, प्रत्येक अगले नोड की ओर इशारा करता है

========    ========    ========    ========    ========
| Data |    | Data |    | Data |    | Data |    | Data |
|      | -> |      | -> |      | -> |      | -> |      | 
|  P1  |    |  P2  |    |  P3  |    |  P4  |    |  P5  |        
========    ========    ========    ========    ========

P(X) stands for Pointer to next node.

ध्यान दें कि मैंने प्रत्येक "नोड" को अपने ब्लॉक में बनाया है। ऐसा इसलिए है क्योंकि वे स्मृति में आसन्न होने की गारंटी नहीं हैं (और सबसे अधिक संभावना नहीं होगी)।

अगर मैं P3 को एक्सेस करना चाहता हूं, तो मैं इसे सीधे एक्सेस नहीं कर सकता, क्योंकि मुझे नहीं पता कि यह मेमोरी में कहां है। मुझे पता है कि रूट (P1) कहां है, इसलिए इसके बजाय मुझे P1 पर शुरू करना है, और प्रत्येक पॉइंटर को वांछित नोड पर फॉलो करना है।

यह एक ओ (एन) लुक अप टाइम है (जैसा कि प्रत्येक तत्व जोड़ा जाता है, लुक अप लागत बढ़ जाती है)। P4 की तुलना में P1000 प्राप्त करना बहुत अधिक महंगा है।

उच्च स्तरीय डेटा संरचनाएं, जैसे कि हैशटेबल्स, स्टैक्स और कतारें, सभी आंतरिक रूप से एक सरणी (या कई सरणियों) का उपयोग कर सकते हैं, जबकि लिंक्ड सूची और बाइनरी ट्रीड आमतौर पर नोड्स और पॉइंटर्स का उपयोग करते हैं।

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

हमारी सरणी फिर से ले लो। इस बार, मैं '5' मान रखने वाले सरणी तत्व को खोजना चाहता हूँ।

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^     ^     ^     ^     ^   FOUND!

इस स्थिति में, मुझे नहीं पता कि इसे खोजने के लिए पॉइंटर को क्या जोड़ना है, इसलिए मुझे 0 पर शुरू करना होगा, और अपना रास्ता तब तक काम करना होगा जब तक कि मैं इसे ढूंढ न लूं। इसका मतलब है कि मुझे 6 चेक करने होंगे।

इस वजह से, किसी सरणी में मान की खोज करना O (N) माना जाता है। जैसे-जैसे सरणी बड़ी होती जाती है, खोज की लागत बढ़ती जाती है।

ऊपर याद रखें जहां मैंने कहा था कि कभी-कभी गैर अनुक्रमिक डेटा संरचना का उपयोग करने के फायदे हो सकते हैं? डेटा की खोज इन फायदों में से एक है और सबसे अच्छा उदाहरण बाइनरी ट्री है।

एक बाइनरी ट्री एक लिंक की गई सूची के समान एक डेटा संरचना है, हालांकि एक नोड से लिंक करने के बजाय, प्रत्येक नोड दो बच्चों के नोड से जुड़ सकता है।

         ==========
         |  Root  |         
         ==========
        /          \ 
  =========       =========
  | Child |       | Child |
  =========       =========
                  /       \
            =========    =========
            | Child |    | Child |
            =========    =========

 Assume that each connector is really a Pointer

जब डेटा को एक बाइनरी ट्री में डाला जाता है, तो यह तय करने के लिए कई नियमों का उपयोग करता है कि नए नोड को कहां रखा जाए। मूल अवधारणा यह है कि यदि नया मूल्य माता-पिता से अधिक है, तो वह इसे बाईं ओर सम्मिलित करता है, यदि यह कम है, तो इसे दाईं ओर सम्मिलित करता है।

इसका मतलब है कि एक बाइनरी ट्री में मान इस तरह दिख सकते हैं:

         ==========
         |   100  |         
         ==========
        /          \ 
  =========       =========
  |  200  |       |   50  |
  =========       =========
                  /       \
            =========    =========
            |   75  |    |   25  |
            =========    =========

75 के मूल्य के लिए एक द्विआधारी पेड़ की खोज करते समय, हमें केवल इस संरचना के कारण 3 नोड्स (O (लॉग एन)) पर जाने की आवश्यकता है:

  • क्या 75 100 से कम है? राइट नोड पर देखें
  • क्या 75 50 से अधिक है? लेफ्ट नोड को देखें
  • वहाँ 75 है!

हालांकि हमारे पेड़ में 5 नोड्स हैं, हमें शेष दो को देखने की आवश्यकता नहीं थी, क्योंकि हम जानते थे कि वे (और उनके बच्चे) संभवतः उस मूल्य को शामिल नहीं कर सकते हैं जिसकी हम तलाश कर रहे थे। यह हमें एक खोज समय देता है कि सबसे खराब स्थिति का मतलब है कि हमें प्रत्येक नोड पर जाना है, लेकिन सबसे अच्छी स्थिति में हमें केवल नोड्स के एक छोटे हिस्से का दौरा करना होगा।

यह वह जगह है जहाँ सरणियों को हराया जाता है, वे ओ (1) एक्सेस समय के बावजूद एक रैखिक ओ (एन) खोज समय प्रदान करते हैं।

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


1
@ जोनाथन: आपने 5 वें तत्व को इंगित करने के लिए आरेख को अपडेट किया लेकिन आपने MyArray [4] को MyArray में भी बदल दिया [5] इसलिए यह अभी भी गलत है, सूचकांक को वापस 4 में बदलें और आरेख को इस प्रकार रखें और आपको अच्छा होना चाहिए ।
रॉबर्ट गैंबल

54
यह मुझे "सामुदायिक विकी" के बारे में क्या बताता है यह पोस्ट "उचित" प्रतिनिधि के लायक है
क्विबल्सम

8
अच्छा उत्तर। लेकिन आप जिस पेड़ का वर्णन करते हैं, वह एक द्विआधारी खोज वृक्ष है - एक द्विआधारी पेड़ सिर्फ एक पेड़ है जहां प्रत्येक नोड में अधिकतम दो बच्चे हैं। आपके पास किसी भी क्रम में तत्वों के साथ एक बाइनरी ट्री हो सकता है। जैसा कि आप वर्णन करते हैं बाइनरी सर्च ट्री का आयोजन किया जाता है।
ग्नुद

1
अच्छी व्याख्या, लेकिन मैं नाइटपिक करने में मदद नहीं कर सकता ... यदि आपको बाइनरी सर्च ट्री में आइटम को फिर से व्यवस्थित करने की अनुमति है, तो आप एरे में तत्वों को फिर से क्यों नहीं बना सकते हैं ताकि बाइनरी सर्च इसमें काम करे? आप किसी ट्री के लिए O (n) इंसर्ट / डिलीट के बारे में अधिक विवरण में जा सकते हैं, लेकिन O (n) एरे के लिए।
बाजार

2
क्या बाइनरी ट्री ओ (लॉग एन) का प्रतिनिधित्व नहीं करता है क्योंकि डेटा सेट के आकार के संबंध में पहुंच समय लॉगरिदमिक रूप से बढ़ जाता है?
इवान प्लाइस

73

O (1) यादृच्छिक अभिगम के लिए, जिसे पीटा नहीं जा सकता।


6
किस बिंदु पर? O (1) क्या है? रैंडम एक्सेस क्या है? इसे क्यों नहीं पीटा जा सकता? और दूसरी बात?
जेसन

3
O (1) का अर्थ है निरंतर समय, उदाहरण के लिए यदि आप किसी सरणी का n-esim तत्व प्राप्त करना चाहते हैं, तो आप इसे सीधे इसके अनुक्रमणिका (सरणी [n-1]) के माध्यम से एक्सेस करते हैं, उदाहरण के लिए एक लिंक की गई सूची के साथ, आपके पास है सिर को खोजने के लिए, और फिर अगले नोड क्रमिक रूप से n-1 बार जाएं जो O (n), रैखिक समय है।
सीएमएस

8
बिग-ओ नोटेशन बताता है कि एल्गोरिथ्म की गति इसके इनपुट के आकार के आधार पर कैसे भिन्न होती है। एक O (n) एल्गोरिथ्म को दो बार चलने में उतना ही समय लगेगा, जितना कि दो बार कई वस्तुओं के साथ चलने में और 8ish को कई वस्तुओं के साथ 8 गुना चलने में। दूसरे शब्दों में O (n) एल्गोरिथ्म की गति [cont ...] के साथ बदलती है
Gareth

8
इसके इनपुट का आकार। O (1) का तात्पर्य है कि इनपुट का आकार ('n') एल्गोरिथ्म की गति का कारक नहीं है, यह इनपुट आकार की परवाह किए बिना एक निरंतर गति है
गैरेथ

9
मैं आपका O (1) देखता हूं, और आपको O (0) बढ़ाता हूं।
क्रिस कॉनवे

23

सभी प्रोग्राम समान कार्य नहीं करते हैं या समान हार्डवेयर पर चलते हैं।

यह आमतौर पर उत्तर है कि विभिन्न भाषा सुविधाएँ क्यों मौजूद हैं। Arrays एक मुख्य कंप्यूटर विज्ञान अवधारणा है। सूची / मेट्रिसेस / वैक्टर / जो भी उन्नत डेटा संरचना होती है, के साथ एरेज़ को बदलना प्रदर्शन को गंभीर रूप से प्रभावित करेगा, और कई प्रणालियों में सर्वथा अयोग्य होगा। ऐसे कई मामले हैं जहां इन "उन्नत" डेटा संग्रह वस्तुओं में से एक का उपयोग करना चाहिए क्योंकि प्रश्न में कार्यक्रम के कारण।

व्यावसायिक प्रोग्रामिंग में (जो हम में से अधिकांश करते हैं), हम हार्डवेयर को लक्षित कर सकते हैं जो अपेक्षाकृत शक्तिशाली है। जावा में C # या वेक्टर में एक सूची का उपयोग करना इन परिस्थितियों में बनाने के लिए सही विकल्प है क्योंकि ये संरचनाएं डेवलपर को तेज़ी से लक्ष्यों को पूरा करने की अनुमति देती हैं, जो बदले में इस प्रकार के सॉफ़्टवेयर को अधिक चित्रित करने की अनुमति देता है।

एम्बेडेड सॉफ्टवेयर या एक ऑपरेटिंग सिस्टम लिखते समय एक सरणी अक्सर बेहतर विकल्प हो सकता है। जबकि एक सरणी कम कार्यक्षमता प्रदान करता है, यह कम रैम लेता है, और कंपाइलर लुक-अप के लिए कोड को अधिक कुशलता से अनुकूलित कर सकता है।

मुझे यकीन है कि मैं इन मामलों के लिए कई लाभ छोड़ रहा हूं, लेकिन मुझे उम्मीद है कि आपको यह बात मिल जाएगी।


4
विडंबना यह है कि जावा में आपको वेक्टर के बजाय एक ArrayList (या LinkedList) का उपयोग करना चाहिए। यह एक वेक्टर के साथ किया जाता है जिसे सिंक्रनाइज़ किया जाता है जो आमतौर पर अनावश्यक ओवरहेड होता है।
एशले

0

सरणियों के लाभों को देखने का एक तरीका यह देखना है कि सरणियों की O (1) पहुंच क्षमता की आवश्यकता कहां है और इसलिए इसे कैपिटल किया गया है:

  1. आपके एप्लिकेशन की लुक-अप तालिकाओं में (कुछ स्पष्ट प्रतिक्रियाओं तक पहुँचने के लिए एक स्थिर सरणी)

  2. संस्मरण (पहले से ही जटिल फ़ंक्शन परिणाम, ताकि आप फिर से फ़ंक्शन मान की गणना न करें, लॉग x कहें)

  3. इमेज प्रोसेसिंग के लिए हाई स्पीड कंप्यूटर विज़न एप्लिकेशन ( https://en.wikipedia.org/wiki/Lookup_table#Lookup_tables_in_image_processing )

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