datastruct_oj

 动态内存分配测试

本道题测试你的动态内存分配的掌握程度

一共有三个函数需要你完善:

int* malloc_memory( int size, int value ); // 分配内存并用指定值填充该内存
void free_memory( int type, int* p );      // 释放分配的内存
char* copy_string( const char* s );        // 分配空间并拷贝字符串

其中malloc_memory需要你分配可以存放sizeint的内存并将该内存用value填充,注意分配和填充的单位是int。你不需要释放空间,判分模板会自动释放。

free_memory函数的type参数为1时,p用new分配出来的,type参数为2时,p用new []分配出来的,C语言模式下不区分这两者。请你将p用合适的方法销毁。

copy_string函数要求你分配足够的空间,并将字符串s复制到对应的空间里。

#include <string.h>
int* malloc_memory( int size, int value ){
  int* p=new int[size];
  for(int i=0;i<size;i++)
    p[i]=value;
    return p;
}

void free_memory( int type, int* p ){
  if(type==1) delete p;
  else delete[] p;
}

char* copy_string( const char* s ){
  int size=strlen(s);
  char* p=new char[size];
  for(int i=0;i<size;i++)
    p[i]=s[i];
    return p;
}

动态数组分配

描述

本道题要求你读入一个动态的一维稀疏数组,将其创建并返回。

你总是应该分配合适的大小,不能多,也不能少。

输入

输入如下:第一行是包含的条目。从第二行开始是稀疏数组的条目,第一个数字是所在的下标(从0开始),第二个数字是值。例如:

2
1 2
2 3

第一行表示有2个条目第二行表示下标1的位置值为2。第三行表示下标2的位置值为3。

所以整个数组的内容为:0 2 3

输出

返回分配完成的指针,其中参数length请放置数组的长度。

请注意你分配的空间不能多,也不能少,要正好能把所有元素放置进去。

如果长度为0,则返回空指针。

#include <memory.h> // 你可能会用得着
#include <vector>
int* read_array( int& length ){
  int n,max=0;
  vector<vector<int>> ve;
  cin>>n;
  if(n==0){ length=0;
    return NULL;}
  else{
    for(int i=0;i<n;i++){
    int x,v;
  		cin>>x>>v;
    if(x>max)max=x;
   	ve.push_back({x,v});
  }
  length=max+1;
  	int* ans=new int[max+1];
  memset(ans, 0, sizeof(int)*(max+1));
  	for(auto o:ve){
    ans[o[0]]=o[1];
    }
    return ans;
  }
}

模拟格斗

描述

本道题需要你模拟一个格斗的场景,一共需要你实现三个函数:

PLAYER* read_player();                                     // 从输入中读取角色信息,创建角色对象返回指针
void destory_player( PLAYER* player );                     // 销毁指定角色对象
void players_fight( PLAYER* player1, PLAYER* player2 );    // 让两个角色互殴

每个角色都有一个HP,还有一套装备,装备的数量是不固定的,以下是对应的定义:

struct EQUIPMENT{   // 装备定义
    int atk;    // 攻击力
    int def;    // 防御力
};

struct PLAYER{     // 角色定义
    int HP;           // 血量
    int eqpt_count;   // 装备的数目
    EQUIPMENT* eqpts; // 放置装备列表的指针
};

角色按如下规则输入:第一行是角色的HP第二行是角色装备的数目第三行开始是角色装备的属性,攻击力在前,防御力在后例如:

50
2
10 5
10 20

说明角色的HP是50,有两件装备,第一件攻击力10、防御力5,第二件攻击力10、防御力20。

角色的攻击力和防御力等于身上所有装备的和,以上面为例,这个角色有20的攻击力和25的防御力,是一个肉盾。

当角色互殴时,有以下规则:

  1. 对对方造成的伤害等于己方攻击力-对方防御力
  2. 如果己方攻击小于对方防御力,则对对方的伤害是1点,即最少会造成1点伤害
  3. 如果角色受到大于自己HP的伤害时,HP归0(而不是变成负数)。

你需要正确计算双方互殴之后的剩余血量并赋值给对应角色的HP。

例如:角色1

10
2
3 1
2 1

角色2

