洛谷题单-线性表(链表,队列,栈)

数组

点名

题目描述
有 n(n \le 2 \times 10^6)n(n≤2×10
6
) 名同学陆陆续续进入教室。我们知道每名同学的学号(在 1 到 10^910
9
之间),按进教室的顺序给出。上课了,老师想知道第 ii 个进入教室的同学的学号是什么(最先进入教室的同学 i=1i=1),询问次数不超过 10^510
5
次。

输入格式
第一行 2 个整数 n 和 m,表示学生个数和询问次数。

第二行 n 个整数,表示按顺序进入教室的学号。

第三行 m 个整数,表示询问第几个进入教室的同学。

输出格式
m 个整数表示答案,用换行隔开。

输入输出样例
输入 #1复制
10 3
1 9 2 60 8 17 11 4 5 14
1 5 9
输出 #1复制
1
8
5
分析:就是按顺序读入到数组中去,再根据老师叫号输出就可以了,记得数组要开大点。
代码:

#include<stdio.h> 
int main()
{
	int a[2000001];
	long int  m,n,i,b;
	scanf("%ld %ld",&m,&n);
	for(i=0;i<m;i++)
	    scanf("%ld",&a[i]);
	    for(i=0;i<n;i++)
	    {
	    	scanf("%ld",&b);
	    	printf("%ld\n",a[b-1]);
		}
		return 0;
}

寄包柜

分析:单纯的我以为用个二维数组或者结构体数组就行了,结果一直re,太菜了我。

所以去看了一下题解,都说要使用动态数组,大部分都是在说c++中的哈希或者map, 看到一个帖子是写c语言的动态数组,太深奥了,菜菜没看懂下面是题解区的c++代码,等我去学一下动态数组再写博客…

验证栈序列

题目描述
给出两个序列 pushed 和 poped 两个序列,其取值从 1 到 n(n\le100000)n(n≤100000)。已知入栈序列是 pushed,如果出栈序列有可能是 poped,则输出 Yes,否则输出 No。为了防止骗分,每个测试点有多组数据。

输入格式
第一行一个整数 qq,询问次数。

接下来 qq 个询问,对于每个询问:

第一行一个整数 nn 表示序列长度;

第二行 nn 个整数表示入栈序列;

第二行 nn 个整数表示出栈序列;

输出格式
对于每个询问输出答案。

输入输出样例
输入 #1复制
2
5
1 2 3 4 5
5 4 3 2 1
4
1 2 3 4
2 4 1 3
输出 #1复制
Yes
No

分析:
先将入栈和出栈的顺序分别储存在a[],b[],两个数组中,将a[]中的元素按顺序入栈,当栈顶与b当前元素相等时,栈顶出栈,并且b中元素后移一位,注意这里要持续操作直到栈顶不等于b当前元素,并且需要注意的是,需要判断每次是否到栈底,到栈底后应当break,当所有的操作都结束之后,判断一下是否是空栈然后再输出就可以啦。记得stack要开大一点哦,之前因为开小了好几个测试点就re了。
AC代码:

#include<stdio.h>
int main()
{
	int k,n;//k组数,每组n个
	int i,j;
	int stack[100004];
	
	scanf("%d",&k);
	for(j=0;j<k;j++)
	{
		
		int count=1;
		int top=0;
		scanf("%d",&n);
		int a[n+1],b[n+1];//a用来存入栈顺序,b用来存出栈顺序 
		for(i=1;i<=n;i++)
		{ 
		    scanf("%d",&a[i]);
		    //printf("a[%d]=%d\n",i,a[i]);
		} 
  		for(i=1;i<=n;i++)
  		{ 
  			scanf("%d",&b[i]);
  			//printf("b[%d]=%d\n",i,b[i]);
  		} 
		for(i=1;i<=n;i++)
		{
			top++;
			stack[top]=a[i];
			//printf("stack[%d]=%d\n",top,stack[top]);
			
			while(stack[top]==b[count])//当栈顶与b当前元素相等时,栈顶出栈 ,b中元素后移一位
			{
				stack[top]=0;
				top--;
				count++;
				
				if(top==0)
				break;
				
			}
			 //printf("stack[%d]=%d\n",top,stack[top]);
			
		}
		
		for(i=1;i<=top;i++)
		    stack[i]=0;
		if(top==0)
		printf("Yes\n");
		else printf("No\n");
	
		
	 } 
	
	return 0;	
} 

