面试考题之9.2:链表(C/C++版)
来源:互联网 发布:成都凶宅数据库查询 编辑:程序博客网 时间:2024/05/29 23:43
2.1 编写代码,移除未排序链表中的重复结点。进阶:如果不得使用临时缓冲区,该怎么解决?
解决方案:
方案1: 使用散列表
暂略
方案2:不借助额外缓冲区
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 <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 == NULL) return;
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[] = { 12, 13, 2, 4, 12, 6, 6, 8, 10, 4, 4, 2 };
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:递归
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 <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[] = { 12, 13, 2, 4, 12, 6, 6, 8, 10, 4, 4, 2 };
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;
return 0;
}
其他方案:
运行结果:
思考体会:
2.3 实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。
示例
输入:单向链表a->b->c->d->e中的结点c.
输出:不返回任何数据,但该链表变为:a->b->d->e.
解决方案:
解法:将后继结点的数据复制到当前结点,然后删除这个后继结点。
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 <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[] = { 12, 13, 2, 4, 12, 6, 6, 8, 10, 4, 4, 2 };
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的结点之前。
解决方案:
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 <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 == NULL) return 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[] = { 12, 13, 2, 4, 12, 6, 6, 8, 10, 4, 4, 2 };
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:数位反向存放
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 <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[] = {6, 1, 7};
int arr2[] = {2, 9, 5};
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:数位正向存放
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 <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[] = {6, 1, 7};
int arr2[] = {3, 9, 5};
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
解决方案:
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 <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[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
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 编写一个函数,检查链表是否为回文。
解决方案:
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 <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[] = {6, 1, 7, 1, 6};
int arr2[] = {3, 9, 5, 5, 9, 3};
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、仔细体会指针的指针的应用.
- 面试考题之9.2:链表(C/C++版)
- 面试考题之9.1:数组与字符串(C/C++版)
- C++ 面试常考题
- C/C++面试总结必考题
- C/C++面试总结必考题 2
- C考题
- c语言考题
- C/C++考题收集
- 一道C语言考题
- C语言知识点考题
- Objective-C 考题
- C语言考题
- 计算机网络之面试常考题
- 计算机网络之面试常考题
- C#—考题字符串2
- 高效面试之操作系统常考题
- c语言面试之华为
- iOS面试之C语言
- 【Treap】[BZOJ 3224]Tyvj 1728 普通平衡树 & 非旋转实现
- How to solve “Dynamic Web Module 3.1 requires Java 1.7 or newer” in Eclipse
- 关于今天
- Hibernate4 新特性
- iOS开发UI高级—15UITabBarController生命周期(使用storyoard搭建)
- 面试考题之9.2:链表(C/C++版)
- 循环单链表合并
- iOS开发UI高级—16APP主流UI框架结构
- U盘安装Centos7
- IOS网络请求封装与下拉刷新上托加载更多
- Network Namespace
- uml精粹——11.活动图(及整个读书笔记分享)
- 高斯消元
- 线性表接口