[关闭]
@qiezhian 2014-07-08T14:58:09.000000Z 字数 11788 阅读 1570

面试题选(2)

算法


1.判断两个链表是否相交(未完成)

题目: 给出两个单链表的头指针,判断两个链表是否相交,并求出第一个相交节点。
分析: 假设两个链表长度为N和M,暴力解法是两链表节点一一比对,时间复杂度Θ(NM),当N和M很大时,则需要亚二次解法。注意到这样一个事实:如果两链表有相交,那么从第一个相交节点之后,两链表完全相同。设链表相交部分长度为K,则首先遍历两链表得到各自长度N和M,假设N>=M,则第一个相交节点一定在后M个节点中。且第一个相交节点距尾节点距离也一样长,所以将长链表指针先指到第(N-M)个节点(首节点设为0节点)与短链表头指针对齐,然后两链表指针可同时走一步并比较,时间复杂度为Θ(N+M)
扩展:当链表有环怎么办?

2.判断整数序列是否为二叉树的后序遍历

题目: 输入一个整数序列,判断其是否为某二叉树的后序遍历结果(此后续遍历可能为树的某一个子树)。
分析: 利用一个事实:根节点一定在后续遍历的最后,且根节点左子树在前部分,右子树紧跟左子树在后部分,最后一个是根节点。所以给定一个序列,要判断是否为以root为根节点的后序遍历,经两步:1.根据后序遍历最后元素,在二叉树中找到此后序遍历的根节点r,如果不在二叉树中,则返回false;2.若找到根节点r,则可将后序遍历序列分为三部分:r的左子树,r的右子树,r。左子树与右子树的分界点即r的左儿子。3.对左子树和右子树递归判断。

  1. /*
  2. Filename: IsLRD.cpp
  3. Function:
  4. bool IsLRD(IsLRD( int A[], int length, Node* root )
  5. 输入一个整数序列,判断其是否为某二叉树的
  6. 后序遍历结果(此后续遍历可能为树的某一个子树)
  7. Details: 递归实现
  8. */
  9. #include <iostream>
  10. using namespace std;
  11. struct Node
  12. {
  13. int m_nElement;
  14. Node* m_pLeft;
  15. Node* m_pRight;
  16. };
  17. /**********************主要函数************************/
  18. bool IsLRD( int A[], int length, Node* root );
  19. bool IsLRD_Core( int A[], int length, Node* pTree );
  20. /*****************************************************/
  21. /*
  22. 在以root为根节点的二叉树中,找到值为element的节点
  23. */
  24. Node* FindInTree( Node* root, int element );
  25. /*
  26. 在长度为length的数组A中找到值element的位置(0起点)
  27. */
  28. int FindInArray( int A[], int length, int element );
  29. /*
  30. 创建一棵树,根节点的左子树只有左儿子,右子树只有右儿子
  31. *
  32. / \
  33. * *
  34. / \
  35. * *
  36. */
  37. Node* create_tree( int n );
  38. /*
  39. 创建一个值为i的节点
  40. */
  41. Node* create_node( int i );
  42. int main()
  43. {
  44. int A[] = { 5,3,1,6,4,2,0};
  45. int length = sizeof(A)/sizeof(int);
  46. Node *root;
  47. root = create_tree( 7 );
  48. if( IsLRD( A, length, root ) )
  49. cout<<"right"<<endl;
  50. else
  51. cout<<"wrong"<<endl;
  52. }
  53. bool IsLRD( int A[], int length, Node* root )
  54. {
  55. if( root==NULL || A==NULL || length<=0 )
  56. return false;
  57. Node *pTree = NULL;
  58. pTree = FindInTree( root, A[length-1] );
  59. if( pTree==NULL )
  60. return false;
  61. return IsLRD_Core( A, length, pTree );
  62. }
  63. bool IsLRD_Core( int A[], int length, Node* pTree )
  64. {
  65. // 同时满足时,才说明此树为空,并在原后序遍历序列中没有其位置
  66. if( length<=0 && pTree==NULL )
  67. return true;
  68. //二者只满足一个,表示pTree为空却在原后序遍历中有位置,或者pTree
  69. //不为空,在原后序遍历中却没有位置
  70. if( length<=0 || pTree==NULL )
  71. return false;
  72. //根节点不对
  73. if( pTree->m_nElement != A[length-1] )
  74. return false;
  75. // i是左子树的根节点在原后序遍历中的位置
  76. int i = -1;
  77. if( pTree->m_pLeft != NULL )
  78. {
  79. //如果没有找到返回-1,说明左子树不空却在A中找不到,false!
  80. i = FindInArray( A, length-1, pTree->m_pLeft->m_nElement );
  81. if( i==-1 )
  82. return false;
  83. }
  84. return ( IsLRD_Core( A, i+1, pTree->m_pLeft )
  85. && IsLRD_Core( A+i+1, length-i-2, pTree->m_pRight ) );
  86. }
  87. Node* FindInTree( Node* root, int element )
  88. {
  89. if( root==NULL )
  90. return NULL;
  91. Node *pTree = root;
  92. while( pTree!=NULL )
  93. {
  94. if( pTree->m_nElement > element )
  95. pTree = pTree->m_pLeft;
  96. else if( pTree->m_nElement < element )
  97. pTree = pTree->m_pRight;
  98. else
  99. break;
  100. }
  101. return pTree;
  102. }
  103. int FindInArray( int A[], int length, int element )
  104. {
  105. if( A==NULL || length<=0 )
  106. return -1;
  107. int i;
  108. for( i=0; i<length; i++ )
  109. if( A[i] == element )
  110. break;
  111. if( i==length )
  112. return -1;
  113. return i;
  114. }
  115. Node* create_tree( int n )
  116. {
  117. Node* head = NULL;
  118. Node* left = NULL;
  119. Node* right = NULL;
  120. for( int i=0; i<n; )
  121. {
  122. if( head==NULL )
  123. {
  124. head = create_node( i++ );
  125. left = head;
  126. right = head;
  127. }
  128. else
  129. {
  130. left->m_pLeft = create_node( i++ );
  131. right->m_pRight = create_node( i++ );
  132. left = left->m_pLeft;
  133. right = right->m_pRight;
  134. }
  135. }
  136. return head;
  137. }
  138. Node* create_node( int i )
  139. {
  140. Node *tmp = new Node;
  141. if( !tmp )
  142. return NULL;
  143. tmp->m_nElement = i;
  144. tmp->m_pLeft = tmp->m_pRight = NULL;
  145. return tmp;
  146. }

