NO.1:集合合并
题目要求
这里是题干:
问题: 线性表的合并A=A∪B
设:有两个集合A和B分别用两个线性表LA和LB表示求:一个新的集合 A=A∪B
输入:两个集合
输出:按照要求合并的集合
注意:提交代码的时候,仅需提交你编写的那三个基本操作函数即可。
然后看下给出的输入输出样例:
测试输入 期待的输出 时间限制 内存限制 测试用例 1 以文本方式显示
- 5↵
- 1 2 3 4 5↵
- 6↵
- 3 4 5 6 7 8↵
以文本方式显示
- Output La:1,2,3,4,5,↵
- Output Lb:3,4,5,6,7,8,↵
- Result La:1,2,3,4,5,6,7,8,↵
1秒 64M 测试用例 2 以文本方式显示
- 0↵
- 4↵
- 1 2 3 4↵
以文本方式显示
- Output La:↵
- Output Lb:1,2,3,4,↵
- Result La:1,2,3,4,↵
1秒 64M 测试用例 3 以文本方式显示
- 5↵
- 8 9 0 1 2↵
- 6↵
- 8 9 0 3 2 1↵
以文本方式显示
- Output La:8,9,0,1,2,↵
- Output Lb:8,9,0,3,2,1,↵
- Result La:8,9,0,1,2,3,↵
1秒 64M
预设代码
#include <stdio.h>
#include <stdlib.h>
#define LIST_MAX_SIZE 100 //空间初始大小
#define OK 1
#define ERROR 0
typedef int ElemType; //元素的数据类型
typedef int Status; //状态。函数返回值
typedef struct {
// ElemType elem[ LIST_MAX_SIZE ]; // 存储空间
ElemType * elem; // 存储空间
int length; // 当前元素个数
int listsize; // 能够保存的最大元素数量
} SqList;
// 以下为函数原型
Status InitList( SqList & );
Status ListInsert( SqList &, int, ElemType ); //这是需要你编写的基本操作
Status GetElem( SqList, int, ElemType & ); //这是需要你编写的基本操作
int ListLength( SqList ); //这是需要你编写的基本操作
Status ListTraverse( SqList &, void (*)( ElemType ) );
void ListUnion( SqList &, SqList );
void out( ElemType );
int equal(ElemType, ElemType );
Status LocateElem(SqList, ElemType, Status (*)(ElemType,ElemType));
// 以下为函数定义
Status InitList( SqList & L ) // 建立一个空的线性表 L
{
L.elem = (ElemType *)malloc(LIST_MAX_SIZE*sizeof(ElemType));
// if ( !L.elem ) exit(-1); // 失败则终止程序
L.length = 0; // 空表长度为0
L.listsize = LIST_MAX_SIZE;
return OK;
}
Status ListTraverse( SqList &L, void (*visit)( ElemType ) )
{ // 依次对L的每个元素调用函数visit()。若visit()失败,则操作失败
int i, L_len = ListLength( L );
ElemType e;
for ( i = 1; i <= L_len; i++ ) {
GetElem(L, i, e);
(*visit)( e );
}
return OK;
}
int equal(ElemType x, ElemType y)
{ return x==y;
}
Status LocateElem( SqList L, ElemType e,
Status (*compare)(ElemType,ElemType) )
{ //在L中查找与元素 e 满足compare() 的第 1 个元素
//返回 L 中第 1 个与 e 满足关系compare( ) 的元素的位序
int i = 1;
ElemType * p;
while ( i<=L.length ) //
if ( (*compare)(e,L.elem[i-1]) ) break;
else i++;
if ( i <= L.length ) return i; // 找到 e,返回位序i
else return 0; //若没有找到,则返回0
}
void out( ElemType e )
{ printf("%d,", e);
}
void ListUnion( SqList &La, SqList Lb ) //求 A=A∪B
{ int La_len, Lb_len, i;
ElemType e;
La_len = ListLength( La ); // 求线性表的长度
Lb_len = ListLength( Lb );
for ( i = 1; i <= Lb_len; i++ ) {
GetElem(Lb, i, e); // 取Lb中第i个数据元素赋给e
if ( !LocateElem( La, e, equal ) )
ListInsert ( La, ++La_len, e ); // La中不存在和 e 相同的数据元素,则插入
}
}
int main()
{ SqList La, Lb;
int n, i;
ElemType e;
InitList( La );
InitList( Lb );
scanf("%d", &n); //读入集合A
for ( i=0; i<n; i++ )
{ scanf("%d", &e);
ListInsert( La, i+1, e );
}
scanf("%d", &n); //读入集合B
for ( i=0; i<n; i++ )
{ scanf("%d", &e);
ListInsert( Lb, i+1, e );
}
printf("Output La:");
ListTraverse( La, out );
printf("\nOutput Lb:");
ListTraverse( Lb, out );
ListUnion( La, Lb );
printf("\nResult La:");
ListTraverse( La, out );
printf("\n");
return OK;
}
/****************
Status ListInsert( SqList &L, int i, ElemType e )
{ //在顺序线性表L中第 i (1≤i≤L.length+1)个位置之前插入元素e,
Here is wating for you.
}
Status GetElem(SqList L, int i, ElemType &e)
{
Here is wating for you.
}
int ListLength(SqList L)
{
Here is wating for you.
}
信息梳理
题目比较简单,所以就简单梳理一下预设代码中的信息。
首先是创建了线性表的结构体SqList,里面包含三个元素:
typedef struct {
ElemType * elem; // 存储空间
int length; // 当前元素个数
int listsize; // 能够保存的最大元素数量
} SqList;
然后是它写好的基本操作:
Status InitList( SqList & );
Status ListTraverse( SqList &, void (*)( ElemType ) );
void ListUnion( SqList &, SqList );
void out( ElemType );
int equal(ElemType, ElemType );
Status LocateElem(SqList, ElemType, Status (*)(ElemType,ElemType));
所以这道题是不需要你去实现合并操作的,你只需要补全基本操作就即可:
Status ListInsert( SqList &, int, ElemType ); //这是需要你编写的基本操作
Status GetElem( SqList, int, ElemType & ); //这是需要你编写的基本操作
int ListLength( SqList ); //这是需要你编写的基本操作
代码实现
因为是基本操作所以就直接放代码了,如果是线性表不熟悉的可以去看一看线性表的介绍:
Status ListInsert( SqList &L, int i, ElemType e )
{ //在顺序线性表L中第 i (1≤i≤L.length+1)个位置之前插入元素e,
if(L.length==0){
L.elem[0]=e;
}
else{
for(int j=L.length;j>=i;j--){
L.elem[j]=L.elem[j-1];
}
L.elem[i-1]=e;
}
L.length++;
}
Status GetElem(SqList L, int i, ElemType &e)
{ e=L.elem[i-1];
return OK;
}
int ListLength(SqList L)
{ return L.length;
}
NO.2:链表原地逆序
题目要求
请编写函数 void inverse( LinkList ),实现单链表的原地置逆。即利用原来的结点将线性表:L =(a1, a2, …… , an)
变换为:L =( an, …… , a2, a1)
预设代码
注意:不要直接划走!!!继续往下看!!!
#include <iostream>
using namespace std;
typedef int ElemType;
typedef struct node
{ ElemType data;
struct node * next;
} NODE;
typedef NODE * LinkList;
void output( LinkList );
void change( int, int, NODE * );
LinkList createList( ElemType );
void inverse( LinkList );
LinkList createList( ElemType finish ) //finish:数据结束标记
{
ElemType x;
NODE *newNode;
LinkList first = new NODE; // 建立头结点
first->next = NULL;
first->data = finish;
cin >> x; // 约定以finish结束连续输入
while ( x != finish )
{
newNode = new NODE; // 建立新结点
newNode->data = x;
newNode->next = first->next; // ①
first->next = newNode; // ②
cin >> x;
}
return first;
}
void output( LinkList head )
{ cout << "List:";
while ( head->next != NULL )
{ cout << head->next->data << ",";
head = head->next;
}
cout << endl;
}
int main(int argc, char** argv)
{
LinkList head;
head = createList( -1 );
output( head );
inverse( head );
output( head );
return 0;
}
PS:这里说明下哈!这些题目的预设代码大多是用C++的,的是仔细看的话会发现,其实跟C语言差不多,并且C语言在C++环境下是可以直接编译的,所以如果老师要求用C++的话,用C语言也是完全没有问题的!
信息梳理
然后进行一个简单的信息梳理,首先是链表结点(包括指针域和数据域),同时注意这个链表是带有头结点的:
typedef struct node
{ ElemType data;
struct node * next;
} NODE;
typedef NODE * LinkList;
然后是定义的三个基本操作,我们需要完成其中的void inverse( LinkList ) :
void output( LinkList );
void change( int, int, NODE * );
LinkList createList( ElemType );
void inverse( LinkList );
然后注意以下就是LinkList createList( ElemType )它是使用头插法的。
代码实现
这是一道经典的链表题,其难点在于”原地“二字。意思就是要求在该链表的基础上实现置逆,而不能再去开辟新的链表空间,而这样的现实意义就是:尽可能地实现空间的复用,是空间效率更高!
直接从整个来链表看的话,似乎有点难下手,我们不妨将其减治:
我们假设只有两个结点,那么问题就很简单了,我们需要将1、2结点的连接断开同时将2指向1,以及将1指向空,这样两步操作。然后我们试着画一下三个结点:
发现同样是两步操作!!!好像问题变的简单起来了!!!我们不妨加上头结点看一看:
头结点好像有点不一样,每次置逆后,它要指向新链表的第一个结点。所以我们先不动头结点,每处理一次结点,就将其指向第一个结点。所以我们用一个结点*current来表示当前遍历的结点,又因为涉及到指针链接的断开,我们需要用一个结点*next来保存当前结点的后继,用*pre存好每次处理后的第一个结点:
LinkList pre=NULL;
LinkList current=head->next; //第一个遍历的结点当然是第一个结点
LinkList next=NULL;
所以我们这样写:
void inverse( LinkList head )
{ LinkList pre=NULL;
LinkList current=head->next;
LinkList next=NULL;
while(current){
next=current->next;
current->next=pre;
pre=current;
current=next;
}
head->next=pre;
}
next=current->next;
//这是保存当前结点的后继
current->next=pre;
pre=current;//将当前结点指向第一个结点,并更新第一个结点
current=next;
//然后更新next,进入下一次循环
然后当current为空时,循环停止。退出循环后,将我们之前没处理的头结点head指向现在链表的第一个结点pre。
试着运行一下:
过啦!!!
源码
集合合并
链表原地置逆
这两道题两周之前的题了(最近题有点多)(●'◡'●)。我抓紧把这两周的题解补上,也算是对知识的巩固吧。