面试一

代码鲁棒性 (输入参数检查等贯穿所有代码当中,不会特别强调

 

1赋值运算符函数

 

注意:

 

1. 返回值类型为该类型引用(链式赋值),返回*this

 

2. 传入参数常量引用

 

3. 释放自身已有空间

 

4. 判断是不是自身赋值

 

例:

[cpp] view plaincopy
MyString&MyString::operator=( const MyString& str ){  
  if ( this == &str ) 
      return *this;  
 
  delete[] m_pData;  
  m_pData = NULL:  
 
  m_pData = new char[strlen(str.m_pData) + 1];  
  strcpy( m_pData,str.m_pData );  
 
  return *this;  
}

 

不足:若内存不足,new 申请内存时抛出异常,则 m_pData 已经被 delete,此时其为一个空指针,非常容易导致程序奔溃。在赋值运算符函数内部抛异常,CMyString 实例不再保持有效的状态,违背异常安全性原则.

 

改进:1.  new  delete 2. 创建临时实例,交换临时实例和原来的实例:

[cpp] view plaincopy
MyString&MyString::operator=( const MyString& str ){  
  if ( &str!= this ){  
      MyString strTemp( str );  
 
      char* pTemp = strTemp.m_pData;  
      strTemp.m_pData = m_pData;  
      m_pData = pTemp;  
  }  
 
  return *this;  
}

 

3. 二维数组中的查找:二维数组,每一行,从左到右递增。每一列,从上到下递增。输入一个二维数组和一个整数,判断数组中是否含有该整数

 

思路:选取数组右上角数字,等于->结束。大于->剔除这一列。小于->剔除这一行。这样每比较一次剔除一列或一行,缩小范围,直到找到要查找的数字 or 查找范围为空

 

例:

[cpp] viewplain copy
bool Findint* matrix, int rows, int cols, int number ){ 
  bool found = false;  
 
  if ( NULL != matrix&& rows > 0 && cols > 0 ){  
      int row = 0;  
      int col = cols - 1;  
 
      while ( row < rows && col >= 0 ){  
          if ( number == matrix[row * cols + col]){  
              found = true;  
              return found;  
          }  
          else if ( matrix[row * cols + col] >number )  
              --col;  
          else  
              ++row;  
      }  
  }  
 
  return found;  
}

 

二维数组在内存中占据连续的空间。在内存中从上到下存储各行元素,每行从左到右顺序存储

 

4. 替换空格

 

1. 在原串上操作 --> 保证原串后面有足够的空间 --> 从后往前替换

 

2.开辟新空间 --> 直接替换

 

两种思路都需遍历原串一次获取空格个数 --> O(N)

 

相关题目:两个排序数组 A1A2A1 后有足够空间容纳 A2,将 A2 中数字插入 A1 中使所有数字依旧有序

 

思路:遍历两个数组各一次得到两数组长度,三个指针,一个指针指向lengthA1+lengthA2-1,其余两个分别指向 A1  A2 末尾。依次比较,将合适数字放到第一个指针所指位置

 