3. 输入一个已经按升序排序过的数组A和一个数字M,在数组中查找两个数,使得它们的和正好是输入的那个数字M

题目: 要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字M,全部输出。
分析: 蛮力解法一般都不是好的解法。本题主要是该数组已经是升序排列,这个好处就大了。两个指针指向数组头和尾,分别为h和t,在h<t的约束下,如果A[h]+A[t]>M,则t--,若A[h]+A[t]<M,则h++A[h]+A[t]=M,则输出A[h]A[t],并继续h++,t--。思想类似于二分查找,毕竟排序是为了能够更方便的查找。

  1. /*
  2. Filename: FindNumberWithSum.cpp
  3. Function: bool FindNumberWithSum( int A[], int length, int sum )
  4. 输入一个已经按升序排序过的数组A和一个数字M,
  5. 在数组中查找两个数,使得它们的和正好是输入的
  6. 那个数字M。要求时间复杂度是O(n)。如果有多对
  7. 数字的和等于输入的数字M,全部输出。
  8. Details:
  9. */
  10. #include <iostream>
  11. using namespace std;
  12. /*
  13. 函数功能:在长为length的数组A中,找到两个数的组合,
  14. 其和为sum,要求输出所有正确的组合
  15. 返回值:bool类型,如果找不到正确的组合,
  16. 则返回false,否则返回true。
  17. */
  18. bool FindNumberWithSum( int A[], int length, int sum );
  19. int main()
  20. {
  21. int A[] = {1,2,4,6,7,8,9,11};
  22. int length = sizeof(A)/sizeof(int);
  23. if(!FindNumberWithSum( A, length, 21 ))
  24. cout<<"No result!"<<endl;
  25. }
  26. bool FindNumberWithSum( int A[], int length, int sum )
  27. {
  28. if( A==NULL || length<=0 )
  29. return false;
  30. if( length>=2 && ( (A[length-1]+A[length-2]<sum)
  31. || (A[0]+A[1]>sum) ) )
  32. return false;
  33. int i=0, j=length-1;
  34. bool bResult = false;
  35. while( i<j )
  36. {
  37. if( A[i]+A[j] < sum )
  38. i++;
  39. else if ( A[i]+A[j] >sum )
  40. j--;
  41. else
  42. {
  43. cout<<A[i]<<" "<<A[j]<<endl;
  44. i++; j--;
  45. bResult = true;
  46. }
  47. }
  48. return bResult;
  49. }

