劍指OFFER(java)-數組中重複的數字

題目:在一個長度爲n的數組裏的全部數字都在 0 到 n-1 的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每一個數字重複了幾回。請找出數組中任意一個重複的數字。

 

題目分析

解決這個問題的一個簡單的方法是先把輸入的數組排序。從排序的數組中找出重複的數字時間很容易的事情,只須要從頭至尾掃描排序後的數組就能夠了。排序一個長度爲 n 的數組須要 O(nlogn)的時間。java

還能夠利用哈希表來解決這個問題。從頭至尾按順序掃描數組的每一個數,每掃描一個數字的時候,均可以用 O(1)的時間來判斷哈希表裏是否已經包含了該數字。若是哈希表裏尚未這個數字,就把它加入到哈希表裏。若是哈希表裏已經存在該數字了,那麼就找到一個 重複的數字。這個算法的時間複雜度是 O(n),但它提升時間效率是以一個大小爲 O(n)的哈希表爲代價的。咱們再看看有沒有空間複雜度爲 O(1)的算法。算法

咱們注意到數組中的數字都在 0 到 n-1 中。若是這個數組中沒有重複的數字,那麼當數組排序以後數字 i 將出如今下標爲 i 的位置。因爲數組中有重複的數字,有些位置可能存在多個數字,同時有些位置可能沒有數字。數組

如今讓咱們重排這個數組,依然從頭至尾一次掃描這個數組中的每一個數字。當掃描到下標爲 i 的數字時,首先比較這個數字(用 m 表示)是否是等於 i。若是是,接着掃描下一個數字。若是不是,再拿它和第 m 個數字進行比較。 若是它和第m個數字相等,就找到了一個重複的數字(該數字在下標爲 i 和 m 的位置都出現了)。若是它和第 m 個數字不相等,就把第 i 個數字和第 m 個數字交換,把 m 放到屬於它的位置。接下來再重讀這個比較、交換的過程,直到咱們發現一個重複的數字。spa

以數組{2,3,1,0,2,5,3}爲例來分析找到重複數字的步驟。數組的第 0 個數字(從 0 開始計數,和數組的下標保持一致)是 2,與它的下標不相等,因而把它和下標爲 2 的數字 1 交換。交換以後的數組是{1.3.2.0.2.5.3}。此時第 0 個數字是 1,仍然與它的下標不相等,繼續把它和下標爲 1 的數字 3 交換,獲得數組{3,1,2,0,2,5,3}.接下來繼續交換第 0 個數字 3 和第 3 個數字 0,獲得數組{0,1,2,3,2,5,3}。此時第 0 個數字的數值爲 0,接着掃描下一個數字。在接下來的幾個數字中,下標爲 1,2,3 的三個數字分別爲 1,2,3 它們的下標和數值都分別相等,所以不須要作任何操做。接下來掃描到下標爲 4 的數字 2。因爲它的數值與它的下標不相等,再比較它和下標爲 2 的數字。注意到此時數組中下標爲 2 的數字也是 2,也就是數字在下標爲 2 和下標爲 4 的兩個位置都出現了,所以找到一個重複的數字。.net

一、哈希實現:排序

package cglib;內存

import java.util.HashMap;
import java.util.Map;get

public class jiekou {io

    
     public int duplicate(int numbers[],int length,int [] duplication) {
            //異常處理
            if(numbers == null || numbers.length <= 1 || length <= 1)
                return -1;
            //建立一個HashMap保存每一個數字出現的次數
            Map<Integer,Integer> counter = new HashMap<Integer, Integer>();
            for(int i = 0; i < length; i++){
                if(counter.containsKey(numbers[i])){
                    //duplication[0] = numbers[i];
                    //return true;
                    return numbers[i];
                }else{
                    counter.put(numbers[i], new Integer(1));
                }
            }
            return -1;
        }class

        public static void main(String[] args) {
            int [] numbers = {4,4,1,0,1,5,3};
            int f = new jiekou().duplicate(numbers, 7, new int[1]);
            System.out.println(f);
        }
    }
   

 

輸出:4

 

下面的代碼中儘管有一兩個重複須要交換,但每一個數字最多隻要交換兩次就能找到屬於本身的位置,所以總的時間複雜度爲O(n),另外,全部的操做步驟都是在輸入數組上進行,不須要額外的分配內存,所以空間複雜度爲O(1).

 

package cglib;


public class jiekou {
    /**
     * 題目:在一個長度爲n的數組裏的全部數字都在0到n-1的範圍內。數組中某些數字是重複的,
     * 但不知道有幾個數字重複了,也不知道每一個數字重複了幾回。請找出數組中任意一個重複的數字。
     * 例如,若是輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是重複的數字2或者。
     *
     * @param number
     * @return
     */
    public static int duplicate(int[] number) {
        if (number == null || number.length < 1) {
            return -1;
        }
        // 判斷輸入的是否在[0, number.length-1]之間
        for (int i : number) {
            if (i < 0 || i >= number.length) {
                return -1;
            }
        }
    for (int i = 0; i < number.length; i++) {
        // 當number[i]與i不相同的時候一直交換
        while (number[i] != i) {
            // 若是i位置與number[i]位置的數字相同,說明有重複數字
            if (number[i] == number[number[i]]) {
                return number[i];
            }
            // 若是不一樣就交換
            else {
                swap(number, i, number[i]);
            }
        }
    }
    return -1;
}
private static void swap(int[] data, int x, int y) {
    int tmp = data[x];
    data[x] = data[y];
    data[y] = tmp;
}
public static void main(String[] args) {
    int[] numbers1 = {2, 1, 3, 1, 4};
    System.out.println(duplicate(numbers1));
    int[] numbers2 = {2, 4, 3, 1, 4};
    System.out.println(duplicate(numbers2));
    int[] numbers3 = {2, 4, 2, 1, 4};
    System.out.println(duplicate(numbers3));
    int[] numbers4 = {2, 1, 3, 0, 4,2,2};
    System.out.println(duplicate(numbers4));
    int[] numbers5 = {2, 1, 3, 5, 4};
    System.out.println(duplicate(numbers5));
}
    }

輸出:

1 4 2 2 -1

相關文章
相關標籤/搜索