दिलचस्प पोस्ट
फ़्लाइट पर, jQuery दिनांकपिकर सेट चयनित तारीख कैसे प्रभावी ढंग से mongoDB संबंधित मुद्दे को हल करने के लिए? क्या मतलब है। Delegate = self? React.js में सरणी बच्चों के लिए अद्वितीय कुंजी को समझना IPython नोटबुक के साथ लिंक स्पार्क लिंक एंड्रॉइड: पृष्ठभूमि छवि का आकार (पिक्सेल में) जो सभी उपकरणों का समर्थन करता है Javascript-function में getElementsByClassName का उपयोग कैसे करें? क्या एक आईओएस ऐप ने अपने बंडल के भीतर प्रवेश का उपयोग किया है? माइक्रोसॉफ्ट विज़ुअल स्टूडियो ~ सी / सी ++ रनटाइम लाइब्रेरी ~ स्थैतिक / गतिशील लिंकिंग जेनरिक -उपन और बंद निर्मित प्रकार Excel VBA में पाश के बिना कई पंक्तियों को कैसे हटाएं? मैं iPhone पर एक लेखन योग्य पथ कैसे प्राप्त करूं? Google लाइन चार्ट / Google लाइन चार्ट किंवदंती हेरफेर के लिए अपने स्वयं के कस्टम किंवदंतियों को कैसे लिखें गिट – डेटाबेस में सभी ऑब्जेक्ट्स को कैसे सूचीबद्ध किया जाए कार्यालय स्थापित किए बिना मैं प्रोग्राम को कैसे एक एक्सेल लिख सकता हूँ, पढ़ सकता / सकती हूं?

आधुनिक सी ++ में क्लासिक सॉर्टिंग एल्गोरिदम कैसे कार्यान्वित करें?

सी ++ मानक लाइब्रेरी से std::nth_element std::sort एल्गोरिदम (और उसके चचेरे भाई std::partial_sort और std::nth_element ) अधिक कार्यान्वयन में एक जटिल और हाइब्रिड एकीकरण है जिसमें अधिक प्राथमिक सॉर्टिंग एल्गोरिदम , जैसे चयन सॉर्ट, प्रविष्टि सॉर्ट, त्वरित सॉर्ट , सॉर्ट करें, या शेप सॉर्ट करें।

यहां और बहन साइटों जैसे कि https://codereview.stackexchange.com/ बग्स, जटिलता और इन क्लासिक सॉर्टिंग एल्गोरिदम के कार्यान्वयन के अन्य पहलुओं से जुड़े कई प्रश्न हैं। प्रस्तावित कार्यान्वयन में से अधिकांश कच्चे छोरों से मिलते हैं, सूचकांक हेरफेर और ठोस प्रकार का उपयोग करते हैं, और आमतौर पर शुद्धता और दक्षता के मामले में विश्लेषण करने के लिए गैर-तुच्छ होते हैं।

प्रश्न : उपर्युक्त क्लासिक सॉर्टिंग एल्गोरिदम को आधुनिक सी ++ का उपयोग कैसे किया जा सकता है?

  • कोई कच्चा छोर नहीं , लेकिन <algorithm> से मानक पुस्तकालय के एल्गोरिथम निर्माण ब्लॉकों का संयोजन
  • इटरेटर अंतरफलक और सूचकांक हेरफेर और कंक्रीट प्रकार के बजाय टेम्पलेट्स का उपयोग
  • सी ++ 14 शैली , पूर्ण मानक पुस्तकालय सहित, साथ ही साथ auto , टेम्पलेट उपनाम, पारदर्शी तुलनित्र और बहुरूप लम्बादा जैसे वाक्यविन्यास शोर reducers।