10
2
1 2
3 1

则角色1攻击力=5,防御力=2,角色2攻击力为4,防御力为3那么角色1对角色2造成5-3=2点伤害,角色2对角色1造成4-2=2点伤害。此时双方的HP都应该是8。

又例如:角色1

10
2
3 1
2 1

角色2

1
2
9 8
7 3

角色1攻击5,防御2,角色2攻击16,防御11。角色1的攻击力比角色2的防御力低,但他依然能造成1点伤害。角色2有16点攻击,对角色1造成14点伤害,超过了角色1的10点hp。所以双方的血量都变为0,双双毙命。

输入

第一行是角色的HP第二行是角色装备的数目第三行开始是角色装备的属性,攻击力在前,防御力在后例如:

50
2
10 5
10 20

说明角色的HP是50,有两件装备,第一件攻击力10、防御力5,第二件攻击力10、防御力20。

互殴时,保证输入的指针是正确的

输出

正确配置对应的变量即可

//struct EQUIPMENT{
//    int atk;
//    int def;
//};
//
//struct PLAYER{
//    int HP;
//    int eqpt_count;
//    EQUIPMENT* eqpts;
//};

PLAYER* read_player(){
  PLAYER* role=new PLAYER;
  cin>>role->HP>>role->eqpt_count;
  EQUIPMENT* e=new EQUIPMENT[role->eqpt_count];
  role->eqpts=e;
  for(int i=0;i<role->eqpt_count;i++){
    cin>>e[i].atk>>e[i].def;
  }
    return role;
}

void destory_player( PLAYER* player ){
  delete[] player->eqpts;
  delete player;
}

void players_fight( PLAYER* player1, PLAYER* player2 ){
  int g1=0,g2=0;
  int f1=0,f2=0;
  for(int i=0;i<player1->eqpt_count;i++){
  g1+=player1->eqpts[i].atk;
  f1+=player1->eqpts[i].def;
  }
  for(int i=0;i<player2->eqpt_count;i++){
  g2+=player2->eqpts[i].atk;
  f2+=player2->eqpts[i].def;
  }
  int damage1=g1<f2?1:g1-f2;
  int damage2=g2<f1?1:g2-f1;
  player1->HP=player1->HP-damage2<=0?0:player1->HP-damage2;
  player2->HP=player2->HP-damage1<=0?0:player2->HP-damage1;
}

顺序表创建与删除

描述

本道题要求你读入数据并创建顺序表,具体的数据见下方。

你还需要编写销毁的额函数,保证内存可以被正确的回收。

本道题会进行内存泄漏检查,请注意。

输入

第一行是数字n,代表元素的数目。

第二行有n个数字,以空格隔开,是具体的元素。

第三行是数字m,表示要删除的次数。

第四行有m个数字,以空格隔开,表示删除元素的下标。

例如:

5
1 2 3 4 5
2
4 2

第一二行说明有5个元素,依次是1 2 3 4 5

第三四行说明要求删除第两次,分别下标是4和2。

所以得到的顺序表就是1 2 4

请注意,下标是要执行删除时对应的下标,而不是最初的下标。

举例来说:

5
1 2 3 4 5
3
1 1 1

对下标1连续删除3次,第一次删除时,得到的顺序表是1 3 4 5。第二次删除时,下标1对应的元素是3,因此变成1 4 5,第三次删除时,就变成1 5

输出

create_sequence的参数seq应该在函数内被正确的设置,seq即返回的创建好的顺序表。

destroy_sequence需保证seq的空间被正确的释放。内存泄漏将无法通过测试

//struct SEQUENCE{
//    int size;   // 总体分配的空间
//    int count;  // 现有的数据
//    int* data;  // data指向
//};
void create_sequence( SEQUENCE& seq ){
  int n,m;
  cin>>n;
  seq.size=n;
  seq.count=n;
  seq.data=new int[n];
  for(int i=0;i<n;i++)
    cin>>seq.data[i];
  cin>>m;
  for(int i=0;i<m;i++){
    int t;
  cin>>t;
    for(int j=t;j<seq.count-1;j++)
      seq.data[j]=seq.data[j+1];
    seq.count--;
  }
    
}

