1.从尾到头打印单链表
- 思路一:定义两个指针,PNode和pend,pNode指向第一个结点,pend指向NULL
每次从头开始遍历直到pNode->pNext=pend,打印出pNode,再将pNode赋给pend
pFirst赋给pNode,保证每次都是从头开始遍历
代码如下:
void PrintReverse(SlistNode *pFirst)
{
SlistNode *pNode = pFirst;
SlistNode *pend = NULL;
while (pend != pFirst)//外循坏是让pend每次减小一个
{
while (pNode->pNext != pend)//内循坏是pNode遍历
{
pNode = pNode->pNext;
}
printf("%d-> ", pNode->data);
pend = pNode;
pNode = pFirst;
}
}
- 思路二:递归
代码如下:
void show(SlistNode *p)
{
if (p->pNext){
show(p->pNext);
}
printf("<-%d", p->data);
}
2.逆置/反转单链表
思路:
- 头删->头插
- 尾删->尾插
以头删头插为例,代码如下:
void ReverseSlist(sListNode *pFirst)
{
//assert(pFirst);
DataType data;
sListNode *pNewNode = NULL;
while (pFirst)
{
data = pFirst->data;
PopFront(&pFirst);
PushFront(&pNewNode, data);
}
return pNewNode;
}
PushFront和PopFront函数参见单链表的博客
3.在无头单链表的一个节点前插入一个节点(不能遍历单链表)
思路:因为不知道pPos的前一个结点,所以需将新结点pNewNode插入到pPos的后面
然后将pPos的数据搬移到新建的结点pNewNode的数据
最后将新数据放到pPos的数据区,pPos的指针指向新节点
总结:pPos放的是要插入的数据,pNewNode放的是pPos的原始数据
代码如下:
void InsertNode(sListNode *pPos,DataType data)
{
sListNode *pNewNode = (sListNode*)malloc(sizeof(sListNode));
assert(pNewNode);
pNewNode->data = pPos->data;
pNewNode->pNext = pPos->pNext;
pPos->data = data;
pPos->pNext = pNewNode;
}
4.删除一个无头单链表的非尾结点(不能遍历单链表)
思路:交换pos和pos->pNextNode的数据(相当于交换了两个结点的位置),使问题转换为删除pos指向的结点的下一个结点
代码如下:
void PopNode(sListNode *pPos)
{
assert(pPos);
sListNode *NextNode = pPos->pNext;
pPos->data = NextNode->data;
pPos->pNext = NextNode->pNext;
free(NextNode);
}
5.单链表排序(冒泡排序)
思路:外层循坏控制循坏次数
内层循坏两两节点比较,每次比较的都是尾节点前面的数
代码如下:
void SortSqlist(sListNode *pFirst)
{
assert(pFirst);
sListNode *pEnd = NULL;
int flag = 0;
//外层循坏
while (pEnd!=pFirst)
{
sListNode *PreNode = pFirst;
//内层循坏,两两进行比较,每次参与比较的都是尾结点前面的结点
while (PreNode->pNext!=pEnd)
{
sListNode *CurNode = PreNode->pNext;
if (PreNode->data > CurNode->data)
{
DataType tmp = PreNode->data;
PreNode->data = CurNode->data;
CurNode->data = tmp;
flag = 1;
}
PreNode = PreNode->pNext;
}
if (flag == 0)
{
break;
}
pEnd = PreNode;
}
}
6.查找单链表的中间节点,要求只能遍历一次链表
思路:用快慢指针的方法,快指针走两步,慢指针走一步,当快指针走完时,慢指针的位置就是中间节点
注意:应该对快指针每走一步就进行判断是否等于NULL
代码如下:
int FindMidNode(sListNode *pFirst)
{
assert(pFirst);
sListNode *pFast = pFirst;
sListNode *pSlow = pFirst;
//while (pFast->pNext != NULL)
//{
// pFast = pFast->pNext->pNext;//没有考率pFast->pNext=NULL的情况
// pSlow = pSlow->pNext;
//}
while (1)
{
pFast = pFast->pNext;
if (pFast == NULL)
{
break;
}
pFast = pFast->pNext;
if (pFast == NULL)
{
break;
}
pSlow = pSlow->pNext;
}
return pSlow;
}
测试代码:
sListNode *pFound = FindMidNode(pFirst);
printf("%d\n", pFound->data);
7.查找单链表的倒数第k个节点,要求只能遍历一次链表
思路:同样是快慢指针的方法,让快慢指针相差k步,即先让快指针走k-1步,再让快慢指针同时走,快指针走完,慢指针走到的位置就是倒数第k个节点
int FindK(sListNode *pFirst, int k)
{
assert(pFirst);
sListNode *pFast = pFirst;
sListNode *pSlow = pFirst;
while (--k)//不能是k--,快指针先走k-1步
{
if (pFast == NULL)
{
break;
}
pFast = pFast->pNext;
}
while (pFast->pNext)
{
pFast = pFast->pNext;
pSlow = pSlow->pNext;
}
return pSlow;
}
8.删除链表中的倒数第k个节点
代码如下:
void PopK(sListNode **ppFirst, int k)
{
assert(ppFirst);
assert(*ppFirst);
sListNode *pFound = FindK(*ppFirst, k);
sListNode *pNode = *ppFirst;
if (pFound == *ppFirst)
{
sListNode *pOldFirst = *ppFirst;
*ppFirst = (*ppFirst)->pNext;
free(pOldFirst);
return;//不加return会出错
}
while (pNode->pNext != pFound)
{
pNode = pNode->pNext;
}
pNode->pNext = pFound->pNext;
free(pFound);
//PopAddress(ppFirst, pFound);
}
9.合并两个有序链表,合并后依然有序
思路:定义两个指针分别指向链表1和链表2,指针小的往后走
注意:不要忘了考虑当一个链表为空的情况
代码如下:
DataType UnitSlist(sListNode *s1, sListNode *s2)
{
assert(s1);
assert(s2);
sListNode *p1=s1;
sListNode *p2=s2;
sListNode *pNewNode = NULL;
while (p1 != NULL&&p2 != NULL)
{
if (p1->data < p2->data)
{
pushBack(&pNewNode, p1->data);
p1 = p1->pNext;
}
else
{
pushBack(&pNewNode, p2->data);
p2 = p2->pNext;
}
}
//有一个链表为空的情况下
sListNode *pNotEmpty = p1;
if (p1 == NULL)
{
pNotEmpty = p2;
}
//如果有一个链表为空,将不空的链表的剩余数据插入新链表
while (pNotEmpty != NULL)
{
pushBack(&pNewNode, pNotEmpty->data);
pNotEmpty = pNotEmpty->pNext;
}
return pNewNode;
}
测试代码:
sListNode *pNewList = UnitSlist(s1, s2);
print(pNewList);