4.输入整数n和m,从数列1,2,...n中随意取几个数,使其和等于m,要求输出所有正确组合(未完成)

分析:中兴面试题。是基本背包问题。

5.反转单链表

题目:就地反转,即要求空间复杂度Θ(1)

  1. /*
  2. Filename: ReverseList.cpp
  3. Function:
  4. ListNode* ReverseList( ListNode* head )
  5. 反转链表,要求空间复杂度为O(1)
  6. Details:
  7. */
  8. #include <iostream>
  9. using namespace std;
  10. struct ListNode
  11. {
  12. int m_nElement;
  13. ListNode *m_pNext;
  14. };
  15. /*
  16. 反转链表
  17. */
  18. ListNode* ReverseList( ListNode* head );
  19. /*
  20. 创建链表,头--尾的序列为 1...n
  21. */
  22. ListNode* BuildList( int n );
  23. /*
  24. 顺序打印链表
  25. */
  26. void PrintList( ListNode* head );
  27. int main()
  28. {
  29. ListNode* head = BuildList(10);
  30. PrintList( head );
  31. ListNode* new_list = ReverseList( head );
  32. PrintList( new_list );
  33. }
  34. ListNode* ReverseList( ListNode* head )
  35. {
  36. if( head==NULL )
  37. return NULL;
  38. ListNode *first, *second, *third;
  39. first = second = head;
  40. third = NULL;
  41. while( first!=NULL )
  42. {
  43. first = second->m_pNext;
  44. second->m_pNext = third;
  45. third = second;
  46. second = first;
  47. }
  48. return third;
  49. }
  50. ListNode* BuildList( int n )
  51. {
  52. if( n<=0 )
  53. return NULL;
  54. ListNode* head = NULL;
  55. ListNode *list, *tmp;
  56. for(int i=1; i<=n; i++)
  57. {
  58. if( head==NULL )
  59. {
  60. head = new ListNode;
  61. head->m_nElement = i;
  62. if( head==NULL )
  63. return NULL;
  64. list = head;
  65. }
  66. else
  67. {
  68. tmp = new ListNode;
  69. if( tmp == NULL )
  70. return NULL;
  71. tmp->m_nElement = i;
  72. list->m_pNext = tmp;
  73. list = list->m_pNext;
  74. }
  75. }
  76. return head;
  77. }
  78. void PrintList( ListNode* head )
  79. {
  80. if( head==NULL )
  81. return;
  82. ListNode* pList = head;
  83. while( pList!=NULL )
  84. {
  85. cout<<pList->m_nElement<<" ";
  86. pList = pList->m_pNext;
  87. }
  88. cout<<endl;
  89. }

6.合并两个排序的链表

