Đầu tiên, một chút nền tảng để chỉ ra tại sao đây không phải là một vấn đề khó. Dòng chảy qua một dòng sông đảm bảo rằng các phân đoạn của nó, nếu được số hóa chính xác, luôn có thể được định hướng để tạo thành một biểu đồ chu kỳ có hướng (DAG). Đổi lại, một đồ thị có thể được sắp xếp tuyến tính khi và chỉ khi nó là DAG, sử dụng một kỹ thuật được gọi là sắp xếp tôpô . Sắp xếp tôpô rất nhanh: yêu cầu về thời gian và không gian của nó là cả O (| E | + | V |) (E = số cạnh, V = số đỉnh), cũng tốt như nó đạt được. Tạo một thứ tự tuyến tính như vậy sẽ giúp bạn dễ dàng tìm thấy dòng suối chính.
Ở đây, sau đó, là một bản phác thảo của một thuật toán . Miệng suối nằm dọc theo giường chính của nó. Di chuyển ngược dòng dọc theo mỗi nhánh gắn vào miệng (có thể có nhiều hơn một, nếu miệng là hợp lưu) và đệ quy tìm giường chính dẫn xuống nhánh đó. Chọn nhánh có tổng chiều dài lớn nhất: đó là "backlink" của bạn dọc theo giường chính.
Để làm cho điều này rõ ràng hơn, tôi cung cấp một số mã giả (chưa được kiểm tra) . Đầu vào là một tập hợp các phân đoạn dòng (hoặc cung) S (bao gồm luồng số hóa), mỗi dòng có hai điểm cuối khác nhau bắt đầu (S) và kết thúc (S) và chiều dài dương, chiều dài (S); và cửa sông p , đó là một điểm. Đầu ra là một chuỗi các phân đoạn hợp nhất miệng với điểm ngược dòng xa nhất.
Chúng tôi sẽ cần phải làm việc với "các phân đoạn được đánh dấu" (S, p). Chúng bao gồm một trong các đoạn S cùng với một trong hai điểm cuối của nó, p . Chúng ta sẽ cần tìm tất cả các phân đoạn S có chung điểm cuối với điểm thăm dò q , đánh dấu các phân đoạn đó bằng các điểm cuối khác của chúng và trả về tập hợp:
Procedure Extract(q: point, A: set of segments): Set of marked segments.
Khi không tìm thấy đoạn nào như vậy, Extract phải trả về tập hợp trống. Là một tác dụng phụ, Extract phải loại bỏ tất cả các phân đoạn nó đang trở lại từ tập A, qua đó thay đổi Một chính nó.
Tôi không đưa ra triển khai Trích xuất: GIS của bạn sẽ cung cấp khả năng chọn các phân đoạn S chia sẻ điểm cuối với q . Đánh dấu chúng chỉ đơn giản là vấn đề so sánh cả bắt đầu (S) và kết thúc (S) với q và trả về bất kỳ điểm nào trong hai điểm cuối không khớp.
Bây giờ chúng tôi đã sẵn sàng để giải quyết vấn đề.
Procedure LongestUpstreamReach(p: point, A: set of segments): (Array of segments, length)
A0 = A // Optional: preserves A
C = Extract(p, A0) // Removes found segments from the set A0!
L = 0; B = empty array
For each (S,q) in C: // Loop over the segments meeting point p
(B0, M) = LongestUpstreamReach(q, A0)
If (length(S) + M > L) then
B = append(S, B0)
L = length(S) + M
End if
End for
Return (B, L)
End LongestUpstreamReach
Quy trình "chắp thêm (S, B0)" dính đoạn S ở cuối mảng B0 và trả về mảng mới.
(Nếu luồng thực sự là một cái cây: không có đảo, hồ, dải bện, v.v. - thì bạn có thể phân phối với bước sao chép A vào A0 .)
Câu hỏi ban đầu được trả lời bằng cách hình thành sự kết hợp của các phân đoạn được trả về bởi LongestUpstreamReach.
Để minh họa , hãy xem xét luồng trong bản đồ gốc. Giả sử nó được số hóa thành một tập hợp gồm bảy cung. Vòng cung a đi từ miệng tại điểm 0 (trên cùng của bản đồ, ở bên phải trong hình bên dưới, được quay) ngược dòng đến ngã ba đầu tiên tại điểm 1. Đó là một cung dài, dài 8 đơn vị. Arc b nhánh bên trái (trong bản đồ) và ngắn, dài khoảng 2 đơn vị. Arc c nhánh ở bên phải và dài khoảng 4 đơn vị, v.v. Để "b", "d" và "f" biểu thị các nhánh bên trái khi chúng ta đi từ trên xuống dưới trên bản đồ và "a", "c", "e" và "g" các nhánh khác và đánh số các đỉnh từ 0 đến 7, chúng ta có thể biểu diễn một cách trừu tượng biểu đồ dưới dạng tập hợp các cung
A = {a=(0,1), b=(1,2), c=(1,3), d=(3,4), e=(3,5), f=(5,6), g=(5,7)}
Tôi sẽ giả sử chúng có độ dài 8, 2, 4, 1, 2, 2, 2 tương ứng từ a đến g . Miệng là đỉnh 0.
Ví dụ đầu tiên là lệnh gọi Trích xuất (5, {f, g}). Nó trả về tập hợp các phân đoạn được đánh dấu {(f, 6), (g, 7)}. Lưu ý rằng đỉnh 5 nằm ở hợp lưu của các cung f và g (hai cung ở dưới cùng của bản đồ) và (f, 6) và (g, 7) đánh dấu mỗi cung này bằng các điểm cuối ngược dòng của chúng .
Ví dụ tiếp theo là cuộc gọi đến LongestUpstreamReach (0, A). Hành động đầu tiên cần thực hiện là gọi tới Trích xuất (0, A). Điều này trả về một tập hợp chứa phân đoạn được đánh dấu (a, 1) và nó loại bỏ phân đoạn a khỏi bộ A0 , hiện bằng {b, c, d, e, f, g}. Có một lần lặp của vòng lặp, trong đó (S, q) = (a, 1). Trong lần lặp này, một cuộc gọi được thực hiện cho LongestUpstreamReach (1, A0). Đệ quy, nó phải trả về chuỗi (g, e, c) hoặc (f, e, c): cả hai đều có giá trị như nhau. Độ dài (M) mà nó trả về là 4 + 2 + 2 = 8. (Lưu ý rằng LongestUpstreamReach không sửa đổi A0 .) Ở cuối vòng lặp, hãy phân đoạn ađã được thêm vào giường luồng và độ dài đã được tăng lên 8 + 8 = 16. Do đó, giá trị trả về đầu tiên bao gồm chuỗi (g, e, c, a) hoặc (f, e, c, a), với độ dài 16 trong cả hai trường hợp cho giá trị trả về thứ hai. Điều này cho thấy LongestUpstreamReach chỉ di chuyển ngược dòng từ miệng, chọn tại mỗi ngã ba nhánh với khoảng cách xa nhất chưa đi và theo dõi các đoạn đi qua tuyến đường của nó.
Việc triển khai hiệu quả hơn là có thể khi có nhiều dải bện và đảo, nhưng đối với hầu hết các mục đích, sẽ có ít nỗ lực lãng phí nếu LongestUpstreamReach được triển khai chính xác như được hiển thị, bởi vì tại mỗi ngã ba không có sự trùng lặp giữa các tìm kiếm trong các nhánh khác nhau: điện toán thời gian (và độ sâu ngăn xếp) sẽ tỷ lệ thuận với tổng số phân đoạn.