Cách hiệu quả để chèn một số vào một mảng số được sắp xếp?


142

Tôi có một mảng JavaScript được sắp xếp và muốn chèn thêm một mục vào mảng để mảng kết quả vẫn được sắp xếp. Tôi chắc chắn có thể thực hiện một chức năng chèn kiểu quicksort đơn giản:

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));

[CẢNH BÁO] mã này có lỗi khi cố gắng chèn vào đầu mảng, ví dụ insert(2, [3, 7 ,9]) tạo ra không chính xác [3, 2, 7, 9].

Tuy nhiên, tôi nhận thấy rằng việc triển khai hàm Array.sort có thể có khả năng thực hiện điều này cho tôi và thực chất:

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));

Có một lý do tốt để chọn thực hiện đầu tiên so với thứ hai?

Chỉnh sửa : Lưu ý rằng đối với trường hợp chung, việc chèn O (log (n)) (như được thực hiện trong ví dụ đầu tiên) sẽ nhanh hơn thuật toán sắp xếp chung; tuy nhiên điều này không nhất thiết là trường hợp của JavaScript nói riêng. Lưu ý rằng:

  • Trường hợp tốt nhất cho một số thuật toán chèn là O (n), vẫn khác biệt đáng kể so với O (log (n)), nhưng không hoàn toàn xấu như O (n log (n)) như được đề cập dưới đây. Nó sẽ đi xuống thuật toán sắp xếp cụ thể được sử dụng (xem triển khai Javascript Array.sort? )
  • Phương thức sắp xếp trong JavaScript là một hàm riêng, do đó có khả năng nhận ra những lợi ích to lớn - O (log (n)) với hệ số khổng lồ vẫn có thể tệ hơn nhiều so với O (n) đối với các tập dữ liệu có kích thước hợp lý.

sử dụng mối nối trong việc thực hiện thứ hai là một chút lãng phí. Tại sao không sử dụng đẩy?
Breton

Điểm tốt, tôi chỉ sao chép nó từ đầu tiên.
Elliot Kroo

4
Bất cứ điều gì có chứa splice()(ví dụ ví dụ đầu tiên của bạn) đã là O (n). Ngay cả khi nó không tạo ra một bản sao mới của toàn bộ mảng, nó có khả năng phải chuyển tất cả n mục trở lại vị trí 1 nếu phần tử được chèn vào vị trí 0. Có thể đó là nhanh vì đó là hàm gốc và hằng số là thấp, nhưng dù sao đó là O (n).
j_random_hacker

6
Ngoài ra, để tham khảo trong tương lai cho những người sử dụng mã này, mã có lỗi khi cố gắng chèn vào đầu mảng. Nhìn xa hơn cho mã sửa chữa.
Pinocchio

3
Đừng sử parseIntdụng Math.floorthay thế.Math.floornhanh hơn nhiều so với parseInt: jsperf.com/test-parseint-and-math-floor
Hubert Schölnast

Câu trả lời:


58

Cũng giống như một điểm dữ liệu duy nhất, đối với các cú đá, tôi đã thử nghiệm điều này bằng cách chèn 1000 phần tử ngẫu nhiên vào một mảng gồm 100.000 số được sắp xếp trước bằng hai phương pháp sử dụng Chrome trên Windows 7:

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

Vì vậy, ít nhất là trên thiết lập này, phương thức gốc không bù đắp cho nó. Điều này đúng ngay cả với các tập dữ liệu nhỏ, chèn 100 phần tử vào một mảng 1000:

First Method:
1 milliseconds
Second Method:
34 milliseconds

1
Arrayays.sort nghe có vẻ khá khủng khiếp
njzk2

2
Có vẻ như mảng.splice phải làm điều gì đó thực sự thông minh, để chèn một phần tử duy nhất trong vòng 54 micro giây.
gnasher729

@ gnasher729 - Tôi không nghĩ các mảng Javascript thực sự giống như các mảng liên tục về mặt vật lý như chúng ta có trong C. Tôi nghĩ rằng các công cụ JS có thể thực hiện chúng như một bản đồ băm / từ điển cho phép chèn nhanh.
Ian

1
Khi bạn sử dụng hàm so sánh với Array.prototype.sort, bạn sẽ mất các lợi ích của C ++ vì hàm JS được gọi rất nhiều.
aleclarson

Phương thức thứ nhất so sánh như thế nào khi Chrome sử dụng TimSort ? Từ TimSort Wikipedia : "Trong trường hợp tốt nhất, xảy ra khi đầu vào đã được sắp xếp, [TimSort] chạy trong thời gian tuyến tính".
mạnh nhất

47

Đơn giản ( Bản trình diễn ):

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
Liên lạc tốt đẹp. Tôi chưa bao giờ nghe nói về việc sử dụng các toán tử bitwise để tìm giá trị trung bình của hai số. Thông thường tôi sẽ chỉ nhân 0,5. Có một tăng hiệu suất đáng kể làm theo cách này?
Jackson

2
@Jackson x >>> 1là sự thay đổi quyền nhị phân theo 1 vị trí, thực sự chỉ là một phép chia cho 2. ví dụ: 11: 1011-> 101kết quả là 5.
Qwerty

3
@Qwerty @Web_Designer Đã có trên đường đua này, bạn có thể giải thích sự khác biệt giữa >>> 1và ( xem ở đây ở đó ) >> 1không?
yckart

4
>>>là một sự thay đổi bên phải không dấu, trong khi đó >>là mở rộng dấu hiệu - tất cả tập trung vào biểu diễn trong bộ nhớ của các số âm, trong đó bit cao được đặt nếu âm. Vì vậy, nếu bạn thay đổi 0b1000đúng 1 vị trí với >>bạn sẽ nhận được 0b1100, nếu thay vào đó >>>bạn sẽ sử dụng 0b0100. Mặc dù trong trường hợp được đưa ra trong câu trả lời, điều đó không thực sự quan trọng (số bị dịch chuyển không lớn hơn giá trị tối đa của số nguyên dương 32 bit đã ký hoặc âm), điều quan trọng là sử dụng đúng trong hai trường hợp đó (bạn cần chọn trường hợp nào bạn cần xử lý).
Asherkin

2
@asherkin - Điều này không đúng: "nếu bạn chuyển 0b1000đúng 1 địa điểm với >>bạn sẽ nhận được 0b1100". Không, bạn nhận được 0b0100. Kết quả của các toán tử dịch chuyển phải khác nhau sẽ giống nhau cho tất cả các giá trị ngoại trừ số âm và số lớn hơn 2 ^ 31 (nghĩa là các số có 1 trong bit đầu tiên).
gilly3

29

Câu hỏi rất hay và đáng chú ý với một cuộc thảo luận rất thú vị! Tôi cũng đã sử dụng Array.sort()hàm sau khi đẩy một phần tử trong một mảng với hàng ngàn đối tượng.

Tôi đã phải mở rộng locationOfchức năng của bạn cho mục đích của tôi vì có các đối tượng phức tạp và do đó cần một chức năng so sánh như trong 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
Có vẻ đáng chú ý, đối với bản ghi, phiên bản này KHÔNG hoạt động chính xác khi cố gắng chèn vào đầu mảng. (Điều đáng nói là vì phiên bản trong câu hỏi ban đầu có lỗi và không hoạt động chính xác cho trường hợp đó.)
garyrob

3
Tôi không chắc việc triển khai của mình có khác không, nhưng tôi cần thay đổi bộ ba thành return c == -1 ? pivot : pivot + 1;để trả về chỉ mục chính xác. Mặt khác, đối với một mảng có độ dài 1, hàm sẽ trả về -1 hoặc 0.
Niel

3
@James: Các tham số bắt đầu và kết thúc chỉ được sử dụng cho cuộc gọi đệ quy và sẽ không được sử dụng cho cuộc gọi bẩm sinh. Bởi vì đây là các giá trị chỉ mục cho mảng, chúng phải có kiểu nguyên và trong lệnh gọi đệ quy, điều này được ngầm định đưa ra.
kwrl

1
@TheRedPea: không, ý tôi là >> 1nên nhanh hơn (hoặc không chậm hơn)/ 2
kwrl

1
Tôi có thể thấy một vấn đề tiềm năng với kết quả của comparerchức năng. Trong thuật toán này, nó được so sánh với +-1nhưng nó có thể là giá trị tùy ý <0/ >0. Xem chức năng so sánh . Phần có vấn đề không chỉ là switchtuyên bố mà còn là dòng: if (end - start <= 1) return c == -1 ? pivot - 1 : pivot;nơi cđược so sánh -1là tốt.
eXavier

19

Có một lỗi trong mã của bạn. Nó nên đọc:

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);
  }
}

Nếu không sửa lỗi này, mã sẽ không bao giờ có thể chèn một phần tử vào đầu mảng.


Tại sao bạn hoặc-int một số 0? tức là cái gì bắt đầu | | 0 làm gì?
Pinocchio

