Sum tree array in reverse order

Posted by milsaw on Thu, 03 Feb 2022 01:54:28 +0100



one. Problem description

Find all the reverse order pairs of a given array. The reverse order pair means that a pair of elements in the array satisfy that the value of the left element is greater than that of the right element


two. Examples

Example 1:

Input: [9,1,0,5,4]
Output: 6


3. Analysis

Tree array

Tree array is a special tree stored in the form of array, and this tree contains many practical mathematical properties, which can be used to efficiently solve the specific interval sum of the original array and the interval update problem.

Visual appearance of tree array



Construction of tree array




Interval summation using tree array




Update the original array elements and update the tree array synchronously




Efficient solution of 2^k


Discretization

I've long heard that tree arrays can sum in reverse order, but I don't know how to implement it. As we know, tree array is used for interval query efficiently, so it can be used for interval sum. But now the problem we are facing is to find the inverse sum of arrays, not just the sum of intervals. Therefore, we must first find some laws of inverse sum, and flexibly use tree array to solve the problem according to these laws.

Before using the idea of tree array, we first discretize the original array. The so-called discretization is to start with the array subscript 1, assign the value of the array element to the value of the array subscript index, and the discretized array changes with the change of the original array. The purpose of discretization is to reduce memory consumption; If discretization is not used, then
For arrays: 9 1 18 5 4, using the idea of tree array needs to open up an array with a size of 18, in which a lot of space is not used, resulting in waste.

The discretized array can be solved in reverse order instead of the original array after the following special processing

For arrays:	               9	1	18	5	4
 Build index array:              1	2	3	4	5
 Then sort the array          1	4	5	9	18
 Index array change                2	5	4	1	3

ps: note that the sorting algorithm used to sort the original array must be stable, that is, the relative index position of the same element remains unchanged after sorting, otherwise the reverse sum will fail

So how to use the idea of tree array to solve it efficiently?

We get the discretized array of the original array: [25 4 1 3]

Next, create a new array: [0]

The new array is what we need to deal with in the form of tree array.

The process is as follows

In order (i), add the value to the position of the corresponding index (record it as index), set the value of the corresponding index to 1 (the tree array carries out the corresponding single point update) as update(index), and calculate the sum of the interval before the index (including the index) (the advantage of the tree array) as sum(index), Then observe the change summary of the new array and get: the reverse sum of each sequential processing = i - sum(index) (i is the total number of current numbers, and sum(index) is the number of values smaller than or equal to the value under index)
(it's a bit surprising, but it's really a rule summed up)

It can be intuitively felt by observing the changes of the new array

2 Processing: 2	5	4	1	3
             0  1	0	0	0
 At this time, the reverse order sum of 2 is processed = 1 - 1 = 0

Then 5 processing: 2	5	4	1	3
              0	1	0	0	1
 At this time, the reverse order and sum of 5 are processed = 2 - 2 =0

Then 4 process: 2	5	4	1	3
              0	1	0	1	1
 At this time, the reverse order and sum of 4 are processed = 3 - 2 = 1

Then 1 process: 2	5	4	1	3
              1	1	0	1	1
 At this time, the reverse order and sum of 1 are processed = 4 - 1 = 3

Then 3 process: 2	5	4	1	3
              1	1	1	1	1
 At this time, the reverse order sum of 3 is processed = 5 - 3 = 2

Finally, add up the inverse sum of each number to be the inverse sum of the original array, that is, the inverse sum of the original array is 6

summary

In retrospect, using the idea of tree array to sum in reverse order is to create a new array and maintain the tree array of the new array. The original array replaces the index of the new array with the value in order. The value of the corresponding index of the new array is + 1 to update the tree array. After each update, the interval sum of the tree number group is the number in front of (including) the number in the original array that is not greater than this number, Each sequence i is the total number of the current number, so the reverse sum of the number = i - sum(index), and the final result is the reverse sum of each number.

However, there is another problem. If you directly use the tree array maintained by the corresponding new array of the original array, throughout the whole process, the size of the new array is not less than the maximum value in the original array because it is updated in order by replacing the index of the new array with the value. For some arrays, such as [1 100000000 2 3], the size of the new array is not less than 100000000, The original array has only four numbers, which is undoubtedly a great waste of memory space. Therefore, it is necessary to use the idea of discretizing the original array to create another discretized array, and use the discretized array to replace the original array for the next processing.

Reference code

public class Nixuhe {
    public static void main(String[] args){
        Nixuhe nixuhe = new Nixuhe();
        int[] arr = {9,1,0,5,4};
        int[] dis_arr = nixuhe.discretize(arr);
        int result = nixuhe.pivot(dis_arr);
        System.out.println(result);
    }
    
    public int pivot(int[] dis_arr){
        int[] new_arr = new int[dis_arr.length];
        int[] tree_arr = treeBuid(new_arr);
        int result = 0;
        for(int i=1;i<=dis_arr.length;i++){
            treeAdd(tree_arr,dis_arr[i-1],1);
            int medium = i - tree_sum(tree_arr,dis_arr[i-1]);
            result += medium;
        }
        return result;
    }

    //The key formula to realize the connection between tree array and original array
    private int lowbit(int i){
        return i&(-i);
    }

    //Query the tree array to realize the summation requirements
    private int tree_sum(int[] tree_arr,int i) {
        int sum=0;
        while (i>0) {
            sum+=tree_arr[i-1];
            i-=lowbit(i);
        }
        return sum;
    }

    //Update tree array
    private void treeAdd(int[] tree_arr,int i, int val) {
        while (i<=tree_arr.length) {
            tree_arr[i-1] += val;
            i+=lowbit(i);
        }
    }

    //Building a tree array
    private int[] treeBuid(int[] new_arr){
        int[] tree_arr = new int[new_arr.length];
        for(int i=1;i<=tree_arr.length;i++){
            for(int j=(i-lowbit(i)+1);j<=i;j++) tree_arr[i-1] += new_arr[j-1];
        }
        return tree_arr;
    }

    //Discretization of original array
    public int[] discretize(int[] arr){
        int[] temp_arr = new int[arr.length];
        for(int i=1;i<=arr.length;i++){
            temp_arr[i-1] = i;
        }
        sort(arr,temp_arr,0,arr.length-1);
        return temp_arr;
    }

    //Merge sorting is realized, and the boundary conditions provided are left closed and right closed
    public void sort(int[] nums,int[] temp_arr,int start,int end){
        if(end-start < 1) return;
        int middle = (end+start)/2;
        sort(nums,temp_arr,start,middle);
        sort(nums,temp_arr,middle+1,end);
        merge(nums,temp_arr,start,middle+1,end+1);
    }

    //Merge the two regions in ascending order, and the array boundary condition provided is left closed and right open
    private void merge(int[] nums,int[] temp_arr,int start,int middle,int end){
        int[] result1 = new int[end-start];
        int[] result2 = new int[end-start];
        int i = start,j = middle,k=0;
        while(i<middle && j<end){
            if(nums[i] <= nums[j]) {
                result1[k] = nums[i];
                result2[k] = temp_arr[i];
                k++;
                i++;
            }else{
                result1[k] = nums[j];
                result2[k] = temp_arr[j];
                k++;
                j++;
            }
        }
        while(i < middle){
            result1[k] = nums[i];
            result2[k] = temp_arr[i];
            k++;
            i++;
        }
        while(j < end){
            result1[k] = nums[j];
            result2[k] = temp_arr[j];
            k++;
            j++;
        }
        for(int p=0;p<result1.length;p++){
            nums[p+start] = result1[p];
            temp_arr[p+start] = result2[p];
        }
    }
}

Topics: Java Algorithm data structure