नोट्स :

  • सॉर्टिंग एल्गोरिदम के कार्यान्वयन पर अधिक संदर्भ के लिए विकिपीडिया , रोजेटा कोड या http://www.sorting-algorithms.com/ देखें
  • शॉन पेरेंट के सम्मेलनों (स्लाइड 39) के अनुसार, एक कच्चा लूप ऑपरेटर के साथ दो फ़ंक्शंस की संरचना से अधिक लंबा है। तो f(g(x)); या f(x); g(x); f(x); g(x); या f(x) + g(x); कच्चे लूप नहीं हैं, और न ही चयन_सॉर्ट में लूप हैं और नीचे insertion_sort हैं
  • मैं पहले सी ++ 1 के रूप में सी ++ के रूप में पहले से ही सी ++ के लिए स्कॉट मैयर्स की शब्दावली का पालन करता हूं और सी ++ 98 और सी ++ 03 दोनों को सी ++ 98 के रूप में दर्शाता हूं, इसलिए मुझे उस के लिए ज्वाला न दें।
  • जैसा कि @ मेहरदड की टिप्पणियों में सुझाव दिया गया है, मैं उत्तर के अंत में एक लाइव उदाहरण के रूप में चार कार्यान्वयन प्रदान करता हूं: C ++ 14, C ++ 11, C ++ 98 और बूस्ट और सी ++ 98।
  • उत्तर ही सी ++ 14 के संदर्भ में प्रस्तुत किया गया है जहां प्रासंगिक हो, मैं वाक्यविन्यास और पुस्तकालय के अंतर को दर्शाता हूं जहां विभिन्न भाषा संस्करण भिन्न होते हैं।

वेब के समाधान से एकत्रित समाधान "आधुनिक सी ++ में क्लासिक सॉर्टिंग एल्गोरिदम कैसे कार्यान्वित करें?"

एल्गोरिथम निर्माण ब्लॉकों

हम मानक पुस्तकालय से एल्गोरिथम निर्माण ब्लॉकों को इकट्ठा करके शुरू करते हैं:

 #include <algorithm> // min_element, iter_swap, // upper_bound, rotate, // partition, // inplace_merge, // make_heap, sort_heap, push_heap, pop_heap, // is_heap, is_sorted #include <cassert> // assert #include <functional> // less #include <iterator> // distance, begin, end, next 
  • इटरेटर उपकरण जैसे गैर सदस्य std::begin() / std::end() साथ ही std::next() के साथ ही सी ++ 11 और उससे आगे के रूप में उपलब्ध हैं। सी ++ 98 के लिए, आपको खुद को लिखना होगा। boost::begin() से विकल्प हैं। boost::begin() में रेंज boost::begin() / boost::end() , और बूस्ट से। boost::next() में उपयोगिता boost::next()
  • std::is_sorted एल्गोरिदम केवल C ++ 11 और उससे आगे के लिए उपलब्ध है। सी ++ 98 के लिए, यह std::adjacent_find और एक हाथ लिखित फ़ंक्शन ऑब्जेक्ट के संदर्भ में कार्यान्वित किया जा सकता है। बूस्ट। boost::algorithm::is_sorted एक boost::algorithm::is_sorted प्रदान करता है boost::algorithm::is_sorted एक विकल्प के रूप में
  • std::is_heap एल्गोरिथ्म केवल C ++ 11 और उससे आगे के लिए उपलब्ध है।

Syntactical उपहार

सी ++ 14 फॉर्म के पारदर्शी तुलनित्रों को प्रदान करता है std::less<> जो उनके तर्कों पर बहुरूपता से कार्य करता है। यह एक इटरेटर प्रकार प्रदान करने से बचा जाता है। इसका उपयोग सी ++ 11 के डिफॉल्ट फ़ंक्शन टेम्प्लेट तर्कों के साथ संयोजन में किया जा सकता है ताकि एल्गोरिदम सॉर्ट करने के लिए एक अधिभार तैयार किया जा सके जो कि तुलनात्मक रूप से और जो कि उपयोगकर्ता परिभाषित तुलना फ़ंक्शन ऑब्जेक्ट है।

 template<class It, class Compare = std::less<>> void xxx_sort(It first, It last, Compare cmp = Compare{}); 

