链表通常是面试中的重点,本文是用C语言写一个无头单链表,并介绍它的相关面试题的解法。
其中涉及到的面试题如下:
1. 比较顺序表和链表的优缺点,说说它们分别在什么场景下使用?
顺序表:内存中地址连续
长度不可变更
支持随机查找 可以在O(1)内查找元素
适用于需要大量访问元素的 而少量增添/删除元素的程序
链表 :内存中地址非连续
长度可以实时变化
不支持随机查找 查找元素时间复杂度O(n)
适用于需要进行大量增添/删除元素操作 而对访问元素无要求的程序
以下题目会在代码中体现:
2. 从尾到头打印单链表
3. 删除一个无头单链表的非尾节点
4. 在无头单链表的一个非头节点前插入一个节点
5. 单链表实现约瑟夫环
6. 逆置/反转单链表
7. 单链表排序(冒泡排序&快速排序)
8. 合并两个有序链表,合并后依然有序
9. 查找单链表的中间节点,要求只能遍历一次链表
10. 查找单链表的倒数第k个节点,要求只能遍历一次链表
11. 判断单链表是否带环?若带环,求环的长度?求环的入口点。
12. 判断两个链表是否相交,若相交,求交点。(假设链表不带环)
13. 复杂链表的复制。一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表
//list.h
#ifndef __LIST_H__
#define __LIST_H__
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int Datetype;
typedef struct Node
{
Datetype data;
struct Node* next;//指向链表的指针,pplist指向链表指针的指针
}Node, *pNode, *pList;
void InitList(pList* pplist);
void DestroyList(pList* pplist);
void Insert(pList* pplist, pNode pos, Datetype d);
void PushBack(pList* pplist, Datetype d);
void PushFront(pList* pplist, Datetype d);
void Print(pList plist);
void PopBack(pList* pplist);
void PopFront(pList* pplist);
void Remove(pList* pplist,Datetype d);
void RemoveAll(pList* pplist, Datetype d);
pNode Find(pList plist,Datetype d);
pNode BubbleSort(pList* pplist);
void Erease(pList* pplist,pNode pos);
void Reverse(pList* pplist);
void PrintReverse(pList plist);
void DelNottailNode(pNode pos);
pNode JosephCycle(pList* pplist, int num);
pList Merge(pList l1, pList l2);
pList Merge2(pList l1, pList l2);
pNode CheckCircle(pList plist);
int Circlelength(pNode meet);
int CheckCross(pList plist1, pList plist2);
pNode GetCircleEntry(pList plist, pNode meet);
typedef struct ComplexNode
{
Datetype data;
struct ComplexNode* next;
struct ComplexNode* random;
}ComplexNode,*pComplexNode;
pComplexNode CreateComplexNode(Datetype d);
void PrintComplexNode(pComplexNode head);
pComplexNode CloneComplexlist(pComplexNode head);
#endif __LIST_H__
再来看各个接口的具体实现:
//list.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
//新增节点
pNode BuyNode(Datetype d)
{
pNode NewNode = (pNode)malloc(sizeof(Node));
NewNode->data = d;
NewNode->next = NULL;
return NewNode;
}
//链表初始化
void InitList(pList* pplist)
{
assert(pplist);
*pplist = NULL;
}
//后插
void PushBack(pList* pplist,Datetype d)
{
pNode cur = *pplist;
pNode newNode = BuyNode(d);
assert(pplist);
if (cur == NULL)
{
*pplist = newNode;
return;
}
while (cur->next)
{
cur = cur->next;
}
cur->next = newNode;
}
//打印链表
void Print(pList plist)
{
if (plist == NULL)
{
return;
}
pNode cur = plist;
while (cur)
{
printf("%d-->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//销毁
void DestroyList(pList* pplist)
{
pNode cur = *pplist;
while (cur)
{
pNode del = cur;
printf("%d\n", del->data);
cur = cur->next;
free(del);
del = NULL;
}
*pplist = NULL;
}
//删除
void Remove(pList* pplist,Datetype d)
{
pNode cur = *pplist;
pNode prev = NULL;
assert(pplist);
if (cur == NULL)
return;
while (cur)
{
if (cur->data == d)
{
pNode del = cur;
if (cur == *pplist)
{
//PopFront(pplist);
*pplist = cur->next;
}
else
{
prev->next = cur->next;
}
free(del);
del = NULL;
return;
}
else
{
prev = cur;
cur = cur->next;
}
}
}
//删除所有相同元素
void RemoveAll(pList* pplist, Datetype d)
{
pNode cur = *pplist;
pNode prev = NULL;
assert(pplist);
if (cur == NULL)
return;
while (cur)
{
if (cur->data == d)
{
pNode del = cur;
if (cur == *pplist)
{
//PopFront(pplist);
*pplist = cur->next;
}
else
{
prev->next = cur->next;
}
cur = cur->next;
free(del);
del = NULL;
return;
}
else
{
prev = cur;
cur = cur->next;
}
}
}
//后删
void PopBack(pList* pplist)
{
pNode cur = *pplist;
assert(pplist);
if (cur == NULL)
{
return;
}
if (cur->next == NULL)
{
free(cur);
cur = NULL;
return;
}
while (cur->next->next != NULL)
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
//前插
void PushFront(pList* pplist, Datetype d)
{
pNode cur = *pplist;
pNode newNode = BuyNode(d);
assert(pplist);
if (cur == NULL)
{
*pplist = newNode;
}
else
{
newNode->next = cur;
*pplist = newNode;
}
}
//后删
void PopFront(pList* pplist)
{
pNode cur = *pplist;
assert(pplist);
if (cur == NULL)
{
return;
}
//一个节点
if (cur->next == NULL)
{
free(cur);
cur = NULL;
*pplist = NULL;
return;
}
//两个以上节点
*pplist = cur->next;
free(cur);
cur = NULL;
}
//任意位置插入
void Insert(pList* pplist, pNode pos, Datetype d)
{
pNode NewNode = BuyNode(d);
pNode cur = pos->next;
assert(pplist);
assert(pos);
if (*pplist == NULL)
{
PushBack(pplist, d);
return;
}
else
{
pos->next = NewNode;
NewNode->next = cur;
}
}
//查找
pNode Find(pList plist, Datetype d)
{
pNode cur = plist;
while (cur)
{
if (cur->data == d)
return cur;
cur = cur->next;
}
return NULL;
}
//任意位置删除
void Erease(pList* pplist,pNode pos)
{
pNode cur = *pplist;
assert(pplist);
assert(pos);
if (*pplist == NULL)
{
return;
}
if (pos->next == NULL)//pos指向尾节点
{
PopBack(pplist);
return;
}
else
{
pNode del = pos->next;
pos->data = pos->next->data;
pos->next = pos->next->next;
free(del);
del = NULL;
}
}
//排序
pNode BubbleSort(pList* pplist)
{
pNode cur = *pplist;
pNode tail = NULL;
assert(pplist);
if (*pplist == NULL)
{
return NULL;
}
if (cur->next == NULL)
{
return *pplist;
}
while (cur != tail)
{
while (cur->next != tail)
{
if ((cur->data) > (cur->next->data))
{
Datetype tmp = 0;
tmp = cur->data;
cur->data = cur->next->data;
cur->next->data = tmp;
}
cur = cur->next;
}
tail = cur;
cur = *pplist;
}
return *pplist;
}
//逆置无头单链表
void Reverse(pList *pplist)
{
pNode newHead = NULL;//指向第一个节点
pNode cur = *pplist;
pNode tmp = NULL;
assert(pplist);
//没有节点、一个节点
if ((*pplist == NULL) || ((*pplist)->next == NULL))
{
return;
}
/*newHead = *pplist;
cur = newHead->next;*/
//newHead->next = NULL;
while (cur)
{
tmp = cur;
cur = cur->next;
tmp->next = newHead;
newHead = tmp;
}
*pplist = newHead;
}
//从尾到头打印单链表 递归实现
void PrintReverse(pList plist)
{
pNode cur = plist;
if (cur == NULL)
{
return;
}
else
{
PrintReverse(cur->next);
printf("%d-->", cur->data);
}
}
//删除一个无头单链表的非尾节点
void DelNottailNode(pNode pos)
{
pNode del = NULL;
assert(pos->next);
del = pos->next;
pos->data = del->data;
pos->next = pos->next->next;
free(del);
del = NULL;
}
//在无头单链表的非头节点前插入一个节点
void InsertFrontNode(pNode pos,Datetype d)
{
Datetype tmp = 0;
pNode newNode = BuyNode(d);
newNode->next = pos->next;
pos->next = newNode;
tmp = pos->data;
pos->data = pos->next->data;
pos->next->data = tmp;
}
//单链表实现约瑟夫环
pNode JosephCycle(pList* pplist,int num)
{
pNode cur = *pplist;
pNode del = NULL;
assert(pplist);
while (1)
{
int count = num;
if (cur = cur->next)
{
break;
}
while (--count)
{
cur = cur->next;
}
printf("%d ", cur->data);
del = cur->next;
cur->data = cur->next->data;
cur->next = cur->next->next;
free(cur);
cur = NULL;
}
return cur;
}
//合并两个有序链表,合并后还是一条有序链表
pList Merge(pList l1, pList l2)
{
pNode cur1 = l1;
pNode cur2 = l2;
pNode newHead = NULL;
pNode tail = NULL;
if ((l1 == NULL) && (l2 == NULL))
{
return NULL;
}
if (l1 == NULL)//则l2不为NULL
{
return l2;
}
if (l2 == NULL)
{
return l1;
}
if (cur1->data <= cur2->data)
{
newHead = cur1;
cur1 = cur1->next;
tail = newHead;
}
else
{
newHead = cur2;
cur2 = cur2->next;
tail = newHead;
}
while (cur1 && cur2)
{
if (cur1->data < cur2->data)
{
tail->next = cur1;
cur1 = cur1->next;
tail = tail->next;
}
else
{
tail->next = cur2;
cur2 = cur2->next;
tail = tail->next;
}
}
if (cur1 == NULL)
{
tail->next = cur2;
}
else
{
tail->next = cur1;
}
return newHead;
}
//递归合并
pList Merge2(pList l1, pList l2)
{
pNode cur1 = l1;
pNode cur2 = l2;
pNode newHead = NULL;
pNode tail = NULL;
if ((l1 == NULL) && (l2 == NULL))
{
return NULL;
}
if (l1 == NULL)//则l2不为NULL
{
return l2;
}
if (l2 == NULL)
{
return l1;
}
if (cur1->data <= cur2->data)
{
newHead = cur1;
cur1 = cur1->next;
tail = newHead;
tail->next = Merge2(cur1, cur2);
}
else
{
newHead = cur2;
cur2 = cur2->next;
tail = newHead;
tail->next = Merge2(cur1, cur2);
}
return newHead;
}
//查找中间节点的位置,要求只能遍历一次链表
pNode FindMidNode(pList plist)
{
pNode fast = plist;
pNode slow = plist;
if (plist == NULL)
return NULL;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
//查找单链表的倒数第k个节点,要求只能遍历一次链表
void DelkNode(pList* pplist, int k)
{
pNode fast = *pplist;
pNode slow = *pplist;
pNode del = NULL;
assert(pplist);
while (fast && fast->next)
{
fast = fast->next;
if (--k <= 0)
slow = slow->next;
}
if (k <= 0)
{
del = slow->next;
slow->data = slow->next->data;
slow->next = slow->next->next;
free(del);
del = NULL;
}
}
//判断链表是否带环
pNode CheckCircle(pList plist)
{
pNode fast = plist;
pNode slow = plist;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
return slow;
}
}
return NULL;
}
//环的长度
int Circlelength(pNode meet)
{
pNode cur = meet;
pNode CheckCircle(pList plist);
int count = 0;
do{
count++;
cur = cur->next;
} while (cur != meet);
return count;
}
//求环的入口点
pNode GetCircleEntry(pList plist, pNode meet)
{
pNode cur = plist;
while (cur != meet)
{
cur = cur->next;
meet = meet->next;
}
return cur;
}
//判断两条链表是否相交(不带环)
int CheckCross(pList plist1, pList plist2)
{
if ((plist1 == NULL) || (plist2 == NULL))
{
return 0;
}
while (plist1->next)
{
plist1 = plist1->next;
}
while (plist2->next)
{
plist2 = plist2->next;
}
return plist1 == plist2;
}
//复杂链表的打印
void PrintComplexNode(pComplexNode head)
{
pComplexNode cur = head;
while (cur)
{
printf("%d-->", cur->data);
printf("random-->[%d]--next-->", cur->random->data);
cur = cur->next;
}
printf("NULL\n");
}
pComplexNode CreateComplexNode(Datetype d)
{
pComplexNode newNode = (pComplexNode)malloc(sizeof(ComplexNode));
if (newNode == NULL)
{
perror("malloc");
return NULL;
}
newNode->data = d;
newNode->next = NULL;
newNode->random = NULL;
return newNode;
}
//复杂链表的复制
pComplexNode CloneComplexlist(pComplexNode head)
{
pComplexNode cur = head;
pComplexNode tmp = NULL;
pComplexNode copy = NULL;
pComplexNode tail = NULL;
//1.复制每个节点并插入到当前节点的后面
while (cur)
{
pComplexNode newNode = CreateComplexNode(cur->data);
tmp = cur;
cur = cur->next;
newNode->next = cur;
tmp->next = newNode;
}
//2.调整random指针
cur = head;
while (cur)
{
cur->next->random = cur->random->next;
cur = cur->next->next;
}
//3.分离两条链表
cur = head;
copy = cur->next;
tail = copy;
while (tail->next)
{
tail->next = tail->next->next;
cur->next = tail->next;
cur = cur->next;
tail = tail->next;
}
cur->next = NULL;
return copy;
}
下面是我在写链表过程中的部分测试代码,很遗憾没有写每一个函数的测试用例,这部分还请读者自行上机测试。
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
void Test()
{
pList plist1 = NULL;
pList plist2 = NULL;
pNode ret = NULL;
InitList(&plist1);
InitList(&plist2);
PushBack(&plist1, 1);
PushBack(&plist1, 2);
PushBack(&plist1, 3);
PushBack(&plist1, 4);
PushBack(&plist1, 5);
PushBack(&plist2, 7);
PushBack(&plist2, 8);
PushBack(&plist2, 9);
Find(plist2, 9)->next = Find(plist1, 3);
printf("%d\n", CheckCross(plist1, plist2));
//Print(plist1);
//ret = CheckCircle(plist1);
//if (ret == NULL)
//{
// printf("no\n");
//}
//else
//{
// printf("%d\n", Circlelength(ret));
// //printf("yes\n");
//}
/*PushBack(&plist2, 2);
PushBack(&plist2, 4);
PushBack(&plist2, 6);
PushBack(&plist2, 8);
PushBack(&plist2, 10);
Print(plist2);
ret = Merge2(plist1, plist2);*/
//Print(ret);
//DestroyList(&ret);
}
void Test1()
{
pComplexNode pNode1 = CreateComplexNode(1);
pComplexNode pNode2 = CreateComplexNode(2);
pComplexNode pNode3 = CreateComplexNode(3);
pComplexNode pNode4 = CreateComplexNode(4);
pComplexNode ret = NULL;
pNode1->next = pNode2;
pNode2->next = pNode3;
pNode3->next = pNode4;
pNode1->random = pNode4;
pNode2->random = pNode1;
pNode3->random = pNode2;
pNode4->random = pNode2;
ret = CloneComplexlist(pNode1);
PrintComplexNode(pNode1);
PrintComplexNode(ret);
}
int main()
{
Test1();
/*pList plist = NULL;
pNode ret = NULL;
InitList(&plist);
PushBack(&plist, 1);
PushBack(&plist, 2);
PushBack(&plist, 3);
PushBack(&plist, 4);*/
//Find(plist, 4)->next = plist;
//ret = JosephCycle(&plist,3);
//printf("%d ", ret);
//Insert(&plist, Find(plist, 3), 6);
/*PushBack(&plist, 5);
DelNottailNode(Find(plist, 2));
InsertFrontNode(Find(plist, 3), 6);*/
//Print(plist);
//BubbleSort(&plist);
//Erease(&plist, Find(plist, 2));
//Erease(&plist, Find(plist, 5));
//Erease(&plist, Find(plist, 4));
//Reverse(&plist);
//Print(plist);
//PrintReverse(plist);
//Remove(&plist, 3);
/*PopBack(&plist);
Print(plist);
PopFront(&plist);
ret = Find(plist, 4);*/
//printf("%d ", ret->data);
//Print(plist);
//void DestroyList(plist);
system("pause\n");
return 0;
}