• 转:链表相交有环 经典面试题(三)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯


    源地址:http://blog.csdn.net/sj13051180/article/details/6754228

    1.判断单链表是否有环,要求空间尽量少(2011年MTK)

    如何找出环的连接点在哪里?

    如何知道环的长度?

    很经典的题目。

    1.判断是否有环。使用两个指针。一个每次前进1,另一个每次前进2,且都从链表第一个元素开始。显然,如果有环,两个指针必然会相遇。

    2.环的长度。记下第一次的相遇点,这个指针再次从相遇点出发,直到第二次相遇。此时,步长为1的指针所走的步数恰好就是环的长度。

    3.环的链接点。记下第一次的相遇点,使一个指针指向这个相遇点,另一个指针指向链表第一个元素。然后,两个指针同步前进,且步长都为1。当两个指针相遇时所指的点就是环的连接点。

    链接点这个很不明显,下面解释一下。

    如图,设链表不在环上的结点有a个,在环上的结点有b个,前两个指针第一次在第x个结点相遇。

    S( i )表示经过的总步长为i之后,所访问到的结点。

    显然,环的链接点位S( a ),即从起点经过a步之后所到达的结点。

    现在要证明:

    从第一次的相遇点x再经过a步之后可到达链接点S( a ),即 S( x + a ) = S( a )

    由环的周期性可知,只要 a = tb 其中( t = 1, 2, …. ),则S( x + a ) = S( a )

    如何证明a = tb?

    再看看已知条件,当两个指针第一次相遇时,必有S( x ) = S( 2x )

    由环的周期性可知,必有 2x = x + bt, 即x = tb. 

    [cpp] view plaincopy
     
    1. struct Node  
    2. {  
    3.     int data;  
    4.     Node* next;  
    5.   
    6.     Node( int value ): data(value), next(NULL) {};  
    7. };  
    8.   
    9. //判断单链表是否有环  
    10. bool IsCircle( Node *pHead )  
    11. {  
    12.     //空指针 或 只有一个元素且next为空时,必无环  
    13.     if( pHead == NULL || pHead->next == NULL ) return false;  
    14.       
    15.     Node *pSlow = pHead;  
    16.     Node *pFast = pHead;  
    17.   
    18.     while( ( pFast != NULL ) && ( pFast->next != NULL )  )  
    19.     {  
    20.         //分别按步长1、2前进  
    21.         pSlow = pSlow->next;  
    22.         pFast = pFast->next->next;  
    23.   
    24.         if( pSlow == pFast ) break;  
    25.     }  
    26.     if( ( pFast == NULL ) || ( pFast->next == NULL ) )   
    27.         return false;  
    28.     else   
    29.         return true;  
    30. }  
    31.   
    32. //求环的长度  
    33. int GetLen( Node *pHead )  
    34. {  
    35.     if( pHead == NULL || pHead->next == NULL ) return false;  
    36.       
    37.     Node *pSlow = pHead;  
    38.     Node *pFast = pHead;  
    39.   
    40.     //求相遇点  
    41.     while( ( pFast != NULL ) && ( pFast->next != NULL )  )  
    42.     {  
    43.         pSlow = pSlow->next;  
    44.         pFast = pFast->next->next;  
    45.   
    46.         if( pSlow == pFast ) break;  
    47.     }  
    48.   
    49.     //计算长度  
    50.     int cnt = 0;  
    51.     while( ( pFast != NULL ) && ( pFast->next != NULL )  )  
    52.     {  
    53.         pSlow = pSlow->next;  
    54.         pFast = pFast->next->next;  
    55.         cnt++;  
    56.   
    57.         //再次相遇时,累计的步数就是环的长度  
    58.         if( pSlow == pFast ) break;  
    59.     }  
    60.     return cnt;  
    61. }  
    62. //求环的入口点  
    63. Node* GetEntrance( Node* pHead )  
    64. {  
    65.     if( pHead == NULL || pHead->next == NULL ) return false;  
    66.       
    67.     Node *pSlow = pHead;  
    68.     Node *pFast = pHead;  
    69.   
    70.     //求相遇点  
    71.     while( ( pFast != NULL ) && ( pFast->next != NULL )  )  
    72.     {  
    73.         pSlow = pSlow->next;  
    74.         pFast = pFast->next->next;  
    75.   
    76.         if( pSlow == pFast ) break;  
    77.     }  
    78.   
    79.     pSlow = pHead;  
    80.     while( pSlow != pFast )  
    81.     {  
    82.         //同步前进  
    83.         pSlow = pSlow->next;  
    84.         pFast = pFast->next;  
    85.     }  
    86.     return pSlow;  
    87. }  

     2.用非递归的方式合并两个有序链表(2011年MTK)

    用递归的方式合并两个有序链表

    基本的链表操作,没什么好说的。

    非递归:就是把一个链表上的所有结点插入到另一个链表中。

    递归:??

    [cpp] view plaincopy
     
    1. //两个有序链表的合并  
    2. Node* merge( Node* pHeadA, Node* pHeadB )  
    3. {  
    4.     //处理空指针  
    5.     if( pHeadA == NULL || pHeadB == NULL )  
    6.     {  
    7.         return ( pHeadA == NULL ) ? pHeadB : pHeadA;  
    8.     }  
    9.   
    10.     //处理第一个节点  
    11.     Node *px, *py;  
    12.     if( pHeadA->data <= pHeadB->data )  
    13.     {  
    14.         px = pHeadA;    py = pHeadB;  
    15.     }  
    16.     else  
    17.     {  
    18.         px = pHeadB;    py = pHeadA;  
    19.     }  
    20.     Node *pResult = px;  
    21.   
    22.     //将py上的节点按顺序插入到px  
    23.     Node *pre = px;  
    24.     px = px->next;  
    25.     while( py != NULL && px != NULL )  
    26.     {  
    27.         //在px上找到py应该插入的位置  
    28.         while( py != NULL && px != NULL && py->data > px->data )  
    29.         {  
    30.             py = py->next;  
    31.             px = px->next;  
    32.             pre = pre->next;   
    33.         }  
    34.         //py插入到pre和px之间  
    35.         if( py != NULL && px != NULL )  
    36.         {  
    37.             //py指针前移  
    38.             Node* tmp = py;  
    39.             py = py->next;  
    40.   
    41.             //pre指针前移  
    42.             Node* tmpPre = pre;  
    43.             pre = pre->next;  
    44.   
    45.             //插入  
    46.             tmp->next = px;  
    47.             tmpPre->next = tmp;  
    48.   
    49.             //px指针前移  
    50.             px = px->next;             
    51.         }  
    52.         else  
    53.             break;  
    54.     }  
    55.     if( px == NULL ) pre->next = py;  
    56.   
    57.     return pResult;  
    58. }  

    4编程实现:把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列 

            用位操作实现。十进制数在计算机里本来就是按二进制存储的,因此通过掩码和移位操作很容易输出二进制形式。这里,要注意的一点:对最高位符号位的处理。符号位应该单独处理,否则结果会出错。十六进制的处理和二进制基本相同,只是每次处理四位。 

    [cpp] view plaincopy
     
    1. void LongFormat( long value )  
    2. {     
    3.     //处理符号位  
    4.     long mask = 0x1 << ( 8 * sizeof(long) - 1 );  
    5.     if( value & mask ) cout << "1";  
    6.     else cout << "0";  
    7.     //转换为二进制  
    8.     mask = 0x1 << ( 8 * sizeof(long) - 2 );  
    9.     forint i=1; i<8*sizeof(long); i++ )  
    10.     {  
    11.         if( value & mask ) cout << "1";  
    12.         else cout << "0";  
    13.         mask >>= 1;  
    14.     }  
    15.     cout << endl;  
    16.   
    17.     //处理符号位  
    18.     cout << "0x";   
    19.     mask = 0xF << ( 8 * sizeof(long) - 4 );  
    20.     long tmp = ( value & mask ) >> ( 8 * sizeof(long) - 4 );  
    21.     if( tmp < 10 )  
    22.         cout << tmp;  
    23.     else  
    24.         cout << (char)( 'a' + ( tmp - 10 ) );  
    25.     //转换为十六进制  
    26.     mask = 0xF << ( 8 * sizeof(long) - 8 );  
    27.     forint i=1; i<2*sizeof(long); i++ )  
    28.     {  
    29.         tmp = ( value & mask ) >> ( 8 * sizeof(long) - 4 * i - 4 );  
    30.         if( tmp < 10 )  
    31.             cout << tmp;  
    32.         else  
    33.             cout << (char)( 'a' + ( tmp - 10 ) );  
    34.   
    35.         mask >>= 4;   
    36.     }  
    37. }  

    5.编程实现:找出两个字符串中最大公共子字符串,如"abccade","dgcadde"的最大子串为"cad" 

    有人说:可用KMP。可惜KMP忘了,找时间补一下。

    还有人说:用两个字符串,一个作行、一个作列,形成一个矩阵。相同的位置填1,不同的位置填0。然后找哪个斜线方向上1最多,就可以得到最大公共子字符串。空间复制度0( m*n ),感觉时间上也差不多O( m*n )

    没想到什么好办法,只会用最笨的办法O( m*n )。即,对于字符串A中的每个字符,在字符串B中找以它为首的最大子串。哎,即便是这个最笨的方法,也写了好长时间,汗。 

    [cpp] view plaincopy
     
    1. void GetSubStr( char *strA, char *strB, char *ans )  
    2. {     
    3.     int max = 0;  
    4.     char *pAns = NULL;  
    5.   
    6.     //遍历字符串A  
    7.     forint i=0; *(strA+i) != ''; i++ )  
    8.     {  
    9.         //保存strB的首地址,每次都从strB的第一个元素开始比较  
    10.         char *pb = strB;  
    11.         while( *pb != '' )  
    12.         {  
    13.             //保存strA的首地址  
    14.             char *pa = strA + i;  
    15.             int cnt = 0;  
    16.             char *pBegin = pb;  
    17.   
    18.             //如果找到一个相等的元素  
    19.             if( *pb == *pa )  
    20.             {  
    21.                 while( *pb == *pa && *pb != '' )   
    22.                 {  
    23.                     pa++;  
    24.                     pb++;  
    25.                     cnt++;  
    26.                 }  
    27.                 if( cnt > max )  
    28.                 {  
    29.                     max = cnt;  
    30.                     pAns = pBegin;  
    31.                 }     
    32.                 if( *pb == '' ) break;  
    33.             }  
    34.             else  
    35.                 pb++;             
    36.         }         
    37.     }  
    38.     //返回结果  
    39.     memcpy( ans, pAns, max );     
    40.     *(ans+max) = '';  
    41. }  

    6.有双向循环链表结点定义为:

    struct node

    {

      int data;

      struct node *front,*next;

    };

    有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中data值相同的结点删除。

           没什么NB算法。就是遍历对链表A,对A的每个元素,看它是否在链表B中出现。如果在B中出现,则把所有的出现全部删除,同时也在A中删除这个元素。

    思路很简单,实现起来也挺麻烦。毕竟,双向循环链表也算是线性数据结构中最复杂的了。如何判断双向循环链表的最后一个元素?p->next == pHead.

    删除操作:

    双向循环链表只有一个节点时

    双向循环链表至少有两个节点时

    [cpp] view plaincopy
     
    1. struct Node  
    2. {   
    3.   int data;  
    4.   struct Node *front,*next;  
    5.   Node( int value ): data( value ), front( NULL ), next( NULL ) { };  
    6.   void SetPointer( Node *pPre, Node *pNext ) { front = pPre; next = pNext; };  
    7. };  
    8.   
    9. //如果成功删除返回真。否则,返回假。  
    10. bool DeleteValue( Node *&pHead, int target )  
    11. {  
    12.     if( pHead == NULL ) return false;     
    13.   
    14.     //至少有两个元素  
    15.     bool flag = false;  
    16.     Node* ph = pHead;  
    17.     while( ph->next != pHead  )  
    18.     {  
    19.         Node *pPre = ph->front;  
    20.         Node *pNext = ph->next;  
    21.   
    22.         if( ph->data == target )  
    23.         {  
    24.             //如果删除的是第一个元素  
    25.             if( ph == pHead ) pHead = ph->next;            
    26.   
    27.             pPre->next = pNext;  
    28.             pNext->front = pPre;   
    29.   
    30.             Node *tmp = ph;  
    31.             delete tmp;  
    32.               
    33.             //设置删除标记  
    34.             flag = true;  
    35.         }  
    36.         ph = pNext;  
    37.     }  
    38.     //只有一个元素或最后一个元素  
    39.     if( ph->next == pHead )  
    40.     {  
    41.         if( ph->data == target )  
    42.         {  
    43.             //如果要删除的是最后一个元素  
    44.             if( ph->front != ph )  
    45.             {                 
    46.                 Node *pPre = ph->front;  
    47.                 Node *pNext = ph->next;  
    48.                 pPre->next = pNext;  
    49.                 pNext->front = pPre;   
    50.   
    51.                 Node *tmp = ph;  
    52.                 delete tmp;  
    53.             }  
    54.             else  
    55.             {  
    56.                 delete pHead;  
    57.                 pHead = NULL;             
    58.             }  
    59.             flag = true;              
    60.         }         
    61.     }  
    62.     return flag;  
    63. }  
    64.   
    65.   
    66. void DeleteSame( Node *&pHeadA, Node *&pHeadB )  
    67. {  
    68.     if( pHeadA != NULL && pHeadB != NULL )  
    69.     {  
    70.         Node *pa = pHeadA;  
    71.         while( pa->next != pHeadA )  
    72.         {             
    73.             //如果B中含有pa->data,并且已经删除            
    74.             if( DeleteValue( pHeadB, pa->data ) )  
    75.             {  
    76.                 //在A中删除pa->data  
    77.                 Node *tmp = pa->next;  
    78.                 DeleteValue( pHeadA, pa->data );  
    79.                 pa = tmp;  
    80.             }  
    81.             else  
    82.                 pa = pa->next;  
    83.         }  
    84.         //只有一个元素或最后一个元素               
    85.         if( DeleteValue( pHeadB, pa->data ) )  
    86.         {  
    87.             DeleteValue( pHeadA, pa->data );  
    88.         }         
    89.     }  
    90. }  

    7.设计函数int atoi(char *s)。

    int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?

    解释局部变量、全局变量和静态变量的含义。

    解释堆和栈的区别。

    论述含参数的宏与函数的优缺点。

    1.字符串转整形,嘿嘿,前面已写过了。

    2.逗号表达式的值等于最后一个逗号之后的表达式的值。对应本题,即i=(m=32)

    3.局部变量:在函数内定义的变量。作用域范围:只在定义它的块内有效。

    全局变量:在函数之外定义的变量。作用域范围:从定义的地方开始直到文件末尾都有效。

    静态变量:static变量,属于静态存储方式。静态局部变量在函数内定义,生存期是整个源代码。但是,作用域范围只在定义它的函数内有效。静态全局变量与一般的全局变量:一般全局变量在整个源程序内有效,静态全局变量只在所在文件内有效。

    4.堆:一般new出来的变量都在堆里,这里变量要由程序员自己管理,即在不用的时候要及时释放,防止内存泄露。

    栈:一般局部变量、函数的参数都在栈里,他们是由编译器来自动管理的。 

    8.顺时针打印矩阵

    题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

    例如:如果输入如下矩阵:

    1              2              3              4

    5              6              7              8

    9              10             11             12

    13             14             15             16

    则依次打印出数字, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10。

    分析:包括Autodesk、EMC在内的多家公司在面试或者笔试里采用过这道题

            本来想写递归的,结果递归的终止条件比较复杂。因为每次把最外面一圈都出来了,所以矩形的行列都减小2,而且还要记录当前矩形的起始位置。递归终止条件,要考虑行列为0、1的情况。哎,想不清楚。最后还是非递归的好写。也很简单,没啥所的,直接看代码把。

    [cpp] view plaincopy
     
    1. const int MAX_ROW = 100;  
    2. const int MAX_COL = 100;  
    3.   
    4. void PrintMatrix( int data[][MAX_COL], int row, int col )  
    5. {  
    6.     int top = 0;  
    7.     int bottom = row-1;  
    8.     int left = 0;  
    9.     int right = col-1;  
    10.   
    11.     int cnt = 0;  
    12.     int total = row * col;  
    13.     while( cnt < total )  
    14.     {  
    15.         //从左到右,打印最上面一行  
    16.         int j;  
    17.         for( j=left; j<=right && cnt<total; j++ )  
    18.         {  
    19.             cout << data[top][j] <<" ";  
    20.             cnt++;  
    21.         }  
    22.         top++;  
    23.   
    24.         //从上到下,打印最右面一列  
    25.         for( j=top; j<=bottom && cnt<total; j++ )  
    26.         {  
    27.             cout << data[j][right] << " ";  
    28.             cnt++;  
    29.         }  
    30.         right--;  
    31.   
    32.         //从右到左,打印最下面一行  
    33.         for( j=right; j>=left && cnt<total; j-- )  
    34.         {  
    35.             cout << data[bottom][j] << " ";  
    36.             cnt++;  
    37.         }  
    38.         bottom--;  
    39.   
    40.         //从下到上,打印最左边一列  
    41.         for( j=bottom; j>=top && cnt<total; j-- )  
    42.         {  
    43.             cout << data[j][left] << " ";  
    44.             cnt++;  
    45.         }  
    46.         left++;  
    47.     }             
    48. }  

    9.对称子字符串的最大长度

    题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。

    比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出。

    分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的加强版

    10.用1、2、3、4、5、6这六个数字,写一个main函数,打印出所有不同的排列,如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连.

           先不考虑限制条件,我们可以用递归打印出所有的排列(嘿嘿,这个前面写过,可以用递归处理)。然后,只要在递归终止时,把限制条件加上,这样只把满足条件的排列打印出来,就可以了。

    [cpp] view plaincopy
     
    1. bool IsValid( char *str )  
    2. {  
    3.     forint i=1; *(str+i) != ''; i++ )  
    4.     {  
    5.         if( i == 2 && *(str+i) == '4' ) return false;  
    6.   
    7.         if( *(str+i) == '3' && *(str+i-1) == '5' || *(str+i) == '5' && *(str+i-1) == '3' )  
    8.             return false;  
    9.     }  
    10.     return true;  
    11. }  
    12.   
    13. void PrintStr( char *str, char *start )  
    14. {  
    15.     if( str == NULL ) return;  
    16.       
    17.     if( *start == '' )  
    18.     {  
    19.         if( IsValid( str ) ) cout << str << endl;  
    20.     }  
    21.   
    22.     forchar *ptmp = start; *ptmp != ''; ptmp++ )  
    23.     {  
    24.         char tmp = *start;  
    25.         *start = *ptmp;  
    26.         *ptmp = tmp;  
    27.   
    28.         PrintStr( str, start+1 );  
    29.   
    30.         tmp = *start;  
    31.         *start = *ptmp;  
    32.         *ptmp = tmp;   
    33.     }  
    34. }  

    11。微软面试题

    一个有序数列,序列中的每一个值都能够被2或者3或者5所整除,1是这个序列的第一个元素。求第1500个值是多少?

           2、3、5的最小公倍数是30。[ 1, 30]内符合条件的数有22个。如果能看出[ 31, 60]内也有22个符合条件的数,那问题就容易解决了。也就是说,这些数具有周期性,且周期为30.

           第1500个数是:1500/22=68   1500%68=4。也就是说:第1500个数相当于经过了68个周期,然后再取下一个周期内的第4个数。一个周期内的前4个数:2,3,4,5。

    故,结果为68*30=2040+5=2045

    12.从尾到头输出链表

    题目:输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:

    struct ListNode

    {

      int  m_nKey;

      ListNode* m_pNext;

    };

    分析:这是一道很有意思的面试题。该题以及它的变体经常出现在各大公司的面试、笔试题中。

    链表的反向输出。前面我们讨论过:链表的逆序,使用3个额外指针,遍历一遍链表即可完成。这里当然可以先把链表逆序,然后再输出。链表上使用递归一般也很简单,虽然递归要压栈,但程序看起来很简洁。

    [cpp] view plaincopy
     
    1. struct ListNode  
    2. {  
    3.     int  m_nKey;  
    4.     ListNode* m_pNext;  
    5. };  
    6.   
    7. void PrintReverse( ListNode* pHead )  
    8. {  
    9.     ListNode* ph = pHead;  
    10.     if( ph != NULL )  
    11.     {  
    12.         PrintReverse( ph->m_pNext );  
    13.         cout << ph->m_nKey << " ";  
    14.     }  
    15. }  

     

  • 相关阅读:
    Cocos2d-x3.0游戏实例之《别救我》第四篇——乱入的主角
    TRIZ系列-创新原理-21-高速通过原理
    “cvSnakeImage”: 找不到标识符
    21世纪创业与知识之间的辩证关系
    Android导航栏ActionBar的具体分析
    HDU4565 && 2013年长沙邀请赛A题
    从切比雪夫不等式到大数定理
    在线笔试琐碎
    在线笔试琐碎
    算法求解中的变量、数组与数据结构(STL 中的容器)
  • 原文地址:https://www.cnblogs.com/xuhj001/p/3389133.html
Copyright © 2020-2023  润新知