सी ++ 11 में, कोई पुनरावचूनीय टेम्पलेट एराइज को एक इटेटर के मूल्य प्रकार को निकालने के लिए परिभाषित कर सकता है जो सॉर्ट एल्गोरिदम के हस्ताक्षरों के लिए छोटे अव्यवस्था को जोड़ता है:

 template<class It> using value_type_t = typename std::iterator_traits<It>::value_type; template<class It, class Compare = std::less<value_type_t<It>>> void xxx_sort(It first, It last, Compare cmp = Compare{}); 

सी ++ 98 में, एक को दो अधिभार लिखने और वर्बोस typename xxx<yyy>::type वाक्यविन्यास का उपयोग करने की आवश्यकता है

 template<class It, class Compare> void xxx_sort(It first, It last, Compare cmp); // general implementation template<class It> void xxx_sort(It first, It last) { xxx_sort(first, last, std::less<typename std::iterator_traits<It>::value_type>()); } 
  • एक अन्य वाक्यविन्यास यह है कि सी ++ 14 पॉलिमॉर्फ़िक लैम्ब्डास के माध्यम से उपयोगकर्ता परिभाषित तुलनित्रों को लपेटने की सुविधा देता है ( auto मापदंडों के साथ जो फ़ंक्शन टेम्पलेट तर्कों की तरह काम करता है)।
  • सी ++ 11 में केवल मोनोमोर्फिक लैंबदास हैं, जिनके लिए उपरोक्त टेम्पलेट उपनाम value_type_t का उपयोग करने की आवश्यकता होती है
  • C ++ 98 में, एक को एक स्टैंडअलोन फ़ंक्शन ऑब्जेक्ट लिखना होगा या वर्बोज़ std::bind1st / std::bind2nd / std::not1 प्रकार के वाक्यविन्यास के लिए std::not1
  • बूस्ट। boost::bind साथ boost::bind और _1 / _2 प्लेसहोल्डर वाक्यविन्यास
  • सी ++ 11 और उसके बाद भी std::find_if_not , जबकि C ++ 98 को std::find_if को फ़ंक्शन वस्तु के चारों ओर एक std::not1 साथ की आवश्यकता है

सी ++ शैली

कोई आम तौर पर स्वीकार्य नहीं है सी + 14 शैली अभी तक इससे भी बदतर के लिए, मैं स्कॉट मैयर्स के मसौदा प्रभावी आधुनिक सी ++ और हर्ब सटर के पुर्नोत्थान GotW का बारीकी से पालन करता हूं । मैं निम्नलिखित शैली सिफारिशों का उपयोग करता हूं:

  • हर्ब सटर की "लगभग हमेशा ऑटो" और स्कॉट मेयर्स की "विशिष्ट प्रकार की घोषणाओं को प्राथमिकता" सिफारिश, जिसके लिए संक्षिप्तता नायाब है, हालांकि इसकी स्पष्टता कभी-कभी विवादित होती है
  • स्कॉट मेयेर्स "वस्तुएं बनाते समय" और {} ऑब्जेक्ट बनाते समय और अच्छे पुराने कोष्ठक के प्रारंभिक () बजाय सामान्य कोड में सभी सबसे अधिक विचलन-पार्स समस्याओं को हल करने के लिए {} बजाय ब्रेसिज़-इनिशियलाइज़ {} चुनते हैं।
  • स्कॉट मेयेर्स "टाइपिंगफेस के लिए उपनाम घोषणा करना पसंद करते हैं" टेम्पलेट्स के लिए यह वैसे भी जरूरी है, और typedef बजाय हर जगह इसे इस्तेमाल करते समय समय बचाता है और स्थिरता जोड़ती है
  • पहले से सॉर्ट किए गए उप-श्रेणियों के लिए लूप अपरिवर्तनीय जांच की अनुमति देने के लिए, मैं कुछ स्थानों में एक के for (auto it = first; it != last; ++it) पैटर्न का उपयोग करता हूं उत्पादन कोड में, while (first != last) और एक ++first लूप के अंदर ++first कहीं थोड़ा बेहतर हो सकता है

चयन छांटना

चयन सॉर्ट किसी भी तरह से डेटा के लिए अनुकूल नहीं होता है, इसलिए इसका रनटाइम हमेशा O(N^2) । हालांकि, चयन सॉर्ट में स्वैप की संख्या को कम करने की संपत्ति होती है। ऐसे अनुप्रयोगों में जहां सामान गमागमन की लागत अधिक है, चयन क्रम बहुत अच्छी तरह से पसंद का एल्गोरिथ्म हो सकता है।

इसे मानक लाइब्रेरी का उपयोग करने के लिए, शेष न्यूनतम तत्व को खोजने के लिए बार-बार std::min_element का उपयोग करें, और iter_swap इसे स्थान में स्वैप करने के लिए:

 template<class FwdIt, class Compare = std::less<>> void selection_sort(FwdIt first, FwdIt last, Compare cmp = Compare{}) { for (auto it = first; it != last; ++it) { auto const selection = std::min_element(it, last, cmp); std::iter_swap(selection, it); assert(std::is_sorted(first, std::next(it), cmp)); } } 

ध्यान दें कि selection_sort की पहले से ही संसाधित सीमा है [first, it) उसकी लूप अपरियंट के रूप में क्रमबद्ध है। std::sort के रैंडम एक्सेस इटेटेटर्स की तुलना में कम से कम आवश्यकताओं को आगे बढ़ाते हैं

विवरण छोड़े गए :

  • चयन क्रम को प्रारंभिक परीक्षण से अनुकूलित किया जा सकता है if (std::distance(first, last) <= 1) return; (या आगे / द्विदिश दोहनकर्ताओं के लिए: if (first == last || std::next(first) == last) return; )
  • द्विदिश तेरेटर के लिए , ऊपर दिए गए परीक्षण को अंतराल पर [first, std::prev(last)) पर जोड़ा जा सकता है, क्योंकि अंतिम तत्व न्यूनतम शेष तत्व होने की गारंटी है और स्वैप की आवश्यकता नहीं है।

सम्मिलन सॉर्ट

यद्यपि यह O(N^2) सबसे खराब-केस समय के साथ प्राथमिक सॉर्टिंग एल्गोरिदम में से एक है, सम्मिलन सॉर्ट विकल्प का एल्गोरिथ्म है, या जब डेटा लगभग क्रमबद्ध होता है (क्योंकि यह अनुकूली है ) या जब समस्या का आकार छोटा है (क्योंकि यह कम भूमि के ऊपर है)। इन कारणों के लिए, और क्योंकि यह भी स्थिर है , सम्मिलन सॉर्ट का उपयोग प्रायः आधारभूत आधार केस (जब समस्या का आकार छोटा होता है) के रूप में किया जाता है क्योंकि उच्च ओवरहेड डिवाइड-और-जीत सॉर्टिंग एल्गोरिदम, जैसे मर्ज सॉर्ट या त्वरित सॉर्ट

मानक लाइब्रेरी के साथ insertion_sort को कार्यान्वित करने के लिए, उस स्थान को ढूंढने के लिए बार-बार std::upper_bound का उपयोग करें जहां वर्तमान तत्व को जाने की जरूरत है, और इनपुट श्रेणी में उर्वरक शेष तत्वों को स्थानांतरित करने के लिए std::rotate का उपयोग करें:

 template<class FwdIt, class Compare = std::less<>> void insertion_sort(FwdIt first, FwdIt last, Compare cmp = Compare{}) { for (auto it = first; it != last; ++it) { auto const insertion = std::upper_bound(first, it, *it, cmp); std::rotate(insertion, it, std::next(it)); assert(std::is_sorted(first, std::next(it), cmp)); } } 

