P19 - 旋转列表 N 位

2026-09-07 00:00    #Haskell   #99题   #列表  

P19 - 旋转列表 N 位

Rotate a list N places to the left

官方模块: Problems.P19 核心函数: rotate


← P18 提取子列表 | P20 删除第 k 个元素 →


题目描述

将列表向左旋转 N 位。N 为正数时左移,N 为负数时右移(等价于左移负数位)。

函数签名

1rotate :: [a] -> Int -> [a]

思路

旋转操作可以理解为切分后交换

关键在于处理负数 N 和 N 超出列表长度的情况。先用 mod 把 N 归一化到 [0, length xs - 1] 范围。

实现

方法一:splitAt + 交换

1rotate :: [a] -> Int -> [a]
2rotate xs n = let (left, right) = splitAt (n `mod` length xs) xs
3              in right ++ left

n mod length xs 把任意整数归一化到合法范围。然后用 splitAt 切分,交换前后两部分。

注意:length xs 对空列表会报错,需要单独处理。

方法二:安全处理空列表

1rotate :: [a] -> Int -> [a]
2rotate [] _ = []
3rotate xs n = let k = n `mod` length xs
4                  (left, right) = splitAt k xs
5              in right ++ left

先处理空列表 case,避免 length [] 导致除以零。

方法三:纯递归

1rotate :: [a] -> Int -> [a]
2rotate xs 0 = xs
3rotate [] _ = []
4rotate (x:xs) n
5  | n > 0     = rotate (xs ++ [x]) (n - 1)
6  | otherwise = rotate (last xs : init xs) (n + 1)

每次左移一位:把头部移到尾部,递归 n-1 次。直观但效率低(O(n × m))。

理解 mod 的作用

Haskell 的 mod 对负数也返回非负结果:

13 `mod` 8   -- 3
2(-3) `mod` 8  -- 5

所以 n mod length xs 可以统一处理正负旋转:

1rotate "abcdefgh" 3     k = 3   "defghabc"    (左移3)
2rotate "abcdefgh" (-2)  k = 6   "ghabcdef"    (右移2 = 左移6)

测试

 1>>> rotate "abcdefgh" 3
 2"defghabc"
 3
 4>>> rotate "abcdefgh" (-2)
 5"ghabcdef"
 6
 7>>> rotate [1,2,3,4,5] 0
 8[1,2,3,4,5]
 9
10>>> rotate [1,2,3,4,5] 7    -- 7 mod 5 = 2
11[3,4,5,1,2]
12
13>>> rotate [] 3
14[]

参考


← P18 提取子列表 | P20 删除第 k 个元素 →