3
@Pinocchio: bắt đầu | | 0 là một tương đương ngắn của: if (! Start) start = 0; - Tuy nhiên, phiên bản "dài hơn" có hiệu quả hơn, vì nó không gán một biến cho chính nó.
SuperNova

11

Tôi biết đây là một câu hỏi cũ đã có câu trả lời, và có một số câu trả lời đàng hoàng khác. Tôi thấy một số câu trả lời đề xuất rằng bạn có thể giải quyết vấn đề này bằng cách tra cứu chỉ số chèn chính xác trong O (log n) - bạn có thể, nhưng bạn không thể chèn vào thời điểm đó, vì mảng cần được sao chép một phần để thực hiện không gian.

Dòng dưới cùng: Nếu bạn thực sự cần O (log n) chèn và xóa vào một mảng được sắp xếp, bạn cần một cấu trúc dữ liệu khác - không phải là một mảng. Bạn nên sử dụng B-Tree . Hiệu suất đạt được mà bạn sẽ nhận được từ việc sử dụng B-Tree cho một tập dữ liệu lớn, sẽ làm giảm bớt bất kỳ cải tiến nào được cung cấp ở đây.

Nếu bạn phải sử dụng một mảng. Tôi cung cấp mã sau, dựa trên sắp xếp chèn, hoạt động, nếu và chỉ khi mảng đã được sắp xếp. Điều này hữu ích cho trường hợp khi bạn cần nghỉ dưỡng sau mỗi lần chèn:

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;
}

Nó nên hoạt động trong O (n), mà tôi nghĩ là tốt nhất bạn có thể làm. Sẽ đẹp hơn nếu js hỗ trợ nhiều bài tập. đây là một ví dụ để chơi với:

Cập nhật:

điều này có thể nhanh hơn:

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;
}

Cập nhật liên kết Bin Bin


Trong JavaScript, loại sắp xếp chèn mà bạn đề xuất sẽ chậm hơn so với phương pháp tìm kiếm nhị phân & mối nối, bởi vì mối nối có triển khai nhanh.
trincot

trừ khi javascript bằng cách nào đó có thể phá vỡ quy luật phức tạp về thời gian, tôi nghi ngờ. Bạn có một ví dụ có thể chạy được về cách phương pháp tìm kiếm nhị phân và mối nối nhanh hơn không?
domoarigato

Tôi lấy lại nhận xét thứ hai của mình ;-) Thật vậy, sẽ có một kích thước mảng vượt ra ngoài giải pháp cây B sẽ vượt trội hơn giải pháp mối nối.
trincot

9

Hàm chèn của bạn giả định rằng mảng đã cho được sắp xếp, nó tìm kiếm trực tiếp vị trí có thể chèn phần tử mới, thường chỉ bằng cách nhìn vào một vài phần tử trong mảng.

Hàm sắp xếp chung của một mảng không thể sử dụng các phím tắt này. Rõ ràng là ít nhất nó phải kiểm tra tất cả các yếu tố trong mảng để xem chúng đã được sắp xếp chính xác chưa. Thực tế này làm cho việc sắp xếp chung chậm hơn chức năng chèn.

Một thuật toán sắp xếp chung thường là trung bình O (n ⋅ log (n)) và tùy thuộc vào việc thực hiện, nó thực sự có thể là trường hợp xấu nhất nếu mảng đã được sắp xếp, dẫn đến độ phức tạp của O (n 2 ) . Thay vào đó, việc tìm kiếm trực tiếp vị trí chèn thay vào đó chỉ là độ phức tạp của O (log (n)) , vì vậy nó sẽ luôn nhanh hơn nhiều.


Cần lưu ý rằng việc chèn một phần tử vào một mảng có độ phức tạp là O (n), vì vậy kết quả cuối cùng sẽ giống nhau.
NemPlayer

5

Đối với một số lượng nhỏ các mặt hàng, sự khác biệt là khá nhỏ. Tuy nhiên, nếu bạn đang chèn nhiều mục hoặc làm việc với một mảng rất lớn, việc gọi .sort () sau mỗi lần chèn sẽ gây ra một lượng chi phí rất lớn.

Cuối cùng tôi đã viết một chức năng tìm kiếm / chèn nhị phân khá trơn tru cho mục đích chính xác này, vì vậy tôi nghĩ rằng tôi muốn chia sẻ nó. Vì nó sử dụng một whilevòng lặp thay vì đệ quy, không có sự nghe lén cho các cuộc gọi chức năng bổ sung, vì vậy tôi nghĩ hiệu suất sẽ còn tốt hơn cả các phương thức được đăng ban đầu. Và nó mô phỏng bộ Array.sort()so sánh mặc định theo mặc định, nhưng chấp nhận chức năng so sánh tùy chỉnh nếu muốn.

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);
};

