数据结构(面向对象方法与C++语言描述)(第2版)线性表内容整理
顺序表
顺序表是基于数组的线性表的存储表示,其特点是用物理位置上的邻接关系来表示结点间的逻辑关系。
优点:
(1)无需为表示结点间的逻辑关系而增加额外的存储空间,存储利用率高;
(2)可以方便地随机存取表中的任一结点,存储速度快。
缺点:
(1)在表中插入新元素或删除无用元素时,为了保持其他元素的相对次序不变,平均需要移动一半元素,运行效率很低;
(2)由于顺序表要求占用连续的空间,如果预先进行存储分配(静态分配),则当表长度变化较大时,难以确定合适的存储空间大小,若按可能达到的最大长度预先分配表的空间,则容易造成一部分空间长期闲置而得不到充分利用。若实现对表长估计不足,则插入操作可能使表长超过预先分配的空间而造成溢出。如果采用指针方式定义数组,在程序运行时动态分配存储空间,一旦需要,可以用另外一个新的更大的数组来代替原来的数组,这样虽然能够扩充数组空间,但时间开销比较大。
代码实现
环境:vs2019
头文件:LinearList.h,SeqList.h
源文件:main.cpp
LinearList.h代码:
#pragma once
//线性表的抽象基类
template<typename T>
class LinearList {
public:
LinearList() {}; //构造函数
~LinearList() {}; //析构函数
virtual int Size()const = 0; //求表最大容量
virtual int Length()const = 0; //求表长度
virtual int Search(T& x)const = 0; //在表中搜索给定值x
virtual int Locate(int i)const = 0; //在表中定位第i个元素位置
virtual bool getData(int i, T& x)const = 0; //取第i个表项的值
virtual void setData(int i, T& x) = 0; //修改第i个表项的值为x
virtual bool Insert(int i, T& x) = 0; //在第i个表项插入x
virtual bool Remove(int i, T& x) = 0; //删除第i个表项,通过x返回
virtual bool IsEmpty()const = 0; //判表空
virtual bool IsFull()const = 0; //判表满
virtual void Sort() = 0; //排序
virtual void input() = 0; //输入
virtual void output() = 0; //输出
//virtual LinearList<T> operator=(LinearList<T>& L) = 0; //复制
};
SeqList.h代码:
#pragma once
#include <iostream>
#include "LinearList.h"
const int defaultSize = 100;
template<typename T>
class SeqList : public LinearList<T> {
protected:
T* data; //存放数组
int maxSize; //最大可容纳表项的项数
int last; //当前已存表项的最后位置(从0开始)
void reSize(int newSize); //改变data数组空间的大小
public:
SeqList(int sz = defaultSize); //构造函数
SeqList(SeqList<T>& L); //拷贝构造函数
~SeqList() { delete[] data; } //析构函数
int Size()const { return maxSize; } //计算表最大可容纳表项个数
int Length()const { return last + 1; } //计算表长度
int Search(T& x)const; //搜索x在表中位置,函数返回表项序号
int Locate(int i)const; //定位第i个表项,函数返回表项序号
bool getData(int i, T& x)const //取第i个表项的值
{
if (i > 0 && i <= last + 1) { x = data[i - 1]; return true; }
else return false;
}
void setData(int i, T& x) //修改第i个表项的值为x
{
if (i > 0 && i <= last + 1) data[i - 1] = x;
}
bool Insert(int i, T& x); //在第i个表项插入x
bool Remove(int i, T& x); //删除第i个表项,通过x返回
bool IsEmpty()const //判表空
{
return (last == -1) ? true : false;
}
bool IsFull()const //判表满
{
return (last == maxSize - 1) ? true : false;
}
void Sort() //排序
{
}
void input(); //输入
void output(); //输出
SeqList<T> operator=(SeqList<T>& L); //表整体赋值
};
template<typename T>
SeqList<T>::SeqList(int sz)
{
//构造函数,通过指定参数sz定义数组的长度。
if (sz > 0)
{
maxSize = sz;
last = -1; //置表的实际长度为空
data = new T[maxSize]; //创建顺序表存储数组
if (data == nullptr) //动态分配失败
{
std::cerr << "存储分配错误!" << std::endl;
exit(1);
}
}
else
{
maxSize = 0;
last = -1;
data = nullptr;
std::cerr << "无效的数组大小!" << std::endl;
exit(1);
}
}
template<typename T>
SeqList<T>::SeqList(SeqList<T>& L)
{
//拷贝构造函数,用参数表中给出的已有顺序表初始化新建的顺序表。
maxSize = L.Size();
last = L.Length() - 1;
T value;
data = new T[maxSize]; //创建顺序表存储数组
if (data == nullptr) //动态分配失败
{
std::cerr << "存储分配错误!" << std::endl;
exit(1);
}
for (int i = 1; i <= last + 1; i++)
{
L.getData(i, value);
data[i - 1] = value;
}
}
template<typename T>
void SeqList<T>::reSize(int newSize)
{
//私有函数:扩充顺序表的存储数组空间大小,新数组的元素个数为newSize。
if (newSize <= 0) //检查参数的合理性
{
std::cerr << "无效的数组大小" << std::endl;
return;
}
if (newSize != maxSize) //修改
{
T* newarray = new T[newSize]; //建立新数组
if (newarray == nullptr)
{
std::cerr << "存储分配错误" << std::endl;
exit(1);
}
int n = last + 1;
T* srcptr = data; //源数组首地址
T* destptr = newarray; //目的数组首地址
while (n--) *destptr++ = *srcptr++; //复制
delete[] data; //删除老数组
data = newarray; //复制新数组
maxSize = newSize;
}
}
template<typename T>
int SeqList<T>::Search(T& x)const
{
//搜索函数:在表中顺序搜索与给定值x匹配的表项,找到则函数返回该表项是第几个元素,
//否则函数返回0,表示搜索失败
for (int i = 0; i <= last; i++)
{
if (data[i] == x) return i + 1; //顺序搜索
}
return 0; //搜索失败
}
template<typename T>
int SeqList<T>::Locate(int i)const
{
//定位函数:函数返回第i(1<=i<=last+1)个表项的位置,否则函数返回0,表示定位失败。
if (i >= 1 && i <= last + 1) return i;
else return 0;
}
template<typename T>
bool SeqList<T>::Insert(int i, T& x)
{
//将新元素x插入到表中第i(1<=i<=last+1)个表项之后。函数返回插入成功的信息,若插入
//成功,则返回true;否则返回false。i=0是虚拟的,实际上是插入到第1个元素位置。
if (last == maxSize - 1) return false; //表满,不能插入
if (i < 0 || i > last + 1) return false; //参数i不合理,不能插入
for (int j = last; j >= i; j--)
data[j + 1] = data[j]; //一次后移,空出第i号位置
data[i] = x; //插入
last++; //最后位置加1
return true; //插入成功
}
template<typename T>
bool SeqList<T>::Remove(int i, T& x)
{
//从表中删除第i(1<=i<=last+1)个表项,通过引用型参数x返回删除的元素值。函数返回删除
//成功的信息,若删除成功则返回true,否则返回false。
if (last == -1) return false; //表空,不能删除
if (i < 0 || i > last + 1) return false; //参数i不合理,不能删除
x = data[i - 1]; //存被删元素的值
for (int j = i; j <= last; j++)
data[j - 1] = data[j]; //依次迁移,填补
last--; //最后位置减1
return true; //删除成功
}
template<typename T>
void SeqList<T>::input()
{
//从标准输入(键盘)逐个数据输入,建立顺序表
std::cout << "开始建立顺序表,请输入表中元素个数:";
while (1)
{
std::cin >> last; //输入元素最后位置
last--;
if (last >= 0 && last <= maxSize - 1) break;
std::cout << "表元素个数输入有误,范围不能超过" << "1-" << maxSize << ":";
}
for (int i = 0; i <= last; i++) //逐个输入表元素
{
std::cin >> data[i];
std::cout << i + 1 << std::endl;
}
}
template<typename T>
void SeqList<T>::output()
{
//将顺序表全部元素输出到屏幕上。
std::cout << "顺序表当前元素最后位置为:" << last << std::endl;
for (int i = 0; i <= last; i++)
{
std::cout << "#" << i + 1 << ":" << data[i] << std::endl;
}
}
template<typename T>
SeqList<T> SeqList<T>::operator= (SeqList<T>& L)
{
//重载操作:顺序表整体赋值。若当前调用此操作的表对象为L1,代换形参L的表对象为L2,
//则使用方式为L1=L2。实现代码可参照拷贝构造函数自行编写。
if (maxSize != L.Size())
{
if(data != nullptr) delete[] data;
maxSize = L.Size();
data = new T[maxSize]; //创建顺序表存储数组
}
last = L.Length() - 1;
T value;
if (data == nullptr) //动态分配失败
{
std::cerr << "存储分配错误!" << std::endl;
exit(1);
}
for (int i = 1; i <= last + 1; i++)
{
L.getData(i, value);
data[i - 1] = value;
}
return *this;
}
main.cpp代码:(仅测试输入输出)
#include "SeqList.h"
int main()
{
SeqList<int> L1;
L1.input();
L1.output();
SeqList<int> L2;
L2 = L1;
L2.output();
return 0;
}
控制台界面: