数组编程题01_剑指Offer03_数组中重复的数字

1 题目

牛客练习地址

在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

2 尝试解决

小弟不才,只想到暴力解决,通过两次遍历数组找出重复的内容,有就将数字放入duplication[0]并返回true,没有就false,下面是代码,方法上的注释是牛客网给出的,方法内的是我自己写的

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        //双重循环遍历数组
        for(int i=0;i<length;i++){
            for(int j=0;j<length;j++){
                //相同下标直接继续
                if(i==j)
                    continue;
                //不同下标进行比较
                if(numbers[i]==numbers[j]){
                    //相等,即重复
                    duplication[0]=numbers[i];
                    return true;
                }
                //不相等则继续运行,不做处理
            }
        }
        //循环结束后仍未停止运行,说明无重复
        return false;
    }
}

运行结果
在这里插入图片描述

3 更进一步

虽然通过了测试,但暴力算法终究是不可取的,不妨看看他人的思路,学习学习。
双重for循环的复杂度为O(n^2).

3.1 排序后比较

先排序后比较的复杂度为 排序的O(nlogn)和一次遍历O(n),取大的为O(nlogn),下面是代码

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        //排序后比较
        //排序
        quickSort(numbers,0,length-1);
        //比较 最大为length-1防止越界
        for(int i=0;i<length-1;i++){
            if(numbers[i]==numbers[i+1]){
                duplication[0]=numbers[i];
                return true;
            }
        }
        //无重复
        return false;
        
    }
    
    public void quickSort(int arr[],int left,int right){
        if(right-left<=0)
            return;
        else{
            int pivot=arr[right];
            int partition=partitionIt(arr,left,right,pivot);
            quickSort(arr,left,partition-1);
            quickSort(arr,partition+1,right);
        }
        
    }
    public int partitionIt(int[]arr,int left,int right,int pivot){
        int leftPtr=left-1;
        int rightPtr=right;
        while(true){
            while(arr[++leftPtr]<pivot)
                ;
            while(rightPtr>0 && arr[--rightPtr]>pivot)
                ;
            if(leftPtr>=rightPtr)
                break;
            else
                swap(arr,leftPtr,rightPtr);
        }
        swap(arr,leftPtr,right);
        return leftPtr;
    }
    public void swap(int arr[],int index1,int index2){
        int temp=arr[index1];
        arr[index1]=arr[index2];
        arr[index2]=temp;
    }
}

运行结果
在这里插入图片描述

3.2 哈希表

这种方法的想法就是在遍历数组的过程中填充一个哈希表,每扫描到一个数字就查看哈希表中是否已有该数字,有就结束,没有就返回。这种方法以O(n)的空间代价将时间效率提升至O(n)
但是在牛客的网页上不允许导入包,所以下面贴一段eclipse中的代码,以题目中的那个例子做测试。

	public static void main(String[] args) {
       int [] numbers= {2,3,1,0,2,5,3};
       int [] duplication=new int[1];
       duplicate(numbers, numbers.length, duplication);
       System.out.println(duplication[0]);
		
	}
	public static boolean duplicate(int numbers[],int length,int [] duplication) {
		 //哈希
        HashSet<Integer> set=new HashSet<>();
        for(int i=0;i<length;i++){
            //判断是否已有
            if(set.contains(numbers[i])){
                //重复
                duplication[0]=numbers[i];
                return true;
            }else{
                //还没有
                set.add(numbers[i]);
            }
        }
        //遍历结束,无重复
        return false;
	}

运行结果
在这里插入图片描述

3.3 结合题干分析

题目中所有数字都是在0到n-1之间,对数组进行排序,如果没有重复的数字,那么排序后的数组下标和值应当是一一对应的,即 numbers[0]==0,numbers[1]==1,numbers[2]==2,以此类推,那么在对数组进行重排的过程中,假设当前扫描到了第 i 个位置,如果numbers[i]==i ,那么这个数字就处在正确的位置上,如果numbers[i]!=i,那么此时的number[i]就错位了,假设此时number[i]==m,尝试判断 numbers[i]会不会等于numbers[m],等于就说明重复了,不等于就应该将该数字放到正确的位置上,即把 i 和 m 上的数字进行交换,然后继续向后判断,直到找出重复的数字或结束重排。

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        //重排
        for(int i=0;i<length;i++){
            //判断下标与当前值是否一一对应
            if(numbers[i]==i){
                //正确对应了,直接继续
                continue;
            }else{
                //不一一对应
                //判断i上的数字是否与numbers[i]上的数字相等
                //此处可将numbers[i]赋值给m,更易读一些,而且可以防止numbers[i]被修改造成死循环
                int m=numbers[i];
                if(numbers[i]==numbers[m]){
                    //相等,重复了
                    duplication[0]=numbers[i];
                    return true;
                }else{
                    //不相等,交换两者位置
                    int temp=numbers[i];
                    numbers[i]=numbers[m];
                    numbers[m]=numbers[i];
                }
            }
        }
        //重排结束,未重复
        return false;
    }
    
}

在这里插入图片描述

3.4 与全解的代码差异

全解的代码

public boolean duplicate(int[] nums, int length, int[] duplication) {
    if (nums == null || length <= 0)
        return false;
    for (int i = 0; i < length; i++) {
        while (nums[i] != i) {
            if (nums[i] == nums[nums[i]]) {
                duplication[0] = nums[i];
                return true;
            }
            swap(nums, i, nums[i]);
        }
    }
    return false;
}

private void swap(int[] nums, int i, int j) {
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
}

可以看出他在一开始判断了数组为空和长度未负数的情况,这是我所没有考虑到的。
将swap方法抽出来单独写这一点我想到了,但是将swap方法设置成private我没注意。
在for循环中直接判断还是使用while这一点我倒是没有注意,但是在leetcode中提交的时候使用if是无法通过的,只能用while,另外替换方法要注意,没写好会死循环的。

4 结语

一开始看书的时候看到下面的分析内容说要进行排序,但是对排序算法不熟悉,纠结了半天决定先按自己的想法试试再说,没想到居然通过了,再一次验证了一道问题有多种解法的,最重要的还是要思考。
排序和集合类方面仍有不足,剑指offer看累了就看点这方面的内容吧。

5 参考链接

1.快速排序—(面试碰到过好几次)
2.时间复杂度大小比较
3.java各种集合类区别
4.官解
5.剑指 Offer 全解(Java 版)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值