劍指offer--知識遷移問題

所謂知識遷移能力就是根據已經掌握的知識、技術、能夠迅速學習理解新的技術並運用到實際工作中去。這是作爲一名軟件開發工程師必備的能力

這裏簡單介紹幾組本科生招聘面試中易考到的考察知識遷移能力的問題

  1.    統計一個數字在排序數組中出現的次數。例{1,2,3,3,3,3,3,4,5,5,5,6} 輸出 重複的數字3出現的次數

思路:本題若使用二分查找找到重複的數字 再向前後探測出現的個數時間複雜度和遍歷數組的解法是一樣的 O(n)   不可取
            
           既然想到了二分查找重複的數字 當然也可以二分查找第一個和最後一個重複的數字出現的位置

           兩個函數分別找第一個和最後一個重複的數字 運用二分法 找到中間數後和 要找的數比較 不等於 在前/後半部找

            本題考查二分查找的知識遷移

//數字在排序數組中出現的次數
//求數字第一次出現的位置
int GetFirstK(int* data, int length, int k, int start, int end)
{
     if (start > end)
          return -1;
     int middleIndex = start + (end - start) / 2;//該求平均數的方法可以防止大小越界的問題
     int middleData = data[middleIndex];//找到中間的數字
     if (middleData == k){
          if ((middleIndex > 0 && data[middleIndex - 1] != k)\
              || middleIndex == 0)//如果中間的數字是第一個出現的該數字 或只有一個數字
              return middleIndex;//返回第一個該數字的位置
          else
              end = middleIndex - 1;//如果不是說明 還要在前半部分找第一個
     }
     else if (middleData > k)
          end = middleIndex - 1;
     else
          start = middleIndex + 1;
     return GetFirstK(data,length,k,start,end);
}
//同理求最後的位置
int GetLastK(int* data, int length, int k, int start, int end)
{
     if (start > end)
          return -1;
     int middleIndex = start + (end - start) / 2;
     int middleData = data[middleIndex];
     if (middleData == k){
          if ((middleIndex < length - 1 && data[middleIndex + 1] != k)\
              || middleIndex == length - 1)
              return middleIndex;
          else
              start = middleIndex + 1;
     }
     else if (middleData > k)
          end = middleIndex - 1;
     else
          start = middleIndex + 1;
}
//知道了該數字第一次和最後一次出現的位置 將兩個位置相減加一就是出現的個數
int GetNumberOfK(int* data, int length, int k)
{
     int number = 0;
     if (data != NULL && length > 0){//處理特殊情況
          int first = GetFirstK(data, length, k, 0, length - 1);
          int last = GetLastK(data, length, k, 0, length - 1);
          if (first >= 0 && last >= 0)
              number = last - first + 1;
     }
     return number;
}

  1.    一個數組裏除了兩個數字之外,其他數字都出現了偶數次。請找出這兩隻只出現了一次的數字。要求時間複雜度O(n) 空間複雜度O(1)

{2,4,3,6,3,2,5,5,}    輸出 4,6

如果本問題是數組中只出現了一個只出現一次的數字 就簡單了 。

大家知道 任何數對自己做按位異或運算是0 , 0按位異或任何數是任何數 把所有數字異或在一起最終的結果就是答案。

可是現在題目是有兩個只出現一次的數。

這裏知識遷移------    把一個數組分成邏輯上的兩部分 每部分包涵一個只出現一次的數

首先按位異或全部元素    最終結果是兩個孤獨的數異或的結果

之後 抓住該數是‘1’一位的  以這一位是一還是零 把數組分成邏輯上的兩組
//找數組中只出現一次的數字 數組中其他數字均出現了兩次 有兩個數字只出現了一次
//在整數num的二進制位中找最低的是1的位
unsigned int FindFirstBitIs1(int num)
{
     int indexBit = 0;
     while (((num & 1) == 0) && (indexBit < 8 * sizeof(int)))
     {
          num = num >> 1;
          ++indexBit;
     }
     return indexBit;
}
//查看 num的二進制表示中從右邊數起的indexBit位是不是1
bool IsBit1(int num, unsigned int indexBit)
{
     num = num >> indexBit;
     return (num & 1);
}
void FindNumsAppearOnce(int data[], int length)
{
     if (data == NULL || length < 2)
          return ;
     int result = 0;
     for (size_t i = 0; i < length; ++i)
          result ^= data[i];
     unsigned int indexOf1 = FindFirstBitIs1(result);
     int num1 = 0;
     int num2 = 0;
     for (int j = 0; j < length; ++j){
          if (IsBit1(data[j], indexOf1))
              num1 ^= data[j];
          else
              num2 ^= data[j];
     }
     cout << num1 << " " << num2 << endl;
}

  1.   旋轉句子問題  把流傳很廣的 反轉句子問題轉化成 先總體逆置後逐個單詞逆置

            左旋字符串問題 先逆置左右兩部分後逆置整體