合并两个数组 (字符串时,如果从前往后赋值每个数字 or 字符需要重复移动数字 or 字符多次 --> 从后往前复制,减少移动次数,提高效率

 

5. 从尾到头打印链表

 

1. 

 

2. 递归

 

7. 用两个栈实现队列

 

思路:栈 S1  S2,入「队列」入栈 S1。出「队列」,若 S2 有元素 S2.pop( ),否则,将 S1 中元素压入栈 S2

 

相关题目:两个队列实现栈

 

8. 旋转数组中最小的数字 --> 把数组最开始的若干个元素搬到数组的末尾(旋转数组),输入一个递增数组的旋转,输出最小数字

 

思路:数组旋转后可将其视为两个排序的子数组,并且前面子数组元素都大于等于后面子数组元素,最小的元素是第二个子数组的第一个元素 (我们要找最小的元素) --> 二分 (O(logn))(每移动一次指针,查找范围缩小为原来的一半) --> p1 指向数组第一个元素,p2 指向数组最后一个元素。如果中间元素大于 p1 指向数据,即中间元素在第一个子数组中,将 p1 指向这。否则如果中间元素小于 p2 指向元素,将 p2 指向中间元素。两个指针慢慢靠近直到第一个指针指向第一个子数组最后一个元素,第二个指针指向第二个子数组第一个元素,两指针相邻,此时第二个指针指向数组中最小的元素。循环结束

 

特殊情况:

 

1. 如果移动元素个数为 0,此时第一个元素就是最小的元素,代码中我们将 indexMid 初始化为 index1,如果第一个数字小于最后一个数字,直接返回。

 

2. {1, 0, 1, 1,1}  {1, 1, 1, 0, 1} 这种两个指针指向的数字及它们中间数字三者相同的时候,无法判断中间数字位于哪个子数组中,也就无法移动指针来缩小查找的范围,此时 --> 顺序查找:

 

[cpp] viewplain copy
int Minint* arr, int len ){  
  if (NULL == arr || len<= 0
      throw new std::exception( "Invalid parameters" );  
 
  int index1= 0;  
  int index2 = len- 1;  
  int indexMid =index1;  
 
  while (arr[index1]>= arr[index2]){  
      if (1 == index2 -index1)  
          return arr[index2];  
 
      indexMid = (index1 + index2) / 2;  
 
      // 顺序查找  
      if (arr[index1]== arr[index2] && arr[index1] == arr[indexMid])  
          return MinInOrder( arr, index1, index2 ); 
 
      if (arr[indexMid]>= arr[index1])  
          index1 = indexMid;  
      else if (arr[indexMid]<= arr[index2])  
          index2 = indexMid;  
  }  

 
int MinInOrderint* arr, int index1, int index2 ){ 
  int result =arr[index1];  
 
  for (int i = index1+ 1; i <= index2;++i)  
      if (arr[i]< result)  
          result = arr[i];  
 
  return result; 
}

 

9. 斐波那契数列

 

n f(n)

 

0 0

 

1 1

 

>1f(n-1)+fr(n-2)

 

 

1. 递归

 

2. 循环:

 

[cpp] viewplain copy
long long Fibonacciunsigned n ){  
  int result[2] = {01};  
  if (n < 2)  
      return result[n];  
 
  long long fibOne= 0;  
  long long fibTwo= 1;  
  long long fibN = 0;  
 
  // 注意这里是 i<=n, 而不是i<n  
  for (unsigned int i = 2; i <= n; ++i){ 
      fibN = fibOne + fibTwo;  
 
      fibOne = fibTwo;  
      fibTwo = fibN;  
  }  
 
  return fibN;  
}

 

相关题目:

 

1. 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法?

 n = 1, 1 种方法 (一次 1 级台阶

 n = 2, 2 种方法 ( 两次 1 级,或者一次 2  ) 

前两种为基准情况 

 

 n = 3, 3( 三次 1 级,或一次 1 级一次 2  (顺序不同,两个方法) ) 

我们通过台阶数为 3 来分析。青蛙一次只能跳一级或者两级 

也就是说青蛙跳上三级台阶最后一跳只有两种情况,跳一级或者跳两级,所以青蛙跳三级台阶总的方法数位:青蛙跳至只剩一级台阶和只剩两级台阶的方法数之和 

 F(n) = F(n-1) + F(n-2) 

或者,我们多写几种情况,也可以发现规律,方法数为前一次方法数 + 后一次方法数:

 

[cpp] view plaincopy
intjumpFloor( int number )  {    
  if (number <= 0)    
      return 0;   
  else if (1 == number)    
      return 1;   
  else if (2 == number)    
      return 2;   
          
  return jumpFloor( number-2 ) +jumpFloor( number-1 );   
}

 

3. 我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n  2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,总共有多少种方法?

思路对于 n>=3 的情况,不管前面矩形是怎样覆盖的。我们只考虑最后一次怎么覆盖。

最后一次只有两种覆盖方式:1. 用一个小矩形竖着覆盖。2. 用两个小矩形横着覆盖。

所以总的方法数无外乎 --> 你用各种方法覆盖到只剩 1 个再竖着覆盖或者你用各种方法覆盖到只剩两个再横着覆盖

:总的方法数 F(n) = n-1 次的方法数 F(n-1)(接着用一个小矩形竖着覆盖) + n-2 次的方法数 F(n-2)(接着用两个小矩形横着覆盖)

[cpp] view plaincopy
intrectCover( int number ) {  
  if ( number <= 0 )  
      return 0
        
  if ( 1 == number )  
      return 1
        
  if ( 2 == number )  
      return 2
    
  return rectCover( number-1 ) +rectCover( number-2 ); 
}

 

10. 二进制中 1 的个数输入一个整数,输出该整数二进制表示中 1 的个数

思路:一个整数减去 1 再和原整数做与行算,会把该整数二进制形式最右边一个 1 变为 0. 那么二进制表示有多少个 1 就可以进行多少次这样的操作。

 

:

[cpp] view plaincopy
int NumberOf1int n ){ 
  int count = 0;  
 
  while (0 != n){  
      ++count;  
      n = (n - 1) & n;  
  }  
 
  return count;  
}

 

