संख्याओं के क्रमबद्ध सरणी में संख्या डालने का कुशल तरीका?


143

मेरे पास एक क्रमबद्ध जावास्क्रिप्ट सरणी है, और एक और आइटम को सरणी में सम्मिलित करना चाहते हैं, जिसके परिणामस्वरूप सरणी क्रमबद्ध रहती है। मैं निश्चित रूप से एक सरल क्विक-स्टाइल प्रविष्टि फ़ंक्शन लागू कर सकता हूं:

var array = [1,2,3,4,5,6,7,8,9];
var element = 3.5;
function insert(element, array) {
  array.splice(locationOf(element, array) + 1, 0, element);
  return array;
}

function locationOf(element, array, start, end) {
  start = start || 0;
  end = end || array.length;
  var pivot = parseInt(start + (end - start) / 2, 10);
  if (end-start <= 1 || array[pivot] === element) return pivot;
  if (array[pivot] < element) {
    return locationOf(element, array, pivot, end);
  } else {
    return locationOf(element, array, start, pivot);
  }
}

console.log(insert(element, array));

[चेतावनी] इस कोड में एक बग है, जब सरणी की शुरुआत में सम्मिलित करने की कोशिश कर रहा है, जैसे insert(2, [3, 7 ,9]) गलत [३, २,], ९] पैदा करता है।

हालाँकि, मैंने देखा कि Array.sort फ़ंक्शन के कार्यान्वयन संभवतः मेरे लिए और मूल रूप से ऐसा कर सकते हैं:

var array = [1,2,3,4,5,6,7,8,9];
var element = 3.5;
function insert(element, array) {
  array.push(element);
  array.sort(function(a, b) {
    return a - b;
  });
  return array;
}

console.log(insert(element, array));

क्या दूसरे पर पहला कार्यान्वयन चुनने का एक अच्छा कारण है?

संपादित करें : ध्यान दें कि सामान्य मामले के लिए, एक O (लॉग (n)) सम्मिलन (जैसा कि पहले उदाहरण में लागू किया गया है) एक सामान्य छँटाई एल्गोरिथ्म से तेज़ होगा; हालांकि यह विशेष रूप से जावास्क्रिप्ट के लिए जरूरी नहीं है। ध्यान दें कि:

  • कई सम्मिलन एल्गोरिदम के लिए सर्वश्रेष्ठ मामला ओ (एन) है, जो अभी भी ओ (लॉग (एन)) से काफी अलग है, लेकिन नीचे दिए गए अनुसार ओ (एन लॉग (एन)) के रूप में काफी बुरा नहीं है। यह इस्तेमाल किए जाने वाले विशेष छँटाई एल्गोरिथ्म के लिए नीचे आता है ( जावास्क्रिप्ट Array.sort कार्यान्वयन देखें ? )
  • जावास्क्रिप्ट में सॉर्ट विधि एक मूल कार्य है, इसलिए संभावित रूप से विशाल लाभ का एहसास - ओ (लॉग (एन)) एक विशाल गुणांक के साथ यथोचित आकार के डेटा सेट के लिए ओ (एन) की तुलना में बहुत खराब हो सकता है।

दूसरे कार्यान्वयन में ब्याह का उपयोग करना थोड़ा व्यर्थ है। पुश का उपयोग क्यों नहीं करें?
ब्रेटन

अच्छी बात है, मैंने इसे पहले से कॉपी किया है।
इलियट क्रू

4
युक्त कुछ भी splice()(जैसे आपका पहला उदाहरण) पहले से ही ओ (एन) है। यहां तक ​​कि अगर यह आंतरिक रूप से पूरे सरणी की एक नई प्रतिलिपि नहीं बनाता है, तो संभावित रूप से सभी n आइटम को 1 स्थिति में वापस करना होगा यदि तत्व को स्थिति में डाला जाना है 0. शायद यह तेज़ है क्योंकि यह एक मूल कार्य और स्थिर है कम है, लेकिन फिर भी यह ओ (एन) है।
j_random_hacker

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

