编程导航算法通过村第三关 | 再论双指针

56 阅读3分钟

数组中出现次数超过一半的数字

剑指offer_在线编程_牛客网

使用Hash

我们先创建一个HashMap的key是元素的值,value是已经出现的次数,然后遍历数组来统计所有元素出现的次数。最后再次遍历Hash,找到出现次数超过一半的数字

public int moreThanHalfNum(int [] array) {
    if(array==null)
        return 0;
    Map<Integer,Integer> res=new HashMap<>();
    int len = array.length;
    for(int i=0;i<array.length;i++){
        res.put(array[i],res.getOrDefault(array[i],0)+1);
        if(res.get(array[i])>len/2)
            return array[i];
    }
    return 0;
}

数组中出现一次的数字

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

Set集合

public static Integer findOneNum(int[] arr) {
    Set<Integer> set = new HashSet<Integer>();
    for(int i : arr) {
        if(!set.add(i))//添加不成功返回false,前加上!运算符变为true
            set.remove(i);//移除集合中与这个要添加的数重复的元素
    }
    //注意边界条件的处理
    if(set.size() == 0)
        return null;
    //如果Set集合长度为0,返回null表示没找到
    return set.toArray(new Integer[set.size()])[0];
}

位运算

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

颜色分类问题

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

基于冒泡排序的双指针

我们可以考虑对数组进行两次遍历。在第一次遍历,我们将数组中所有的 0 交换到数组的头部,这样第二次遍历只需要处理1和2的问题就行了,而这两次寻找本身又是非常漂亮的双指针。代码如下:

class Solution {
    public void sortColors(int[] nums) {
        int n = nums.length;
        int left = 0;
        //将所有的0交换到数组的最前面
        for (int right = 0; right < n; right++) {
            if (nums[right] == 0) {
                int temp = nums[right];
                nums[right] = nums[left];
                nums[left] = temp;
                left++;
            }
        }

        //将所有的1交换到2的前面
        for (int right = left; right < n; ++right) {
            if (nums[right] == 1) {
                int temp = nums[right];
                nums[right] = nums[left];
                nums[left] = temp;
                ++left;
            }
        }
    }
}

三指针

如果要求只用一次遍历就要解决问题,该怎么办呢?我们隐约感觉到要使用三个指针才行:

  • left指针,表示left左侧的元素都是0
  • right指针 ,表示right右侧的元素都是2
  • index指针,从头到尾遍历数组,根据nums[index]是0还是2决定与left交换还是与right交换。

index位置上的数字代表着我们当前需要处理的数字。当index为数字1的时候,我们什么都不需要做,直接+1即可。如果是0,我们放到左边,如果是2,放到右边。如果index=right,则可以停止。 我们看一下图示:

这里的重点和难点index位置为2进行交换后为什么只进行right--,而不用index++呢?这是因为我们right位置交换过来的元素可能是0,也可能是1。如果是0自然没问题,但是如果是1则执行index++就将1跳过了无法处理了。所以我们先不动index,在下一次循环时继续判断这个index位置元素是不是0。

那为啥index位置是0的时候执行swap就可以index++了呢,这是因为如果index前面位置如果存在位置都会被swap到right位置去了,这里只需要处理0和1的情况就可以了。

代码如下:

public void sortColors(int[] nums) {
    int left=0,right=nums.length-1;
    int index=0;
    while(index<=right){
        if(nums[index]==0)
            swap(nums,index++,left++);
        else if(nums[index]==2)
            swap(nums,index,right--);
        else
            index++;
    }
}
private void swap(int[] nums,int i,int j){
    int temp=nums[i];
    nums[i]=nums[j];
    nums[j]=temp;
}