ध्यान दें कि insertion_sort में पहले से ही संसाधित सीमा है [first, it) उसकी लूप अपरिवर्तनीय के रूप में क्रमबद्ध है। सम्मिलन सॉर्ट अग्रेषित Iterators के साथ काम करता है।

विवरण छोड़े गए :

  • सम्मिलन सॉर्ट जल्दी परीक्षण के साथ अनुकूलित किया जा सकता है if (std::distance(first, last) <= 1) return; (या आगे / द्विदिश दोहनकर्ताओं के लिए: if (first == last || std::next(first) == last) return; ) और अंतराल पर एक पाश [std::next(first), last) , क्योंकि पहला तत्व जगह में होने की गारंटी है और उसे घुमाए जाने की आवश्यकता नहीं है।
  • द्विदिश तेरेटर के लिए , सम्मिलन बिंदु को खोजने के लिए द्विआधारी खोज को मानक पुस्तकालय के std::find_if_not एल्गोरिथम का उपयोग कर एक रिवर्स रैखिक खोज से बदला जा सकता है।

नीचे दिए गए टुकड़े के लिए चार लाइव उदाहरण ( C ++ 14 , C + + 11 , C ++ 98 और बूस्ट , C ++ 98 ):

 using RevIt = std::reverse_iterator<BiDirIt>; auto const insertion = std::find_if_not(RevIt(it), RevIt(first), [=](auto const& elem){ return cmp(*it, elem); } ).base(); 
  • यादृच्छिक इनपुट के लिए यह O(N^2) तुलना करता है, लेकिन यह लगभग क्रमबद्ध इनपुट के लिए O(N) तुलना में सुधार करता है। बाइनरी खोज हमेशा O(N log N) तुलना करती है।
  • छोटी इनपुट श्रेणियों के लिए, रैखिक खोज का बेहतर मेमोरी इलाका (कैश, प्रीफेटिंग) भी द्विआधारी खोज पर हावी हो सकता है (एक कोर्स को इसका परीक्षण करना चाहिए)।

जल्दी से सुलझाएं

जब सावधानी से कार्यान्वित किया जाता है, तो त्वरित क्रम मजबूत होता है और O(N log N) उम्मीद की जटिलता होती है, लेकिन O(N^2) खराब-केस जटिलता के साथ जो प्रतिकूल रूप से चुने हुए इनपुट डेटा से शुरू हो सकती है जब एक स्थिर प्रकार की जरुरत नहीं होती है, तो त्वरित सॉर्ट एक उत्कृष्ट सामान्य प्रयोजन सॉर्ट है।

