【无标题】

一、约瑟夫问题

  思路:约瑟夫问题用链表做其实很方便,约瑟夫问题的核心在于如何成“环”,再就是如何解决“出列”的问题。

  实现:实现起这个思路其实很简单。成环无非就是最后一个结点的指针域存结点下一个结点的地址(没有头结点就是指向第一个结点);其次就是出列,这个问题也就是内存释放的问题,这个就只要用free函数就可以实现。

  代码演示

//成环
typedef struct sn
{
	int data;
	struct sn* next;
}SN;



int main()
{
	int n, m;
	int i;
	scanf("%d%d", &n, &m);
	SN* head, * tail, * p, * q;
	head = (SN*)malloc(sizeof(SN));
    for (i = 0; i < n; i++)
	{
	    p = (SN*)malloc(sizeof(SN));
		p->data = i + 1;
		tail->next = p;
		p->next = head->next;//形成环状箭头
		tail = p;
	}
}
//出列
while (p != q)
			{
				if (i == m)
				{
					q->next = q->next->next;
					printf("%d ", p->data);
					free(p);
					p = q->next;
                     count++;
					i = 1-count;
                   
				}
				else
				{
					q = p;
					p = p->next; 
					i++;
				}
			}
			printf("%d ", p->data);
			break;
		}

但是只要成”环“和出列就行了吗?

这里还有一个特殊情况,就是说,当有0个人,或者报道0个人出列的情况,如果用上面的方法统一去做的话,是不行的。所以,我们还得考虑这种情况。

接下来用实际题目演示:

                                          约瑟夫问题变形

编号为1…N的N个小朋友玩游戏,他们按编号顺时针围成一圈,按顺时针次序报数,从第1个人报到第M个人出列;然后再从下个人开始报到第M+1个人出列;再从下一个人开始报到第M+2个人出列……以此类推不断循环,直至最后一人出列。请编写程序按顺序输出出列人的编号。 

pic.jpg

输入格式:

输入为2个整数,分别表示N、M(1≤N,M,K≤10000)。

输出格式:

输出为一行整数,为出列人的编号。每个整数后一个空格。

输入样例1:

6 3

结尾无空行

输出样例1:

3 1 2 6 4 5 

结尾无空行

输入样例2:

10 2

结尾无空行

输出样例2:

2 5 9 6 4 8 7 3 1 10 

结尾无空行

输入样例3:

5 1

结尾无空行

输出样例3:

1 3 2 5 4 

结尾无空行

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1



#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>




typedef struct sn
{
	int data;
	struct sn* next;
}SN;


int main()
{
	int n, m;
	int i;
	scanf("%d%d", &n, &m);
	SN* head, * tail, * p, * q;
	head = (SN*)malloc(sizeof(SN));
	head->data = -1;
	head->next = NULL;
    int count=0;
	while (1)
	{
		if (n == 0 || m == 0)
		{
			free(head);
			break;
		}
		else
		{
			tail = head;
			for (i = 0; i < n; i++)
			{
				p = (SN*)malloc(sizeof(SN));
				p->data = i + 1;
				tail->next = p;
				p->next = head->next;//形成环状箭头
				tail = p;
			}
			p = head->next;
			q = tail;
			i = 1;
            
			while (p != q)
			{
				if (i == m)
				{
					q->next = q->next->next;
					printf("%d ", p->data);
					free(p);
					p = q->next;
                     count++;
					i = 1-count;
                   
				}
				else
				{
					q = p;
					p = p->next; 
					i++;
				}
			}
			printf("%d ", p->data);
			break;
		}
	}
	return 0;
}




 这个问题其实还是一个很基础的链表问题,没有用到算法或者很难的思路,这个就是对尾结点作了特殊处理,以往都是对尾结点指针域进行tail->next=NULL这样的处理变成了tail->next=head->next。其次就是考察了free在链表的基础用法。



二、链表的冒泡排序

  链表和数组有一点相似的感觉,都是链表麻烦就麻烦在数据要一个一个从前往后找,找到后面的结点后,还得主动讲起始结点往后调。

   这个是数组的冒泡

