数组问题常用算法

摩尔投票法

适用于找出数组中出现次数超过总数一半的题目。
具体算法:遍历数组,记第一个数的次数为1,遇到相同的数则该次数+1,不同的数则该次数-1,当次数为0时,重新令当前的数为目标数。遍历结束时,目标数即为所求的数。
例题

Leetcode 169
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/majority-element
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

	//题目明确指出多数元素存在,如果没有说明,则还需要加判断验证是否是最多的元素。
	class Solution {
    public int majorityElement(int[] nums) {
        int count=0;
        int target=nums[0];
        for(int i=0;i<nums.length;i++){
        	if(count==0){  
                target=nums[i];
            }
            if(nums[i]==target){
                count++;
            }else{
                count--;
            }
        }
        return target;
    }
}

摩尔投票法加强版

如果遇到求数组中出现次数超过三分之一或者四分之一等等的问题,该如何用摩尔投票法解决?

Leetcode 229
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/majority-element-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

//作者:wotxdx
//链接:https://leetcode-cn.com/problems/majority-element-ii/solution/liang-fu-dong-hua-yan-shi-mo-er-tou-piao-fa-zui-zh/
//来源:力扣(LeetCode)
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
	class Solution {
    public List<Integer> majorityElement(int[] nums) {
        // 创建返回值
        List<Integer> res = new ArrayList<>();
        if (nums == null || nums.length == 0) return res;
        // 初始化两个候选人candidate,和他们的计票
        int cand1 = nums[0], count1 = 0;
        int cand2 = nums[0], count2 = 0;

        // 摩尔投票法,分为两个阶段:配对阶段和计数阶段
        // 配对阶段
        for (int num : nums) {
            // 投票
            if (cand1 == num) {
                count1++;
                continue;
            }
            if (cand2 == num) {
                count2++;
                continue;
            }

            // 第1个候选人配对
            if (count1 == 0) {
                cand1 = num;
                count1++;
                continue;
            }
            // 第2个候选人配对
            if (count2 == 0) {
                cand2 = num;
                count2++;
                continue;
            }

            count1--;
            count2--;
        }

        // 计数阶段
        // 找到了两个候选人之后,需要确定票数是否满足大于 N/3
        count1 = 0;
        count2 = 0;
        for (int num : nums) {
            if (cand1 == num) count1++;
            else if (cand2 == num) count2++;
        }

        if (count1 > nums.length / 3) res.add(cand1);
        if (count2 > nums.length / 3) res.add(cand2);

        return res;
    }
}

数组中数的出现次数

数组中有数消失,求消失的数。或者找出数组中出现次数与其他数字不同的数。

求和法

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:
输入: [1]
输出: [2,3]
示例 2:
输入: [2,3]
输出: [1,4]
提示:
nums.length <= 30000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/missing-two-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:先求1-N的和,减去现有的和,得到缺少两个数的和。因为这两个数不相等,所以对两数之和除以2,其中一个数必定大于此数,一个数必定小于等于此数。再计算比此数小的数的和。这道题也是利用了分组的思想,如果告诉你一个数组中只缺了一个数,那么可以求1-N的和减去数组的和,这道题是少了两个数,所以要分别求和。

	class Solution {
    public int[] missingTwo(int[] nums) {
        int len=nums.length;
        int total=0;
        for(int num : nums){
            total+=num;
        }
        int sum=0;
        for(int i=1;i<=len+2;i++){
            sum+=i;
        }
        int tmp=(sum-total)/2;
        int a=0;
        for(int num : nums){
            if(num<=tmp){
                a+=num;
            }
        }
        a=(1+tmp)*tmp/2-a;
        return new int[]{a,sum-total-a};
    }
}

位运算

位运算教程

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:只有1个数出现了1次,其余数都出现了2次,那么可以用异或运算,最终的结果是出现1次的数。

	class Solution {
    public int singleNumber(int[] nums) {
        int ans=nums[0];
        for(int i=1;i<nums.length;i++){
            ans^=nums[i];
        }
        return ans;
    }
}

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:
这题与上面一体的不同之处在于有2个数出现了一次,那么可以进行分组操作,把数组分组之后进行异或,得到的两个结果就是答案。

怎么进行分组呢?首先想到的可以按照奇偶进行分组,但是这样有可能只出现一次的数字会被分在同一组,无法分开。还有一种做法是用&运算,把数组进行^运算之后得到的就是那两个只出现一次的数的异或结果,异或结果中肯定有1(因为两个数不同)。

如例题中4,1,4,6进行异或操作后,得到的结果是111(B),随便取一个位上的1,这里就取001(B),把每个数都和001进行&运算,4与6的结果都是000(B),1的结果是001(B),这样就把数组分成了两组。

class Solution {
    public int[] singleNumbers(int[] nums) {
        int len=nums.length;
        int a=0;
        for(int num : nums){
            //得到异或结果
            a^=num;
        }
        int k=1;
        while((k & a)==0){
        //得到第一个为1的结果
            k<<=1;
        }
        int x=0,y=0;
        for(int num : nums){
            //分组,如果&为0,说明那一位为0
            if((num & k)==0){
                x^=num;
            }else{
                y^=num;
            }
        }
        return new int[]{x,y};
    }
}

双指针

设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
示例 1:
输入: nums = [5,6,5], target = 11
输出: [[5,6]]
示例 2:
输入: nums = [5,6,5,6], target = 11
输出: [[5,6],[5,6]]
提示:
nums.length <= 100000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/pairs-with-sum-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:先进行排序,再用首位双指针。

class Solution {
    public List<List<Integer>> pairSums(int[] nums, int target) {
        Arrays.sort(nums);
        int i=0,j=nums.length-1;
        List<List<Integer>> res=new ArrayList<>();
        while(i<j){
            List<Integer> list=new ArrayList<>();
            int sum=nums[i]+nums[j];
            if(sum==target){
                list.add(nums[i]);
                list.add(nums[j]);
                res.add(list);
                i++;
                j--;
            }else if(sum<target){
                i++;
            }else if(sum>target){
                j--;
            }
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值