劍指offer 51 - 陣列中的逆序對
阿新 • • 發佈:2020-09-14
題目
https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof
題意
在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數。
示例 1:
輸入: [7,5,6,4]
輸出: 5
思路
author's blog == http://www.cnblogs.com/toulanboy/
基礎想法
(1)本題要求的是逆序對,逆序對的定義題面已經給出。
(2)而求逆序對的常見解法是藉助歸併排序來實現,更具體是藉助他的merge過程。
歸併排序求逆序對
歸併邏輯
我們知道,歸併排序實際上分治的思想,歸併排序的邏輯如下:
(1)其先將陣列不斷細分,最後細分到每個陣列只有1個元素
1個元素
陣列中就是有序的。(2)然後將兩個有序的長度為1的陣列依次合併為長度為2的有序陣列,後面合併過程就是依次類推,
2合4
,4合8
,...,n/2合n
。
Merge過程
值得注意的是:在上述的合併過程中,我們是可以得到其逆序對個數的?
舉個例子來說明合併過程:
陣列A: 11 22 33 44
陣列B: 12 20 55 66
陣列A和陣列B合併為一個數組。
正常做法是:分別用2個指標p1, p2指向A和B的第一個元素。然後進行兩兩對比,誰小誰下來放進新陣列。
進一步地,我們可以利用這個合併過程。若在上述例子中,p1指向22, p2指向20。那麼可知p1 > p2,,們可知逆序對(22, 20),而且還可知道p1後面的數,都比20要大,所以可得到逆序對(33, 20)、(44, 20)。 故可得到的逆序對個數是p1後面剩餘的元素個數。
通過合併過程累加,就可以得到整個序列的逆序對。
可能有同學想問:上述合併,只是得到了兩個相對位置陣列的逆序對,但逆序對個數還應該包括這個位置相對其他位置的逆序對,這部分是如何統計上去的?
答:實際上,上述兩個相對位置數組合併為1個大陣列後,他必然會經歷和前面大陣列、後面大陣列的過程,所以他前面的元素、他後面的元素相對他的逆序對個數也會被統計到的。
程式碼
//author's blog == http://www.cnblogs.com/toulanboy/ class Solution { public: int ans = 0; vector<int> temp;//輔助陣列,用來儲存新的有序陣列 void fenzhi(vector<int>& nums, int left, int right){ if(left >= right){ return; } int mid = (left+right) >> 1; fenzhi(nums, left, mid);//拆分,得到左邊 fenzhi(nums, mid+1, right);//拆分,得到右邊 merge(nums, left, mid, right);//左右合併 } void merge(vector<int>& nums, int left, int mid, int right){ int p1 = left; int p2 = mid+1; temp.clear(); while(p1 <= mid && p2 <= right){ if(nums[p1] <= nums[p2]){ temp.push_back(nums[p1]); p1++; } else{ temp.push_back(nums[p2]); p2++; ans += (mid-p1+1); } } while(p1 <= mid){ temp.push_back(nums[p1]); p1++; } while(p2 <= right){ temp.push_back(nums[p2]); p2++; } for(int i=0; i<temp.size(); ++i){ nums[left+i] = temp[i]; } } int reversePairs(vector<int>& nums) { fenzhi(nums, 0, nums.size()-1); return ans; } };