void destroy_sequence( SEQUENCE& seq ){
  delete[] seq.data;
}

顺序表的区间删除

描述

请编写函数

void remove_batch( SEQUENCE* seq, int begin, int end ); // C语言版本
void remove_batch( SEQUENCE& seq, int begin, int end ); // C++版本

删除[begin,end)区间的元素。

顺序表的count属性必须被正确的设置。

例如有顺序表:

1 2 3 4 5

要求你删除[1,4)的区间,那么你会得到:

1 5

输入

seq就是所要操作的顺序表,beginend是区间删除的起点和终点。

请注意区间的定义是符合大多数程序员编码习惯的[begin, end)。即所谓前闭后开区间,要删除的元素包括begin,但不包括end

seq.count<1000000

输出

顺序表seq必须被正确的设置,包括data的值和count

不需要(实际上,是不允许)对data进行销毁,伸缩,重新赋值等操作,这些操作可能导致内存检测失败。你只需要就地修改元素的值就可以了。

//struct SEQUENCE {
//    int  size; // 预留的空间
//    int  count; // 当前的数目
//    int* data; // 数据指针
//};

void remove_batch( SEQUENCE& seq, int begin, int end ){
  int i=begin,j=end;
  for(;j<seq.count;)
    seq.data[i++]=seq.data[j++];
  seq.count-=end-begin;
}

重整队列

描述

请编写重整顺序表的函数:

void reform_sequence( SEQUENCE* src, SEQUENCE* des ); // C版本
void reform_sequence( SEQUENCE& src, SEQUENCE& des ); // C++版本

对顺序表内容进行整理。

要求:去除src里相同的数字,同时将其在des中按顺序排列。

例如,假设src的值为:

1 1 3 3 2 2 2

去除重复数字并按顺序排列后,des的值应该为:

1 2 3

本道题必须在顺序表上进行操作,不允许调用库函数

请注意并不保证重复的数据一定相邻,也就是说这样的数据也是可能的:

1 3 1 2 2 3 1

得到的结果也是:

1 2 3

输入

srcdes都已初始化。des的空间足够你写入。

输出

des的内容应该被正确设置,包括元素和count属性。

//struct SEQUENCE{
//    int size; // 预留尺寸
//    int count; // 当前数量
//    int* data; // 数据
//};
#include <queue>
#include <unordered_set>
void reform_sequence( SEQUENCE& src, SEQUENCE& des ){
  priority_queue<int,vector<int>,greater<int>> q;
  unordered_set<int> s;
  for(int i=0;i<src.count;i++){
  		if(s.count(src.data[i])==0)
          q.push(src.data[i]);
    s.insert(src.data[i]);
  }
  des.count=q.size();
  for(int i=0;i<des.count;i++){
  des.data[i]=q.top();
    	q.pop();
  }
}

祖玛大消除(新)

描述

祖玛是大家都熟悉的游戏。如果射出的球击中球链,且此时生成的新链中相同颜色的球大于等于3个。则这些球可以被消除。

例如,你有一串链是:112233。此时你射出一个2的球,球击中了链中的第一个2(第3个球),此时链变成:1122233

因为中间的2有3个,满足消除的条件,他们会被消除,于是你获得:

1133

消除可以连击,例如:1122113射出2还是击中第一个2。此时链变成112221133,消除2后,链变成:

111133

前后两串1拼接在一起后,形成四个1,又满足消除条件,继续消除,得到:

33

注意连击只能发生在重新拼接的场合,如果原来的链就可以消除,不是通过拼接达到消除条件,则不能消除。

举例来说:

当前链是11122333,射出2击中第一个2,此时中间形成3个2111222333,2个2被消除,得到111333

尽管此时前后的13都满足消除条件,但他们不是拼接之后达到消除条件的,因此不能产生连击,最后的结果就是:

111333

基于上述的要求,我们将球组织成带头节点的双向循环链表的形式。请你编写消除的函数完成消除工作。

输入

函数

void hit_zuma_chain( DUAL_NODE* head, int pos, int color );

的第一个参数head是链表的头节点,第二个参数是击中球链的球的位置。

