Khi nào nên sử dụng danh sách liên kết qua danh sách mảng / mảng?


176

Tôi sử dụng rất nhiều danh sách và mảng nhưng tôi chưa bắt gặp một kịch bản trong đó danh sách mảng không thể được sử dụng dễ dàng như, nếu không dễ hơn danh sách được liên kết. Tôi đã hy vọng ai đó có thể cho tôi một số ví dụ khi danh sách được liên kết tốt hơn đáng kể.


Trong Java, ArrayList và LinkedList sử dụng chính xác cùng một mã khác với hàm tạo. "Danh sách mảng ... của bạn được sử dụng dễ dàng hoặc dễ dàng hơn danh sách được liên kết" không có ý nghĩa. Vui lòng cung cấp một ví dụ về ArrayList là "dễ dàng" hơn so với LinkedList.
S.Lott

2
Cũng kiểm tra điều này, stackoverflow.com/questions/322715/ Cách
NoNaMe


3
S.Lott Điều đó không đúng. Java ArrayList là một trình bao bọc xung quanh một Array, với một số chức năng tiện ích được thêm vào. Một danh sách liên kết rõ ràng là một danh sách liên kết. developer
classpath.org/doc/java/util/ArrayList-source.html

Câu trả lời:


261

Danh sách liên kết được ưu tiên hơn các mảng khi:

  1. bạn cần chèn / xóa thời gian liên tục khỏi danh sách (chẳng hạn như trong điện toán thời gian thực trong đó khả năng dự đoán thời gian là cực kỳ quan trọng)

  2. bạn không biết có bao nhiêu mặt hàng trong danh sách. Với mảng, bạn có thể cần phải khai báo lại và sao chép bộ nhớ nếu mảng phát triển quá lớn

  3. bạn không cần truy cập ngẫu nhiên vào bất kỳ yếu tố nào

  4. bạn muốn có thể chèn các mục vào giữa danh sách (chẳng hạn như hàng đợi ưu tiên)

Mảng được ưa thích hơn khi:

  1. bạn cần truy cập được lập chỉ mục / ngẫu nhiên vào các yếu tố

  2. bạn biết số lượng phần tử trong mảng trước thời hạn để bạn có thể phân bổ số lượng bộ nhớ chính xác cho mảng

  3. bạn cần tốc độ khi lặp qua tất cả các yếu tố theo trình tự. Bạn có thể sử dụng toán con trỏ trên mảng để truy cập từng phần tử, trong khi bạn cần tra cứu nút dựa trên con trỏ cho từng phần tử trong danh sách được liên kết, điều này có thể dẫn đến lỗi trang có thể dẫn đến kết quả hoạt động.

  4. bộ nhớ là một mối quan tâm. Các mảng được lấp đầy chiếm ít bộ nhớ hơn các danh sách được liên kết. Mỗi phần tử trong mảng chỉ là dữ liệu. Mỗi nút danh sách được liên kết yêu cầu dữ liệu cũng như một (hoặc nhiều) con trỏ tới các thành phần khác trong danh sách được liên kết.

Danh sách mảng (như những người trong .Net) cung cấp cho bạn những lợi ích của mảng, nhưng phân bổ động các tài nguyên cho bạn để bạn không cần phải lo lắng quá nhiều về kích thước danh sách và bạn có thể xóa các mục ở bất kỳ chỉ mục nào mà không cần nỗ lực hoặc tái sử dụng xáo trộn các yếu tố xung quanh. Hiệu suất-khôn ngoan, danh sách mảng chậm hơn mảng thô.


7
Khởi đầu tốt, nhưng điều này bỏ qua những điều quan trọng: liệt kê chia sẻ cấu trúc hỗ trợ, mảng dày đặc hơn và có địa phương tốt hơn.
Darius Bacon

1
Thực tế, sự khác biệt hiệu suất giữa danh sách mảng và mảng là không đáng kể. Điều này giả định rằng bạn so sánh so sánh và, ví dụ, khi bạn biết kích thước trước đó, bạn nói với danh sách mảng về nó.
Svick

