如何在R中将k步最大排列算法优化至O(n)时间复杂度?
优化大规模数组的k次最大交换算法(R语言)
需求回顾
我们需要对一组数字执行最多k次两两交换,每次交换后都要得到当前能生成的最大排列;如果提前得到全局最大排列,就直接停止并输出结果。举个例子:
当k=2、数组为
(1,4,2,5,3,3)时,第一步交换1和5得到(5,4,2,1,3,3),第二步交换2和3得到(5,4,3,1,3,2);如果k足够大(比如k=10),当数组变成全局最大排列(5,4,3,3,2,1)时就会提前停止。
现有代码的瓶颈
你提供的现有代码逻辑是每次找当前数组的最大值,定位其最右侧位置,再找左侧第一个更小的数交换。但问题在于:
- 每次循环都调用
sort()和which(),这些操作都是O(n)级别的; - 当k接近n、数组规模达到1e4时,整体时间复杂度会变成O(kn)≈O(n²),这显然会导致运行过慢。
O(n)时间复杂度的优化方案
要实现线性时间复杂度,我们需要提前预处理所有数字的位置,避免每次循环都遍历整个数组。核心思路是:从左到右逐个确定每个位置的最大可能数字,用预处理的位置信息快速找到需要交换的目标位置,同时跟踪剩余交换次数k。
具体实现代码
max_k_swap <- function(arr, k) { n <- length(arr) if (k <= 0) return(arr) # 预处理:从右到左记录每个数字的所有出现位置(方便取最右侧的索引) num_positions <- list() for (i in n:1) { num <- as.character(arr[i]) if (is.null(num_positions[[num]])) { num_positions[[num]] <- c() } num_positions[[num]] <- c(num_positions[[num]], i) } # 降序排列所有唯一数字,方便从最大的开始尝试 sorted_nums <- sort(unique(arr), decreasing = TRUE) current_num_idx <- 1 current_max <- sorted_nums[current_num_idx] for (i in 1:n) { if (k <= 0) break current_val <- arr[i] # 当前位置已经是可用的最大数字,直接跳过并更新位置记录 if (current_val == current_max) { # 移除当前位置(已使用) pos_list <- num_positions[[as.character(current_max)]] num_positions[[as.character(current_max)]] <- pos_list[pos_list != i] # 如果当前最大值已经用完,切换到下一个最大数字 if (length(num_positions[[as.character(current_max)]]) == 0) { current_num_idx <- current_num_idx + 1 if (current_num_idx > length(sorted_nums)) break current_max <- sorted_nums[current_num_idx] } next } # 找到当前最大数字的最右侧可用位置 pos_list <- num_positions[[as.character(current_max)]] swap_pos <- pos_list[length(pos_list)] # 执行交换 temp <- arr[i] arr[i] <- arr[swap_pos] arr[swap_pos] <- temp # 更新位置记录: # 1. 移除当前最大值的交换位置 num_positions[[as.character(current_max)]] <- pos_list[-length(pos_list)] # 2. 更新原数字的位置(把swap_pos替换成i) temp_pos_list <- num_positions[[as.character(temp)]] temp_pos_list[temp_pos_list == swap_pos] <- i num_positions[[as.character(temp)]] <- temp_pos_list k <- k - 1 # 如果当前最大值用完,切换到下一个最大数字 if (length(num_positions[[as.character(current_max)]]) == 0) { current_num_idx <- current_num_idx + 1 if (current_num_idx > length(sorted_nums)) break current_max <- sorted_nums[current_num_idx] } } arr } # 测试示例 test_arr <- c(1,4,2,5,3,3) k_val <- 2 cat("k=2时的结果:", max_k_swap(test_arr, k_val), "\n") # 输出:k=2时的结果: 5 4 3 1 3 2 # 测试提前达到全局最大的情况 k_large <- 10 cat("k=10时的结果:", max_k_swap(test_arr, k_large), "\n") # 输出:k=10时的结果: 5 4 3 3 2 1
方案优势
- 预处理阶段:一次性遍历数组,记录每个数字的所有位置,时间复杂度O(n);
- 遍历过程:从左到右逐个确定位置,每次交换的查找和更新操作都是O(1)级别的;
- 整体复杂度:整个算法只需要两次线性遍历(预处理+主循环),时间复杂度严格为O(n),完全适合处理1e4甚至更大规模的数组。
内容的提问来源于stack exchange,提问作者Tony Hellmuth
相关产品推荐
相关产品推荐