位置采用如下约定,下标以0开始计算,插入对应元素的前面。例如:

12345

如果此时pos=2,color=6,则应该形成,下标为2的元素是3,6应该插入3前面,形成

126345

的链条,而不是

162234

或者

123645ni

头指针的颜色被设计成绝对不会与其他颜色相同,这可以简化你的逻辑判断。

输出

你需要处理消除,确保head指向的链表的结果是正确的。

//struct DUAL_NODE {
//    DUAL_NODE* prev;
//    DUAL_NODE* next;
//    int color;
//};
void hit_zuma_chain( DUAL_NODE* head, int pos, int color ){
  	DUAL_NODE* node=new DUAL_NODE;
  	node->color=color;
  	DUAL_NODE* p=head->next;
  	for(int i=0;i<pos;i++)
      p=p->next;
  	node->prev=p;
  	node->next=p->next;
  	p->next->prev=node;
  	p->next=node;
  	while (1) {
		int num = 0;
		DUAL_NODE* p = node->prev;
		DUAL_NODE* n = node->next;
		while (node->color == p->color && p != head) {
			p = p->prev;
			num++;
		}
		while (node->color == n->color && n != head) {
			n = n->next;
			num++;
		}
		if (num > 1) { 
			while (p->next != n) {
				DUAL_NODE* t = p->next;
				p->next = t->next;
				t->next->prev = p;
				delete t;
			}
			p->next = n;  //更新p与n指针
			n->prev = p;
			if (n == p || n->color != p->color) 
				break;
			if (p != head)
				node = p;
			else
				node = n;
		}
		else
			break;
	}
}

链表排序

描述

给定带头节点的单链表head,请编写函数sort_list,对其中的元素进行排序。

你应该返回一个排好序的链表。你可以复用原来的链表,也可以新建链表。

当你新建链表时,你需要保证原链表被正确销毁,避免内存泄漏。

判分程序只负责销毁返回的链表,不会销毁原链表,请注意!

输入

待排序的链表head

输出

排好序的链表。

注意如果你新建了链表,你需要负责将原链表销毁。

//struct Node{
//    int   value;
//    Node* next{nullptr};
//};
#include <vector>
#include <algorithm>
Node* sort_list( Node* head ){
  vector<int> v;
  Node* p=head->next;
  while(p!=nullptr){
    v.push_back(p->value);
    p=p->next;
  }
  sort(v.begin(),v.end());
  p=head->next;
  int index=0;
  while(p!=nullptr){
  p->value=v[index++];
    p=p->next;
  }
    return head;
}

多项式乘法--简单版

描述

本道题要求你计算多项式的乘法,但乘数只有一项:

例如:

(1+4*x^2+5*x^3+2*x^5)*(2*x^3) = 2*x^3+8*x^5+10*x^6+4*x^8

其中4*x^2表示4乘x的二次方。

多项式保存在一个链表里,节点表示为:

struct PolyNode{
    int coef;
    int pow;
    PolyNode* next{nullptr};
};

其中coef为系数项,即上面的4pow为次数,即上面的2,链表带头节点,各项以次数为序依次排列。

因此

1+4*x^2+5*x^3+2*x^5

可以被表示为如下的链表:

head->(coef=1, pow=0)->(coef=4,pow=2)->(coef=5,pow=3)->(coef=2,pow=5)

现在编写函数:

PolyNode* polynomial_times_term( const PolyNode* p1, int coef, int pow )

计算多项式乘某项的结果。

输入

PolyNode* polynomial_times_term( const PolyNode* p1, int coef, int pow )

p1为被乘数,即上面的1+4*x^2+5*x^3+2*x^5,链表带头指针。

coefpow为乘数的系数项和幂次,在上例中,乘数是2*x^3,因此coef=2pow=3

输出

返回乘法的结果,结果应该为一个链表,带头节点。

各项必须以幂次为单位升序排列

//struct PolyNode{
//    int coef;//系数
//    int pow;//次数
//    PolyNode* next{nullptr};
//};
PolyNode* polynomial_times_term( const PolyNode* p1, int coef, int pow ){
  	PolyNode* ans=new PolyNode;
  	PolyNode* node=new PolyNode;
  	ans->next=node;
  	PolyNode* p=p1->next;
  	while(p!=nullptr){//创建答案链表
    node->coef=p->coef*coef;
    node->pow=p->pow+pow;
    if(p->next!=nullptr){
    node->next=new PolyNode;
    node=node->next;
    }
    p=p->next;
    }
    return ans;
}

