Big O của mảng JavaScript


105

Mảng trong JavaScript rất dễ sửa đổi bằng cách thêm và bớt các mục. Nó phần nào che giấu thực tế rằng hầu hết các mảng ngôn ngữ có kích thước cố định và yêu cầu các hoạt động phức tạp để thay đổi kích thước. Có vẻ như JavaScript làm cho nó dễ dàng viết mã mảng hoạt động kém. Điều này dẫn đến câu hỏi:

Tôi có thể mong đợi hiệu suất nào (về độ phức tạp theo thời gian O lớn) từ việc triển khai JavaScript liên quan đến hiệu suất mảng?

Tôi giả sử rằng tất cả các triển khai JavaScript hợp lý có ít nhất các chữ O lớn sau đây.

  • Truy cập - O (1)
  • Appending - O (n)
  • Chi tiêu trước - O (n)
  • Chèn - O (n)
  • Xóa - O (n)
  • Hoán đổi - O (1)

JavaScript cho phép bạn điền trước một mảng đến một kích thước nhất định, sử dụng new Array(length)cú pháp. (Câu hỏi bổ sung: Đang tạo một mảng theo cách này O (1) hoặc O (n)) Đây giống như một mảng thông thường và nếu được sử dụng như một mảng có kích thước trước, có thể cho phép O (1) thêm vào. Nếu logic đệm tròn được thêm vào, bạn có thể đạt được O (1) trước. Nếu một mảng mở rộng động được sử dụng, O (log n) sẽ là trường hợp trung bình cho cả hai.

Tôi có thể mong đợi hiệu suất tốt hơn cho một số điều so với giả định của tôi ở đây không? Tôi không mong đợi bất kỳ điều gì được nêu trong bất kỳ thông số kỹ thuật nào, nhưng trên thực tế, có thể là tất cả các triển khai chính đều sử dụng các mảng được tối ưu hóa đằng sau hậu trường. Có các mảng mở rộng động hoặc một số thuật toán tăng hiệu suất khác đang hoạt động không?

PS

Lý do tôi tự hỏi điều này là tôi đang nghiên cứu một số thuật toán sắp xếp, hầu hết trong số đó dường như giả sử thêm và xóa là các phép toán O (1) khi mô tả tổng thể O lớn của chúng.


6
Phương thức khởi tạo Array có kích thước khá vô dụng trong các triển khai JavaScript hiện đại. Nó hầu như không làm gì ở dạng tham số duy nhất đó. (Nó thiết lập .lengthnhưng đó là về nó.) Mảng thực sự không khác nhiều so với các đối tượng đơn giản.
Pointy

3
Đặt thuộc lengthtính và phân bổ trước không gian là hai việc hoàn toàn khác nhau.
Pointy

1
@Pointy: Có phải tôi đã kỳ vọng quá nhiều khi tôi mong đợi thiết lập array[5]trên a new Array(10)là O (1) không?
Kendall Frey

1
Mặc dù ECMAScript không xác định cách một đối tượng Mảng được triển khai (nó chỉ xác định một số quy tắc ngữ nghĩa), rất có thể các triển khai khác nhau sẽ tối ưu hóa cho các trường hợp mong đợi (ví dụ: có một "mảng thực" hỗ trợ cho các mảng có kích thước nhỏ hơn một số n ). Tôi không hiểu lắm về việc triển khai, nhưng sẽ thực sự ngạc nhiên nếu điều này không được thực hiện ở đâu đó ...

5
@KendallFrey "Câu trả lời tốt nhất" là khả năng viết một số jsperf thử trường hợp cho n / mô hình truy cập khác nhau và xem những gì xuất phát của nó ;-)

Câu trả lời:


111

LƯU Ý: Mặc dù câu trả lời này đúng vào năm 2012, nhưng ngày nay các công cụ sử dụng các biểu diễn bên trong rất khác nhau cho cả đối tượng và mảng. Câu trả lời này có thể đúng hoặc không.