सरलतम संस्करणों के लिए, क्लासिक सॉर्टिंग एल्गोरिदम की तुलना में मानक लाइब्रेरी का उपयोग करने के लिए त्वरित सॉर्ट काफी जटिल है। नीचे दिए गए दृष्टिकोण इनपुट रेंज [first, last) को धुरी के रूप में मध्यरेखा का पता लगाने के लिए कुछ इटरेटर उपयोगिताओं का उपयोग करता है, फिर दो कॉल का उपयोग std::partition (जो O(N) ) से तीन तरह के विभाजन के लिए इनपुट क्रमशः चयनित तत्वों से छोटे, समान, और बड़े तत्वों के क्षेत्र में रेंज। अंत में पिवोट से छोटे और बड़े तत्वों के साथ दो बाहरी खंड फिर से सॉर्ट किए जाते हैं:

 template<class FwdIt, class Compare = std::less<>> void quick_sort(FwdIt first, FwdIt last, Compare cmp = Compare{}) { auto const N = std::distance(first, last); if (N <= 1) return; auto const pivot = *std::next(first, N / 2); auto const middle1 = std::partition(first, last, [=](auto const& elem){ return cmp(elem, pivot); }); auto const middle2 = std::partition(middle1, last, [=](auto const& elem){ return !cmp(pivot, elem); }); quick_sort(first, middle1, cmp); // assert(std::is_sorted(first, middle1, cmp)); quick_sort(middle2, last, cmp); // assert(std::is_sorted(middle2, last, cmp)); } 

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

विवरण छोड़े गए :

  • उपरोक्त क्रियान्वयन विशेष रूप से विशेष निविष्टियों के लिए कमजोर है, उदाहरण के लिए, " अंग पाइप " इनपुट 1, 2, 3, ..., N/2, ... 3, 2, 1 लिए O(N^2) जटिलता है क्योंकि मध्य सभी अन्य तत्वों से हमेशा बड़ा होता है)।
  • लगभग क्रमबद्ध निविष्टियों के खिलाफ इनपुट रेंज गार्ड से बेतरतीब ढंग से चुने गए तत्वों से -3 के औसत चयन के लिए जटिलता अन्यथा O(N^2) बिगड़ती है।
  • 3-रास्ता विभाजन (पिवोट से छोटा, बराबर और बड़ा तत्वों को अलग करना) के रूप में दो कॉल द्वारा std::partition को दिखाया गया है, यह परिणाम प्राप्त करने के लिए सबसे कुशल O(N) एल्गोरिथ्म नहीं है।
  • यादृच्छिक अभिगम इटेरेटर के लिए , एक गारंटीकृत O(N log N) जटिलता std::nth_element(first, middle, last) का उपयोग करते हुए मध्यकालीन धुरी चयन के माध्यम से प्राप्त की जा सकती है, जिसके बाद quick_sort(first, middle, cmp) और quick_sort(middle, last, cmp)
  • हालांकि यह गारंटी एक लागत पर आता है, क्योंकि std::nth_element की O(N) जटिलता के निरंतर कारक O(1) की औसत से अधिक महंगी हो सकती है, जो कि उसके बाद के मध्य-तीन-पिवट की जटिलता है O(N) को std::partition कॉल करें (जो आंकड़ों पर एक कैश-मैत्रीपूर्ण एकल फ़ॉरवर्ड पास है)।

मर्ज़ सॉर्ट

यदि O(N) अतिरिक्त स्थान का उपयोग किसी भी चिंता का नहीं है, तो मर्ज सॉर्ट एक उत्कृष्ट विकल्प है: यह एकमात्र स्थिर O(N log N) सॉर्टिंग एल्गोरिदम है

