一,环形链表约瑟夫问题
1,题目描述
编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。
下一个人继续从 1 开始报数。
n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?
例如:一共有5个人,报到2的人离开,1开始报数,1->1,2->2,2离开;接着继续从下一个人开始重新报数,也就是从3开始,3->1,4->2,4离开......依次类推,直到剩下最后一个人。
2,思路(链表)
33第一个方法是用环形链表。当数到2时,head所指向的节点需要被释放,但这样就找不到下一个节点了,所以我们需要一个tail指针来记录上一个节点。比如在删除节点2时,如果直接free掉,就找不到节点3了,应先让tail->next指向head->next,再让head释放掉,最后让head走到下一个节点也就是tail->next;
3,代码实现
typedef struct ListNode ListNode;
ListNode* buynode(int x)
{
//创建节点
ListNode* node=(ListNode*)malloc(sizeof(ListNode));
if(node==NULL)
{
exit(1);
}
node->val=x;
node->next=NULL;
return node;
}
ListNode* circle(int n)
{
//创建带环链表
ListNode* phead=buynode(1);
ListNode* ptail=phead;//ptail为尾节点
for(int i=2;i<=n;i++)
{
ptail->next=buynode(i);
ptail=ptail->next;
}
ptail->next=phead;//首尾相连
return ptail;//返回尾不返回头,是因为我们需要两个指针,一个指向当前位置,一个指向下一个位置
}
int ysf(int n, int m ) {
// write code here
ListNode* prev=circle(n);//起始,prev为尾节点
ListNode* pcur=prev->next;//起始,pcur为头节点
int count=1; //用于计数
//pcur->next指向本身,链表还剩一个人,循环结束
while(pcur->next!=pcur)
{
if(count==m)
{
//此时需要销毁数据
prev->next=pcur->next;
free(pcur);
pcur=prev->next;//销毁后,pcur走向下一个位置
count=1;//重新开始计数
}
else
{
//不需要销毁数据,继续向后走
prev=pcur;
pcur=pcur->next;
count++;//走一步计数加加
}
}
return pcur->val;
}
二,合并两个有序链表
1,题目描述
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 :
2,思路
这两个链表都是有序的,但两个链表的节点个数可能不相等。我们可以创建一个新链表,让两个链表的值向比较后,将较小一个尾插在新链表上。
3,代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
ListNode* l1=list1;
ListNode* l2=list2;
//创建新链表
ListNode* head=NULL;
ListNode* tail=NULL;
//遍历l1 l2进行比较
while(l1&&l2)
{
if(l1->val<l2->val)
{
//将l1尾插到新链表中
if(head==NULL)
{
head=tail=l1;
}
else
{
tail->next=l1;
tail=l1;
}
l1=l1->next; //l1指向下一个节点
}
else
{
//将l2尾插到新链表中
if(head==NULL)
{
head=tail=l2;
}
else
{
tail->next=l2;
tail=l2;
}
l2=l2->next;
}
}
//出循环后,l1可能没有遍历完,或者l2可能没有遍历完
//l1没有遍历完,将剩下的插入到tail后
if(l1)
{
tail->next=l1;
}
//l2没有遍历完,将剩下的插入到tail后
if(l2)
{
tail->next=l2;
}
return head;
}
三,分割链表
1,题目描述
有一个链表,给一个头节点和一个特定值x,要求对链表进行分割,使所有小于x的节点都出现在大于或等于x节点之前。
2,思路
这个和上面的思路相似。我们可以创建两个链表,将原链表中小于x的节点尾插到小链表,
大于或等于x的节点尾插到大链表中。
3,代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
if(head==NULL)
return NULL;
//创建两个带头链表
ListNode* lesshead,*lesstail;
ListNode* greathead,*greattail;
lesshead=lesstail=(ListNode*)malloc(sizeof(ListNode));
greathead=greattail=(ListNode*)malloc(sizeof(ListNode));
ListNode* pcur=head;
while(pcur)
{
//尾插到小链表中
if(pcur->val<x)
{
lesstail->next=pcur;
lesstail=pcur;
}
else
{
//尾插到大链表中
greattail->next=pcur;
greattail=pcur;
}
pcur=pcur->next;
}
//修改大链表尾节点指向,避免死循环
greattail->next=NULL;
//需要先置空,再连接,因为greathead->next可能未知
//小链表尾节点和大链表有效头节点 首尾相连
lesstail->next=greathead->next;
return lesshead->next;
}