剑指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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章