括号匹配

题目描述
定义如下规则序列(字符串):

1.空序列是规则序列;

2.如果S是规则序列,那么(S)和[S]也是规则序列;

3.如果A和B都是规则序列,那么AB也是规则序列。

例如,下面的字符串都是规则序列:

(),[],(()),([]),()[],()[()]

而以下几个则不是:

(,[,],)(,()),([()

现在,给你一些由‘(’,‘)’,‘[’,‘]’构成的序列,你要做的,是补全该括号序列,即扫描一遍原序列,对每一个右括号,找到在它左边最靠近它的左括号匹配,如果没有就放弃。在以这种方式把原序列匹配完成后,把剩下的未匹配的括号补全。

输入格式
输入文件仅一行,全部由‘(’,‘)’,‘[’,‘]’组成,没有其他字符,长度不超过100。

输出格式
输出文件也仅有一行,全部由‘(’,‘)’,‘[’,‘]’组成,没有其他字符,把你补全后的规则序列输出即可。

输入输出样例
输入 #1复制
([()
输出 #1复制
()
说明/提示
将前两个左括号补全即可。
分析:其实这到题目跟上面的验证栈序列思路差不多,只是多了一个补充括号完整的操作,然后我就不会了,看了看题解,可算是明白了,主要是题意有点绕,没有用栈,设置一个用来标记的数组book[]反应括号有没有被匹配成功,然后从左往右扫描,当找到右括号时,就在前一段里面寻找,找到最近的左括号,匹配成功就标记,失败就不标记然后接着寻找。最后输出的时候,被标记的直接输出,没有被标记的成对输出。然后括号就愉快的牵手成功啦

代码:

#include<stdio.h>
#include<string.h>
int book[101];//全局变量开始都是0 
int main()
{
	char s[102];
	gets(s);
	int len;
	len=strlen(s);
	int i,j;
	for(i=0;i<len;i++)
	{
		if(s[i]==')')
		{
			for(j=i-1;j>=0;j--)
			{
				if(s[j]=='('&&book[j]==0)//找到了最近的一个(并且没有匹配
				{
					book[i]=book[j]=1;//将两个都标记一下
					break; 
				 }
				 else if(s[j]=='['&&book[j]==0)//找到了一个左括号但是匹配失败
				 {
				 	break;
				  } 
			}
		}
		else if(s[i]==']')
		{
			for(j=i-1;j>=0;j--)
			{
				if(s[j]=='['&&book[j]==0)
				{
					book[i]=book[j]=1;//将两个都标记一下
					break; 
				}
				else if(s[j]=='('&&book[j]==0)//找到了一个左括号但是匹配失败
				 {
				 	break;
				  } 
			}
		}
	}
	
	for(i=0;i<len;i++)
	{
		if(book[i]==1)//匹配成功的括号
		    printf("%c",s[i]);
		    
		else
		{
			
			if(s[i]=='('||s[i]==')')
			    printf("()");
			    
			    
			else if(s[i]=='['||s[i]==']')
			  printf("[]");
			
		} 
	}
	
	
	return 0;
	
}

后缀表达式

题目描述
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。

如:3*(5–2)+7对应的后缀表达式为:3.5.2.-*7.+@。’@’为表达式的结束符号。‘.’为操作数的结束符号。

输入格式
输入:后缀表达式

输出格式
输出:表达式的值

输入输出样例
输入 #1复制
3.5.2.-*7.+@
输出 #1复制
16
说明/提示
字符串长度,1000内。

分析:一直读入,读到@结束,因为有可能会有好几位的数字,所以需要一个a来记录,当督导‘.'的时候就清零,然后再根据加减乘除进行运算,运算结果返回栈中,操作结束后将栈顶输出。

代码:

#include<stdio.h>
int main()
{
	int stk[500];
	int i=0;
	char ch;
	int a=0;//用于数字位数问题 
	while((ch=getchar())!='@')
	{
		if(ch<='9'&&ch>='0')
		{ 
			a*=10;
			a=a+ch-'0';
		}
		else if(ch=='.')
		{
			stk[++i]=a;
			a=0;
		}	
		else if(ch=='-')
		{
			stk[i-1]=stk[i-1]-stk[i];
			//出 
			stk[i]=0;
			i--;
		}
		else if(ch=='+')
		{
			stk[i-1]=stk[i-1]+stk[i];
			//出 
			stk[i]=0;
			i--;
		}
		else if(ch=='*')
		{
			stk[i-1]=stk[i-1]*stk[i];
			//出 
			stk[i]=0;
			i--;
		}
		else if(ch=='/')
		{
			stk[i-1]=stk[i-1]/stk[i];
			//出 
			stk[i]=0;
			i--;
		} 
		
	}
	
	
	printf("%d",stk[1]);
	return 0;
 } 

队列

队列安排

题目描述
一个学校里老师要将班上NN个同学排成一列,同学被编号为1\sim N1∼N,他采取如下的方法:

先将11号同学安排进队列,这时队列中只有他一个人;

2-N2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1\sim (i -1)1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;

从队列中去掉M(M<N)M(M<N)个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

输入格式
第11行为一个正整数NN,表示了有NN个同学。

第2-N2−N行,第ii行包含两个整数k,pk,p,其中kk为小于ii的正整数,pp为00或者11。若pp为00,则表示将ii号同学插入到kk号同学的左边,pp为11则表示插入到右边。

第N+1N+1行为一个正整数MM,表示去掉的同学数目。

接下来MM行,每行一个正整数xx,表示将xx号同学从队列中移去,如果xx号同学已经不在队列中则忽略这一条指令。

输出格式
11行,包含最多NN个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。

输入输出样例
输入 #1复制
4
1 0
2 1
1 0
2
3
3
输出 #1复制
2 4 1

说明/提示
样例解释:

将同学22插入至同学11左边,此时队列为:

2 121

将同学33插入至同学22右边,此时队列为:

2 3 1231

将同学44插入至同学11左边,此时队列为:

2 3 4 12341

将同学33从队列中移出,此时队列为:

2 4 1241

同学33已经不在队列中,忽略最后一条指令

最终队列:

2 4 1241

数据范围

对于20%20%的数据,有N≤10N≤10;

对于40%40%的数据,有N≤1000N≤1000;

对于100%100%的数据,有N, M≤100000N,M≤100000。

分析:刚开始我用的是链表来做的,因为插入同学的时候比较方便好操作,然后没有考虑到后续删除我都需要遍历整个链表,当人数过大的时候,程序太慢了,所以最后re了好几个测试点。后来用静态链表写了,用数组模拟链表,虽然逻辑比较复杂,就是插入时候顺序要理清楚,但是删除很棒,直接O(1)。在下面也放出了本菜菜的链表代码,万一以后有的题不删除能用上呢哈哈哈哈哈
静态链表代码:

#include<stdio.h>
int main()
{
	int a[100002][2];
	int N;
	scanf("%d",&N);
	int num,f,m,k;
	int i;
	int start; 
	if(N!=0)
	{
		start=1;//开始将开始的人设置为1号 
		a[1][0]=-1;
		a[1][1]=-1;
		
		//插入操作 
		for(i=2;i<=N;i++)
		{
			scanf("%d %d",&num,&f);
			if(f==1)//插入到num右边 
			{
				if(a[num][1]==-1)//num的右边没人
				{
					a[i][0]=num;
					a[i][1]=-1;
					a[num][1]=i; 
					
				 } 
				 else 
				 { 
					a[i][0]=num;//i的左边设置成num
					a[i][1]=a[num][1];//i的右边设置成num的右边
					a[a[num][1]][0]=i;//num的右边的左边设置成i
					a[num][1]=i;//num的右边设置成i 
				} 
			}
			else if(f==0)//将i放到num的左边
			{
				if(a[num][0]==-1)//如果num是第一个娃
				{
					a[i][0]=-1;//则i的左边没人 
					a[i][1]=num;//i的右边是num
					a[num][0]=i;//num的左边是i
					start=i;//i为第一个 
				 } 
				 else//num不是第一个 
				 {
				 	a[i][0]=a[num][0];//i的左边设置成num的左边
					 a[i][1]=num;//i的右边设置成num
					 a[a[num][0]][1]=i;//num的左边的右边设置成i
					 a[num][0]=i;//num的左边设置成i 
				 	
				 }
			 } 
			
		}
		/*int l=start;
		
		 while(start!=-1)
		 {
		 	printf("%d ",start);
		 	start=a[start][1];
		 	
		 }
		printf("\n");*/
		//删除操作
		 scanf("%d",&m);
		 for(i=0;i<m;i++)
		 {
		 	scanf("%d",&k);//删除k
		 	
		 	if(a[k][0]==-1&&a[k][1]==-1)
		 	{ 
		 	    //如果两边都是-1,表示已经删除过了,直接跳过
				 continue; 
			
			}
			else 
			{
				if(a[k][0]==-1)//要删除第一个人
				{
					start=a[k][1];
					a[a[k][1]][0]=-1; 
					a[k][1]=-1;
					
				 }
				 else if(a[k][1]==-1)//删除最后一个人
				 {
				 	 a[a[k][0]][1]=-1;
					  a[k][0]=-1;
					   
				 	
				  } 
				  else //删除队伍中间的人的时候
				  {
				  	a[a[k][0]][1]=a[k][1];
				  	a[a[k][1]][0]=a[k][0];
					  
					  a[k][0]=-1;
					  a[k][1]=-1;				  	
				   } 
				
			}
			
		 }
		
		 while(start!=-1)
		 {
		 	printf("%d ",start);
		 	start=a[start][1];
		 }
	}
	
	return 0;
	
} 

链式链表代码:

#include<stdio.h>
#include<stdlib.h>
struct stu{
	int number;
	struct stu *next;
};
int main()
{
	int N;//N个娃
	scanf("%d\n",&N);
	//printf("N=%d\n",N);
	//先将所有学生按照老师指定顺序存好 
	int i,f,num;//num用来存在哪个娃的左右边,f用来储存方向 
	if(N!=0)//当人数至少有一个时
	{
		struct stu *t,*p,*a;
		struct stu* h=(struct stu*)malloc(sizeof(struct stu));
		h->next=NULL;
		struct stu* p1=(struct stu*)malloc(sizeof(struct stu));
		p1->number=1;
		p1->next=NULL;
		h->next=p1;
		for(i=2;i<=N;i++)
		{
			t=(struct stu*)malloc(sizeof(struct stu));
			t->number=i;
			t->next=NULL;
			scanf("%d %d",&num,&f);
			//printf("num=%d f=%d\n",num,f);
			if(f==1)//存到num的右边,即下一个
			{
				//printf("111111\n");
				p=h->next;
				while(p!=NULL)
				{
					if(p->number==num)
					{
					
						t->next=p->next;
						p->next=t;
						
						
						break;
					}
				    p=p->next;		
				}
				
			
		    }
			
			else if(f==0)//存到num的左边
			{
			//	printf("11dsbhbhjwb1\n");
				if(h->next->number==num)//如果·是·存到第一个的左边 
                {
                	t->next=h->next;
                	h->next=t;
                	
				 }
				 
				 else
				 {
				 	p=h->next;
				 	while(p->next!=NULL)
				 	{
				 		if(p->next->number==num)//插入到p的左边也就是插入到p上一个的右边 
				 		{
				 		
				 			t->next=p->next;
				 			
				 			p->next=t;
				 			break;
				 			
						 }
						 p=p->next;
					 }
				  } 
			 } 
			
		}
		
	
		   
		int M,m;//m次操作
		scanf("%d",&M);
		//printf("M=%d\n",M);
		for(i=0;i<M;i++)
		
		{ 
			scanf("%d",&m);
			//printf("m=%d\n",m);
			p=h;
			while(p->next!=NULL)
			{
				if((p->next->number)==m)//删去m号的娃 
				{
					//printf("-----%d\n",p->number);
					//printf("-----%d\n",p->next->number);
					a=p->next;
					p->next=a->next;
					
					break;
				}
				p=p->next;
	       } 
			
		}   
		   free(a);
		   p=h->next;
		while(p!=NULL)
		{   
		    printf("%d ",p->number);
		    p=p->next;
		} 
	 
		
	}  
	
 } 

机器翻译

题目背景
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。

题目描述
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。

假设内存中有 MM 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 M-1M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入 MM 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。

假设一篇英语文章的长度为 NN 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。

输入格式
共 22 行。每行中两个数之间用一个空格隔开。

第一行为两个正整数 M,NM,N,代表内存容量和文章的长度。

第二行为 NN 个非负整数,按照文章的顺序,每个数(大小不超过 10001000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

输出格式
一个整数,为软件需要查词典的次数。

输入输出样例
输入 #1复制
3 7
1 2 1 5 4 4 1
输出 #1复制
5
说明/提示
样例解释
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:

1:查找单词 1 并调入内存。
1 2:查找单词 2 并调入内存。
1 2:在内存中找到单词 1。
1 2 5:查找单词 5 并调入内存。
2 5 4:查找单词 4 并调入内存替代单词 1。
2 5 4:在内存中找到单词 4。
5 4 1:查找单词 1 并调入内存替代单词 2。
共计查了 55 次词典。

数据范围
对于 10%10% 的数据有 M=1M=1,N \leq 5N≤5;
对于 100%100% 的数据有 1 \leq M \leq 1001≤M≤100,1 \leq N \leq 10001≤N≤1000。
AC代码:

#include<stdio.h>
#include<stdlib.h>
struct queue{
	int danci;
	struct queue *next;
};
int main()
{
	struct queue *front,*rear,*t,*q,*p;
	front=rear=(struct queue*)malloc(sizeof(struct queue));
	front->next=NULL;//front指向队列头 
	rear->next=NULL;
	int i,m,n;
	int len=0;
	int count=0;
	
	scanf("%d %d",&m,&n);
	if(n<=m) printf("%d",n);
	else
	{ 
		for(i=0;i<n;i++)
		{
			int flag=0;
			t=(struct queue*)malloc(sizeof(struct queue));
			scanf("%d",&t->danci);
			t->next=NULL;
			//判断内存中是否已经有了
			p=front->next;
			while(p!=NULL)
			{
				if(p->danci==t->danci)
				{
					flag=1;
					break;
				}
				p=p->next;
			 } 
			if(flag==1) continue;
			
			if(rear==NULL)//第一个单词
			{
				front->next=t;
				rear->next=t;
				rear=t;
				
			}else
			{
				rear->next=t;
				rear=t;
				len++;
				
			}
			if(len>m)//长度超过容量时 
			{
				//a=front;
				front=front->next;//头后移,即将第一个删掉
				count++; 
			}
			
			
		}
	
		printf("%d",count+m);
	}
	
	return 0; 
}

链表

约瑟夫问题

题目描述
nn 个人围成一圈,从第一个人开始报数,数到 mm 的人出列,再由下一个人重新从 11 开始报数,数到 mm 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。树上表述是给出淘汰 n-1 名小朋友,而该题是全部出圈。

输入格式
输入两个整数 n,mn,m。

输出格式
输出一行 nn 个整数,按顺序输出每个出圈人的编号。

输入输出样例
输入 #1复制
10 3
输出 #1复制
3 6 9 2 7 1 8 5 10 4
说明/提示
1 \le m, n \le 1001≤m,n≤100
AC代码:

#include<stdio.h>
#include<stdlib.h>
struct people
{
	int num;
	struct people *next;
	
};
int main()
{
	struct people *head,*t,*s,*p;
	head=(struct people*)malloc(sizeof(struct people));
	head->next=NULL;
	int n,m;
	scanf("%d %d",&n,&m);
	int i;
	for(i=1;i<=n;i++)
	{
		t=(struct people*)malloc(sizeof(struct people));
		t->num=i;
		if(head->next==NULL)
		head->next=t;
		else s->next=t;
		
		s=t;
	}
	
	s->next=head->next;//头尾相连
	p=head;
	while(p->next!=p)//删到只剩下一个人的时候 
	{
		for(i=0;i<m-1;i++)//走着走着走到m的前一个,将m的序号打出来再将m跳过 
		{
			p=p->next;
		}
		printf("%d ",p->next->num);
		p->next=p->next->next;
	}
	
	//最后再将最后一个娃的序号打出来
	printf("%d",p->num);
	
	return 0; 
}
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值