मानक एल्गोरिदम का उपयोग करना आसान है: इनपुट रेंज [first, last) के बीच का पता लगाने के लिए कुछ इटरेटर उपयोगिताओं का उपयोग करें और std::inplace_merge साथ दो पुनरावर्ती क्रमबद्ध खंडों को std::inplace_merge :

 template<class BiDirIt, class Compare = std::less<>> void merge_sort(BiDirIt first, BiDirIt last, Compare cmp = Compare{}) { auto const N = std::distance(first, last); if (N <= 1) return; auto const middle = std::next(first, N / 2); merge_sort(first, middle, cmp); // assert(std::is_sorted(first, middle, cmp)); merge_sort(middle, last, cmp); // assert(std::is_sorted(middle, last, cmp)); std::inplace_merge(first, middle, last, cmp); // assert(std::is_sorted(first, last, cmp)); } 

मर्ज सॉर्ट std::inplace_merge iterators की आवश्यकता है, बाधाओं std::inplace_merge । ध्यान दें कि जब लिंक्ड सूचियों को सॉर्ट करते हैं, तो मर्ज सॉर्ट के लिए केवल O(log N) अतिरिक्त स्थान (रिकर्सन के लिए) की आवश्यकता होती है। उत्तरार्द्ध एल्गोरिथ्म std::list<T>::sort मानक लाइब्रेरी में std::list<T>::sort द्वारा कार्यान्वित किया गया है।

ढेर बनाएं और छांटें

हीप सॉर्ट को लागू करने के लिए सरल है, O(N log N) इन-प्लेस सॉर्ट करता है, लेकिन स्थिर नहीं है।

पहला लूप, O(N) "हेफ़ेइफ़" चरण, सरणी को ढेर के क्रम में रखता है दूसरा पाश, O(N log N ) "सॉर्टडाउन" चरण, बार-बार अधिकतम निकासी करता है और ढेर के आदेश को पुनर्स्थापित करता है। मानक पुस्तकालय यह अत्यंत सीधा बनाता है:

 template<class RandomIt, class Compare = std::less<>> void heap_sort(RandomIt first, RandomIt last, Compare cmp = Compare{}) { lib::make_heap(first, last, cmp); // assert(std::is_heap(first, last, cmp)); lib::sort_heap(first, last, cmp); // assert(std::is_sorted(first, last, cmp)); } 

यदि आप std::make_heap और std::sort_heap का उपयोग करने के लिए "धोखाधड़ी" std::sort_heap , तो आप एक स्तर की गहराई से जा सकते हैं और उन कार्यों को स्वयं std::push_heap और std::pop_heap संदर्भ में लिख सकते हैं:

 namespace lib { // NOTE: is O(N log N), not O(N) as std::make_heap template<class RandomIt, class Compare = std::less<>> void make_heap(RandomIt first, RandomIt last, Compare cmp = Compare{}) { for (auto it = first; it != last;) { std::push_heap(first, ++it, cmp); assert(std::is_heap(first, it, cmp)); } } template<class RandomIt, class Compare = std::less<>> void sort_heap(RandomIt first, RandomIt last, Compare cmp = Compare{}) { for (auto it = last; it != first;) { std::pop_heap(first, it--, cmp); assert(std::is_heap(first, it, cmp)); } } } // namespace lib 

मानक लाइब्रेरी push_heap और pop_heap को जटिलता O(log N) रूप में निर्दिष्ट करती है हालांकि ध्यान दें कि सीमा पर [first, last) बाहरी लूप को make_heap लिए O(N log N) जटिलता में make_heap , जबकि std::make_heap में केवल O(N) जटिलता है समग्र O(N log N) के लिए heap_sort जटिलता यह कोई फर्क नहीं पड़ता।

विवरण छोड़े गए : O(N) make_heap कार्यान्वयन

परिक्षण

यहां विभिन्न प्रकार के इनपुटों (संपूर्ण या कठोर होने के लिए नहीं) के सभी पांच एल्गोरिदम का परीक्षण यहां चार लाइव उदाहरण ( C ++ 14 , C ++ 11 , C ++ 98 और बूस्ट , C ++ 98 ) हैं। बस एलओसी: सी ++ 11 / सी ++ 14 में 130 एलओसी, सी +8 9 और बूस्ट 190 (+ 50%) और सी +2 9 0 270 से अधिक (+ 100%) की आवश्यकता है।

मूल रूप से कोड समीक्षा में पाया गया एक और छोटा और बहुत सुरुचिपूर्ण मैंने सोचा कि यह साझा करने के लायक था

सॉर्ट करना गिनती

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

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

 template<typename ForwardIterator> void counting_sort(ForwardIterator first, ForwardIterator last) { if (first == last || std::next(first) == last) return; auto minmax = std::minmax_element(first, last); // avoid if possible. auto min = *minmax.first; auto max = *minmax.second; if (min == max) return; using difference_type = typename std::iterator_traits<ForwardIterator>::difference_type; std::vector<difference_type> counts(max - min + 1, 0); for (auto it = first ; it != last ; ++it) { ++counts[*it - min]; } for (auto count: counts) { first = std::fill_n(first, count, min++); } } 

जबकि यह केवल तब उपयोगी होता है जब पूर्णांक के प्रकार को छोटा (आम तौर पर संग्रह के आकार के आकार से बड़ा नहीं) जाना जाता है, और अधिक सामान्य रूप से गिनती करते हुए इसे अपने सर्वोत्तम मामलों के लिए धीमा कर देता है यदि सीमा छोटा नहीं है, तो एक अन्य एल्गोरिथ्म ऐसे राडिक्स सॉर्ट , स्का_सोर्ट या स्प्रेडसोर्ट का उपयोग इसके बजाय किया जा सकता है

विवरण छोड़े गए :

  • हम एल्गोरिथ्म द्वारा स्वीकार किए गए मूल्यों की सीमाओं की सीमा पार कर सकते हैं क्योंकि पैरामीटर से पूरी तरह से छुटकारा मिलता है संग्रह के माध्यम से पहले std::minmax_element पास। इससे एल्गोरिथ्म भी तेज़ हो जाएगा, जब एक उपयोगी-छोटी सीमा सीमा अन्य तरीकों से जानी जाती है। (यह सटीक नहीं होना चाहिए, निरंतर 0 से 100 तक गुजरने के लिए दस लाख से अधिक तत्वों के अतिरिक्त एक अतिरिक्त पास से बेहतर है, यह जानने के लिए कि सच सीमाएं 1 से 95 हैं। यहां तक ​​कि 0 से 1000 मूल्य इसके होंगे; अतिरिक्त तत्व एक बार शून्य के साथ लिखे जाते हैं और एक बार पढ़ते हैं)।

  • मक्खी पर counts बढ़ रही है एक अलग पहला पास से बचने का एक और तरीका है। हर बार यह बढ़ने के लिए counts मात्रा को दोहराते हुए हे (O) (1) समय प्रति सॉर्ट किए गए तत्व को समेकित करता है (प्रमाण के लिए हैश तालिका प्रविष्टि लागत विश्लेषण देखें जो कि घातीय वृद्धि हुई चाबी है)। एक नई max लिए अंत में बढ़ रहा है std::vector::resize के साथ नए शून्य तत्वों को जोड़ने के लिए आसान है। मक्खी पर min बदलने और सामने वाले नए शून्य तत्वों को डालने से वेक्टर बढ़ने के बाद std::copy_backward साथ किया जा सकता है। फिर std::fill नए तत्वों को शून्य करें।

  • counts वेतन वृद्धि लूप एक हिस्टोग्राम है यदि डेटा अत्यधिक पुनरावृत्त होने की संभावना है, और डिब्बे की संख्या छोटा है, तो उसी बिन में स्टोर / पुनः लोड की धारावाहिक डेटा निर्भरता बाधा को कम करने के लिए कई सरणियों पर अनारोल करना संभव है । इसका मतलब यह है कि शुरुआत में शून्य की संख्या और अंत में लूप को और अधिक, लेकिन लाखों 0 से 100 अंकों की हमारी उदाहरण के लिए अधिकतर सीपीयू पर इसका मूल्य होना चाहिए, खासकर अगर इनपुट पहले से ही हो (आंशिक रूप से) सॉर्ट किया हो और एक ही नंबर के लंबे समय से चलते हैं

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

  • चूंकि एल्गोरिथ्म केवल पूर्णांक मूल्यों के साथ काम करता है, चूंकि उपयोगकर्ताओं को स्पष्ट प्रकार की गलतियों को बनाने से रोकने के लिए स्थिर दावा किया जा सकता है। कुछ संदर्भों में, std::enable_if_t साथ प्रतिस्थापन विफलता को प्राथमिकता दी जा सकती है

  • जबकि आधुनिक सी ++ शांत है, भविष्य में C ++ भी कूलर हो सकता है: संरचित बांधों और श्रेणी टीएस के कुछ हिस्सों में एल्गोरिदम भी क्लीनर हो जाएगा।