Làm thế nào để tôi đi qua một cây mà không sử dụng đệ quy?


19

Tôi có một cây nút bộ nhớ rất lớn và cần phải đi qua cây. Truyền các giá trị được trả về của mỗi nút con vào nút cha của chúng. Điều này phải được thực hiện cho đến khi tất cả các nút có bong bóng dữ liệu của chúng lên đến nút gốc.

Traversal hoạt động như thế này.

private Data Execute(Node pNode)
{
    Data[] values = new Data[pNode.Children.Count];
    for(int i=0; i < pNode.Children.Count; i++)
    {
        values[i] = Execute(pNode.Children[i]);  // recursive
    }
    return pNode.Process(values);
}

public void Start(Node pRoot)
{
    Data result = Execute(pRoot);
}

Điều này hoạt động tốt, nhưng tôi lo lắng rằng ngăn xếp cuộc gọi giới hạn kích thước của cây nút.

Làm thế nào mã có thể được viết lại để không có cuộc gọi đệ quy Executeđược thực hiện?


8
Bạn sẽ phải duy trì ngăn xếp của riêng mình để theo dõi các nút hoặc thay đổi hình dạng của cây. Xem stackoverflow.com/q/5496464stackoverflow.com/q/4581576
Robert Harvey

1
Tôi cũng tìm thấy rất nhiều sự giúp đỡ tại Tìm kiếm Google này , cụ thể là Morris Traversal .
Robert Harvey

@RobertHarvey cảm ơn Rob, tôi không chắc điều này sẽ diễn ra như thế nào.
Phản ứng

2
Bạn có thể ngạc nhiên về các yêu cầu bộ nhớ nếu bạn đã làm toán. Ví dụ, cây nhị phân teranode cân bằng hoàn hảo chỉ cần một ngăn xếp sâu 40 mục.
Karl Bielefeldt

@KarlBielefeldt Giả sử rằng cây hoàn toàn cân bằng. Đôi khi bạn cần phải mô hình hóa những cây không cân đối, và trong trường hợp đó, rất dễ dàng để thổi tung ngăn xếp.
Phục vụ

Câu trả lời:


27

Dưới đây là cách thực hiện duyệt qua cây mục đích chung mà không sử dụng đệ quy:

public static IEnumerable<T> Traverse<T>(T item, Func<T, IEnumerable<T>> childSelector)
{
    var stack = new Stack<T>();
    stack.Push(item);
    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childSelector(next))
            stack.Push(child);
    }
}

Trong trường hợp của bạn, sau đó bạn có thể gọi nó như vậy:

IEnumerable<Node> allNodes = Traverse(pRoot, node => node.Children);

Sử dụng Queuethay vì Stackmột hơi thở trước, thay vì sâu trước, tìm kiếm. Sử dụng PriorityQueuecho một tìm kiếm đầu tiên tốt nhất.


Tôi có đúng không khi nghĩ rằng điều này sẽ làm phẳng cây thành một bộ sưu tập?
Phản ứng

1
@MathewFoscarini Vâng, đó là mục đích của nó. Tất nhiên, nó không nhất thiết phải được vật chất hóa thành một bộ sưu tập thực tế. Nó chỉ là một chuỗi. Bạn có thể lặp qua nó để truyền dữ liệu mà không cần phải kéo toàn bộ dữ liệu vào bộ nhớ.
Phục vụ

Tôi không nghĩ rằng giải quyết vấn đề.
Phản ứng

4
Anh ta không chỉ đi qua biểu đồ thực hiện các hoạt động độc lập như tìm kiếm, anh ta tổng hợp dữ liệu từ các nút con. Làm phẳng cây phá hủy thông tin cấu trúc anh ta cần để thực hiện tổng hợp.
Karl Bielefeldt

1
FYI Tôi nghĩ rằng đây là câu trả lời chính xác mà hầu hết mọi người đang tìm kiếm câu hỏi này đang tìm kiếm. +1
Anders Arpi

4

Nếu bạn có ước tính về độ sâu của cây trước đó, có lẽ nó đủ để trường hợp của bạn thích nghi với kích thước ngăn xếp? Trong C # kể từ phiên bản 2.0, điều này có thể xảy ra bất cứ khi nào bạn bắt đầu một chủ đề mới, xem tại đây:

http://www.irthasoft.com/cs/bloss/rickm/archive/2008/04/22/increasing-the-size-of-your-stack-net-memory-man Quản lý-part-3.aspx

Bằng cách đó bạn có thể giữ mã đệ quy của mình mà không phải thực hiện một cái gì đó phức tạp hơn. Tất nhiên, việc tạo một giải pháp không đệ quy với ngăn xếp của riêng bạn có thể sẽ tốn nhiều thời gian và bộ nhớ hơn, nhưng tôi chắc chắn rằng mã sẽ không đơn giản như bây giờ.


Tôi chỉ làm một bài kiểm tra nhanh. Trên máy của tôi, tôi có thể thực hiện 14000 cuộc gọi đệ quy trước khi đạt được stackoverflow. Nếu cây được cân bằng chỉ cần 32 cuộc gọi để lưu trữ 4 tỷ nút. Nếu mỗi nút là 1 byte (sẽ không mất) Sẽ mất 4 GB ram để lưu trữ một cây cân bằng có chiều cao 32.
Esben Skov Pedersen

Tôi đã sử dụng tất cả 14000 cuộc gọi trong ngăn xếp. Cây sẽ chiếm 2,6x10 ^ 4214 byte nếu mỗi nút là một byte (sẽ không như vậy)
Esben Skov Pedersen

-3

Bạn không thể di chuyển cấu trúc dữ liệu theo hình dạng của cây mà không sử dụng đệ quy - nếu bạn không sử dụng các khung ngăn xếp và các lệnh gọi hàm do ngôn ngữ của bạn cung cấp, về cơ bản bạn phải lập trình các lệnh gọi ngăn xếp và hàm của riêng bạn, và đó là không chắc rằng bạn quản lý để thực hiện nó trong ngôn ngữ theo cách hiệu quả hơn so với các trình soạn thảo trình biên dịch đã làm trên máy mà chương trình của bạn sẽ chạy.

Do đó, tránh đệ quy vì sợ chạy vào giới hạn tài nguyên thường là sai lầm. Để chắc chắn, tối ưu hóa tài nguyên sớm luôn bị nhầm lẫn, nhưng trong trường hợp này có khả năng là ngay cả khi bạn đo lường và xác nhận rằng việc sử dụng bộ nhớ là nút cổ chai, bạn có thể sẽ không thể cải thiện nó mà không giảm xuống mức biên dịch nhà văn.


2
Điều này chỉ đơn giản là sai. Chắc chắn là có thể đi qua một cái cây mà không cần sử dụng đệ quy. Nó thậm chí không khó . Bạn cũng có thể làm như vậy một cách hiệu quả, khá tầm thường, vì bạn chỉ có thể bao gồm nhiều thông tin trong ngăn xếp rõ ràng vì bạn chắc chắn rằng bạn cần cho giao dịch cụ thể của mình, trong khi sử dụng đệ quy bạn sẽ lưu trữ nhiều thông tin hơn bạn thực sự cần trong nhiều các trường hợp.
Phục vụ

2
Cuộc tranh cãi này cứ thỉnh thoảng xuất hiện ở đây. Một số áp phích xem xét việc cuộn ngăn xếp của riêng bạn không phải là đệ quy, trong khi những người khác chỉ ra rằng họ chỉ đang làm điều tương tự một cách rõ ràng rằng thời gian chạy sẽ làm ngầm. Không có điểm nào để tranh luận về các định nghĩa như thế này.
Kilian Foth

Làm thế nào để bạn xác định đệ quy sau đó? Tôi sẽ định nghĩa nó như là một hàm gọi chính nó trong định nghĩa riêng của nó. Bạn chắc chắn có thể đi ngang qua một cái cây mà không bao giờ làm điều đó, như tôi đã chứng minh trong câu trả lời của mình.
Phục vụ

2
Tôi có ác không khi thưởng thức hành động nhấp vào downvote trên người có điểm đại diện cao như vậy? Đó là một niềm vui hiếm có trên trang web này.
Phản ứng

2
Thôi nào @Mat, đó là đồ trẻ con. Bạn có thể không đồng ý, giống như nếu bạn sợ ném bom vào một cái cây quá sâu, đó là một mối quan tâm hợp lý. Bạn chỉ có thể nói như vậy.
Mike Dunlavey
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.