单链表的基本操作(二)练习题

1.反转一个单链表
方法一:不断的进行后删,前插操作。

void reaversal(SListNode **pphead)//不断后删前插操作
{
 SListNode *head = *pphead;//此指针在每次循环中始终指向当前链表的头
 SListNode *oldp = head;//此指针在每次循环中始终指向原本的头节点,不会改变方向
 SListNode *tmp = head->next;//此指针在每次循环中始终指向要被后删再头插的节点
 while (tmp)//如果tmp为空,则代表逆序结束,旧头的next已经是空的了,成为新链表的尾
 {
  oldp->next = tmp->next;//将tmp架空,实际是后删操作的一部分
  tmp->next = head;//将tmp变成新的头,实际是头插操作的一部分
  head = tmp;//换头
  tmp = oldp->next;//让tmp变成下次循环中待删除的节点
 }
 *pphead = head;
}

方法二:让每个节点转指向<向后转>

void reaversal_1(SListNode **pphead)
{
 SListNode *pre= *pphead;//被执行操作的前一个节点
 SListNode *cur = pre->next;//被执行操作的节点
 SListNode *nex = cur;//被执行操作的后一个节点,暂时指向cur,在循环开始时指向下一个节点

 pre->next = NULL;//此时的头,将会是转化后的尾,这里是在设置链表尾节点
 
 while (nex)
 {
  nex = nex->next;//先让nex变成下一个节点,之所以不放在最后,是因为会有nex为空的限制
  cur->next = pre;//让原本指着后面的指到前面来(向后转)
  pre = cur;//为了下次循环而传递数据,这里是设置下次循环的上一个节点(本次执行操作的节点将会成下次循环的上一个节点)
  cur = nex;//同上(本次的下一个节点将会成为下次的被执行节点)
 }
 *pphead = pre;//循环跳出后cur和next都已经指向空,pre才是新的头
}

2.输入两个链表,找出他们的第一个公共结点
(单链表不可能分叉,因为一个结点只有一个指向)
思路分析:如果两个单链表相交,从第一个公共结点开始,后面的结点都相同。
1)遍历两个单链表,找出较长的链表比较短的链表多gap个结点。
2)让指向较长的链表的指针先遍历gap个结点。
3)然后,两个链表同时开始依次遍历。
4)如果遍历到相同的结点,返回结点。

#include<math.h>
SListNode *getIntersectionNode(SListNode *headA, SListNode *headB)
{
 int lenA = 0;
 int lenB = 0;
 SListNode *cur=NULL;
 SListNode *longer=headA;
 SListNode *shorter=headB;
 int gap;
 int i;

 for (cur = headA; cur; cur = cur->next)
 {
  lenA++;
 }

 for (cur =headB; cur; cur = cur->next)
 {
  lenB++;
 }

 gap = abs(lenA - lenB);

 if (lenA < lenB)
 {
  longer = headB;
  shorter = headA;
 }
 
 for (i = 0; i < gap; i++)
 {
  longer = longer->next;
 }
 
 for (; longer && shorter; longer = longer->next, shorter=shorter->next)
 {
  if (longer == shorter)
  {
   return shorter;
  }
 }
 return NULL;
}

3.给定一个链表,返回链表开始入环的第一个结点。
如果链表无环,则返回NULL。
解题思路:
<数学模型>A和B同时在操场起点(环的开始)开始跑步,如果A的速度是B速度的两倍。那么他们会在起点(环的开始)相遇。
如果A和B同时从离操场起点(环的开始)1/3处(链表的头)开始跑,那么他们也会在离操场起点(环的开始)的1/3处(环中的一个结点)相遇。
可以想到:定义两个指针变量fast,slow。slow依次遍历,fast跳跃遍历 ,如果有相遇的地方,则证明链表中有环。在分别从链表头和相遇的结点进行依次遍历,相遇的地方就是入环的第一个结点。

SListNode *detectCycle(SListNode *phead)
{
 SListNode *fast = phead;
 SListNode *slow = phead;
 while (fast&&slow&&fast->next)
 {
  fast = fast->next->next;
  slow = slow->next;
  if (fast == slow)
  {
   break;
  }
 }

 for (; fast&&fast->next; fast = fast->next, phead = phead->next)
 {
  if (fast = phead)
  {
   return fast;
  }
 }
 return NULL;
}

3.约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。最后一个人获胜。

int main()
{
 SListNode *phead;//指向头结点
 SListNode *plast;//指向尾结点
 SListNode *cur;
 SListInit(&phead);
 int i;
 int m = 50, n = 3;//50个人数到3就退出
 
 SListPushFront(&phead, 50);//首次头插
 plast = phead;//首次头插的结点最后会是尾部
 
 for (i = m-1; i >=1; i--)//依次头插
 {
  SListPushFront(&phead, i);
 }

 plast->next = phead;//连接成环
 cur = plast;//保持第一个被删除的结点与起始位置相差n-个,

 for (m; m > 1; m--)//剩最后一个人循环退出
 {
  for (i = 1; i < n; i++)//遍历n-1找到要进行后删的结点
  {
   cur = cur->next;
  }
  SListEraseAfter(cur);//后删
 }
 printf("%d", cur->data);
 free(cur);
 system("pause");
 return 0;
}

猜你喜欢

转载自blog.csdn.net/unique_IT/article/details/96633506