题目: 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。要求空间复杂度为O(1)。
分析: 第一是复杂的指针操作多,第二是有循环和递归两种实现。循环实现代码较复杂,递归的代码结构更清晰。个人认为在链表上使用递归不可取,若链表长度过长,会有因过度调用产生的时间消耗以及可能产生栈溢出的风险。递归在树结构上使用比较好,因为其调用次数一般和树的深度成线性关系,而树的深度一般都较小。

  1. /*
  2. Filename: MergeSortedList.cpp
  3. Function:
  4. ListNode* MergeSortedList( ListNode *list1, ListNode *list2 );
  5. 输入两个递增排序的链表,合并这两个链表并使新链表中
  6. 的节点仍然是递增排序的。要求空间复杂度为O(1)。
  7. Details: 有递归和循环两种实现
  8. */
  9. #include <iostream>
  10. using namespace std;
  11. struct ListNode
  12. {
  13. int m_nElement;
  14. ListNode *m_pNext;
  15. };
  16. /*************************循环实现**************************/
  17. ListNode* MergeSortedList( ListNode *list1, ListNode *list2 );
  18. /*************************递归实现**************************/
  19. ListNode* MergeSortedListR( ListNode *list1, ListNode *list2 );
  20. /*
  21. 用长为length的数组X的数据创建链表
  22. */
  23. ListNode* create_list( int X[], int length );
  24. /*
  25. 顺序打印链表
  26. */
  27. void PrintList( ListNode* head );
  28. int main( )
  29. {
  30. int A[] = {1,3,5,7,9};
  31. int B[] = {2,4,6,8,10};
  32. int length_A = sizeof(A)/sizeof(int);
  33. int length_B = sizeof(B)/sizeof(int);
  34. ListNode* list1 = create_list( A, length_A );
  35. ListNode* list2 = create_list( B, length_B );
  36. PrintList( list1 );
  37. PrintList( list2 );
  38. //ListNode* list_result = MergeSortedList( list1, list2 );
  39. ListNode* list_result = MergeSortedListR( list1, list2 );
  40. PrintList( list_result );
  41. }
  42. ListNode* MergeSortedList( ListNode *list1,ListNode *list2 )
  43. {
  44. if( list1==NULL )
  45. return list2;
  46. else if( list2==NULL )
  47. return list1;
  48. ListNode *head = NULL;
  49. //p1和p2是头节点的那条链表上的指针,h1是另一条链表的指针
  50. ListNode *p1 , *p2, *h1 ;
  51. p1 = p2 = h1 = NULL;
  52. if( list1->m_nElement <= list2->m_nElement )
  53. head = list1;
  54. else
  55. head = list2;
  56. p1 = head;
  57. p2 = p1->m_pNext;
  58. h1 = ( head==list1 ? list2: list1 );
  59. while( h1!=NULL && p2!=NULL )
  60. {
  61. if( p2->m_nElement <= h1->m_nElement )
  62. {
  63. p1 = p2; p2 = p2->m_pNext;
  64. }
  65. else
  66. {
  67. p1->m_pNext = h1;
  68. h1 = h1->m_pNext;
  69. p1->m_pNext->m_pNext = p2;
  70. p1 = p1->m_pNext;
  71. }
  72. }
  73. if( p2==NULL )
  74. p1->m_pNext = h1;
  75. return head;
  76. }
  77. ListNode* MergeSortedListR( ListNode *list1, ListNode *list2 )
  78. {
  79. if( list1==NULL )
  80. return list2;
  81. else if( list2==NULL )
  82. return list1;
  83. ListNode *pMergeHead = NULL;
  84. if( list1->m_nElement <= list2->m_nElement )
  85. {
  86. pMergeHead = list1;
  87. pMergeHead->m_pNext = MergeSortedListR(
  88. list1->m_pNext, list2 );
  89. }
  90. else
  91. {
  92. pMergeHead = list2;
  93. pMergeHead->m_pNext = MergeSortedListR(
  94. list1, list2->m_pNext );
  95. }
  96. return pMergeHead;
  97. }
  98. ListNode* create_list(int X[], int length )
  99. {
  100. if( X==NULL || length<=0 )
  101. return NULL;
  102. ListNode* head = NULL;
  103. ListNode *list, *tmp;
  104. for(int i=0; i<length; i++)
  105. {
  106. if( head==NULL )
  107. {
  108. head = new ListNode;
  109. head->m_nElement = X[i];
  110. if( head==NULL )
  111. return NULL;
  112. list = head;
  113. }
  114. else
  115. {
  116. tmp = new ListNode;
  117. if( tmp == NULL )
  118. return NULL;
  119. tmp->m_nElement = X[i];
  120. list->m_pNext = tmp;
  121. list = list->m_pNext;
  122. }
  123. }
  124. return head;
  125. }
  126. void PrintList( ListNode* head )
  127. {
  128. if( head==NULL )
  129. return;
  130. ListNode* pList = head;
  131. while( pList!=NULL )
  132. {
  133. cout<<pList->m_nElement<<" ";
  134. pList = pList->m_pNext;
  135. }
  136. cout<<endl;
  137. }

