剑指Offer-Java-数组中重复的数字

题目

这里写图片描述
此题有好几种解法
1.时间复杂度为O(n),空间复杂度为O(n),利用临时数组
2.时间复杂度为O(n),会改变数组原来的结构
3.时间复杂度为O(nlogn),空间复杂度为O(1),不会改变原来的数组

第一种解法

    /**
     * 查找(0-----n-1)数组中相同的数
     * @param a
     */
    public static void select(int[] a)
    {   
        //合法性检测
        if(a==null||a.length<=0)
        {
            System.err.println("数组长度非法");
            return;
        }
        for (int i = 0; i < a.length; i++) {
            if(a[i]<0||a[i]>=a.length)
            {
                System.out.println("数组"+i+"号下标数据非法");
                return;
            }
        }       
        int[] test = a;
        int[] inspect= new int[test.length];
        //遍历数组
        for (int i = 0; i < test.length; i++) {
            //如果已经存在 
            if(inspect[test[i]]==1)
            {
                System.out.println(test[i]+" 重复");
            }
            //如果不重复则置1
            else
            {
                inspect[test[i]]=1;
            }
        }
    }

此解法的详细步骤如下
这里写图片描述

第二种解法

    /**
     * 查找(0-----n-1)数组中相同的数
     * @param a
     */
    public static void select(int[] a)
    {   
        //合法性检测
        if(a==null||a.length<=0)
        {
            System.err.println("数组长度非法");
            return;
        }
        for (int i = 0; i < a.length; i++) {
            if(a[i]<0||a[i]>=a.length)
            {
                System.out.println("数组"+i+"号下标数据非法");
                return;
            }
        }       
        int[] test = a;
        for (int i = 0; i < test.length; i++) {
            //一直交换直到当前下标和数字相同
            while(test[i]!=i)
            {
                if(test[i]==test[test[i]])
                {
                    System.out.println(test[i]+" 重复");
                    return;
                }
                //将test[i]和test[test[i]]进行交换
                //注意在交换过程中test[i]的变化
                int temp = test[i];
                test[i] = test[temp];
                test[temp] = temp;
            }

        }
    }

此解法的详细步骤如下
这里写图片描述

第三种解法

    /**
     * 查找(0-----n-1)数组中相同的数
     * @param a
     */
    public static void select(int[] a)
    {   
        //合法性检测
        if(a==null||a.length<=0)
        {
            System.err.println("数组长度非法");
            return;
        }
        for (int i = 0; i < a.length; i++) {
            if(a[i]<0||a[i]>=a.length)
            {
                System.out.println("数组"+i+"号下标数据非法");
                return;
            }
        }
        int[] test = a;
        int start = 0;
        int end = test.length-1;
        //循环
        while(end>=start)
        {
            int mid = ((end-start)>>1)+start;
            //计算当前区间所的数
            int count = getcount(test, start, mid);
            //如果当只剩一个数的时候
            if(end==start)
            {
                //如果当前计数大于1说明有两个
                if(count>1)
                {
                    System.out.println(start+" 重复");
                    return;
                }else
                {
                    break;
                }
            }
            if(count>(mid-start+1))
            {
                end = mid;
            }else
            {
                start = mid+1;
            }
        }
    }
    /**
     * 计算数组中[start,end]区间的数
     * @param a     数组
     * @param start 区间
     * @param end   
     * @return      数的个数
     */
    public static int getcount(int[] a ,int start,int end)
    {
        if(a==null){
        return 0;
        }
        int count = 0;
        for (int i = 0; i < a.length; i++) {
            if(a[i]>=start&&a[i]<=end)
            {
                count++;
            }
        }
        return count;
    }

此解法类似于二分查找,每次查看在指定区间内数字的个数,如果个数大于(mid-start)+1,即当前前半段范围内的数字数目大于当前前半段区间内的个数的时候,将范围缩小到前半段,如果不大于的话,将范围缩小到后半段。
此解法步骤如下
这里写图片描述
这几种解法各有各的好处,所以应该和面试官沟通好后,再进行coding。

猜你喜欢

转载自blog.csdn.net/qq_38345606/article/details/80584620