@qiezhian
2014-07-08T14:58:09.000000Z
字数 11788
阅读 1642
算法
题目: 给出两个单链表的头指针,判断两个链表是否相交,并求出第一个相交节点。
分析: 假设两个链表长度为N和M,暴力解法是两链表节点一一比对,时间复杂度
扩展:当链表有环怎么办?
题目: 输入一个整数序列,判断其是否为某二叉树的后序遍历结果(此后续遍历可能为树的某一个子树)。
分析: 利用一个事实:根节点一定在后续遍历的最后,且根节点左子树在前部分,右子树紧跟左子树在后部分,最后一个是根节点。所以给定一个序列,要判断是否为以root为根节点的后序遍历,经两步:1.根据后序遍历最后元素,在二叉树中找到此后序遍历的根节点r,如果不在二叉树中,则返回false;2.若找到根节点r,则可将后序遍历序列分为三部分:r的左子树,r的右子树,r。左子树与右子树的分界点即r的左儿子。3.对左子树和右子树递归判断。
/*Filename: IsLRD.cppFunction:bool IsLRD(IsLRD( int A[], int length, Node* root )输入一个整数序列,判断其是否为某二叉树的后序遍历结果(此后续遍历可能为树的某一个子树)Details: 递归实现*/#include <iostream>using namespace std;struct Node{int m_nElement;Node* m_pLeft;Node* m_pRight;};/**********************主要函数************************/bool IsLRD( int A[], int length, Node* root );bool IsLRD_Core( int A[], int length, Node* pTree );/*****************************************************//*在以root为根节点的二叉树中,找到值为element的节点*/Node* FindInTree( Node* root, int element );/*在长度为length的数组A中找到值element的位置(0起点)*/int FindInArray( int A[], int length, int element );/*创建一棵树,根节点的左子树只有左儿子,右子树只有右儿子*/ \* */ \* **/Node* create_tree( int n );/*创建一个值为i的节点*/Node* create_node( int i );int main(){int A[] = { 5,3,1,6,4,2,0};int length = sizeof(A)/sizeof(int);Node *root;root = create_tree( 7 );if( IsLRD( A, length, root ) )cout<<"right"<<endl;elsecout<<"wrong"<<endl;}bool IsLRD( int A[], int length, Node* root ){if( root==NULL || A==NULL || length<=0 )return false;Node *pTree = NULL;pTree = FindInTree( root, A[length-1] );if( pTree==NULL )return false;return IsLRD_Core( A, length, pTree );}bool IsLRD_Core( int A[], int length, Node* pTree ){// 同时满足时,才说明此树为空,并在原后序遍历序列中没有其位置if( length<=0 && pTree==NULL )return true;//二者只满足一个,表示pTree为空却在原后序遍历中有位置,或者pTree//不为空,在原后序遍历中却没有位置if( length<=0 || pTree==NULL )return false;//根节点不对if( pTree->m_nElement != A[length-1] )return false;// i是左子树的根节点在原后序遍历中的位置int i = -1;if( pTree->m_pLeft != NULL ){//如果没有找到返回-1,说明左子树不空却在A中找不到,false!i = FindInArray( A, length-1, pTree->m_pLeft->m_nElement );if( i==-1 )return false;}return ( IsLRD_Core( A, i+1, pTree->m_pLeft )&& IsLRD_Core( A+i+1, length-i-2, pTree->m_pRight ) );}Node* FindInTree( Node* root, int element ){if( root==NULL )return NULL;Node *pTree = root;while( pTree!=NULL ){if( pTree->m_nElement > element )pTree = pTree->m_pLeft;else if( pTree->m_nElement < element )pTree = pTree->m_pRight;elsebreak;}return pTree;}int FindInArray( int A[], int length, int element ){if( A==NULL || length<=0 )return -1;int i;for( i=0; i<length; i++ )if( A[i] == element )break;if( i==length )return -1;return i;}Node* create_tree( int n ){Node* head = NULL;Node* left = NULL;Node* right = NULL;for( int i=0; i<n; ){if( head==NULL ){head = create_node( i++ );left = head;right = head;}else{left->m_pLeft = create_node( i++ );right->m_pRight = create_node( i++ );left = left->m_pLeft;right = right->m_pRight;}}return head;}Node* create_node( int i ){Node *tmp = new Node;if( !tmp )return NULL;tmp->m_nElement = i;tmp->m_pLeft = tmp->m_pRight = NULL;return tmp;}
题目: 要求时间复杂度是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--。思想类似于二分查找,毕竟排序是为了能够更方便的查找。
/*Filename: FindNumberWithSum.cppFunction: bool FindNumberWithSum( int A[], int length, int sum )输入一个已经按升序排序过的数组A和一个数字M,在数组中查找两个数,使得它们的和正好是输入的那个数字M。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字M,全部输出。Details:*/#include <iostream>using namespace std;/*函数功能:在长为length的数组A中,找到两个数的组合,其和为sum,要求输出所有正确的组合返回值:bool类型,如果找不到正确的组合,则返回false,否则返回true。*/bool FindNumberWithSum( int A[], int length, int sum );int main(){int A[] = {1,2,4,6,7,8,9,11};int length = sizeof(A)/sizeof(int);if(!FindNumberWithSum( A, length, 21 ))cout<<"No result!"<<endl;}bool FindNumberWithSum( int A[], int length, int sum ){if( A==NULL || length<=0 )return false;if( length>=2 && ( (A[length-1]+A[length-2]<sum)|| (A[0]+A[1]>sum) ) )return false;int i=0, j=length-1;bool bResult = false;while( i<j ){if( A[i]+A[j] < sum )i++;else if ( A[i]+A[j] >sum )j--;else{cout<<A[i]<<" "<<A[j]<<endl;i++; j--;bResult = true;}}return bResult;}
分析:中兴面试题。是基本背包问题。
题目:就地反转,即要求空间复杂度
/*Filename: ReverseList.cppFunction:ListNode* ReverseList( ListNode* head )反转链表,要求空间复杂度为O(1)Details:*/#include <iostream>using namespace std;struct ListNode{int m_nElement;ListNode *m_pNext;};/*反转链表*/ListNode* ReverseList( ListNode* head );/*创建链表,头--尾的序列为 1...n*/ListNode* BuildList( int n );/*顺序打印链表*/void PrintList( ListNode* head );int main(){ListNode* head = BuildList(10);PrintList( head );ListNode* new_list = ReverseList( head );PrintList( new_list );}ListNode* ReverseList( ListNode* head ){if( head==NULL )return NULL;ListNode *first, *second, *third;first = second = head;third = NULL;while( first!=NULL ){first = second->m_pNext;second->m_pNext = third;third = second;second = first;}return third;}ListNode* BuildList( int n ){if( n<=0 )return NULL;ListNode* head = NULL;ListNode *list, *tmp;for(int i=1; i<=n; i++){if( head==NULL ){head = new ListNode;head->m_nElement = i;if( head==NULL )return NULL;list = head;}else{tmp = new ListNode;if( tmp == NULL )return NULL;tmp->m_nElement = i;list->m_pNext = tmp;list = list->m_pNext;}}return head;}void PrintList( ListNode* head ){if( head==NULL )return;ListNode* pList = head;while( pList!=NULL ){cout<<pList->m_nElement<<" ";pList = pList->m_pNext;}cout<<endl;}
题目: 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。要求空间复杂度为O(1)。
分析: 第一是复杂的指针操作多,第二是有循环和递归两种实现。循环实现代码较复杂,递归的代码结构更清晰。个人认为在链表上使用递归不可取,若链表长度过长,会有因过度调用产生的时间消耗以及可能产生栈溢出的风险。递归在树结构上使用比较好,因为其调用次数一般和树的深度成线性关系,而树的深度一般都较小。
/*Filename: MergeSortedList.cppFunction:ListNode* MergeSortedList( ListNode *list1, ListNode *list2 );输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。要求空间复杂度为O(1)。Details: 有递归和循环两种实现*/#include <iostream>using namespace std;struct ListNode{int m_nElement;ListNode *m_pNext;};/*************************循环实现**************************/ListNode* MergeSortedList( ListNode *list1, ListNode *list2 );/*************************递归实现**************************/ListNode* MergeSortedListR( ListNode *list1, ListNode *list2 );/*用长为length的数组X的数据创建链表*/ListNode* create_list( int X[], int length );/*顺序打印链表*/void PrintList( ListNode* head );int main( ){int A[] = {1,3,5,7,9};int B[] = {2,4,6,8,10};int length_A = sizeof(A)/sizeof(int);int length_B = sizeof(B)/sizeof(int);ListNode* list1 = create_list( A, length_A );ListNode* list2 = create_list( B, length_B );PrintList( list1 );PrintList( list2 );//ListNode* list_result = MergeSortedList( list1, list2 );ListNode* list_result = MergeSortedListR( list1, list2 );PrintList( list_result );}ListNode* MergeSortedList( ListNode *list1,ListNode *list2 ){if( list1==NULL )return list2;else if( list2==NULL )return list1;ListNode *head = NULL;//p1和p2是头节点的那条链表上的指针,h1是另一条链表的指针ListNode *p1 , *p2, *h1 ;p1 = p2 = h1 = NULL;if( list1->m_nElement <= list2->m_nElement )head = list1;elsehead = list2;p1 = head;p2 = p1->m_pNext;h1 = ( head==list1 ? list2: list1 );while( h1!=NULL && p2!=NULL ){if( p2->m_nElement <= h1->m_nElement ){p1 = p2; p2 = p2->m_pNext;}else{p1->m_pNext = h1;h1 = h1->m_pNext;p1->m_pNext->m_pNext = p2;p1 = p1->m_pNext;}}if( p2==NULL )p1->m_pNext = h1;return head;}ListNode* MergeSortedListR( ListNode *list1, ListNode *list2 ){if( list1==NULL )return list2;else if( list2==NULL )return list1;ListNode *pMergeHead = NULL;if( list1->m_nElement <= list2->m_nElement ){pMergeHead = list1;pMergeHead->m_pNext = MergeSortedListR(list1->m_pNext, list2 );}else{pMergeHead = list2;pMergeHead->m_pNext = MergeSortedListR(list1, list2->m_pNext );}return pMergeHead;}ListNode* create_list(int X[], int length ){if( X==NULL || length<=0 )return NULL;ListNode* head = NULL;ListNode *list, *tmp;for(int i=0; i<length; i++){if( head==NULL ){head = new ListNode;head->m_nElement = X[i];if( head==NULL )return NULL;list = head;}else{tmp = new ListNode;if( tmp == NULL )return NULL;tmp->m_nElement = X[i];list->m_pNext = tmp;list = list->m_pNext;}}return head;}void PrintList( ListNode* head ){if( head==NULL )return;ListNode* pList = head;while( pList!=NULL ){cout<<pList->m_nElement<<" ";pList = pList->m_pNext;}cout<<endl;}
题目:给出一个圆形的圆心
分析:我能想到最简单也是最暴力的方法就是求出圆心到正方形四条边(线段,不是直线)的最短距离
/*Filename: SquareIntersectCircle.cppFunction:Details:*/#include <iostream>#include <math.h>using namespace std;#ifndef MIN#define MIN( a, b ) (a)>(b)?(b):(a)#endifstruct cPoint{double x;double y;};struct Line_Segment{cPoint A;cPoint B;};struct Circle{cPoint Center;double Radius;};struct Square{cPoint p[4];};typedef Line_Segment Segment;bool IsIntersect( Circle c, Square s );double DistancePS( cPoint p, Segment s );double dist( cPoint a, cPoint b );int main(){Circle c;Square s;c.Center.x = 3;c.Center.y = 3;c.Radius = 1.5;s.p[0].x = 0;s.p[0].y = 0;s.p[1].x = 2;s.p[1].y = 0;s.p[2].x = 2;s.p[2].y = 2;s.p[3].x = 0;s.p[3].y = 2;if( IsIntersect( c, s ) )cout<< "The square is intersect with the circle!"<<endl;elsecout<< "The square is not intersect with the circle!"<<endl;}bool IsIntersect( Circle c, Square s ){bool bResult = false;int i=0;Segment line;for( ; i<4; i++ ){line.A.x = s.p[i%4].x;line.A.y = s.p[i%4].y;line.B.x = s.p[(i+1)%4].x;line.B.y = s.p[(i+1)%4].y;if( DistancePS( c.Center, line ) <= c.Radius ){bResult = true;break;}}return bResult;}double DistancePS( cPoint p, Segment s ){cPoint intersection;cPoint line_gradient;cPoint tmp;line_gradient.x = s.A.x - s.B.x;line_gradient.y = s.A.y - s.B.y;tmp.x = p.x - s.A.x;tmp.y = p.y - s.A.y;intersection.x = s.B.x * line_gradient.x;intersection.y = s.B.y * line_gradient.y;double ratio ;ratio = ( tmp.x * line_gradient.x ) + ( tmp.y * line_gradient.y );ratio /= ( intersection.x + intersection.y );if( ratio>=0 && ratio<=1 ){intersection.x = s.A.x + ratio * s.B.x;intersection.y = s.A.x + ratio * s.B.y;return dist( p, intersection );}return MIN( dist( p, s.A ), dist( p, s.B ) );}double dist( cPoint a, cPoint b ){return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );}
题目:在字符串中找出连续最长的数字串,并把这个最长数字串付给其中一个函数参数 outputstr所指内存,返回outputstr,将其长度保存在变量length中。
分析:思路清晰简单,用两个变量start和end标识数字串的开头和结尾索引即可。
/*Filename: LongestNumberString.cppFunction: char* continumax( char *inputstr, int *length )在字符串中找出连续最长的数字串,并把这个最长数字串付给其中一个函数参数 outputstr所指内存,返回outputstr,将其长度保存在变量length中。Details:*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <memory.h>using namespace std;char* continumax( char *inputstr, int *length );int main(){char inputstr[]="I'm so happy in 2014, \and my birthday is 19901120.";char *outputstr = NULL;int length =0;outputstr = continumax( inputstr, &length );//cout<<" Longest number string is: "<<outputstr<<endl;printf("Longest nmumber string is %d : %s \n",length, outputstr );}char* continumax( char *inputstr, int *length ){if( inputstr==NULL )return 0;int start=0, end=0;int s_max=0, e_max=0;int max_length=0;int str_length = strlen( inputstr );for( ; end<str_length; )if( inputstr[end]<='9' && inputstr[end]>='0' )end++;else{if( end-start > max_length ){max_length = end-start;s_max = start;e_max = end-1;}start = ++end;}if( end-start > max_length ){max_length = end-start;s_max = start;e_max = end-1;}*length = max_length;char *outputstr;if( outputstr==NULL )outputstr = (char*) malloc( sizeof(char)*(max_length+1) );for( int i=0; i<max_length; i++ )outputstr[i] = inputstr[i+s_max];outputstr[max_length] = '\0';return outputstr;}
题目:一个台阶总共有 n 级,如果一次可以跳 1 级,也可以跳 2 级。
求总共有多少总跳法,并分析算法的时间复杂度。
分析:假设跳n级台阶有
题目:输入一个整数,求该整数的二进制表达中有多少个 1。
例如输入 10,由于其二进制表示为 1010,有两个 1,因此输出 2。
分析:这是一道很基本的考查位运算的面试题。包括微软在内的很多公司都曾采用过这道题。
思路1:简单的思路是从最低位检验到最高位,如果int是32位,则需检验32次。
思路2:这里用到一个技巧,即对于整数m,m-1会将m最低位的1置0并将比最低位的1更低位的所有0变为1,如二进制1100,减1后变为1011.
/*Filename: NumbersBit1.cppFunction: int NumbersBit1( int n ); int Numbers2Bit1( int n );输入一个整数,求该整数的二进制表达中有多少个 1,例如输入 10,由于其二进制表示为 1010,有两个 1,因此输出 2Details: 两种实现*/#include <iostream>using namespace std;int NumbersBit1( int n );int Numbers2Bit1( int n );int main(){cout<<NumbersBit1( 64 )<<endl;cout<<Numbers2Bit1( 64 )<<endl;}int NumbersBit1( int n ){int num = 0;int i=1;while( i ){if( n & i )num++;i = i<<1;}return num;}int Numbers2Bit1( int n ){int num = 0;while( n ){num++;n = n & n-1;}return num;}