Ngược lại với hầu hết các ngôn ngữ triển khai mảng với, tốt, mảng, trong Javascript Mảng là đối tượng và giá trị được lưu trữ trong bảng băm, giống như giá trị đối tượng thông thường. Như vậy:

  • Truy cập - O (1)
  • Appending - Phân bổ O (1) (đôi khi cần thay đổi kích thước bảng băm; thường chỉ cần chèn)
  • Chi trước - O (n) qua unshift, vì nó yêu cầu gán lại tất cả các chỉ mục
  • Phần chèn - Phân bổ O (1) nếu giá trị không tồn tại. O (n) nếu bạn muốn thay đổi các giá trị hiện có (Ví dụ: sử dụng splice).
  • Xóa - Phân bổ O (1) để xóa một giá trị, O (n) nếu bạn muốn chỉ định lại các chỉ số qua splice.
  • Hoán đổi - O (1)

Nói chung, việc thiết lập hoặc bỏ đặt bất kỳ khóa nào trong một chính tả được khấu hao O (1), và điều tương tự cũng xảy ra đối với mảng, bất kể chỉ mục là gì. Bất kỳ thao tác nào yêu cầu đánh số lại các giá trị hiện có là O (n) đơn giản vì bạn phải cập nhật tất cả các giá trị bị ảnh hưởng.


4
Không nên thêm vào trước là O (n)? Vì tất cả các chỉ số cần phải được thay đổi. Tương tự cho việc chèn và xóa (ở chỉ mục tùy ý, và dịch chuyển / thu gọn các phần tử).
nhahtdh 18/07/12

2
Ngoài ra, được lengthđặt trên đột biến Mảng, hay getnó sẽ có độ dài và có thể ghi nhớ nó?
alex

27
Đáng nói câu trả lời này không còn đúng nữa. Các công cụ hiện đại không lưu trữ Mảng (hoặc các đối tượng có khóa số nguyên được lập chỉ mục) dưới dạng hashtable (nhưng cũng giống như ... các mảng như trong C) trừ khi chúng thưa thớt. Để giúp bạn bắt đầu ở đây là một chuẩn mực 'cổ điển' minh họa này
Benjamin Gruenbaum

4
Điều này được định nghĩa bởi tiêu chuẩn hay đây chỉ là một triển khai phổ biến trong các công cụ JS? Còn về V8?
Albert

4
@BenjaminGruenbaum sẽ rất tuyệt nếu bạn có thể phát triển một chút về cách chúng được lưu trữ. Hoặc đưa ra một số nguồn.
Ced

1

Bảo hành

Không có đảm bảo độ phức tạp về thời gian cụ thể cho bất kỳ hoạt động mảng nào. Các mảng hoạt động như thế nào phụ thuộc vào cấu trúc dữ liệu cơ bản mà công cụ chọn. Các động cơ cũng có thể có các cách biểu diễn khác nhau và chuyển đổi giữa chúng tùy thuộc vào một số kinh nghiệm nhất định. Kích thước mảng ban đầu có thể là một heuristic như vậy hoặc không.

thực tế

Ví dụ: V8 sử dụng (tính đến ngày hôm nay) cả hashtablesdanh sách mảng để đại diện cho mảng. Nó cũng có nhiều cách biểu diễn khác nhau cho các đối tượng, vì vậy không thể so sánh mảng và đối tượng. Do đó truy cập mảng luôn luôn tốt hơn là O (n) là gì, và có thể thậm chí được nhanh như C ++ truy cập mảng. Appending là O (1), trừ khi bạn đạt đến kích thước của cơ cấu dữ liệu và nó phải được chia tỷ lệ (wich là O (n)). Chi tiêu trước còn tệ hơn. Việc xóa có thể thậm chí còn tồi tệ hơn nếu bạn làm điều gì đó như delete array[index](không!), Vì điều đó có thể buộc động cơ thay đổi cách biểu diễn của nó.

khuyên bảo

Sử dụng mảng cho cấu trúc dữ liệu số. Đó là những gì họ có nghĩa là cho. Đó là những gì động cơ sẽ tối ưu hóa chúng. Tránh các mảng thưa thớt (hoặc nếu bạn phải làm vậy, mong đợi hiệu suất kém hơn). Tránh các mảng có kiểu dữ liệu hỗn hợp (vì điều đó làm cho các biểu diễn bên trong phức tạp hơn ).

Nếu bạn thực sự muốn tối ưu hóa cho một công cụ (và phiên bản) nhất định, hãy kiểm tra mã nguồn của nó để có câu trả lời tuyệt đối.


Chờ một chút, chúng ta có thể có các mảng với kiểu dữ liệu hỗn hợp? Javascript thật tuyệt!
Anurag

@Anurag chính xác, nhưng trong 99% trường hợp, bạn sẽ không cần tính năng này
Desiigner
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.