40
Kể từ khi LinkedList có chèn (xóa) O (1) (đó là điều tôi cho là bạn muốn nói khi bạn nói chèn / xóa thời gian không đổi )? Chèn nội dung vào giữa Danh sách liên kết luôn là O (n)
Pacerier

28
LinkedLists có chèn O (1) nếu bạn đã ở vị trí chèn (thông qua một trình vòng lặp). Không phải lúc nào cũng vậy.
Adam

4
Sử dụng danh sách liên kết cho hàng đợi ưu tiên là một ý tưởng rất ngu ngốc. Các đống được hỗ trợ mảng động cho phép chèn khấu hao O (lg n) và xóa logarit trong trường hợp xấu nhất và là một trong các cấu trúc hàng đợi ưu tiên thực tế nhanh nhất.
Fred Foo

54

Mảng có quyền truy cập ngẫu nhiên O (1), nhưng thực sự tốn kém để thêm nội dung vào hoặc xóa nội dung khỏi.

Danh sách được liên kết thực sự rẻ để thêm hoặc xóa các mục ở bất cứ đâu và lặp lại, nhưng truy cập ngẫu nhiên là O (n).


3
Việc xóa các mục khỏi phần cuối của một mảng là thời gian tiếp theo, cũng như việc chèn / xóa các mục khỏi một trong hai danh sách được liên kết. Ở giữa ... không quá nhiều cho một trong hai.
Joey

1
@Joey không chèn / xóa ở cuối Danh sách được liên kết O (n)? Trừ khi bạn đã được định vị ở liên kết áp chót, bạn vẫn sẽ cần các bước O (n) để tìm phần tử cuối cùng, phải không?
Alex Moore-Niemi

@ AlexMoore-Niemi: Đối với một danh sách liên kết đơn, có. Nhưng nhiều người có liên kết tiến và lùi, và do đó giữ con trỏ đến một trong hai.
Joey

2
Có một danh sách được liên kết đôi sẽ khiến bạn phải tìm kiếm tiến và lùi, trừ khi LL của bạn đã đặt hàng các giá trị ... và trường hợp xấu nhất là O (n)
securecurve

"Danh sách được liên kết thực sự rẻ để thêm hoặc xóa các mục ở bất cứ đâu và lặp đi lặp lại" không hoàn toàn đúng. Nếu tôi muốn xóa một mục nằm giữa danh sách được liên kết, tôi sẽ phải lặp lại từ đầu cho đến khi tôi đạt được mục đó trong danh sách. Thời gian O (n / 2) của nó trong đó n = số mục trong danh sách. Từ câu trả lời của bạn, có vẻ như bạn đang đề xuất thời gian không đổi O (1) giống như trong mảng. Đây là thời gian liên tục để thêm / xóa khỏi nút đầu / gốc của danh sách được liên kết.
Yawar Murtaza

21
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

ArrayLists tốt cho ghi-đọc-đọc-nhiều hoặc nối thêm, nhưng xấu khi thêm / xóa từ trước hoặc giữa.


14

Để thêm vào các câu trả lời khác, hầu hết các triển khai danh sách mảng đều dự trữ thêm dung lượng ở cuối danh sách để các phần tử mới có thể được thêm vào cuối danh sách trong thời gian O (1). Khi vượt quá khả năng của một danh sách mảng, một mảng mới, lớn hơn sẽ được phân bổ bên trong và tất cả các phần tử cũ được sao chép lại. Thông thường, mảng mới gấp đôi kích thước của mảng cũ. Điều này có nghĩa là trung bình , việc thêm các phần tử mới vào cuối danh sách mảng là thao tác O (1) trong các triển khai này. Vì vậy, ngay cả khi bạn không biết trước số lượng phần tử, một danh sách mảng vẫn có thể nhanh hơn danh sách được liên kết để thêm các phần tử, miễn là bạn thêm chúng vào cuối. Rõ ràng, chèn các phần tử mới vào các vị trí tùy ý trong danh sách mảng vẫn là thao tác O (n).