7.用最简单,最快速的方法计算出下面这个圆形是否和正方形相交

题目:给出一个圆形的圆心r与半径Ry,一个正方形的四个顶点Ai,1<=i<=4,的坐标,判断两者是否相交。
分析:我能想到最简单也是最暴力的方法就是求出圆心到正方形四条边(线段,不是直线)的最短距离dd若小于等于Ry则相交。所以主要的函数应该是求一点到一条线段的最短距离。

  1. /*
  2. Filename: SquareIntersectCircle.cpp
  3. Function:
  4. Details:
  5. */
  6. #include <iostream>
  7. #include <math.h>
  8. using namespace std;
  9. #ifndef MIN
  10. #define MIN( a, b ) (a)>(b)?(b):(a)
  11. #endif
  12. struct cPoint
  13. {
  14. double x;
  15. double y;
  16. };
  17. struct Line_Segment
  18. {
  19. cPoint A;
  20. cPoint B;
  21. };
  22. struct Circle
  23. {
  24. cPoint Center;
  25. double Radius;
  26. };
  27. struct Square
  28. {
  29. cPoint p[4];
  30. };
  31. typedef Line_Segment Segment;
  32. bool IsIntersect( Circle c, Square s );
  33. double DistancePS( cPoint p, Segment s );
  34. double dist( cPoint a, cPoint b );
  35. int main()
  36. {
  37. Circle c;
  38. Square s;
  39. c.Center.x = 3;
  40. c.Center.y = 3;
  41. c.Radius = 1.5;
  42. s.p[0].x = 0;
  43. s.p[0].y = 0;
  44. s.p[1].x = 2;
  45. s.p[1].y = 0;
  46. s.p[2].x = 2;
  47. s.p[2].y = 2;
  48. s.p[3].x = 0;
  49. s.p[3].y = 2;
  50. if( IsIntersect( c, s ) )
  51. cout<< "The square is intersect with the circle!"<<endl;
  52. else
  53. cout<< "The square is not intersect with the circle!"<<endl;
  54. }
  55. bool IsIntersect( Circle c, Square s )
  56. {
  57. bool bResult = false;
  58. int i=0;
  59. Segment line;
  60. for( ; i<4; i++ )
  61. {
  62. line.A.x = s.p[i%4].x;
  63. line.A.y = s.p[i%4].y;
  64. line.B.x = s.p[(i+1)%4].x;
  65. line.B.y = s.p[(i+1)%4].y;
  66. if( DistancePS( c.Center, line ) <= c.Radius )
  67. {
  68. bResult = true;
  69. break;
  70. }
  71. }
  72. return bResult;
  73. }
  74. double DistancePS( cPoint p, Segment s )
  75. {
  76. cPoint intersection;
  77. cPoint line_gradient;
  78. cPoint tmp;
  79. line_gradient.x = s.A.x - s.B.x;
  80. line_gradient.y = s.A.y - s.B.y;
  81. tmp.x = p.x - s.A.x;
  82. tmp.y = p.y - s.A.y;
  83. intersection.x = s.B.x * line_gradient.x;
  84. intersection.y = s.B.y * line_gradient.y;
  85. double ratio ;
  86. ratio = ( tmp.x * line_gradient.x ) + ( tmp.y * line_gradient.y );
  87. ratio /= ( intersection.x + intersection.y );
  88. if( ratio>=0 && ratio<=1 )
  89. {
  90. intersection.x = s.A.x + ratio * s.B.x;
  91. intersection.y = s.A.x + ratio * s.B.y;
  92. return dist( p, intersection );
  93. }
  94. return MIN( dist( p, s.A ), dist( p, s.B ) );
  95. }
  96. double dist( cPoint a, cPoint b )
  97. {
  98. return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );
  99. }

8.在字符串中找出连续最长的数字串