3
का प्रयोग न करें parseIntउपयोग Math.floorके बजाय। Math.floorइससे बहुत तेज़ है parseInt: jsperf.com/test-parseint-and-math-floor
Hubert Schölnast

जवाबों:


58

केवल एक डेटा बिंदु के रूप में, किक के लिए मैंने विंडोज 7 पर क्रोम का उपयोग करके दो तरीकों का उपयोग करके 100,000 यादृच्छिक प्रकारों की एक सरणी में 1000 यादृच्छिक तत्वों को सम्मिलित करने का परीक्षण किया:

First Method:
~54 milliseconds
Second Method:
~57 seconds

तो, कम से कम इस सेटअप पर, देशी विधि इसके लिए नहीं है। यह छोटे डेटा सेट के लिए भी सच है, 1000 के एक सरणी में 100 तत्वों को सम्मिलित करना:

First Method:
1 milliseconds
Second Method:
34 milliseconds

1
arrays.sort काफी भयानक लगता है
njzk2

2
लगता है कि array.splice को 54 माइक्रोसेकंड के भीतर एक तत्व को सम्मिलित करने के लिए वास्तव में कुछ चतुर करना चाहिए।
gnasher729

@ gnasher729 - मुझे नहीं लगता है कि जावास्क्रिप्ट एरेज़ वास्तव में शारीरिक रूप से निरंतर सरणियों के समान हैं जैसे कि हम सी में हैं। मुझे लगता है कि जेएस इंजन उन्हें हैश मैप / डिक्शनरी के रूप में लागू कर सकते हैं जो त्वरित इंसर्ट कर सकते हैं।
इयान

1
जब आप एक तुलनित्र फ़ंक्शन का उपयोग करते हैं Array.prototype.sort, तो आप C ++ के लाभों को खो देते हैं क्योंकि JS फ़ंक्शन को इतना कहा जाता है।
एलेक्लेरसन

पहला तरीका अब कैसे तुलना करता है कि क्रोम टिमर्ट का उपयोग करता है ? से TimSort विकिपीडिया : "सबसे अच्छा मामले है, जो पहले से ही जब इनपुट, [TimSort] रैखिक समय में रन क्रमबद्ध हो जाता है तब होता है में"।
poshest

47

सरल ( डेमो ):

function sortedIndex(array, value) {
    var low = 0,
        high = array.length;

    while (low < high) {
        var mid = (low + high) >>> 1;
        if (array[mid] < value) low = mid + 1;
        else high = mid;
    }
    return low;
}

4
अच्छा स्पर्श। मैंने कभी भी दो नंबरों के मध्य मान का पता लगाने के लिए बिटवाइज़ ऑपरेटरों का उपयोग करने के बारे में नहीं सुना। आम तौर पर मैं सिर्फ 0.5 से गुणा करूंगा। क्या इस तरह से एक महत्वपूर्ण प्रदर्शन को बढ़ावा मिल रहा है?
जैक्सन

2
@ जैक्सन x >>> 11 पोजीशन द्वारा बाइनरी राइट शिफ्ट है, जो कि प्रभावी रूप से सिर्फ एक डिविजन है 2. उदाहरण के लिए 11: 1011-> 101परिणाम 5.
Qwerty

3
@Qwerty @Web_Designer इस ट्रैक पर होने के नाते, क्या आप >>> 1और ( यहाँ और वहाँ देखा गया ) के बीच का अंतर बता सकते हैं ? >> 1
यॉकर्ट

