链表的相关面试题
- 我这里是单链表,并且带头结点,其实我感觉带不带头结点都无所谓了。只要思路是正确的,那么代码实现起来相对简单。
链表的基本操作
- 我的头文件中的一些定义
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
#define Max 100
typedef struct Node //一般链表的定义
{
DataType data;
struct Node* next;
}Node, *pNode, List, *pList;
typedef struct ComplexNode //定义复杂链表,一个链表的每个节点,有一个指向next指针指向下一个节点,
//还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表。
{
DataType _data;
struct ComplexNode* _next;
struct ComplexNode* _random;
}ComplexNode;
- 这些是我的单链表中的基本操作,(增删查改)
void InitLinkList(pList* pplist); //初始化
void DestroyLinkList(pList* pplist); //删除
void PushBack(pList* pplist, DataType d); //尾插
void PrintLinkList(pList plist); //显示
void PopBack(pList* pplist); //尾删
int GetListLength(pList head);//链表长度
void PushFront(pList *pplist, DataType x);//头插
void PopFront(pList *pplist);//头删
pList Find(pList plist, DataType d);//查找
- 这里是链表的一些面试题
void Insert(pList *pplist, int pos, DataType d);//插入
void Remove(pList *pplist, DataType d);//移除链表中指定元素的第一个
void RemoveAll(pList *pplist, DataType x);//移除链表中所有指定元素
void Erase(pList *pplist,int pos);//删除指定位置的元素
void PrintTailToHead(pList *pplist);//从尾到头打印单链表
void ReverseList(pList* pplist);//逆置/反转单链表
void BubbleSort(pList * pplist);//单链表排序(冒泡排序)
int Size(pList plist); //大小
int FindMidNode(pList plist);//查找单链表的中间节点,要求只能遍历一次链表
int DelLastKNode(pList plist, int k); //查找单链表的倒数第k个节点,要求只能遍历一次链表
int pLinkNodeJosephCycle(pList * pplist, int num); //单链表实现约瑟夫环
void EraseNotTail(pList pos);//删除一个无头单链表的非尾节点
void InsertNode(pList pos);//在无头单链表的一个节点前插入一个节点
pList pLinkNodeMerge(pList plist1, pList plist2); //合并两个有序链表, 合并后依然有序
void UnionSet(pList* plist1, pList* plist2); //求两个有序单链表交集(差集)
pList CheckCycleandLength(pList plist);//判断单链表是否带环?若带环,求环的长度?求环的入口点?
void CheckNoCross(pList list1, pList list2);////判断两个链表是否相交,若相交,求交点。(链表不带环)
pList CheckHaveCross(pList list1, pList list2);//判断两个链表是否相交,若相交,求交点。(链表带环)
ComplexNode * BuyComplexNode(DataType x);// 创建一个复杂链表的结点
void Display(const ComplexNode * cplist);//打印复杂的单链表
ComplexNode * CopyComplexNode(ComplexNode * cplist);//复杂链表的复制
- 初始化我的链表,给链表申请一个头结点
void InitLinkList(pList* pplist)
{
pList head=NULL; //头结点
head = (pList)malloc(sizeof(List));
head->next = NULL;
(*pplist) = head;
return;
}
- 清空链表中所有节点,需要一个节点一个节点的删除。
void DestroyLinkList(pList* pplist)
{
pList stmp = NULL;
pList etmp = NULL;
stmp = *pplist;
etmp = stmp;
if ((*pplist) == NULL)
{
printf("链表为空\n");
}
else
{
while (etmp != NULL)
{
stmp = stmp->next;
free(etmp);
etmp = NULL;
etmp = stmp;
}
printf("删除链表成功!\n");
}
(*pplist)->next = NULL;
return;
}
- 尾插一个节点,先找到链表的最后一个节点,然后在加新的节点
void PushBack(pList* pplist, DataType d)
{
pList Add = NULL;
pList tmp = NULL;
tmp = (pList)malloc(sizeof(List));
tmp = (*pplist);
Add = (pList)malloc(sizeof(List));
Add->data = d;
Add->next = NULL;
if (tmp->next == NULL)
{
tmp->next = Add;
}
else
{
while (tmp != NULL)
{
if (tmp->next == NULL)
{
tmp->next = Add;
break;
}
tmp = tmp->next;
}
}
return;
}
- 打印链表的节点
void PrintLinkList(pList plist)
{
pList tmp = NULL;
tmp = plist;
tmp = tmp->next;
if (tmp == NULL)
{
printf("链表为空\n");
return;
}
while (tmp)
{
printf("%d ", tmp->data);
tmp = tmp->next;
}
printf("\n");
}
- 尾删链表的节点
void PopBack(pList* pplist) //尾删
{
pList tmp = NULL;
tmp = (*pplist);
if (tmp->next == NULL) //无节点
{
printf("链表为空\n");
return;
}
else if (tmp->next->next == NULL)
{
free(tmp->next);
tmp->next = NULL;
return;
}
else
{
while (tmp->next->next != NULL) //二个以上节点的删除
{
tmp= tmp->next;
}
free(tmp->next->next);
tmp->next = NULL;
}
}
- 求链表的长度
int GetListLength(pList plist)
{ //不需要断言 链表可以为空
int len = 0;
while (plist->next != NULL)
{
plist = plist->next;
len++;
}
return len;
}
- 在链表中头插一个节点
void PushFront(pList *pplist, DataType d)
{
assert(pplist);
pList Add = NULL;
pList tmp = NULL;
tmp = (*pplist);
Add = (pList)malloc(sizeof(List));
Add->data = d;
Add->next = tmp->next;
tmp->next = NULL;
tmp->next = Add;
}
- 在链表中头删一个节点
void PopFront(pList* pplist)
{
assert(pplist);
pList tmp = NULL;
pList p = NULL;
tmp = (*pplist);
if (tmp->next == NULL)
{
return;
}
else if (tmp->next->next == NULL)
{
free(tmp->next);
tmp->next = NULL;
}
else
{
p = tmp->next->next;
free(tmp->next);
tmp->next = NULL;
tmp->next = p;
}
}
- 在链表中查找节点
pList Find(pList plist, DataType d)
{
while (plist)
{
if (d == plist->data)
{
printf("找到了");
return plist;
}
plist = plist->next;
}
return NULL;
}
- 在链表中指定位置插入一个节点
void Insert(pList *pplist, int pos, DataType d)
{
pList tmp = NULL;
pList Add = NULL;
Add = (pList)malloc(sizeof(List));
Add->data = d;
Add->next = NULL;
tmp = (*pplist);
if (pos <= 0 || pos > Size(*pplist))
{
printf("位置有误\n");
return;
}
if (tmp->next == NULL)
{
printf("空链表");
return;
}
else if (tmp->next->next == NULL)
{
PushFront(pplist, d);
}
else
{
for (int i = 1; i < pos ; i++)
{
tmp = tmp->next;
}
Add->next = tmp->next;
tmp->next = NULL;
tmp->next = Add;
}
}
- 删除链表中指定元素的第一个
void Remove(pList *pplist, DataType d)
{
assert(pplist);
pList tmp = NULL;
pList smp = NULL;
tmp = (*pplist);
if (tmp->next == NULL)
{
printf("空链表\n");
return;
}
else if ( tmp->next->next == NULL && d == tmp->next->data)
{
free(tmp->next);
tmp->next = NULL;
}
else
{
while (tmp->next->next != NULL)
{
if (d == tmp->next->data)
{
smp = tmp->next->next;
free(tmp->next);
tmp->next = NULL;
tmp->next = smp;
return;
}
tmp = tmp->next;
}
}
}
- 删除链表中所有指定元素
void RemoveAll(pList *pplist, DataType d)
{
pList tmp = NULL;
pList smp = NULL;
tmp = (*pplist);
while (tmp->next->next != NULL)
{
if (d == tmp->next->data)
{
smp = tmp->next->next;
tmp->next = NULL;
tmp->next = smp;
continue;
}
tmp = tmp->next;
}
}
- 删除链表中指定位置的节点
void Erase(pList *pplist, pList pos)
{
pList tmp = NULL;
pList smp = NULL;
tmp = (*pplist);
if (tmp->next == NULL)
{
printf("空链表\n");
return;
}
else if (tmp->next->next == NULL && pos == tmp->next)
{
free(tmp->next);
tmp->next = NULL;
}
else
{
smp = pos->next;
int i = pos->data;
pos->data = smp->data;
smp->data = i;
pos->next = pos->next->next;
}
}
- 把链表从尾到头打印
void PrintTailToHead(pList *pplist)
{
//时间复杂度较高
pList tmp = NULL;
pList end = NULL;//标兵
tmp = (*pplist);
end = (*pplist);
/*while (end->next != NULL)
{
end = end->next;
}
while (tmp->next != NULL)
{
if (tmp->next == end)
{
printf("%d ", end->data);
end = tmp;
tmp = (*pplist);
continue;
}
tmp = tmp->next;
}*/
//空间复杂度较高
/*DataType arr[Max] = { 0 };
int i = 0;
while (tmp->next != NULL)
{
arr[i] = tmp->data;
tmp = tmp->next;
i++;
}
arr[i] = tmp->data;
for (; i > 0; i--)
{
printf("%d ", arr[i]);
}*/
//递归的方式
if (tmp->next == NULL)
{
return;
}
PrintTailToHead(&(tmp->next));
printf("%d ", tmp->next->data);
}
- 链表的逆置。利用两个指针,不懂得画画图
void ReverseList(pList* pplist) //逆置(反转0)
{
pList q = (*pplist)->next;
pList p = q->next;
while (p) //当p指向NULL时,表明倒置完成,退出循环
{
q->next = p->next;
p->next = (*pplist)->next;
(*pplist)->next = NULL;
(*pplist)->next = p;
p = q->next;
}
}
- 链表的冒泡排序 (只交换数据,不交换节点)。
void BubbleSort(pList * pplist) //交换数据没写交换节点
{
pList q = NULL;
pList p = NULL;
for (int i = 0; i < Size(*pplist); i++) //Size 函数是求链表的长度
{
pList q = (*pplist)->next;
pList p = q->next;
for (int j = 0; j < Size(*pplist) - i - 1; j++)
{
while (p)
{
if (q->data < p->data)
{
DataType tmp = q->data;
q->data = p->data;
p->data = tmp;
}
p = p->next;
q = q->next;
}
}
}
}
- 求链表的中间节点(定义快慢指针,快指针一次走两个节点,慢指针一次走一个节点,当快指针走到链表的结尾时,慢指针刚好走到链表中间)
int FindMidNode(pList plist)
{
pList fast = NULL;
pList slow = NULL;
fast = plist->next;
slow = fast;
while (fast)
{
if (fast->next)
{
fast = fast->next->next;
}
else
{
break;
}
slow = slow->next;
}
return slow->data;
}
- 查找单链表的倒数第k个节点,要求只能遍历一次链表。(定义两个指针指向头节点,先让一个指针走k个节点,然后两个同时往后走,快指针到链表的结尾时,慢指针刚好就是链表倒数第k个节点)
int DelLastKNode(pList plist, int k)
{
pList p = NULL;
pList q = NULL;
q = plist->next;
p = q;
for (int i = 1; i < k; i++)
{
q = q->next;
}
while (q->next)
{
p = p->next;
q = q->next;
}
return p->data;
}
- 单链表实现约瑟夫环(约瑟夫环相当于链表的尾节点指向链表中的某一节点,从而形成环。)
int pLinkNodeJosephCycle(pList * pplist, int num)
{
pList tmp = NULL;
pList p = NULL;
p = (*pplist)->next;
tmp = (*pplist);
while (tmp->next)
{
tmp = tmp->next;
}
tmp->next = (*pplist)->next;
while (p != p->next)
{
for (int i = 1; i < num - 1; i++)
{
p = p->next;
}
p->next = p->next->next;
p = p->next;
}
return p->data;
}
-删除一个无头单链表的非尾节点(要删除指定位置元素,那么就把指定位置元素和下一位置元素交换位置,然后删除下一个节点即可)
void EraseNotTail(pList pos) //删除一个无头单链表的非尾节点
{
pList tmp = pos->next;
pList smp = NULL;
smp = pos->data;
pos->data = tmp->data;
tmp->data = smp;
pos->next = tmp->next;
}
- 在无头单链表的一个节点前插入一个节点(在这个节点后面插入一个新的节点,然后交换这个节点和新的节点的位置就可以了)
void InsertNode(pList pos) //在无头单链表的一个节点前插入一个节点
{
pList tmp = pos;
pList smp = NULL;
pList rmp = NULL;
smp = (pList)malloc(sizeof(List));
smp->data = 6;
smp->next = NULL;
while (tmp->next)
{
tmp = tmp->next;
}
tmp->next = smp;
int t = pos->data;
pos->data = smp->data;
pos = pos->next;
while (pos)
{
int m = pos->data;
pos->data = t;
t = m;
pos = pos->next;
}
}
- 列表内容
pList pLinkNodeMerge(pList plist1, pList plist2) //合并两个(有序链表!!!), //合并后依然有序。(申请一个新链表,然后两个链表从一个元素开始比较,小的存储,并 //且指针向后移动,大的不动,再次比较。等到一个链表到尾的时候,就把另一个链表的直 //接插到新链表的后面即可。)
{
pList pOneNode = plist1->next;
pList pTwoNode = plist2->next;
pList pRemain = plist1->next;
pList pResult = NULL;
pList pNode = NULL;
InitLinkList(&pResult);
while (pOneNode != NULL && pTwoNode != NULL)
{
if (pOneNode->data < pTwoNode->data)
{
PushBack(&pResult, pOneNode->data);
pOneNode = pOneNode->next;
}
else
{
PushBack(&pResult, pTwoNode->data);
pTwoNode = pTwoNode->next;
}
}
if (pOneNode == NULL)
{
pRemain = pTwoNode;
}
else
{
pRemain = pOneNode;
}
for (pNode = pRemain; pNode != NULL; pNode = pNode->next)
{
PushBack(&pResult, pNode->data);
}
return pResult;
}
- 求两个有序单链表交集
void UnionSet(pList* plist1, pList* plist2) //求两个有序单链表交集(差集)
{
pList tmp = NULL;
pList smp = NULL;
pList amp = NULL;
amp = (*plist1);
smp = (*plist2)->next;
tmp = (*plist1)->next;
while (tmp)
{
smp = (*plist2)->next;
while (smp)
{
if (tmp->data == smp->data)
{
amp->next->data = smp->data;
amp = amp->next;
break;
}
smp = smp->next;
}
tmp = tmp->next;
}
amp->next = NULL;
}
- 判断单链表是否带环?若带环,求环的长度?求环的入口点?(判断带环,我这里用的这种方法。定义两个指针,一个每次走两步,一个每次走一步,等到两个指针相等的时候,单链表有环。然后顺着这个交点走一圈就是环的长度。这个交点就是环的入口点)
pList CheckCycleandLength(pList plist)
{
int count = 1;
pList Head = NULL;
pList Slow = NULL;
pList tmp = NULL;
Head = plist;
Slow = plist;
while (Head && Slow->next)
{
Head = Head->next->next;
Slow = Slow->next;
if (Head == Slow)
{
printf("链表带环\n");
break;
}
}
tmp = Head->next;
while (tmp != Head)
{
tmp = tmp->next;
count++;
}
printf("链表长度:%d\n", count);
if (Head == Slow)
{
printf("环的起始位置:");
return Slow->next;
}
}
- -
void CheckNoCross(pList list1, pList list2) //判断两个链表是否相交。(假设链表不带环)(将一个链表的尾指向另一个链表的头,如果能构成一个环,那么两链表是相交的。)
{
pList plist1 = NULL;
pList plist2 = NULL;
plist1 = list1;
plist2 = list2;
while (plist1->next)
{
plist1 = plist1->next;
}
while (plist2->next)
{
plist2 = plist2->next;
}
if (plist1 == plist2)
{
printf("两链表相交\n");
}
else
{
printf("两链表不相交\n");
}
}
- 不带环链表相交后的交点(求两个链表的长度,两个长度相减,假设为k,那么先让长链表走k步,然后两个链表一起走,走到相等的位置了就是交点。)
void PointList(pList plist1, pList plist2) //不带环链表相交的交点。(假设链表不带环)
{
int count1 = Size(plist1);
int count2 = Size(plist2);
int k = count1 - count2;
if (k > 0)
{
while (k--)
{
plist1 = plist1->next;
}
while (1)
{
plist1 = plist1->next;
plist2 = plist2->next;
if (plist1 == plist2)
{
printf("交点为:%d\n", plist1->data);
break;
}
}
}
if (k < 0)
{
while (k++)
{
plist2 = plist2->next;
}
while (1)
{
plist1 = plist1->next;
plist2 = plist2->next;
if (plist1 == plist2)
{
printf("交点为:%d\n", plist1->data);
break;
}
}
}
if (k == 0)
{
while (1)
{
plist1 = plist1->next;
plist2 = plist2->next;
if (plist1 == plist2)
{
printf("交点为:%d\n", plist1->data);
break;
}
}
}
}
- 判断带环链表是否相交,并且求两链表的交点。(带环链表的相交有两种情况,1、环外相交。2、环内相交。先求两个链表的环的入口点,如果两个链表环的入口点相等,那么两链表相交,并且交点在环的上面的某一位置;那么接下来就把环的入口点的位置记下来,就可以求出交点。如果两个链表环的入口点不相等,那么两链表可能不相交,或者可能在环内部相交;让其中一个链表从环开始往后走,如果找到另外一个链表的环的入口点,那么说明两链表相交,否则两链表不相交。)
pList CheckHaveCross(pList list1, pList list2)
{
pList plist1 = NULL;
pList plist2 = NULL;
pList plist3 = NULL;
pList plist4 = NULL;
pList loop1 = NULL;
pList loop2 = NULL;
pList Cur1 = NULL;
pList Cur2 = NULL;
plist1 = list1;
plist2 = list1;
plist3 = list2;
plist4 = list2;
Cur1 = list1;
Cur2 = list2;
while (plist1 && plist2->next) //求链表1的环的起始点
{
plist1 = plist1->next->next;
plist2 = plist2->next;
if (plist1 == plist2)
{
loop1 = plist1;
break;
}
}
while (plist3 && plist4->next) //求链表2的环的起始点
{
plist3 = plist3->next->next;
plist4 = plist4->next;
if (plist3 == plist4)
{
loop2 = plist3;
break;
}
}
if (loop1 == loop2) //两链表相交,并且交点在环上面
{
int n = 0;
while (Cur1 != loop1)
{
n++;
Cur1 = Cur1->next;
}
while (Cur2 != loop2)
{
n--;
Cur2 = Cur2->next;
}
Cur1 = n > 0 ? list1 : list2;
Cur2 = Cur1 == list1 ? list2 : list1;
n = abs(n);
while (n != 0)
{
n--;
Cur1 = Cur1->next;
}
while (Cur1 != Cur2)
{
Cur1 = Cur1->next;
Cur2 = Cur2->next;
}
return Cur1;
}
else //两种情况,两链表不相交;两链表在环内相交
{
Cur1 = loop1->next;
while (Cur1 != loop1)
{
if (Cur1 == loop2)
{
return loop1;
}
Cur1 = Cur1->next;
}
return NULL;
}
}
- 关于复杂链表的复制。(创建复杂链表的节点和打印复杂链表我就不说了。我来说说关于复杂链表的复制。)
1、第一步:根据原始链表的每个结点N创建对应的N’,然后将N‘通过next
接到N的后面;
2、 第二步:设置复制出来的结点的random
。假设原始链表上的N的random
指向结点S,那么其对应复制出来的N’是N->next指向的结点,同样S’也是结点S->next指向的结点。
3、第三步:把长链表拆分成两个链表,把奇数位置的结点用next
连接起来的就是原始链表,把偶数位置的结点通过next
连接起来的就是复制链表。
ComplexNode * BuyComplexNode(DataType x) //创建一个复杂链表的结点
{
ComplexNode *cnode = (ComplexNode *)malloc(sizeof(ComplexNode));
if (cnode == NULL)//创建失败
{
perror("BuyComplexNode()::malloc");
return NULL;
}
//创建成功
cnode->_data = x;
cnode->_next = NULL;
cnode->_random = NULL;
return cnode;
}
void Display(const ComplexNode * cplist) //打印复杂的单链表
{
ComplexNode *pnode = cplist;
while (pnode)
{
printf("%d::%d -->", pnode->_data, pnode->_random->_data);
pnode = pnode->_next;
}
printf("over\n");
}
ComplexNode * CopyComplexNode(ComplexNode * cplist) //复杂链表的复制
{
ComplexNode * pold = NULL;
ComplexNode * pnew = NULL;
ComplexNode * newlist = NULL;//指向新的复杂链表的头结点的指针
pold = cplist;
//创建一条新的复杂链表
while (pold != NULL)
{
ComplexNode * new_node = BuyComplexNode(pold->_data);
if (newlist == NULL)//当新的复杂链表中没有结点时
{
newlist = new_node;
}
else //当新的复杂链表有结点时
{
ComplexNode * node = newlist;
while (node->_next != NULL)//找到最后一个结点
{
node = node->_next;
}
node->_next = new_node;//插入新的结点
}
pold = pold->_next;
}//创建新的复杂链表结束
//合并两条复杂链表
pold = cplist;
pnew = newlist;
while (pold)
{
ComplexNode * curold = NULL;
ComplexNode * curnew = NULL;
curold = pold->_next;
curnew = pnew->_next;
if (pold->_next == NULL)
{
pold->_next = pnew;
pold = curold;
pnew = curnew;
break;
}
pold->_next = pnew;
pnew->_next = curold;
pold = curold;
pnew = curnew;
}//合并两条复杂链表结束
//让新创建的那条复杂链表上的所有结点的random指针指向相应的结点
pold = cplist;
pnew = newlist;
while (pnew)
{
pnew->_random = pold->_random->_next;
pold = pnew->_next;
if (pold == NULL)//这是pnew的_next指针已经指向空
{
break;
}
pnew = pold->_next;
}//结束
//分离合并后的复杂链表
pold = cplist;
pnew = newlist;
while (pold)
{
ComplexNode * curold = NULL;
ComplexNode * curnew = NULL;
if (pnew->_next == NULL)//已经分离完成
{
pold->_next = NULL;
pnew->_next = NULL;
break;
}
curold = pold->_next->_next;
curnew = pnew->_next->_next;
pold->_next = curold;
pnew->_next = curnew;
pold = curold;
pnew = curnew;
}//分离合并的复杂链表结束
return newlist;
}