一、模板类
参考自《数据结构(用面向对象方法与c++语言描述)第2版》,殷人昆主编,有改动。
1.线性表
//线性表模板
template<class 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;//在表中定位元素x位置
virtual T getData(int i)const = 0;//取第i个表项的值
virtual void setData(int i, T& x) = 0;//修改第i个表项的值
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;//输出
};
2.顺序表
template<class T>
class SeqList :public LinearList<T>//使用整型的线性表,并以递增正数序列作为线性表的顺序
{
protected:
T *A;//数组首地址
int arrsize;//数组的最大容量
int elenum;//最后元素的下标
void reSize(int newSize);//改变顺序表大小
public:
SeqList(int size = maxSize);//构造函数
SeqList(SeqList<T>&L);
~SeqList();//析构函数
int Size()const;//最大容量
bool getData(int i,T& x)const;//返回指定位置的数值
int Length()const { return elenum; }//返回elenum
bool IsFull()const override { return elenum == arrsize; }
bool IsEmpty()const override { return elenum == -1; }
int Search(T& x)const;//搜索x在表中位置
int Locate(int i)const;//搜索第i个表项,返回表项序号
void setData(int i, T& x);//设置第i项值
bool Remove(int i, T& x);//删除,删除的元素通过x返回
bool Insert(int i, T& x);//插入x到第i项
void Sort();
void input();
void output();
};
3.单链表
template<class T>
struct LinkNode//结点
{
T data;
LinkNode<T> *link;
LinkNode(LinkNode<T> *ptr = NULL) { link = ptr; }
LinkNode(const T& item, LinkNode<T> *ptr = NULL) { data = item;link = ptr; }
};
template<class T>
class List
{
public:
List() { first = new LinkNode<T>; }
List(const T& x) { first = new LinkNode<T>(x); }
List(List<T>& L);
~List() { makeEmpty(); }
void makeEmpty();//置空
LinkNode<T> *Locate(int i)const;//第i个元素地址
bool getData(int i, T& x)const;//获取第i个元素,通过x返回
int Length()const;//链表长度
LinkNode<T> *getHead()const { return first; }
LinkNode<T> *Search(T x);//搜索含x的元素
void setData(int i, T& x);//设置第i个结点为x
bool Insert(int i, T& x);//在第i个结点后插入x
bool Remove(int i, T& x);//移除第i个结点,通过x返回
bool IsEmpty()const { return first->link == NULL ? true : false; }
void Sort();//排序
void input();//输入
void output();//输出
protected:
LinkNode<T> *first;
};
有两种思路进行逆置,分别是破坏性的逆置和构造性的逆置。
破坏性的逆置:对原有的储存结点进行逆置,调用方法后原线性表的结点顺序反转。
构造性的逆置:构造一个新的空间用于储存逆置顺序的线性表,不会影响原线性表的结点顺序。
二、顺序表逆置
1.破坏性逆置
代码如下:
template<class T>
void SeqList<T>::reverse()
{
if (IsEmpty())
return;
for (int i = 1;i <= elenum / 2;i++)//检索半个链表
{
T temp = A[i];
A[i] = A[elenum + 1 - i];
A[elenum + 1 - i] = temp;
}
}
分析:通过对称性,将第elenum(顺序表尾部元素下标)+1-i的元素与第i个元素的值互换。
2.构造性逆置
代码如下:
T* SeqList<T>::reverse()const
{
if (IsEmpty())
NULL;
T* newList=new T[Length()];
for (int i = 1;i <= elenum;i++)//注意顺序表中元素下标从1开始,而新数组下标从0开始
{
newList[i-1] = A[elenum + 1 - i];//赋值给新数组i-1位
}
return newList;
}
分析:思路和上面的相同,返回逆置数组头。
三.单链表逆置
说明:单链表采用附加头结点形式。
1.破坏性逆置
代码如下:
void List<T>::reverse()
{
if (IsEmpty())
return;
LinkNode<T>* tail = Locate(Length());//保存当前尾结点
while (Length() > 1)
{
LinkNode<T>* p = Locate(Length());//链表当前尾结点
Locate(Length() - 1)->link = NULL;
p->link = Locate(Length());//反指后,由于断开了一个结点,原来的length-1变为现在的length
}
first->link = tail;
}
分析:tail:指当前链表尾结点。Locate(Length())固定返回当前尾结点,由于该链表是附加头结点,所以循环条件设置为长度大于1。在循环中,使当前尾结点反指向上一个结点,最终得到的tail是逆置后链表的第一个结点。
2.构造性逆置
说明:返回无附加头结点的单链表
LinkNode<T>* reverse()const
{
if (IsEmpty())
return NULL;
LinkNode<T>* newHead = new LinkNode<T>(Locate(Length())->data);
LinkNode<T>* newTail = newHead;
for (int i = Length() - 1;i >= 1;i--)
{
LinkNode<T>* newNode = new LinkNode<T>(Locate(i)->data);
newTail->link = newNode;
newNode->link = NULL;
newTail = newTail->link;//更新尾部定位
}
return newHead;
}
分析:创建newHead作为新链表头部,newTail始终指向新链表尾部,利用Locate倒序遍历链表,获取结点插入到newTail后面,最后返回逆置链表头newHead。