多项式加法

描述

本道题要求你计算多项式的加法:

例如:

(3+2*x^1-3*x^2+2*x^5) + (1+3*x^1+3*x^2+3*x^3) = 4+5*x^1+3*x^3+2*x^5

其中3*x^2表示3乘x的二次方。

注意这里x^2项在相加之后系数为0,应该消项

多项式保存在一个链表里,节点表示为:

struct PolyNode{
    int coef;
    int pow;
    PolyNode* next{nullptr};
};

其中coef为系数项,即上面的4pow为次数,即上面的2,链表带头节点,各项以次数为序依次排列。

因此

3+2*x^1-3*x^2+2*x^5

可以被表示为如下的链表:

head->(coef=3, pow=0)->(coef=2,pow=1)->(coef=-3,pow=2)->(coef=2,pow=5)

现在编写函数:

PolyNode* polynomial_add( const PolyNode* p1, const PolyNode* p2 );

计算多项式加法的结果。

输入

p1p2为待相加的两个多项式。

输出

返回乘法的结果,结果应该为一个链表,带头节点。

各项必须以幂次为单位升序排列

//struct PolyNode{
//    int coef;
//    int pow;
//    PolyNode* next{nullptr};
//};

PolyNode* polynomial_add( const PolyNode* p1, const PolyNode* p2 ){
    PolyNode* ans=new PolyNode;
    PolyNode* node=ans;
	int* value=new int[1005];
	for(int i=0;i<1005;i++)
    value[i]=0;
  	PolyNode* temp=p1->next;

  	while(temp!=nullptr){//对p1和p2都进行一次遍历,记录x的每个次数对应的系数
    value[temp->pow]+=temp->coef;
    temp=temp->next;
    }
  	temp=p2->next;
    while(temp!=nullptr){
    value[temp->pow]+=temp->coef;
    temp=temp->next;
    }
  	for(int i=0;i<1005;i++){//创建答案链表
    	if(value[i]!=0){
        PolyNode* p=new PolyNode;
        p->coef=value[i];
        p->pow=i;
        node->next=p;
        node=node->next;
        }
    }
  	delete[] value;
    return ans;
}

多项式乘法-完全体

描述

该来的总会来,本道题要你计算两个多项式相乘的结果。

多项式的描述不再赘述,请参考之前的内容。

注意,本题有内存泄漏检测,p1p2和你返回的链表,由判题模板负责销毁。

除此之外你计算用到的临时数据,需要你自己负责销毁。

显然你需要善用上面前面两道题的结果,才能计算本道题。

输入

p1p2为待相乘的两个多项式。

你不用关心p1p2的销毁,由判题模板自动销毁。

输出

返回乘法的结果,结果应该为一个链表,带头节点。

各项必须以幂次为单位升序排列

返回的链表也由判题模板负责销毁。

//struct PolyNode{
//    int coef;
//    int pow;
//    PolyNode* next{nullptr};
//};
PolyNode* polynomial_times( const PolyNode* p1, const PolyNode* p2 ){
    PolyNode* ans=new PolyNode;
    PolyNode* node=ans;
  	int* value=new int[1005];
  	for(int i=0;i<1005;i++)
    value[i]=0;
  	PolyNode* temp=p1->next;
  	PolyNode* t;
  	while(temp!=nullptr){//对于p1的每一项都与p2所有项相乘,存储相乘结果
    t=p2->next;
      while(t!=nullptr){
      	value[temp->pow+t->pow]+=temp->coef*t->coef;
       t=t->next;
      }
    temp=temp->next;
    }
  
  for(int i=0;i<1005;i++){//创建答案链表
  if(value[i]!=0){
    PolyNode* q=new PolyNode;
    q->coef=value[i];
    q->pow=i;
  	node->next=q;
    node=node->next;
  	} 
  }
  delete[] value;
    return ans;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值