4
>>>एक अहस्ताक्षरित सही बदलाव है, जबकि >>साइन-एक्सटेंडिंग है - यह सभी नकारात्मक संख्याओं की इन-मेमोरी प्रतिनिधित्व को उबालता है, जहां नकारात्मक होने पर उच्च बिट सेट होता है। इसलिए यदि आप 0b1000सही जगह पर शिफ्ट >>हो गए हैं 0b1100, तो यदि आप उपयोग करते हैं >>>तो आपको मिलेगा 0b0100। हालांकि उत्तर में दिए गए मामले में यह वास्तव में मायने नहीं रखता है (जिस संख्या के साथ स्थानांतरित किया जा रहा है वह हस्ताक्षरित 32-बिट सकारात्मक पूर्णांक के अधिकतम मूल्य से अधिक नहीं है और न ही नकारात्मक), उन दो मामलों में सही एक का उपयोग करना महत्वपूर्ण है (आप आपको कौन सा मामला संभालने की जरूरत है)।
आशेरिन

2
@asherkin - यह सही नहीं है: "यदि आप 0b1000सही स्थान पर शिफ्ट >>करेंगे तो आपको मिलेगा 0b1100"। नहीं, तुम जाओ 0b0100। अलग-अलग राइट शिफ्ट ऑपरेटरों का परिणाम नकारात्मक संख्या और 2 ^ 31 (यानी, पहले बिट में 1 के साथ संख्या) से अधिक संख्या को छोड़कर सभी मूल्यों के लिए समान होगा।
gilly3

29

एक बहुत ही दिलचस्प चर्चा के साथ बहुत अच्छा और उल्लेखनीय सवाल! मैं Array.sort()एक एलीमेंट को कुछ हज़ारों ऑब्जेक्ट्स के साथ सरणी में धकेलने के बाद फ़ंक्शन का उपयोग कर रहा था ।

मुझे locationOfअपने उद्देश्य के लिए अपने काम का विस्तार जटिल वस्तुओं की वजह से करना पड़ा और इसलिए तुलनात्मक कार्य की आवश्यकता है जैसे Array.sort():

function locationOf(element, array, comparer, start, end) {
    if (array.length === 0)
        return -1;

    start = start || 0;
    end = end || array.length;
    var pivot = (start + end) >> 1;  // should be faster than dividing by 2

    var c = comparer(element, array[pivot]);
    if (end - start <= 1) return c == -1 ? pivot - 1 : pivot;

    switch (c) {
        case -1: return locationOf(element, array, comparer, start, pivot);
        case 0: return pivot;
        case 1: return locationOf(element, array, comparer, pivot, end);
    };
};

// sample for objects like {lastName: 'Miller', ...}
var patientCompare = function (a, b) {
    if (a.lastName < b.lastName) return -1;
    if (a.lastName > b.lastName) return 1;
    return 0;
};

7
यह रिकॉर्ड के लिए ध्यान देने योग्य है, कि यह संस्करण सरणी की शुरुआत में सम्मिलित करने की कोशिश करते समय सही ढंग से काम करता है। (यह ध्यान देने योग्य है क्योंकि मूल प्रश्न में संस्करण में बग है और उस मामले के लिए सही ढंग से काम नहीं करता है।)
गैरीब्रो

3
मुझे यकीन नहीं है कि मेरा कार्यान्वयन अलग था, लेकिन मुझे return c == -1 ? pivot : pivot + 1;सही इंडेक्स को वापस करने के लिए टर्नरी को बदलने की आवश्यकता थी । अन्यथा लंबाई 1 के साथ एक सरणी के लिए फ़ंक्शन -1 या 0.
Niel

3
@ नाम: पैरामीटर प्रारंभ और समाप्ति केवल पुनरावर्ती कॉल पर उपयोग किया जाता है और इनटेल कॉल पर उपयोग नहीं किया जाएगा। क्योंकि ये उस सरणी के लिए अनुक्रमणिका मान हैं, जो उन्हें पूर्णांक का होना चाहिए और पुनरावर्ती कॉल पर यह स्पष्ट रूप से दिया गया है।
kwrl

1
@ TheRedPea: नहीं, मेरा मतलब है >> 1कि तेज (या धीमा नहीं) होना चाहिए/ 2
kwrl

1
मैं comparerफ़ंक्शन के परिणाम के साथ एक संभावित मुद्दा देख सकता हूं । इस एल्गोरिथ्म में इसकी तुलना की जाती है +-1लेकिन यह मनमाना मूल्य हो सकता है<0 />0तुलना समारोह देखें । समस्याग्रस्त भाग केवल switchकथन ही नहीं, बल्कि रेखा भी है: if (end - start <= 1) return c == -1 ? pivot - 1 : pivot;जहाँ cकी तुलना -1भी की जाती है।
eXavier

19

आपके कोड में एक बग है। इसे पढ़ना चाहिए:

function locationOf(element, array, start, end) {
  start = start || 0;
  end = end || array.length;
  var pivot = parseInt(start + (end - start) / 2, 10);
  if (array[pivot] === element) return pivot;
  if (end - start <= 1)
    return array[pivot] > element ? pivot - 1 : pivot;
  if (array[pivot] < element) {
    return locationOf(element, array, pivot, end);
  } else {
    return locationOf(element, array, start, pivot);
  }
}

इस फिक्स के बिना कोड सरणी की शुरुआत में एक तत्व सम्मिलित करने में सक्षम नहीं होगा।


आप 0 के साथ एक इंट क्यों कर रहे हैं? अर्थात क्या शुरू होता है || 0 करते हो?
पिनोच्चियो

3
@Pinocchio: शुरू || 0 एक छोटा समतुल्य है: if (! Start) start = 0; - हालांकि, "लंबे समय तक" संस्करण अधिक प्रभावशाली है, क्योंकि यह खुद को एक चर प्रदान नहीं करता है।
सुपरनोवा

11

मुझे पता है कि यह एक पुराना सवाल है जिसका पहले से ही एक उत्तर है, और कई अन्य अच्छे उत्तर हैं। मुझे कुछ उत्तर दिखाई देते हैं जो प्रस्तावित करते हैं कि आप O (लॉग एन) में सही प्रविष्टि इंडेक्स को देखकर इस समस्या को हल कर सकते हैं - आप कर सकते हैं, लेकिन आप उस समय में सम्मिलित नहीं कर सकते, क्योंकि सरणी को आंशिक रूप से कॉपी करने के लिए आवश्यक है अंतरिक्ष।

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

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

function addAndSort(arr, val) {
    arr.push(val);
    for (i = arr.length - 1; i > 0 && arr[i] < arr[i-1]; i--) {
        var tmp = arr[i];
        arr[i] = arr[i-1];
        arr[i-1] = tmp;
    }
    return arr;
}

यह O (n) में संचालित होना चाहिए, जो मुझे लगता है कि सबसे अच्छा आप कर सकते हैं। अगर js कई असाइनमेंट का समर्थन करता है तो वह अच्छा होगा। यहाँ एक उदाहरण के साथ खेलने के लिए है:

अपडेट करें:

यह तेज हो सकता है:

function addAndSort2(arr, val) {
    arr.push(val);
    i = arr.length - 1;
    item = arr[i];
    while (i > 0 && item < arr[i-1]) {
        arr[i] = arr[i-1];
        i -= 1;
    }
    arr[i] = item;
    return arr;
}

अपडेटेड जेएस बिन लिंक


जावास्क्रिप्ट में आप जिस तरह का प्रस्ताव रखते हैं, वह द्विआधारी खोज और ब्याह विधि की तुलना में धीमा होगा, क्योंकि ब्याह में तेजी से कार्यान्वयन होता है।
1919

जब तक जावास्क्रिप्ट किसी तरह समय जटिलता के कानूनों को तोड़ नहीं सकता है, मैं उलझन में हूं। क्या आपके पास एक बिकाऊ उदाहरण है कि बाइनरी सर्च और स्प्लिस विधि कैसे तेज है?
डोमोइरैगेटो

