数组中异常数发现

数组中1个数出现一次其他出现2次

Leetcode136 https://leetcode.com/problems/single-number/
思路:有一个异常数:整体抑或一遍。适合只有1个数出现奇数次,其他数出现偶数次

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

如果使用哈希,有两种可行的方法:
第一,就是存储每个数的个数,最后遍历一遍输出所需个数的数。这个适用于所有找数组异常数情况,但空间复杂度很高。
第二,设置一个32位数组(或者int)存储所有数不同位上1的个数,最后按情况取余。这个要视情况而定,可以节省很多空间。


2. 数组中2个数出现一次其他出现2次

剑指offer40_数组中两个数出现一次其他出现两次
思路:有两个异常数:将数组按两个异常数分开(按照两个异常数某个不同位分),再分别抑或。适合有2个数出现奇数次,其他数出现偶数次。
Trick: 找不同位可以找第一个不同的位置,即抑或后右数第一个1,用(i&(i-1))注意位运算优先级最低。!!

public void twoNumber(int[] nums){
    int a = 0, b = 0, rs = 0;
    for(int i = 0; i < nums.length; i++) rs ^= nums[i];
    rs = rs ^ rs & rs - 1;
    for(int i = 0; i < nums.length; i++){
        if((nums[i] & rs) == 0) a ^= nums[i];
        else b ^= nums[i];
    }
    System.out.println(a+" "+b);
}

3. 数组中1个数出现一次其他出现3次

Leetcode137 https://leetcode.com/problems/single-number-ii/

Given an array of integers, every element appears three times except for one. Find that single one.

思路: 第一种想法:与第1题hash思路一样,可以利用第二个hash思路记录每位1出现次数。开辟32位数组,记录每个位上1出现的个数,最后各位模k,最后剩的数组转二进制就是这个数。

@Checked
public int singleNumber(int[] nums) {
    int[] flag = new int[32];
    for(int i = 0; i < nums.length; i++){
        for(int j = 0; j < 32; j++, nums[i] >>>= 1){
            flag[j] += (nums[i] & 1);
            if(flag[j] == 3) flag[j] = 0;
        }
    }
    int rs = 0;
    for(int j = 31; j >= 0; j--) rs = rs<<1 | flag[j];
    return rs;
}

如果再简化一点:用变量来存储出现一个1、两个1和三个1的分布,最终返回一个1的分布就是这个数

public int singleNumber(int[] nums) {
    int one = 0, two = 0, three = 0;
    for(int i = 0; i < nums.length; i++){
        two |= one & nums[i]; //A[i]&one得到有两个1的位分布,与two或,增加two的位
        one ^= nums[i]; //A[i]^one得到一个1的位分布
        three = one & two; //one&two可以得到三个1的位分布
        one &= ~three; //取反来对one和two的对应位进行清零操作
        two &= ~three; //取反来对one和two的对应位进行清零操作
    }
}

4. 数组中1个数出现了n/2多次

Leetcode169 https://leetcode.com/problems/majority-element/
剑指offer29_数组中出现超过一半的数字

Given an array of size n, find the majority element. The majority element is the element that appears more than n/2 times.
You may assume that the array is non-empty and the majority element always exist in the array.

本题的思路和方法很多

第一种方法:用hash记录每个数字出现次数,最后遍历一遍得到次数符合条件的数字。时间复杂度o(n),空间复杂度o(n)

@Checked
public int majorityElement(int[] nums) {
    if(nums != null){
        Map<Integer, Integer> hm = new HashMap<Integer, Integer>();
        for(int i = 0; i < nums.length; i++){
            int num = hm.containsKey(nums[i]) ? hm.get(nums[i]) : 0;
            hm.put(nums[i], ++num);
            if(num > nums.length/2) return nums[i];
        }
    }
    return 0;
}

第二种方法:将数组排序,排序后index为n/2的就是该元素。时间复杂度o(nlogn),空间复杂度o(1)

/**
用自己的快排,stackOverFlow了
*/
public int majorityElement(int[] nums) {
    if(nums != null){
        quickSort(nums, 0, nums.length-1);
        return nums[nums.length/2];
    }
    return 0;
}
public void quickSort(int[] nums, int start, int end){
    if(start >= end) return;
    int pivot =  nums[end], mid = start -1;
    for(int i = start; i < end; i++) 
        if(nums[i] < pivot) swap(nums, ++mid, i);
    swap(nums, ++mid, end);
    quickSort(nums, start, mid-1);
    quickSort(nums, mid+1, end);
}
public void swap(int[] nums, int i, int j){
    if(i == j) return;
    nums[i] ^= nums[j];
    nums[j] ^= nums[i];
    nums[i] ^= nums[j];
}
/**
用java提供的sort工具,AC了,果然是效率问题。
*/
public int majorityElement(int[] nums) {
    if(nums != null){
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
    return 0;
}

第三种方法:利用随机快速排序中找中位数的思想,即分治查找来找到n/2的元素。时间复杂度o(n),空间复杂度o(1)

@Checked
public int majorityElement(int[] nums) {
    if(nums != null){
        quickSort(nums, 0, nums.length-1);
        return nums[nums.length/2];
    }
    return 0;    
}
public void quickSort(int[] nums, int start, int end){
    if(start >= end) return;
    int pivot =  nums[end], mid = start -1;
    for(int i = start; i < end; i++) 
        if(nums[i] < pivot) swap(nums, ++mid, i);
    swap(nums, ++mid, end);
    if(mid == nums.length/2) return; // 如果找到的刚好index为n/2,返回。
    else if(mid < nums.length/2) quickSort(nums, mid+1, end);// 如果该元素在左半边,则继续在右半边找
    else quickSort(nums, start, mid-1);//如果该元素在右半边,则继续在左半边找
}
public void swap(int[] nums, int i, int j){
    if(i == j) return;
    nums[i] ^= nums[j];
    nums[j] ^= nums[i];
    nums[i] ^= nums[j];
}

第四种方法:利用Moore投票的算法,记录当前元素C和当前元素数量count。遍历数组元素num[i]:
若num[i] == C, 则count++;//增加主元素数量
若num[i] != C, count == 0, 则 num[i] = C, count=1;//更换主元素
若num[i] != C, count != 0, 则 count–;//减少主元素数量

@Checked
public int majorityElement(int[] nums) {
    if(nums == null) return 0;
    int curN = 0 , count = 0;
    for(int i = 0; i < nums.length; i++){
        if(count <= 0) {
            curN = nums[i];
            count = 1;
        }else if(nums[i] == curN) count++;
        else count--;
    }
    return curN;
}

第五种方法:利用位特点,记录每个位上1和0的数量,由于同一个数出现了一半以上,统计所有数在每个位上0和1的个数时,1和0哪个多说明该数在该位上是什么。

//代码:用一个数组记录每位上的1与0之差
@Checked
public int majorityElement(int[] nums) {
    if(nums == null) return 0;
    int[] flag = new int[32];
    for(int i = 0; i < nums.length; i++){
        for(int j = 0; j < 32; j++, nums[i] >>= 1)
            flag[j] = (nums[i] & 1) == 1 ? flag[j] + 1 : flag[j] -1;
    }
    int rs = 0;
    for(int i = 31; i >= 0; i--){
        int p = flag[i] < 0 ? 0 : 1;
        rs = (rs << 1) | p;
    }
    return rs;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值