当我对指针的使用感到很简单的时候,链表给了我一记重击。
在面试中,最经常被提及的就是链表,因为它简单,但又因为需要对指针进行操作,凡是涉及到指针的,都需要我们具有良好的编程基础才能确保代码没有任何错误。
所以,整理了一些面试题,特别记录下来。
注:本文是对上一篇文章的补充。(http://blog.youkuaiyun.com/qq_35524916/article/details/66296292)
另外:在对参数进行if判空还是assert判空上,我才用如下方法:若对参数的引用非法,使用assert,否则使用if。
附上,本文要解决的问题
// 使用递归实现从尾到头打印单链表
void PrintFromTail2Head(PNode pHead);
// 删除单链表的非尾结点(不能遍历链表)
void DeleteNotTailNode(PNode pos);
// 在单链表非头结点前插入结点data
void InsertNotHeadNode(PNode pos, DataType data);
//约瑟夫环问题
void JosephCircle(PNode *pHead, size_t M);
// 查找单链表的中间结点,要求只便利一次链表
PNode FindMidNode(PNode pHead);
// 查找单链表的倒数第K个结点,要求只遍历一次链表
PNode FindLastKNode(PNode pHead, size_t k);
// 使用冒泡排序对单链表进行排序
void BubbleSort(PNode pHead);
下面一一分析:
1,利用递归逆序打印单链表
解析:利用递归思想,可以实现从后往前的打印。
注:参入参数为空的情况。
void PrintFromTail2Head(PNode pHead)
{
if (NULL == pHead)
return ;
if (pHead->_pNext)
PrintFromTail2Head(pHead->_pNext);
printf("%d->", pHead->_data);
}
2, 删除单链表的非尾结点(不能遍历单链表)
解析:如何在不知道链表节点的前一节点的情况下删除该节点?
可以遍历一次链表,然后保存两个节点的位置,再实现删除操作。
可是,当面试官要求不能遍历单链表呢?那就比较难了。
既然是非尾节点,那么可以激荡pos的下一节点,与当前节点的值互换,然后删除pos的下一节点即可。
注:链表为空或只有一个节点的情况。
void DeleteNotTailNode(PNode pos)
{
PNode pDelNode = NULL;
if ((NULL == pos) || (NULL == pos->_pNext))
return ;
pDelNode = pos->_pNext;
pos->_data = pDelNode->_data;
pos->_pNext = pDelNode->_pNext;
free(pDelNode);
pDelNode = NULL;
}
3,在单链表非头结点前插入结点data
解析:和上面的思想差不多,先将节点插入到data后面,然后互换两个节点的值。
注:链表为空。
void InsertNotHeadNode(PNode pos, DataType data)
{
PNode pNewNode = NULL;
DataType temp_data = 0;
if(NULL != pos)
return ;
if (!(pNewNode = BuyNode(data)))
return ;
pNewNode->_pNext = pos->_pNext;
pos->_pNext = pNewNode;
temp_data = pos->_data;
pos->_data = pNewNode->_data;
pNewNode->_data = temp_data;
}
4,约瑟夫环问题
解析:先将单链表的首尾相连,形成环。
然后设定一个计数器m,开始就遍历环,每遍历一个节点,m减1,同时删除该节点,直到链表只剩于一个链表。
注:链表传入的参数为空,无法解引用。
void JosephCircle(PNode *pHead, size_t M)
{
PNode pTailNode = NULL; //用来指向最后一个节点
PNode pCurNode = NULL; //用来指向数到M的节点
size_t m = 0;
assert(NULL != pHead);
assert(0 != M); //M不能小于0
pCurNode = *pHead; //pCurNode从首节点开始
if (!(pTailNode = Back(*pHead))) //指向最后一个节点。
return ;
pTailNode->_pNext = *pHead; //首尾相连形成约瑟夫环
while (pCurNode->_pNext != pCurNode) //当链表剩余不止一个节点时
{
PNode pDelNode = NULL; //用来删除的节点
m = M;
while (--m)
pCurNode = pCurNode->_pNext;//pCurNode指向数到M的节点
printf("%d->", pCurNode->_data); //打印节点信息
//将下一节点的值赋给当前节点,删除下一节点。
pDelNode = pCurNode->_pNext; //pDelNode指向下一节点
pCurNode->_data = pDelNode->_data;//拷贝数据
pCurNode->_pNext = pDelNode->_pNext;//跳过pDelNode,指向pDelNode的下一节点。
free(pDelNode);//释放节点。
pDelNode = NULL;
}
printf("%d-><null>\n", pCurNode->_data);//打印剩余节点信息
free(pCurNode); //释放最后一个节点。
pCurNode = NULL;
*pHead = NULL; //链表为空
}
5,查找单链表的中间结点,要求只遍历一次链表
解析:只遍历一次话,只利用一个节点遍历的话,实现比较麻烦。
所以采用两个节点遍历,一个快指针,一个慢指针。快指针一次走一步,慢指针一次走一步。
当快指针走到链表尾时,慢指针刚好指向链表额中间节点。返回慢指针即可。
注:链表为空的情况。
PNode FindMidNode(PNode pHead)
{
//定义一个快指针,一次走两步,定义一个慢指针,一次走一步
PNode pFast = NULL;
PNode pSlow = NULL;
//当链表为空,返回头指针
if (NULL == pHead)
return pHead;
//都从pHead开始遍历
pFast = pSlow = pHead;
//当快指针走完后,返回慢指针的地址。
while (NULL != pFast->_pNext)
{
//快慢指针都先走一步
pFast = pFast->_pNext;
pSlow = pSlow->_pNext;
//快指针多走一步
if (NULL != pFast->_pNext)
pFast = pFast->_pNext;
}
return pSlow;
}
6,查找单链表的倒数第K个结点,要求只遍历一次链表
解析:类似利用了快慢指针方法,快指针先走k-1个几点,然后快慢指针一起走,直到快指针走完,返回慢指针。
注:链表为空。
PNode FindLastKNode(PNode pHead, size_t k)
{
//利用快慢指针,快指针比慢指针先走(k-1)步。当快指针走完后,返回慢指针地址。
PNode pAhead = NULL;
PNode pBehind = NULL;
if (NULL == pHead) //链表为空时返回NULL
return NULL;
pAhead = pBehind = pHead; //从头结点开始遍历
while ((k-- > 1) && (NULL != pAhead->_pNext)) //pFast先走k-1步
pAhead = pAhead->_pNext;
while (NULL != pAhead->_pNext)
{
pAhead = pAhead->_pNext;
pBehind = pBehind->_pNext;
}
return pBehind;
}
7,使用冒泡排序对单链表排序
解析:定义一个开始节点pStart,从头开始遍历。每遍历一个几点,
在循环内定义一个当前节点pCurNode和pStart比较大小,判断是否需要交换值,若需要,跳出内循环,继续外循环
若不需要,继续内循环,直到pCurNode遍历完或满足交换条件。
void BubbleSort(PNode pHead)
{
PNode pStart = NULL;//开始比较的节点指针
if (NULL == pHead)
return ;
pStart = pHead;//从头结点开始遍历
while (NULL != pStart->_pNext)
{
PNode pCurNode = pStart->_pNext;//定义要比较的节点。
while (NULL != pCurNode)
{
if (pStart->_data > pCurNode->_data)
{
DataType data = pCurNode->_data;
pCurNode->_data = pStart->_data;
pStart->_data = data;
}
pCurNode = pCurNode->_pNext;//要比较的节点往后移一步
}
pStart = pStart->_pNext;//开始比较节点往后移一步
}
}