算法面試題:數組中的逆序對

題目

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。

示例

輸入: [7,5,6,4]
輸出: 5

限制

0 <= 數組長度 <= 50000

實現代碼(歸併算法)

public static void main(String[] args) {
        int[] nums = new int[]{7,5,6,4};

        System.out.println(reversePairs(nums));
    }

    /**
     * 歸併排序算法
     *
     *執行用時 :
     * 35 ms
     * 內存消耗 :
     * 48.7 MB
     * @param nums
     * @return
     */
    public static int reversePairs(int[] nums) {
        int len = nums.length;

        //如果是長度小於2則沒有逆序對
        if (len < 2) {
            return 0;
        }

        //拷貝數組
        int[] tmpNums = nums.clone();

        //輔助臨時數組
        int[] temp = new int[len];
        return reversePairs(tmpNums, 0, len - 1, temp);
    }

    /**
     * nums[left..right] 計算逆序對個數並且排序
     *
     * @param nums
     * @param start
     * @param end
     * @param temp
     * @return
     */
    private static int reversePairs(int[] nums, int start, int end, int[] temp) {

        //數組長度只有1的情況下則沒有逆序對
        if (start == end) {
            return 0;
        }

        //中間長度 (left+right)/2會有整型溢出問題
        int mid = start + (end - start) / 2;
        //左邊逆序對數量
        int leftPairs = reversePairs(nums, start, mid, temp);
        //右邊逆序對數量
        int rightPairs = reversePairs(nums, mid + 1, end, temp);

        //如果是已經有序了則返回左邊的+右邊的
        if (nums[mid] <= nums[mid + 1]) {
            return leftPairs + rightPairs;
        }

        //跨越區間逆序對計算
        int crossPairs = mergeAndCount(nums, start, mid, end, temp);
        return leftPairs + rightPairs + crossPairs;
    }

    /**
     * nums[start..mid] 有序,nums[mid + 1..right] 有序
     *
     * @param nums
     * @param start
     * @param mid
     * @param end
     * @param temp
     * @return
     */
    private static int mergeAndCount(int[] nums, int start, int mid, int end, int[] temp) {

        //拷貝 start到end數組到輔助數組
        for (int i = start; i <= end; i++) {
            temp[i] = nums[i];
        }

        //i, j兩位置的第一個元素
        int i = start;
        int j = mid + 1;

        int count = 0;
        for (int k = start; k <= end; k++) {

            if (i == mid + 1) {//當i超出範圍[start..mid],j歸併
                nums[k] = temp[j];
                j++;
            } else if (j == end + 1) {//當j超出範圍[mid+1..end],i歸併
                nums[k] = temp[i];
                i++;
            } else if (temp[i] <= temp[j]) {//當i小於J i歸並回去
                nums[k] = temp[i];
                i++;
            } else {//當j<i時 j歸並回去 並且計數
                nums[k] = temp[j];
                j++;

                //計數
                count += (mid - i + 1);
            }
        }

        return count;
    }

如果大家對java架構相關感興趣,可以關注下面公衆號,會持續更新java基礎面試題, netty, spring boot,spring cloud等系列文章,一系列乾貨隨時送達, 超神之路從此展開, BTAJ不再是夢想!

架構殿堂

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章