找出2n+1個數中不成對的那個(升級版)


上篇談到了用異或來解決,確實是個好方法,時間復雜度為o(n),比例一遍ok,空間復雜度為o(1),只占用一個空間足矣。現在把這個問題升級下:

(1)給出n個數,其中有且僅有一個出現了奇數次,其余的都出現了偶數次。用線性時間常數空間找出這個出現奇數次的數

(2)給定n個數,其中有且僅有兩個出現了奇數次,其余的都出現了偶數次。用線性時間常數空間找出這兩個出現奇數次的數

原理(原理不是很懂的,先看看上篇

  • 任何數和自己異或為0
  • 任何數和0異或為自己
  • 異或具有交換律

思路

(1)一個出現奇數次

  1. 出現偶數次的一異或為0了,對剩下的奇數次數不造成干擾
  2. 奇數次(2n+1)的前2n次一異或為0了,對剩下那個數不造成干擾
  3.  剩下的那個數就是結果

(2)兩個出現奇數次

  1. 常規的從頭到尾異或一遍,得到數肯定不為0,這個數是那兩個出現奇數次的數異或的結果
  2. 找出這個數中不為1的那個位pos(在這個位置處,兩個奇數次的數肯定不同——要是相同這個位也是0)
  3. 整個序列根據位pos的值分成兩組(0的一組,1的一組,這樣把出現偶數次的分到一組,無礙。出現奇數次的分到兩組,正好)
  4. 對着兩組,利用(1)的方法,解決

細節:如何找到一個二進制中第一個是"1"的位

參考代碼

int Judge(int val, int j)
{   int i;
  for(i=2; i!=j; i*=2)
    val /= 2;
  return val % 2;
}
#include <stdio.h> int main() { int val[6] = {4, 3, 1, 56, 3, 1}, i, j; int pos = val[0]; int left=0, right=0; //分別表示兩個奇數次數 for (i = 1; i < 6; i++) { pos ^= val[i]; //得出兩奇數次數的異或 } for (j = 2; ; j *= 2) { if (0 != pos % j) //找出開始為1的位置 break; } for (i = 0; i < 6; i++) //根據標志位,分別找出那個奇數次數 { if ( 0 == Judge(val[i], j)) left ^= val[i]; else right ^= val[i]; } printf("%d........%d\n", left, right); return 0; }

 

 另一寫法(記錄移動次數)

#include <iostream>
using namespace std;
void printOneNum(int *a, int size)
{
    if(size <= 0)
        return;
    int tmpsum = 0;
    for(int i = 0; i < size; ++i)
        tmpsum ^= a[i];
    cout << "hello:" << tmpsum << endl;
    int movestep = 0;
    while(tmpsum % 2 == 0)
    {
        ++movestep;
        tmpsum >>= 1;
    }
    cout << "movestep:" << movestep << endl;
    int left = 0, right = 0;
    for(int i = 0; i < size; ++i)
    {
        int tmp = a[i] << movestep;
        if(tmp % 2 == 0)
            left ^= a[i];
        else
            right ^= a[i];
    }
    cout << right << " " << left << endl;
}
int main()
{
    int a[] = {4, 3, 1, 5, 3, 1};
    int size = sizeof(a) / sizeof(*a);
    printOneNum(a, size);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM