本文是我关于单链表的一些面试题的理解。有不正确的请批评指正。主要有:
1 单链表从尾到头打印:(递归与非递归方式)
递归方式实现单链表从尾到头打印:要实现长度为k的链表从尾到头打印;,若长度为k-1的单链表从尾到头打印已知,只需要打印最后一个结点,再从尾到头打印长度为k-1的单链表,以此类推;主要代码如下所示:
//递归方式打印
//1. 长度为k的链表直接打印
//2. 长度为k+1的单链表
//(已知:长度为K的单链表的从尾到头的打印问题,{同类型子问题},求k+1)
void ReversePrint2(SListNode *pFirst)
{
//只有一个结点,直接打印
if(pFirst->pNext == NULL)
printf("%d ",pFirst->data);
//多个结点,递归打印
else
{
ReversePrint2(pFirst->pNext);
//链表中,除了pFirst之外的所有结点均已经打印
printf("%d ",pFirst->data);
}
}
非递归方式实现单链表从尾到头打印:找到要打印的结点,并记录,(如果该结点的下一个结点不为空,就一直循环)然后打印该结点的数据域的值。主要代码如下所示:
////从尾到头打印单链表
// 非递归方法
void ReversePrint1(SListNode *pFirst)
{
SListNode *cur;
SListNode *end = NULL;//要打印结点的后一个结点
while(end != pFirst)
{
cur = pFirst;
while(cur ->pNext != end)
{
cur = cur ->pNext;
}
//要打印的结点
printf("%d ",cur->data);
end = cur;
}
printf(" \n");
return 0;
}
2 逆置链表
逆置链表的实现思想主要是:将目标链表上的值头删下来,分别头插到新的空链表中。举例:(如下图)
主要的代码如下:
//链表逆置
SListNode* ReverseList(SListNode *pFirst)
{
SListNode * pNode;//记录被头删的值
SListNode * cur = pFirst;
SListNode * end=NULL;//声明空链表
SListInit(&end);
while(cur != NULL)
{//从原链表中头删(并未删除,只是取下来)
pNode = cur;
cur = cur ->pNext;
//pNode就是被删下来的结点
pNode->pNext = end;
end = pNode;
}
return end;
}
3 删除非尾无头链表
主要思想是替换法:将下一个结点的值赋值给指定结点,然后将指定结点的指针域改为下一个结点的指针域,free掉下一个结点即可。即:将下一个结点cur的data赋值给pos->data;然后让pos->pNext = cur ->pNext最后free掉cur。主要代码如下:
// 删除非尾无头链表
//替换法,将下一个结点cur的data赋值给pos->data;
//然后让pos->pNext = cur ->pNext最后free掉cur
void RemoveNodeNotTail(SListNode *pos)
{
SListNode *cur = pos->pNext;
pos->data = cur->data ;
pos->pNext = cur->pNext ;
free(cur);
}
4 无头链表前插入
主要思想也是替换法。创建一个新结点,依次将指定位置后边的值前移一位。即:pNode ->data = pos ->data;pNode ->pNext = pos ->pNext;改变pos的值与指针指向,即:pos ->data = data;pos ->pNext = pNode。主要代码如下:
// 无头链表前插入
// 替换法,(创建一个新结点Pnode,依次将pos后边的值向前移一位,
// 即:pNode ->data = pos ->data;pNode ->pNext = pos ->pNext;改变pos的值与指针指向,
// 即:pos ->data = data;pos ->pNext = pNode;)
void InsertNoHead(SListNode *pos, int data)
{
SListNode *pNode = (SListNode *)malloc(sizeof(SListNode));
assert(pNode != NULL);
pNode ->data = pos ->data;
pNode ->pNext = pos ->pNext;
pos ->data = data;
pos ->pNext = pNode;
}
5 约瑟夫环
主要分两步:1 把链表构成环(先找到最后一个结点tail,再让tail->pNext = pFirst) 2 如果剩下的个数>1,每前进k个结点,删除该结点。主要代码如下:
SListNode * JocephCircle(SListNode *pFirst, int k)
{//1 把链表构成环(先找到最后一个结点tail,再让tail->pNext = pFirst)
SListNode * tail;
SListNode * cur;
SListNode * pre;
int i;
tail = pFirst;
for(tail = pFirst; tail->pNext != NULL; tail = tail->pNext)
{
}//此时tail是最后一个结点
tail->pNext = pFirst;//构成环
//2 如果剩下的个数大于,每前进K个结点,删除该结点
cur = pFirst;
//结束条件是链表中只剩下一个结点,即:cur->pNext != cur
while(cur->pNext != cur)
{
pre = pFirst;//用来记录要删除结点的前一个结点
for(i=0; i<k-1; i++)
{
cur = cur->pNext;
}
//cur就是第k个结点,即要删除的结点
pre ->pNext = cur ->pNext;
free(cur);
//继续循环
cur = pre->pNext;
}
cur ->pNext = NULL;
return cur;
}
以上程序需要建立在链表创建、插入,打印的前提下,用到的代码如下:
typedef int DataType;
//创建单链表的成员,其实就是结点
typedef struct SListNode {
DataType data; // 值
struct SListNode *pNext; // 指向下一个结点
} SListNode; //SListNode为这个结构体的别名
// 初始化链表
void SListInit(SListNode **ppFirst)
{
*ppFirst = NULL;
}
//打印
void SListprint(SListNode *ppFirst)
{
SListNode *p = ppFirst;
if(p == NULL)
{
printf("空链表\n");
}
else{
for(p = ppFirst;p != NULL;p = p->pNext)
{
printf("%d ",p->data);
}
}
printf(" \n");
}
//创建新结点,结点的数据域为data,pNext域设置为空
SListNode *_CreateNode(DataType data)
{
SListNode *NewNode; //声明新结点
NewNode = (SListNode *)malloc(sizeof(SListNode));//创建新结点
if(NewNode == NULL)
{
return;
}//创建失败
NewNode ->data = data;
NewNode ->pNext = NULL;//创建成功
}
// 尾部插入(先找到最后一个结点,并把它记录下来,再把最后一个结点的pNext域指向新的结点)
void SListPushBack(SListNode** ppFirst, DataType data)
{
SListNode *p;//新结点p
SListNode *p1;
p = _CreateNode(data);
p1 = *ppFirst;//用来遍历链表(开始位于头结点处)
//空链表
if( (*ppFirst) == NULL ){
*ppFirst = p;
return;
} else{
for(p1 = *ppFirst; p1->pNext != NULL; p1 = p1->pNext)
{
}
p1 ->pNext = p;//此时p1为最后一个结点
}
}
上述5个面试题的测试代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>
int main()
{
SListNode *p1;
SListNode *result;
SListInit(&p1);
SListPushBack(&p1,3);
SListPushBack(&p1,5);
SListPushBack(&p1,6);
SListPushBack(&p1,1);
SListPushBack(&p1,2);
SListPushBack(&p1,4);
SListPushBack(&p1,7);
SListprint(p1);
result = JocephCircle(p1, 3);
SListprint(result);
RemoveNodeNotTail(result);
InsertNoHead(result,9);
SListprint(p1);
result = ReverseList(p1);
SListprint(result);
system("pause");
return 0;
}
剩余的冒泡排序、合并有序链表、找中间节点、找到并删除倒数第k个结点的说明见下一篇博客(单链表面试题(二))。由于篇幅问题,造成不便,敬请谅解。