लिंक्ड लिस्ट में हमेशा टेल पॉइंटर होना चाहिए?


11

मेरी समझ...

लाभ:

  • अंत में डालने से O (N) के बजाय O (1) है।
  • यदि सूची एक डबली लिंक्ड सूची है, तो अंत से हटाने के लिए ओ (एन) के बजाय ओ (1) भी है।

हानि:

  • अतिरिक्त स्मृति की एक तुच्छ राशि लेता है: 4-8 बाइट्स
  • कार्यान्वयनकर्ता को पूंछ का ट्रैक रखना होगा।

इन फायदों और नुकसानों को देखते हुए, मैं यह नहीं देख सकता कि एक लिंक्ड लिस्ट कभी टेल पॉइंटर के इस्तेमाल से क्यों बचती है। क्या मुझे कुछ याद आ रहा है?


1
एक टेल पॉइंटर 4-8 बाइट्स (32 या 64 बिट सिस्टम पर निर्भर करता है)
शाफ़्ट फ्रीक

1
लगता है कि आपने पहले ही इसे बहुत संक्षेप में प्रस्तुत कर दिया है।
रॉबर्ट हार्वे

@RobertHarvey मैं अभी डेटा संरचनाओं का अध्ययन कर रहा हूं और मुझे इस बात की जानकारी नहीं है कि सर्वोत्तम प्रथाएं क्या हैं। इसलिए जो मैंने लिखा वह मेरे इंप्रेशन हैं, लेकिन मैं पूछ रहा हूं कि क्या वे सही हैं। लेकिन स्पष्ट करने के लिए धन्यवाद!
एडम ज़र्नर

7
"सर्वोत्तम प्रथाएं" आम जनता का अफीम हैं । इस तथ्य का जश्न मनाएं कि आपके पास अभी भी अपने लिए सोचने की क्षमता है।
रॉबर्ट हार्वे

लिंक @RobertHarvey के लिए धन्यवाद - मुझे यह पसंद है! मैं निश्चित रूप से एक लागत-लाभ का दृष्टिकोण लेता हूं जो स्थिति की बारीकियों को देखता है।
एडम Zerner

जवाबों:


7

आप सही हैं, एक पूंछ सूचक कभी भी दर्द नहीं करता है और केवल मदद कर सकता है। हालांकि, एक ऐसी स्थिति है जहां किसी को पूंछ सूचक की बिल्कुल भी आवश्यकता नहीं है।

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


9

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


3
क्या आप यह समझाना चाहेंगे कि वे दृढ़ता और अपरिवर्तनशीलता क्यों तोड़ते हैं?
एडम ज़र्नर

कृपया कैश
फ्रेंडली

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

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

0

मैं शायद ही कभी लिंक्ड सूचियों के लिए टेल पॉइंटर का उपयोग करता हूं और अधिक बार एकल-लिंक्ड सूचियों का उपयोग करने की प्रवृत्ति रखता हूं जहां एक स्टैक जैसा धक्का / सम्मिलन और हटाने का पॉप पैटर्न (या बीच से सिर्फ रैखिक-समय को हटाने) पर्याप्त होता है। ऐसा इसलिए है क्योंकि मेरे सामान्य उपयोग के मामलों में, टेल पॉइंटर वास्तव में महंगा है, जिस तरह एकल-लिंक की गई सूची को दोगुनी-लिंक वाली सूची में बनाना महंगा है।

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

एक उदाहरण के रूप में, एक 400x400 ग्रिड का उपयोग करके एक वीडियो गेम की कल्पना करें जो टकराव का पता लगाने में तेजी लाने के लिए एक दूसरे के चारों ओर घूमता है और एक-दूसरे से उछलता है। उस मामले में, स्टोर करने के लिए एक बहुत ही कुशल तरीका 160,000 एकल-लिंक की गई सूचियों को संग्रहीत करना है, जो मेरे मामले में 160,000 32-बिट पूर्णांक (~ 640 किलोबाइट) और एक 32-बिट पूर्णांक ओवरहेड प्रति कण में अनुवाद करता है। अब जैसे कि कण स्क्रीन पर घूमते हैं, हमें बस इतना करना है कि एक कण को ​​एक कोशिका से दूसरी कोशिका में स्थानांतरित करने के लिए कुछ 32-बिट पूर्णांकों को अपडेट किया जाए, जैसे:

यहाँ छवि विवरण दर्ज करें

... nextएक कण नोड के सूचकांक ("पॉइंटर") के साथ कोशिका में अगले कण के सूचकांक के रूप में सेवारत या अगले मुक्त कण को ​​पुनः प्राप्त करने के लिए यदि कण मर गया है (मूल रूप से सूचकांकों का उपयोग करके एक मुक्त सूची आवंटन कार्यान्वयन):

यहाँ छवि विवरण दर्ज करें

सेल से लीनियर-टाइम रिमूवल वास्तव में एक ओवरहेड नहीं है क्योंकि हम सेल में कणों के माध्यम से पुनरावृत्ति करके कण तर्क को संसाधित कर रहे हैं, इसलिए एक डबल-लिंक की गई सूची बस एक तरह का ओवरहेड जोड़ देगी जो कि फायदेमंद नहीं है मेरे सभी मामले में एक पूंछ के रूप में मुझे या तो बिल्कुल भी फायदा नहीं होगा।

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

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

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

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

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

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