这篇博客只有针对单链表的不同面试题的不同函数,没有对单链表的具体实现方法的介绍。
单链表的具体实现方法(创建,初始化,前插,后插,删除,插入,销毁等),可以参考我的另一边博客:
http://blog.csdn.net/ljx_5489464/article/details/50893430
以下是单链表面试题的具体实现:
1、从尾到头打印单链表
void PrintListTailToHead(PSListNode pHead)
{
if (NULL != pHead)
{
//递归实现
PrintListTailToHead(pHead->pNextNode);
printf("%d ", pHead->data);
}
}
2、删除一个无头单链表的非尾节点
void DelNotTailNode(PSListNode pos)
{
PSListNode pNode = NULL;
assert(pos);
if (NULL == pos->pNextNode)
{
return;
}
else
{
DataType temp = 0;
//交换pos和pos->pNextNode的数据(相当于交换了两个结点的位置),使问题转换为删除pos指向的结点的下一个结点
temp = pos->data;
pos->data = pos->pNextNode->data;
pos->pNextNode->data = temp;
pNode = pos->pNextNode;
pos->pNextNode = pos->pNextNode->pNextNode;
free(pNode);
pNode = NULL;
}
}
3、在无头单链表的一个非头节点前插入一个节点
void InsertNotHead(PSListNode pos, DataType data)
{
if (NULL == pos)
{
return;
}
else
{
PSListNode pNewNode = ByeNode(data);
if (NULL == pNewNode)
{
printf("开辟结点空间失败!\n");
return;
}
else
{
//交换pos和pNewNode的数据(相当于交换了两个结点的位置),使问题转换为在pos指向的结点的下一个结点处插入新结点
pNewNode->data = pos->data;
pos->data = data;
pNewNode->pNextNode = pos->pNextNode;
pos->pNextNode = pNewNode;
}
}
}
4、单链表实现约瑟夫环(JosephCircle)
//使链表形成一个环
void FormCyc(PSListNode *pHead)
{
if (NULL == pHead)
{
return;
}
else
{
PSListNode pNode = *pHead;
while (NULL != (pNode->pNextNode))
{
pNode = pNode->pNextNode;
}
pNode->pNextNode = *pHead;
}
}
PSListNode JosephCircle(PSListNode pHead, int M)
{
if ((NULL == pHead) || (M <= 0))
{
return NULL;
}
else
{
//让链表中所有元素形成一个环
FormCyc(&pHead);
PSListNode pPreNode = NULL;
PSListNode pCurNode = pHead;
PSListNode pDesNode = NULL;
int temp = M;
while (pCurNode->pNextNode != pCurNode)
{
temp = M;
pPreNode = pCurNode;
while (--temp)
{
pPreNode = pCurNode;
pCurNode = pCurNode->pNextNode;
}
//记住要从链表中删除的节点的位置,把它的空间释放了
pDesNode = pCurNode;
pCurNode = pCurNode->pNextNode;
pPreNode->pNextNode = pCurNode;
free(pDesNode);
pDesNode = NULL;
}
//如果M=1,就说明所有结点都要被删除,那么就返回空,否则就返回剩下的那个结点的指针
if (1 == M)
{
free(pCurNode);
pCurNode = NULL;
}
else
{
pCurNode->pNextNode = NULL;
}
return pCurNode;
}
}
5、逆置/反转单链表
////逆置链表:方法一:前插法
//void ReverseList(PSListNode* pHead)
//{
// if (NULL == *pHead)
// {
// return;
// }
// else
// {
// //创建一个新的空链表,遍历pHead指向的链表里的所有节点,每找到一个,就前插到新链表里
// PSListNode pNewHead = *pHead;
// *pHead = (*pHead)->pNextNode;
// //第一次前插到新链表里的结点,是新链表的尾节点,所以要使它的下一个结点为空
// pNewHead->pNextNode = NULL;
// while (NULL != *pHead)
// {
// PSListNode pNode = *pHead;
// *pHead = (*pHead)->pNextNode;
// pNode->pNextNode = pNewHead;
// pNewHead = pNode;
// }
// *pHead = pNewHead;
// }
//}
//逆置链表:方法二:三指针法
void ReverseList(PSListNode* pHead)
{
assert(pHead);
if ((*pHead == NULL) || ((*pHead)->pNextNode) == NULL)
{
return;
}
else
{
PSListNode pPreNode = *pHead;
PSListNode pCurNode = (*pHead)->pNextNode;
PSListNode pOffNode = (*pHead)->pNextNode->pNextNode;
PSListNode pNode = NULL;
while (1)
{
pCurNode->pNextNode = pPreNode;
pPreNode->pNextNode = pNode;
pNode = pPreNode;
if (pOffNode == NULL)
{
break;
}
else
{
pPreNode = pCurNode;
pCurNode = pOffNode;
pOffNode = pOffNode->pNextNode;
}
}
*pHead = pCurNode;
}
}
6、单链表排序(冒泡排序)
void SortList(PSListNode pHead)
{
if (NULL == pHead)
{
return;
}
else
{
int flag = 0;
PSListNode pTailNode = NULL;
//当设置的尾节点与头结点指向同一个节点时,说明只有一个元素为排序,那么冒泡完成
while (pTailNode != pHead)
{
PSListNode pPreNode = pHead;
//每次参与比较的都是尾节点前面的结点
while (pPreNode->pNextNode != pTailNode)
{
PSListNode pCurNode = pPreNode->pNextNode;
if (pPreNode->data > pCurNode->data)
{
DataType dTemp = pPreNode->data;
pPreNode->data = pCurNode->data;
pCurNode->data = dTemp;
flag = 1;
}
pPreNode = pPreNode->pNextNode;
}
//对冒泡的优化,只要有一趟比较没有发生结点交换,说明冒泡完成,就可以退出冒泡的代码块了
if (0 == flag)
{
break;
}
pTailNode = pPreNode;
}
}
}
7、合并两个有序链表,合并后依然有序
PSListNode MergeList(PSListNode pL1, PSListNode pL2)
{
PSListNode pNewNode = NULL;
PSListNode pListNode1 = pL1;
PSListNode pListNode2 = pL2;
PSListNode pNode = NULL;
if (NULL == pListNode1)
{
return pListNode2;
}
else if (NULL == pListNode2)
{
return pListNode1;
}
else
{
//先把新链表的头结点的指针找到,每次取两个链表中保存的数据较小的结点后插到新链表中
if (pListNode1->data > pListNode2->data)
{
pNode = pListNode2;
pListNode2 = pListNode2->pNextNode;
pNewNode = pNode;
}
else
{
pNode = pListNode1;
pListNode1 = pListNode1->pNextNode;
pNewNode = pNode;
}
while ((NULL != pListNode1) && (NULL != pListNode2))
{
if (pListNode1->data > pListNode2->data)
{
pNode->pNextNode = pListNode2;
pListNode2 = pListNode2->pNextNode;
pNode = pNode->pNextNode;
}
else
{
pNode->pNextNode = pListNode1;
pListNode1 = pListNode1->pNextNode;
pNode = pNode->pNextNode;
}
}
if (NULL == pListNode1)
{
pNode->pNextNode = pListNode2;
return pNewNode;
}
else
{
pNode->pNextNode = pListNode1;
return pNewNode;
}
}
}
8、查找单链表的中间节点,要求只能遍历一次链表
PSListNode FindMidNode(PSListNode pHead)
{
if (NULL == pHead)
{
return NULL;
}
else
{
//快慢指针
PSListNode pSlow = pHead;
PSListNode pFast = pHead;
//注意结束条件得加上NULL != pFast->pNextNode,否则当NULL == pFast->pNextNode时,进入循环,
//执行pFast = pFast->pNextNode->pNextNode时会崩溃
while ((NULL != pFast) && (NULL != pFast->pNextNode))
{
pSlow = pSlow->pNextNode;
pFast = pFast->pNextNode->pNextNode;
}
return pSlow;
}
}
9、查找单链表的倒数第k个节点,要求只能遍历一次链表
PSListNode FindLastKNode(PSListNode pHead, int K)
{
if ((NULL == pHead) || (K <= 0))
{
return NULL;
}
else
{
PSListNode pFast = pHead;
PSListNode pSlow = pHead;
//利用快慢指针,让快指针先走K-1步,然后两指针同时走,直到快指针指向的下一个结点为空为止
while (--K)
{
pFast = pFast->pNextNode;
if (NULL == pFast)
{
return NULL;
}
}
while (NULL != pFast->pNextNode)
{
pFast = pFast->pNextNode;
pSlow = pSlow->pNextNode;
}
return pSlow;
}
}
10、判断单链表是否带环?若带环,求环的长度?求环的入口点?
PSListNode HasCycle(PSListNode pHead)
{
if ((NULL == pHead) || (NULL == pHead->pNextNode))
{
return NULL;
}
else
{
PSListNode pFast = pHead->pNextNode->pNextNode;
PSListNode pSlow = pHead->pNextNode;
//利用快慢指针,让快指针每次走两步,慢指针每次走一步,要是快指针没有走到NULL,且快指针与慢指针指向相同就说明是有环
while (pFast != pSlow)
{
//快指针要是没有指向为空,那么慢指针就不可能指向空(快指针走得快)
if (NULL == pFast)
{
return;
}
else
{
pFast = pFast->pNextNode;
pSlow = pSlow->pNextNode;
if (NULL == pFast)
{
return;
}
else
{
pFast = pFast->pNextNode;
}
}
}
return pFast;
}
}
int GetCyleLen(PSListNode pMeetNode)
{
//默认传的参数是HasCycle函数返回的环中的一个结点
if (NULL == pMeetNode)
{
return 0;
}
else
{
int nCount = 1;
PSListNode pNode = pMeetNode;
while (pMeetNode != pNode->pNextNode)
{
pNode = pNode->pNextNode;
nCount++;
}
return nCount;
}
}
//pMeetNode参数是用HasCycle函数求链表是否有环时pFast指针与pSlow指针的碰撞点
//定律:在链表头,pFast指针与pSlow指针的碰撞点分别设定一个指针,每次各走一步,两个指针必定相遇,则相遇第一点为环入口点
PSListNode FindEnterNode(PSListNode pHead, PSListNode pMeetNode)
{
PSListNode pNode = pHead;
if ((NULL == pHead) || (NULL == pMeetNode))
{
return NULL;
}
while (pNode != pMeetNode)
{
pNode = pNode->pNextNode;
pMeetNode = pMeetNode->pNextNode;
}
return pNode;
}
11、判断两个链表是否相交,若相交,求交点。(假设链表不带环)
int IsListCrose(PSListNode pL1, PSListNode pL2)
{
if ((NULL == pL1) || (NULL == pL2))
{
return 0;
}
else
{
PSListNode PSList1 = pL1;
PSListNode PSList2 = pL2;
while (NULL != PSList1->pNextNode)
{
PSList1 = PSList1->pNextNode;
}
while (NULL != PSList2->pNextNode)
{
PSList2 = PSList2->pNextNode;
}
//不带环的两个链表相交,那么它们的最后一个结点的指针的值一定是相等的
if (PSList1 == PSList2)
{
return 1;
}
else
{
return 0;
}
}
}
12、判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
int IsListCroseWithCycle(PSListNode pL1, PSListNode pL2)
{
PSListNode pMeetNode1 = HasCycle(pL1);
PSListNode pMeetNode2 = HasCycle(pL2);
if ((NULL == pL1) || (NULL == pL2))
{
return 0;
}
//两个链表都没环的情况
else if ((NULL == pMeetNode1) && (NULL==pMeetNode2))
{
PSListNode PSList1 = pL1;
PSListNode PSList2 = pL2;
while (NULL != PSList1->pNextNode)
{
PSList1 = PSList1->pNextNode;
}
while (NULL != PSList2->pNextNode)
{
PSList2 = PSList2->pNextNode;
}
//不带环的两个链表相交,那么它们的最后一个结点的指针的值一定是相等的
if (PSList1 == PSList2)
{
return 1;
}
else
{
return 0;
}
}
// 两个链表都有环的情况(这种情况肯定是两个链表共用一个环)
else if ((NULL != pMeetNode1) && (NULL != pMeetNode2))
{
while (pMeetNode1->pNextNode != pMeetNode1)
{
//找到一个链表中处于环中的点,遍历另一个链表中环中的点,看他们是否会出现相等的情况,出现,则可能相交
if (pMeetNode1 == pMeetNode2)
{
return 1;
}
pMeetNode1 = pMeetNode1->pNextNode;
}
//跳出循环时,遍历的组后一个结点还没进行比较,此处处理这种情况
if (pMeetNode1 == pMeetNode2)
{
return 1;
}
else
{
return 0;
}
}
// 一个链表有环,一个没环,这种情况不会相交
else
{
return 0;
}
}
13、求链表相交时的交点
//链表相交时的交点
PSListNode IntersectionNode(PSListNode pL1, PSListNode pL2)
{
int count1 = 0;
int count2 = 0;
PSListNode PSList1 = pL1;
PSListNode PSList2 = pL2;
PSListNode pMeetNode1 = HasCycle(pL1);
PSListNode pMeetNode2 = HasCycle(pL2);
if ((NULL == pL1) || (NULL == pL2))
{
return NULL;
}
else
{
//先求每个链表的长度
//两个链表都没环
if ((NULL == pMeetNode1) && (NULL == pMeetNode2))
{
while (NULL != PSList1)
{
PSList1 = PSList1->pNextNode;
count1++;
}
while (NULL != PSList2)
{
PSList2 = PSList2->pNextNode;
count2++;
}
}
//两个链表都有环
else if ((NULL != pMeetNode1) && (NULL != pMeetNode2))
{
PSListNode pInNode1 = FindEnterNode(PSList1, pMeetNode1);
PSListNode pInNode2 = FindEnterNode(PSList2, pMeetNode2);
//先计算头指针到环入口结点的长度,再计算环的长度
while (PSList1 != pInNode1)
{
PSList1 = PSList1->pNextNode;
count1++;
}
while (PSList1->pNextNode != PSList1)
{
PSList1 = PSList1->pNextNode;
count1++;
}
count1++;;
while (PSList2 != pInNode2)
{
PSList2 = PSList2->pNextNode;
count2++;
}
while (PSList2->pNextNode != PSList1)
{
PSList2 = PSList2->pNextNode;
count2++;
}
count2++;;
}
//一个有环,一个没环,不会相交
else
{
return NULL;
}
//让长度长的链表的头指针先走它长于另一个链表的结点数
//在计算链表长度时修改了这两个指针的值,在这儿需要把它们改回来
PSList1 = pL1;
PSList2 = pL2;
if (count1 > count2)
{
int temp = count1 - count2;
while (0 == temp--)
{
PSList1 = PSList1->pNextNode;
}
}
else
{
int temp = count2 - count1;
while (0 == temp--)
{
PSList2 = PSList2->pNextNode;
}
}
//此时,让两个链表的头指针同时移动,直到它们相等就找到了交点
//因为题目是找交点,那么交点就存在,所以这儿不用怕死循环
while (1)
{
if (PSList1 = PSList2)
{
break;
}
PSList1 = PSList1->pNextNode;
PSList2 = PSList2->pNextNode;
}
return PSList1;
}
}
14、求两个已排序单链表中相同的数据。void UnionSet(Node* l1, Node* l2);
PSListNode ByeNode(DataType data)
{
PSListNode pNewNode = (PSListNode)malloc(sizeof(struct SListNode));
if (NULL != pNewNode)
{
pNewNode->data = data;
//注意使开辟的新节点的指向为空
pNewNode->pNextNode = NULL;
}
return pNewNode;
}
PSListNode UnionSet(PSListNode pL1, PSListNode pL2)
{
PSListNode PSList1 = pL1;
PSListNode PSList2 = pL2;
PSListNode pNewHead = NULL;
PSListNode pNode = NULL;
//每次比较两个链表头指针指向的数据是否相等,不相等,就让数据小的头指针后移,相等,则把该数据保存起来,
//两个头指针同时后移,直到其中一个指向空为止
while ((NULL == PSList1) || (NULL == PSList2))
{
if (PSList1->data > PSList2->data)
{
PSList2 = PSList2->pNextNode;
}
else if (PSList1->data < PSList2->data)
{
PSList1 = PSList1->pNextNode;
}
//用一个新的链表来保存两个链表中相同的数据
else
{
if (pNewHead == NULL)
{
pNewHead = ByeNode(PSList1->data);
pNode = pNewHead;
PSList1 = PSList1->pNextNode;
PSList2 = PSList2->pNextNode;
}
else
{
pNode = pNode->pNextNode;
pNode = ByeNode(PSList1->data);
PSList1 = PSList1->pNextNode;
PSList2 = PSList2->pNextNode;
}
}
}
return pNewHead;
}