//栈
//定义和应用
//定义8-1 栈(stack) 是一种特殊的线性表,其插入(也称入栈或压栈)和删除(也称出栈或弹栈)操作都在表的同一端进行。这一端称为栈顶(top),另一端称为栈底(bottom),图给出了一个4个元素的栈。假定要在的栈中插入一个元素E,这个元素删除E之后的结果是图如果对栈连续执行三次删除操作,结果从上面的讨论可以看出,栈是一个后进先出表。这种类型的表在计算过程中将频繁使用。
E<-top
D<-top D
C C
B B B<-top
A<-bottom A<-bottom A<-bottom
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
//数据结构算法与应用-C++语言描述 stack数组的实现
using namespace std;
//线性表一数组
//抽象数据类型
template<class T>
class linearList
{
public:
virtual ~linearList() {}
//返回true,当且仅当线性表为空
virtual bool empty() const = 0;
//返回线性表的元素个数
virtual int size() const = 0;
//返回索引为 theIndex 的元素
virtual T &get(int theIndex) const = 0;
//返回元素 theElement 第一次出现时的索引
virtual int indexOf(const T &theElement) const = 0;
//删除索引为 theIndex 的元素
virtual void erase(int theIndex) = 0;
//把 theElement 插入线性表中索引为 theIndex 的位置上
virtual void insert(int theIndex, const T &theElement) = 0;
//把线性表插入输出流 out
virtual void output(ostream &out) const = 0;
};
//改变一个一维数组长度
template<class T>
void changeLength1D(T *&a, int oldLength, int newLength)
{
if (newLength < 0)
throw logic_error("new length must be >= 0");
//新数组
T *temp = new T[newLength];
//需要复制的元素个数
int number = min(oldLength, newLength);
copy(a, a + number, temp);
//释放老数组的内存空间
delete [] a;
a = temp;
}
//arrayList 的类定义
template<class T>
class arrayList : public linearList<T>
{
public:
//构造函数,复制构造函数和析构函数
arrayList(int initialCapacity = 10);
arrayList(const arrayList<T> &);
~arrayList() {delete[] element;}
//ADT 方法
bool empty() const override { return listSize == 0; }
int size() const override { return listSize; }
T &get(int theIndex) const override;
int indexOf(const T &theElement) const override;
void erase(int theIndex) override;
void insert(int theIndex, const T &theElement) override;
void output(ostream &out) const override;
//其他方法
int capacity() const { return arrayLength; }
//ex5. 编写一个方法 arrayList<T>::trimToSize,它使数组的长度等于 max{listSize,1}。
//这个方法的复杂度是多少?” 测试你的代码。
arrayList<T>& trimToSize();
//ex6. 编写方法 arrayList<T>::setSize,它使线性表的大小等于指定的大小。若线性表开始的大小
//小于指定的大小,则不增加元素。若线性表开始的大小大于指定的大小,则删除多余的元
//素。这个方法的复杂度是多少? 测试你的代码。
arrayList<T>& setSize(int length);
//ex7. 重载操作符[],使得表达式 x加返回对线性表第i个元素的引用。
//若线性表没有第i个元则抛出异常语句 x[i]=y 和 y= x[i]] 按以往预期的方式执行。
//测试你的代码。
T& operator[](int i);
//ex8. 重载操作符 ==,使得表达式 x==y 返回 true,当上且仅当两个用数组描述的线性表x
//和y 相等(即对所有的 i,两个线性表的第i 个元素相等 )。测试你的代码。
bool operator==(const arrayList<T>& right) const;
//ex9.
bool operator!=(const arrayList<T>& right) const;
//ex10. 重载操作符 <,使得表达式 x<y 返回 true,当且仅当用数组描述的线性表 x 按字典顺序小
//于用数组描述的线性表v ( 见练习 8 )。测试你的代码。
bool operator<(const arrayList<T>& right) const;
//ex11. 编写方法 arrayList<T>::push_back,它把元素 theElement 插到线性表的右端。不要利用
//insert 方法。方法的时间复杂度是多少?测试你的代码。
void push_back(const T& theElement);
//ex12. 编写方法 arrayList<T>::pop_back,它把线性表右端的元素删除。不要利用 erase 方法。方
//法的时间复杂度是多少? 测试你的代码。
T& pop_back();
//ex13. 编写方法 arrayList<T>::swap(theLisb,它交换线性表的元素 *this 和 theList。方法的时间
//复杂度是多少? 测试你的代码。
arrayList<T>& swap(arrayList<T>& theList);
//ex14. 编写方法 arrayList<T>::reserve(theCapacity),它把数组的容量改变为当前容量和 theCapacity
//的较大者。测试你的代码。
arrayList<T>& reserve(int theCapacity);
//ex15. 编写方法 arrayList<T>::set(theIndex,theElement),它用元素 theElement 替换索引为 theIndex
//的元素。若索引 theIndex 超出范围,则抛出异常。返回原来索引为 theIndex 的元素。测试
//你的代码。
T set(int theIndex, const T& theElement);
//ex16. 编写方法 arrayList<T>::clear,它使线性表为空。方法的复杂度是多少?” 测试你的代码。
arrayList<T>& clear();
//ex17. 编写方法 arrayList<T>::removeRange,它删除指定索引范围内的所有元素。方法的复杂度
//是多少?测试你的代码。
arrayList<T>& removeRange(int startIndex,int endIndex);
//ex18. 编写方法 arrayList<T>::lastInexOf,它的返回值是指定元素最后出现时的索引。如果这样
//的元素不存在,则返回 -1。方法的复杂度是多少? 测试你的代码。
int lastIndexOf(const T& theElement) const;
//ex20. 类 arrayList ( 程序 5-1 ) 的缺点是,它不减少数组 element 的长度。
//1: 编写类 arrayList 的一个新版本。如果在删除之后,线性表的大小降至 arrayLength/4 以
// 下,就创建一个新的数组,长度为 max{arrayLength/2,initialCapacity}。然后将老表
// 中的元素复制到新表。
//2 ( 选择性练习 ) 从空表开始,考察大小为地的线性表的操作序列。假设当初始容量等于
// 或超过线性表大小的最大值时,总的执行步数是f(n)。证明,如果起始容量为1,而且
// 在插入和删除操作中,可以按照上面和 5.3 节所述的方式改变数组长度,那么执行步
// 数最多为 cf(n),其中 c 是某个常数。
arrayList<T>& decreaseArray(int capacity);
///ex33. 1 ) 编写方法 arrayList<T>::reverse,它原地颠倒线性表元素的顺序 ( 即在数组 element 中
// 完成操作,不创建新的数组 )。颠倒顺序之前,线性表的第 k 个元素是 element[k],站
// 倒之后,线性表的第 k 个元素是 element[listSize-k-1]。不要利用 STL 函数 reverse。
// 2 ) 方法应具有 listSize 的线性复杂度。证明这个性能。
// 3 ) 设计测试数据,测试方法的正确性。
// 4 ) 编写另一个原地颠倒 arrayList 对象的方法。它不是 arrayList 的成员函数,不能访问
// arrayList 的数据成员。不过,这个方法可以调用 arrayList 的成员函数。
// 5 ) 计算方法的复杂度
// 6 ) 使用大小分别为 1000、5000 和 10 000 的线性表,比较两个颠倒顺序算法的运行时间
arrayList<T>& reverse();
//ex23.1) 编写方法 arrayList<T>::leftShift(i),它将线性表的元素向左移动i个位置。如果
// x=[0,1,2,3,4],那么 x.leftShift(2) 的结果是 x=[2,3,4]。
// 2 ) 计算方法的复杂度。
// 3 ) 测试你的代码-
arrayList<T>& leftShift(int shift);
//ex24. 在一个循环移动的操作中,线性表的元素根据给定的值,按顺时针方向移动。例如,
//x=[0,1,2,3,4],循环移动 2 的结果是 x=[2,3,4,0,1]。
// 1 ) 描述一下如何利用 3 次逆转操作完成循环移动。每一次逆转操作都可以将线性表的一
// 部分或全部逆转。
// 2 ) 编写方法 arrayList<T>::circularShiftt),它将线性表的元素循环移动i个位置。方法应
// 具有线性表长度的线性复杂度。
// 3 ) 测试你的代码。
arrayList<T>& circularShift(int shift);
//ex25. 调用语句 x.half,可以将 x 的元素隔一个删除一个。如果 xsize() 是 7, xelement[]=[2,13,4.5,17,8,29],
// 那么 x.half() 的结果是 x.size() 是 4,x.element[]=[2,4.17,9]。如果 x.size() 是 4,x.element[]=[2,13,4.5],
// 那么 x.half() 的结果是 x.size() 是 2,x.element[]=[2,4]。如果 x 为空,那么 x.half() 的结果也是X 为空。
// 1 ) 编写方法 arrayList<T>::half()。不能利用类 arrayList 的其他方法。复杂度应该为O(listSize)
// 2 ) 证明方法的复杂度为 O(listSize)。
// 3 ) 测试你的代码。
arrayList<T>& half();
///ex34.令 a 和b 是类 arrayList 的两个对象。
// 1 ) 编写方法 arrayList<T>::meld(ab),它生成一个新的线性表,从 a 的第 0 个元素开始,
// 交替地包含a 和 b的元素。如果一个表的元素取完了,就把另一个表的剩余元素附加
// 到新表中。调用语句 c.meld(a,b) 使 成为合并后的表。方法应具有两个输入线性表大
// 小的线性复杂度。
// 2 ) 证明方法具有 a 和b 大小之和的线性复杂度。
// 3 ) 测试你的代码。
arrayList<T>& meld(arrayList<T>& a, arrayList<T>& b);
///ex35.令a 和b 是类 arrayList 的两个对象。假设它们的元素从左到右非递减有序。
// 1 ) 编写方法 arrayList<T>::merge(a,b),它生成一个新的有序线性表,包含a 和 的所有元
// 素。归并后的线性表是调用对象 *this。不要利用 STL 本数 merge。
// 2 ) 计算方法的复杂度。
// 3 ) 测试你的代码。
arrayList<T>& merge(arrayList<T>& a, arrayList<T>& b);
///ex36. 1 ) 编写方法 arrayList<T>::split(lab),它生成两个线性表a 和b。a 包含 *this 中索引为偶
// 数的元素,b 包含其余的元素。
// 2 ) 计算方法的复杂度。
// 3 ) 测试你的代码。
arrayList<T>& split(arrayList<T>& a, arrayList<T>& b);
//ex31. 假设用公式 ( 5-3 ) 来表示线性表。分别用变量 first 和 last 表示线性表首元素和尾元素的
// 位置。
// 1 ) 开发一个类 circularArrayList,它与 arrayList 类似。实现所有的方法。在删除和插人方
// 法中,对位于删除或插和人元素的左面或右面的元素,有选择地决定向左移动或向右移
// 动,以此提高方法的性能。
// 2 ) 计算每一个方法的复杂度。
// 3 ) 测试你的代码。
public:
class iterator
{
public:
// C++ 的typedef 语句实现双向迁代器
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
//构造函数
iterator(T* thePosition = 0){ position = thePosition; }
//解引用操作符
T& operator*() const{ return *position; }
T* operator->() const{ return &*position; }
//迭代器的值增加
iterator& operator++() //前加
{
++position;
return *this;
}
iterator operator++(int) //后加
{
iterator old = *this;
++position;
return old;
}
//迭代器的值减少
iterator& operator--() //前减
{
--position;
return *this;
}
iterator operator--(int) //后减
{
iterator old = *this;
--position;
return old;
}
//测试是否相等
bool operator!=(const iterator right) const
{
return position != right.position;
}
bool operator==(const iterator right) const
{
return position == right.position;
}
//27. 扩展迭代器类 arrayList::iterator ( 程序 5-11 ),使得它成为随机访问迭代器。利用 STL 的
// 排序函数对一个线性表排序,以测试这个和迭代器类。
T& operator[](int i)
{
return *(position + i);
}
ptrdiff_t operator-(const arrayList<T>::iterator & right) const
{
return position - right.position;
}
T& operator-(const ptrdiff_t & diff) const
{
return *(position - diff);
}
T& operator+(const ptrdiff_t & diff) const
{
return *(position + diff);
}
protected:
T* position; //指向表元素的指针
};
iterator begin(){ return iterator(element); }
iterator end(){ return iterator(element+listSize); }
protected:
//索引 theIndex 无效,则抛出异常
void checkIndex(int theIndex) const;
//扩容
void increaseArray(int capacity);
//存储线性表元素的一维数组
T* element = NULL;
//一维数组的容量
int arrayLength = 0;
//线性表的元素个数
int listSize = 0;
//初始容量
int initialCapacity = 10;
};
//类 arrayList 的构造函数
template<class T>
arrayList<T>::arrayList(int initialCapacity)
{
//构造函数
if(initialCapacity < 1)
{
ostringstream s;
s<<"Initial capacity = "<<initialCapacity<<" must be > 0";
throw logic_error(s.str());
}
arrayLength = initialCapacity;
initialCapacity = initialCapacity;
element = new T[arrayLength];
listSize = 0;
}
template<class T>
arrayList<T>::arrayList(const arrayList<T>& theList)
{
//复制构造函数
arrayLength = theList.arrayLength;
listSize = theList.listSize;
element = new T[arrayLength];
//利用了 STL 的算法copy
copy(theList.element,theList.element + listSize,element);
}
//方法 checkIndex的时间复杂度是 (1),
template<class T>
void arrayList<T>::checkIndex(int theIndex) const
{
//确定索引theIndex在0 和 listSize - 1之间
if(theIndex < 0 || theIndex >= listSize)
{
//字符串输出流对象
ostringstream s;
s<<"index = "<<theIndex<<" size = "<<listSize;
throw logic_error(s.str());
}
}
//方法 get的时间复杂度是 (1),
template<class T>
T& arrayList<T>::get(int theIndex) const
{
//返回索引为 theIndex 的元素
//若此元素不存在,则抛出异常
checkIndex(theIndex);
return element[theIndex];
}
//indexOf 的时间复杂度是 O(max{listSize,1})
template<class T>
int arrayList<T>::indexOf(const T& theElement) const
{
//返回元素 theElement 第一次出现时的索引
//该元素不存在,则返回 -1
//查找元素 theElement
int theIndex = (int)(find(element,element+listSize,theElement) - element);
//确定元素 theElement 是否找到
if(theIndex == listSize)
{
//没有找到
return -1;
}
else
{
return theIndex;
}
}
/*
删除一个元素为了从线性表中删除索引为 theIndex 的元素,
首先要确定线性表包含这个元素,然后删除这个元素。
若没有这个元素,则抛出类型为异常
如果没有索引 theIndex 的元素,
就抛出异常,erase 时间复杂度是 O(1)。如果有这个元素,
那么要移动的元素个数是 listSize-theIndex
时间复杂度是 O(listSize-theIndex)
*/
template<class T>
void arrayList<T>::erase(int theIndex)
{
//删除其索引为 theIndex 的元素
//如果该元素不存在,则抛出异常
checkIndex(theIndex);
//有效索引,移动其索引大于 theIndex 的元素
copy(element+theIndex+1,element+listSize,element+theIndex);
//调用析构函数
element[--listSize].~T();
}
/*
插入一个元素
要在线性表中索引为 theIndex 的位置上插和人一个新元素,
首先把索引从 theIndex 到listSize-1的元素向右移动一个位置
然后将新元素插人索引为 theIndex 的位置最后将变量listSize 的值增 1。
向右移动元素的操作是利用 STL 函数 copy_backward 来完成的
而没有利用 copy 函数该函数是从最右端的元素开始移动。
*/
template<class T>
void arrayList<T>::insert(int theIndex,const T& theElement)
{
//在索引 theIndex 处插入元素 theElement
//确定索引theIndex在0 和 listSize 之间
if(theIndex < 0 || theIndex > listSize)
{
//字符串输出流对象
ostringstream s;
s<<"index = "<<theIndex<<" size = "<<listSize;
throw logic_error(s.str());
}
//有效索引,确定数组是否已满
if(listSize == arrayLength)
{
//数组空间已满,数组长度倍增
changeLength1D(element,arrayLength,2*arrayLength);
arrayLength *= 2;
}
//把元素向右移动一个位置
copy_backward(element + theIndex,
element + listSize,
element + listSize + 1);
element[theIndex] = theElement;
listSize++;
}
//输出函数 output 和重载 <<
template<class T>
void arrayList<T>::output(ostream& out) const
{
//把线性表插入输出流
copy(element, element + listSize,ostream_iterator<T>(cout," "));
}
//重载 <<
template<class T>
ostream& operator<<(ostream& out,const arrayList<T>& x)
{
x.output(out);
return out;
}
//扩容
template<class T>
void arrayList<T>::increaseArray(int capacity)
{
if(capacity <= arrayLength)
{
cout<<"input capacity <= arrayLength";
return;
}
changeLength1D(element,arrayLength,capacity);
arrayLength = capacity;
}
//ex5. 编写一个方法 arrayList<T>::trimToSize,它使数组的长度等于 max{listSize,1}。
//这个方法的复杂度是多少?” 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::trimToSize()
{
if(listSize == arrayLength) return *this;
int oldLength = arrayLength;
arrayLength = max(listSize,1);
changeLength1D(element,oldLength,arrayLength);
return *this;
}
//ex6. 编写方法 arrayList<T>::setSize,它使线性表的大小等于指定的大小。若线性表开始的大小
//小于指定的大小,则不增加元素。若线性表开始的大小大于指定的大小,则删除多余的元
//素。这个方法的复杂度是多少? 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::setSize(int length)
{
if(length < 0 || length > listSize)
return *this;
listSize = length;
return *this;
}
//ex7. 重载操作符[],使得表达式 x加返回对线性表第i个元素的引用。
//若线性表没有第i个元则抛出异常语句 x[i]=y 和 y= x[i] 按以往预期的方式执行。
//测试你的代码。
template<class T>
T& arrayList<T>::operator[](int i)
{
checkIndex(i);
return element[i];
}
//ex8. 重载操作符 ==,使得表达式 x==y 返回 true,当上且仅当两个用数组描述的线性表x
//和y 相等(即对所有的 i,两个线性表的第i 个元素相等 )。测试你的代码。
template<class T>
bool arrayList<T>::operator==(const arrayList<T>& right) const
{
if(right.listSize != listSize) return false;
for(int i = 0;i < listSize;i++)
{
if(element[i] != right.element[i]) return false;
}
return true;
}
//ex9.
template<class T>
bool arrayList<T>::operator!=(const arrayList<T>& right) const
{
if(*this == right)
{
return false;
}
else
{
return true;
}
}
//ex10. 重载操作符 <,使得表达式 x<y 返回 true,当且仅当用数组描述的线性表 x 按字典顺序小
//于用数组描述的线性表v ( 见练习 8 )。测试你的代码。
template<class T>
bool arrayList<T>::operator<(const arrayList<T>& right) const
{
int minSize = min(size(),right.size());
for(int i = 0;i<size();i++)
{
if(element[i] < right.element[i]) return true;
}
if(size() < right.size()) return true;
return false;
}
//ex11. 编写方法 arrayList<T>::push_back,它把元素 theElement 插到线性表的右端。不要利用
//insert 方法。方法的时间复杂度是多少?测试你的代码。
template<class T>
void arrayList<T>::push_back(const T& theElement)
{
if(arrayLength == listSize)
increaseArray(2*arrayLength);
element[listSize++] = theElement;
}
//ex12. 编写方法 arrayList<T>::pop_back,它把线性表右端的元素删除。不要利用 erase 方法。方
//法的时间复杂度是多少? 测试你的代码。
template<class T>
T& arrayList<T>::pop_back()
{
if(listSize == 0)
{
throw logic_error("arrayList is empty");
}
T& theElement = element[--listSize];
if(listSize < arrayLength/4)
decreaseArray(arrayLength/2);
return theElement;
}
//ex13. 编写方法 arrayList<T>::swap(theLisb,它交换线性表的元素 *this 和 theList。方法的时间
//复杂度是多少? 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::swap(arrayList<T>& theList)
{
std::swap(listSize, theList.listSize);
std::swap(arrayLength,theList.arrayLength);
std::swap(element,theList.element);
return *this;
}
//ex14. 编写方法 arrayList<T>::reserve(theCapacity),它把数组的容量改变为当前容量和 theCapacity
//的较大者。测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::reserve(int theCapacity)
{
if(theCapacity <= arrayLength) return *this;
changeLength1D(element,arrayLength,theCapacity);
arrayLength = theCapacity;
return *this;
}
//ex15. 编写方法 arrayList<T>::set(theIndex,theElement),它用元素 theElement 替换索引为 theIndex
//的元素。若索引 theIndex 超出范围,则抛出异常。返回原来索引为 theIndex 的元素。测试
//你的代码。
template<class T>
T arrayList<T>::set(int theIndex, const T& theElement)
{
checkIndex(theIndex);
T tmp = element[theIndex];
element[theIndex] = theElement;
return tmp;
}
//ex16. 编写方法 arrayList<T>::clear,它使线性表为空。方法的复杂度是多少?” 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::clear()
{
for(int i = 0; i < size(); i++)
{
element[i].~T();
}
listSize = 0;
decreaseArray(initialCapacity);
arrayLength = initialCapacity;
return *this;
}
//ex17. 编写方法 arrayList<T>::removeRange,它删除指定索引范围内的所有元素。方法的复杂度
//是多少?测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::removeRange(int startIndex,int endIndex)
{
checkIndex(startIndex);
checkIndex(endIndex);
int count = endIndex - startIndex + 1;
copy(element+endIndex, element+listSize, element+startIndex);
for(int i = startIndex+count;i<listSize;i++)
element[i].~T();
listSize -= count;
if(listSize < arrayLength/4)
{
decreaseArray(arrayLength/2);
}
return *this;
}
//ex18. 编写方法 arrayList<T>::lastInexOf,它的返回值是指定元素最后出现时的索引。如果这样
//的元素不存在,则返回 -1。方法的复杂度是多少? 测试你的代码。
template<class T>
int arrayList<T>::lastIndexOf(const T& theElement) const
{
for(int i = listSize - 1;i>=0;i--)
{
if(element[i] == theElement)
return i;
}
return -1;
}
//ex20. 类 arrayList ( 程序 5-1 ) 的缺点是,它不减少数组 element 的长度。
//1: 编写类 arrayList 的一个新版本。如果在删除之后,线性表的大小降至 arrayLength/4 以
// 下,就创建一个新的数组,长度为 max{arrayLength/2,initialCapacity}。然后将老表
// 中的元素复制到新表。
//2 ( 选择性练习 ) 从空表开始,考察大小为地的线性表的操作序列。假设当初始容量等于
// 或超过线性表大小的最大值时,总的执行步数是f(n)。证明,如果起始容量为1,而且
// 在插入和删除操作中,可以按照上面和 5.3 节所述的方式改变数组长度,那么执行步
// 数最多为 cf(n),其中 c 是某个常数。
template<class T>
arrayList<T>& arrayList<T>::decreaseArray(int capacity)
{
if(capacity >= arrayLength || capacity < 0)
{
return *this;
}
changeLength1D(element, arrayLength, capacity);
arrayLength = capacity;
return *this;
}
///ex33. 1 ) 编写方法 arrayList<T>::reverse,它原地颠倒线性表元素的顺序 ( 即在数组 element 中
// 完成操作,不创建新的数组 )。颠倒顺序之前,线性表的第 k 个元素是 element[k],站
// 倒之后,线性表的第 k 个元素是 element[listSize-k-1]。不要利用 STL 函数 reverse。
// 2 ) 方法应具有 listSize 的线性复杂度。证明这个性能。
// 3 ) 设计测试数据,测试方法的正确性。
// 4 ) 编写另一个原地颠倒 arrayList 对象的方法。它不是 arrayList 的成员函数,不能访问
// arrayList 的数据成员。不过,这个方法可以调用 arrayList 的成员函数。
// 5 ) 计算方法的复杂度
// 6 ) 使用大小分别为 1000、5000 和 10 000 的线性表,比较两个颠倒顺序算法的运行时间
template<class T>
arrayList<T>& arrayList<T>::reverse()
{
for(int i = 0;i < listSize / 2; i++)
{
std::swap(element[i],element[listSize - i - 1]);
}
return *this;
}
//ex23.1) 编写方法 arrayList<T>::leftShift(i),它将线性表的元素向左移动i个位置。如果
// x=[0,1,2,3,4],那么 x.leftShift(2) 的结果是 x=[2,3,4]。
// 2 ) 计算方法的复杂度。
// 3 ) 测试你的代码-
template<class T>
arrayList<T>& arrayList<T>::leftShift(int shift)
{
if(shift < 0 || shift > listSize)
{
ostringstream s;
s << "shift shift < 0 || shift > listSize";
throw logic_error(s.str());
}
copy(element + shift,element + listSize, element);
listSize -= shift;
for (int i = listSize - shift; i < listSize; i++)
{
element[i].~T();
}
if (listSize < arrayLength / 4)
{
changeLength1D(element, arrayLength, arrayLength / 2);
}
return *this;
}
//ex24. 在一个循环移动的操作中,线性表的元素根据给定的值,按顺时针方向移动。例如,
//x=[0,1,2,3,4],循环移动 2 的结果是 x=[2,3,4,0,1]。
// 1 ) 描述一下如何利用 3 次逆转操作完成循环移动。每一次逆转操作都可以将线性表的一
// 部分或全部逆转。
// 2 ) 编写方法 arrayList<T>::circularShiftt),它将线性表的元素循环移动i个位置。方法应
// 具有线性表长度的线性复杂度。
// 3 ) 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::circularShift(int shift)
{
if(shift < 0)
{
ostringstream s;
s <<"circularShift shift < 0";
throw logic_error(s.str());
}
shift %= listSize;
reverse();
std::reverse(element, element + listSize - shift);
std::reverse(element + listSize - shift, element + listSize);
return *this;
}
//ex25. 调用语句 x.half,可以将 x 的元素隔一个删除一个。如果 xsize() 是 7, xelement[]=[2,13,4.5,17,8,29],
// 那么 x.half() 的结果是 x.size() 是 4,x.element[]=[2,4.17,9]。如果 x.size() 是 4,x.element[]=[2,13,4.5],
// 那么 x.half() 的结果是 x.size() 是 2,x.element[]=[2,4]。如果 x 为空,那么 x.half() 的结果也是X 为空。
// 1 ) 编写方法 arrayList<T>::half()。不能利用类 arrayList 的其他方法。复杂度应该为O(listSize)
// 2 ) 证明方法的复杂度为 O(listSize)。
// 3 ) 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::half()
{
if(listSize <= 1) return *this;
for(int i = 1;i < (listSize + 1) / 2; i++)
{
element[i] = element[i*2];
}
int oldSize = listSize;
listSize = (listSize + 1) / 2;
for(int i = listSize; i < oldSize; i++)
{
element[i].~T();
}
if(listSize < arrayLength / 4)
{
changeLength1D(element, arrayLength, arrayLength / 2);
}
return *this;
}
///ex34.令 a 和b 是类 arrayList 的两个对象。
// 1 ) 编写方法 arrayList<T>::meld(ab),它生成一个新的线性表,从 a 的第 0 个元素开始,
// 交替地包含a 和 b的元素。如果一个表的元素取完了,就把另一个表的剩余元素附加
// 到新表中。调用语句 c.meld(a,b) 使 成为合并后的表。方法应具有两个输入线性表大
// 小的线性复杂度。
// 2 ) 证明方法具有 a 和b 大小之和的线性复杂度。
// 3 ) 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::meld(arrayList<T>& a, arrayList<T>& b)
{
if(a.listSize == 0 && b.listSize == 0) return *this;
int minSize = a.listSize < b.listSize ? a.listSize:b.listSize;
int maxSize = a.listSize > b.listSize ? a.listSize:b.listSize;
int newLength = a.listSize > b.listSize ? a.arrayLength : b.arrayLength;
T* maxElement = a.listSize > b.listSize ? a.element:b.element;
if(maxSize + minSize > arrayLength)
{
newLength = a.arrayLength + b.arrayLength;
}
increaseArray(newLength);
listSize = maxSize + minSize;
for(int i = 0;i<minSize;i++)
{
element[i*2] = a.element[i];
element[i*2 + 1] = b.element[i];
}
copy(maxElement + minSize, maxElement + maxSize,element+2*minSize);
return *this;
}
///ex35.令a 和b 是类 arrayList 的两个对象。假设它们的元素从左到右非递减有序。
// 1 ) 编写方法 arrayList<T>::merge(a,b),它生成一个新的有序线性表,包含a 和 的所有元
// 素。归并后的线性表是调用对象 *this。不要利用 STL 本数 merge。
// 2 ) 计算方法的复杂度。
// 3 ) 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::merge(arrayList<T>& a, arrayList<T>& b)
{
if(a.listSize == 0 && b.listSize == 0) return *this;
int minSize = a.listSize < b.listSize ? a.listSize:b.listSize;
int maxSize = a.listSize > b.listSize ? a.listSize:b.listSize;
int newLength = a.listSize > b.listSize ? a.arrayLength : b.arrayLength;
T* maxElement = a.listSize > b.listSize ? a.element:b.element;
if(minSize == 0) return *this;
if(maxSize + minSize > arrayLength)
{
newLength = a.arrayLength + b.arrayLength;
}
listSize = maxSize + minSize;
increaseArray(newLength);
int index = 0,aindex = 0,bindex = 0;
cout<<listSize<<endl;
for(int i = 0;i<listSize;i++)
{
if(aindex < a.size() && bindex < b.size())
{
if(a.element[aindex]<=b.element[bindex])
element[index] = a.element[aindex++];
else
element[index] = b.element[bindex++];
}
else
{
if(aindex < a.size() && bindex >= b.size())
element[index] = a.element[aindex++];
else if(aindex >= a.size() && bindex < b.size())
element[index] = b.element[bindex++];
}
}
return *this;
}
//30. 1 ) 编写方法 arrayList<T>::split(lab),它生成两个线性表a 和b。a 包含 *this 中索引为偶
// 数的元素,b 包含其余的元素。
// 2 ) 计算方法的复杂度。
// 3 ) 测试你的代码。
template<class T>
arrayList<T>& arrayList<T>::split(arrayList<T>& a, arrayList<T>& b)
{
if(listSize == 0)
{
cout<<"listSize == empty";
return *this;
}
a.increaseArray(listSize);
b.increaseArray(listSize);
int index,aindex = 0,bindex = 0;
for(int i = 0;i < listSize; i++)
{
if(i % 2 == 0)
{
a.element[aindex++] = element[i];
}
else
{
b.element[bindex++] = element[i];
}
}
a.listSize = (listSize + 1) / 2;
b.listSize = listSize / 2;
return *this;
}
template<class T>
class stack
{
public:
virtual ~stack(){}
//返回true ,当且仅当栈为空
virtual bool empty() const = 0;
//返回栈中元素个数
virtual int size() const = 0;
//返回栈顶元素的引用
virtual T& top() = 0;
//删除栈顶元素
virtual void pop() = 0;
//将元素 theElement 压入栈顶
virtual void push(const T& theElement) = 0;
};
//从类 arrayList 和 stack 派生的类 derivedArrayStack。
//类 derivedArrayStack 的构造函数直接调用类 arrayList 的构造函数,动态创建一维数组,
//数组容量 ( 长度 ) 是参数 initialCapaticy 的值。initialCapaticy 的缺省值是 10。其他成员函数
//的代码也是直接调用基类的成员函数来实现的。
template <class T>
class derivedArrayStack : public arrayList<T>,
public stack<T>
{
public:
derivedArrayStack(int initialCapacity = 10)
:arrayList<T>(initialCapacity){}
bool empty() const
{
return arrayList<T>::empty();
}
int size() const
{
return arrayList<T>::size();
}
//函数top和pop都在类方法get和erase之前检查栈是否为空
//因为基类的方法get和erase在遇到空栈时将抛出异常
//所以我们可以从top和pop中删除对空栈的检查
//而不会影响程序结果。
T& top()
{
if(arrayList<T>::empty())
throw logic_error("空栈");
return arrayList<T>::get(arrayList<T>::size() - 1);
}
void pop()
{
if(arrayList<T>::empty())
throw logic_error("空栈");
arrayList<T>::erase(arrayList<T>::size() - 1);
}
void push(const T& theElement)
{
arrayList<T>::insert(arrayList<T>::size(),theElement);
}
};
//类 arrayStack
template<class T>
class arrayStack : public stack<T>
{
public:
arrayStack(int initialCapacity = 10);
~arrayStack(){delete [] stack;}
bool empty() const {return stackTop == -1;}
int size() const
{
return stackTop+1;
}
T& top()
{
if(stackTop == -1)
throw logic_error("stack empty");
return stack[stackTop];
}
void pop()
{
if(stackTop == -1)
throw logic_error("stack empty");
stack[stackTop--].~T(); //T的析构函数
}
void push(const T& theElement);
T* stack; //元素数组
private:
int stackTop;//当前栈顶
int arrayLength;//栈容量
};
template<class T>
arrayStack<T>::arrayStack(int initialCapacity)
{
//构造函数
if(initialCapacity < 1)
{
ostringstream s;
s << "Initial capacity = "<<initialCapacity<<"must be > 0";
throw logic_error(s.str());
}
arrayLength = initialCapacity;
stack = new T[arrayLength];
stackTop = -1;
}
template<class T>
void arrayStack<T>::push(const T& theElement)
{
//将元素 theElement 压入栈
if(stackTop == arrayLength - 1)
{
//空间已满,容量加们
changeLength1D(stack,arrayLength,2*arrayLength);
arrayLength *= 2;
}
//在栈顶插入
stack[++stackTop] = theElement;
}
//7.
//1 ) 对栈的 ADT 进行扩充,增加以下函数:
//i. 输入栈。
//将一个栈转变为一个适合输出的串。
//将一个栈分裂为两个栈。第一个栈包含从栈底开始的一半元素,第二个栈包含剩余元素。
//iv. 将两个栈合并,把第二个栈的所有元素置于第一个栈的顶部。不改变第二个栈中元素
//的相对顺序。
//2 ) 定义抽象类 extendedStack,它扩展抽象类 stack,而且包含与 1) 中函数对
//应的方法。
//4) 测试你的代码的正确性。
template<class T>
class extendedStack : public virtual stack<T>
{
public:
virtual std::istream& input(std::istream& in) = 0;
virtual std::string toString() const = 0;
virtual void split(extendedStack<T>& st1,
extendedStack<T>& st2) = 0;
virtual void merge(extendedStack<T>& st2) = 0;
};
template<class T>
class extendedArrayStack : public arrayStack<T>,
public extendedStack<T>
{
template<class U>
friend std::istream& operator>>(std::istream& in,
extendedArrayStack<U>& st);
template<class U>
friend std::ostream& operator<<(std::ostream& out,
const extendedArrayStack<U>& st);
public:
extendedArrayStack<T>(int initialCapacity = 10)
:arrayStack<T>(initialCapacity)
{
}
virtual std::istream& input(std::istream& in) override;
virtual std::string toString() const override;
virtual void split(extendedStack<T>& st1,
extendedStack<T>& st2);
virtual void merge(extendedStack<T>& st2);
virtual bool empty() const override
{
return arrayStack<T>::empty();
}
virtual int size() const override
{
return arrayStack<T>::size();
}
virtual T& top() override
{
return arrayStack<T>::top();
}
virtual void pop() override
{
arrayStack<T>::pop();
}
virtual void push(const T& theElement) override
{
arrayStack<T>::push(theElement);
}
std::ostream& output(std::ostream& out) const
{
out<<toString();
return out;
}
};
template<class T>
std::istream& extendedArrayStack<T>::input(std::istream& in)
{
T t;
while(in >> t)
{
push(t);
}
return in;
}
template<class U>
std::istream& operator>>(std::istream& in, extendedArrayStack<U>& st)
{
st.input(in);
return in;
}
template<class U>
std::ostream& operator<<(std::ostream& out,
const extendedArrayStack<U>& st)
{
st.output(out);
return out;
}
template <class T>
std::string extendedArrayStack<T>::toString() const
{
std::string s = "";
for(int i = 0; i < size(); i++)
{
s += std::to_string(arrayStack<T>::stack[i]);
s += " ";
}
return s;
}
template<class T>
void extendedArrayStack<T>::split(extendedStack<T>& st1,
extendedStack<T>& st2)
{
auto& st1_t = dynamic_cast<extendedArrayStack<T>&>(st1);
auto& st2_t = dynamic_cast<extendedArrayStack<T>&>(st2);
int i = 0;
for(;i < size() / 2;i++)
{
st1_t.push(arrayStack<T>::stack[i]);
}
for(;i<size();i++)
{
st2_t.push(arrayStack<T>::stack[i]);
}
}
template<class T>
void extendedArrayStack<T>::merge(extendedStack<T>& st2)
{
auto& st_t = dynamic_cast<extendedArrayStack<T>&>(st2);
for(int i = 0; i < st_t.size();i++)
{
push(arrayStack<T>::stack[i]);
}
}
//ex7
//3 ) 开发具体类 extendedDerivedArrayStack 和 extendedArrayStack,它们的基类是 extendedStack,
//而且各自派生于 derivedArrayStack 和 arrayStack。
//4) 测试你的代码的正确性。
template <class T>
class extendedDerivedArrayStack : private arrayList<T>,public extendedStack<T>
{
template <class U>
friend istream& operator>>(std::istream& in,extendedDerivedArrayStack<U>& st);
template<class U>
friend ostream& operator<<(std::ostream& out, const extendedDerivedArrayStack<U>& st);
public:
extendedDerivedArrayStack<T>(int initialCapacity = 10)
:arrayList<T>::arrayList(initialCapacity) {}
virtual bool empty() const override
{
return arrayList<T>::empty();
}
virtual int size() const override
{
return arrayList<T>::size();
}
virtual T& top() override
{
if(empty())
{
throw logic_error("out of bound");
}
return arrayList<T>::get(size() - 1);
}
virtual void pop() override
{
if(empty())
{
throw logic_error("out of bound");
}
arrayList<T>::erase(size() - 1);
}
virtual void push(const T& theElement) override
{
arrayList<T>::insert(size(),theElement);
}
virtual std::istream& input(std::istream& in) override;
virtual std::string toString() const override;
virtual void split(extendedStack<T>& st1,extendedStack<T>& st2) override;
virtual void merge(extendedStack<T>& st2) override;
// ostream& output(ostream& out) const
// {
// out<<toString();
// return out;
// }
};
template <class T>
istream& extendedDerivedArrayStack<T>::input(istream& in)
{
T t;
while(in>>t)
{
push(t);
}
return in;
}
template <class U>
istream& operator>>(istream& in, extendedDerivedArrayStack<U>& st)
{
st.input(in);
return in;
}
template <class U>
ostream& operator<<(ostream& out,const extendedDerivedArrayStack<U>& st)
{
st.output(out);
return out;
}
template <class T>
string extendedDerivedArrayStack<T>::toString() const
{
string s = "";
for(int i = 0; i < size(); i++)
{
s += std::to_string(arrayList<T>::get(i));
s += " ";
}
return s;
}
template <class T>
void extendedDerivedArrayStack<T>::split(extendedStack<T>& st1,extendedStack<T>& st2)
{
int i = 0;
for(;i < size() / 2;i++)
{
st1.push(arrayList<T>::get(i));
}
for(;i < size();i++)
{
st2.push(arrayList<T>::get(i));
}
}
template <class T>
void extendedDerivedArrayStack<T>::merge(extendedStack<T>& st1)
{
auto& st_t = dynamic_cast<extendedDerivedArrayStack<T>&>(st1);
for(int i = 0; i < st_t.size(); i++)
{
push(st_t.arrayList<T>::get(i));
}
}
//ex10. 开发具体类 stackWithArrayList,它派生于抽象类 stack。这个类具有单个数据成员 list,其
//类型为 arrayList<T>。
template <class T>
class stackWithArrayList : public stack<T>
{
public:
stackWithArrayList<T>(int initialCapacity = 10)
:list(initialCapacity){}
virtual bool empty() const override
{
return list.empty();
}
virtual int size() const override
{
return list.size();
}
virtual T& top() override
{
if(empty())
{
throw logic_error("out of bound");
}
return list.get(size() - 1);
}
virtual void pop() override
{
if(empty())
{
throw logic_error("out of bound");
}
list.erase(size() - 1);
}
virtual void push(const T& theElement) override
{
list.insert(size(),theElement);
}
private:
arrayList<T> list;
};
//ex11. 开发类 twoStacks,它用一个数组描述两个栈。一个栈的栈底在位置 0,另一个栈的栈底在
//位置 arrayLength-1。两个栈都向数组的中间增长〈见图 8-4 )。该类的方法 ”中
//必须能够在每一个栈中实施 ADT 栈的所操作。而且每一个坟计的旬杂度 上直下上
//应为 O(1),其中不包括改变数组大小所需要的时间 -
template <class T>
class twoStacks
{
public:
twoStacks(int initialCapacity = 10);
~twoStacks(){ delete[] stack; }
bool empty1() const
{
return stackTop1 == -1;
}
bool empty2() const
{
return stackTop2 == arrayLength;
}
int size1() const
{
return stackTop1 + 1;
}
int size2() const
{
return arrayLength - stackTop2;
}
T& top1()
{
if(stackTop1 == -1)
throw logic_error("empty stack");
return *(stack + stackTop1);
}
T& top2()
{
if(stackTop2 == arrayLength)
throw logic_error("empty stack");
return *(stack + stackTop2);
}
void pop1()
{
if(stackTop1 == -1)
throw logic_error("empty stack");
stack[stackTop1--].~T();
if(size1() + size2() < arrayLength / 4);
decreaseLength();
}
void pop2()
{
if(stackTop2 == arrayLength)
{
throw logic_error("empty stack");
}
stack[stackTop2++].~T();
if(size1() + size2() < arrayLength / 4)
{
decreaseLength();
}
}
void push1(const T& theElement)
{
if(size1() + size2() == arrayLength)
{
increaseLength();
}
stack[++stackTop1] = theElement;
}
void push2(const T& theElement)
{
if(size1() + size2() == arrayLength)
{
increaseLength();
}
stack[--stackTop2] = theElement;
}
private:
void decreaseLength()
{
int sz2 = size2();
changeLength(false);
arrayLength *= 2;
stackTop2 = arrayLength - sz2;
}
void increaseLength()
{
int sz2 = size2();
changeLength(true);
arrayLength *= 2;
stackTop2 = arrayLength - sz2;
}
void changeLength(bool bigger)
{
int sz1 = size1();
int sz2 = size2();
int newLength = arrayLength / 2;
if(bigger)
newLength = arrayLength * 2;
T* temp = new T[newLength];
std::copy(stack,stack+sz1,temp);
std::copy_backward(stack + arrayLength - sz2, stack+arrayLength, temp + newLength);
delete[] stack;
stack = temp;
}
int stackTop1,stackTop2;
int arrayLength;
T* stack;
};
template<class T>
twoStacks<T>::twoStacks(int initialCapacity)
{
if(initialCapacity <= 0)
throw logic_error("capacity should be > 0");
stack = new T[initialCapacity];
stackTop1 = -1;
stackTop2 = initialCapacity;
arrayLength = initialCapacity;
}
int main()
{
derivedArrayStack<double> stack1;
arrayStack<double> stack2;
cout<<stack1.empty()<<endl;
cout<<stack2.empty()<<endl;
for(int i = 0; i < 10; i++)
{
stack1.push(i);
stack2.push(i);
}
cout<<stack1.size()<<endl;
cout<<stack2.size()<<endl;
extendedArrayStack<double> stack3;
string str("1 3 4 5 6 7");
istringstream iss1(str);
iss1>>stack3;
cout<<stack3<<endl;
extendedArrayStack<double> stack4;
string str2("2 3 1 1 2 1");
istringstream iss2(str2);
iss2>>stack4;
cout<<stack4<<endl;
stack3.merge(stack4);
cout<<stack3<<endl;
extendedDerivedArrayStack<double>stack5;
string str3("2 3 1 1 2 1");
istringstream iss3(str2);
iss3>>stack5;
cout<<stack5<<endl;
twoStacks<double> twoStacks1;
for(int i = 0; i < 10; i++)
{
twoStacks1.push1(i);
twoStacks1.push2(10-i);
}
cout<<twoStacks1.size1()<<endl;
cout<<twoStacks1.size2()<<endl;
cout<<twoStacks1.top1()<<endl;
cout<<twoStacks1.top2()<<endl;
/*
for(int i = 0; i < 10; i++)
{
std::cout<<stack1.top()<<std::endl;
std::cout<<stack2.top()<<std::endl;
stack1.pop();
stack2.pop();
}
*/
return 0;
}
测试输出
xz@xiaqiu:~/study/algorithm/c++/1/build$ ./test
1
1
10
10
1.000000 3.000000 4.000000 5.000000 6.000000 7.000000
2.000000 3.000000 1.000000 1.000000 2.000000 1.000000
1.000000 3.000000 4.000000 5.000000 6.000000 7.000000 1.000000 3.000000 4.000000 5.000000 6.000000 7.000000
2 3 1 1 2 1
10
10
9
1
xz@xiaqiu:~/study/algorithm/c++/1/build$