1.逆序打印单链表
解题思路:通过递归实现
viod PrintListFromTail2Head(PNode pHead)
{
if(pHead)
{
PrintListFromTail2Head(pHead->_pNext);
printf("%d ",pHead->data);
}
}
2.删除单链表的非尾结点 (不能遍历链表)
解题思路:采用替换删除法,用下一个节点的值来更新当前节点,然后删除下一节点
void EraseNotTailNode(PNode pos)
{
pNode pDel = NULL;
//若当前节点不存在或为尾结点,不进行删除直接返回
if(NULL == pos||NULL == pos->_pNext)
return;
pDel = pos->_pNext;
pos->data = pDel->data;
pos->_pNext = pDel->_pNext;
free(pDel);
}
3.在无头单链表某节点前插入值为data新节点 (不能遍历单链表)
解题思路:也可以用替换插入法,在当前节点的后面插入一个与当前节点值相等的结点,再将当前节点的值更新为data
void InsertFront(PNode pos, DataType data)
{
pNode pNewNode = NULL;
//PNode pNewNode = BuySListNode(pos->data);不可取,会存在内存泄漏
if(NULL == pos)
return;
pNewNode = BuySListNode(pos->data);
pNewNode->_pNext = pos->_pNext;
pos->_pNext = pNewNode;
pos->data = data;
}
4.用单链表模拟实现约瑟夫环
约瑟夫环运作如下:
1、一群人围在一起坐成环状(如:N)
2、从某个编号开始报数(如:K)
3、数到某个数(如:M)的时候,此人出列,下一个人重新报数
4、一直循环,直到所有人出列 ,约瑟夫环结束
作为一个单链表,我们首先要做的第一步是将该单链表构建成一个环,接着我们要使每一个结点进行报数,从1开始若遇到报数为K的结点,即删除该节点,接着从下一个节点重新从1报数,以此类推,直至链表中只剩一个结点,结束
void JosephCircle(PNode* ppHead, size_t M)
{
assert(ppHead);
//构环
PNode pTailNode = *ppHead;
while (pTailNode->_pNext)
{
pTailNode = pTailNode->_pNext;
}
pTailNode->_pNext = *ppHead;
PNode pCur = *ppHead;
while (pCur->_pNext != pCur)
{
//报数
size_t count = M;
while (--count)//循环次数为2,前置--
{
pCur = pCur->_pNext;
}
//删结点
PNode pDel = pCur->_pNext;
pDel->data = pCur->data;
pCur->_pNext = pDel->_pNext;
free(pDel);
pDel = NULL;
}
*ppHead = pCur;//最后使链表的头指向最后唯一一个结点
}
5.单链表的逆置
方法一:三指针法
解题思路:解决这个问题,我们可以通过三个指针来实现pre ,cur, next
先让pre指向空,cur指向单链表的头,next指向头的下一个节点,
第一步:使cur的next域指向pre
第二步:pre走到cur的位置
第三步:cur接着走到next的位置
第四步:next走到cur的next域处
令这几步一直循环,直到next走到NULL时停止,即完成了单链表的逆序
void ReverseList(PNode* pHead)
{
PNode pPre = NULL;
PNode pCur = NULL;
PNode pNext = NULL;
assert(pHead);
//当单链表为空或只有一个结点时直接返回
if (NULL == *pHead || NULL == (*pHead)->_pNext)
return;
pCur = *pHead;
while (pCur)
{
pNext = pCur->_pNext;//放在最前面以防越界
pCur->_pNext = pPre;
pPre = pCur;
pCur = pNext;
}
*pHead = pPre;//最后使单链表的头指向原单链表的最后一个结点
}
方法二:头插法
与上述方法类似,给出代码
PNode ReverseList_P(PNode pHead)
{
PNode pCur = pHead;
PNode pNext = NULL;
PNode pNewNode = NULL;
while (pCur)
{
pNext = pCur->_pNext;
pCur->_pNext = pNewNode;
pNewNode = pCur;
pCur = pNext;
}
return pNewNode;
}
6.使用冒泡排序对单链表进行排序
void BubbleSort(PNode pHead)
{
PNode pCur = NULL;
PNode pNext = NULL;
PNode pTail = NULL;
if (NULL == pHead || NULL == pHead->_pNext)
return;
int flag = 0;
while (pHead != pTail)
{
flag = 0;
pCur = pHead;
pNext = pCur->_pNext;
while (pNext != pTail)
{
if (pCur->data > pNext->data)
{
flag = 1;
DataType tmp = pCur->data;
pCur->data = pNext->data;
pNext->data = tmp;
}
pCur = pNext;
pNext = pNext->_pNext;
}
pTail=pCur;//每一轮排序结束更新尾结点的位置
if (0 == flag)
return;
}
}
7.查找单链表的中间结点,要求只能遍历一次链表
1—>2—>3—>4—>NULL 偶数个结点,要求返回中间两节点中的前一个节点
1—>2—>3—>4—>5—>NULL 奇数个结点,返回中间结点
解题思路:用快慢指针来实现,两指针同时从链表的头部开始向后走,快指针每次走两步,慢指针一次走有一步,当快指针走到空或其下一位置为空时,慢指针刚好走到链表的中间结点处,妥妥的
PNode FindMiddleNode(PNode pHead)
{
PNode pFast = NULL;
PNode pSlow = NULL;
PNode pPre = NULL;
pFast = pHead;
pSlow = pHead;
while ( pFast && pFast->_pNext)
{
pPre = pSlow;//用来标记慢指针的前一个节点
pSlow = pSlow->_pNext;
pFast = pFast->_pNext->_pNext;
}
if (NULL == pFast)//偶
return pPre;
else //奇
return pSlow;
}
8.查找链表的倒数第K个结点
解题思路:也是给两个快慢指针,快指针先走K步,然后快慢指针同时走,由于快慢指针之间总是相差k个结点 , 所以当快指针走到空时,慢指针指向的结点即为倒数第K个结点
PNode FindLastKNode(PNode pHead, size_t K)
{
PNode pFast = NULL;
PNode pSlow = NULL;
if (NULL == pHead || K == 0)
return NULL;
pFast = pHead;
pSlow = pHead;
//pFast先走K/K-1步
while (K--)//或while(--K)
{
if (NULL == pFast)
return NULL;
pFast = pFast->_pNext;
}
//pFast和pSlow同时走
while (pFast)//或while(pFast->_pNext)
{
pFast = pFast->_pNext;
pSlow = pSlow->_pNext;
}
return pSlow;
}
10.删除单链表的倒数第K个结点
解题思路:运用上述方法先找到倒数第K个结点,同时标记慢指针的前一结点
,找到后删除该节点即可,注意传参时应传二级指针,因为待删结点有可能是头节点,会改变头节点的指向
int DeleteLastKNode(PNode* ppHead, size_t K)
{
PNode pFast = NULL;
PNode pSlow = NULL;
PNode pSlowPre= NULL;
assert(ppHead);
if (NULL == *ppHead || K == 0)
return 0;
pFast = *ppHead;
pSlow = *ppHead;
while (K--)
{
if (NULL == pFast)
return 0;
pFast = pFast->_pNext;
}
while (pFast)
{
pSlowPre = pSlow;
pSlow = pSlow->_pNext;
pFast = pFast->_pNext;
}
pSlowPre->_pNext = pSlow->_pNext;
free(pSlow);
pSlow = NULL;
return 1;
}
11.合并两个已序链表,合并之后依然有序
解题思路:运用二路归并的思想
若两链表有一个为空,直接返回另一个链表。两链表都不为空时,先比较两链表头节点的数值大小来确定新链表的头,将新链表的尾也放在头部,再比较两链表其余节点的值的大小,依次链接在新链表的后面,当有一个链表走到空时,将非空的另一链表链在新链表后面即可
PNode MergeList(PNode pHead1, PNode pHead2)
{
PNode pL1 = pHead1;
PNode pL2 = pHead2;
PNode pNewHead = NULL;
PNode pTail = NULL;
//若两个链表有一个为空,直接返回另一个链表
if (NULL == pHead1)
return pHead2;
if (NULL == pHead2)
return pHead1;
//if(NULL==pHead1||NULL==pHead2)
// return (pHead1) ? pHead1 : pHead2;
//先确定新链表的头从哪个链表开始
if (pL1->data < pL2->data)
{
pNewHead = pL1;
pTail = pL1;
pL1 = pL1->_pNext;
}
else
{
pNewHead = pL2;
pTail = pL2;
pL2 = pL2->_pNext;
}
while (pL1&&pL2)
{
if (pL1->data < pL2->data)
{
pTail->_pNext = pL1;
pL1 = pL1->_pNext;
}
else
{
pTail->_pNext = pL2;
pL2 = pL2->_pNext;
}
pTail = pTail->_pNext;
}
//若L2已经全都链接在新链表上,直接将L1接在新链表的尾结点上
if (pL1)
pTail->_pNext = pL2;
//若L1已经全都链接在新链表上,直接将L2接在新链表的尾结点上
if (pL2)
pTail->_pNext = pL1;
return pNewHead;
}