目录
对于在oj上跑的题目,不一定要完全重现类,可以剥离类模板,或者不使用类,来加快速度
问题 A: 算法2-1:集合union
假设利用两个线性表LA和LB分别表示两个集合A和B(即:线性表中的数据元素即为集合中的成员),现要求一个新的集合A=A∪B。这就要求对线性表做如下操作:扩大线性表LA,将存在于线性表LB中而不存在于线性表LA中的数据元素插入到线性表LA中去。只要从线性表LB中依次取得每个元素,并依值在线性表LA中进行查访,若不存在,则插入之。上述操作过程可用下列算法描述之。
输入
有多组测试数据,每组测试数据占两行。第一行是集合A,第一个整数m(0<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 3
3 1 7 9
1 3
2 2 7
4 2 5 1 4
4 1 2 4 5
样例输出
1 5 2 6 3
1 7 9
1 5 2 6 3
1 5 2 6 3 7
1 5 2 6 3 7 9
3
2 7
3 2
3 2 7
2 5 1 4
1 2 4 5
2 5 1 4
2 5 1 4
2 5 1 4
2 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 中的所有元素输出即可。
没有什么坑,代码如下
#include<iostream>
#include <stdio.h>
using namespace std;
struct LinkNode {
int data;
LinkNode* link;
LinkNode(LinkNode* ptr = NULL) { link = ptr; data = 0; }
LinkNode(const int& iteam, LinkNode* ptr = NULL) { link = ptr; data = iteam; }
};
class List {
public:
List() { first = new LinkNode; }
List(const int& x) { first = new LinkNode(x); }
List(List& L);
~List() { makeEmpty(); }
void makeEmpty();
int Length()const;
LinkNode* getHead() { return first; }
LinkNode* Search(int x);
LinkNode* Locate(int i);
LinkNode* InputRear(int x);
bool SearchData(int x);
bool getData(int i, int& x);
bool setData(int i, int& x);
bool Insert(int i, int& x);
bool Remove(int i, int& x);
bool IsEmpty() { return first->link == NULL ? true : false; }
bool IsFull()const { return false; }
void output();
void input();
protected:
LinkNode* first;
};
List::List(List& L)
{
int value;
LinkNode* srcptr = L.getHead();
LinkNode* desptr = first = new LinkNode;
while (srcptr->link != NULL) {
value = srcptr->link->data;
desptr->link = new LinkNode(value);
desptr = desptr->link;
srcptr = srcptr->link;
}
desptr->link = NULL;
}
void List::makeEmpty() {
LinkNode* q;
while (first->link != NULL)
{
q = first->link;
first->link = q->link;
delete q;
}
}
int List::Length()const
{
LinkNode* p = first->link; int count = 0;
while (p!= NULL)
{
count++; p = p->link;
}
return count;
}
LinkNode* List::Search(int x) {
LinkNode* p = first->link;
while (p != NULL)
{
if (p->data == x) break;
else p = p->link;
}
return p;
}
LinkNode* List::InputRear(int x){
LinkNode*p=first;
while(p->link!=NULL){
p=p->link;
}
LinkNode*n=new LinkNode(x);
n->link=p->link;
p->link=n;
}
LinkNode* List::Locate(int i) {
if (i < 0)return NULL;
//cout<<"la"<<endl;
LinkNode* p = first; int k = 0;
while (p!= NULL && k < i)
{
p = p->link; k++;
}
return p;
}
bool List::getData(int i, int& x) {
if (i <= 0) return false;
LinkNode* p = Locate(i);
if (p == NULL) return false;
else { x = p->data; return true; }
}
bool List::setData(int i, int & x) {
if (i < 0) return false;
LinkNode* p = Locate(i);
if (p == NULL) return false;
else { p->data = x; return true; }
}
bool List::Insert(int i, int& x) {
LinkNode* p = Locate(i);
//cout<<"fa"<<endl;
if (p == NULL || i - 1 < 0 ) return false;
LinkNode* newNode = new LinkNode(x);
if (newNode == NULL)
{
cerr << " " << endl; return 0;
}
newNode->link = p->link;
p->link = newNode;
return true;
}
bool List::Remove(int i, int& x) {
LinkNode* p = Locate(i - 1);
if (p == NULL || p->link == NULL) return false;
LinkNode* del = p->link;
p ->link= del->link;
x = del->data; delete del;
return true;
}
void List::output()
{ //cout<<"ou"<<endl;
LinkNode* p = first->link;//cout<<"ea"<<endl;
if(p!=NULL) {
cout<<p->data;
p=p->link;}
while (p != NULL) {
cout << ' ' << p->data;
p = p->link;
}
}
bool List::SearchData(int x){
LinkNode*p=first->link;
while(p!=NULL){
if(p->data==x) return true;
else p=p->link;
}
if(p==NULL) return false;
}
void List::input(){
}
void Union(List&La,List&Lb){
int La_len,Lb_len,i,e;
La_len=La.Length();
Lb_len=Lb.Length();
La.output();
cout<<endl;
Lb.output();
cout<<endl;
//cout<<";r"<<La_len<<" "<<Lb_len<<endl;
for(i=1;i<=Lb_len;i++){
Lb.getData(i,e);
//cout<<i<<endl;
if(!La.SearchData(e)){
La.Insert(La_len,e);
La_len++;
La.output();
cout<<endl;
}
else{
La.output();
cout<<endl;
}
}
}
int main(){
List a,b;
int n1=0,n2=0;
int data=0;
int m=0;
while(scanf("%d",&n1) != EOF){
for(int i=0;i<n1;i++){
cin>>data;
a.InputRear(data);
}
cin>>n2;
for(int j=0;j<n2;j++){
cin>>data;
b.InputRear(data);
}
Union(a,b);
cout<<endl;
a.makeEmpty();
b.makeEmpty();
}
return 0;
}
问题 B: 算法2-2:有序线性表的有序合并
已知线性表 LA 和 LB 中的数据元素按值非递减有序排列,现要求将 LA 和 LB 归并为一个新的线性表 LC, 且 LC 中的数据元素仍然按值非递减有序排列。例如,设LA=(3,5,8,11) ,LB=(2,6,8,9,11,15,20) 则
LC=(2,3,5,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,第一个整数m(0<=m<=100)代表集合A起始有m个元素,后面有m个非递减排序的整数,代表A中的元素。第二行是集合B,第一个整数n(0<=n<=100)代表集合B起始有n个元素,后面有n个非递减排序的整数,代表B中的元素。每行中整数之间用一个空格隔开。
输出
每组测试数据只要求输出一行,这一行含有 m+n 个来自集合 A 和集合B 中的元素。结果依旧是非递减的。每个整数间用一个空格隔开。
样例输入
4 3 5 8 11
7 2 6 8 9 11 15 20
样例输出
2 3 5 6 8 8 9 11 11 15 20
提示
本题书中提供的算法是基于顺序表的。在使用顺序表时需要两倍于数据元素数目。如果使用链表则只需要存储一倍的元素。然而使用链表同样需要存储一倍的指针。所以对于这类问题数据结构的选取,如果数据域占用的空间很大则可以使用链表存储来节省空间,而对于数据域占用不大的情况,则使用顺序表也可以。
代码如下
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
struct LinkNode{
LinkNode *link;
int data;
LinkNode(LinkNode*ptr=NULL){
link=ptr;
data= 0;
}
LinkNode(const int& item,LinkNode*ptr=NULL){data=item,link=ptr;}
};
class List{
private:
LinkNode* first;//这是一个由附加链表头的链表
public:
List(){first=new LinkNode;}//链表头中不存放数据
List(const int&x){
first = new LinkNode(x);//生成一个链表头,包含数据
}
List(List& L);//复制
~List(){makeEmpty();}
void makeEmpty();
int Length()const;
LinkNode* getHead()const{return first;}
LinkNode* Search(int x);
LinkNode* Locate(int i);
bool getData(int i,int& x);
void setData(int i,int&x);
bool InsertRear(int i, int& x);//插入第i个结点之后,即新插入的结点在链表的i+1位上
bool InsertFront(int i, int&x);//插到第i个结点之前,即新插入的结点在链表的i位上
bool Remove(int i, int& x);
bool IsEmpty()const{return first->link==NULL?true:false;}
bool IsFull()const{return false;}
void input1(int num);
void inputFront(int endTag=0);
void inputRear(int endTag=0);
void output();
void input();
};
List::List(List& L){
int value;
LinkNode *scrptr = L.getHead();//获取头指针
LinkNode *desptr = first = new LinkNode;//创建一个新的链表
while(scrptr->link!=NULL){//被复制的链表下一节点不为空时
scrptr = scrptr->link;//被复制的指针往后挪一位
value = scrptr->data;//获取该节点的值
desptr->link = new LinkNode(value);//在新的链表里加入这一个值的结点
desptr = desptr->link;//将指向的空间的地址赋予desptr,使该节点成为新的desptr,便于创造下一结点
}
desptr->link = NULL;//扫尾处理
};
void List::makeEmpty(){
LinkNode *q;
while(first->link!=NULL){//头指针下面有结点
q = first->link;
first->link = q->link;//将节点向前挪,即删掉这个节点
delete q;//清除这个结点
}
};
int List::Length()const{
LinkNode *p = first->link;//p指向第一个结点
int count = 0;
while(p!=NULL){
p = p->link;
count++;
}
return count;
};
LinkNode* List::Search(int x){
LinkNode *current = first->link;//先定位到第一个结点
while(current!=NULL){
if(current->data==x) break;
else current = current->link;
}
return current;//返回的要么使包含所招X的结点的地址,要么是空指针
};
LinkNode* List::Locate(int i){
if(i<0)
return NULL;
LinkNode *current = first;
int k = 0;//把current放在头指针,k为0,方便从第零个开始计;
while(current!=NULL&&k<i){
current = current->link;
k++;
}
return current;//此处返回第i个节点地址,若返回NULL,表示i值太大
//此处也可以改成先和Length()比较
};
bool List::getData(int i,int& x){
if(i<=0)
return NULL;//头指针,即链表头中只包含下一结点地址,无本身数据
LinkNode *current = Locate(i);
if(current==NULL)
return false;
else {
x = current->data;
return true;
}
};
void List::setData(int i,int& x){
if(i<=0)
return;
LinkNode *current = Locate(i);
if(current==NULL)
return;
else
current->data = x;
};
bool List::InsertRear(int i,int& x){
LinkNode *current = Locate(i);
if(current==NULL)
return false;
LinkNode *newNode = new LinkNode(x);
if(newNode==NULL){
cerr << "存储分配错误" << endl;
exit(1);
}
newNode->link = current->link;
current->link = newNode;
return true;
};
bool List::InsertFront(int i, int &x){
LinkNode *current = Locate(i - 1);
if(current==NULL)
return false;
LinkNode *newNode = new LinkNode(x);
if(newNode==NULL){
cerr << "存储分配错误" << endl;
exit(1);
}
newNode->link = current->link;
current->link= newNode;
return true;
};
bool List::Remove(int i,int& x){
LinkNode *current = Locate(i-1);
if(current==NULL||current->link==NULL)
return false;
LinkNode *del = current->link;
current->link = del->link;
x = del->data;
delete del;
};
void List::output(){
LinkNode *current = first->link;
while(current!=NULL){
cout << current->data << " ";
current = current->link;
}
cout << endl;
};
/*void List::inputFront(int endTag){
int val=0;
LinkNode *newNode;
makeEmpty();
cin >> val;
while(val!=endTag){
newNode = new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link = first->link;
first->link = newNode;
cin >> val;
}
};*/
void List::input(){
makeEmpty();
int num=0;
LinkNode*newNode;
cin>>num;
int val=0;
LinkNode*p=first;
/*while(p->link!=NULL){
p=p->link;
}*/
for(int i=0;i<num;i++)
{
cin>>val;
newNode=new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link=p->link;
p->link=newNode;
p=p->link;
//cout<<p->data<<endl;
}
}void List::input1(int num){
makeEmpty();
//int num=0;
LinkNode*newNode;
//cin>>num;
int val=0;
LinkNode*p=first;
/*while(p->link!=NULL){
p=p->link;
}*/
for(int i=0;i<num;i++)
{
cin>>val;
newNode=new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link=p->link;
p->link=newNode;
p=p->link;
//cout<<p->data<<endl;
}
}
/*void List::inputRear(int endTag){
int val=0;
LinkNode *newNode;
makeEmpty();
cin >> val;
newNode = new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link = first->link;
first->link = newNode;
cin >> val;
LinkNode *current = newNode;
while(val!=endTag){
newNode = new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
current->link = newNode;
current = current->link;
cin >> val;
}
}*/
void fun(List& a,List& b){
int temp1=0;
int temp2=0;
int len1=a.Length();
int len2=b.Length();//cout << len1 << " " << len2 << endl;
int count2 = 1;
for(int i=1;i<=len1;i++){
for(int j=count2;j<=len2;j++){
a.getData(i,temp1);
b.getData(j,temp2);
if(temp1>=temp2){
a.InsertFront(i,temp2);
i++;
count2++;
}
}
len1 = a.Length();
}
for (int m = count2; m <= len2;m++){
b.getData(m, temp2);
a.InsertRear(len1, temp2);
len1++;
//a.output();
}
//b.makeEmpty();
a.output();
len1=a.Length();
//cout << len1 << endl;
/*for(int k = 1;k<=len1;k++){
a.getData(k,temp1);
b.InsertFront(1,temp1);
}*/
}
int main(){
List a;
List b;
int n1=0;
while(scanf("%d",&n1) != EOF){
a.input1(n1);
b.input();
// a.output();
//b.output();
fun(a,b);
//b.output();
}
return 0;
}
问题 C: 算法2-3~2-6:Big Bang
题目描述
Sheldon每天都会在小本本里记录些人名,当然有时也会与他们和好就会从小本本中将这个人名删除。我们假设Sheldon会在一个空的小本本上插入、删除、查询某个人。
要帮助Penny,你需要知道一个顺序表是怎么初始化、插入、删除以及查找的。下面我就将这些算法列举在下方。
输入
输入数据只有一组,有很多行。每行的格式可能是下列一种:
insert a name
delete name
show
search name
其中 a 是一个整数,代表在第a个名字前插入名字。name是一个姓名,只包含英文字母的大小写,每个名字不超过30个字符。
输入保证不会插入列表中已经存在的姓名,不会删除列表中不存在的姓名,也不会搜索列表中不存在的姓名。
输出
起始时,列表是空的。只输出show和search name 的结果。show将列表中的姓名全部输出,search只输出找到该名字的序号(从1开始)。每次输出占一行,姓名间用空格隔开。如果列表中没有名字了,show时也要输出一个空行。
样例输入
insert 1 Stuart
insert 2 Bernadette
show
search Stuart
delete Stuart
show
insert 2 Stuart
show
insert 1 Amy
insert 2 Leslie
insert 3 Stephanie
show
delete Leslie
show
search Stuart
样例输出
Stuart Bernadette
1
Bernadette
Bernadette Stuart
Amy Leslie Stephanie Bernadette Stuart
Amy Stephanie Bernadette Stuart
4
提示
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、题目要求每个输出占一行,所以要注意换行。
,题目中几乎将主要代码都写出来了。解决这道题使用上面的代码是可能复杂了点,但将各个功能独立出来是个不错的思路。以后修改就方便了,特别适用于代码量较大的程序。
2、C语言中参数的传递分为值传递和指针传递,而C++中多了一个引用传递。值传递和指针传递都不可以改变传递进来的值,但指针可以改变其所指向的值。在C语言中,调用函数时传入的参数叫做“实参”,而在函数声明或定义中在函数头中的参数叫做“形参”。值传递与指针传递中,形参的改变是不影响实参的。C++中,引用传递,形参与实参实际上是同一个内容的不同名字,因而形参的变化会改变实参。引用传递是C++中一个很重要也很方便的特性,比如在可能会产生不必要的复制时,采用引用传递是一个很不错的解决方案。
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
struct LinkNode{
LinkNode *link;
string data;
LinkNode(LinkNode*ptr=NULL){
link=ptr;
data=" ";
}
LinkNode(const string& item,LinkNode*ptr=NULL){data=item,link=ptr;}
};
class List{
private:
LinkNode* first;//这是一个由附加链表头的链表
public:
List(){first=new LinkNode;}//链表头中不存放数据
List(const string&x){
first = new LinkNode(x);//生成一个链表头,包含数据
}
List(List& L);//复制
~List(){makeEmpty();}
void makeEmpty();
int Length()const;
LinkNode* getHead()const{return first;}
LinkNode* Search(string x);
LinkNode* Locate(int i);
bool getData(int i,string& x);
void setData(int i,string&x);
bool InsertRear(int i, string& x);//插入第i个结点之后,即新插入的结点在链表的i+1位上
bool InsertFront(int i, string&x);//插到第i个结点之前,即新插入的结点在链表的i位上
bool Remove(int i, string& x);
bool IsEmpty()const{return first->link==NULL?true:false;}
bool IsFull()const{return false;}
void input1(int num);
void inputFront(int endTag=0);
void inputRear(int endTag=0);
void output();
void input();
int Searchnum(string x);
};
List::List(List& L){
string value;
LinkNode *scrptr = L.getHead();//获取头指针
LinkNode *desptr = first = new LinkNode;//创建一个新的链表
while(scrptr->link!=NULL){//被复制的链表下一节点不为空时
scrptr = scrptr->link;//被复制的指针往后挪一位
value = scrptr->data;//获取该节点的值
desptr->link = new LinkNode(value);//在新的链表里加入这一个值的结点
desptr = desptr->link;//将指向的空间的地址赋予desptr,使该节点成为新的desptr,便于创造下一结点
}
desptr->link = NULL;//扫尾处理
};
void List::makeEmpty(){
LinkNode *q;
while(first->link!=NULL){//头指针下面有结点
q = first->link;
first->link = q->link;//将节点向前挪,即删掉这个节点
delete q;//清除这个结点
}
};
int List::Length()const{
LinkNode *p = first->link;//p指向第一个结点
int count = 0;
while(p!=NULL){
p = p->link;
count++;
}
return count;
};
LinkNode* List::Search(string x){
LinkNode *current = first->link;//先定位到第一个结点
while(current!=NULL){
if(current->data==x) break;
else current = current->link;
}
return current;//返回的要么使包含所招X的结点的地址,要么是空指针
};
LinkNode* List::Locate(int i){
if(i<0)
return NULL;
LinkNode *current = first;
int k = 0;//把current放在头指针,k为0,方便从第零个开始计;
while(current!=NULL&&k<i){
current = current->link;
k++;
}
return current;//此处返回第i个节点地址,若返回NULL,表示i值太大
//此处也可以改成先和Length()比较
};
bool List::getData(int i,string& x){
if(i<=0)
return NULL;//头指针,即链表头中只包含下一结点地址,无本身数据
LinkNode *current = Locate(i);
if(current==NULL)
return false;
else {
x = current->data;
return true;
}
};
void List::setData(int i,string& x){
if(i<=0)
return;
LinkNode *current = Locate(i);
if(current==NULL)
return;
else
current->data = x;
};
bool List::InsertRear(int i,string& x){
LinkNode *current = Locate(i);
if(current==NULL)
return false;
LinkNode *newNode = new LinkNode(x);
if(newNode==NULL){
cerr << "存储分配错误" << endl;
exit(1);
}
newNode->link = current->link;
current->link = newNode;
return true;
};
bool List::InsertFront(int i, string &x){
LinkNode *current = Locate(i - 1);
if(current==NULL)
return false;
LinkNode *newNode = new LinkNode(x);
if(newNode==NULL){
cerr << "存储分配错误" << endl;
exit(1);
}
newNode->link = current->link;
current->link= newNode;
return true;
};
bool List::Remove(int i,string& x){
LinkNode *current = Locate(i-1);
if(current==NULL||current->link==NULL)
return false;
LinkNode *del = current->link;
current->link = del->link;
x = del->data;
delete del;
};
void List::output(){
LinkNode *current = first->link;
while(current!=NULL){
cout << current->data << " ";
current = current->link;
}
cout << endl;
};
/*void List::inputFront(int endTag){
int val=0;
LinkNode *newNode;
makeEmpty();
cin >> val;
while(val!=endTag){
newNode = new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link = first->link;
first->link = newNode;
cin >> val;
}
};*/
void List::input(){
makeEmpty();
int num=0;
LinkNode*newNode;
cin>>num;
string val="";
LinkNode*p=first;
/*while(p->link!=NULL){
p=p->link;
}*/
for(int i=0;i<num;i++)
{
cin>>val;
newNode=new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link=p->link;
p->link=newNode;
p=p->link;
//cout<<p->data<<endl;
}
}void List::input1(int num){
makeEmpty();
//int num=0;
LinkNode*newNode;
//cin>>num;
string val="";
LinkNode*p=first;
/*while(p->link!=NULL){
p=p->link;
}*/
for(int i=0;i<num;i++)
{
cin>>val;
newNode=new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link=p->link;
p->link=newNode;
p=p->link;
//cout<<p->data<<endl;
}
}
/*void List::inputRear(int endTag){
int val=0;
LinkNode *newNode;
makeEmpty();
cin >> val;
newNode = new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
newNode->link = first->link;
first->link = newNode;
cin >> val;
LinkNode *current = newNode;
while(val!=endTag){
newNode = new LinkNode(val);
if(newNode==NULL){
cerr << "wrong" << endl;
exit(1);
}
current->link = newNode;
current = current->link;
cin >> val;
}
}*/
int List::Searchnum(string x){
LinkNode*p=first;
int count=0;
while(p!=NULL){
p=p->link;
count++;
if(p->data==x){
return count;
}
}
return 0;
}
void fun(string s,List& a){
int num=0;
string x;
if(s=="insert"){
cin>>num;
cin>>x;
a.InsertFront(num,x);
}
else if(s=="search"){
cin>>x;
cout<<a.Searchnum(x)<<endl;
}
else if(s=="delete"){
cin>>x;
num=a.Searchnum(x);
a.Remove(num,x);
}
else if(s=="show"){
a.output();
}
}
int main(){
List a;
string s;
while(cin>>s){
fun(s,a);
}
return 0;
}
问题 D: 算法2-8~2-11:链表的基本操作
题目描述
输入
输入数据只有一组,第一行有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 1
21
show
delete 1
show
delete 2
show
delete 1
show
delete 2
insert 2 5
show
insert 1 5
show
insert 1 7
show
insert 2 5
show
insert 3 6
show
insert 1 8
show
get 2
样例输出
1 2 3
delete OK
2 3
delete OK
2
delete OK
Link list is empty
delete fail
insert fail
Link list is empty
insert OK
5
insert OK
7 5
insert OK
7 5 5
insert OK
7 5 6 5
insert OK
8 7 5 6 5
7
提示
1、因为输入数据中含有大量的插入和删除操作(不管你信不信,反正我信了),所以必须使用链表,否则很可能会超时。这也是考查链表的特性吧。
2、初始化链表的元素是倒序的,这个使用题目中创建列表的方法(从头部插入)就可以了。
这题考查的是链表的特性。顺序表中,怎样判断何时使用顺序表何时使用链表呢?就要看它们的特点了。顺序表的特点是随机存取、随机访问,也就是说如果存取和查询比较频繁的话使用顺序表比较合适;链表的特点是插入和删除时不必移动其后的节点,如果插入和删除操作比较频繁的话使用链表比较合适。
比较坑的一点是,cin>>的话因为缓冲区的原因会使时间延长,从而超时,可能凭运气能卡进去,但是用scanf能更快,但是scanf不能输入string,只能设置字符数组
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
struct LinkNode{
LinkNode *link;
int data;
LinkNode(LinkNode*ptr=NULL){
link=ptr;
data=0;
}
LinkNode(const int& item,LinkNode*ptr=NULL){data=item,link=ptr;}
};//insert, show,delete,get
void insertpoint(int n,int x,LinkNode*p){
LinkNode*cur=p;
LinkNode*newnode=new LinkNode(x);
while(n-1){
n--;
cur=cur->link;
}
if(cur==NULL){
cout<<"insert fail"<<endl;
return;
}
newnode->link=cur->link;
cur->link=newnode;
cout<<"insert OK"<<endl;
}
void showpoint(LinkNode*p){
LinkNode*cur=p->link;
if(cur==NULL){
cout<<"Link list is empty"<<endl;
return;
}
while(cur!=NULL){
cout<<cur->data<<" ";
cur=cur->link;
}
}
void getpoint(int n,LinkNode*p){
LinkNode*cur=p;
while(n){
n--;
cur=cur->link;
}
cout<<cur->data<<endl;
}
void deletepoint(int n,LinkNode*p){
LinkNode*cur=p;
while(cur->link==NULL){
cout<<"delete fail"<<endl;
return;
}
while(n-1){
cur=cur->link;
n--;
}
LinkNode*temp=cur->link;
cur->link=temp->link;
delete temp;
cout<<"delete OK"<<endl;
}
int main(){
LinkNode*first=new LinkNode;
int n=0;
cin>>n;
LinkNode*newnode;
LinkNode*cur=first;
int x=0;
for(int i=0;i<n;i++){
cin>>x;
newnode=new LinkNode(x);
newnode->link=first->link;
first->link=newnode;
}
cin>>n;
char s[10];
int num=0;
while(n){
scanf("%s",s);
if(s[0]=='s'){
showpoint(first);
}
else if(s[0]=='i'){
cin>>num>>x;
insertpoint(num,x,first);
}
else if(s[0]=='g'){
cin>>num;
getpoint(num,first);
}
else if(s[0]=='d'){
cin>>num;
deletepoint(num,first);
}
n--;
}
return 0;
}
问题 E: 算法2-13~2-16:静态链表
题目描述
静态链表是使用顺序存储结构来实现的链表。严蔚敏《数据结构(C语言版)》在介绍静态链表时使用的是一个姓氏列表。
图1是书本上的静态链表示例,图(a)是初始化后插入了8个姓氏的链表,图(b)是在第5个元素前插入了“SHI”而删除了“WANG”的结果。
图1:静态链表示例
(a)修改前的状态;(b)修改后的状态
现在,我们就来实现一下这个静态链表。实际上静态链表与一般含有指针的链表没有太大的差别,只是静态链表的结点存放的空间不是在使用时临时分配的,而是在一开始就分配了固定的一些,一般是用数组。同时一般的链表使用指针来指向下一个结点而在静态链表中则使用数组下标了。大家如果看严蔚敏的书会发现书上的算法还是有些问题的。下面我就直接给大家展示一种静态链表的实现算法。
最重要的是模拟系统分配内存的过程。可以预先定义一个全局数组(space)作为后面分配的空间,然后再初始化这个数组,为以后分配做准备,如图2。初始化后,这个数组中的状态应该如图3(a)一样。这样,数组的第0个节点就是用来标识哪个结点可用来存储数据的。那么如果是含有头结点的静态链表,则一般开始时数组的第1个节点就是用来存放头结点的,此时数组第0个结点标识第2个结点可以用来存储数据,而第1个结点(静态链表的头结点)的下一个结点下标为0,标识着这个静态链表为空,如图3(b)。静态链表的初始化以及插入删除各种算法与一般的链表是相似的。具体描述如下:
略
输入
静态链表的存储空间(图2中的space)始终只有11个节点,起始为空表。insert a e代表在第a个姓氏前插入姓氏e;delete a代表删除第a个姓氏;search e代表查找姓氏e的位置;show代表输出静态链表存储空间的状态。输入保证操作都合法。
输出
只遇到search和show时才输出。当遇到search时输出姓氏e在space中的位置;当遇到show时输出这11个结点的状态。姓氏占8个字符而数字占2个字符,姓氏左对齐。每个指令输出后面跟着含有20个星号的行。
样例输入
show
insert 1 ZHAO
show
insert 2 QIAN
show
insert 3 SUN
show
insert 4 LI
insert 5 ZHOU
insert 6 WU
insert 7 ZHENG
insert 8 WANG
show
insert 1 ZHANG
show
search LI
show
样例输出
2
0
3
4
5
6
7
8
9
10
0
********************
3
2
ZHAO 0
4
5
6
7
8
9
10
0
********************
4
2
ZHAO 3
QIAN 0
5
6
7
8
9
10
0
********************
5
2
ZHAO 3
QIAN 4
SUN 0
6
7
8
9
10
0
********************
10
2
ZHAO 3
QIAN 4
SUN 5
LI 6
ZHOU 7
WU 8
ZHENG 9
WANG 0
0
********************
0
10
ZHAO 3
QIAN 4
SUN 5
LI 6
ZHOU 7
WU 8
ZHENG 9
WANG 0
ZHANG 2
********************
5
********************
0
10
ZHAO 3
QIAN 4
SUN 5
LI 6
ZHOU 7
WU 8
ZHENG 9
WANG 0
ZHANG 2
********************
提示
1、怎样将字符串类型定义为ElemType呢?形如typedef int num一样,数组或者指针可以放在定义的类型名后面,例如将具有8个字符的姓氏定义为ElemType可以这样定义:typedef char ElemType[8]。
2、题目和书中给的算法描述还缺少静态链表的插入、删除以及显示,都需要自己写。
3、要求每个指令输出后跟一个空行,别忘了。
4、姓氏占8个字符,数字占2个字符,姓氏左对齐,可以这样输出printf("%-8s%2d");对于指令search也要输出占2个字符的数字。
5、静态链表初始化时将所有内存设为空,可以在InitSpace_SL中使用下面的方法:
memset(space, 0 ,sizeof(space));
总结:
静态链表与一般链表极为相似:使用数组来模拟内存,使用数组下表来模拟内存中的地址。
在设置要输入的数据时,基于自己的常规想法,设置为char*s=new char[8];结果发现在生成节点的时候因为直接赋值导致name的指向地址变成了s,为此在中间插入一个变量,每次插入重新生成一个char*t=new char[8];,以此来避免地址的指向问题
后来在search函数中,因为elem[q].name无法用string的函数(希望有大佬解惑),因此使用的是自己写一个for循环来比对是否能找到相同的字符串
delete不能删除数据块,比较坑
11个大小的数组,elem[0].link指向能插入的结点,即avil,elem[1].link指向链表头
#include<iostream>
#include<iomanip>
#include <string.h>
//elem[0]指向能用的空间
//elem[1]指向第一个结点
using namespace std;
//char t[8]={' ',' ',' ',' ',' ',' ',' ',' '};
struct slinknode{
char*name=new char[8];
int link;
/*slinknode(){
memset(name,' ',8*sizeof(char));
}*/
};
class slist{
private:
slinknode elem[11];
int avil;
public:
void initlist();
int locate(int i);
bool insert(int i,char* x);
bool remove(int i);
void output();
void search(char* x);
};
void slist::search(char*x){
int p=elem[1].link;
char * m=new char[8];
strcpy(m,x);
while(p!=0){
int size=strlen(m);int i=0;
for(;i<size;i++){
if(elem[p].name[i]!=m[i])
break;
}
if(i!=size)
p=elem[p].link;
else {
printf("%2d\n", p);
return;}
/*if(strcmp(elem[p].name,m)){
cout<<p<<endl;
}else{cout<<"Fa0"<<endl;
}*/
}
}
void slist::output(){
for(int i= 0;i<11;i++){
//cout<<elem[i].link;
printf("%-8s%2d\n", elem[i].name,elem[i].link);
//cout<<setiosflags(ios::left)<<setw(8)<<elem[i].name<<resetiosflags(ios::left)<<setiosflags(ios::right)<<setw(2)<<elem[i].link<<endl;
}
}//cout<<"********************"<<endl;<<endl
bool slist::remove(int i){
int p=locate(i-1);
if(p==-1) return false;
int q=elem[p].link;
//cout<<"S"<<endl;
//elem[q].name="";//清空内容
elem[p].link=elem[q].link;
elem[q].link=avil;//联通可以用的结点
avil=q;
elem[0].link=avil;
return true;
}
void slist::initlist(){
elem[0].link=2;
avil=2;
elem[1].link=0;
for(int i=2;i<10;i++){
elem[i].link=i+1;
}
elem[10].link=0;
for(int i=0;i<11;i++){
elem[i].name="";
}
//memset(elem, 0, sizeof(elem));
};
int slist::locate(int i){
if(i<0) return -1;
if(i==0) return 1;
int j=1,p=elem[1].link;
while(p!=-1&&j<i){
p=elem[p].link;
j++;
}
return p;
}
bool slist::insert(int i,char* x){
int p=locate(i-1);
int q=avil;
avil=elem[avil].link;
//cout<<avil<<endl;
//strcpy(elem[q].name,x);
/*for(int i=0;i<8;i++)
{
elem[q].name[i]=x[i];
};*/
char*t=new char[8];
strcpy(t,x);
elem[q].name=t;
elem[q].link=elem[p].link;
elem[p].link=q;
elem[0].link=avil;
//cout<<elem[0].link<<endl;
//cout<<elem[1].link<<endl;
//cout<<elem[q].name[1]<<endl;
}
int main(){
slist sl;
sl.initlist();
char*s=new char[8];
int x=0;
while(scanf("%s",s)!=EOF){
switch(s[2]){
case 'o':sl.output();cout<<"********************"<<endl;
break;
case 'l':
cin>>x;
sl.remove(x);
break;
case 's':
s=new char[8];
cin>>x>>s;
sl.insert(x,s);
break;
case 'a':
cin>>s;
sl.search(s);
cout<<"********************"<<endl;
break;
}//cout<<"va"<<endl;
}
}
以上是用笨拙的c++写的,一位大佬提供了标准写法
#include <bits/stdc++.h>
#include<cstring>
#define MAXSIZE 11
using namespace std;
typedef char ElemType[8];
typedef struct
{
ElemType data;
int cur;
} NodeType;
NodeType space[MAXSIZE];
typedef struct
{
int elem;
int length;
int listSize;
} SLinkList;
SLinkList SL;
void InitSpace_SL()
{
memset(space, 0, sizeof space);
for (int i = 0 ; i < MAXSIZE - 1; i++)
space[i].cur = i + 1;
space[MAXSIZE - 1].cur = 0;
space[0].cur = 2;
space[1].cur = 0;
SL.elem = 1;
SL.length = 0;
SL.listSize = 10;
}
int LocateELem_SL(SLinkList &S, ElemType e)
{
int i;
i = S.elem;
while (i && strcmp(space[i].data, e))
i = space[i].cur;
return i;
}
int Malloc_SL()
{
int i = space[0].cur;
if (space[0].cur)
space[0].cur = space[space[0].cur].cur;
return i;
}
void Free_SL(int k)
{
space[k].cur = space[0].cur;
space[0].cur = k;
}
void Insert_SL(NodeType *space, int n, ElemType e)
{
int alloc = Malloc_SL();
strcpy(space[alloc].data,e);
int i = 1;
for (int j = 1;j < n && space[i].cur;j++)
{
i = space[i].cur;
}
space[alloc].cur = space[i].cur;
space[i].cur = alloc;
SL.length++;
}
void Delete_SL(int n)
{
int i = 1,j=1;
for(j=1;j < n && space[i].cur;j++)
{
i = space[i].cur;
}
j = space[i].cur;
space[i].cur = space[j].cur;
space[j].cur = space[0].cur;
space[0].cur = j;
}
int main()
{
InitSpace_SL();
char op[100];
while (cin >> op)
{
if (!strcmp(op,"show"))
{
for (int i = 0; i < 11; i++)
{
printf("%-8s%2d\n", space[i].data, space[i].cur);
}
puts("********************");
}
else if (!strcmp(op,"insert"))
{
int n;
ElemType e;
cin >> n >> e;
Insert_SL(space, n, e);
}
else if (!strcmp(op,"delete"))
{
int n;
cin >> n;
Delete_SL(n);
}
else if (!strcmp(op,"search"))
{
ElemType v;
cin >> v;
printf("%2d\n", LocateELem_SL(SL, v));
puts("********************");
}
}
return 0;
}
问题 F: 算法2-18~2-19:双向循环链表
输入
输入数据只有一组,包含很多行。每行有1~3个整数。第一个整数如果是0,则表示输出双向链表中的所有元素;第一个整数如果是1,表示插入1个整数,其后跟2个整数i、e代表在第i个位置插入e;第一个整数如果是2,表示删除1个整数,其后跟1个整数i,表示删除的位置为i。
起始双向链表为空表。保证链表中每个元素不会重复,同时所有的操作都合法。
输出
当需要输出双向链表中的所有元素时输出,每次输出一行。整数间用一个空格隔开。
样例输入
1 1 2
0
1 2 7
0
2 1
0
1 2 4
1 3 5
1 2 6
0
2 3
0
样例输出
2
2 7
7
7 6 4 5
7 6 5
提示
提示:
1、如果使用switch,注意每个case后面使用break。
2、结构体定义时,因为定义里面有用到这个类型的指针,所以需要在开始struct后面写上结构体名。
3、注意循环链表全部遍历结束的条件是遍历的指针是否又指向了头结点。
总结:
1、双向链表的重要之处在于插入和删除时的操作顺序,切勿弄乱顺序而丢失数据。
2、循环链表全部遍历的结束条件需要注意,不是非空,而是判断是否到头结点了。
本题考查的是双向循环链表,所以上述都需要注意。
就写的时候只写自己要用的函数
#include<iostream>
using namespace std;
struct dnode{
int data;
dnode*llink,*rlink;
dnode(dnode*l=NULL,dnode*r=NULL):llink(l),rlink(r){
}
dnode(int x,dnode*l=NULL,dnode*r=NULL):data(x),llink(l),rlink(r){
}
};
class dlist{
private:
dnode*first;
dnode*rear;
int len;
public:
dlist();
~dlist();
dnode* locate(int i);
bool insert(int i,int x);
bool remove(int i);
void output();
};
void dlist::output(){
dnode*p=first->rlink;
while(p!=rear){
//cout<<" ";
cout<<p->data<<" ";
p=p->rlink;
}
cout<<endl;
}
bool dlist::remove(int i){
dnode*p=first;
dnode*del;
if(i==1){
del=p->rlink;
p->rlink=del->rlink;
del->rlink->llink=first;
delete del;
}else {
p=locate(i-1);
del=p->rlink;
p->rlink=del->rlink;
del->rlink->llink=first;
delete del;
}
len--;
return true;
}
bool dlist::insert(int i,int x){
dnode*newnode;
dnode*p;
if(i==1){
newnode=new dnode(x);
newnode->rlink=first->rlink;
newnode->llink=first;
first->rlink->llink=newnode;
first->rlink=newnode;
len++;
}
else {
p=locate(i-1);
newnode=new dnode(x);
newnode->rlink=p->rlink;
newnode->llink=p;
p->rlink->llink=newnode;
p->rlink=newnode;
len++;
}
return true;
}
dnode* dlist::locate(int i){
int x=0;
dnode*cur=first;
while(cur!=rear&&x<i){
x++;
cur=cur->rlink;
}
return cur;
}
dlist::dlist(){
first=new dnode;
rear=new dnode;
first->rlink=rear;
first->llink=rear;
rear->llink=rear->rlink=first;//cout<<"V"<<endl;
len=0;
}
dlist::~dlist(){
dnode* cur=first->rlink;
dnode*nex=cur->rlink;
while(cur!=first)
{
delete cur;
cur=nex;
nex=nex->rlink;}
delete first;
}
int main(){
dlist dl;
int n=0;
int num=0;
int da=0;
while(scanf("%d",&n)!=EOF){
switch(n){
case 0:dl.output();break;
case 1:cin>>num>>da;dl.insert(num,da);break;
case 2:cin>>num;dl.remove(num);break;
}
}
}
ps:头一次知道c++中的自动补全挺鸡肋所以一般不怎么用驼峰原则来命名变量,嘤