一则关于某大厂校招生高薪的讨论引发了广泛关注,其工作内容常被描述为“洗数据”。抛开薪酬话题,从技术视角看,数据清洗(Data Cleaning)确实是数据处理流程中至关重要且繁琐的一环,旨在提升数据质量以服务于后续的分析与建模。
与此同时,一道经典的算法练习题——“换座位”——常被用于考察基础的数组操作与逻辑思维能力。这恰好可以类比为对一组无序或杂乱的数据进行规则化的重组与交换。
算法题:换座位问题描述
我们可以将问题场景抽象如下:公司聚餐时,大家按顺序坐在一张长桌旁。为了活跃气氛,主持人要求每两位相邻的同事互换座位。如果总人数为奇数,则最后一位同事保持不动。
用算法语言描述即为:
- 给定一个数组,代表从左到右的顺序。
- 需要两两交换相邻的元素:第1个与第2个交换,第3个与第4个交换,依此类推。
- 若数组长度为奇数,则最后一个元素位置不变。
示例:
- 输入:
["A", "B", "C", "D", "E"]
- 输出:
["B", "A", "D", "C", "E"]
- 输入:
[1, 2, 3, 4, 5]
- 输出:
[2, 1, 4, 3, 5]
思路分析与Python实现
最直观的解法是遍历数组,每次步进两个位置,并交换当前元素与其下一个元素。
方法一:原地交换
这种方法直接修改原数组,空间效率高。
def swap_seats_in_place(arr):
"""
两两交换相邻元素,原地修改数组。
例如:[1, 2, 3, 4, 5] -> [2, 1, 4, 3, 5]
"""
n = len(arr)
# 步长为2遍历,确保 i+1 不越界
for i in range(0, n - 1, 2):
arr[i], arr[i + 1] = arr[i + 1], arr[i]
return arr
if __name__ == "__main__":
seats = [1, 2, 3, 4, 5]
print(swap_seats_in_place(seats)) # 输出: [2, 1, 4, 3, 5]
代码解析:
range(0, n - 1, 2) 确保了循环索引i的取值序列为0, 2, 4...,并且i+1始终有效。
arr[i], arr[i + 1] = arr[i + 1], arr[i] 是Python中优雅的元组解包特性,用于原地交换两个变量的值,无需临时变量。
方法二:数学映射法
如果不方便修改原数组,或者需要计算每个位置的新索引,可以使用基于数学规则的映射方法。
def new_position(i, n):
"""
给定原座位号 i(从1开始计数),总人数 n,返回交换后的新座位号。
"""
# 如果是最后一个且总人数为奇数,则位置不变
if i == n and n % 2 == 1:
return i
# 原座位为奇数,则移动到 i+1
if i % 2 == 1:
return i + 1
# 原座位为偶数,则移动到 i-1
return i - 1
if __name__ == "__main__":
n = 5
# 生成从原座位号到新座位号的映射
mapping = {i: new_position(i, n) for i in range(1, n + 1)}
print(mapping) # 输出: {1: 2, 2: 1, 3: 4, 4: 3, 5: 5}
复杂度与边界情况分析
- 时间复杂度:两种方法都只需线性遍历一次数组或序列,时间复杂度为 O(n)。
- 空间复杂度:原地交换法为 O(1);映射法如果需要存储新数组则为 O(n),若仅计算映射关系则可更低。
- 边界情况:
- 空数组
[]:应原样返回。
- 单元素数组
[1]:无需交换,原样返回。
- 双元素数组
[1, 2]:直接交换为 [2, 1]。
掌握这类基础的算法与数组操作,是构建更复杂数据处理逻辑的基石。无论是简单的数据重排,还是复杂的数据清洗流程,其核心都在于对数据结构的精确理解和操作。