当前位置:首页 > 力扣 > 力扣1649题解:利用树状数组与离散化创建有序数组

力扣1649题解:利用树状数组与离散化创建有序数组

6个月前 (09-11)

力扣1649题解:利用树状数组与离散化创建有序数组 力扣题解 树状数组 离散化 C++ 第1张

一、题目解读

力扣1649题要求计算将无序数组转化为有序数组所需的最小代价,每次插入元素的代价为两侧比它大或小的元素数量中的较小值。题目核心在于高效统计元素插入前后的相对位置关系,需要设计一种支持动态更新与区间查询的数据结构

二、解题思路

采用离散化 + 树状数组(Fenwick Tree)的解决方案。首先通过离散化将原数组映射到索引范围[1, n],消除数值差异带来的计算复杂度。随后利用状数组维护有序序列的统计信息:通过查询前缀和快速获取小于/等于目标值的元素数量,进而推导出插入代价。该思路巧妙将数值比较转化为索引操作,大幅降低时间复杂度。

三、解题步骤

1. 离散化处理:对原数组排序并去重,生成有序列表sorted,每个元素num通过lower_bound映射为排名rank(即离散化索引)。

2. 初始化树状数组:基于sorted长度构建FenwickTree,用于维护累计计数。

3. 循环处理每个元素:

○ 查询小于当前值的元素数量less = ft.query(rank-1),小于等于的数量lessEqual = ft.query(rank)。

○ 计算当前元素的出现次数same = lessEqual - less,大于当前值的数量greater = 总数 - lessEqual。

○ 代价取两侧较小值:min(less, greater),累加总代价并取模防溢出。

○ 更新树状数组:ft.update(rank, 1)记录当前元素插入。

4. 返回总代价:最终累加值即为最小插入代价。

四、代码与注释

class FenwickTree {
private:
    vector<int> tree;
public:
    FenwickTree(int size) : tree(size + 1) {} // 构造树状数组(多1位防止索引越界)
    
    void update(int index, int delta) {
        while (index < tree.size()) { // 从index开始向上更新区间和
            tree[index] += delta;
            index += index & -index; // 二进制最低位+1跳跃
        }
    }
    
    int query(int index) {
        int sum = 0;
        while (index > 0) { // 从index开始向下累加前缀和
            sum += tree[index];
            index -= index & -index; // 二进制最低位清零跳跃
        }
        return sum;
    }
};

class Solution {
public:
    int createSortedArray(vector<int>& instructions) {
        const int MOD = 1e9 + 7; // 防止整数溢出
        // 离散化:排序去重生成有序映射列表
        vector<int> sorted = instructions;
        sort(sorted.begin(), sorted.end());
        sorted.erase(unique(sorted.begin(), sorted.end()), sorted.end());
        
        FenwickTree ft(sorted.size());
        int totalCost = 0;
        
        for (int num : instructions) {
            // 获取离散化后的排名(索引+1,因树状数组从1开始)
            int rank = lower_bound(sorted.begin(), sorted.end(), num) - sorted.begin() + 1;
            // 统计小于当前值的元素数
            int less = ft.query(rank - 1);
            // 统计小于等于当前值的元素数
            int lessEqual = ft.query(rank);
            // 当前元素出现次数
            int same = lessEqual - less;
            // 大于当前值的元素数
            int greater = ft.query(sorted.size()) - lessEqual;
            
            // 代价取两侧较小值,累加并取模
            int cost = min(less, greater);
            totalCost = (totalCost + cost) % MOD;
            
            // 更新树状数组计数
            ft.update(rank, 1);
        }
        
        return totalCost;
    }
};

五、总结

该解法巧妙结合离散化与树状数组,将数值操作转化为索引统计,规避了暴力排序或复杂比较。核心优化点:

1. 离散化将任意数值映射到固定范围,降低空间与时间复杂度。

2. 树状数组支持O(logn)的区间更新与查询,完美契合动态统计需求。

3. 利用前缀和推导元素两侧数量关系,避免重复计算。

此思路为处理动态有序序列统计问题提供了高效模板,适用于类似区间查询与更新的场景。


原创内容 转载请注明出处

分享给朋友:

相关文章

IOI 1994 洛谷1216:如何用O(1)空间解决数字三角形问题?附代码实现

IOI 1994 洛谷1216:如何用O(1)空间解决数字三角形问题?附代码实现

题目重解:数字三角形是一个经典的动态规划问题,给定一个由数字组成的三角形结构,从顶部出发,每次可以移动到下方相邻的数字,最终到达底部。我们需要找到一条路径,使得路径上经过的数字总和最大。这个问题可以很...

力扣3112题解法:带时间限制的最短路径问题解析(C++代码)

力扣3112题解法:带时间限制的最短路径问题解析(C++代码)

一、题目解读力扣3112题要求解决带时间限制的最短路径问题:给定一个有向图,节点具有消失时间,需计算从起点到各节点的最短路径,且路径总时间不能超过节点的消失时间。题目难点在于需在传统最短路径算法(如D...

【洛谷1184题解析】用C++高效解决地点匹配问题(附代码与解题思路)

【洛谷1184题解析】用C++高效解决地点匹配问题(附代码与解题思路)

一、题目解读洛谷1184题要求处理一组地点列表与行程记录,统计其中匹配的天数。题目难点在于高效处理带有空格的字符串输入,以及快速判断每日行程是否在高手可去地点集合中。需要兼顾输入格式解析与算法效率。二...

牛客NC67题解:汉诺塔递归算法与解题步骤

牛客NC67题解:汉诺塔递归算法与解题步骤

一、题目解读牛客NC67题要求解决汉诺塔问题,这是一个经典的递归算法题目。题目给定整数n,代表汉诺塔中的盘子数量,需要输出将n个盘子从起始柱移动到目标柱的所有步骤。汉诺塔问题规则为:每次只能移动一个盘...

【力扣2846题】图论+二进制提升:高效解决连通性问题(附C++代码)

【力扣2846题】图论+二进制提升:高效解决连通性问题(附C++代码)

一、题目解读力扣2846题要求解决一个基于图连通性的操作优化问题。给定一个无向图,包含边权重,以及一系列查询,每个查询询问两点间路径的最小操作次数。题目关键在于高效计算路径上权重分布的统计信息,并转化...

洛谷1656题解:基于Tarjan算法求解割边问题(附代码与详细步骤)

洛谷1656题解:基于Tarjan算法求解割边问题(附代码与详细步骤)

一、题目解读洛谷1656题要求在无向图中找出所有割边(即删除后导致图不连通的边)。题目核心在于判断图的连通性,并识别哪些边是“桥”。需理解图论中的连通分量概念,以及如何通过算法高效定位割边。二、解题思...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。