Counting inversions in an array

前端 未结 30 1827
死守一世寂寞
死守一世寂寞 2020-11-22 04:14

I\'m designing an algorithm to do the following: Given array A[1... n], for every i < j, find all inversion pairs such that A[i] > A[j]

相关标签:
30条回答
  • 2020-11-22 04:47

    Check this out: http://www.cs.jhu.edu/~xfliu/600.363_F03/hw_solution/solution1.pdf

    I hope that it will give you the right answer.

    • 2-3 Inversion part (d)
    • It's running time is O(nlogn)
    0 讨论(0)
  • 2020-11-22 04:47

    The easy O(n^2) answer is to use nested for-loops and increment a counter for every inversion

    int counter = 0;
    
    for(int i = 0; i < n - 1; i++)
    {
        for(int j = i+1; j < n; j++)
        {
            if( A[i] > A[j] )
            {
                counter++;
            }
        }
    }
    
    return counter;
    

    Now I suppose you want a more efficient solution, I'll think about it.

    0 讨论(0)
  • 2020-11-22 04:47

    Here's my O(n log n) solution in Ruby:

    def solution(t)
        sorted, inversion_count = sort_inversion_count(t)
        return inversion_count
    end
    
    def sort_inversion_count(t)
        midpoint = t.length / 2
        left_half = t[0...midpoint]
        right_half = t[midpoint..t.length]
    
        if midpoint == 0
            return t, 0
        end
    
        sorted_left_half, left_half_inversion_count = sort_inversion_count(left_half)
        sorted_right_half, right_half_inversion_count = sort_inversion_count(right_half)
    
        sorted = []
        inversion_count = 0
        while sorted_left_half.length > 0 or sorted_right_half.length > 0
            if sorted_left_half.empty?
                sorted.push sorted_right_half.shift
            elsif sorted_right_half.empty?
                sorted.push sorted_left_half.shift
            else
                if sorted_left_half[0] > sorted_right_half[0]
                    inversion_count += sorted_left_half.length
                    sorted.push sorted_right_half.shift
                else
                    sorted.push sorted_left_half.shift
                end
            end
        end
    
        return sorted, inversion_count + left_half_inversion_count + right_half_inversion_count
    end
    

    And some test cases:

    require "minitest/autorun"
    
    class TestCodility < Minitest::Test
        def test_given_example
            a = [-1, 6, 3, 4, 7, 4]
            assert_equal solution(a), 4
        end
    
        def test_empty
            a = []
            assert_equal solution(a), 0
        end
    
        def test_singleton
            a = [0]
            assert_equal solution(a), 0
        end
    
        def test_none
            a = [1,2,3,4,5,6,7]
            assert_equal solution(a), 0
        end
    
        def test_all
            a = [5,4,3,2,1]
            assert_equal solution(a), 10
        end
    
        def test_clones
            a = [4,4,4,4,4,4]
            assert_equal solution(a), 0
        end
    end
    
    0 讨论(0)
  • 2020-11-22 04:49

    Here is a C code for count inversions

    #include <stdio.h>
    #include <stdlib.h>
    
    int  _mergeSort(int arr[], int temp[], int left, int right);
    int merge(int arr[], int temp[], int left, int mid, int right);
    
    /* This function sorts the input array and returns the
       number of inversions in the array */
    int mergeSort(int arr[], int array_size)
    {
        int *temp = (int *)malloc(sizeof(int)*array_size);
        return _mergeSort(arr, temp, 0, array_size - 1);
    }
    
    /* An auxiliary recursive function that sorts the input array and
      returns the number of inversions in the array. */
    int _mergeSort(int arr[], int temp[], int left, int right)
    {
      int mid, inv_count = 0;
      if (right > left)
      {
        /* Divide the array into two parts and call _mergeSortAndCountInv()
           for each of the parts */
        mid = (right + left)/2;
    
        /* Inversion count will be sum of inversions in left-part, right-part
          and number of inversions in merging */
        inv_count  = _mergeSort(arr, temp, left, mid);
        inv_count += _mergeSort(arr, temp, mid+1, right);
    
        /*Merge the two parts*/
        inv_count += merge(arr, temp, left, mid+1, right);
      }
      return inv_count;
    }
    
    /* This funt merges two sorted arrays and returns inversion count in
       the arrays.*/
    int merge(int arr[], int temp[], int left, int mid, int right)
    {
      int i, j, k;
      int inv_count = 0;
    
      i = left; /* i is index for left subarray*/
      j = mid;  /* i is index for right subarray*/
      k = left; /* i is index for resultant merged subarray*/
      while ((i <= mid - 1) && (j <= right))
      {
        if (arr[i] <= arr[j])
        {
          temp[k++] = arr[i++];
        }
        else
        {
          temp[k++] = arr[j++];
    
         /*this is tricky -- see above explanation/diagram for merge()*/
          inv_count = inv_count + (mid - i);
        }
      }
    
      /* Copy the remaining elements of left subarray
       (if there are any) to temp*/
      while (i <= mid - 1)
        temp[k++] = arr[i++];
    
      /* Copy the remaining elements of right subarray
       (if there are any) to temp*/
      while (j <= right)
        temp[k++] = arr[j++];
    
      /*Copy back the merged elements to original array*/
      for (i=left; i <= right; i++)
        arr[i] = temp[i];
    
      return inv_count;
    }
    
    /* Driver progra to test above functions */
    int main(int argv, char** args)
    {
      int arr[] = {1, 20, 6, 4, 5};
      printf(" Number of inversions are %d \n", mergeSort(arr, 5));
      getchar();
      return 0;
    }
    

    An explanation was given in detail here: http://www.geeksforgeeks.org/counting-inversions/

    0 讨论(0)
  • 2020-11-22 04:49

    C++ Θ(n lg n) Solution with the printing of pair which constitute in inversion count.

    int merge(vector<int>&nums , int low , int mid , int high){
        int size1 = mid - low +1;
        int size2= high - mid;
        vector<int>left;
        vector<int>right;
        for(int i = 0  ; i < size1 ; ++i){
            left.push_back(nums[low+i]);
        }
        for(int i = 0 ; i <size2 ; ++i){
            right.push_back(nums[mid+i+1]);
        }
        left.push_back(INT_MAX);
        right.push_back(INT_MAX);
        int i = 0 ;
        int j = 0;
        int start  = low;
        int inversion = 0 ;
        while(i < size1 && j < size2){
            if(left[i]<right[j]){
                nums[start] = left[i];
                start++;
                i++;
            }else{
                for(int l = i ; l < size1; ++l){
                    cout<<"("<<left[l]<<","<<right[j]<<")"<<endl;
                }
                inversion += size1 - i;
                nums[start] = right[j];
                start++;
                j++;
            }
        }
        if(i == size1){
            for(int c = j ; c< size2 ; ++c){
                nums[start] = right[c];
                start++;
            }
        }
        if(j == size2){
            for(int c =  i ; c< size1 ; ++c){
                nums[start] = left[c];
                start++;
            }
        }
        return inversion;
    }
    int inversion_count(vector<int>& nums , int low , int high){
        if(high>low){
            int mid = low + (high-low)/2;
            int left = inversion_count(nums,low,mid);
            int right = inversion_count(nums,mid+1,high);
            int inversion = merge(nums,low,mid,high) + left + right;
            return inversion;
        }
        return 0 ;
    }
    
    0 讨论(0)
  • 2020-11-22 04:51

    I've found it in O(n * log n) time by the following method.

    1. Merge sort array A and create a copy (array B)
    2. Take A[1] and find its position in sorted array B via a binary search. The number of inversions for this element will be one less than the index number of its position in B since every lower number that appears after the first element of A will be an inversion.

      2a. accumulate the number of inversions to counter variable num_inversions.

      2b. remove A[1] from array A and also from its corresponding position in array B

    3. rerun from step 2 until there are no more elements in A.

    Here’s an example run of this algorithm. Original array A = (6, 9, 1, 14, 8, 12, 3, 2)

    1: Merge sort and copy to array B

    B = (1, 2, 3, 6, 8, 9, 12, 14)

    2: Take A[1] and binary search to find it in array B

    A[1] = 6

    B = (1, 2, 3, 6, 8, 9, 12, 14)

    6 is in the 4th position of array B, thus there are 3 inversions. We know this because 6 was in the first position in array A, thus any lower value element that subsequently appears in array A would have an index of j > i (since i in this case is 1).

    2.b: Remove A[1] from array A and also from its corresponding position in array B (bold elements are removed).

    A = (6, 9, 1, 14, 8, 12, 3, 2) = (9, 1, 14, 8, 12, 3, 2)

    B = (1, 2, 3, 6, 8, 9, 12, 14) = (1, 2, 3, 8, 9, 12, 14)

    3: Rerun from step 2 on the new A and B arrays.

    A[1] = 9

    B = (1, 2, 3, 8, 9, 12, 14)

    9 is now in the 5th position of array B, thus there are 4 inversions. We know this because 9 was in the first position in array A, thus any lower value element that subsequently appears would have an index of j > i (since i in this case is again 1). Remove A[1] from array A and also from its corresponding position in array B (bold elements are removed)

    A = (9, 1, 14, 8, 12, 3, 2) = (1, 14, 8, 12, 3, 2)

    B = (1, 2, 3, 8, 9, 12, 14) = (1, 2, 3, 8, 12, 14)

    Continuing in this vein will give us the total number of inversions for array A once the loop is complete.

    Step 1 (merge sort) would take O(n * log n) to execute. Step 2 would execute n times and at each execution would perform a binary search that takes O(log n) to run for a total of O(n * log n). Total running time would thus be O(n * log n) + O(n * log n) = O(n * log n).

    Thanks for your help. Writing out the sample arrays on a piece of paper really helped to visualize the problem.

    0 讨论(0)
提交回复
热议问题