[Leetcode学习-c++&java]树状数组 Create Sorted Array through Instructions

树状数组

leetcode每日一题遇上了一道线段树问题,没看过模板的我可以懵逼一早上了,不过只要看懂了树状数组的模板就可以很轻松解决这个问题,毕竟也确实只是个模板题,就看你有没有涉及过,如果自己硬生生憋出树状数组的结构也确实厉害。

树状数组学习:

https://oi-wiki.org/ds/fenwick/

1、结构性质

数组内,从索引 1 开始,每两个相邻索引值连接,然后索引值大的提到索引值小的上一层 ,假设是 C 层,然后再把在 C 层的索引值再两两相连,依然是索引值大的提到上一层,假设是 D 层,小的留在 C 层,然后再递归 两两相连 D 层 继续 创建 E F G H...... 这样的层。

很明显看到 1 3 5 7 9 这样奇数的索引值,他们都是叶子节点,因为 在 第一次 两两相连的时候,已经决定了 奇数 的 索引值 会被连接到 偶数的索引值后面。

而且 两两相连 的做法 也正是 2 ^ N 体现,只有 2 ^N 的索引值才会提升到 更高的 N 层。

2、查找

那这样搞了结构,都是有序的索引值的结构,那么数组内就可以假设存放每个节点的数量,查找方法,也就是查目标节点(索引值)以及小于该索引值下一共存放多少个节点数量,或者反过来找父节点,把子节点的节点数统计到父节点。

lowbit:

    public int lowbit(int i) {
        return i & -i;
    }

这块是在统计过程中,我计算完了当前节点 i (索引值 i),然后我跳到哪个节点继续统计呢?就是跳到 i + lowbit(i) 或 i - lowbit(i) 继续统计。

因为根据性质可以看到数组索引值是两两相连而成,奇数都是孤儿,2 ^ N 都是人上人,那么二进制 2 ^ N 就代表 1 000 000... (N 个 0),多少个 0 就多少层,下面往上找就得 i + lowbit(i),上面往下找就得 i - lowbit(i)。

而 i 是正整数, - i 是一个补数,如果 i 是奇数 i & - i = 1 如果 i 是偶数 i & -i = 2 ^ N 是定理来的。

往上更新父节点数据

    public void update(int i, int v) { // 索引 i 插入数量 v
        while(i <= N) {
            tree[i] += v;
            i += lowbit(i); // 往上找父节点,每个节点都更新数量 v
        }
    }

往下统计

    public int getSum(int i) { // 从索引 i 下面,统计所有 <= i 的索引存放数量
        int ans = 0;
        while(i > 0) {
            ans += tree[i];
            i -= lowbit(i); // 不断往 <= i 的索引找
        }
        return ans;
    }

 


问题:Create Sorted Array through Instructions

难度:hard

说明:

给出一个无序数组,然后将数组进行插入排序,要求求出,数组内每个元素进行插入排序时候,所有元素需要最小共多少的比较次数(称为消耗),插入排序可以队前插入或者队后插入,每个元素只需要计算队前或队后插入的最小消耗就行(所以题目就说,多少个比它大,多少个比它小的最小值)。

题目连接:https://leetcode.com/problems/create-sorted-array-through-instructions/submissions/

输入范围:

  • 1 <= instructions.length <= 105
  • 1 <= instructions[i] <= 105

输入案例:

Example 1:
Input: instructions = [1,5,6,2]
Output: 1
Explanation: Begin with nums = [].
Insert 1 with cost min(0, 0) = 0, now nums = [1].
Insert 5 with cost min(1, 0) = 0, now nums = [1,5].
Insert 6 with cost min(2, 0) = 0, now nums = [1,5,6].
Insert 2 with cost min(1, 2) = 1, now nums = [1,2,5,6].
The total cost is 0 + 0 + 0 + 1 = 1.

Example 2:
Input: instructions = [1,2,3,6,5,4]
Output: 3
Explanation: Begin with nums = [].
Insert 1 with cost min(0, 0) = 0, now nums = [1].
Insert 2 with cost min(1, 0) = 0, now nums = [1,2].
Insert 3 with cost min(2, 0) = 0, now nums = [1,2,3].
Insert 6 with cost min(3, 0) = 0, now nums = [1,2,3,6].
Insert 5 with cost min(3, 1) = 1, now nums = [1,2,3,5,6].
Insert 4 with cost min(3, 2) = 2, now nums = [1,2,3,4,5,6].
The total cost is 0 + 0 + 0 + 0 + 1 + 2 = 3.

