2014-04-14 102 views
3

我有以下類型:找到最嵌套列表

data NestedList a = Elem a | List [NestedList a] 

我想編寫一個返回給定的列表中最嵌套列表的功能,但我不知道從哪裏開始。任何幫助感謝!

實施例:

的功能輸入是一樣的東西:

(List [List [List [List [Elem 1, Elem 2, Elem 3], Elem 5, Elem 6], List [Elem 5, Elem 6]], List [Elem 5, Elem 6]]) 

期望的功能輸出:

(List [Elem 1, Elem 2, Elem 3]) 
+1

你能舉一些例子讓你的問題更清楚嗎? –

+4

也許它有助於改變觀點,並考慮樹木的高度和/或深度 – epsilonhalbe

+1

我已經添加了我的意思的例子... – Pajac

回答

2

我會使用二進制樹代替舉一個例子,這是非常類似於你的結構。您將有將其轉換爲適用於您的數據類型的練習。

說我有一個二叉樹

data Tree a 
    = Leaf a 
    | Node (Tree a) (Tree a) 
    deriving (Eq, Show) 

,我想找到具有最大深度值(可以有不止一個!)。我如何解決這個問題是遞歸地遍歷每個分支,隨着時間的推移記錄深度,然後返回底部的值以及它們的深度。

首先,我將定義我的功能結構

import Data.List (sortBy, groupBy) 
import Data.Ord (comparing) 
import Data.Function (on) 


getDeepest :: Tree a -> [a] 
getDeepest tree 
    = map fst      -- Strip the depth from the values 
    . head       -- Get just the ones with the largest depth 
    . groupBy ((==) `on` snd)  -- Group by the depth 
    . sortBy (flip (comparing snd)) -- Reverse sort by the depth (largest first) 
    $ go tree 0      -- Find all the "bottom" nodes 
    where 
     go :: Tree a -> Int -> [(a, Int)] 
     go (Leaf a) n = undefined 
     go (Node l r) n = undefined 

這是你在Haskell看到一個共同的遞歸格式。我有一個本地幫助函數,它攜帶一個額外的值,我想在特定的值初始化,在這種情況下深度爲0。我已經包含了我知道我想要的邏輯,以便以良好的格式獲得輸出。 flip (comparing snd)將進行反向排序,因此最大深度將排在第一位。然後,我們按深度分組,然後提取第一組,然後從值中去除深度。

現在我們只需要定義go的功能。我們知道,當我們見底,我們要的價值添加到我們與我們找到的深度累加器,所以

go (Leaf a) n = [(a, n)] 

那樣的話是很容易的,我們只是做從價值和深度的元組並將其作爲列表包裝。對於其他情況下,我們要向下遍歷每個分支,找到最深的元素,並從兩個分支

go (Node l r) n = go l (n + 1) ++ go r (n + 1) 

這就是遞歸發生返回最深。雖然這當然不是最有效的算法(Haskell列表對此並不好,但爲簡單起見,我們將使用它們),但它仍然非常簡單。我們所做的只是往下走,通過1來增加我們的深度。所以整個算法一起:

getDeepest :: Tree a -> [a] 
getDeepest tree 
    = map fst      -- Strip the depth from the values 
    . head       -- Get just the ones with the largest depth 
    . groupBy ((==) `on` snd)  -- Group by the depth 
    . sortBy (flip (comparing snd)) -- Reverse sort by the depth (largest first) 
    $ go tree 0      -- Find all the "bottom" nodes 
    where 
     go :: Tree a -> Int -> [(a, Int)] 
     go (Leaf a) n = [(a, n)] 
     go (Node l r) n = go l (n + 1) ++ go r (n + 1) 

因此,作爲一個例子:

myTree :: Tree Int 
myTree = 
    Node 
     (Node 
      (Leaf 1) 
      (Node 
       (Leaf 2) 
       (Leaf 3))) 
     (Leaf 4) 

然後可以通過應用getDeepest它返回[2, 3]被形象化爲

   Node 
      / \ 
      Node Leaf 4 
     / \ 
     Leaf 1 Node 
       / \ 
      Leaf 2 Leaf 3 

。我建議您從getDeepest中刪除類型簽名,並嘗試在go tree 0(從頂部開始)之前刪除各種函數,以便您可以在每個步驟中看到它的外觀,它應該可以幫助您更好地顯示算法。

+0

不是OP,但這是一個很好的解釋!不過,如果我們有兩個或更多的「葉級」都在同一級別,那麼就有一個問題。然後,這個函數不是將它們都返回,而是將它們放回到一起,並且無法辨別出來自哪裏。不知道如何自己解決這個問題,是否需要某種方式來對值構造函數進行模式匹配,並且在OP中,確保NestedList是包含僅由Elem組成的事物的List? – rafalio

+0

@radicality首先,感謝編輯,我打算把這個導入(我認爲我實際上在某些時候多次打開撤銷)。其次,OP要求提供「最多嵌套列表」,這意味着它按定義只能包含「Elem」值。這與我的算法類似,最底層只能是'Leaf'值。是的,儘管可以很容易地修改它,以便一次返回最深值和深度的元組(即'fmap head.unzip'而不是'map fst'),但是很容易丟失這些信息。 OP沒有要求,所以我沒有包括它。 – bheklilr

+0

@bheklir不知道我明白。假設我有'節點(節點(葉子1)(節點(葉子2)(葉子3)))(節點(葉子4)(節點(葉子5)(葉子6))'''。然後(假設我們正在解釋嵌套列表的情況),我希望答案可以是[2,3]或者[5,6]或者[[2,3], [5,6]],但是這個解決方案只是將它合併在一起並返回'[2,3,5,6]',所以在嵌套列表的情況下,我們會用這種方法得到一個錯誤的答案,不是嗎? – rafalio