算法通关村第三关——爱不起的数组与双指针思想(黄金)

算法通关村第三关——爱不起的数组与双指针思想(黄金)

    • 1. 数组中出现超过一半的数字
      • 方法一:Hash法
      • 方法二:排序后再找
    • 2. 只出现一次的数字
      • 方法一:排序再查找
      • 方法二:位运算
    • 3. 颜色分类
      • 方法一:两次遍历
      • 方法二:双指针

1. 数组中出现超过一半的数字

169. 多数元素

方法一:Hash法

class Solution {
    public int majorityElement(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int num : nums){
            map.put(num, map.getOrDefault(num, 0)+1);
            if (map.get(num) > nums.length/2) {
                return num;
            }
        }
        return 0;
    }
}

没什么好说的,不过这种方式的空间复杂度是n,使用了额外的数据结构

方法二:排序后再找

因为该数超过一半还多,也就是,如果把数组排序后,中间一定是这个数

例如:

例1:**1 1 1 1 2 ** 例2:**1 1 1 1 2 2 ** 例3:1 1 1 1 2 2 2 例3:1 1 1 1 2 2 2 2 (这就不是了)

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }
}

这样空间复杂度就是1

2. 只出现一次的数字

136. 只出现一次的数字

方法一:排序再查找

这道题通过上一题的想法,那么也可以使用先排序再查找的方法

class Solution {
    public int singleNumber(int[] nums) {
        Arrays.sort(nums);
        for(int i=0; i< nums.length; i++){
            if(i != nums.length-1 && nums[i] != nums[i+1] ){
                return nums[i];
            }else {
                i++;
            }
        }
        return  nums[nums.length - 1];
    }
}

方法二:位运算

位运算有个叫做:异或(^),位数相同就是0,位数不同就是1

以下是一个使用^=运算符的例子:

int a = 5; // 二进制表示为 00000101
int b = 3; // 二进制表示为 00000011

a ^= b; // a = a ^ b,即 a = 5 ^ 3

// 经过按位异或运算后,a的值发生变化,变为二进制表示的00000110,即十进制的6
System.out.println(a); // 输出6

那么把一个数,异或两次相同的数,会发生什么呐?

int a = 5; // 二进制表示为 00000101
int b = 3; // 二进制表示为 00000011

a ^= b; // a = a ^ b,即 a = 5 ^ 3, 则a = 00000110
a ^= b; // a = a ^ b,即 a = 6 ^ 3, 则00000110 与 00000011 进行异或运算

// 经过按位异或运算后,a的值发生变化,变为二进制表示的00000101,即十进制的5
System.out.println(a); // 输出5

会发现,最后等于原来的数,通过这种方式,回到题目

要找到不是两次的数,我只需要遍历一遍数组,然后每次都进行异或运算,最后得出来的结果,就是只出现一次的数

class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;
        for(int num : nums){
            res ^= num;
        }
        return res;
    }
}

3. 颜色分类

75. 颜色分类

方法一:两次遍历

在第一次遍历中,我们从左向右遍历整个数组,如果找到了 0,那么就需要将 0 与「头部」位置的元素进行交换,并将「头部」向后扩充一个位置。在遍历结束之后,所有的 0都被交换到「头部」的范围,并且「头部」只包含 0。

在第二次遍历中,我们从「头部」开始,从左向右遍历整个数组,如果找到了 1,那么就需要将 1 与「头部」位置的元素进行交换,并将「头部」向后扩充一个位置。在遍历结束之后,所有的 1都被交换到「头部」的范围,并且都在 0 之后,此时 2 只出现在「头部」之外的位置,因此排序完成。

例如:nums = [2,0,2,1,1,0]

第一次遍历: 0,0,2,1,1,2
               ^
    		  ptr
第二次遍历: 0,0,1,2,1,2
                 ^
    		    ptr
第二次遍历: 0,0,1,1,2,2
                   ^
		          ptr

代码如下:

class Solution {
    public void sortColors(int[] nums) {
        int n = nums.length;
        int ptr = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 0) {
                int temp = nums[i];
                nums[i] = nums[ptr];
                nums[ptr] = temp;
                ++ptr;
            }
        }
        for (int i = ptr; i < n; ++i) {
            if (nums[i] == 1) {
                int temp = nums[i];
                nums[i] = nums[ptr];
                nums[ptr] = temp;
                ++ptr;
            }
        }
    }
}

方法二:双指针

这里有两种双指针方案,我不偏向官方提供的答案

思路:

0 挪到最前面,2 挪到最后面
注意:2 挪完如果换出来的不是 1,那么指针要回退,因为 0 和 2 都是需要再次移动的

class Solution {
    public void sortColors(int[] nums) {
        int n = nums.length;
        if (n < 2) {
            return;
        }
        int p = 0, q = n - 1;
        for (int i = 0; i <= q; ++i) {
            if (nums[i] == 0) {
                nums[i] = nums[p];
                nums[p] = 0;
                ++p;
            }
            if (nums[i] == 2) {
                nums[i] = nums[q];
                nums[q] = 2;
                --q;
                // 注意回退
                if (nums[i] != 1) {
                    --i;
                }
            }
        }
        return;
    }
}

over~~~

你可能感兴趣的:(数据结构,算法,算法,java,数据结构,笔记)