1. 程式人生 > >找出陣列中只出現了一次的數字(Java)

找出陣列中只出現了一次的數字(Java)

找出陣列中只出現了一次的數字

題目

給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個 元素均出現兩次。找出那個只出現了一次的元素。

  • 示例
    • 示例1:
      輸入:[2,2,1] 輸出:1
    • 示例2:
      輸入:[4,1,2,1,2] 輸出:4

題目分析

注意輸入格式,在主方法中不能直接得到陣列,應先定義字串變數接收鍵盤輸入,在使用String類的幾種方法得到這個整數陣列。
另外定義一個方法找只出現了一次的元素,這個方法返回值為int型別。在方法中,要找到那個元素,有以下解決方法:

方法1

最基本的方法就是雙重迴圈遍歷陣列,可以直接找出只出現了一次的數,但是,顯然這種方法時間複雜度高,效率低下

方法2

使用Set集合儲存,Set集合不儲存重複值,add()方法返回值為boolean型別,這一特點可以利用。
Set集合的add方法,新增成功返回true,否則返回false,而Set集合不儲存重複值,所以當要新增的數與集合中已存在的數重複時,不會再進行新增操作,返回false,這時再進行remove操作,將集合中已存在的那個與要新增的數相同的元素移除,這樣將作為方法引數傳遞過來的整型陣列遍歷完後,到最後集合中就只剩下了那個只出現了一次的數字。

方法3

使用異或運算子^,0與其他數字異或的結果是那個數字,相等的數字異或得0。要操作的陣列中除了某個數字只出現了一次之外,其他數字都出現了兩次,所以可以定義一個變數賦初始值為0,用這個變數與陣列中每個數字做異或運算,並將這個變數值更新為那個運算結果,直到陣列遍歷完畢,最後得到的變數的值就是陣列中只出現了一次的數字了。這種方法只需遍歷一次陣列,提高了程式執行的效率。

總結一下:這種解法要用到異或運算的幾個規則:

  1. 0^0 = 0;
  2. 0^a = a;
  3. a^a = 0;
  4. a ^ b ^ a = b.

當然這些只是一部分,不過剛好夠用,想要了解異或運算子的小夥伴可以去看看百度百科的介紹:異或運算

程式碼實現

主方法

public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String str = sc.nextLine();
		str = str.substring(1, str.length()-1);//去掉鍵盤輸入的字串中的"["和"]"
		String[
] s = str.split(",");//將字串以","分割,轉為一個字串陣列 int[] arr = new int[s.length];//定義一個整形陣列,準備用來存資料了 for(int i = 0; i < arr.length; i++) arr[i] = Integer.parseInt(s[i]);//將字串轉為int型別資料存入整型陣列中 System.out.println(Find_Num(arr));//調方法 }

注:這裡直接將字串轉為整型資料使用的是包裝類Integer類的parseInt()靜態方法,呼叫時直接類名呼叫,方法返回值為Integer型別,但是可以自動拆箱為int型,因此用int或Integer型別接收均可。
另外,注意一下主方法中沒有對方法返回值進行判斷而直接做了輸出,也就是說不考慮那個數字不出現的情況

方法1

第一種:使用計數器的

public static Integer Find_Num_1(int[] arr){
		for(int i = 0; i < arr.length; i++) {
			int count = 1;//計數器
			for(int j = 0; j < arr.length; j++) {
				if(i == j)
					continue;//下標相等則直接進入下一次迴圈
				if(arr[i] == arr[j])
					count++;//如果不同下標的陣列元素相等,則計數器加1
			}
			if(count == 1)
				return arr[i];//返回只出現了一次的元素
		}
		return null;//找不到則返回null
	}

第二種:不使用計數器

public static Integer Find_Num_1(int[] arr){
		for(int i = 0; i < arr.length; i++){
			for(int j = 0; j <= arr.length; j++){
			//注意迴圈結束的條件,實際執行時j不會超出陣列範圍
				if(i == j)
					continue;
				if(j == arr.length)
				//因為這裡的判斷,讓程式碼執行時在超出範圍前就結束了迴圈
					return arr[i];
				if(arr[i] == arr[j])
					break;
			}
		}
		return null;//找不到則返回null
	}

可能已經有人注意到,方法1中兩段程式碼方法的返回值為Integer型別,這樣就方便了在沒有找到時返回null作為標誌

方法2

public static int Find_Num_2(int[] arr) {
		Set<Integer> set = new HashSet<Integer>();//建立Set集合
		for(int i : arr) {//增強for迴圈遍歷陣列
			if(!set.add(i))//新增不成功返回false,前加上!運算子變為true
				set.remove(i);//移除集合中與這個要新增的數重複的元素
		}
		return set.toArray(new Integer[set.size()])[0];
		//返回它轉為Integer陣列後陣列的第一個元素
	}

使用時要注意前提,必須存在那個只出現了一次的數字,否則Set集合長度將為0,最後一句程式碼執行時出錯,改進一下的話,就像方法1那樣,將返回值型別設定為Integer:

public static Integer Find_Num_2(int[] arr) {
		Set<Integer> set = new HashSet<Integer>();//建立Set集合
		for(int i : arr) {//增強for迴圈遍歷陣列
			if(!set.add(i))//新增不成功返回false,前加上!運算子變為true
				set.remove(i);//移除集合中與這個要新增的數重複的元素
		}
		if(set.size() == 0)	return null;
		//如果Set集合長度為0,返回null表示沒找到
		return set.toArray(new Integer[set.size()])[0];
	}

方法3

public static int Find_Num_3(int[] arr) {
		int flag = 0;
		for(int i : arr) {
			flag ^= i;
		}
		return flag;
	}

這段程式碼的問題在於,如果使用者輸入了偶數個數的資料,方法將返回0值,而當這個只出現了一次的數字為0時,方法當然也返回0,所以使用前提是必須存在一個只出現了一次的數字

總結

需要注意的是: 這個題的前提條件是這個只出現了一次的數字是存在的,那麼使用時在輸入階段需要使用者注意到這個問題,輸入的資料個數只能是奇數,否則在使用方法3時會出現答案錯誤的情況,而方法1和改進後的方法2可以正常使用,如果輸出為null代表著沒有找到。