You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求使数组非降序的lexicographical swap次数:高效算法探索

字典序交换的高效计数算法问题

定义

字典序交换(lexicographical swap):每一步选择最早的逆序对(i,j)——即先找最左侧的i(存在j>i使得a[i]>a[j]),再在i之后找索引最小的j满足a[i]>a[j]——进行交换,重复操作直到数组有序。

示例

数组[3,2,1]的交换过程:

  1. 交换索引(0,1) → [2,3,1]
  2. 交换索引(0,2) → [1,3,2]
  3. 交换索引(1,2) → [1,2,3]
    共需3次交换。

当前暴力模拟解法的最坏时间复杂度为O(n²),初始数组可为任意顺序。

技术问题

是否存在时间复杂度优于O(n²)的高效算法,无需显式模拟每一步交换即可计算总交换次数?

已有尝试

该问题和O(n log n)时间的逆序数统计类似,但字典序约束改变了交换顺序;曾尝试适配Fenwick树(BIT)或线段树,但未能正确建模该约束。

暴力模拟代码实现

def lexicographical_swaps(arr):
    n = len(arr)
    swaps = 0

    while not is_sorted(arr):
        # 寻找最早的逆序对
        for i in range(n-1):
            found = False
            for j in range(i+1, n):
                if arr[i] > arr[j]:
                    # 找到最早的(i,j)逆序对,执行交换
                    arr[i], arr[j] = arr[j], arr[i]
                    swaps += 1
                    found = True
                    break
            if found:
                break

    return swaps

高效解法思路

实际上,这种字典序交换过程的总交换次数等于数组的逆序数,因此可以直接用O(n log n)时间的逆序数统计算法来计算,无需模拟每一步交换。

关键推导

该交换过程中,每次选择的是最左侧的i,以及i之后第一个比a[i]小的j(索引最小的j满足a[i]>a[j])。由于j是i之后第一个小于a[i]的元素,因此对于所有i<k<j,都有a[k]≥a[i]>a[j]。交换i和j后:

  • 原逆序对(i,j)被消除
  • 对于i<k<j,原逆序对(k,j)(共j-i-1个)会被替换为新的逆序对(i,k)(同样j-i-1个),这部分逆序对数量不变
  • 其他逆序对不受影响

因此每一次交换都会让数组的逆序数恰好减少1。当数组有序时逆序数为0,总交换次数等于初始数组的逆序数。

O(n log n)实现方案

可以使用**Fenwick树(树状数组)**来统计逆序数,步骤如下:

  1. 对数组进行离散化处理(处理重复元素和大数情况)
  2. 从数组末尾向前遍历,对于每个元素x:
    • 查询Fenwick树中已插入的、小于x的元素数量,累加到逆序数中
    • 将x插入Fenwick树中
  3. 最终累加的结果即为总交换次数

代码示例(Python)

class FenwickTree:
    def __init__(self, size):
        self.n = size
        self.tree = [0]*(self.n + 1)
    
    def update(self, idx, delta=1):
        while idx <= self.n:
            self.tree[idx] += delta
            idx += idx & -idx
    
    def query(self, idx):
        res = 0
        while idx > 0:
            res += self.tree[idx]
            idx -= idx & -idx
        return res

def count_lex_swaps(arr):
    # 离散化处理
    sorted_unique = sorted(set(arr))
    rank = {v:i+1 for i, v in enumerate(sorted_unique)}  # 树状数组索引从1开始
    max_rank = len(sorted_unique)
    
    ft = FenwickTree(max_rank)
    inversions = 0
    
    for num in reversed(arr):
        r = rank[num]
        # 查询当前已插入的、比num小的元素数量
        inversions += ft.query(r - 1)
        ft.update(r)
    
    return inversions

# 测试用例
print(count_lex_swaps([3,2,1]))  # 输出3,与示例一致
print(count_lex_swaps([3,4,2,1]))  # 输出5,符合推导结果

内容的提问来源于stack exchange,提问作者ISG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 11:29:51