int main()
{
	int a[1001];
	int n = strlen(a);
	int i = 0, j = 0;
	for (i = 0; i < n; i++)
	{
		for (j = i + 1; j < n; j++)
		{
			if (a[i] > a[j])
			{
				int tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}
	return 0;
}

    如果是链表的话,当n=2时,我们又要如何像数组那样,可以直接找到a[2]的位置呢?

我是用2个结构体指针来操作的,一个指针用于内嵌里面一个一个往后走,和未交换最大值的起始点比较,一个用于记录未交换最大值的起始点。

p = head->next;
int h = p->data;
for (i = 0; i < count; i++)
	{
		q = p->next;
		for (j = i+1; j < count; j++)
		{
			if ((p->data) >(q->data))
			{
				int tmp = p->data;
				(p->data) = (q->data);
				q->data = tmp;
			}
			q = q->next;
		}
		
		p = p->next;
	}

这样处理。

再看实例:

                               7-6 程序设计综合实践 1.4

1.4 编写程序,输入若干正整数,按从小到大次序建立1个带头结点单链表,设计一个实现单链表分离算法的Split函数,将原单链表中值为偶数的结点分离出来形成一个新单链表,新单链表中头结点重新申请,其余结点来自原链表,分离后,原链表中只剩非偶数值所在结点,最后显示2个单链表,在程序退出前销毁单链表。要求Split算法时间复杂性达到O(n),程序不可存在内存泄漏。

输入格式:

若干正整数。

输出格式:

每个单链表输出占一行,元素间用分隔符->分隔;初始单链表、剩余元素单链表、偶数元素单链表,共3行。

输入样例:

100 2 3  50 2 1 5 8

结尾无空行

输出样例:

1->2->2->3->5->8->50->100
1->3->5
2->2->8->50->100

结尾无空行

#define _CRT_SECURE_NO_WARNINGS 1


#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>



typedef struct sn
{
	int data;
	struct sn* next;
}SN;


int main()
{
	int n;
	SN* head, * tail, * p, * q;
	head = (SN*)malloc(sizeof(SN));
	head->next = NULL;
	tail = head;
	int count = 0;
	int i = 0, j = 0;
	while (scanf("%d",&n)!=EOF)
	{
			p = (SN*)malloc(sizeof(SN));
			p->data = n;
			tail->next = p;
			tail = p;
			count++;
	}
	tail->next = NULL;
	int cnt = 0;
	p = head->next;
	int h = p->data;
	
	for (i = 0; i < count; i++)
	{
		q = p->next;
		for (j = i+1; j < count; j++)
		{
			if ((p->data) >(q->data))
			{
				int tmp = p->data;
				(p->data) = (q->data);
				q->data = tmp;
			}
			q = q->next;
		}
		
		p = p->next;
	}
	p = head->next;
	for (i = 0; i < count; i++)
	{
		printf("%d", p->data);
		p = (p->next);
		if (i < count - 1)
		{
			printf("->");
		}
	}
	printf("\n");
	p = head->next;
	for (i = 0; i < count; i++)
	{
		if ((p->data) % 2 != 0)
		{
			printf("%d", p->data);
		}
		
		p = (p->next);
		if (i < count - 1&& (p->data) % 2 != 0)
		{
			printf("->");
		}
		
	}
	printf("\n");
	p = head->next;
	for (i = 0; i < count; i++)
	{
		if ((p->data) % 2 == 0)
		{
			printf("%d", p->data);
		}
		
		
		if (i < count - 1&& (p->data) % 2 == 0)
		{
			printf("->");
		}
		p = (p->next);
	}
	return 0;
}




这个题的箭头就类似空格。



                                       7-7 链表的逆置

输入若干个不超过100的整数,建立单链表,然后将链表中所有结点的链接方向逆置,要求仍利用原表的存储空间。输出逆置后的单链表。

输入格式:

首先输入一个整数T,表示测试数据的组数,然后是T组测试数据。每组测试数据在一行上输入数据个数n及n个不超过100的整数。

输出格式:

对于每组测试,输出逆置后的单链表,每两个数据之间留一个空格。

输入样例:

1
11 55 50 45 40 35 30 25 20 15 10 5

输出样例:

5 10 15 20 25 30 35 40 45 50 55

 知道冒泡排序处理链表后,我们就很容易用类似冒泡的方法去进行链表的逆序输出。

思路:假如有n个数,我们第m次就往后找n-m次,就是当前最后一个结点,对里面的data进行输出。在这里我们不需要用free处理,这样我们更方便,但是用free处理一下更好,这样更节省空间。

这里的代码也很简单

#define _CRT_SECURE_NO_WARNINGS 1

//

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>



typedef struct sn
{
	int data;
	struct sn* next;
}SN;


int main()
{
	int n;
	scanf("%d", &n);
	int m;
	int a;
	while (n--)
	{
		scanf("%d", &m);
		SN* head, * r, * p;
		head = (SN*)malloc(sizeof(SN));
		r = p = head;
		int h = m;
		while (m--)
		{
			scanf("%d", &a);
			p = (SN*)malloc(sizeof(SN));
			p->data = a;
			r->next = p;
			r = p;
		}
		r->next = NULL;
		p = head->next;
		int i = 0, j = 0;
		for (i = 0; i < h; i++)
		{
			p = head;
			for (j = 0; j < h - i; j++)
			{
				p = p->next;
			}
			printf("%d", p->data);
			if (i < h - 1) printf(" ");
		}
		printf("\n");
	}
	return 0;
}



                                7-4 单链表基本操作

请编写程序实现单链表插入、删除结点等基本算法。给定一个单链表和一系列插入、删除结点的操作序列,输出实施上述操作后的链表。单链表数据域值为整数。

输入格式:

输入第1行为1个正整数n,表示当前单链表长度;第2行为n个空格间隔的整数,为该链表n个元素的数据域值。第3行为1个正整数m,表示对该链表施加的操作数量;接下来m行,每行表示一个操作,为2个或3个整数,格式为0 k d或1 k。0 k d表示在链表第k个结点后插入一个数据域值为d的结点,若k=0则表示表头插入。1 k表示删除链表中第k个结点,此时k不能为0。注:操作序列中若含有不合法的操作(如在长度为5的链表中删除第8个结点、删除第0个结点等),则忽略该操作。n和m不超过100000。

输出格式:

输出为一行整数,表示实施上述m个操作后的链表,每个整数后一个空格。输入数据保证结果链表不空。

输入样例:

5
1 2 3 4 5
5
0 2 8
0 9 6
0 0 7
1 0 
1 6

输出样例:

7 1 2 8 3 5 

 这个题也很好处理。

思路:对于超出处理范围的,我们用continue处理即可。对于处理范围内的,我们在对应位置进行插入和删除就行。删除后,我们还要对删除的下一个结点和删除的结点的前一个结点进行连接,所以我们最好用2个结构体指针,一前一后的进行处理最好。插入就更简单了,假如在a,b间插入c这个结点,那我们就是要用,a->c,c->b即可。

#define _CRT_SECURE_NO_WARNINGS 1


#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>



typedef struct sn
{
	int data;
	struct sn* next;
}SN;


int main()
{
	int n, m, l, a, b, c, i = 0;
	scanf("%d", &n);
	SN* head, * p, * q, * tail, * node;
	head = (SN*)malloc(sizeof(SN));
	//head->next = NULL;
	tail = head;
	int cnt = n;
	while (n--)
	{
		scanf("%d", &m);
		p = (SN*)malloc(sizeof(SN));
		p->data = m;
		tail->next = p;
		tail = p;
	}
	tail->next = NULL;
	
	scanf("%d", &l);
	
	int count = 0;
	while (l--)
	{
		
		q = head;
		p = head->next;
		scanf("%d", &a);
		if (a == 0)
		{
			scanf("%d%d", &b, &c);
			if (b > cnt + count)
			{
				continue;
			}
			for (i = 0; i < b; i++)
			{
				q = p;
				p = p->next;
			}
			node = (SN*)malloc(sizeof(SN));
			node->data = c;
			node->next = q->next;
			q->next = node;
			count++;
		}
		else
		{
			scanf("%d", &b);
			if (b == 0 || b > (count + cnt))
			{
				continue;
			}
			else
			{
				for (i = 1; i < b; i++)
				{
					q = p;
					p = p->next;
				}
				q->next = q->next->next;
				free(p);
				count--;
			}
			
		}
	}
	p = head;
	for (i = 0; i < cnt+count; i++)
	{
		p = p->next;
		printf("%d ", p->data);
	}
	return 0;
}






                                       7-1 重排链表

给定一个单链表 L1​→L2​→⋯→Ln−1​→Ln​,请编写程序将链表重新排列为 Ln​→L1​→Ln−1​→L2​→⋯。例如:给定L为1→2→3→4→5→6,则输出应该为6→1→5→2→4→3。

输入格式:

每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数,即正整数N (≤105)。结点的地址是5位非负整数,NULL地址用−1表示。

接下来有N行,每行格式为:

Address Data Next

其中Address是结点地址;Data是该结点保存的数据,为不超过105的正整数;Next是下一结点的地址。题目保证给出的链表上至少有两个结点。

输出格式:

对每个测试用例,顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。

输入样例:

00100 6
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

结尾无空行

输出样例:

68237 6 00100
00100 1 99999
99999 5 12309
12309 2 00000
00000 4 33218
33218 3 -1

结尾无空行

   思路:这个题目,我采用的是数组模拟链表去做的,先用结构体数组去模拟链表去做,虽然这样会用空间更多,但是这个方法也更快。用结构体数组去存输入,下表就是前一个(add),用.data存下一个地址的下表,这样去操作。然后再用2个整型数组,一个用于存改变前的链表,一个用于储存改变后。

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>



typedef struct sn
{
	int data;
	int next;
}SN;



int main()
{
	int begin, n;
	int i = 0;
	SN s[100001];
	int before[100001], last[100001];
	int add, data, next;
	scanf("%d%d", &begin, &n);
	for (i = 0; i < n; i++)
	{
		scanf("%d%d%d", &add, &data, &next);
		s[add].data = data;
		s[add].next = next;
	}
	add = begin;
	int count = 0;
	while (add != -1)
	{
		before[count] = add;
		add = s[add].next;
		count++;
	}
	int left = 0, right = count - 1;
	int key = 0;
	while (left <= right)
	{
		if (left < right)
		{
			last[key] = before[right];
			key++;
			right--;
			last[key] = before[left];
			key++;
			left++;
		}
		else
		{
			last[key] = before[left];
			key++;
			left++;
		}
	}

	for (i = 0; i < count - 1; i++)
	{
		printf("%05d %d %05d\n", last[i], s[last[i]].data, last[i + 1]);
	}
	printf("%05d %d -1\n", last[i], s[last[i]].data);
	return 0;
}




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值