題目:
給定一個包含1-n的數列,我們通過交換任意兩個元素給數列重新排序。
求最少需要多少次交換,能把數組排成按1-n遞增的順序,(數組中的元素互不重復)。
比如 初始狀態 5 4 3 2 1 。交換5和1的位置 得到 1 4 3 2 5,再交換4 2的位置得到 1 2 3 4 5.只需要兩次即可。
方法一:
第一個數排序之后應該是1,但此時第一個位置是5,那么把5放到5應該在的位置上,
然后再把5原來位置上那個數字放到第一個位置上, 即得到 1 4 3 2 5 。此時第一個位置的數字已經找到 ,往后遍歷位置2.
得到4,接下來把4放到4應該在的位置,得到 1 2 3 4 5.此時已經完全有序。
總結一下就是:
交換的時候,遍歷數組,判斷當前元素是否在最終位置,否則把它交換到它的最終位置,(即每次交換至少讓其中一個元素被放到其最終位置),這樣統計總交換次數。
int getMinSwaps(vector<int> &nums) { map<int, int>mp; vector<int> sortedNums(nums); sort(sortedNums.begin(), sortedNums.end()); for (int i = 0; i < sortedNums.size(); i++)mp[sortedNums[i]] = i ;//記錄下標索引 int cnt = 0; for (int i = 0; i < nums.size();i++) { if (nums[i] == sortedNums[i])continue; swap(nums[i], nums[mp[nums[i]]]);//交換到nums[i]這個值應該在的位置 cnt++; } return cnt; }
方法二:
在原數組中,每個元素添加一個出邊指向它最終的位置,這樣畫完出邊后,
最少會成一個環,最多n個環。 然后原理就是, 最少交換次數=結點數n-形成的環數。
int getMinSwaps(vector<int> &nums)
{ //排序 vector<int> nums1(nums); sort(nums1.begin(),nums1.end()); unordered_map<int,int> m; int len = nums.size(); for (int i = 0; i < len; i++){ m[nums1[i]] = i;//建立每個元素與其應放位置的映射關系 } int loops = 0;//循環節個數 vector<bool> flag(len,false); //找出循環節的個數 for (int i = 0; i < len; i++){ if (!flag[i]){//已經訪問過的位置不再訪問 int j = i; while (!flag[j]){ flag[j] = true; j = m[nums[j]];//原序列中j位置的元素在有序序列中的位置 } loops++; } } return len - loops; }
參考: