线性表专题(未完待续)


Problem  Link:http://codeup.cn/problemset.php?search=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E9%AB%98%E5%88%86%E7%AC%94%E8%AE%B0



1323: 算法2-1:集合union

时间限制: 1 Sec
内存限制: 32 MB
提交: 2292
解决: 588

题目描述

假设利用两个线性表LA和LB分别表示两个集合A和B(即:线性表中的数据元素即为集合中的成员),现要求一个新的集合A=A∪B。这就要求对线性表做如下操作:扩大线性表LA,将存在于线性表LB中而不存在于线性表LA中的数据元素插入到线性表LA中去。只要从线性表LB中依次取得每个元素,并依值在线性表LA中进行查访,若不存在,则插入之。上述操作过程可用下列算法描述之。
图:将两个列表合并的算法(C/C++描述)

上图算法中,在第8行取得集合B中的元素,然后再在第10行插入到集合A中。你的任务是先输出集合A和集合B中的元素,每个集合在一行中输出。然后每次在将集合B中的元素取出插入到集合A尾部后输出集合A中的元素。当然你的代码可以和上面的代码不一样,只要有相同的输出即可。

输入

有多组测试数据,每组测试数据占两行。第一行是集合A,第一个整数m0<m<=100)代表集合A起始有m个元素,后面有m个整数,代表A中的元素。第二行是集合B,第一个整数n(0<n<=100)代表集合B起始有n个元素,后面有n个整数,代表B中的元素。每行中整数之间用一个空格隔开。

输出

每组测试数据输出n+2行:前两行分别输出集合A、集合B中的数据,后面n行是每次从B中取出元素插入到A尾部后的集合A。每行整数之间用一个空格隔开,每组测试数据之间用一行空行隔开。

样例输入

5 1 5 2 6 33 1 7 91 32 2 74 2 5 1 44 1 2 4 5

样例输出

1 5 2 6 31 7 91 5 2 6 31 5 2 6 3 71 5 2 6 3 7 932 73 23 2 72 5 1 41 2 4 52 5 1 42 5 1 42 5 1 42 5 1 4

提示

提示:

1、使用数组时,给集合 A 分配的空间不小于200。因为将 B 中的元素添加到 A 中时,可能会超过 100 了。

2、利用 scanf("%d",&m) != EOF 来判断是否还有输入数据。

3、一个细节问题就是题目要求输出的格式是每行中元素之间用一个空格隔开,每组输出间用一个空行隔开。也就是说4个元素间只有3个空格,2组输出间只有1个空行。处理方法都一样。两种方法:一是除了第一个元素,后面的每个元素之前输出个空格;二是除了最后一个元素,前面的每个元素之后都输出一个空格。我往往采用第一种方式,因为许多编程语言中的数组都是从0开始的,而0正是判断语句中的“假”(当然Java中不是这样的)。

总结:

本题考查的是线性表的基本操作。实际上只考察了遍历和添加操作。虽然算法中使用的是“插入”,然而本题只要求插入到链表的尾部,因而只是添加而已。线性表按存储结构分为顺序表和链表。顺序表在插入时往往需要移动某些元素,而移动元素需要消耗大量时间。如果插入操作次数很多的话,采用链表会好些。但由于只是插入到线性表的尾部,因而也不必移动元素。所以采用顺序表解本题也不失为一个好方法。

如果采用顺序表,事先需要分配足够的内存。题目中 m 和 n 都是不大于100的,是不是给两个顺序表(数组实现)分配100的内存就够了呢?答案是否定的。因为将集合 B 添加到集合 A 中很可能就超过100个元素了。

还有,题目没有给定多少组测试数据,我们的方法就是判断是否读到了文件结尾。利用 scanf("%d",&m) != EOF 来作判断即可。

      对于解本题的算法,题目描述中已经有了,我就不再赘述。除了基本操作以外,还要看怎么输出。实际上就是在每次插入后使用一个循环将集合  A  中的所有元素输出即可。

来源

AC code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#define LL long long
#define MAXN 1000010
using namespace std;
const int INF=0x3f3f3f3f;
int A[222],B[222];
int vis[MAXN]; 
int main()
{
//	freopen("D:\\in.txt","r",stdin);
	int m,n,i,j;
	while(scanf("%d",&m)!=EOF)
	{
		memset(vis,0,sizeof(vis));
		for(i=0;i<m;i++)
		{
			scanf("%d",&A[i]);
			vis[A[i]]=1;
		}
		scanf("%d",&n);
		for(i=0;i<n;i++)
		{
			scanf("%d",&B[i]);
		}
		printf("%d",A[0]);
		for(j=1;j<m;j++)
		{
			printf(" %d",A[j]);
		}
		printf("\n");
		printf("%d",B[0]);
		for(j=1;j<n;j++)
		{
			printf(" %d",B[j]);
		}
		printf("\n");
		for(i=0;i<n;i++)
		{
			if(!vis[B[i]])
			{
				A[m++]=B[i];
				printf("%d",A[0]);
				for(j=1;j<m;j++)
				{
					printf(" %d",A[j]);
				}
				printf("\n");
			}
			else
			{
				printf("%d",A[0]);
				for(j=1;j<m;j++)
				{
					printf(" %d",A[j]);
				}
				printf("\n");
			}
		}
		printf("\n");
	}
	return 0;
}




1324: 算法2-2:有序线性表的有序合并

时间限制: 1 Sec
内存限制: 32 MB
提交: 1863
解决: 378

题目描述

已知线性表 LA 和 LB 中的数据元素按值非递减有序排列,现要求将 LA 和 LB 归并为一个新的线性表 LC, 且 LC 中的数据元素仍然按值非递减有序排列。例如,设LA=(3,5,8,11) ,LB=(2,6,8,9,11,15,20) 则
LC=(2,3,6,6,8,8,9,11,11,15,20)
算法描述如下:
从上述问题要求可知,LC中的数据元素或是LA中的数据元素,或是LB中的数据元素,则只要先设LC为空表,然后将LA或LB中的元素逐个插入到LC中即可。为使LC中元素按值非递减有序排列,可设两个指针 i 和 j 分别指向LA和LB中某个元素,若设 i 当前所指的元素为 a,j 所指的元素为 b,则当前应插入到 LC 中的元素 c 为 c = a < b ? a : b显然,指针 i 和 j 的初值均为1(实际写代码时往往是从 0 开始的),在所指元素插入 LC 之后,在 LA 或者 LB 中顺序后移。上述归并算法如下图:
 
图:有序列表有序插入算法

输入

有多组测试数据,每组测试数据占两行。第一行是集合A,第一个整数m0<=m<=100)代表集合A起始有m个元素,后面有m个非递减排序的整数,代表A中的元素。第二行是集合B,第一个整数n(0<=n<=100)代表集合B起始有n个元素,后面有n个非递减排序的整数,代表B中的元素。每行中整数之间用一个空格隔开。

输出

每组测试数据只要求输出一行,这一行含有 m+n 个来自集合 A 和集合中的元素。结果依旧是非递减的。每个整数间用一个空格隔开。

样例输入

4 3 5 8 117 2 6 8 9 11 15 20

样例输出

2 3 5 6 8 8 9 11 11 15 20

提示

        总结:

      本题书中提供的算法是基于顺序表的。在使用顺序表时需要两倍于数据元素数目。如果使用链表则只需要存储一倍的元素。然而使用链表同样需要存储一倍的指针。所以对于这类问题数据结构的选取,如果数据域占用的空间很大则可以使用链表存储来节省空间,而对于数据域占用不大的情况,则使用顺序表也可以。

来源

AC  code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#define LL long long
#define MAXN 1000010
using namespace std;
const int INF=0x3f3f3f3f;
int A[MAXN],B[MAXN],C[MAXN];
int main()
{
//	freopen("D:\\in.txt","r",stdin);
	int m,n,i,j,len;
	while(scanf("%d",&m)!=EOF)
	{
		for(i=1;i<=m;i++)
		{
			scanf("%d",&A[i]);
		}
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&B[i]);
		}
		i=j=1;
		len=0;
		while(i<=m&&j<=n)
		{
			if(A[i]<=B[j])
			{
				C[++len]=A[i];
				i++;
			}
			else
			{
				C[++len]=B[j];
				j++;
			}
		}
		while(i<=m)
		{
			C[++len]=A[i];
			i++;
		}
		while(j<=n)
		{
			C[++len]=B[j];
			j++;
		}
		if(len!=0)
		{
			printf("%d",C[1]);
			for(i=2;i<=len;i++)
			{
				printf(" %d",C[i]);
			}
			printf("\n");
		}
		else//注意为空的情况 
		{
			printf("\n");
		}
	}
	return 0;
}




1325: 算法2-3~2-6:Big Bang

时间限制: 1 Sec
内存限制: 32 MB
提交: 497
解决: 199

题目描述

复习考研累了的时候看看一集二十分钟左右的《生活大爆炸》也不失为一种乐趣。在剧中Sheldon可以说是一个极品,真不知Leonard是如何忍受这位极品室友成天的唠叨。
你知道么?Sheldon有一个神秘的小本本,记录了所有他从小开始讨厌的人名。Stuart这位漫画店老板就是小本本的一员哦,谁叫他常常毫不客气地挤兑Sheldon,曾多次赌赢过Sheldon呢。
Penny是一个漂亮的女孩,好奇心也很强。为了满足她的好奇心,我当回编剧让她意外知道了Sheldon的那个小本本放在了哪里。于是她几乎每天都去看,看看上面有哪些人。但是那个小本本上的人名实在太多。要知道她可是没上过大学在饭店里面当服务员啊。请聪明的你帮帮她处理处理那个小本本吧。
图1:《生活大爆炸》里的角色
Sheldon每天都会在小本本里记录些人名,当然有时也会与他们和好就会从小本本中将这个人名删除。我们假设Sheldon会在一个空的小本本上插入、删除、查询某个人。
要帮助Penny,你需要知道一个顺序表是怎么初始化、插入、删除以及查找的。下面我就将这些算法列举在下方。
图2:线性表的动态分配顺序存储结构以及初始化
图3:线性表的插入算法
图4:线性表的删除算法
图5:线性表的查找算法

输入

输入数据只有一组,有很多行。每行的格式可能是下列一种:
insert a name
delete name
show
search name
其中 a 是一个整数,代表在第a个名字前插入名字。name是一个姓名,只包含英文字母的大小写,每个名字不超过30个字符。
输入保证不会插入列表中已经存在的姓名,不会删除列表中不存在的姓名,也不会搜索列表中不存在的姓名。

输出

起始时,列表是空的。只输出show和search name 的结果。show将列表中的姓名全部输出,search只输出找到该名字的序号(从1开始)。每次输出占一行,姓名间用空格隔开。如果列表中没有名字了,show时也要输出一个空行。

样例输入

insert 1 Stuartinsert 2 Bernadetteshowsearch Stuartdelete Stuartshowinsert 2 Stuartshowinsert 1 Amyinsert 2 Leslieinsert 3 Stephanieshowdelete Leslieshowsearch Stuart

样例输出

Stuart Bernadette1BernadetteBernadette StuartAmy Leslie Stephanie Bernadette StuartAmy Stephanie Bernadette Stuart4

提示

提示:

1、名字是不含空格的,指令也是一定的,所以可以用scanf("%s", str)来读取。

2、上述代码有些函数头中变量类型与变量之间有个&,这个表示该变量是引用类型的,是C++特性。在C语言中存在值传递与指针传递,值传递中形参不可以改变实参的值,需要通过指针来修改。而引用变量实际上就是实参的另一个名字,这种类型的形参改变会影响实参的值。

3、使用题目中的代码需要自己定义其中缺失的类型和变量,例如Status、OK以及Error等。

4、ElemType类型中可以只有一个字符数组用来存储姓名。

5、LocateElem_Sq函数在调用时需要传递一个函数指针,可以这样定义:

Status cmp(ElemType e1, ElemType e2);

注意这个函数用来判断e1和e2是否相等,如果相等则返回非零值,否则返回0。因此可以在这个函数里直接返回 !strcmp(...)但最好需要改变返回类型。

6、内存分配以及字符串操作需要的头文件分别是stdlib.h和string.h需要被包含进来。

7、题目要求每个输出占一行,所以要注意换行。

总结:

1、实际上,题目中几乎将主要代码都写出来了。解决这道题使用上面的代码是可能复杂了点,但将各个功能独立出来是个不错的思路。以后修改就方便了,特别适用于代码量较大的程序。

2、C语言中参数的传递分为值传递和指针传递,而C++中多了一个引用传递。值传递和指针传递都不可以改变传递进来的值,但指针可以改变其所指向的值。在C语言中,调用函数时传入的参数叫做“实参”,而在函数声明或定义中在函数头中的参数叫做“形参”。值传递与指针传递中,形参的改变是不影响实参的。C++中,引用传递,形参与实参实际上是同一个内容的不同名字,因而形参的变化会改变实参。引用传递是C++中一个很重要也很方便的特性,比如在可能会产生不必要的复制时,采用引用传递是一个很不错的解决方案。

来源

AC code:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm> 
#define MAXN 1000010
using namespace std;

string names[111];
int len;

void insert(int pos,string name)
{
	int i;
	for(i=len;i>=pos;i--)
	{
		names[i+1]=names[i];
	}
	names[pos]=name;
	len++;
}

int search(string name)
{
	int i;
	for(i=1;i<=len;i++)
	{
		if(names[i]==name)
			return i;
	}
}

void delet(string name)
{
	int i,pos;
	pos=search(name);
	for(i=pos;i<len;i++)
	{
		names[i]=names[i+1];
	}
	len--;
}

void show()
{
	int i;
	if(len==0)
		puts("");
	else
	{
		cout<<names[1];
		for(i=2;i<=len;i++)
		{
			cout<<" "<<names[i];
		}
		cout<<endl;
	}
}

int main()
{
//	freopen("D:\\in.txt","r",stdin);
	string op,name;
	int id;
	len=0;
	while(cin>>op)
	{
		if(op=="insert")
		{
			cin>>id>>name;
			insert(id,name);
		}
		else if(op=="show")
		{
			show();
		}
		else if(op=="search")
		{
			cin>>name;
			cout<<search(name)<<endl;
		}
		else if(op=="delete")
		{
			cin>>name;
			delet(name);
		}
	}
	return 0;
}


问题 I: 算法2-8~2-11:链表的基本操作

时间限制: 1 Sec
内存限制: 32 MB
提交: 1048
解决: 271

题目描述

链表是数据结构中一种最基本的数据结构,它是用链式存储结构实现的线性表。它较顺序表而言在插入和删除时不必移动其后的元素。现在给你一些整数,然后会频繁地插入和删除其中的某些元素,会在其中某些时候让你查找某个元素或者输出当前链表中所有的元素。
下面给你基本的算法描述:
图1:链表类型的定义以及获得链表元素的算法描述
图2:链表的插入算法描述
图3:链表的删除算法描述
图4:链表的创建算法描述

输入

输入数据只有一组,第一行有n+1个整数,第一个整数是这行余下的整数数目n,后面是n个整数。这一行整数是用来初始化列表的,并且输入的顺序与列表中的顺序相反,也就是说如果列表中是1、2、3那么输入的顺序是3、2、1。
第二行有一个整数m,代表下面还有m行。每行有一个字符串,字符串是“get”,“insert”,“delete”,“show”中的一种。如果是“get”或者“delete”,则其后跟着一个整数a,代表获得或者删除第a个元素;如果是“insert”,则其后跟着两个整数a和e,代表在第a个位置前面插入e;“show”之后没有整数。

输出

如果获取成功,则输出该元素;如果删除成功则输出“delete OK”;如果获取失败或者删除失败,则输出“get fail”以及“delete fail”。如果插入成功则输出“insert OK”,否则输出“insert fail”。如果是“show”则输出列表中的所有元素,如果列表是空的,则输出“Link list is empty”。注:所有的双引号均不输出。

样例输入

3 3 2 121showdelete 1showdelete 2showdelete 1showdelete 2insert 2 5showinsert 1 5showinsert 1 7showinsert 2 5showinsert 3 6showinsert 1 8showget 2

样例输出

1 2 3delete OK2 3delete OK2delete OKLink list is emptydelete failinsert failLink list is emptyinsert OK5insert OK7 5insert OK7 5 5insert OK7 5 6 5insert OK8 7 5 6 57

提示

提示:

1、因为输入数据中含有大量的插入和删除操作(不管你信不信,反正我信了),所以必须使用链表,否则很可能会超时。这也是考查链表的特性吧。

2、初始化链表的元素是倒序的,这个使用题目中创建列表的方法(从头部插入)就可以了。

总结:

这题考查的是链表的特性。顺序表中,怎样判断何时使用顺序表何时使用链表呢?就要看它们的特点了。顺序表的特点是随机存取、随机访问,也就是说如果存取和查询比较频繁的话使用顺序表比较合适;链表的特点是插入和删除时不必移动其后的节点,如果插入和删除操作比较频繁的话使用链表比较合适。
AC code:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm> 
#define MAXN 1000010
#define ERROR 0
#define OK 1
using namespace std;

typedef int ElemType;
typedef bool Status;

typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode,*LinkList;

Status GetElem_L(LinkList &L,int i,ElemType &e){
	LinkList p;
	p=L->next;
	int j=1;
	while(p && j < i ){
		p=p->next;
		++j;
	}
	if(!p || j>i)
		return ERROR;
	e = p->data;
	return OK;
}

Status ListInsert_L(LinkList &L,int i,ElemType e){
	LinkList p,s;
	p=L;
	int j=0;
	while(p && j < i-1)
	{
		p=p->next;
		++j;
	}
	if(!p || j > i-1)
		return ERROR;
	s=(LinkList) malloc (sizeof(LNode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return OK;
}

Status ListDelete_L(LinkList &L,int i,ElemType &e){
	LinkList p,q;
	p=L;
	int j=0;
	while(p->next && j<i-1){
		p=p->next;
		++j;
	}
	if(!(p->next) || j>i-1)
		return ERROR;
	q=p->next;
	p->next=q->next;
	e=q->data;
	free(q);
	return OK;
}

void CreateList_L(LinkList &L,int n){
	LinkList p;
	int i;
	L=(LinkList) malloc(sizeof(LNode));
	L->next=NULL;
	for(i=n;i>0;--i){
		p=(LinkList) malloc(sizeof(LNode));
		scanf("%d",&p->data);
		p->next=L->next;
		L->next=p;
	}
}

void ShowList_L(LinkList L)
{
	LinkList p;
	p=L->next;
	if(p==NULL) printf("Link list is empty\n");
	else
	{
		printf("%d",p->data);//注意输出格式
		p=p->next;
		while(p)
		{
			printf(" %d",p->data);
			p=p->next;
		}
		printf("\n");
	}
}

int main()
{
//	freopen("D:\\in.txt","r",stdin);
	int n,m,i,e,s,cnt;
	LinkList L;
	char op[55];
	scanf("%d",&n);
	CreateList_L(L,n);
//	ShowList_L(L);
	scanf("%d",&m);
	for(cnt=0;cnt<m;cnt++)
	{
		scanf("%s",op);
		if(strcmp(op,"show")==0)
		{
			ShowList_L(L);
		}
		else if(strcmp(op,"delete")==0)
		{
			scanf("%d",&i);
			s=ListDelete_L(L,i,e);
			if(s==OK)
				printf("delete OK\n");
			else
				printf("delete fail\n");
		}
		else if(strcmp(op,"insert")==0)
		{
			scanf("%d%d",&i,&e);
			s=ListInsert_L(L,i,e);
			if(s==OK) printf("insert OK\n");
			else printf("insert fail\n");
		}
		else if(strcmp(op,"get")==0)
		{
			scanf("%d",&i);
			s=GetElem_L(L,i,e);
			if(s==ERROR)
				printf("get fail\n");
			else
				printf("%d\n",e);
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林下的码路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值