姓名:Far_Rainbow
学号: 202505000X
专业: 软件工程
年级:2020级
实验名称:实验2 基于顺序表的非递减有序表的合并
实验内容:
(1)实验目的
通过该实验,深入理解顺序表的逻辑结构、物理结构等概念,掌握顺序表基本操作的编程实现,注意顺序表插入、删除等操作过程中数据元素的移动现象,培养学生编写程序时,要考虑程序的健壮性,全面考虑问题,熟练掌握通过函数参数返回函数结果的办法。
(2)实验内容
编程实现顺序表下教材第二章定义的线性表的基本操作,并根据已经实现的基本操作,实现两个非递减有序的线性表的合并,注意,合并时,如果有重复的元素(一个表内部的重复元素和两个表之间的重复元素),请保留一个。
(3)实验要求
- 求前驱是指,输入一个元素值(而不是位置),求该元素在顺序表中的直接前驱元素值。求后继是指:输入一个元素值(而不是位置),求该元素在顺序表中的直接后继元素值;
- 为了方便修改数据元素的类型,请使用类型重定义,可以方便修改线性表中的数据元素的类型;
- 大部分函数的返回结果应是函数执行是否成功的一种状态,执行成功了,才返回具体的结果值;
- 对每个功能进行测试时,要求把不合法的情况也测试一下。具体见下面的测试用例;
- 采用菜单形式对应各个操作,使其编成一个完整的小软件,参考界面如下。注意:程序运行过程中菜单不要做成刷屏的效果,测试过的数据不要清除,这样方便截图和查看。
(4)验收/测试用例
通过菜单调用各个操作,测试点:
- 没有初始化前进行其他操作,程序是否能控制住;即,如果没有初始化线性表,其他的功能是无法正常进行的,如果选择进行其他操作,要提示先进行初始化;
- 先选择菜单1,初始化一个顺序表(初始化顺序表,是指初始化一个空的线性表,里面的元素个数是0);
- 选择菜单10,插入数据(位置, 数据),要测插入位置不合法的情况(0,1)、(2,1),正确插入3个数据(1,20)、(1,10)、(3,30);
- 显示顺序表中的数据,屏幕输出10, 20, 30;
- 判空,屏幕输出顺序表非空;
- 输出顺序表长度,屏幕输出3;
- 获取指定位置元素,要测指定位置在【1,3】范围之外的情况和之内的情况;
- 定位,输入:40, 输出:不存在,输入20,输出位置为2;
- 求直接前驱,要测求第一个元素的前驱、不存在顺序表中的元素的直接前驱,其他元素的直接前驱;输入10,输出:第一个元素没有前驱,输入20,输出前驱是10,输入40,输出该元素不存在;
- 求直接后继,要测最后一个元素的后继、不存在顺序表中的元素的直接后继,其他元素的直接后继;同上求前驱;
- 删除,要测位置在【1,3】范围之外的情况和之内的情况;
- 清空操作后再测长度,判断是否为空;清空后,测试菜单6到11的功能,看是否能够正确提示。
- 销毁顺序表,销毁线性表之后还能不能做插入,删除等操作,如果选其他操作,就要提示线性表已经销毁不存在;
- 测试合并操作,第一个线性表中的元素是(2,3,3,4,5),第二个线性表中的内容是(1,4,5,6,6,7),合并后的结果,请输出。
实验类型:验证性
实验的重难点:顺序表的定义和实现,销毁和清空的区别、 两个非递减有序表的去重合并
实验环境:TDM-GCC 4.9.2 64-bit
实验步骤及完成任务情况:
一、设计思想
- 采用模板实现泛型编程,可创建任意数据类型的顺序表;
- 采用动态内存分配的方式管理顺序表的容量,在顺序表内存空间不够时通过expend()函数扩容;
- 体会顺序表插入删除时间复杂度都是 O ( n ) O(n) O(n)的特点
二、主要源代码
#include <iostream>
#define SEQUENCESIZE 1000
#define SEQUENCEINCREMENT 500
using std::cin;
using std::cout;
using std::endl;
template <typename T> class Sequence {
private:
T* elem;
int _length;
int _capacity;
void expand();
public:
Sequence() {elem = NULL; _length = 0; }
Sequence(T arr[], int size)
{init(); for (int i = 0; i < size; ++ i) elem[i] = arr[i]; _length = size; }
~Sequence() {destroy(); }
void init();
void destroy();
void clear() {_length = 0; }
T operator[](int pos) {return elem[pos]; }
bool isempty() const {return _length > 0 ? 0 : 1; }
bool exist() const {return elem > 0 ? 1 : 0; }
int getlength() const {return _length; }
bool getelem(int pos, T& e) const
{if(pos > _length + 1 || pos < 1) return false; e = elem[pos-1]; return true; }
int elemlocate(T const& e) const
{for(int i = 0; i < _length; ++ i) if(elem[i] == e) return i+1; return -1;}
bool priorelem(T const& e, int& pos);
bool nextelem(T const& e, int& pos);
bool insert(T const& e, int pos);
bool remove(T& e, int pos);
void push(T const& e);
T pop() {-- _length; return _length + 1; }
void traverse() const
{for(int i = 0; i < _length; i ++) cout<<elem[i]<<' '; }
void merge(Sequence<T>& Sa, Sequence<T>& Sb);
};
template <typename T> void Sequence<T>::init()
{
elem = new T[SEQUENCESIZE];
_capacity = SEQUENCESIZE;
}
template <typename T> void Sequence<T>::destroy()
{
if (!exist()) return;
delete[] elem;
elem = NULL;
_length = 0;
_capacity = 0;
}
template <typename T> void Sequence<T>::expand()
{
T* newelem = new T[_capacity+SEQUENCEINCREMENT];
for(int i = 0; i < _length; ++ i) newelem[i] = elem[i];
delete[] elem;
elem = newelem;
}
template <typename T> bool Sequence<T>::priorelem(T const& e, int& pos)
{
int i = elemlocate(e);
if (i < 2 || i > _length) return false;
pos = i - 1;
return true;
}
template <typename T> bool Sequence<T>::nextelem(T const& e, int& pos)
{
int i = elemlocate(e);
if (i < 1 || i > _length - 1) return false;
pos = i + 1;
return true;
}
template <typename T> bool Sequence<T>::insert(T const& e, int pos)
{
if (pos < 1 || pos > _length + 1) return false;
if (_capacity < _length + 1) expand();
for(int j = _length - 1; j >= pos - 1; -- j) // 最开始写成j>i-1了,debug了好几个小时,血的教训
elem[j+1] = elem[j];
elem[pos-1] = e;
++ _length;
return true;
}
template <typename T> bool Sequence<T>::remove(T& e, int pos)
{
if(pos < 1 || pos > _length) return false;
e = elem[pos-1];
for(int j = pos - 1; j < _length - 1; ++ j)
elem[j] = elem[j+1];
-- _length;
return true;
}
template <typename T> void Sequence<T>::push(T const& e)
{
if (_capacity < _length + 1) expand();
elem[_length] = e;
++ _length;
}
template <typename T> void Sequence<T>::merge(Sequence<T>& Sa, Sequence<T>& Sb)
{
int i = 0, j = 0, k = 0;
while(i < Sa._length && j < Sb._length) { //不能 <= ,否则会数组下标越界
if (Sa[i] < Sb[j]) elem[k++] = Sa[i++];
else if (Sa[i] > Sb[j]) elem[k++] = Sb[j++];
else if (Sa[i] == Sb[j++]) elem[k++] = Sa[i++]; // 去重
}
while (i < Sa._length) elem[k++] = Sa[i++];
while (j < Sb._length) elem[k++] = Sb[j++];
_length = k;
}
int main()
{
cout<<"\n 基于顺序表的非递减有序表的合并\n\n";
cout<<"1---初始化一个线性表\n";
cout<<"2---销毁线性表\n";
cout<<"3---清空线性表\n";
cout<<"4---判断线性表是否为空\n";
cout<<"5---求线性表长度\n";
cout<<"6---获取线性表中指定位置的元素\n";
cout<<"7---获取线性表元素的位置\n";
cout<<"8---求前驱\n";
cout<<"9---求后继\n";
cout<<"10--在线性表指定位置插入元素\n";
cout<<"11--删除线性表指定位置的元素\n";
cout<<"12--显示线性表\n";
cout<<"13--合并两个非递减有序线性表\n";
cout<<"\n\t退出,输入一个负数!\n\n";
Sequence<int> Se;
while(true)
{
int myOption;
cout<<"\n请输入操作序号:";
cin>>myOption;
switch(myOption){
case 1:{
if(Se.exist()){
cout<<"初始化失败,顺序表已存在!\n";
break;
}
Se.init();
cout<<"初始化成功!\n";
break;
}
case 2:{
if (!Se.exist()){
cout<<"销毁失败,顺序表不存在\n";
break;
}
Se.destroy();
cout<<"销毁成功!\n";
break;
}
case 3:{
if (!Se.exist()){
cout<<"清空失败,顺序表不存在\n";
break;
}
if (Se.isempty()) ;
else Se.clear();
cout<<"清空成功!\n";
break;
}
case 4:{
if (!Se.exist()){
cout<<"判空失败,顺序表不存在\n";
break;
}
if (Se.isempty()) cout<<"顺序表为空!\n";
else cout<<"顺序表非空!\n";
break;
}
case 5:{
if (!Se.exist()){
cout<<"请先创建顺序表!\n";
break;
}
cout<<"顺序表的长度是"<<Se.getlength()<<endl;
break;
}
case 6:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
if (Se.isempty()){
cout<<"顺序表为空,请先插入元素!\n";
break;
}
cout<<"请输入你要获取元素的位置:";
int pos, e;
cin>>pos;
if (!Se.getelem(pos, e)) {
cout<<"不存在该位置的元素\n";
break;
}
cout<<"第"<<pos<<"位元素是 "<<e<<endl;
break;
}
case 7:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
if (Se.isempty()){
cout<<"顺序表为空,请先插入元素!\n";
break;
}
cout<<"请输入您要获取位置的元素:";
int e;
cin>>e;
if (!Se.elemlocate(e)){
cout<<"顺序表中不存在该元素!\n";
break;
}
cout<<"元素"<<e<<"的位置是"<<Se.elemlocate(e)<<endl;
break;
}
case 8:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
if (Se.isempty()){
cout<<"顺序表为空,请先插入元素!\n";
break;
}
cout<<"请输入您要获取前驱的元素:";
int pos, e;
cin>>e;
if (!Se.priorelem(e, pos)){
cout<<"获取失败,该元素不存在或无前驱!";
break;
}
cout<<"元素"<<e<<"的前驱元素是"<<Se[pos-1]<<endl;
break;
}
case 9:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
if (Se.isempty()){
cout<<"顺序表为空,请先插入元素!\n";
break;
}
cout<<"请输入您要获取后继的元素:";
int pos, e;
cin>>e;
if (!Se.nextelem(e, pos)){
cout<<"获取失败,该元素不存在或无后继!";
break;
}
cout<<"元素"<<e<<"的后继元素是"<<Se[pos-1]<<endl;
break;
}
case 10:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
cout<<"请输入指定位置:";
int pos;
cin>>pos;
cout<<"请输入想要插入的值:";
int e;
cin>>e;
if (!Se.insert(e, pos)){
cout<<"插入失败,请重新尝试!\n";
break;
}
else cout<<"插入完毕!\n";
break;
}
case 11:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
if (Se.isempty()){
cout<<"顺序表为空!\n";
break;
}
cout<<"请输入你要删除的元素的位置:";
int pos;
cin>>pos;
int e;
if (!Se.remove(e, pos)){
cout<<"删除失败,请重新尝试!\n";
break;
}
else cout<<"删除完毕!"<<"被删除的元素是"<<e<<endl;;
break;
}
case 12:{
if (!Se.exist()){
cout<<"顺序表不存在,请先创建顺序表!\n";
break;
}
if (Se.isempty()){
cout<<"顺序表为空!\n";
break;
}
cout<<"顺序表的所有元素是:";
Se.traverse();
cout<<endl;
break;
}
case 13:{
int a[5] = {17, 21, 39, 40, 56};
int b[8] = {10, 20, 30, 40, 50, 60, 70, 80};
Sequence<int> Sa(a, 5);
Sequence<int> Sb(b, 8);
cout<<"顺序表Sa的所有元素是:";
Sa.traverse();
cout<<endl;
cout<<"顺序表Sb的所有元素是:";
Sb.traverse();
cout<<endl;
Sequence<int> Sc;
Sc.init();
Sc.merge(Sa, Sb);
cout<<"\n合并Sa和Sb为Sc";
cout<<"顺序表Sa的所有元素是:";
Sc.traverse();
cout<<endl;
break;
}
}
if(myOption >13) cout<<"\n请输入小于13的正整数\n\n";
if(myOption < 0){
cout<<"\n退出程序!\n";
break;
}
}
return 0;
}
实验结果: