剑指Offer39.数组中出现次数超过一半的数字

  • 剑指Offer39.数组种出现次数超过一半的数字

  • 题目:
    找出一个数组中出现次数超过一半的数字;
    当不存在该目标数时,返回0;

  • 思路:
    1.(最优解)摩尔投票:O(n):需要遍历两次,一次用来找候选人,第二次用来验证候选人是否真的超过半数,O(1)
    目标数个数超过一半,哪怕剩下所有数联合起来对付我也是我赢,更何况其它数也可能内讧;

class Solution {
    
    
public:
    int majorityElement(vector<int>& nums) {
    
    
        int cnt = 0, val;//当前目标数val,以及一个计数器cnt
        for (auto x : nums) {
    
    
            if (cnt == 0) {
    
    //第一个数,或抵消过后的下个数
                val = x;
                cnt = 1;
            }
            else if (x == val) ++cnt;
            else --cnt;
        }
        //若该数组真的存在超过半数的数,则一定是val;
        //但也可能数组中不存在这样的数,需要第二次遍历来确认
        int len = nums.size();
        cnt = 0;
        for (auto x : nums)
        	if (x == val) ++cnt;
        if (cnt > len / 2) return val;
        else return 0;
    }
};

//类似的题,[求众数Ⅱ](https://leetcode-cn.com/problems/majority-element-ii/),区别在于最多要找两个数(最多有两个数的个数超过1/3)
class Solution {
    
    
public:
    vector<int> majorityElement(vector<int>& nums) {
    
    
        int mcnt = 0, m = 0;
        int ncnt = 0, n = 0;
        for (auto x : nums) {
    
    //第一次遍历,找候选人m,n; 以下if-else的顺序有东西, m,n的初始化也有东西
            if (x == m) ++mcnt;//唱到m,就累加mcnt
            else if (x == n) ++ncnt;//唱到n,就累加ncnt
           
            else if (mcnt == 0) m = x, mcnt = 1;
            else if (ncnt == 0) n = x, ncnt = 1;

            else --mcnt, --ncnt;//新出现了个竞争对手,则目前票数最高的俩人的优势减弱一票;
        }

        //重新遍历一次,验证m和n是否真的超过len/3
        mcnt = 0, ncnt = 0;
        for (auto x : nums) {
    
    
            if (x == m) ++mcnt;
            else if (x == n) ++ncnt;
        }

        vector<int> ans;
        int len = nums.size();
        if (mcnt > len / 3) ans.push_back(m);
        if (ncnt > len / 3) ans.push_back(n);

        return ans;
    }
};

2.位运算:O(n):实际位O(32n):循环32次,每次循环确定目标数二进制的一位,O(1)

class Solution {
    
    
public:
    int majorityElement(vector<int>& nums) {
    
    
        int n = nums.size();
        int ans = 0;//初始32位都为0,把它逐位改造成目标数
        for (int i = 0; i < 32; ++i) {
    
    
            int cnt = 0;
            for (auto x : nums) {
    
    //判断目标数的第i位是1还是0
                if (x & (1 << i)) ++cnt;//如果x的第i位是1,就累加
            }
            if (cnt > n / 2) ans ^= (1 << i);//若统计第i位为1的数超过半数,说明目标数的第i位就是1,那就把ans的第i位置为1;否则说明目标数的第i位是0,那不动(本身就为0)
        }
        return ans;
    }
};

3.排序:O(nlogn),O(1)

class Solution {
    
    
public:
    int majorityElement(vector<int>& nums) {
    
          
        sort(nums.begin(), nums.end());
        int n = nums.size();
        
        return nums[n / 2];//目标数这一段无论排在数组的哪一段,最中间处肯定都是目标数
    }
};

4.哈希表:O(n),O(n)

class Solution {
    
    
public:
    int majorityElement(vector<int>& nums) {
    
          
        unordered_map<int, int> um;//<元素值,对应出现的次数>
        int n = nums.size();
        for (auto x : nums) {
    
    
            if (++um[x] > n / 2) return x;
        }
        return -1;//说明没有个数超过一半的数
    }
};

猜你喜欢

转载自blog.csdn.net/jiuri1005/article/details/114254503
今日推荐