Example 3:
Input: instructions = [1,3,3,3,2,4,2,1,2]
Output: 4
Explanation: Begin with nums = [].
Insert 1 with cost min(0, 0) = 0, now nums = [1].
Insert 3 with cost min(1, 0) = 0, now nums = [1,3].
Insert 3 with cost min(1, 0) = 0, now nums = [1,3,3].
Insert 3 with cost min(1, 0) = 0, now nums = [1,3,3,3].
Insert 2 with cost min(1, 3) = 1, now nums = [1,2,3,3,3].
Insert 4 with cost min(5, 0) = 0, now nums = [1,2,3,3,3,4].
​​​​​​​Insert 2 with cost min(1, 4) = 1, now nums = [1,2,2,3,3,3,4].
​​​​​​​Insert 1 with cost min(0, 6) = 0, now nums = [1,1,2,2,3,3,3,4].
​​​​​​​Insert 2 with cost min(2, 4) = 2, now nums = [1,1,2,2,2,3,3,3,4].
The total cost is 0 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 2 = 4.

我的代码:

开始用 BST 完全超时了。

没学过树状数组就是地狱难度,不过学了这个别人家的天才树,就一模板,拿元素值作为 key 对应 tree 的下标值。

java:

class Solution {
    private static int N = 100001;
    private static int[] tree = new int[N];

    public int lowbit(int i) {
        return i & -i;
    }

    public void update(int i, int v) {
        while(i <= N) {
            tree[i] += v;
            i += lowbit(i);
        }
    }

    public int getSum(int i) {
        int ans = 0;
        while(i > 0) {
            ans += tree[i];
            i -= lowbit(i);
        }
        return ans;
    }

    public int createSortedArray(int[] inst) {
        long res = 0;
        int len = inst.length;
        Arrays.fill(tree, 0);
        for(int i = 0;i < len;i ++) {
            res += Math.min(getSum(inst[i] - 1), i - getSum(inst[i]));
            update(inst[i], 1);
        }
        return (int)(res % ((int)Math.pow(10, 9) + 7));
    }
}

C++:

class Solution {
public:
    int N = 100001;
    int *tree = new int[N];
    int lowbit(int i) { 
        return i & -i; 
    }
    
    void update(int i, int v) { 
        while (i < N) {
            tree[i] += v;
            i += lowbit(i);
        }
    }
    
    int getSum(int i) { 
        int ans = 0;
        while (i) {
            ans += tree[i];
            i -= lowbit(i);
        }
        return ans;
    } 
    
    int createSortedArray(vector<int>& instr) {
        memset(tree, 0, N);
        long res = 0;
        int n = instr.size();
        for (int i = 0; i < n; ++i) {
            res += min(getSum(instr[i] - 1), i - getSum(instr[i]));
            update(instr[i], 1); 
        }
        return (int)(res % 1000000007);
    }
};

还有个超时的 BST 但是占用内存太大,而且比较统计次数过多。

class Solution {
    private static long res;
    private static int num, len, size;
    public int createSortedArray(int[] inst) {
        res = 0;
        num = 1;
        len = inst.length;
        size = len + 1;
        int[] val = new int[size], left = new int[size], right = new int[size]
                ,nleft = new int[size], nright = new int[size], n = new int[size];
        val[1] = inst[0];
        n[1] = 1;
        for(int i = 1;i < len;i ++) {
            int[] mamx = new int[2];
            insert(inst[i], 1, val, left, right, nleft, nright, n, mamx);
            res += Math.min(mamx[0], mamx[1]);
        }
        return (int)(res % ((int)Math.pow(10, 9) + 7));
    }

    public int insert(int v, int r, int[] val, int[] left, int[] right, int[] nleft, int[] nright, int[] n, int[] mamx) {
        if(val[r] == 0) {
            r = ++ num;
            n[r] ++;
            val[r] = v;
        } else if(val[r] == v) {
            n[r] ++;
            mamx[0] += nleft[r];
            mamx[1] += nright[r];
        } else if(val[r] < v) {
            right[r] = insert(v, right[r], val, left, right, nleft, nright, n, mamx);
            nright[r] ++;
            mamx[0] += n[r] + nleft[r];
        } else if(val[r] > v) {
            left[r] = insert(v, left[r], val, left, right, nleft, nright, n, mamx);
            nleft[r] ++;
            mamx[1] += n[r] + nright[r];
        }
        return r;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_28033719/article/details/112506925
今日推荐