题目:在字符串中找出连续最长的数字串,并把这个最长数字串付给其中一个函数参数 outputstr所指内存,返回outputstr,将其长度保存在变量length中。
分析:思路清晰简单,用两个变量start和end标识数字串的开头和结尾索引即可。

  1. /*
  2. Filename: LongestNumberString.cpp
  3. Function: char* continumax( char *inputstr, int *length )
  4. 在字符串中找出连续最长的数字串,并把这
  5. 个最长数字串付给其中一个函数参数 outputstr
  6. 所指内存,返回outputstr,将其长度保存在变量length中。
  7. Details:
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <memory.h>
  13. using namespace std;
  14. char* continumax( char *inputstr, int *length );
  15. int main()
  16. {
  17. char inputstr[]="I'm so happy in 2014, \
  18. and my birthday is 19901120.";
  19. char *outputstr = NULL;
  20. int length =0;
  21. outputstr = continumax( inputstr, &length );
  22. //cout<<" Longest number string is: "<<outputstr<<endl;
  23. printf("Longest nmumber string is %d : %s \n",length, outputstr );
  24. }
  25. char* continumax( char *inputstr, int *length )
  26. {
  27. if( inputstr==NULL )
  28. return 0;
  29. int start=0, end=0;
  30. int s_max=0, e_max=0;
  31. int max_length=0;
  32. int str_length = strlen( inputstr );
  33. for( ; end<str_length; )
  34. if( inputstr[end]<='9' && inputstr[end]>='0' )
  35. end++;
  36. else
  37. {
  38. if( end-start > max_length )
  39. {
  40. max_length = end-start;
  41. s_max = start;
  42. e_max = end-1;
  43. }
  44. start = ++end;
  45. }
  46. if( end-start > max_length )
  47. {
  48. max_length = end-start;
  49. s_max = start;
  50. e_max = end-1;
  51. }
  52. *length = max_length;
  53. char *outputstr;
  54. if( outputstr==NULL )
  55. outputstr = (char*) malloc( sizeof(char)*(max_length+1) );
  56. for( int i=0; i<max_length; i++ )
  57. outputstr[i] = inputstr[i+s_max];
  58. outputstr[max_length] = '\0';
  59. return outputstr;
  60. }

9.跳台阶问题(递归)

题目:一个台阶总共有 n 级,如果一次可以跳 1 级,也可以跳 2 级。
求总共有多少总跳法,并分析算法的时间复杂度。
分析:假设跳n级台阶有f(n)种跳法,则可以推出f(n+1)=f(n)+f(n1)。归结到底是Fabinocci数列问题。

10.整数的二进制表示中 1 的个数(运算)

题目:输入一个整数,求该整数的二进制表达中有多少个 1。
例如输入 10,由于其二进制表示为 1010,有两个 1,因此输出 2。
分析:这是一道很基本的考查位运算的面试题。包括微软在内的很多公司都曾采用过这道题。
思路1:简单的思路是从最低位检验到最高位,如果int是32位,则需检验32次。
思路2:这里用到一个技巧,即对于整数m,m-1会将m最低位的1置0并将比最低位的1更低位的所有0变为1,如二进制1100,减1后变为1011.(m&m1)会将最低位的1置0.这种方法所需检验次数为整数m中1的个数。

  1. /*
  2. Filename: NumbersBit1.cpp
  3. Function: int NumbersBit1( int n ); int Numbers2Bit1( int n );
  4. 输入一个整数,求该整数的二进制表达中有多少个 1,
  5. 例如输入 10,由于其二进制表示为 1010,有两个 1,
  6. 因此输出 2
  7. Details: 两种实现
  8. */
  9. #include <iostream>
  10. using namespace std;
  11. int NumbersBit1( int n );
  12. int Numbers2Bit1( int n );
  13. int main()
  14. {
  15. cout<<NumbersBit1( 64 )<<endl;
  16. cout<<Numbers2Bit1( 64 )<<endl;
  17. }
  18. int NumbersBit1( int n )
  19. {
  20. int num = 0;
  21. int i=1;
  22. while( i )
  23. {
  24. if( n & i )
  25. num++;
  26. i = i<<1;
  27. }
  28. return num;
  29. }
  30. int Numbers2Bit1( int n )
  31. {
  32. int num = 0;
  33. while( n )
  34. {
  35. num++;
  36. n = n & n-1;
  37. }
  38. return num;
  39. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注