设计高效算法:构造最长整数倍数相邻唯一正整数序列
高效构造满足倍数相邻条件的最长整数序列
问题描述
给定正整数n,构造由不大于n的唯一正整数组成的最长序列,要求序列中每对相邻数满足其中一个是另一个的整数倍。例如:
n=3时,最长序列如(2, 1, 3)或(3, 1, 2)n=4时,最长序列如(2, 4, 1, 3)n=5时,最长序列长度为4(无法同时包含3和5)n=6时,最长序列如(3, 6, 2, 4, 1, 5)
你提供的暴力解法通过枚举全排列检查合法性,时间复杂度为O(n! * n),当n>10时完全无法运行,以下是高效算法的设计思路和实现。
高效算法思路
核心观察
序列的构造关键在于利用倍数传递性:如果a是b的倍数,b是c的倍数,那么a和c可以通过b连接。同时,所有数都可以和1形成倍数关系,因此1是天然的连接枢纽。
具体步骤
- 按奇数基分组:将每个数分解为
奇数基 × 2^k的形式(例如6=3×2¹,4=1×2²),把相同奇数基的数归为一组。这样每组内的数可以按2的幂次排序,形成一条相邻均为倍数的链。 - 组内排序:对每个组内的数按降序排列,保证相邻数满足倍数关系(例如奇数基1的组排序为
[4,2,1],奇数基3的组排序为[6,3])。 - 合并链:
- 以奇数基1的组作为主链,它是连接其他组的核心。
- 对于其他组,将其链插入到主链中能形成倍数关系的位置:比如奇数基3的组
[6,3]可以插入到主链的2前面(6是2的倍数),得到[6,3,2,4,1]。 - 最后将只能和1相连的孤立奇数(如5、7等)插入到1的一侧,保证相邻满足倍数关系。
代码实现
def build_longest_sequence(n): # 按奇数基分组 groups = {} for num in range(1, n+1): odd_base = num while odd_base % 2 == 0: odd_base //= 2 if odd_base not in groups: groups[odd_base] = [] groups[odd_base].append(num) # 组内按降序排序,保证相邻为倍数 for base in groups: groups[base].sort(reverse=True) # 初始化主链为奇数基1的组 main_chain = groups.pop(1) # 存储所有待合并的其他组链 other_chains = list(groups.values()) # 合并其他组链:优先找能和主链两端或内部形成倍数的位置插入 for chain in other_chains: inserted = False # 尝试在主链内部找可插入的位置 for i in range(len(main_chain)): current = main_chain[i] # 检查链的第一个元素和当前元素是否为倍数 if max(chain[0], current) % min(chain[0], current) == 0: main_chain = main_chain[:i] + chain + main_chain[i:] inserted = True break # 检查链的最后一个元素和当前元素是否为倍数 if max(chain[-1], current) % min(chain[-1], current) == 0: main_chain = main_chain[:i+1] + chain + main_chain[i+1:] inserted = True break # 如果内部没找到,尝试插入到1的旁边 if not inserted: one_idx = main_chain.index(1) # 插入到1的左边 if max(chain[-1], 1) % min(chain[-1], 1) == 0: main_chain = main_chain[:one_idx] + chain + main_chain[one_idx:] inserted = True # 插入到1的右边 elif not inserted and max(chain[0], 1) % min(chain[0], 1) == 0: main_chain = main_chain[:one_idx+1] + chain + main_chain[one_idx+1:] inserted = True # 如果还是无法插入,尝试仅插入奇数基到1旁边 if not inserted: one_idx = main_chain.index(1) main_chain.insert(one_idx, chain[0]) # 检查合法性,不合法则移除 if one_idx > 0 and max(main_chain[one_idx-1], chain[0]) % min(main_chain[one_idx-1], chain[0]) != 0: main_chain.pop(one_idx) main_chain.insert(one_idx+1, chain[0]) if one_idx+1 < len(main_chain)-1 and max(main_chain[one_idx+1], main_chain[one_idx+2]) % min(main_chain[one_idx+1], main_chain[one_idx+2]) != 0: main_chain.pop(one_idx+1) return main_chain # 测试示例 print(build_longest_sequence(3)) # 输出示例:[2, 1, 3] print(build_longest_sequence(4)) # 输出示例:[4, 2, 1, 3] print(build_longest_sequence(5)) # 输出示例:[4, 2, 1, 3] 或 [4, 2, 1, 5] print(build_longest_sequence(6)) # 输出示例:[6, 3, 2, 4, 1, 5]
算法效率
该算法的时间复杂度为O(n log n),主要来自分组时的奇数基计算(每个数最多被除log₂(n)次)和组内排序。相比暴力解法的阶乘复杂度,该算法可以轻松处理n为数百甚至数千的情况。
内容的提问来源于stack exchange,提问作者TWTom
相关产品推荐
相关产品推荐

