So sánh biểu diễn đồ thị đối tượng với danh sách kề và biểu diễn ma trận


81

Tôi hiện đang làm theo lời khuyên của Steve Yegge về việc chuẩn bị cho một cuộc phỏng vấn lập trình kỹ thuật: http://steve-yegge.blogspot.com/2008/03/get-that-job-at-google.html

Trong phần về Đồ thị, anh ấy nói:

Có ba cách cơ bản để biểu diễn một biểu đồ trong bộ nhớ (đối tượng và con trỏ, ma trận và danh sách kề), và bạn nên tự làm quen với từng biểu diễn và ưu nhược điểm của nó.

Ưu và nhược điểm của biểu diễn ma trận và danh sách kề được mô tả trong CLRS, nhưng tôi không thể tìm thấy tài nguyên so sánh chúng với biểu diễn đối tượng.

Chỉ cần suy nghĩ về nó, tôi có thể tự mình suy ra một số điều này, nhưng tôi muốn đảm bảo rằng mình không bỏ lỡ điều gì quan trọng. Nếu ai đó có thể mô tả điều này một cách toàn diện, hoặc chỉ cho tôi một tài nguyên làm được như vậy, tôi sẽ đánh giá rất cao.


còn đồ thị quy nạp thì sao - chúng thuộc loại nào trong 3 loại này?
Erik Kaplun

Câu trả lời:


94

đối tượng và con trỏ

Đây chỉ là những cấu trúc dữ liệu cơ bản như hammar đã nói trong câu trả lời khác, trong Javabạn sẽ biểu diễn điều này bằng các lớp như cạnh và đỉnh. Ví dụ, một cạnh nối hai đỉnh và có thể được định hướng hoặc vô hướng và nó có thể chứa một trọng số. Một đỉnh có thể có một ID, tên, v.v. Hầu hết cả hai đều có các thuộc tính bổ sung. Vì vậy, bạn có thể xây dựng biểu đồ của mình với chúng như

Vertex a = new Vertex(1);
Vertex b = new Vertex(2);
Edge edge = new Edge(a,b, 30); // init an edge between ab and be with weight 30  

Cách tiếp cận này thường được sử dụng cho các triển khai hướng đối tượng, vì nó dễ đọc và thuận tiện hơn cho người dùng hướng đối tượng;).

ma trận

Ma trận chỉ là một mảng 2 chiều đơn giản. Giả sử bạn có ID đỉnh có thể được biểu diễn dưới dạng một mảng int như sau:

int[][] adjacencyMatrix = new int[SIZE][SIZE]; // SIZE is the number of vertices in our graph
adjacencyMatrix[0][1] = 30; // sets the weight of a vertex 0 that is adjacent to vertex 1

Điều này thường được sử dụng cho các đồ thị dày đặc nơi cần truy cập chỉ mục. Bạn có thể đại diện cho một cấu trúc không / có hướng và có trọng số với điều này.

danh sách gần kề

Đây chỉ là một hỗn hợp cấu trúc dữ liệu đơn giản, tôi thường thực hiện điều này bằng cách sử dụng a HashMap<Vertex, List<Vertex>>. Tương tự có thể được sử dụng HashMultimaptrong Ổi.

Cách tiếp cận này rất hay, bởi vì bạn có tra cứu đỉnh O (1) (phân bổ) và nó trả về cho tôi danh sách tất cả các đỉnh liền kề với đỉnh cụ thể mà tôi yêu cầu này.

ArrayList<Vertex> list = new ArrayList<>();
list.add(new Vertex(2));
list.add(new Vertex(3));
map.put(new Vertex(1), list); // vertex 1 is adjacent to 2 and 3

Điều này được sử dụng để biểu diễn các đồ thị thưa thớt, nếu bạn đang nộp đơn tại Google, bạn nên biết rằng các đồ thị thưa thớt. Bạn có thể đối phó với chúng theo cách có thể mở rộng hơn bằng cách sử dụng BigTable .

Ồ và BTW, đây là một bản tóm tắt rất hay về bài đăng này với những hình ảnh lạ mắt;)


Cách tiếp cận này rất hay, bởi vì bạn có O (1) tra cứu đỉnh , độ phức tạp này hơi sai, cụ thể là O (1 + alpha) trong đó alpha = num các vị trí trong bản đồ băm / num các đỉnh. Do đó, tôi đề xuất sử dụng mảng thay vì bản đồ băm
Timofey

@Tim nó được khấu hao O (1). Tính toán độ phức tạp của bạn là phụ thuộc thực hiện mạnh mẽ. Xem javadoc của HashMap( docs.oracle.com/javase/7/docs/api/java/util/HashMap.html ) nó cho biết: This implementation provides constant-time performance for the basic operations= O (1) được khấu hao.
Thomas Jungblut

6
@Tim Tôi nghĩ mọi người ở đây đều biết rằng truy cập mảng nhanh hơn bất kỳ HashTablecách sử dụng nào . Vì vậy, không cần phải thực hiện arround với chi phí alpha không đổi nhỏ có thể bị bỏ qua.
Thomas Jungblut

2
Xin đừng hiểu sai ý tôi, tôi không xúc phạm bạn câu trả lời hay, nhưng tôi có cảm giác rằng câu trả lời của bạn có thể được cải thiện, vì vậy tại sao không đề cập ở đây :)
Timofey

2
@Tim Tôi đã thêm ghi chú khấu hao vào câu trả lời. Cảm ơn.
Thomas Jungblut

7

Các đối tượng và con trỏ hầu hết giống với danh sách kề, ít nhất là cho mục đích so sánh các thuật toán sử dụng các biểu diễn này.

So sánh

struct Node {
    Node *neighbours[];
};

với

struct Node {
    Node *left;
    Node *right;
};

Bạn có thể dễ dàng xây dựng danh sách hàng xóm đang di chuyển trong trường hợp sau, nếu nó dễ làm việc hơn so với các con trỏ được đặt tên.


4

Lợi thế của biểu diễn đối tượng ( danh sách tỷ lệ ) là hai đỉnh liền kề có chung một thể hiện của cạnh. Điều này giúp bạn dễ dàng thao tác với dữ liệu cạnh vô hướng (chiều dài, chi phí, luồng hoặc thậm chí hướng). Tuy nhiên nó sử dụng thêm bộ nhớ cho con trỏ.


5
tại sao có một liên kết đến đại diện danh sách gần kề có tên là "danh sách tỷ lệ mắc phải"? Có lẽ tốt hơn là sử dụng một thuật toán
này.com/index.php/Graph_data_structures

1

Một tài nguyên hay khác: Học viện Khan - "Biểu đồ đại diện"

Bên cạnh danh sách kề và ma trận kề, chúng liệt kê "danh sách cạnh" như một loại biểu diễn đồ thị thứ ba. Danh sách cạnh có thể được hiểu là danh sách các "đối tượng cạnh" giống như trong câu trả lời "đối tượng và con trỏ" của Thomas.

Ưu điểm: Chúng tôi có thể lưu trữ thêm thông tin về cạnh (được đề cập bởi Michal)

Nhược điểm: Đó là một cấu trúc dữ liệu rất chậm để làm việc với:

  • Tra cứu một cạnh: O (log e)
  • Loại bỏ một cạnh: O (e)
  • Tìm tất cả các nút lân cận với một nút đã cho: O (e)
  • Xác định xem có tồn tại một con đường giữa hai nút: O (e ^ 2)

e = số cạnh

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.