常规思路:将 1 依次左移判断数字每一位是否是 1(注意不能将数字右移 1 进行&运算,因为负数右移补符号位,数字最终变为0XFFFFFFFF,死循环)

 

[cpp] viewplain copy
int NumberOf1int n ){  
  int count = 0;  
  unsigned int flag = 1;  
 
  while (0 != flag){ 
      if (0 != (flag& n))  
          ++count;  
 
      flag = flag << 1;  
  }  
 
  return count;  
}

 

相关题目:

 

1. 用一条语句判断一个整数是不是 2 的整数次方

 

思路:如果一个整数是 2 的整数次方,则它二进制表示中只有一位为 1,把这个整数减去 1 和它自己做与运算,这个整数中唯一的 1 就会变为 0

 

2.输入两个整数 mn。计算需要改变 m 的二进制表示中多少位才能得到 n

 

思路:第一步 m  n 异或,第二部求异或结果中 1 的位数

 

13.  O(1) 时间删除链表结点

 

思路:1. 如果链表只有一个结点 (头节点),直接删除 2. 如果要删除结点为尾结点,则用常规方法 (O(N)) 3. 删除多个节点的非尾结点 (将下一个节点 value 值赋给被删结点,删除下个节点)

 

14. 调整数组顺序使奇数位于偶数前面

 

两个指针 p1p2,一个从前往后遍历,一个从后往前遍历

 

