面试考题之9.2:链表(C/C++版)

2.1 编写代码,移除未排序链表中的重复结点。进阶:如果不得使用临时缓冲区,该怎么解决?

解决方案:

方案1: 使用散列表

暂略

方案2:不借助额外缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()
#include <cstdlib>   // free()

#include  "LinkedList.h"

using  namespace std;

void printData(LinkedList aList)
{
    cout << aList->data <<  "  ";
     return;
}


/************************************************************************/
// 函数名称:deleteDups
// 函数目的:移除链表中重复的节点
// 函数参数:myList: 待操作的链表
// 函数返回:无
// 使用条件:
/************************************************************************/

void deleteDups(LinkedList theList)
{
     if (theList ==  NULLreturn;

    LinkedList current = theList;
     while(current !=  NULL){
         // 移除后续值相同的所有结点
        LinkedList runner = current;
         while (runner->next !=  NULL){
             if (runner->next->data == current->data){
                LinkedList nextNode = runner->next;
                runner->next = runner->next->next;
                freeNode(nextNode);
            }
             else
                runner = runner->next;
        }
        current = current->next;
    }

     return;
}

int main()
{
     int arr[] = {  1213241266810442 };

     for (size_t i =  0; i <  sizeof(arr) /  sizeof( int); i++){
         //insert( makeNode(arr[i]) );
        insertBack( makeNode(arr[i]) );
    }

    traverse(printData);
    cout << endl;

    LinkedList theList = getHead();
    deleteDups(theList);
    cout <<  "调用deleteDups()函数后:" << endl;

    traverse(printData);
    cout << endl;

    destroy();  // 释放链表

    getchar();
     return  0;
}

运行结果:


思考体会:

1、散列表怎样解决该问题?


2.2 实现一个算法,找出单向链表中倒数第K个结点。

解决方案:

方案1:递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;

void printData(LinkedList aList)
{
    cout << aList->data <<  "  ";
     return;
}

/************************************************************************/
// 函数名称:nthToLast
// 函数目的:求倒数第K个结点
// 函数参数:aList: 待操作的链表, k:第K个结点、i:计数器
// 函数返回:无
// 使用条件:
/************************************************************************/

LinkedList nthToLast(LinkedList aList, size_t k, size_t& i)
{
     if ( NULL == aList) {
         return  NULL;
    }

    LinkedList theList = nthToLast(aList->next, k, i);
    i +=  1;
     if (i == k){
         return aList;
    }

     return theList;
}


int main()
{
     int arr[] = {  1213241266810442 };

     for (size_t i =  0; i <  sizeof(arr) /  sizeof( int); i++){
        insertBack( makeNode(arr[i]) );
    }

    traverse(printData);
    cout << endl;

    LinkedList theList = getHead();
    size_t k1 =  1, k2 =  4, i1 =  0, i2 =  0;
    LinkedList alist1 = nthToLast(theList, k1, i1);
    LinkedList alist2 = nthToLast(theList, k2, i2);

    cout <<  "K1 = " << alist1->data << endl;
    cout <<  "K2 = " << alist2->data << endl;
    destroy(); // 释放链表
    getchar();
     return  0;
}

其他方案:

运行结果:


思考体会:

1、链表与递归的关系总是那么若影若离,本题主要考查递归在链表中的使用。
2、其他高效方案?


2.3 实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。

示例

输入:单向链表a->b->c->d->e中的结点c.

输出:不返回任何数据,但该链表变为:a->b->d->e.

解决方案:

解法:将后继结点的数据复制到当前结点,然后删除这个后继结点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;

void printData(LinkedList aList)
{
    cout << aList->data <<  "  ";
     return;
}

/************************************************************************/
// 函数名称:deletNode2
// 函数目的:删除单链表中的某个结点
// 函数参数:pNode:链表结点
// 函数返回:true:删除成功
// 使用条件:pNode为非尾结点
/************************************************************************/

bool deletNode2(LinkedList pNode)
{
     if (pNode ==  NULL || pNode->next ==  NULL)
         return  false;

    LinkedList nextNode = pNode->next;
    pNode->data = nextNode->data;
    pNode->next = nextNode->next;
    freeNode(nextNode);

     return  true;
}

int main()
{
     int arr[] = {  1213241266810442 };

     for (size_t i =  0; i <  sizeof(arr) /  sizeof( int); i++){
        insertBack( makeNode(arr[i]) );
    }

    traverse(printData);
    cout << endl;

    LinkedList theList = getHead();

    LinkedList pNode = search( 8);
    deletNode2(pNode);

    traverse(printData);

    destroy();  // 释放链表

    getchar();
     return  0;
}

运行结果:


思考体会:

1、只要考查特殊情况。

2、在C++中删除后继结点时要手动删除。


2.4 编写代码,以给定值x为基准将链表分割为两部分,所有小于x的结点排在大于或等于x的结点之前。

解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;

void printData(LinkedList aNode)
{
    cout << aNode->data <<  "  ";
     return;
}

/************************************************************************/
// 函数名称:partition
// 函数目的:以定值X分割pList
// 函数参数:pList:待操作链表首结点
// 函数返回:
// 使用条件:
/************************************************************************/

LinkedList partition(LinkedList pList,  int x)
{
    LinkedList beforeStart  =  NULL;
    LinkedList beforeEnd    =  NULL;
    LinkedList afterStart   =  NULL;
    LinkedList afterEnd     =  NULL;

     if (pList ==  NULLreturn  NULL;

     // 分割链表
    LinkedList current = pList;   // 指向第一个元素
     while (current !=  NULL){
         // current结点要插入before或after表
        LinkedList nextNode = current->next;
        current->next =  NULL;

         if (current->data < x){
             // 将结点插入before链表
             if (beforeStart ==  NULL){
                beforeStart = current;
                beforeEnd   = beforeStart;
            } else {
                beforeEnd->next = current;
                beforeEnd   = current;
            }
        } else {
             // 将结点插入before链表
             if (afterStart ==  NULL){
                afterStart  = current;
                afterEnd    = afterStart;
            } else {
                afterEnd->next = current;
                afterEnd = current;
            }
        }

        current = nextNode;
    }  // end while()

     if (beforeStart ==  NULL) {
         return afterStart;
    }

     // 合并before和after链表
    beforeEnd->next = afterStart;
     return beforeStart;
}

int main()
{
     int arr[] = {  1213241266810442 };

     for (size_t i =  0; i <  sizeof(arr) /  sizeof( int); i++){
        insertBack( makeNode(arr[i]) );
    }

    traverse(printData);
    cout << endl;

    LinkedList theList = getHead();
    theList = partition(theList,  8);

    sethead(theList);

    traverse(printData);
    cout << endl;

    destroy();  // 释放链表

    getchar();
     return  0;
}


运行结果:


思考体会:

1、没有重新分配内存存放新的链表,靠移动指针来完成题设要求。

2、注意处理时一些细节。


2.5  给定两个链表表示的整数,每个结点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。编写函数对这两个整数求和,并用链表形式返回结果。

示例:

输入:(7->1->6) + (5->9->2), 即:617 + 295.

输出:2->1->9,即:912.

进阶:假设这些数位是正向存放的,请在做一遍。

示例:(6->1->7) + (2->9->5), 即:617 + 295.

输出:9->1->2, 即:912。

解决方案:

方案1:数位反向存放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;

void printData(LinkedList aNode)
{
    cout << aNode->data <<  "  ";
     return;
}

/************************************************************************/
// 函数名称:addLists
// 函数目的:两个链表相加
// 函数参数:list1、ist2
// 函数返回:相加之后的链表
// 使用条件: 数位反向存放
/************************************************************************/

LinkedList addLists(LinkedList list1, LinkedList list2)
{
    LinkedList lt1 = list1, lt2 = list2, newList =  NULL;

     int addData =  0;     // 相加和
     int carryI  =  0;     // 进位值
     while ( lt1 !=  NULL || lt2 !=  NULL ){

         if (lt1 !=  NULL && lt2 !=  NULL){
            addData = lt1->data + lt2->data;
            lt1 = lt1->next;
            lt2 = lt2->next;
        } else  if (lt1 ==  NULL && lt2 !=  NULL){
            addData = lt2->data;
            lt2 = lt2->next;
        } else {
            addData = lt1->data;
            lt1 = lt1->next;
        }

         int totalData = addData + carryI;
        newList = insertBack( newList, makeNode(totalData %  10) );
         //newList = insert( newList, makeNode(totalData % 10) );
        carryI = totalData /  10;
    }

     if (carryI >  0){
        newList = insertBack( newList, makeNode(carryI) );
         //newList = insert( newList, makeNode(carryI) );
    }

     return newList;
}


int main()
{
     int arr1[] = { 617};
     int arr2[] = { 295};

    LinkedList list1 =  NULL, list2 =  NULL, newList =  NULL;

     for (size_t i =  0; i <  sizeof(arr1) /  sizeof( int); i++){
         //list1 = insertBack( list1, makeNode(arr1[i]) );
        list1 = insert( list1, makeNode(arr1[i]) );
    }

     for (size_t i =  0; i <  sizeof(arr2) /  sizeof( int); i++){
         //list2 = insertBack( list2, makeNode(arr2[i]) );
        list2 = insert( list2, makeNode(arr2[i]) );
    }

    newList = addLists(list1, list2);

    cout <<  "list1 = ";   traverse(list1, printData);    cout << endl;
    cout <<  "list2 = ";   traverse(list2, printData);    cout << endl;
    cout <<  "newList = "; traverse(newList, printData);  cout << endl;

    getchar();
     return  0;
}

运行结果:


方案2:数位正向存放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;

void printData(LinkedList aNode)
{
    cout << aNode->data <<  "  ";
     return;
}

/************************************************************************/
// 函数名称:addLists
// 函数目的:两个链表相加
// 函数参数:list1、ist2
// 函数返回:相加之后的链表
// 使用条件: 数位反向存放
/************************************************************************/

LinkedList addLists(LinkedList list1, LinkedList list2)
{
    LinkedList lt1 = list1, lt2 = list2, newList =  NULL;

     int addData =  0;     // 相加和
     int carryI  =  0;     // 进位值
     while ( lt1 !=  NULL || lt2 !=  NULL ){

         if (lt1 !=  NULL && lt2 !=  NULL){
            addData = lt1->data + lt2->data;
            lt1 = lt1->next;
            lt2 = lt2->next;
        } else  if (lt1 ==  NULL && lt2 !=  NULL){
            addData = lt2->data;
            lt2 = lt2->next;
        } else {
            addData = lt1->data;
            lt1 = lt1->next;
        }

         int totalData = addData + carryI;
        newList = insertBack( newList, makeNode(totalData %  10) );
        carryI = totalData /  10;
    }

     if (carryI >  0){
        newList = insertBack( newList, makeNode(carryI) );
    }

     return newList;
}


int main()
{
     int arr1[] = { 617};
     int arr2[] = { 395};

    LinkedList list1 =  NULL, list2 =  NULL, newList =  NULL;

     for (size_t i =  0; i <  sizeof(arr1) /  sizeof( int); i++){
        list1 = insertBack( list1, makeNode(arr1[i]) );
    }

     for (size_t i =  0; i <  sizeof(arr2) /  sizeof( int); i++){
        list2 = insertBack( list2, makeNode(arr2[i]) );
    }

    list1 = reverse(list1);
    list2 = reverse(list2);
    newList = addLists(list1, list2);

     // 转换回原来的顺序
    list1 = reverse(list1);
    list2 = reverse(list2);
    newList = reverse(newList);
    cout <<  "list1 = ";   traverse(list1, printData);    cout << endl;
    cout <<  "list2 = ";   traverse(list2, printData);    cout << endl;
    cout <<  "newList = "; traverse(newList, printData);  cout << endl;

    getchar();
     return  0;
}

运行结果:


思考体会:

1、正向存放题设解决这里用一个函数reverse来反转链表,在通过原来的addList计算,再把得到结果反转回去。

2、其他更高效简洁方法?

3、递归求解?


2.6  给定一个有环链表,实现一个算法返回环路的开头结点。有环链表定义:

在链表中某个结点的next元素指向在它前面出现过的结点,则表明该链表存在环路。

示例:

输入:A->B->C->D->E->C(C结点出现两次)。

输出:C 

解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;


/************************************************************************/
// 函数名称:findBegining
// 函数目的:找到有环链表的开头结点
// 函数参数:head:有环链表
// 函数返回:环开头结点
// 使用条件:
/************************************************************************/

Node* findBegining(LinkedList head)
{
    LinkedList slow = head;
    LinkedList fast = head;

     /* 找出碰撞处,将处于链表中LOOP_SIZE-k步的位置 */
     while (fast !=  NULL && fast->next !=  NULL){
        slow = slow->next;
        fast = fast->next->next;

         if (slow == fast) {   // 碰撞
             break;
        }
    }

     /* 错误检查,没有碰撞处,也即没有环路*/
     if (fast ==  NULL || fast->next ==  NULL){
         return  NULL;
    }

     /* 将slow指向首部,fast指向碰撞处,两者
     * 距离环路起始处k步,若两者以相同的速度移动,
     * 则必定会在环路处碰撞在一起 */

     slow = head;
      while (slow != fast){
        slow = slow->next;
        fast = fast->next;
     }

     /* 至此两者均指向环路起始处 */
     return fast;
}


int main()
{
     int arr[] = {  123456789};

     for (size_t i =  0; i <  sizeof(arr) /  sizeof( int); i++){
        insertBack( makeNode(arr[i]) );
    }

     // 插入后形成环路
    Node* pNode = search( 5);
    insertBack( pNode );

    LinkedList  head = getHead();
    Node* nodeBegin = findBegining( head );

    cout <<  "回环开头结点是:" << ((nodeBegin !=  NULL) ? nodeBegin :  NULL ) << endl;
    cout <<  "回环开头结点值是:" << ((nodeBegin !=  NULL) ? nodeBegin->data :  0 ) << endl;

    getchar();
     return  0;
}

运行结果:


思考体会:

1、解答该题找到条件成立的点,得到规律。

2、经典面试题:检测链表是否有环路,的变体。


2.7 编写一个函数,检查链表是否为回文。

解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <cstring>
#include <cstdio>    // getchar()

#include  "LinkedList.h"

using  namespace std;


/************************************************************************/
// 函数名称:isPalindromes (递归实现)
// 函数目的:判断一个链表是否是回文
// 函数参数:head: 链表
// 函数返回:true: 是回文
// 使用条件:
/************************************************************************/

bool isPalindrome(LinkedList head, size_t length, Node** nextNode)
{
     bool success =  false;
     if (  NULL == head || length ==  0){
         return  false;
    }
     else  if (length ==  2){   // 偶数
        *nextNode = head->next;
    }
     else  if (length ==  3 ) {  // 链表有奇数个元素,跳过中间元素
        *nextNode = head->next->next;
    }
     else {
         success =  isPalindrome(head->next, length -  2, nextNode);
          if (!success)   return  false;
    }

     if ( nextNode ==  NULL || (*nextNode) ==  NULL) {
         return  false;
    }

     // test
    cout << head->data <<  "\t" << (*nextNode)->data << endl;

     if (head->data != (*nextNode)->data) {
         return  false;
    }

    *nextNode = (*nextNode)->next;   // 后移一位
     return  true;
}

int main()
{
     int arr1[] = { 61716};
     int arr2[] = { 395593};

    LinkedList list1 =  NULL, list2 =  NULL;

     for (size_t i =  0; i <  sizeof(arr1) /  sizeof( int); i++){
        list1 = insertBack( list1, makeNode(arr1[i]) );
    }

     for (size_t i =  0; i <  sizeof(arr2) /  sizeof( int); i++){
        list2 = insertBack( list2, makeNode(arr2[i]) );
    }

    Node** theNode = &list1;
    cout <<  "list1 = " << (isPalindrome(list1, size(list1), theNode) ?
                            "true" :  "false") << endl;
    cout <<  "list2 = " << (isPalindrome(list2, size(list2), theNode) ?
                            "true" :  "false") << endl;

    getchar();
     return  0;
}

运行结果:


思考体会:

1、理解递归的的的巧妙运用.

2、仔细体会指针的指针的应用.





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值