【发布时间】:2010-07-23 06:56:45
【问题描述】:
您将如何对单个链表进行排序。 (这里的问题是单个属性+使用LinkedList进行排序比数组更难) 我想看一个伪代码..
尽量让它在时间和空间上都高效。
谢谢!
【问题讨论】:
-
家庭作业?如果是,请说明您到目前为止已经走了多远。
-
Sorting a linked list 的可能重复项
您将如何对单个链表进行排序。 (这里的问题是单个属性+使用LinkedList进行排序比数组更难) 我想看一个伪代码..
尽量让它在时间和空间上都高效。
谢谢!
【问题讨论】:
合并排序只涉及几个简单的步骤:
检查列表是否为空或只有一个元素。如果是,则原样返回列表。
将列表分成两半。合并两个部分。
通过重复从两个列表中取出较小的元素来合并列表。由于零件列表已经排序,因此只需查看两个列表中的第一个元素并选择较小的元素即可。
返回合并列表。
结果你会得到一个按最小元素排序的链表。
Haskell 中的代码示例:
import Data.List(splitAt)
--Mergesorts a list by smallest element first.
sort :: Ord a => [a] -> [a]
sort = sortBy (<)
--Generic sort that can sort in any order.
sortBy :: (a -> a -> Bool) -> [a] -> [a]
sortBy _ [] = []
sortBy _ [x] = [x]
sortBy f xs = merge f (sortBy f first) (sortBy f second)
where
(first,second) = split xs
--Splits a list in half.
split :: [a] -> ([a],[a])
split xs = splitAt (halfLength xs) xs
--Returns a lists length divided by two as a whole number (rounded).
halfLength :: [a] -> Int
halfLength = round . (/2) . fromIntegral . length
--Merges two lists in the provided order.
merge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
merge _ [] [] = []
merge _ [] (y:ys) = y:ys
merge _ (x:xs) [] = x:xs
merge f (x:xs) (y:ys) =
if f x y
then x : merge f xs (y:ys)
else y : merge f (x:xs) ys
【讨论】:
我一直在考虑这个问题,我认为您可以通过合并排序实现 O(n log(n)) 时间和 O(1) 空间条件。
让我们看看...
拿清单:
3 -> 2 -> 1 -> 5 -> 6 -> 4
第一关:
为第 1 项、第 2 项和第 3 项设置指针
将第一个和第二个指针之间的较小项设置为指向较大的项。
将 2 个术语中的最后一个设置为指向原始列表的其余部分。
重复直到列表的末尾。
2 -> 3 -> 1 -> 5 -> 4 -> 6
此时,每对术语都是有序的。
第N遍:
设置指向第 (2^(N-1))th 和 (2^(N))+1 项的指针
取值较小的节点并增加其指针。
重复该过程,直到两个列表(长度为 2^N)都用完,每次将较小值的节点附加到前一个较小值的节点。
将指针设置为原始列表的其余部分。
重复直到列表的末尾。
第 0 遍:3 -> 2 -> 1 -> 5 -> 6 -> 4(1 个术语的每个块都是有序的)(微不足道的)
第一遍:2 -> 3 -> 1 -> 5 -> 4 -> 6(每个包含 2 个术语的块都是有序的)
第二遍:1 -> 2 -> 3 -> 5 -> 4 -> 6(每块 4 个词都是有序的)
第三遍:1 -> 2 -> 3 -> 4 -> 5 -> 6(每个 8 个词的块都是有序的)
时间:log(n) 次通过,每次通过 n 次比较。
O(n log(n))
空格:指针和整数变量
O(1)
【讨论】:
由于链表只是由其他项目指向的一些项目,您可以构造一个具有 O(n) 时间和 O(n) 空间的指针数组,使用任何优秀的排序算法对它进行排序O(n log n) 时间和 O(1) 空间,然后用 O(n) 时间和 O(1) 空间从头开始重构链表。
总的来说,这是 O(n log n) 时间和 O(n) 空间。
【讨论】:
我相信您可以使用就地快速排序来做到这一点。
我错了。这不适用于单链表,因为 in 需要能够在列表中后退。
所以真正的问题变成了如何进行就地快速排序。
我们开始...
1) 获取链表的第一项、第二项和最后一项的指针。
2) 将第二个指针逐个遍历列表,直到找到大于第一个项的项。
3) 将第三个指针在列表中向后移动,直到找到一个小于第一个项的项。 此步骤不适用于单链表。
4) 交换第二个和第三个指针的值。
5) 重复步骤 2) 到 5),直到第二个和第三个指针相等。
6) 在第二个指针之后插入第一项
-此时链表分为:
[小于 x 的项] x [大于 x 的项]
7) 对 [terms less than x] 和 [terms greater than x] 重复步骤 2) 到 7),直到 [terms ________ than x] 块的大小为 1。
空格:每层 3 个指针。
O(log(n))
时间:与快速排序相同
一般 O(n log(n))
O(n * n) 在最坏的情况下(如果列表已经排序或倒序)
为格式化和愚蠢而编辑
【讨论】:
插入排序和快速排序都可以在单链表上完成,其效率与在数组上相同。
【讨论】: