【剑指offer】3-2不修改数组找出重复的数字

题目描述

给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内。

请找出数组中任意一个重复的数,但不能修改输入的数组。

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思路

1.暴力法

参考3-1链接

2.辅助数组

创建一个长度为n+1的辅助数组,如果原数组中被复制的数字是m,就把它度知道辅助数组的下标为m的位置。

public static Integer findDuplicateInArray(int[] a){
        int[] newA = new int[a.length];
        
        for (int m : a) {
            if (newA[m] == m) return m;
            newA[m] = m;
        }
        return -1;
    }

public static void main(String[] args) {
    int[] a = new int[]{2, 3, 5, 4, 3, 2, 6, 7};
    System.out.println(findDuplicateInArray(a));
}

3.二分法

将数组长度二等分,计算其中一半的数字在数组中出现的次数,如果次数大于一半说明这一半数字中必有重复。否则,另一半数字中出现重复数字。
举例:[2, 3, 5, 4, 3, 2, 6, 7]长度为8,分为1-4,5-8两部分。计算数字1,2,3,4在这个数组中出现的总次数为5次。所以1-4中必有重复,同理拆分为1-2和3-4。最后找出重复数字

public static Integer findDuplicateInArray2(int[] a){
        if(a == null || a.length <= 0) return -1;
        
        int start = 1;
        int end = a.length - 1;
        while(start <= end){
            int mid = start + (end - start) >> 2;
            int count = countRange(a, start, mid);
            if(end == start){
                if(count > 1) return start;
                else break;
            }
            if(count > mid - start + 1) end = mid;
            else start = mid + 1;
        }
        return -1;
    }

private static int countRange(int[] a, int start, int end) {
        int count = 0;
        if(a == null) return count;

        for (int value : a) {
            if (value >= start && value <= end) {
                ++count;
            }
        }
        return count;
       
        
    }

public static void main(String[] args) {
        int[] a = new int[]{2, 3, 5, 4, 3, 2, 6, 7};
        System.out.println(findDuplicateInArray2(a));
    }

countRange会被调用O(logn)次,每次是O(n),因此总的时间复杂度为O(nlogn)
这种二分法不能保证找到所有的重复数字:[2,3,5,4,3,2,6,7]中的2就不能找出来,因为1-2在数组中出现了两次。

发布了89 篇原创文章 · 获赞 13 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Dawn510/article/details/105086505