下面的面试题用到的数据类型及函数
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
typedef struct SLinkList
{
DataType data;
struct SLinkList*PNext;
}SNode;
// 初始化
void SLinkList_InIt(SNode **Phead)
{
assert(Phead != NULL);
*Phead = NULL;
}
1.从尾到头打印单链表
有两种方法:
- 递归法
注意事项:递归的结束结束条件比较重要,当链表的指针走到尾部(NULL)时结束递归。
下面是原理图,便于理解:
void SLinkList_Print(SNode *Phead) { assert(Phead != NULL); while (Phead != NULL)//递归结束条件 { printf("%d ", Phead->data); Phead = Phead->PNext; } printf("\n"); }
- 循环法
思路:定义两个指针,一个从头开始遍历Pnode,另外一个从尾部开始Pend每次当Pnode等于Pend的 时候,更新Pend的值,使它往前走一步,Phead又一次从头遍历,直到Pend走到Phead的位置时打印完成。
void ReverseLoop(SNode*Phead)
{
assert(Phead != NULL);
SNode*Pnode = Phead;
SNode*Pend = NULL;
if (Pnode == NULL)
{
printf("NULL\n");
return;
}
while (Pend != Phead)//当Pend的位置从链表的尾部移到头部时说明打印完成
{
for (Pnode = Phead; Pnode->PNext != Pend; Pnode = Pnode->PNext)//让Pnode每一次走到Pend的位置停下来让后打印Pnode->data
{
;
}
printf("%d ", Pnode->data);
Pend = Pnode;
}
printf("\n");
}
原理图:
2.删除一个无头单链表的分为节点(不能遍历链表)
之前我们找单链表中的某个节点的时候是遍历整个链表,现在要求不能遍历链表,那我们就另辟蹊径。
思路:
- 将要删除的节点的下一个节点Pnext中的data放到要删除的节点Pnode中
- 删除Pnext
- 让要删除的节点Pnode指向PNext的下一个节点
void ListDel(SNode**Pos)
{
assert(Pos != NULL);
assert((*Pos)->PNext != NULL);
SNode*PNode = (*Pos)->PNext;
(*Pos)->data = PNode->data;
(*Pos)->PNext = PNode->PNext;
free(PNode);
}
原理图:
3.在无头链表的一个节点前插入一个节点(不能遍历链表)
这个问题其实和上面的那个问题十分的相似,
思路:
- 因为不能遍历链表,所以这里是将要插入的位置对应的节点的data给要插入的新节点的data
- 然后将要插入的内容(data)放到要插入的位置的节点的内容中即Posnode->data=data
- 将存有要插入位置data的新节点的PNext改为原来要插入的位置对应节点的下一个节点
- 使要插入的位置的节点的PNext指向新的节点
原理图:
//创建一个新结点
static SNode* BuySListNode(DataType data)
{
SNode *pNewNode = (SNode*)malloc(sizeof(SNode));
if (pNewNode == NULL)
{
assert(0);
return NULL;
}
pNewNode->data = data;
pNewNode->PNext = NULL;
return pNewNode;
}
//头部插入
void PushFront(SNode**Phead, DataType data)
{
assert(Phead != NULL);
if (*Phead == NULL)
{
*Phead = BuySListNode(data);
return;
}
else
{
SNode *pNewNode = BuySListNode(data);
pNewNode->PNext = *Phead;
*Phead = pNewNode;
}
}
void ListInsert(SNode**Posnode, DataType data)
{
assert(Posnode != NULL);
if (*Posnode == NULL)
{
PushFront(Posnode, data);
return;
}
SNode*Pnode = *Posnode;
SNode*PNewnode = BuySListNode(Pnode->data);
Pnode->data = data;
PNewnode->PNext = Pnode->PNext;
Pnode->PNext = PNewnode;
}
4.单链表实现约瑟夫环
思路:
- 报数
1. 先将单链表围成一个环
2.用一个指针遍历链表,如果遇到要删的数字保存它的前一个节点
- 删除节点,然后继续报数,直至只剩一个节点退出(直接删除和替换删除。替换法删除无法删除最后一个节点,但是这里是一个循环的环所以不考虑)
原理图:
SNode* ListJosephCircle(SNode*PFirst, int k)
{
assert(PFirst != NULL);
if (PFirst == NULL)
{
return NULL;
}
SNode *pDel = PFirst;
SNode *pCur = NULL;
int n = 0;
//将单链表构成环
while (pDel->PNext != NULL)
{
pDel = pDel->PNext;
}
pDel->PNext = PFirst;
while (pDel->PNext != pDel)//当只剩下一个节点的时候停止循环
{
n = k;
while (--n)//这里每次循环k-1次,注意是--n
{
pCur = pDel;
pDel = pDel->PNext;
}
pCur->PNext = pDel->PNext;//更新链表删除要删的结点
free(pDel);
pDel = pCur->PNext;//更新游标重新开始从k开始遍历链表
}
return pDel;
}
5.逆置/反转单链表
这里有两种方法。
- 改变Pnext的指向
让链表内部每个节点的PNext反向指向前一个(三个指针) 时间复杂度为O(N),利用三者指针遍历链表P1,P2,P3,让他们之间的Pnext反过来指向前一个。
原理图:
//让链表内部每个节点的PNext反向指向前一个(三个指针) 时间复杂度为O(N)
void ReverseList(SNode**PFirst)
{
assert(PFirst != NULL);
SNode*P1 = NULL;
SNode*P2 = *PFirst;
SNode*P3 = (*PFirst)->PNext;
while (P2 != NULL)
{
P2->PNext = P1;
P1 = P2;
P2 = P3;
if (P3 != NULL)
{
P3 = P3->PNext;
}
}
*PFirst = P1;
}
- 头删头插
思路:
1.新建一个链表将旧链表从头部开始把节点一个一个POP出来
2.将POP出来的节点头插到新的链表中
注意:
这里是头删所以当删掉Pnode的第一个节点后不需要使Pnode指向它原来的下一个节点(不然会跳过节点)
//时间复杂度为O(1)
SNode* PopFront(SNode**Phead)
{
assert(Phead != NULL);
if (*Phead != NULL)
{
SNode* pNewNode = *Phead;
*Phead = (*Phead)->PNext;
return pNewNode;
}
else
return NULL;
}
//头部插入
void PushFront(SNode**Phead, DataType data)
{
assert(Phead != NULL);
if (*Phead == NULL)
{
*Phead = BuySListNode(data);
return;
}
else
{
SNode *pNewNode = BuySListNode(data);
pNewNode->PNext = *Phead;
*Phead = pNewNode;
}
}
void ReverseList1(SNode**PFirst)
{
assert(PFirst!= NULL);
SNode*PHead = NULL;
SNode*Pnode = *PFirst;
SLinkList_InIt(&PHead);
while(Pnode!= NULL)
{
SNode*PNewnode = PopFront(&Pnode);//弹出第一个节点
PushFront(&PHead, PNewnode->data); //头插到新的链表中
}
*PFirst = PHead;//将新的链表赋给旧的链表
}
6.单链表的冒泡排序
冒泡排序法是最简单的排序法,这里排序链表和之前给数组排序的思路相同,这里就不再赘述了。
void ListBubbleSort(SNode**Phead)
{
assert(Phead != NULL);
if (*Phead == NULL)
{
return;
}
SNode*tmp = (SNode*)malloc(sizeof(SNode));
SNode*Pnode = *Phead;
SNode*Pcur = NULL;
for (Pnode = *Phead; Pnode != NULL; Pnode = Pnode->PNext)
{
for (Pcur = Pnode->PNext; Pcur != NULL; Pcur = Pcur->PNext)
{
if (Pnode->data > Pcur->data)
{
tmp->data = Pnode->data;
Pnode->data = Pcur->data;
Pcur->data = tmp->data;
}
}
}
}