算法演练一:数组中重复的数字

260 阅读2分钟

题目:在一个长度为n的数组里的所有数字都在0~n-1的范围内,找出对应重复的数字。例如输入长度为7的数组int[] m ={2,3,1,0,2,5,3},则重复的数字为2和3。

解法一:使用快排,排序后再去做比较找重复的数字,这个方法的时间复杂度为O(nlogn),这是最常见的方法,但是不是最快的办法。

解法二:这里有个重点,就是n属于[0, n-1],也就是说如果没有重复的数字,那么下标i和m[i]是相等的,这个很好理解吧。那么让我们重排数组,从头到尾扫描这个数组,过程如下:

  • 1.当扫描到下标i的数字m[i],把i和m[i]作比较,如果相等,则扫描下一个,否则进入第二步。
  • 2.将m[i]和第m[i]个数字进行比较,如果相等,这就是一个重复的数字,扫描下一个数字。否则进入第三步。
  • 3.将m[i]和第m[i]进行交换,继续仅从头开始新的一轮扫描,直到结束。

上面的过程实质上就是比较交换的过程。

这里给出java版本的代码,如下:

public class Main {

    static int[] a = {2, 3, 1, 0, 2, 5, 3};

    public static void main(String[] args){
        duplicate(a);
    }

    private static void duplicate(int[] numbers) {
        if (numbers == null || numbers.length == 0) return;
        for (int i = 0; i < numbers.length; i++){
            while (numbers[i] != i){//第一步
                if (numbers[i] == numbers[numbers[i]]){//第二步
                    System.out.println("重复的数字-----> " + numbers[i]);
                    break;
                }
                //第三步
                int tmp = numbers[i];
                numbers[i] = numbers[tmp];
                numbers[tmp] = tmp;
            }
        }
    }
}

代码中尽管有两重循环,但是最多进行了2次交换就找到了它的位置,因此总的时间复杂度为O(n),所有的操作都在同一个数组上,没有额外的分配内存空间,空间复杂度为O(1);