Nếu bạn mở để sử dụng các thư viện khác, lodash cung cấp các hàm sort IndexsortLastIndex , có thể được sử dụng thay cho whilevòng lặp. Hai nhược điểm tiềm năng là 1) hiệu suất không tốt bằng phương pháp của tôi (tôi nghĩ tôi không chắc nó tệ đến mức nào) và 2) nó không chấp nhận chức năng so sánh tùy chỉnh, chỉ là phương pháp để lấy giá trị để so sánh (sử dụng bộ so sánh mặc định, tôi giả sử).


cuộc gọi đến arr.splice()chắc chắn là độ phức tạp thời gian O (n).
domoarigato

4

Dưới đây là một vài suy nghĩ: Thứ nhất, nếu bạn thực sự lo lắng về thời gian chạy mã của mình, hãy chắc chắn biết điều gì sẽ xảy ra khi bạn gọi các hàm tích hợp! Tôi không biết từ trên xuống trong javascript, nhưng một hàm google nhanh chóng đã trả về hàm này , điều này dường như cho thấy rằng bạn đang tạo một mảng hoàn toàn mới cho mỗi cuộc gọi! Tôi không biết nếu nó thực sự quan trọng, nhưng nó chắc chắn có liên quan đến hiệu quả. Tôi thấy rằng Breton, trong các ý kiến, đã chỉ ra điều này, nhưng nó chắc chắn giữ cho bất kỳ chức năng thao tác mảng nào bạn chọn.

Dù sao, thực sự giải quyết vấn đề.

Khi tôi đọc rằng bạn muốn sắp xếp, suy nghĩ đầu tiên của tôi là sử dụng sắp xếp chèn! . Nó rất tiện lợi vì nó chạy trong thời gian tuyến tính trên các danh sách được sắp xếp hoặc sắp xếp gần . Vì các mảng của bạn sẽ chỉ có 1 phần tử không theo thứ tự, được tính là sắp xếp gần như (ngoại trừ, các mảng có kích thước 2 hoặc 3 hoặc bất cứ thứ gì, nhưng tại thời điểm đó, c'mon). Bây giờ, việc triển khai loại này không quá tệ, nhưng đó là một rắc rối mà bạn có thể không muốn giải quyết, và một lần nữa, tôi không biết một điều gì về javascript và nó sẽ dễ hay khó hay không. Điều này loại bỏ sự cần thiết cho chức năng tra cứu của bạn và bạn chỉ cần đẩy (như Breton đề xuất).

Thứ hai, chức năng tra cứu "quicksort-esque" của bạn dường như là một thuật toán tìm kiếm nhị phân ! Nó là một thuật toán rất hay, trực quan và nhanh chóng, nhưng với một nhược điểm: rất khó để thực hiện chính xác. Tôi sẽ không dám nói nếu bạn đúng hay không (tất nhiên tôi hy vọng là như vậy! :)), nhưng hãy cảnh giác nếu bạn muốn sử dụng nó.

Dù sao, tóm tắt: sử dụng "đẩy" với sắp xếp chèn sẽ hoạt động theo thời gian tuyến tính (giả sử phần còn lại của mảng được sắp xếp) và tránh mọi yêu cầu thuật toán tìm kiếm nhị phân lộn xộn. Tôi không biết liệu đây có phải là cách tốt nhất (thực hiện cơ bản các mảng, có thể là một hàm dựng sẵn điên rồ sẽ làm tốt hơn, ai biết được), nhưng nó có vẻ hợp lý với tôi. :) - Agor.


1
+1 vì mọi thứ chứa splice()đều là O (n). Ngay cả khi nó không tạo ra một bản sao mới của toàn bộ mảng, nó có khả năng phải chuyển tất cả n mục trở lại vị trí 1 nếu phần tử được chèn vào vị trí 0.
j_random_hacker

Tôi tin rằng loại chèn cũng là trường hợp tốt nhất O (n) và trường hợp xấu nhất O (n ^ 2) (mặc dù trường hợp sử dụng của OP có lẽ là trường hợp tốt nhất).
domoarigato

Trừ đi một để nói chuyện với OP. Đoạn đầu tiên cảm thấy như một lời khuyên răn vô tình vì không biết làm thế nào mối nối hoạt động dưới mui xe
Matt Zera

2

