1. 程式人生 > 其它 >C++資料結構和演算法:位運算、字串

C++資料結構和演算法:位運算、字串

--------------------------------位運算---------------------------------

Q1. 用位運算交換兩個值

前提:要交換的兩個值是獨立記憶體

void Swap(int& a, int& b)
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

 Q2. 有一個數組,有些數字出現次數為偶數,只有一個數字出現次數為奇數,請在O(N)內找到這個數

解析:假設陣列為 { 2,1,3,1,2,1,3 } ,∵ N^N=0,N^0=N,∴ 0^2^1^3^1^2^1^3 =  2^1^3^1^2^1^3 = 0^1^3^1^1^3 = 0^1^1^1^0 = 0^1^1^1 = 0^0^1 = 0^1 = 1

偶數次的一定會累計異或為0,奇數次的一定會累計異或為本身,所以只要用0去累計異或所有數就行了,最後得到的就是這個奇數次數。

1 void PrintOddTimesNum(int arr[],int len)
2 {
3     int eor = 0;
4     for (int i=0;i<len;i++)
5     {
6         eor ^= arr[i];
7     }
8     cout << eor << endl;
9 }

Q4. 上一題條件改為出現奇數次的數字有兩個,要找出這兩個數

解析:如果按上一題方法,0跟所有數異或之後得到的是 a^b,那麼怎麼分離a和b呢?

∵ 異或運算偶數次的數會抵消為0,奇數次的數會抵消為本身

∴ 所有數累計異或,一定會得到奇數次的數之間異或,eor=a^b

∵ a≠b,異或運算相同位為0,不同位為1

∴ eor至少有一位為1,而且這一位,在a中為1,在b中為0,那麼就可以把陣列分為,這1位為1的數和這一位為0的數。

假設我們先找這一位為1的數,全部累計異或,偶數次的數會抵消為0,奇數次的數會抵消為本身,所以最後會得到這一位為1且奇數次的數;

先找這一位為0的數道理一樣。

假設找到的這個數是a ,∵ eor = a^b

∴ eor^a = a^b^a = 0^b = b,用a異或eor就能得到b。

 1 void PrintOddTimesNum(int
arr[],int len) 2 { 3 int eor = 0; 4 for (int i=0;i<len;i++) 5 { 6 eor ^= arr[i]; 7 } //EOR = a^b 8 9 //找最右1 10 //一個數&自己的補碼得到最右1 11 //補碼 = 取反進1 12 int rightOne = eor & (~eor + 1); 13 14 int a=0; 15 for (int i=0;i<len;i++) 16 { 17 if ((arr[i] & rightOne) == rightOne) //也可以是==0來判斷 18 a ^= arr[i]; 19 } 20 21 cout << "a=" << a << " b=" << (a ^ eor) << endl; 22 }

 

--------------------------------字串---------------------------------

—— KMP 字串匹配演算法 ——

核心:利用匹配失敗後的資訊,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。

時間複雜度:O(m+n)

第一步:構造next陣列

法則:值=公共前後綴的長度

eg.  abaa 

下標 比較元素 字首 字尾
0 a {}  {} 0
1 ab a b 0
2 aba a、ab a、ba 1
3 abaa a、ab、aba a、aa、baa 1

 第二步:匹配

比較到字尾不匹配位置,把模式串字首移動到原來的字尾位置;

 1 //計算next陣列
 2 int arr_next[1000];
 3 void BuildNext(const string pattern)
 4 {
 5     arr_next[0] = 0; // 第一個字元的最長相同字首字尾為0
 6     for (int i = 1, j = 0; i < pattern.length(); i++)
 7     {
 8         while (j && pattern[i] != pattern[j])
 9         {
10             j = arr_next[j - 1];//如果不相同,移動p,這裡如果j=0並且兩個字元還不相同,也就預設pmt[i] = 0了
11         }
12 
13         if (pattern[i] == pattern[j])
14         {
15             j++;
16             arr_next[i] = j;//如果相同,則得到該位置pmt[i]的值,繼續向後比較
17         }
18     }
19     for (int i = 0; i < pattern.length(); i++)
20         cout << arr_next[i] << " ";
21     cout << endl;
22 }
23 
24 int KMP(const string str, const string pattern)
25 {
26     for (int i = 0, j = 0; i < str.length(); i++)
27     {
28         while (j && str[i] != pattern[j])  j = arr_next[j - 1];
29         if (str[i] == pattern[j])
30         {
31             j++;   // 兩者相等,繼續匹配
32         }
33         if (j == pattern.length())
34         {
35             return i - j + 1;//匹配成功,返回下標
36 
37         }
38     }
39     return -1;// 未匹配成功,返回-1
40 }

—— 鍵索引計數法 字串排序演算法 ——

假設要給不同班級的學生按班級排序

1     struct student
2     {
3         int stuClass;
4         string stuName;
5     };
6     vector<student> students = { {2,"Anderson"},{3,"Brown"},{3,"Davis"},{4,"Garcia"},{1,"Harris"},{3,"Jackson"},{4,"Johnson"},{3,"Jones"},{1,"Martin"},{2,"Martinez"},{2,"Miller"},{1,"Moore"},{2,"Robinson"},{4,"Smith"},
7         {3,"Taylor"},{4,"Thomas"},{4,"Thompson"},{2,"White"},{3,"Williams"},{4,"Wilson"} };

準備一個用於接收排序後資料的容器

vector<student> aux = students; 

排序規模N,索引陣列大小R

const int N = students.size();
const int R = 5;  //班級數量+1

索引陣列

int count[R+1] = { 0 };   //+1防止溢位,用於儲存計算結果

===> 計算班級出現的頻率

for (int i = 0; i < N; i++)
    count[students[i].stuClass]++;

===> 將頻率轉化為索引    count[i] 表示 ≤ i 的總數

0 1 2 3 4
0 3 5 6 6
  3 5 6 6
    8 6 6
      14 6
        20
for (int r = 0; r < R; r++)
    count[r + 1] += count[r];

===> 將學生按班級分類

拆解
aux[count[students[i].stuClass - 1]++].stuClass = students[i].stuClass;
students[i].stuClass 代表 第 i 個學生所在班級
count[students[i].stuClass] 代表 小於等於 所在班級的人數
count[students[i].stuClass - 1] 代表 小於所在班級的人數
aux[count[students[i].stuClass - 1]++] 把當前學生放在 小於所在班級的人數 後面一個,再++ 表示小於所在班級的人數+1,這樣下次遇到同一班級就在這個學生後面1位了
    for (int i = 0; i < N; i++)
    {
        aux[count[students[i].stuClass - 1]].stuName = students[i].stuName;
        aux[count[students[i].stuClass - 1]++].stuClass = students[i].stuClass;
    }

===> 輸出排序後結果

    for (int i = 0; i < N; i++)
    {
        cout << "class: "<<aux[i].stuClass<<" name: "<<aux[i].stuName<< endl;
    }

 舉一反三:替換比較物件,可以實現按指定位排序

 其他演算法參考:https://www.cnblogs.com/mcomco/p/10366184.html