मैं अपनी दूसरी टिप्पणी वापस लेता हूं ;-) दरअसल, एक सरणी आकार होगा जिसके आगे एक बी-ट्री समाधान ब्याह समाधान को बेहतर बनाएगा।
19 '

9

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

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

एक सामान्य सॉर्ट एल्गोरिथ्म आमतौर पर ओ (एन ⋅ लॉग (एन)) पर होता है और कार्यान्वयन के आधार पर यह वास्तव में सबसे खराब स्थिति हो सकती है यदि सरणी पहले से ही सॉर्ट की जाती है, जिससे ओ (एन 2 ) की जटिलताएं होती हैं । सीधे प्रविष्टि की स्थिति के लिए खोज करने के बजाय ओ (लॉग (एन)) की एक जटिलता है , इसलिए यह हमेशा बहुत तेज होगा।


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

5

आइटम की एक छोटी संख्या के लिए, अंतर बहुत तुच्छ है। हालाँकि, यदि आप बहुत सारे आइटम सम्मिलित कर रहे हैं, या एक बहुत बड़ी सरणी के साथ काम कर रहे हैं, तो प्रत्येक प्रविष्टि के बाद .sort () कॉलिंग ओवरहेड की एक जबरदस्त मात्रा का कारण बनेगी।

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

function insertSorted(arr, item, comparator) {
    if (comparator == null) {
        // emulate the default Array.sort() comparator
        comparator = function(a, b) {
            if (typeof a !== 'string') a = String(a);
            if (typeof b !== 'string') b = String(b);
            return (a > b ? 1 : (a < b ? -1 : 0));
        };
    }

    // get the index we need to insert the item at
    var min = 0;
    var max = arr.length;
    var index = Math.floor((min + max) / 2);
    while (max > min) {
        if (comparator(item, arr[index]) < 0) {
            max = index;
        } else {
            min = index + 1;
        }
        index = Math.floor((min + max) / 2);
    }

    // insert the item
    arr.splice(index, 0, item);
};

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


कॉल arr.splice()निश्चित रूप से हे (एन) समय की जटिलता है।
डोमोइरैगेटो

4

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

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

जब मैंने पढ़ा कि आप सॉर्ट करना चाहते हैं, तो मेरा पहला विचार प्रविष्टि सॉर्ट का उपयोग करना है! । यह आसान है क्योंकि यह क्रमबद्ध, या लगभग-क्रमबद्ध सूचियों पर रैखिक समय में चलता है । जैसा कि आपके सरणियों में केवल 1 तत्व होता है, जो लगभग क्रमबद्ध (को छोड़कर, अच्छी तरह से, आकार 2 या 3 या जो भी हो, लेकिन उस बिंदु पर, c'mon) के रूप में गिना जाता है। अब, इस तरह से लागू करना बहुत बुरा नहीं है, लेकिन यह एक ऐसी परेशानी है जिससे आप निपटना नहीं चाहते हैं, और फिर, मैं जावास्क्रिप्ट के बारे में एक बात नहीं जानता और अगर यह आसान या कठिन या व्हाट्सएप होगा। यह आपके लुकअप फंक्शन की आवश्यकता को हटा देता है, और आप बस पुश करते हैं (जैसा कि ब्रेटन ने सुझाव दिया है)।

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

वैसे भी, सारांश: प्रविष्टि प्रकार के साथ "पुश" का उपयोग रैखिक समय में काम करेगा (बाकी सरणी को सॉर्ट किया गया है), और किसी भी गड़बड़ बाइनरी खोज एल्गोरिथ्म आवश्यकताओं से बचें। मुझे नहीं पता कि यह सबसे अच्छा तरीका है (सरणियों का अंतर्निहित कार्यान्वयन, शायद एक पागल बनाया गया फ़ंक्शन इसे बेहतर करता है, जो जानता है), लेकिन यह मुझे उचित लगता है। :) - आगोर।


1
+1 क्योंकि कुछ भी splice()पहले से ही O (n) है। यहां तक ​​कि अगर यह आंतरिक रूप से पूरे सरणी की एक नई प्रतिलिपि नहीं बनाता है, तो संभावित रूप से सभी n आइटम को 1 स्थिति में वापस करना होगा यदि तत्व को स्थिति में डाला जाना है 0.
j_random_hacker

मेरा मानना ​​है कि प्रविष्टि प्रकार भी ओ (एन) सबसे अच्छा मामला है, और ओ (एन ^ 2) सबसे खराब मामला है (हालांकि ओपी का उपयोग मामला शायद सबसे अच्छा मामला है)।
डोमोइरैगेटो

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

2

इसे पूरा करने के लिए यहां चार अलग-अलग एल्गोरिदम की तुलना की गई है: https://jsperf.com/sorted-array-insert-comison/1

एल्गोरिदम

  • भोले: बस धक्का और तरह () बाद में
  • रैखिक: सरणी पर पुनरावृति और जहां उपयुक्त डालें
  • बाइनरी सर्च: https://stackoverflow.com/a/20352387/154329 से लिया गया
  • "क्विक सॉर्ट लाइक": सिंथेटिकजेरो से परिष्कृत समाधान ( https://stackoverflow.com/a/18341744/154326 )

भोला हमेशा भयानक होता है। यह छोटे सरणी आकारों के लिए लगता है, अन्य तीनों में बहुत अधिक अंतर नहीं है, लेकिन बड़े सरणियों के लिए, अंतिम 2 सरल रैखिक दृष्टिकोण को बेहतर बनाते हैं।


तेजी से सम्मिलन और खोज को लागू करने के लिए डिज़ाइन किए गए डेटा संरचनाओं का परीक्षण क्यों नहीं किया जाता है? पूर्व। सूचियों और BST को छोड़ें। stackoverflow.com/a/59870937/3163618
qwr

मूल निवासी अब कैसे तुलना करता है कि क्रोम टिमर्ट का उपयोग करता है ? से TimSort विकिपीडिया : "सबसे अच्छा मामले है, जो पहले से ही जब इनपुट क्रमबद्ध हो जाता है तब होता है में, यह रेखीय समय में चलाता है"।
poshest

2

यहाँ एक संस्करण है जो लॉश का उपयोग करता है।

const _ = require('lodash');
sortedArr.splice(_.sortedIndex(sortedArr,valueToInsert) ,0,valueToInsert);

नोट: SortedIndex एक बाइनरी खोज करता है।


1

सबसे अच्छी डेटा संरचना, जिसके बारे में मैं सोच सकता हूं कि एक अनुक्रमित स्किप सूची है, जो एक पदानुक्रम संरचना के साथ लिंक किए गए सूचियों के सम्मिलन गुणों को बनाए रखती है, जो लॉग समय संचालन को सक्षम करता है। ओ, (लॉग एन) समय में औसतन खोज, प्रविष्टि, और रैंडम एक्सेस लुकअप किया जा सकता है।

एक ऑर्डर स्टेटिस्टिक ट्री एक रैंक फ़ंक्शन के साथ लॉग टाइम इंडेक्सिंग को सक्षम करता है।

यदि आपको यादृच्छिक पहुँच की आवश्यकता नहीं है, लेकिन आपको O (लॉग एन) प्रविष्टि की आवश्यकता है और कुंजियों की खोज करने पर, आप सरणी संरचना को खोद सकते हैं और किसी भी प्रकार के बाइनरी सर्च ट्री का उपयोग कर सकते हैं ।

उन सभी उत्तरों में से कोई भी उपयोग नहीं किया गया है जो array.splice()औसत ओ (एन) समय पर है। Google Chrome में array.splice () की समय जटिलता क्या है?


यह कैसे जवाब देता हैIs there a good reason to choose [splice into location found] over [push & sort]?
ग्रेबियर

1
@greybeard यह शीर्षक का जवाब देता है। निंदनीय रूप से न तो विकल्प कुशल है।
qwr

यदि वे किसी सरणी के कई तत्वों को कॉपी करते हैं, तो न तो विकल्प कुशल हो सकता है।
qwr

1

यहाँ मेरा कार्य है, आइटम खोजने के लिए द्विआधारी खोज का उपयोग करता है और फिर उचित रूप से सम्मिलित करता है:

function binaryInsert(val, arr){
    let mid, 
    len=arr.length,
    start=0,
    end=len-1;
    while(start <= end){
        mid = Math.floor((end + start)/2);
        if(val <= arr[mid]){
            if(val >= arr[mid-1]){
                arr.splice(mid,0,val);
                break;
            }
            end = mid-1;
        }else{
            if(val <= arr[mid+1]){
                arr.splice(mid+1,0,val);
                break;
            }
            start = mid+1;
        }
    }
    return arr;
}

console.log(binaryInsert(16, [
    5,   6,  14,  19, 23, 44,
   35,  51,  86,  68, 63, 71,
   87, 117
 ]));


0

हर आइटम के बाद फिर से सॉर्ट न करें, इसका ओवरकिल ..

यदि डालने के लिए केवल एक आइटम है, तो आप बाइनरी खोज का उपयोग करके डालने के लिए स्थान पा सकते हैं। फिर मेम्ची या थोक के समान का उपयोग करें शेष आइटम को सम्मिलित करने के लिए जगह बनाने के लिए कॉपी करें। बाइनरी खोज ओ (लॉग एन) है, और प्रतिलिपि ओ (एन) है, जो ओ (एन + लॉग एन) कुल दे रही है। उपरोक्त विधियों का उपयोग करते हुए, आप हर प्रविष्टि के बाद एक पुन: सॉर्ट कर रहे हैं, जो O (n log n) है।

फर्क पड़ता है क्या? कहते हैं कि आप बेतरतीब ढंग से कश्मीर तत्वों को सम्मिलित कर रहे हैं, जहाँ k = 1000 है। सॉर्ट की गई सूची 5000 आइटम है।

  • Binary search + Move = k*(n + log n) = 1000*(5000 + 12) = 5,000,012 = ~5 million ops
  • Re-sort on each = k*(n log n) = ~60 million ops

यदि डालने के लिए आइटम जब भी आते हैं, तो आपको खोज + चाल करनी चाहिए। हालाँकि, यदि आपको क्रमबद्ध सरणी में सम्मिलित करने के लिए k आइटम की सूची दी गई है - समय से पहले - तो आप और भी बेहतर कर सकते हैं। K आइटम्स को सॉर्ट करें, पहले से सॉर्ट किए गए n सरणी से अलग। फिर एक स्कैन सॉर्ट करें, जिसमें आप दोनों सॉर्ट किए गए सरणियों को एक साथ स्थानांतरित करते हैं, एक को दूसरे में विलय करते हैं। - वन-स्टेप मर्ज सॉर्ट = k log k + n = 9965 + 5000 = ~ 15,000 ऑप्स

अपडेट: अपने प्रश्न के बारे में।
First method = binary search+move = O(n + log n)Second method = re-sort = O(n log n)ठीक उसी समय की व्याख्या करता है जो आप प्राप्त कर रहे हैं।


हां, लेकिन नहीं, यह आपके सॉर्ट एल्गोरिथ्म पर निर्भर करता है। अंतिम क्रम में बुलबुला सॉर्ट का उपयोग करते हुए, यदि आपका अंतिम तत्व सॉर्ट नहीं किया गया है तो हमेशा o (n) में होता है
njzk2

-1
function insertOrdered(array, elem) {
    let _array = array;
    let i = 0;
    while ( i < array.length && array[i] < elem ) {i ++};
    _array.splice(i, 0, elem);
    return _array;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.