思路:

题目的主要信息:

  • 一个循环的圈,即数组首尾相接,数组中只有1 2 3三种元素
  • 要使这三种元素相同的元素都排在一起,即类似111122222333或者112222233311
  • 123的顺序不限

方法一:哈希表+贪心
具体做法:
因为123的顺序不限,因此我们的结果有6中排列组合方式,分别是:
123
132
213
231
312
321
因为我们只需要检查这六种预定结果需要变换多少次,取其最小值即可,其中我们使用到了哈希表记录每个数字出现的次数。
检查每个预定结果需要多少次交换的时候,我们调用findMin函数,利用贪心思想,首先我们看原数组与预定结果之间有多少位的差别,然后比较从每个预定结果的末尾位开始比较,若是相同则因为交换的时候跳过了,结果要加一,若是刚好等于预定结果的后一个数字,则结果减一,因为它的位置是正确的,我们算的交换次数。
findMin函数如下图所示:
图片说明

class Solution {
public:
    int findMin(vector<int>& Position, map<int, int>& m, int *a){
        int k = 0, cnt = 0, res = 0;
        for(int i = 0; i < 3; i++){
            for(int j = 0; j < m[a[i]]; j++){
                if(Position[k] != a[i])  //预定的位置不符合的数量
                    cnt++;
                k++;
            }
        }
        res = cnt;
        int x = m[a[0]] - 1, y = x + m[a[1]], z = y + m[a[2]];  //每个数字预定模板中的结束下标
        for(; x > 0; x--, y--, z--){ //每一位比较
            if(Position[x] == a[0])
                cnt++;
            else if(Position[x] == a[1])
                cnt--;
            if(Position[y] == a[1])
                cnt++;
            else if(Position[y] == a[2])
                cnt--;
            if(Position[z] == a[2])
                cnt++;
            else if(Position[z] == a[0])
                cnt--;
            res = min(res, cnt);
        }
        return res;
    }
    int ChangePosition(vector<int>& Position) {
        map<int, int> mp;  //哈希表统计每个数字出现的次数
        int n = Position.size();
        for(int i = 0; i < n; i++)
            mp[Position[i]]++;
        int res = INT_MAX;
        int L[6][3] = {{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}}; //6种次序
        for(int i = 0; i < 6; i++){ //遍历每一种次序,找到最小值
            int s = findMin(Position, mp, L[i]);
            res = min(res, s);
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:为数组长度,因为都是一次遍历
  • 空间复杂度:,虽然借助哈希表和辅助数组,但是辅助数组L只有18个变量,哈希表只使用了3个空间

方法二:优化方法一
具体做法:
因为题目说的这是一个循环,因此123和132两个预定结果就可以代替所有六种:
123 = 312 = 231
132 = 321 = 213
因此我们可以对方法一优化成只有两种情况,再类似比较两种情况的较小值。因为要用循环来比较,因此我们比较的时候下标需要取模运算。

class Solution {
public:
    int ChangePosition(vector<int>& Position) {
        int n = Position.size();
        int count1 = 0, count2 = 0, count3 = 0; //统计每个数字出现的次数
        for (int i = 0; i < n; i++) {
            if (Position[i] == 1)
                count1++;
            else if (Position[i] == 2)
                count2++;
            else
                count3++;
        }
        int win123 = 0, win132 = 0;
        for (int i = 0; i < n; i++){  //统计与两个预定结果不同位置数
            if (i < count1) {
                if (Position[i] != 1) {
                    win123++;
                    win132++;
                }
            }
            else {
                if (i < count1 + count2){
                    if (Position[i] != 2)
                        win123++;
                }
                else{
                    if (Position[i] != 3)
                        win123++;
                }
                if (i < count1 + count3){
                    if (Position[i] != 3)
                        win132++;
                }
                else{
                    if (Position[i] != 2)
                        win132++;
                }
            }
        }
        int res = min(win123, win132);
        for(int i = 1; i < n; i++){ //遍历类似方法一开始调换
            if(Position[(i - 1) % n] == 1){
                win123++;
                win132++;
            }
            if(Position[(count1 - 1 + i) % n] == 1){
                win123--;
                win132--;
            }
            if(Position[(count1 - 1 + i) % n] == 2)
                win123++;
            if(Position[(count1 + count2 - 1 + i) % n] == 2)
                win123--;
            if(Position[(count1 - 1 + i) % n] == 3)
                win132++;
            if(Position[(count1 + count3 - 1 + i) % n] == 3)
                win132--;
            if(Position[(count1 + count2 - 1 + i) % n] == 3)
                win123++;
            if(Position[(count1 + count2 + count3 - 1 + i) % n] == 3)
                win123--;
            if(Position[(count1 + count3 - 1 + i) % n] == 2)
                win132++;
            if(Position[(count1 + count2 + count3 - 1 + i) % n] == 2)
                win132--;
            res = min(res, win123); //维护最小值
            res = min(res, win132);
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,三次单层遍历
  • 空间复杂度:,常数空间