//翻轉單詞順序   左旋字符串
//1.
void Reverse(char* pBegin, char* pEnd)
{
     if (pBegin == NULL || pEnd == NULL)
          return;
     while (pBegin < pEnd){
          char temp = *pBegin;
          *pBegin = *pEnd;
          *pEnd = temp;
          pBegin++;
          pEnd--;
     }
}
char* ReverseSentencs(char* pData)
{
     if (pData == NULL)
          return NULL;
     char* pBegin = pData;
     char* pEnd = pData;
     while (*pEnd != '\0'){
          ++pEnd;
     }
     --pEnd;
     Reverse(pBegin, pEnd);
     pBegin = pEnd = pData;
     while (*pBegin != '\0'){
          if (*pBegin == ' '){
              pBegin++;
              pEnd++;
          }//爲什麼*pBegin != ‘、0’要在最前面判斷
          //因爲否則下面三行代碼如果最先判斷會形成死循環
         else if (*pEnd == ' ' || *pEnd == '\0'){
              Reverse(pBegin, --pEnd);
              pBegin = ++pEnd;
          }
          else{
              pEnd++;
          }
     }
     return pData;
}
//2.
char* LeftRotateString(char* pStr, int n)
{
     if (pStr != NULL){
          int nLength = static_cast<int>(strlen(pStr));
          if (nLength > 0 && n > 0 && n < nLength){
              char* pStrFS = pStr;
              char* pStrFE = pStr + n - 1;
              char* pStrSS = pStr + n;
              char* pStrSE = pStr + nLength - 1;
              Reverse(pStrFS, pStrFE);
              Reverse(pStrSS, pStrSE);
              Reverse(pStr, pStrSE);
          }
     }
     return pStr;
}
4. 
  1. 找出數組中和爲s的兩個數    找出數組中何爲s的連續所有數字 將其全部組合輸出出來

        思路:
                無序的數組很難處理  首先想到的是對數組排序  要找到其中的兩個數字 先取兩個初始值  排序後的第一個(最小值) 和第一個(最大值)

                取到和之後 和s比較 大了最大值變成次大值 (倒數第二個數字) 小了最小值變成次小值 (第二個數字) 以此類推直到找到合適的兩個數

      遷移到第二個問題上  現在求的是合爲s的多個數字之和    還是先排序   之後初始定義第一二個數字爲數字序列   若序列的和小於s擴充下一個更大的
      
       數字爲序列的成員 若序列之和小於s除去序列中最小的(序列中位置前在第一個)數字爲數字序列成員  指直到找到合適的數字序列。
                
//和爲s的兩個數字 ------和爲s的連續正數序列
//1.找出數組中和爲s的兩個數字 找到了不同找其他的。
bool FindNumBerWithSum(int data[], int length, int sum, int* num1, int*num2)
{
       bool found = false;
       if (num1 == NULL || num2 == NULL || length < 1)
              return found;
       size_t begin = 0;
       size_t end = length - 1;
       while (begin < end){
              long long curSum = data[begin] + data[end];
              if (curSum == sum){
                     *num1 = data[begin];
                     *num2 = data[end];
                     found = true;
                     break;
              }
              else if (curSum < sum)
                     begin++;
              else
                     end--;
       }
       return found;
}
//----將此問題遷移到和爲s的連續整數序列
//輸入一個正數s,打印出所有和爲s的連續正數序列
void Print(int smal, int big)
{
       for (int i = smal;  i <= big; ++i)
              cout << i ;
       cout << endl;
}
void FindContinuousSequence(int sum)
{
       if (sum < 3)
              return;
       int smal = 1;
       int big = 2;
       int middle = (1 + sum) / 2;
       int curSum = smal + big;//請注意這裏curSum是動態變化的 最初是1+2
       while (smal < middle){
              if (curSum == sum)
                     Print(smal, big);
              while (smal < middle && curSum > sum){
                     curSum -= smal;
                     smal++;
                     if (curSum == sum)
                           Print(smal, big);
              }
              big++;
              curSum += big;
       }
}



發佈了47 篇原創文章 · 獲贊 21 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章