找出陣列中只出現了一次的數字(Java)
找出陣列中只出現了一次的數字
題目
給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個 元素均出現兩次。找出那個只出現了一次的元素。
- 示例
- 示例1:
輸入:[2,2,1] 輸出:1 - 示例2:
輸入:[4,1,2,1,2] 輸出:4
- 示例1:
題目分析
注意輸入格式,在主方法中不能直接得到陣列,應先定義字串變數接收鍵盤輸入,在使用String類的幾種方法得到這個整數陣列。
另外定義一個方法找只出現了一次的元素,這個方法返回值為int型別。在方法中,要找到那個元素,有以下解決方法:
方法1
最基本的方法就是雙重迴圈遍歷陣列,可以直接找出只出現了一次的數,但是,顯然這種方法時間複雜度高,效率低下
方法2
使用Set集合儲存,Set集合不儲存重複值,add()方法返回值為boolean型別,這一特點可以利用。
Set集合的add方法,新增成功返回true,否則返回false,而Set集合不儲存重複值,所以當要新增的數與集合中已存在的數重複時,不會再進行新增操作,返回false,這時再進行remove操作,將集合中已存在的那個與要新增的數相同的元素移除,這樣將作為方法引數傳遞過來的整型陣列遍歷完後,到最後集合中就只剩下了那個只出現了一次的數字。
方法3
使用異或運算子^,0與其他數字異或的結果是那個數字,相等的數字異或得0。要操作的陣列中除了某個數字只出現了一次之外,其他數字都出現了兩次,所以可以定義一個變數賦初始值為0,用這個變數與陣列中每個數字做異或運算,並將這個變數值更新為那個運算結果,直到陣列遍歷完畢,最後得到的變數的值就是陣列中只出現了一次的數字了。這種方法只需遍歷一次陣列,提高了程式執行的效率。
總結一下:這種解法要用到異或運算的幾個規則:
- 0^0 = 0;
- 0^a = a;
- a^a = 0;
- 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代表著沒有找到。