您的位置:首页 > 编程语言 > C语言/C++

C语言中的单链表面试题----基础

2017-06-13 21:14 387 查看


题目总览

从尾到头打印单链表
删除一个无头单链表的非尾节点
在无头单链表的一个节点前插入一个节点
单链表实现约瑟夫环
逆置/反转单链表
单链表排序(冒泡排序&快速排序)
合并两个有序链表,合并后依然有序
查找单链表的中间节点,要求只能遍历一次链表
查找单链表的倒数第n个节点,要求只能遍历一次链表


思路及代码


从尾到头打印单链表

递归法,若当前节点不为空,在打印当前节点前打印上一个节点。
void ConversePrint(ListNode* pList)
{
if (pList != NULL)
{
ConversePrint(pList->next);
printf("%d ", pList->data);
}
}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8


删除一个无头单链表的非尾节点

将当前节点的内容改为下个节点的内容,释放下个节点,让当前节点指向下下个节点。
void popNode(ListNode* pList)
{
if (pList != NULL)
{
ListNode* tmp = pList->next;
pList->data = pList->next->data;
pList->next = pList->next->next;
free(tmp);
}
}
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10


在无头单链表的一个节点前插入一个节点

新建一个节点保存当前节点的内容,并指向下一个节点。 

将当前节点改为插入的内容,指向新节点。
void InsertBefore(ListNode* pList, DataType x)
{
assert(pList);
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode->data = pList->data;
newNode->next = pList->next;
pList->next = newNode;
pList->data = x;
}
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9


单链表实现约瑟夫环

接收一个环形链表的头指针地址,将头指针改为剩下的最后一个节点。
处理空链表。
用一个节点指针指向头节点,当前节点的下一节点不是当前节点(节点数大于一),循环,当前节点为第一个节点,走 n-1 步,指向第n个节点,删除当前节点。
将初始头指针赋值最后节点。
void Joseph(ListNode** ppList, int n)
{
assert(ppList);
if (*ppList == NULL)return;
ListNode* pList = *ppList;
while (pList->next != pList)
{
int i;
for (i = 1; i < n; i++)
pList = pList->next;
popNode(pList);
}
*ppList = pList;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14


逆置/反转单链表

头插法,建立新链表,用 cur 指向旧链表头结点。
循环,用 tmp 保存要拿下来的原链表头结点 cur, cur 指向下一节点,将 tmp 放到新链表的头部(tmp的下一节点指向新链表头结点,再将新链表的头结点改为 tmp),直到 cur 为 NULL。
将原链表头指针改为新链表。
void ReverseList(ListNode** ppList)
{
assert(ppList);

ListNode* newList = NULL;
ListNode* cur = *ppList;
while (cur)
{
ListNode* tmp = cur;
cur = cur->next;
tmp->next = newList;
newList = tmp;
}
*ppList = newList;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


单链表排序(冒泡排序)

接收一个普通单链表,处理空链表和只有一个节点的情况。
用 tail 指示一次冒泡的终点,第一次要全排,赋值为NULL。
循环冒泡比较,每次比较(交换)后,cur 和 next 都指向它们的下一个节点,直到 next == tail。交换中,修改交换标记 exchange。
外层循环,用 cur 表示比较的第一个节点,用 next 表示比较的下一个节点,exchange 记录是否交换,一趟比较没有交换,说明已排好,直接返回。每趟比较之后,此次最后一个值已经为最大,tail 前移一个节点。
void ListBubbleSort(ListNode* pList)
{
if (pList == NULL || pList->next == NULL)return;

ListNode* tail = NULL;
while (tail != pList)
{
ListNode* cur = pList;
ListNode* next = pList->next;
int exchange = 0;
while (next != tail)
{
if (cur->data > next->data)
{
DataType num = cur->data;
cur->data = next->data;
next->data = num;
exchange = 1;
}
cur = next;
next = next->next;
}
if (exchange == 0)
return;
tail = cur;
}
}
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
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


合并两个有序链表,合并后依然有序

方法一 

- 新链表段接法 

- 接收两个链表指针,处理有空链表的情况。 

- 创建新链表头指针,指向两链表头节内值点较小的的节点。 

- 循环,节点小的指针后移,直到节点下一节点大于另一指针或节点为空,tmp 保存下一节点,当前节点指向另一指针,当前指针赋值为 tmp,如果当前指针为 NULL,说明合并完成,结束循环。 

- 返回新链表头指针。 

- 可以总是让 p1 指向小的,p2 指向大的,循环挪 p1,接完后再把小的给 p1,大的给 p2。可以不用比较。(原方法 else 是复制 if 后,1 和 2 换,其实更简单,看起来多) 
思路图 



ListNode* MergeList(ListNode* List1, ListNode* List2)
{
if (List1 == NULL)return List2;
if (List2 == NULL)return List1;

ListNode* newList = List1->data <= List2->data ? List1 : List2;
while (true)
{
if(List1->data <= List2->data)
{
while (List1->next != NULL && List1->next->data <= List2->data)
{
List1 = List1->next;
}
ListNode* tmp = List1->next;
List1->next = List2;
List1 = tmp;
if (List1 == NULL)
break;
}
else
{
while (List2->next != NULL && List2->next->data <= List1->data)
{
List2 = List2->next;
}
ListNode* tmp = List2->next;
List2->next = List1;
List2 = tmp;
if (List2 == NULL)
break;
}
}
return newList;
}
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
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

方法二 

- 新链单节点表尾插法 

- 新建 newList = NULL, 循环,每次比较两指针内容,保存小的节点,小节点指针后移,小节点尾接到 newList。 

- 如果有一个指针为 NULL,另一个指针尾接到 newList,返回 newList。 
方法三 

- 介于上两个方法之间,以头结点小的为 newList,另一个链表每次拿下一个节点接到 newList 的合适位置,直到其中一个指针为空。


查找单链表的中间节点,要求只能遍历一次链表

快慢指针法,初始快指针、慢指针都是头指针。
循环,如果快指针能走两步(快指针的下一个节点和下下一个节点不为空),快指针走两步,慢指针走一步。
返回慢指针。
ListNode* FindMidNode(ListNode* pList)//mid = total>>1
{
ListNode* fast = pList;
while (fast->next != NULL && fast->next->next != NULL)
{
fast = fast->next->next;
pList = pList->next;
}
return pList;
}
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10


查找单链表的倒数第n个节点,要求只能遍历一次链表

当节点数少于n时,NULL。
快慢指针法,创建返回指针 ret ,快指针先走 n-1 步(此时 ret 为快指针的倒数第 n 个节点)或到 NULL 为止。
循环,快指针的下一个节点不为空,快指针和 ret 都向后走一步。
返回返回指针。
ListNode* FindFromLast(ListNode* pList, int n)
{
ListNode* ret = pList;
while (--n)
{
if(ret == NULL)return NULL;
pList = pList->next;
}
while (pList->next != NULL)
{
ret = ret->next;
pList = pList->next;
}
return ret;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: