我有一个用Python和Haskell编写的简单脚本.它读取一个包含1,000,000个换行符分隔整数的文件,将该文件解析为整数列表,对其进行快速排序,然后将其写入已排序的其他文件.此文件的格式与未排序的文件相同.简单.
这是Haskell:
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
main = do
file <- readFile "data"
let un = lines file
let f = map (\x -> read x::Int ) un
let done = quicksort f
writeFile "sorted" (unlines (map show done))
Run Code Online (Sandbox Code Playgroud)
这是Python:
def qs(ar):
if len(ar) == 0:
return ar
p …Run Code Online (Sandbox Code Playgroud) 可能重复:
是否存在无法使用尾递归写入的问题?
根据我的理解,尾递归是一种优化,当递归调用不需要来自它将发送垃圾邮件的递归调用的信息时,您可以使用它.
那么可以使用尾递归实现所有递归函数吗?像DFS这样的东西,你需要最内层的孩子在父母可以之前返回?
我知道quicksort的O(n log n)平均时间复杂度.伪快速排序(当你从足够远的地方看它,具有适当高的抽象级别时只是一个快速排序),通常用于演示函数语言的简洁性如下(在Haskell中给出):
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (p:xs) = quicksort [y | y<-xs, y<p] ++ [p] ++ quicksort [y | y<-xs, y>=p]
Run Code Online (Sandbox Code Playgroud)
好的,所以我知道这件事有问题.最大的问题是它没有排序,这通常是quicksort的一大优势.即使这没关系,它仍然需要比典型的快速排序更长的时间,因为它在分区时必须进行两次列表传递,然后它会进行昂贵的附加操作以将其拼接回来.此外,选择第一个元素作为枢轴不是最佳选择.
但即便考虑所有这些,这个快速排序的平均时间复杂度与标准快速排序不同吗?即,O(n log n)?因为追加和分区仍然具有线性时间复杂度,即使它们效率低下.
我正在考虑将二进制树展平为列表,以便进行后续处理.
我首先考虑使用(++)加入左右分支,但后来想到了更糟糕的情况需要O(n^2)时间.
然后我考虑向后构建列表,使用(:)在线性时间附加到前面.然而,我想如果我将这个列表发送到类似折叠的函数,它必须等到整个树遍历才能开始折叠,因此不能使用列表融合.
然后我想出了以下内容:
data Tree a = Node a (Tree a) (Tree a) | Tip
flatten :: Tree a -> [a]
flatten x = (flatten' x) []
flatten' :: Tree a -> [a] -> [a]
flatten' (Node x left right) l = (flatten' left (x:(flatten' right l)))
flatten' Tip l = l
main =
putStrLn $ show $ flatten $
(Node 2 (Node 1 Tip Tip) (Node 4 (Node …Run Code Online (Sandbox Code Playgroud) 我开始学习 Haskell,我一直在阅读Haskell wiki 上的这个页面,它报告了这个 qsort 实现:
qsort :: (Ord a) => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort less ++ [x] ++ qsort more
where less = filter (<x) xs
more = filter (>=x) xs
Run Code Online (Sandbox Code Playgroud)
接着是警告说这不是最有效的方法,并链接了一篇文章,该文章显示了相同算法的极其冗长的版本。只是看着它,我认为那不是我学习 Haskell 的目的,我想在不牺牲其优雅的情况下制作初始 qsort 的更好版本。我主要集中在消除filter每次调用运行两次的需要,这就是我提出的:
filter' :: (a -> Bool) -> [a] -> ([a], [a])
filter' _ [] = ([], [])
filter' f a = filterInternal a ([], [])
where
filterInternal …Run Code Online (Sandbox Code Playgroud) 我想通过一个数据透视值将[a]分成([a],[a]),我有我的代码
splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot list =
([x | x <- list, x <= pivot], [x | x <- list, x > pivot])
Run Code Online (Sandbox Code Playgroud)
但它迭代列表两次以生成两个列表,有没有办法只迭代一次?
当我有一个功能
func :: Int -> Int -> [[a]]
func a b = func' a ++ func' b
Run Code Online (Sandbox Code Playgroud)
哪里
func' :: Int -> [[a]],
Run Code Online (Sandbox Code Playgroud)
什么是避免(++)的好机会?