这篇博客只有针对单链表的不同面试题的不同函数,没有对单链表的具体实现方法的介绍。
单链表的具体实现方法(创建,初始化,前插,后插,删除,插入,销毁等),可以参考我的另一边博客:
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;
}