[cpp] viewplain copy
voidReorder( int* arr, unsigned int lenbool (*func)(int) ){  
  if (NULL == arr|| len == 0
      return;  
 
  int* pBegin = arr; 
  int* pEnd = arr+ len - 1;  
 
  while (pBegin < pEnd){  
      while (pBegin < pEnd && !func(*pBegin))  
          ++pBegin;  
 
      while (pBegin < pEnd && func(*pEnd))  
          --pEnd;  
 
      if (pBegin< pEnd){  
          int temp = *pBegin;  
          *pBegin = *pEnd;  
          *pEnd = temp;  
      }  
  }  

 
bool IsEven( int n ){  
  return 0 == ( n& 1 ); 

 
voidReorderOddEven( int* arr, unsigned int len ){  
  Reorder( arr, len, IsEven );  
}

 

15. 链表倒数第 k 个结点

想象有一把长度为 k 的尺子放在链表上,两个指针 p1(尺子头),p2(尺子尾)。当 p2 到达链表尾部时 p1 正好指向倒数第 k 个结点

 

注意这个题重点考虑鲁棒性

 

相关题目:

 

1. 求链表中间结点

 

思路:两个指针,p1p2p1 一次走一步,p2 一次走两步。当 p2 指向链表最后一个结点时,p1 正好指向链表中间节点

 

2. 判断链表是否带环

 

思路:快慢指针,相遇->带环。快指针到链表尾->不带环

 

当我们用一个指针遍历链表不能解决问题时,可以尝试两个指针来遍历链表。可以让其中一个指针走的快些 (如一次走两步),或者让它先在链表上走若干步

 

16. 反转链表

 

:

[cpp] view plaincopy
ListNode*ReverseList( ListNode* pHead )     
{   
  // 链表为空和链表只有一个结点在这个语句进行处理  
  if (NULL == pHead|| NULL == pHead->next)    
      return pHead;    
      
  ListNode* pPrev = NULL;    
  ListNode* pCur = pHead;    
  ListNode* pReverseHead = NULL;    
          
  while (NULL != pCur){   
      ListNode* pNext = pCur->next;    
 
      if (NULL == pNext) 
          pReverseHead = pCur;  
 
      pCur->next = pPrev;  
 
      pPrev = pCur;    
      pCur = pNext;    
  }    
      
  return pReverseHead;   
}

 

17. 合并两个排序的链表

[cpp] view plaincopy
ListNode*Merge( ListNode* pHead1, ListNoe* pHead2 )  {    
  if (NULL == pHead1)   
      return pHead2;    
  else if (NULL == pHead2)   
      return pHead1;    
  
  ListNode* pMergedHead = NULL;    
  
  if (pHead1->val< pHead2->val){    
      pMergeHead = pHead1;    
      pMergeHead->next = Merge( pHead1->next, pHead2 );   
  }    
  else{    
      pMergeHead = pHead2;    
      pMergeHead->next = Merge( pHead1, pHead2->next );   
  }    
  
  return pMergedHead;   
}

 

21. 包含 min 函数的栈定义一个可以使 min, push, pop 都为 O(1) 的栈

思路:借用辅助栈,每次数据栈元素入栈时,将所有入栈元素中最小值入辅助栈 (保证辅助栈和数据栈->同入同出)

 

[cpp] view plain copy
void Push( const int&data ){    
    dataS.push( data );    
 
if (0 == minS.size() || data < minS.top( ))  
  minS.push( data );  
else  
  minS.push( minS.top( ) ); 
 }   
 
 void Pop( ){  
assert( dataS.size( )> 0 &&minS.size( ) > 0 );  
 
dataS.pop( );  
minS.pop( );  
 }   
 
 const T& Min( ) const {  
assert( dataS.size( )> 0 &&minS.size( ) > 0 );  
 
return minS.top( );  
 }

 

23. 从上往下打印二叉树 (层序遍历二叉树)

借助队列

 

[cpp] view plaincopy
voidLevelPrint( Node* pRoot ){    
  if (NULL == pRtoot)   
      return;  
  
  queue<Node*> queueNode;    
  queueNode.push( pRoot );    
  
  while (0 !=queueNode.size( ))  {    
      Node* pNode = queueNode.front( );    
      queueNode.pop( );  
  
      Visit( pNode );    
  
      if (NULL !=pNode->pLeft)    
          queueNode.push(pNode->pLeft);   
  
      if (NULL !=pNode->pRight)    
          queueNode.push(pNode->pRight);   
  }    
}

 

29. 数组中出现次数超过一半的数字

 

思路:如果有一个数字出现次数超过数组元素总个数的一半,则它出现次数比其他数字出现的次数总和还要多。

 

:

[cpp] viewplain copy
bool g_inputInvalid= false;  
 
 
bool CheckInvalidArr( int* arr, int len ){  
  g_inputInvalid = false;  
 
  if (NULL == arr|| len <= 0)  
      g_inputInvalid = true;  
 
  return g_inputInvalid; 

 
 
bool CheckMoreThanHalf( int* arr, int lenint number ){ 
  int times = 0;  
 
  for (int i = 0; i < len; ++i)  
      if (number== arr[i])  
          ++times;  
 
  bool isMoreThanHalf= true;  
  if (times* 2 <= len){  
      g_inputInvalid = true;  
      isMoreThanHalf = false;  
  }  
 
  return isMoreThanHalf; 

 
 
int MoreThanHalfNum( int* arr, int len ){  
  // 如果只返回0存在二义性,是无效参数返回0还是0出现次数超过一半呢?  
  // 定义一个函数和 一个全局变量,即可区分这两种情况  
  if (CheckInvalidArray(arr, len ))  
      return 0
 
  int result = arr[0];  
  int times = 1;  
 
  for (int i = 1; i < len; ++i){  
      if (0 == times){ 
          result = arr[i];  
          times = 1;  
      }  
      else if (result== arr[i])  
          ++times;  
      else  
          --times;  
  }  
    
  // 此时result中保存的数字并不一定是数组中出现次数超过数组长度一半的数字,需要验证 
  // 11234  
  if (!CheckMoreThanHalf(arr, len, result ))  
      result = 0;  
 
  return result; 
}

 

37. 两个链表的第一个公共结点

思路:

 

1. 借助栈,两个链表都从后往前遍历,直到找到最后一个相同结点

 

2. 遍历两个链表一次,得到链表长度,len1len2。假设 len1  5len2  4。则在链表 1 上先走 (len1 - len2) 步,接着同时开始走,第一个相同节点

 

38. 数字在排序数组中出现的次数

 

思路:

 

因为是排序数组,找到第一个 k 和最后一个 k 即可知道 k 出现的次数 O(log n) 二分

 

[cpp] viewplain copy
int GetFirstK( int* arr, int lenint k, int start, int end ){  
  // 这段区间内没找到k, 返回-1  
  if (start >end)  
      return -1
 
  int midIndex =(start + end) / 2;  
  int midData =arr[midIndex];  
 
  if (k ==midData){  
      if ((midIndex> 0 &&k != arr[midIndex - 1])  
          || 0 == midIndex)  
          return midIndex;  
      else  
          end = midIndex - 1;  
  }  
  else if (midData >k)  
      end = midIndex - 1;  
  else  
      start = midIndex + 1;  
 
  return GetFirstK(arr, len, k, start, end );  

 
 
int GetLastK( int* arr, int lenint k, int start, int end ){  
  if (start >end)  
      return -1
 
  int midIndex =(start + end) / 2;  
  int midData =arr[midIndex];  
 
  if (k ==midData){  
      if ((midIndex< len - 1 &&k != arr[midIndex + 1])  
          || len - 1 == midIndex)  
          return midIndex;  
      else  
          start = midIndex + 1;  
  }  
  else if (midData >k)  
      end = midIndex - 1;  
  else  
      start = midIndex + 1;  
 
  return GetLastK(arr, len, k, start, end );  

 
 
int GetTimesOfK( int* arr, int lenint k ){  
  int times = 0;  
 
  if (NULL != arr&& len > 0 ){  
      int first = GetFirstK( arr, len, k, 0len - 1 );  
      int last = GetLastK( arr, len, k, 0len - 1 );  
 
      if (first> -1 &&last > -1
          times = last - first + 1;  
  }  
 
  return times;  
}

 

39. 二叉树的深度

:

 

[cpp] viewplain copy
int TreeDepth( BinaryTreeNode*pRoot ){  
  if (NULL == pRoot) 
      return 0
     
  int depthLeft =TreeDepth( pRoot->left );  
  int depthRight =TreeDepth( pRoot->right );  
 
  return (depthleft> depthRight) ? (depthLeft + 1) : (depthRight + 1);  
}

 

相关题目:判断一棵树是不是平衡二叉树。左右子树的深度相差不超过 1.

1. 需要重复遍历多次的解法

 

[cpp] viewplain copy
bool IsBalanced( BinaryTreeNode*pRoot ){  
  if (NULL == pRoot) 
      return true;  
     
  int depthLeft =TreeDepth( pRoot->left );  
  int depthRight =TreeDepth( pRoot->right );  
  int diff = left -right;  
 
  if (diff> 1 ||diff < -1
      return false;  
 
  return IsBalanced(pRoot->left ) && IsBalanced( pRoot->right );  
}

 

后序遍历,每个节点只遍历一次

 

 

[cpp] viewplain copy
bool IsBalanced( BinaryTreeNode*pRoot, int* pDepth ){  
  if (NULL == pRoot){ 
      *pDepth = 0;  
      return true;  
  }  
 
  int left;  
  int right;  
 
  if (IsBalanced(pRoot->left, &left ) && IsBalanced( pRoot->tight, &right)){  
      int diff = left - right;  
      if (diff>= -1 &&diff <= 1){ 
          *pDepth = left > right ? (left + 1) : (right + 1);  
          return true;  
      }  
  }  
 
  return false;  

 
 
bool IsBlanced( BinaryTreeNode*pRoot ){  
  int depth = 0;  
    
  return IsBlanced(pRoot, &depth );  
}

 

42. 翻转单词顺序 vs 左旋转字符串

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。标点符号和普通字母一样处理。--> "Iam a student." 变为 "student. a am I"

 

思路:

 

1. 翻转句子中所有的字符。此时不但翻转了句子中单词的顺序,连单词内的字符的顺序也翻转了

 

2. 翻转每个单词中字符的顺序

 

[cpp] viewplain copy
void Reversechar* pBegin, char* pEnd ){  
  if (NULL == pBegin|| NULL == pEnd)  
      return;  
 
  while (pBegin <pEnd){  
      char temp = *pBegin;  
      *pBegin = *pEnd;  
      *pEnd = temp;  
 
      ++pBegin;  
      --pEnd;  
  }  

 
 
charReverseSentencechar* pData ){  
  if (NULL == pData) 
      return NULL;  
 
  char* pBegin = pData; 
  char* pEnd = pData; 
 
  while ('\0' != *pEnd) 
      ++pEnd;  
 
  --pEnd;  
 
  // 翻转整个句子  
  Reverse( pBegin, pEnd );  
 
  // 翻转句子中每个单词  
  pBegin = pEnd = pData;  
  while ('\0' != *pBegin){ 
      if (' ' == *pBegin){ 
          ++pBegin;  
          ++pEnd;  
      }  
      else if (' ' == *pEnd|| '\0' == *pEnd){  
          Reverse( pBegin, --pEnd );  
          pBegin = ++pEnd;  
      }  
      else  
          ++pEnd;  
  }  
 
  return pData;  
}

 

相关题目:字符串左旋/右旋是把字符串前面的若干个字符转移到字符串的尾部。「abcdefg」和数字 2 --> "cdefgab"

思路:将要左旋的字符串和剩余字符串分别翻转,再翻转整个字符串

 

[cpp] viewplain copy
charLeftRotateStringchar* pStr, int n ){  
  if (NULL != pStr){ 
      int nLength = strlen( pStr );  
      if (nLength> 0 &&n > 0 &&n < nLength){  
          char* pFirstStart = pStr;  
          char* pFirstEnd = pStr + n - 1;  
          char* pSecondStart = pStr + n;  
          char* pSecondEnd = pStr + nLength - 1;  
 
          Reverse( pFirstStart, pFirstEnd );  
          Reverse( pSecondStart, pSecondEnd );  
          Reverse( pFirstStart, pSecondEnd );  
      }  
  }  
}

 

50. 树中两个结点的最低公共祖先

1. 二叉搜索树

 

[cpp] view plaincopy
BSTNode*GetLastCommonNode( BSTNode* pRoot, BSTNode* pNode1, BSTNode* pNode2 ){  
  if (NULL == pRoot) 
      return NULL;  
  else if (NULL == pNode1) 
      return pNode2;  
  else if (NULL == pNode2) 
      return pNode1;  
  else if (pNode1 ==pNode2)  
      return pNode1;  
 
  int val = pRoot->val;  
  int val1 = pNode1->val;  
  int val2 = pNode2->val;  
 
  if (val1 <val && val2 < val)  
      // 注意,递归时,有返回值,调用函数一定要加 return。 无返回值则可以只递归调用,做你想做的事情,不用加return  
      return GetLastCommonNode( pRoot->left, pNode1, pNode2 );  
  else if (val1 >val && val2 > val)  
      return GetLastCommonNode( pRoot->right, pNode1, pNode2 );  
  else{  
      if (val1== val)  
          return pNode1;  
      else if (val2== va1)  
          return pNode2;  
      else  
          return pRoot;  
  }  
}

 

2. 普通树,含指向父节点指针

 

[cpp] viewplain copy
BTreeNode* FindFirstCommonNode( TreeNode* pNode1,TreeNode* pNode2 ){  
  if (NULL == pNode1) 
      return pNode2;  
  else if (NULL == pNode2) 
      return pNode1;  
 
  stack<TreeNode*>s1;  
  stack<TreeNode*>s2;  
 
  // 注意:入栈第一个结点分别是这两个结点,而不是它们的父结点  
  while (NULL != pNode1){ 
      s1.push( pNode1 );  
      pNode1 = pNode1->parent;  
  }  
 
  while (NULL != pNode2){ 
      s2.push( pNode2 );  
      pNode2 = pNode2->parent;  
  }  
 
  TreeNode* firstCommonNode = NULL;  
 
  // 如果两个结点没在同一颗二叉树中,返回NULL  
  if (s1.top( ) !=s2.top( ))  
      return NULL;  
 
  while (0 != s1.size( )&& 0 !=s2.size( )){  
      if (s1.top() == s2.top( )){  
          firstCommonNode = s1.top( );  
 
          s1.pop( );  
          s2.pop( );  
      }  
      else  
          break;  
  }  
 
  return firstCommonNode; 
}

 

56. 链表中环的入口结点

思路:定义指针 P1P2P1 在链表上走环大小步后 P2 开始以相同速度遍历链表,两个指针相遇点即为链表中环的入口结点

 

:

[cpp] view plaincopy
// 首先判断单链表是否带环,若带环(此时快慢指针已经相遇),从此刻起计步,直到下次两个指针再相遇,快指针步数减去慢指针步数,即为环的长度  
 ListNode* EntryNodeOfLoop( ListNode* pHead )    
 {    
     if (NULL == pHead)   
         return NULL;    
  
     // 定义快慢指针判断是否有环    
     ListNode* pFast = pHead;    
     ListNode* pSlow = pHead;    
  
     // 如果有环,通过这两个变量求得环长度    
     int fastLength = 0;    
     int slowLength = 0;    
     int loopLength = 0;    
         
     // 求出环长度后通过这两个结点求出环入口节点    
     ListNode* p1 = pHead;    
     ListNode* p2 = pHead;    
        
     // 设置个死循环,如果链表不带环,我们在循环里直接退出即可   
     while ( 1 ){   
         // 在两个指针走之前必须判断pFast和pFast的next是否为空,两个条件都必须判断而且判断顺序不能出错    
         // 因为快指针走得快,我们只需要判断快指针而不需要判断慢指针。如果为空,即链表不带环,返回NULL  
         // 检测是否带环同时也避免指针越界访问   
         if (NULL == pFast || NULL == pFast->next)    
             return NULL;    
             
         pFast = pFast->next->next;    
         pSlow = pSlow->next;    
           
         // 此时我们再判断两个指针是否相遇,不能在指针还没走的时候判断,因为最开始,两个指针就是相遇的    
         if (pFast == pSlow)    
             break;  
     }    
         
     // 程序运行到此处,两个指针已经相遇,此时我们求环的长度    
     while ( 1 ){   
         pFast = pFast->next->next;    
         fastLength += 2;    
             
         pSlow = pSlow->next;    
         ++slowLength;    
             
         if (pFast == pSlow)    
             break;    
     }    
         
     loopLength = fastLength - slowLength;    
     // 环长度已知,此时我们来找环的入口结点    
     while ( 1 ){   
         // p1先走环的长度步 loopLength   
         for ( int i = 0; i < loopLength; ++i )    
             p1 = p1->next;    
             
         // 因为这个带环链表入口结点有可能就是第一个结点,所以如果此时p1走了环的长度步后回到了原点(即回到环的入口处(链表的第一个结点))与p2相遇了,则直接返回p1。即为环入口结点    
         if ( p1 == p2 )    
             return p1;    
             
         //否则p1 p2以相同速度开始走    
         while ( 1 ){    
             p1 = p1->next;    
             p2 = p2->next;    
                 
             //同样的,先走后判断    
             //此时即找到了环的入口结点    
             if ( p1 == p2 )    
                 return p1;   
         }    
     }    
 }

猜你喜欢

转载自blog.csdn.net/fujibao/article/details/79875475