Dưới đây là so sánh của bốn thuật toán khác nhau để thực hiện điều này: https://jsperf.com/sort-array-insert-comparison/1

Thuật toán

Ngây thơ luôn kinh khủng. Có vẻ như đối với các kích thước mảng nhỏ, ba kích thước khác không khác nhau quá nhiều, nhưng đối với các mảng lớn hơn, 2 mảng cuối vượt trội hơn so với cách tiếp cận tuyến tính đơn giản.


Tại sao không kiểm tra cấu trúc dữ liệu được thiết kế để thực hiện chèn và tìm kiếm nhanh? Ví dụ. bỏ qua danh sách và BST. stackoverflow.com/a/59870937/3163618
qwr

Làm thế nào để so sánh bản địa bây giờ rằng Chrome sử dụng TimSort ? Từ TimSort Wikipedia : "Trong trường hợp tốt nhất, xảy ra khi đầu vào đã được sắp xếp, nó chạy trong thời gian tuyến tính".
mạnh nhất

2

Đây là một phiên bản sử dụng lodash.

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

lưu ý: sort Index thực hiện tìm kiếm nhị phân.


1

Cấu trúc dữ liệu tốt nhất tôi có thể nghĩ đến là một danh sách bỏ qua được lập chỉ mục duy trì các thuộc tính chèn của danh sách được liên kết với cấu trúc phân cấp cho phép các hoạt động thời gian đăng nhập. Trung bình, tìm kiếm, chèn và tra cứu truy cập ngẫu nhiên có thể được thực hiện trong thời gian O (log n).

Một cây trật tự thống kê cho phép log thời gian lập chỉ mục với một chức năng xếp hạng.

Nếu bạn không cần truy cập ngẫu nhiên nhưng bạn cần chèn O (log n) và tìm kiếm các khóa, bạn có thể bỏ cấu trúc mảng và sử dụng bất kỳ loại cây tìm kiếm nhị phân nào .

Không có câu trả lời nào sử dụng array.splice()hiệu quả cả vì đó là thời gian trung bình O (n). Độ phức tạp thời gian của mảng.splice () trong Google Chrome là gì?


Làm thế nào để trả lời nàyIs there a good reason to choose [splice into location found] over [push & sort]?
greybeard

1
@greybeard Nó trả lời tiêu đề. hoài nghi không có sự lựa chọn là hiệu quả.
qwr

Không có tùy chọn nào có thể hiệu quả nếu chúng liên quan đến việc sao chép nhiều phần tử của một mảng.
qwr

1

Đây là chức năng của tôi, sử dụng tìm kiếm nhị phân để tìm mục và sau đó chèn thích hợp:

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

Đừng sắp xếp lại sau mỗi mục, quá mức cần thiết ..

Nếu chỉ có một mục để chèn, bạn có thể tìm vị trí cần chèn bằng tìm kiếm nhị phân. Sau đó sử dụng memcpy hoặc tương tự để sao chép hàng loạt các mục còn lại để tạo khoảng trống cho mục được chèn. Tìm kiếm nhị phân là O (log n) và bản sao là O (n), cho tổng O (n + log n). Sử dụng các phương pháp trên, bạn đang thực hiện sắp xếp lại sau mỗi lần chèn, đó là O (n log n).

Có vấn đề gì không? Hãy nói rằng bạn đang chèn ngẫu nhiên các phần tử k, trong đó k = 1000. Danh sách được sắp xếp là 5000 mục.

  • 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

Nếu các mục k để chèn đến bất cứ khi nào, thì bạn phải tìm kiếm + di chuyển. Tuy nhiên, nếu bạn được cung cấp một danh sách các mục k để chèn vào một mảng được sắp xếp - trước thời hạn - thì bạn có thể làm tốt hơn nữa. Sắp xếp các mục k, tách biệt với mảng n đã được sắp xếp. Sau đó thực hiện sắp xếp quét, trong đó bạn di chuyển xuống cả hai mảng được sắp xếp đồng thời, hợp nhất một mảng với nhau. - Sắp xếp hợp nhất một bước = k log k + n = 9965 + 5000 = ~ 15.000 ops

Cập nhật: Liên quan đến câu hỏi của bạn.
First method = binary search+move = O(n + log n). Second method = re-sort = O(n log n)Giải thích chính xác thời gian bạn nhận được.


có, nhưng không, nó phụ thuộc vào thuật toán sắp xếp của bạn. Sử dụng sắp xếp bong bóng theo thứ tự ngược lại, sắp xếp của bạn nếu phần tử cuối cùng không được sắp xếp luô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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.