Truy cập các phần tử trong danh sách mảng cũng nhanh hơn danh sách được liên kết, ngay cả khi các truy cập là tuần tự. Điều này là do các phần tử mảng được lưu trữ trong bộ nhớ liền kề và có thể được lưu trữ dễ dàng. Các nút danh sách liên kết có khả năng có thể được phân tán trên nhiều trang khác nhau.

Tôi chỉ khuyên bạn nên sử dụng danh sách được liên kết nếu bạn biết rằng bạn sẽ chèn hoặc xóa các mục tại các vị trí tùy ý. Danh sách mảng sẽ nhanh hơn cho hầu hết mọi thứ khác.


1
Ngoài ra, bạn cũng có thể triển khai các danh sách được liên kết (theo nghĩa Loại dữ liệu trừu tượng) bằng cách sử dụng mảng động. Bằng cách này, bạn có thể tận dụng bộ đệm của máy tính trong khi đã khấu hao các lần chèn và xóa thời gian liên tục ở đầu danh sách và cũng được khấu hao các lần chèn và xóa thời gian liên tục ở giữa danh sách khi bạn có chỉ mục của phần tử sau đó phải chèn được thực hiện hoặc chỉ mục của phần tử cần xóa (không cần dịch chuyển / không dịch chuyển). Một tài liệu tham khảo tốt cho điều này là CLRS 10.3 .
Domenico De Felice

7

Lợi thế của danh sách xuất hiện nếu bạn cần chèn các mục ở giữa và không muốn bắt đầu thay đổi kích thước mảng và thay đổi mọi thứ xung quanh.

Bạn đúng ở chỗ điều này thường không phải là trường hợp. Tôi đã có một vài trường hợp rất cụ thể như thế, nhưng không quá nhiều.


Thay đổi và thay đổi kích thước mảng là những gì thực sự xảy ra khi bạn thực hiện đảo ngược ở giữa. Bạn sẽ chỉ cần thay đổi mà không thay đổi kích thước nếu bạn không đạt được mức khấu hao.
bảo mật

3

Tất cả phụ thuộc vào loại hoạt động bạn đang thực hiện trong khi lặp, tất cả các cấu trúc dữ liệu có sự đánh đổi giữa thời gian và bộ nhớ và tùy thuộc vào nhu cầu của chúng ta, chúng ta nên chọn DS phù hợp. Vì vậy, có một số trường hợp LinkedList nhanh hơn mảng và ngược lại. Hãy xem xét ba hoạt động cơ bản trên cấu trúc dữ liệu.

  • Đang tìm kiếm

Vì mảng là cấu trúc dữ liệu dựa trên chỉ mục, việc tìm kiếm Array.get (index) sẽ mất thời gian O (1) trong khi danh sách được liên kết không phải là chỉ mục DS, do đó bạn sẽ cần phải duyệt qua chỉ mục, trong đó chỉ mục <= n, n là kích thước của danh sách được liên kết, vì vậy mảng nhanh hơn danh sách được liên kết khi có quyền truy cập ngẫu nhiên của các phần tử.

Q.So những gì vẻ đẹp đằng sau này?

Vì Mảng là các khối bộ nhớ liền kề, các khối lớn của chúng sẽ được tải vào bộ đệm khi truy cập lần đầu, điều này giúp truy cập các phần tử còn lại của mảng tương đối nhanh, vì chúng ta truy cập vào các phần tử trong tham chiếu mảng cũng tăng nên ít bị bắt hơn bỏ lỡ, địa phương Cache đề cập đến các hoạt động trong bộ đệm và do đó thực thi nhanh hơn nhiều so với trong bộ nhớ, về cơ bản Trong mảng chúng tôi tối đa hóa cơ hội truy cập phần tử tuần tự trong bộ đệm. Mặc dù các danh sách được liên kết không nhất thiết phải nằm trong các khối bộ nhớ liền kề, nhưng không có gì đảm bảo rằng các mục xuất hiện tuần tự trong danh sách thực sự được sắp xếp gần nhau trong bộ nhớ, điều này có nghĩa là ít lần truy cập bộ đệm hơn

  • Chèn

Điều này thật dễ dàng và nhanh chóng trong LinkedList vì việc chèn là hoạt động O (1) trong LinkedList (trong Java) so với mảng, hãy xem xét trường hợp khi mảng đầy, chúng ta cần sao chép nội dung sang mảng mới nếu mảng đầy đủ khiến việc chèn một trong trường hợp xấu nhất là ArrayList của O (n), trong khi ArrayList cũng cần cập nhật chỉ mục của nó nếu bạn chèn một cái gì đó ngoại trừ ở cuối mảng, trong trường hợp danh sách được liên kết, chúng ta không cần thay đổi kích thước, bạn chỉ cần thay đổi kích thước cập nhật con trỏ.

  • Xóa

Nó hoạt động như các phần chèn thêm và tốt hơn trong LinkedList so với mảng.


2

Đó là những triển khai được sử dụng phổ biến nhất của Bộ sưu tập.

Lập danh sách:

  • chèn / xóa ở cuối nói chung O (1) trường hợp xấu nhất O (n)

  • chèn / xóa ở giữa O (n)

  • lấy bất kỳ vị trí O (1)

Danh sách liên kết:

  • chèn / xóa ở bất kỳ vị trí O (1) (lưu ý nếu bạn có tham chiếu đến phần tử)

  • lấy ở giữa O (n)

  • lấy phần tử đầu tiên hoặc cuối cùng O (1)

Vector: không sử dụng nó. Đây là một triển khai cũ tương tự như ArrayList nhưng với tất cả các phương thức được đồng bộ hóa. Đây không phải là cách tiếp cận chính xác cho danh sách chia sẻ trong môi trường đa luồng.

Bản đồ băm

chèn / xóa / truy xuất bằng phím trong O (1)

Cây / chèn / xóa / chứa trong O (log N)

Hashset chèn / xóa / chứa / kích thước trong O (1)


1

Trong thực tế bộ nhớ địa phương có ảnh hưởng hiệu suất rất lớn trong xử lý thực.

Việc tăng cường sử dụng phát trực tuyến đĩa trong xử lý "dữ liệu lớn" so với truy cập ngẫu nhiên cho thấy cách cấu trúc ứng dụng của bạn xung quanh điều này có thể cải thiện đáng kể hiệu suất ở quy mô lớn hơn.

Nếu có bất kỳ cách nào để truy cập một mảng một cách tuần tự thì đó là hiệu suất tốt nhất. Thiết kế với mục đích này ít nhất nên được xem xét nếu hiệu suất là quan trọng.


0

Hmm, Arraylist có thể được sử dụng trong các trường hợp như sau tôi đoán:

  1. bạn không chắc có bao nhiêu yếu tố
  2. nhưng bạn cần truy cập ngẫu nhiên tất cả các yếu tố thông qua lập chỉ mục

Ví dụ: bạn cần nhập và truy cập tất cả các thành phần trong danh sách liên hệ (kích thước không xác định đối với bạn)


0

Sử dụng danh sách được liên kết cho Radix Sắp xếp trên các mảng và cho các hoạt động đa thức.


0

1) Như đã giải thích ở trên, các thao tác chèn và xóa cho hiệu năng tốt (O (1)) trong LinkedList so với ArrayList (O (n)). Do đó, nếu có yêu cầu bổ sung và xóa thường xuyên trong ứng dụng thì LinkedList là một lựa chọn tốt nhất.

2) Các hoạt động tìm kiếm (phương thức get) nhanh trong Arraylist (O (1)) nhưng không có trong LinkedList (O (n)) vì vậy Nếu có ít thao tác thêm và xóa và yêu cầu hoạt động tìm kiếm nhiều hơn, ArrayList sẽ là lựa chọn tốt nhất của bạn.


0

Tôi nghĩ rằng sự khác biệt chính là liệu bạn có thường xuyên cần phải chèn hoặc xóa nội dung khỏi đầu danh sách hay không.

Với một mảng, nếu bạn loại bỏ thứ gì đó khỏi đầu danh sách thì độ phức tạp là o (n) vì tất cả các chỉ số của các thành phần mảng sẽ phải thay đổi.

Với danh sách được liên kết, nó là o (1) vì bạn chỉ cần tạo nút, gán lại đầu và gán tham chiếu cho tiếp theo như đầu trước.

Khi thường xuyên chèn hoặc xóa ở cuối danh sách, các mảng được ưu tiên hơn vì độ phức tạp sẽ là o (1), không yêu cầu reindexing, nhưng đối với danh sách được liên kết sẽ là o (n) vì bạn cần phải đi từ đầu đến nút cuối cùng.

Tôi nghĩ rằng việc tìm kiếm trong cả danh sách và mảng được liên kết sẽ là o (log n) bởi vì bạn có thể sẽ sử dụng tìm kiếm nhị phân.


0

Tôi đã thực hiện một số điểm chuẩn và thấy rằng lớp danh sách thực sự nhanh hơn LinkedList để chèn ngẫu nhiên:

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 20000;
            Random rand = new Random(12345);

            Stopwatch watch = Stopwatch.StartNew();
            LinkedList<int> ll = new LinkedList<int>();
            ll.AddLast(0);
            for (int i = 1; i < count; i++)
            {
                ll.AddBefore(ll.Find(rand.Next(i)),i);

            }
            Console.WriteLine("LinkedList/Random Add: {0}ms", watch.ElapsedMilliseconds);

            watch = Stopwatch.StartNew();
            List<int> list = new List<int>();
            list.Add(0);
            for (int i = 1; i < count; i++)
            {
                list.Insert(list.IndexOf(rand.Next(i)), i);

            }
            Console.WriteLine("List/Random Add: {0}ms", watch.ElapsedMilliseconds);

            Console.ReadLine();
        }
    }
}

Phải mất 900 ms cho danh sách được liên kết và 100ms cho lớp danh sách.

Nó tạo ra danh sách các số nguyên tiếp theo. Mỗi số nguyên mới được chèn sau một số ngẫu nhiên đã có trong danh sách. Có lẽ lớp List sử dụng một cái gì đó tốt hơn chỉ là một mảng.


Danh sách là một giao diện, không phải là một lớp
borgmater

0

Mảng, cho đến nay, là cấu trúc dữ liệu được sử dụng rộng rãi nhất. Tuy nhiên, các danh sách được liên kết tỏ ra hữu ích theo cách độc đáo của riêng họ, nơi các mảng là vụng về - hoặc đắt tiền, để nói rằng ít nhất.

Danh sách được liên kết là hữu ích để thực hiện ngăn xếp và hàng đợi trong các tình huống trong đó kích thước của chúng có thể thay đổi. Mỗi nút trong danh sách được liên kết có thể được đẩy hoặc bật mà không làm phiền phần lớn các nút. Tương tự với việc chèn / xóa các nút ở đâu đó ở giữa. Tuy nhiên, trong các mảng, tất cả các yếu tố phải được thay đổi, đây là một công việc tốn kém về thời gian thực hiện.

Cây nhị phân và cây tìm kiếm nhị phân, bảng băm và thử là một số cấu trúc dữ liệu trong đó - ít nhất là trong C - bạn cần các danh sách được liên kết làm thành phần cơ bản để xây dựng chúng.

Tuy nhiên, nên tránh các danh sách được liên kết trong các tình huống dự kiến ​​có thể gọi bất kỳ phần tử tùy ý nào bằng chỉ mục của nó.


0

Một câu trả lời đơn giản cho câu hỏi có thể được đưa ra bằng cách sử dụng các điểm sau:

  1. Mảng được sử dụng khi cần một tập hợp các yếu tố dữ liệu loại tương tự. Trong khi đó, danh sách được liên kết là một tập hợp các yếu tố liên kết dữ liệu loại hỗn hợp được gọi là các nút.

  2. Trong mảng, người ta có thể truy cập bất kỳ phần tử nào trong thời gian O (1). Trong khi đó, trong danh sách được liên kết, chúng ta sẽ cần phải duyệt toàn bộ danh sách được liên kết từ đầu đến nút yêu cầu mất thời gian O (n).

  3. Đối với mảng, một kích thước cụ thể cần phải được khai báo ban đầu. Nhưng danh sách liên kết là kích thước năng động.

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.