数据结构算法与应用-C++语言描述 chain 单链表2

chain.cpp

#include <iostream>
#include <sstream>
#include <iterator>
using namespace std;

//数据结构算法与应用-C++语言描述 chain 单链表

//一个线性表的抽象类
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; 
};

/*
 结构 chainNode
 为了用链表摘述线性表,我们要定义一个结构 chainNode 和一个类 chain。
 数据成员 element 是节点的数据域,存储表元素; 数据成员 next 是节点的链域,存储下一个节点的指针。
*/
 //链表节点的结构定义
template <class T>
struct chainNode
{
public:
	//方法
	chainNode(){}
	chainNode(const T& element)
	{
		this->element = element;
	}
	chainNode(const T& element,chainNode<T>* next)
	{
		this->element = element;
		this->next = next;
	}
	
	//数据成员
	T element;
	chainNode<T> *next;
};

//类 chain
/*
 1. 链表 chain 的方法 header、empty 和 size
 类 chain 用单向链表实现了线性表,其中最后一个节点的指针域为NULL,即它用单向
 链接的一组节点实现线性表
*/
template<class T>
class chain : public linearList<T>
{
public:
	//构造函数,复制构造函数和析构函数
	chain(int initialCapacity = 10);
	chain(const chain<T>&);
	~chain();

	//抽象数据类型 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;
	
	T getFirstNode(){return firstNode->element;}
	T getLastNode(){return lastNode->element;}
	//清表
	void clear();

	//将元素 theElement 插到表尾
	void push_back(const T& theElement);

	//ex2.大小小于 theSize,则不增加元素。若初始线性表的大小大于 theSize,则删除多余的元素。
	//计算方法的复杂度。测试你的代码。
	void setSize(int theSize);

	//ex3. 编写方法 chain<T>::set(theIndex,theElement),它用元素 theElement 替换索引为 thelndex 的
	//元素。若索引 theIndex 超出范围,则抛出异常。计算方法的复杂度。测试你的代码。
	void set(int theIndex,const T& theElement);

	//ex4. 编写方法 chain<T>::removeRange(fromIndex,toIndex),它删除指定索引范围内的所有元素。
	//计算方法的复杂度。测试你的代码。
	void removeRange(int fromIndex,int toIndex);

	//ex5. 编写方法 chain<T>::lastIndexOf(theElemen),返回值是指定元素最后出现时的索引。若这
	//样的元素不存在,则返回 -1。计算方法的复杂度。测试你的代码。
	int lastIndexOf(const T& theElement) const;

	//ex6. 重载操作符 [] ,使得表达式 x[i] 返回对链表 x 的第 i 个元素的引用。若链表没有第 i 个元素,
	//则抛出异常。语句 x[i] = y 和y=x[i]按以往预期的方式执行。测试你的代码。
	chainNode<T>& operator[](int theIndex);

	//ex7. 重载操作符 ==,使得表达式 x==y 返回 true,当且仅当两个链表 x 和y 相等,即对所有的
	//i,两个链表的第 i 个元素相等。测试你的代码。
	bool operator==(const chain<T>& right) const;

	//ex8. 重载操作符 !=,使得表达式 x!=y 返回 true,当且仅当两个链表 x 和y 不等 ( 见练习7 )。测
	//试你的代码。
	bool operator!=(const chain<T>& right) const;
	
	//ex9 重载操作符 <,使得表达式 x<y 返回 true,当且仅当链表 x 按字典顺序小于链表 y( 见练习
	//7)。测试你的代码。
	bool operator<(const chain<T>& right) const;

	//ex10. 编写方法 chain<T>::swap(theChain),它交换链表元素 *this 和 theChain。计算方法的复杂
	//度。测试你的代码。
	void swap(chain<T>& right);

	//ex14. 1 ) 编写方法 chain<T>::leftShiftt),它将表中的元素向左移动个位置。如果 1=[0,1,2,3,4] ,
	//那么 leftShift(2) 的结果是 1=[2,3,4]。
	//2 ) 计算方法的复杂度。
	//3 ) 测试你的代码。	
	void leftShift(int i);

	//ex15. 1 ) 编写方法 chain<T>::reverse,它颠倒 *this 中的元素的顺序,而且原地完成,不用分配
	//任何新的节点空间。
	//2 ) 计算方法的复杂度。
	//3 ) 使用自己的测试数据检验方法的正确性。
	void reverse();

	//ex17
	//1 ) 编写一个非成员方法 meld,它生成一个新的扩展的链表 c,它从 a 的首元素开始,交
	//替地包含a 和 b的元素。如果一个链表的元素取完了,就把另一个链表的剩余元素附
	//加到新的扩展链表 c 中。方法的复杂度应与链表 a 和 的长度具有线性关系。
	//2 ) 证明方法具有线性复杂度。
	//3 ) 使用自己的测试数据检验方法的正确性。
	chain<T>& meld1(const chain<T>& a, const chain<T>& b);

	//ex18. 编写方法 chain<T>::meld。它与练习 17 的方法 meld类似。然而,a 和b 以及合并结果,
	//都是 chain<T> 类型。合并后的链表使用的应该是链表 a 和 的节点空间。合并之后,输
	//链表a 和b 是空表。
	//1 ) 编写方法 meld,其复杂度应该与输入链表的长度具有线性关系。
	//2 ) 证明方法具有线性复杂度。
	//3 ) 测试代码的正确性。使用自己的测试数据。
	chain<T>& meld2(chain<T>& a, chain<T>& b);

	//ex19.
	//1 ) 编写一个非成员方法 merge,它生成一个新的有序链表 c,包含a 和 b的所有元素。
	//2 ) 计算方法的复杂度。
	//3 ) 使用自己的测试数据检验方法的正确性。
	chain<T>& merge1(chain<T>& a, chain<T>& b); 

	//ex20. 重做练习 19,但是编写的是方法 chain<T>::merge。归并之后,两个输入链表a 和 为空
	chain<T>& merge2(chain<T>& a, chain<T>& b); 

	//ex22. 编写方法 chain<T>::split,它与练习 21 的函数类似。然而,它用输入链表 *this 的空间建
	//立了链表a和b。
	chain<T>& split(chain<T>& a, chain<T>& b);

	//ex23. 在一个循环移动的操作中,线性表的元素根据给定的值,按顺时针方向移动。例如
	//L=[0,1,2,3,4],循环移动 2 的结果是 L=[2,3,4,0,1]。
	//1 ) 编写方法 circularShift(i),它将线性表的元素循环移动i 个位置。
	//2 ) 测试你的代码。
	chain<T>& circularShift(int index);

	//ex26. 编写方法 chain<T>::insertSort,它使用插入排序 ( 见程序 2-15 ) 对链表按非递减顺序重新排序。
	//不开发新的节点,也不删除老的节点。可以假设元素的类型定义了关系操作符 (<、> 等)。
	//1 ) 方法在最坏情况下的复杂度是多少? 如果元素已经有序,那么该方法需要多少时间?
	//2 ) 使用自己的测试数据检验方法的正确性。
	chain<T>& insertionSort();
	//ex27. 对如下的排序方法 ( 见第 2 章的描述 ) 重做练习 26:
	//1 ) 冒泡排序。
	//2 ) 选择排序。
	//3 ) 计数排序或排列排序。
	chain<T>& bubbleSort();
	chain<T>& selectionSort();

	//void rank(int rank[]);
	// chain<T>& rankSort();

	//每个箱子都以底部节点作为首节点,项部节点作为尾节点。每个箱子都有两个指
	//针,分别存储在数组 bottom 和top 中,分别指向尾节点和头节点。bottom[theBin] 指向箱
	//子theBin 的尾节点,而top[theBin] 指向箱子 theBin 的首节点。所有箱子开始为空表,这时
	//bottom[theBin]=NULL。
	chain<T>& binSort(int range);

	//根据 棋牌的组号 样式 面值排序
	chain<T>& binSort(int range,int (*func)(chainNode<T>*));


	//例 6-1 假定对0 ~ 999 之间的 10 个整数进行排序。如果使用 range=1000 的箱子排序方
	//法,那么箱子链表的初始化需要 1000 个执行步,节点分配需要 10 个执行步,从箱子中收集
	//节点需要 1000 个执行步,总的执行步数为 2010。而基数排序方法是:
	//1 ) 利用箱子排序方法,根据最低位数字 ( 即个位数字 ),对 10 个数进行排序。因为每个
	//数字都在0 ~ 9之间,所以range=10。图 6-9a 是 10 个数的输入链表,图 6-9b 是按最低位数
	//字排序后的链表。
	//216->521->425->116->91->515->124->34->96->24 //a输入链表
	//521->91->124->34->24->425->515->216->116->96 //b按照最后一位数字进行排序
	//515->216->116->521->124->24->425->34->91->96 //c按照第二位数字进行排序
	//24->34->91->96->116->124->216->425->515->521 //d按照最高位进行排序
	//2 ) 利用箱子排序方法, 对 1 ) 的结果按次低位数字 ( 即十位数字 ) 进行排序。同样有
	//range=10。因为箱子排序是稳定排序,所以次低位数字相同的节点,按最低位数字排序所得
	//到的次序保持不变。因此,现在的链表是按照最后两位数字进行排序的。图 6-9c 是相应的排
	//序结果
	//3 ) 利用箱子排序方法,对 2 ) 的结果按第三位数字 ( 即百位数字 ) 进行排序。小于 100
	//的数,第三位数字为 0。因为按第三位数字排序是稳定排序, 所以第三位数字相同的节点,
	//按最后两位数字排序所得到的次序保持不变。因此,现在的链表是按照后三位数字进行排序
	//的。图 6-9d 是相应的排序结果。
	//上述基数排序方法是以 10 为基数,把数分解为十进制数字进行排序。因为每个数至少有
	//三位数字,所以要进行三次排序,每次排序都使用 range=10 个箱子排序。每次箱子的初始化
	//需要 10 个执行步,节点分配需要 10 个执行步,从箱子中收集节点需要 10 个执行步,总的执
	//行步数为 90,比使用 range=1000 的箱子排序要少得多。单个箱子排序 (一数一个箱子的 ) 实
	//际上等价于 一1000 的基数排序。                                                                   


	//64. 设计一个方法,它应用基数排序思想给链表排序。
	//1 ) 编写方法 chain<T>::radixSort(cd),它使用基数排序思想,按递增顺序给链表排序。方
	//法的输入为 : 基数 ”、按基数”分解的数字的个数d。假设定义了从类型T到 int 的类
	//型转换。方法的复杂度应为 O(d(r+n))。证明这个复杂度成立。
	//2 ) 使用自己设计的测试数据来测试方法的正确性。
	//3 ) 比较你的方法和基于链表的插入排序方法的性能。
	//为此可使用m=100, 1000, 10 000;r=10 和 d=3。
	chain<T>& radixSort(int r,int d);

public:
	class iterator
	{
	public:
		//向前迭代器所需要的上typedef语句
		typedef forward_iterator_tag iterator_capacity;
		typedef T value_type;
		typedef ptrdiff_t difference_type;
		typedef T* pointer;
		typedef T& reference;

		//构造函数
		iterator(chainNode<T>* theNode = NULL)
		{
			node = theNode;
		}

		//解引用操作符
		T& operator*() const { return node->element; }
		T* operator->() const { return &node->element; }

		//迭代器加法操作 不能operator--
		iterator& operator++() //前加
		{
			node = node->next;
			return *this;
		}
		iterator operator++(int) //后加
		{
			iterator old = *this;
			node = node->next;
			return old;
		}

		//相等检验
		bool operator!=(const iterator right) const
		{
			return node != right.node;
		}
		bool operator==(const iterator right) const
		{
			return node == right.node;
		}
	protected:
		chainNode<T>* node;
	};

	iterator begin(){ return iterator(firstNode);}
	iterator end(){return iterator(NULL);}
protected:
	//如果索引无效,抛出异常
	void checkIndex(int theIndex) const;
	//指向链表第一个节点的指针
	chainNode<T>* firstNode;
	chainNode<T>* lastNode;
	//线性表的元素个数
	int listSize; 
};

//ex66. 一堆部组卡片。每张卡片有三个域 : 卡片的组号、卡片的样式及卡片的面值。每组最多有
//52 张卡片(因为每一组可能有丢失的卡片 ),因此该堆卡片最多有 522 张。可以假设每组
//卡片至少有一张,因此卡片总数至少为n
//1 ) 按照组号对卡片进行排序,对组号相同的卡片按样式排序,对样式也相同的卡片按其
//面值排序。应该采用三次箱子排序过程来完成这种排序。
//2 ) 编写一段程序,输入为上和一个卡片堆,输出为有序的卡片。把卡片堆描述为一个链
//表,链表节点包含如下域 : deck、suit、face 和link。程序的复杂度应为 O(n),证明这
//个复杂度成立。
//3 ) 测试程序的正确性。
struct Card
{
	enum Suit{ Space, Heart, Diamond, Club};
	enum Face{Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace};
	int deck;
	Suit suit;
	Face face;

	Card(){}
	Card(const int deck, const int suit, const int face)
		:deck(deck),
		suit(Suit(suit)),
		face(Face(face))
	{

	}
	Card(const Card& rec)
		:deck(rec.deck),
		suit(rec.suit),
		face(rec.face)
	{

	}
	Card& operator=(const Card& rec)
	{
		deck = rec.deck;
		suit = rec.suit;
		face = rec.face;
		return *this;
	}
	bool operator!=(const Card& x) const
	{
		return deck != x.deck||
			   suit != x.suit||
			   face != x.face;
	}
	operator int() const{ return face; }
};

//构造函数和复制构造函数
/*
 与数组描述的线
 性表不同,链表在创建时不需要估计元素的最大个数以分配初始空间。不过,构造函数还是
 具有一个表示初始容量的形参 initialCapacity,目的是与类 arrayList 相容。尤其在应用中,可
 能需要一个类型为 linearList 的数组,对数组成员的初始化将会用到如下所示的每一种形式的
 构造函数。
 linearList<int>* list[10];
 list[0] = new arrayList<int>(20);
 list[1] = new arrayList<int>();
 list[2] = new chain<int>(5);
 list[3] = new chain<int>;
*/
//链表的构造函数和复制构造函数
template<class T>
chain<T>::chain(int initialCapacity)
{
	//构造函数
	if(initialCapacity < 1)
	{
		ostringstream s;
		s<<"Initial capacity = "<<initialCapacity<<" must be >0";
		throw logic_error(s.str());
	}
	//为了创建一个空链表,只需令第一个节点指针 firstNode 的值为NULL。
	firstNode = NULL;
	listSize = 0;
	lastNode = 0;
}

template<class T>
chain<T>::chain(const chain<T>& theList)
{
	// 复制构造函数
	listSize = theList.listSize;
	if(listSize == 0)
	{
		//如果theList 为空创建空链表
		firstNode = NULL;
		return;
	}
	//链表 theList 为非空
	//要复制链表 theList 的节点
	chainNode<T>* sourceNode = theList.firstNode;
	//复制链表 theList 的首元素
	firstNode = new chainNode<T>(sourceNode->element);
	//sourceNode 指向下一个节点
	sourceNode = sourceNode->next;
	//当前链表 *this 的最后一个节点
	chainNode<T>* targetNode = firstNode;
	while(sourceNode != NULL)
	{
		//复制剩余元素
		targetNode->next = new chainNode<T>(sourceNode->element);
		targetNode = targetNode->next;
		sourceNode = sourceNode->next;
	}
	//链表结束
	targetNode->next = NULL;
}

//析构函数
/*
 析构函数要逐个清除链表的节点。实现的策略是重复清除链表的首元素节点,
 直到链表为空。注意,我们必须在清除首元素节点之前用变量nextNode 
 保存第 2 个元素节点的指针。析构函数的时间复杂度是 O(listSize)。
*/
//链表的析构函数
template<class T>
chain<T>::~chain()
{
	// 链表析构函数 .删除链表的所有节点
	while(firstNode != NULL)
	{
		//保存下一个元素节点的指针
		chainNode<T>* nextNode = firstNode->next;
		//删除当前节点
		delete firstNode;
		//指向下一个节点
		firstNode = nextNode;
	}
	lastNode = NULL;
}

//get方法
/*
 在数组描述的线性表中,我们根据公式来计算一个表元素的位置。然而在链表中,要寻
 找索引为 theIndex 的元素,必须从第一个节点开始,跟踪链域 next 直至找到所需的元素节点
 指针,也就是说,必须跟踪 theIndex 个指针。不可能对 firstNode 套用公式来计算所需节点的
 位置。其中的方法 checkIndex 与在 arrayList 中定义的一样。方
 法 get 的时间复杂度在链表 chain 中为 O(theIndex),而在数组描述的线性表 arrayList 中是
*/
//get方法的返回值是索引为 thelndex 的元素
template<class T>
T& chain<T>::get(int theIndex) const
{
	//返回索引为 theIndex 的元素
	//若该元素不存在,则抛出异常
	checkIndex(theIndex);

	//开始时当前节点指向链表的第一个节点
	chainNode<T>* currentNode = firstNode;
	//依次寻找到下标为theIndex的节点 theIndex 是从0开始
	for(int i = 0; i < theIndex && currentNode; i++)
	{
		currentNode = currentNode->next;
	}
	return currentNode->element;
}

//indexOf方法
/*
 它与 arrayList<T>::indexOf 的代码不同
 主要体现在从一个元素寻找下一个相邻元素的方式上面数组描述的线性表中
 在链表描述的线性表中,唯一的方法是用当前节点的指针确定下一个相邻节点的位置。
 返回元素 theElement 首次出现时的索引
*/
template<class T>
int chain<T>::indexOf(const T& theElement) const
{
	//返回元素 theElement 首次出现时的索引
	//该元素不存在,则返回 -1
	//搜索链表寻找元素 theELement
	chainNode<T>* currentNode = firstNode;
	// 当前节点的索引
	int index = 0;
	while(currentNode != NULL &&
		  currentNode->element != theElement)
	{
		//移向下一个节点
		currentNode = currentNode->next;
		index++;
	}
	//确定是否找到所需的元素
	if(currentNode == NULL)
		return -1;
	else
		return index;
}
//erase方法
/*
 它删除索引为 theIndex 的元素
 需要考虑三种情况:
 1.theIndex<0 或者 theIndex > listSize。这时,删除操作无效,
 因为没有这个位置上的元素。这种情况可能表示链表为空。
 2.删除非空表的第 0 个元素节点。
 3.删除其他元素节点。
*/
template<class T>
void chain<T>::erase(int theIndex)
{
	//删除索引为 theIndex 的元素
	//该元素不存在,则抛出异常
	checkIndex(theIndex);

	//索引有效,需找要删除的元素节点
	chainNode<T>* deleteNode;
	if(theIndex == 0)
	{
		//删除链表的首节点
		deleteNode = firstNode;
		firstNode = firstNode->next;
	}
	else
	{
		//用指针P指向要删除节点的前驱节点
		chainNode<T>*p = firstNode;
		for(int i = 0; i < theIndex - 1;i++)
			p = p->next;

		deleteNode = p->next;
		if(theIndex == listSize - 1)
		{
			p->next = nullptr;
			lastNode = p;
		}
		else
		{
			//删除 deleteNode 指向的节点
			p->next = p->next->next;
		}
	}
	listSize--;
	delete deleteNode;
}

//insert方法
/*
	插入和删除的过程很相似。为了在链表中索引为theIndex的位置上插入一个新元素,需
	要首先找到索引为 theIndex-1 的元素节点,然后在该节点之后插人新元素节点。
*/
template<class T>
void chain<T>::insert(int theIndex,const T& theElement)
{
	// 在索引为 theIndex 的位置上插入元素 theElement
	//如果theIndex = listSize是在链表尾插入元素
	//如果theIndex = 0是在链表头插入元素
	if(theIndex < 0 || theIndex > listSize)
	{
		//无效索引
		ostringstream s;
		s<<"index = "<<theIndex <<" size = "<<listSize;
		throw logic_error(s.str());
	}
	if(theIndex == 0)
	{
		//在链表头插入
		firstNode = new chainNode<T>(theElement,firstNode);
		if(listSize == theIndex)
		{
			lastNode = firstNode;
		}
	}
	else
	{
		//寻找新元素的前驱
		chainNode<T>* p = firstNode;
		//寻找到下标为theIndex-1的位置,就是第theIndex个节点
		for(int i = 0;i < theIndex - 1;i++)
			p = p->next;

		//在P之后插入
		p->next = new chainNode<T>(theElement,p->next);
		if(theIndex == listSize-1)
		{
			lastNode = p->next->next;
		}
	}
	listSize++;
}

//输出链表
/*
 既是对输出方法 output 的实现,又是对流插入符 << 的重载。
 chain<T>::output与arrayList<T>::output 不同,
 主要在于它从一个节点向另一个节点移动时使用了指针 next。
 不过,两者的时间复杂度一样,都是 O(listSize)。
*/
template<class T>
void chain<T>::output(ostream& out) const
{
	//把链表放入输出流
	for(chainNode<T>* currentNode = firstNode;
		   			  currentNode != NULL;
		   			  currentNode = currentNode->next)
	{
		out<<currentNode->element<<" ";
	}
}

template<class T>
ostream& operator<<(ostream& out, const chain<T>& x)
{
	x.output(out);
	return out;
}

template<class T>
void chain<T>::clear()
{
	//删除链表的所有节点
	while(firstNode != NULL)
	{
		//删除节点 firstNode
		chainNode<T>* nextNode = firstNode->next;
		delete firstNode;
		firstNode = nextNode;
	}
	listSize = 0;
};

template<class T>
void chain<T>::push_back(const T& theElement)
{
	//在链表尾端插入元素 rheELement 的节点
	chainNode<T>* newNode = new chainNode<T>(theElement,NULL);
	if(firstNode == NULL)
	{
		//链表为空
		firstNode = lastNode = newNode;
	}
	else
	{
		//把新元素节点附加到 lastNode 指向的节点
		lastNode->next = newNode;
		lastNode = newNode;
	}
	listSize++;
}

template<class T>
void chain<T>::checkIndex(int theIndex) const
{
	if(theIndex < 0 || theIndex >= listSize)
	{
		ostringstream s;
		s << "index = " << theIndex << " size = " << listSize;
		throw logic_error(s.str());
	}
}

//ex2.大小小于 theSize,则不增加元素。若初始线性表的大小大于 theSize,则删除多余的元素。
//计算方法的复杂度。测试你的代码。
template<class T>
void chain<T>::setSize(int theSize)
{
	if(listSize == 0 || theSize >= listSize)
	{
		cout<<"listSize == 0 || theSize >= listSize";
		return;
	}
	chainNode<T>* currentNode = firstNode;
	for(int i = 0; i < theSize-1; i++)
	{
		currentNode = currentNode->next;
	}
	chainNode<T>* prev = currentNode;
	if(theSize == 0) 
	{
		firstNode = nullptr;
	}
	else
	{
		currentNode = currentNode->next;
		lastNode = prev;
		prev->next = nullptr;
	}

	for(int i = theSize; i < listSize-1;i++)
	{
		prev = currentNode;
		currentNode = currentNode->next;
		delete prev;
	}
	listSize = theSize;
}

//ex3. 编写方法 chain<T>::set(theIndex,theElement),它用元素 theElement 替换索引为 thelndex 的
//元素。若索引 theIndex 超出范围,则抛出异常。计算方法的复杂度。测试你的代码。
template<class T>
void chain<T>::set(int theIndex,const T& theElement)
{
	checkIndex(theIndex);
	if(theIndex == 0)
	{
		firstNode->element = theElement;
		return;
	}
	chainNode<T>* currentNode = firstNode;

	for(int i = 0; i < theIndex; i++)
	{
		currentNode = currentNode->next;
	}
	currentNode->element = theElement;
}

//ex4. 编写方法 chain<T>::removeRange(fromIndex,toIndex),它删除指定索引范围内的所有元素。
//计算方法的复杂度。测试你的代码。
template<class T>
void chain<T>::removeRange(int fromIndex,int toIndex)
{
	checkIndex(fromIndex);
	checkIndex(toIndex);
	if(toIndex < fromIndex) 
	{
		return;
	}

	chainNode<T>* currentNode = firstNode;

	//获取删除节点的前一个节点
	for(int i = 1;i < fromIndex;i++)
	{
		currentNode = currentNode->next;
	}
	chainNode<T>* preDeleteNode = currentNode;
	if(toIndex == listSize - 1)
	{
		lastNode = currentNode;
	}
	for(int i = fromIndex;i < toIndex; i++)
	{
		if(fromIndex == 0)
		{
			firstNode = currentNode->next;
			delete currentNode;
			currentNode = firstNode;
		}
		else
		{
			currentNode = preDeleteNode->next;
			preDeleteNode->next=  currentNode->next;
			delete currentNode;
		}
	}
	listSize = listSize - (toIndex -  fromIndex);
}

//ex5. 编写方法 chain<T>::lastIndexOf(theElemen),返回值是指定元素最后出现时的索引。若这
//样的元素不存在,则返回 -1。计算方法的复杂度。测试你的代码。
template<class T>
int chain<T>::lastIndexOf(const T& theElement) const
{
	if(listSize < 1) return -1;

	int index =  -1;
	chainNode<T>* currentNode = firstNode;
	for(int i = 0;i < listSize; i++)
	{
		if(currentNode->element == theElement)
		{
			index = i;
		}
		currentNode = currentNode->next;
	}
	return index;
}

//ex6. 重载操作符 [] ,使得表达式 x[i] 返回对链表 x 的第 i 个元素的引用。若链表没有第 i 个元素,
//则抛出异常。语句 x[i] = y 和y=x[i]按以往预期的方式执行。测试你的代码。
template<class T>
chainNode<T>& chain<T>::operator[](int theIndex)
{
	checkIndex(theIndex);
	chainNode<T>* currentNode = firstNode;
	if(theIndex == 0)
	{
		return *currentNode;
	}
	for(int i = 0;i < theIndex;i++)
	{
		currentNode = currentNode->next;
	}
	return *currentNode;
}


//ex7. 重载操作符 ==,使得表达式 x==y 返回 rue,当且仅当两个链表 x 和y 相等,即对所有的
//i,两个链表的第 i 个元素相等。测试你的代码。
template<class T>
bool chain<T>::operator==(const chain<T>& right) const
{
	if(listSize == 0 && right.listSize == 0) 
		return true;
	if(listSize != right.listSize)
		return false;

	chainNode<T>* currentNode1 = firstNode;
	chainNode<T>* currentNode2 = right.firstNode;

	for(int i = 0;i < listSize;i++)
	{
		if(currentNode1->element != currentNode2->element)
			return false;
		currentNode1 = currentNode1->next;
		currentNode2 = currentNode2->next;
	}
	return true;
}

//ex8. 重载操作符 !=,使得表达式 x!=y 返回 true,当且仅当两个链表 x 和y 不等 ( 见练习7 )。测
//试你的代码。
template<class T>
bool chain<T>::operator!=(const chain<T>& right) const
{
	return !(*this == right);
}


//ex9 重载操作符 <,使得表达式 x<y 返回 true,当且仅当链表 x 按字典顺序小于链表 y( 见练习
//7)。测试你的代码。
template<class T>
bool chain<T>::operator<(const chain<T>& right) const
{
	if(listSize == 0 && right.listSize > 0) return true;
	if(listSize > 0 && right.listSize == 0) return false;
	int small = listSize <= right.listSize ? listSize : right.listSize;

	chainNode<T>* cur1 = firstNode;
	chainNode<T>* cur2 = right.firstNode;

	for (int i = 0; i < small; i++){
		if(cur1->element < cur2->element) return true;
		if (cur1->element > cur2->element) return false;
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	if(cur2 != nullptr) return true;
	return false;
}

//ex10. 编写方法 chain<T>::swap(theChain),它交换链表元素 *this 和 theChain。计算方法的复杂
//度。测试你的代码。
template<class T>
void chain<T>::swap(chain<T>& right)
{
	std::swap(firstNode,right.firstNode);
	std::swap(lastNode,right.lastNode);
	std::swap(listSize,right.listSize);
}

//ex14. 1 ) 编写方法 chain<T>::leftShiftt),它将表中的元素向左移动个位置。如果 1=[0,1,2,3,4] ,
//那么 leftShift(2) 的结果是 1=[2,3,4]。
//2 ) 计算方法的复杂度。
//3 ) 测试你的代码。	
template<class T>
void chain<T>::leftShift(int i)
{
	if(i < 0 || i>listSize)
	{
		ostringstream s;
		s<<"i < 0 || i>listSize";
		throw logic_error(s.str());
	}
	if(int i = 0)
	{
		return;
	}

	chainNode<T>* currentNode = firstNode;
	for(int j = 0;j < i; j++)
	{
		firstNode = currentNode->next;
		delete currentNode;
		currentNode = firstNode;
		listSize--;
	}
}

//ex15. 1 ) 编写方法 chain<T>::reverse,它颠倒 *this 中的元素的顺序,而且原地完成,不用分配
//任何新的节点空间。
//2 ) 计算方法的复杂度。
//3 ) 使用自己的测试数据检验方法的正确性。
template<class T>
void chain<T>::reverse()
{
	if(listSize <= 0) return ;
	chainNode<T>* currentNode = firstNode->next;
	chainNode<T>* nextNode = currentNode->next;
	firstNode->next = nullptr;
	lastNode = firstNode;
	while(nextNode)
	{
		currentNode->next = firstNode;
		firstNode = currentNode;
		currentNode = nextNode;
		nextNode = nextNode->next;
	}
	currentNode->next = firstNode;
	firstNode = currentNode;	
}

//ex17
//1 ) 编写一个非成员方法 meld,它生成一个新的扩展的链表 c,它从 a 的首元素开始,交
//替地包含a 和 b的元素。如果一个链表的元素取完了,就把另一个链表的剩余元素附
//加到新的扩展链表 c 中。方法的复杂度应与链表 a 和 的长度具有线性关系。
//2 ) 证明方法具有线性复杂度。
//3 ) 使用自己的测试数据检验方法的正确性。
template<class T>
chain<T>& chain<T>::meld1(const chain<T>& a, const chain<T>& b)
{
	if(a.listSize == 0 && b.listSize == 0)
	{
		return *this;
	}

	chainNode<T>* currentNodeA = a.firstNode;
	chainNode<T>* currentNodeB = b.firstNode;

	firstNode = new chainNode<T>(currentNodeA->element);
	chainNode<T>* currentNode = firstNode;
	currentNodeA = currentNodeA->next;
	while(currentNodeA && currentNodeB)
	{
		currentNode->next = new chainNode<T>(currentNodeA->element);
		currentNode = currentNode->next;
		currentNodeA = currentNodeA->next;
		currentNode->next = new chainNode<T>(currentNodeB->element);
		currentNode = currentNode->next;
		currentNodeB = currentNodeB->next;
	}
	while(currentNodeA)
	{
		currentNode->next = new chainNode<T>(currentNodeA->element);
		currentNode = currentNode->next;
		if(!currentNodeA->next)
		{
			currentNode->next = nullptr;
			lastNode = currentNode;	
		}
		currentNodeA = currentNodeA->next;
	}
	while(currentNodeB)
	{
		currentNode->next = new chainNode<T>(currentNodeB->element);
		currentNode = currentNode->next;		
		if(!currentNodeB->next)
		{
			currentNode->next = nullptr;
			lastNode = currentNode;	
		}
		currentNodeB = currentNodeB->next;
	}
	listSize = a.listSize+b.listSize;
	return *this;
}


//ex18. 编写方法 chain<T>::meld。它与练习 17 的方法 meld类似。然而,a 和b 以及合并结果,
//都是 chain<T> 类型。合并后的链表使用的应该是链表 a 和 的节点空间。合并之后,输
//链表a 和b 是空表。
//1 ) 编写方法 meld,其复杂度应该与输入链表的长度具有线性关系。
//2 ) 证明方法具有线性复杂度。
//3 ) 测试代码的正确性。使用自己的测试数据。
template<class T>
chain<T>& chain<T>::meld2(chain<T>& a, chain<T>& b)
{
	if(a.listSize == 0 && b.listSize == 0)
	{
		return *this;
	}

	chainNode<T>* currentNodeA = a.firstNode;
	chainNode<T>* currentNodeB = b.firstNode;

	firstNode = currentNodeA;
	chainNode<T>* currentNode = firstNode;
	currentNodeA = currentNodeA->next;
	while(currentNodeA && currentNodeB)
	{
		currentNode->next = currentNodeA;
		currentNode = currentNode->next;
		currentNodeA = currentNodeA->next;
		currentNode->next = currentNodeB;
		currentNode = currentNode->next;
		currentNodeB = currentNodeB->next;
	}
	while(currentNodeA)
	{
		currentNode->next = currentNodeA;
		currentNode = currentNode->next;
		if(!currentNodeA->next)
		{
			currentNode->next = nullptr;
			lastNode = currentNode;	
		}
		currentNodeA = currentNodeA->next;
	}
	while(currentNodeB)
	{
		currentNode->next = currentNodeB;
		currentNode = currentNode->next;		
		if(!currentNodeB->next)
		{
			currentNode->next = nullptr;
			lastNode = currentNode;	
		}
		currentNodeB = currentNodeB->next;
	}
	listSize = a.listSize+b.listSize;
	a.firstNode = b.firstNode = nullptr;
	a.lastNode = b.lastNode = nullptr;
	a.listSize = b.listSize = 0;
	return *this;
}


//ex19.
//1 ) 编写一个非成员方法 merge,它生成一个新的有序链表 c,包含a 和 b的所有元素。
//2 ) 计算方法的复杂度。
//3 ) 使用自己的测试数据检验方法的正确性。
template<class T>
chain<T>& chain<T>::merge1(chain<T>& a, chain<T>& b)
{
	chainNode<T>* currentNodeA = a.firstNode;
	chainNode<T>* currentNodeB = b.firstNode;
	chainNode<T>* currentNodeC = firstNode;
	while(currentNodeA || currentNodeB)
	{
		if(currentNodeA == nullptr ||
		   currentNodeB != nullptr &&
		   currentNodeA->element > currentNodeB->element)
		{
			if(!firstNode)
			{
				firstNode = new chainNode<T>(currentNodeB->element);
				currentNodeC = firstNode;
			}
			else
			{
				currentNodeC->next = currentNodeB;
				currentNodeC = new chainNode<T>(currentNodeB->element);;
			}
			currentNodeB = currentNodeB->next;
		}
		else if(currentNodeB == nullptr || 
			    currentNodeA != nullptr && 
			    currentNodeA->element <= currentNodeB->element)
		{
			if(!firstNode)
			{
				firstNode = new chainNode<T>(currentNodeA->element);;
				currentNodeC = firstNode;
			}
			else
			{
				currentNodeC->next = currentNodeA;
				currentNodeC = new chainNode<T>(currentNodeA->element);;
			}
			currentNodeA = currentNodeA->next;
		}
		lastNode = currentNodeC;
		listSize = a.listSize + b.listSize;
		a.firstNode = b.firstNode = nullptr;
		a.lastNode = b.lastNode = nullptr;
		a.listSize = b.listSize = 0;
		return *this;
	}
}

//ex20. 重做练习 19,但是编写的是方法 chain<T>::merge。归并之后,两个输入链表a 和 为空
template<class T>
chain<T>& chain<T>::merge2(chain<T>& a, chain<T>& b)
{
	chainNode<T>* currentNodeA = a.firstNode;
	chainNode<T>* currentNodeB = b.firstNode;
	chainNode<T>* currentNodeC = firstNode;
	while(currentNodeA || currentNodeB)
	{
		if(currentNodeA == nullptr ||
		   currentNodeB != nullptr &&
		   currentNodeA->element > currentNodeB->element)
		{
			if(!firstNode)
			{
				firstNode = currentNodeB;
				currentNodeC = firstNode;
			}
			else
			{
				currentNodeC->next = currentNodeB;
				currentNodeC = currentNodeB;
			}
			currentNodeB = currentNodeB->next;
		}
		else if(currentNodeB == nullptr || 
			    currentNodeA != nullptr && 
			    currentNodeA->element <= currentNodeB->element)
		{
			if(!firstNode)
			{
				firstNode = currentNodeA;
				currentNodeC = firstNode;
			}
			else
			{
				currentNodeC->next = currentNodeA;
				currentNodeC = currentNodeA;
			}
			currentNodeA = currentNodeA->next;
		}
		lastNode = currentNodeC;
		listSize = a.listSize + b.listSize;
		a.firstNode = b.firstNode = nullptr;
		a.lastNode = b.lastNode = nullptr;
		a.listSize = b.listSize = 0;
		return *this;
	}
}

//ex22. 编写方法 chain<T>::split,它与练习 21 的函数类似。然而,它用输入链表 *this 的空间建
//立了链表a和b。
template<class T>
chain<T>& chain<T>::split(chain<T>& a, chain<T>& b)
{
	chainNode<T>* currentNode = firstNode;
	chainNode<T>* next = nullptr;
	chainNode<T>* currentNodeA = nullptr;
	chainNode<T>* currentNodeB = nullptr;

	for(int i = 0; i<listSize && currentNode;++i)
	{
		next = currentNode->next;
		if(i % 2 == 0)
		{
			if(i == 0)
			{
				a.firstNode = currentNode;
				currentNodeA = currentNode;
			}
			else
			{
				currentNodeA->next = currentNode;
				currentNodeA = currentNode;
				currentNodeA->next = nullptr;
			}
			a.listSize++;
		}
		else
		{
			if(i == 1)
			{
				b.firstNode = currentNode;
				currentNodeB = currentNode;
			}
			else
			{
				currentNodeB->next = currentNode;
				currentNodeB = currentNode;
				currentNodeB->next = nullptr;
			}
			b.listSize++;
		}
		currentNode = next;
	}
	firstNode = nullptr;
	listSize = 0;

	return *this;
}

//ex23. 在一个循环移动的操作中,线性表的元素根据给定的值,按顺时针方向移动。例如
//L=[0,1,2,3,4],循环移动 2 的结果是 L=[2,3,4,0,1]。
//1 ) 编写方法 circularShift(i),它将线性表的元素循环移动i 个位置。
//2 ) 测试你的代码。
template<class T>
chain<T>& chain<T>::circularShift(int index)
{
	if(listSize <= 1)
		return *this;
	for(int i = 0;i < index;i++)
	{
		lastNode->next = firstNode;
		lastNode = firstNode;
		firstNode = firstNode->next;
		lastNode->next = nullptr;
	}
	return *this;
}

//ex26. 编写方法 chain<T>::insertSort,它使用插入排序 ( 见程序 2-15 ) 对链表按非递减顺序重新排序。
//不开发新的节点,也不删除老的节点。可以假设元素的类型定义了关系操作符 (<、> 等)。
//1 ) 方法在最坏情况下的复杂度是多少? 如果元素已经有序,那么该方法需要多少时间?
//2 ) 使用自己的测试数据检验方法的正确性。
template<class T>
chain<T>& chain<T>::insertionSort()
{
	int len = listSize;
	if(len <= 1) return *this;
	for(int i = 2;i<=len;++i)
	{
		//寻找要插入的元素
		chainNode<T>* Elem = firstNode;
		chainNode<T>* ElemPre = nullptr;
		for(int j = 1;j < i;++j)
		{
			ElemPre = Elem;
			Elem = Elem->next;
		}
		//找到之前已经排序好的队列里第一个比要插入元素大的位置
		chainNode<T>* insertPos = firstNode;
		chainNode<T>* insertPrePos = nullptr;
		for(int j = 1;j < i && insertPos->element < Elem->element;++j)
		{
			insertPrePos = insertPos;
			insertPos = insertPos->next;
		}
   	    
		//把值Elem插入到currentMaxPreNode 和 insertPos
		//如果当前要插入元素是要插入的位置则跳过
		if(insertPos == Elem) 
			continue;
		//如果要插入的元素是首节点
		if(!ElemPre) 
			firstNode = Elem->next;
		else 
		{
			ElemPre->next = Elem->next;
			if(Elem == lastNode)
			{
				lastNode = ElemPre;
				ElemPre->next = nullptr;
			}
		}

		//如果要插的位置首节点的位置
		if(!insertPrePos)
			firstNode = Elem;
		else
		{
			insertPrePos->next = Elem;
		}
		Elem->next = insertPos;
	}
	return *this;
}	

template<class T>
chain<T>& chain<T>::bubbleSort()
{
	int len = listSize;
	if(len <= 1) return *this;

	//循环扫描链表len-1次如果前面的元素大于后面元素进行交换
	for(int i = len; i >= 2;--i)
	{
		chainNode<T>* currentPreNode = nullptr;
		chainNode<T>* currentNode = firstNode;
		chainNode<T>* currentNextNode = currentNode->next;
		while(currentNextNode)
		{
			if(currentNode->element > currentNextNode->element)
			{
				if(!currentPreNode) 
					firstNode = currentNextNode;
				else
					currentPreNode->next = currentNextNode;
				currentNode->next = currentNextNode->next;
				currentNextNode->next = currentNode;
			}
			currentPreNode = currentNode;
			currentNode = currentNextNode;
			currentNextNode = currentNode->next;
		}
		lastNode = currentNode;
	}
	return *this;
}

template<class T>
chain<T>& chain<T>::selectionSort()
{
	int len = listSize;
	if(len <= 1) return *this;
	for(int i = len;i >= 2;--i)
	{
		chainNode<T>* currentPreNode = firstNode;
		chainNode<T>* currentNode = firstNode->next;
		chainNode<T>* maxNode = currentPreNode;
		chainNode<T>* maxPreNode = nullptr;
		//寻找最大值
		for(int j = 2; j <= i;++j)
		{
			if(currentNode->element > maxNode->element)
			{
				maxNode = currentNode;
				maxPreNode = currentPreNode;
			}
			currentPreNode = currentNode;
			currentNode = currentNode->next;
		}
		if(currentPreNode == maxNode) continue;
		if(!maxPreNode)
			firstNode = maxNode->next;
		else
			maxPreNode->next = maxNode->next;
		currentPreNode->next = maxNode;
		maxNode->next = currentNode;
		if(i == len)
		{
			lastNode = maxNode;
			lastNode->next = nullptr;
		}
	}
	return *this;
}
// void rank(int rank[]);
// chain<T>& rankSort();


//每个箱子都以底部节点作为首节点,项部节点作为尾节点。每个箱子都有两个指
//针,分别存储在数组 bottom 和top 中,分别指向尾节点和头节点。bottom[theBin] 指向箱
//子theBin 的尾节点,而top[theBin] 指向箱子 theBin 的首节点。所有箱子开始为空表,这时
//bottom[theBin]=NULL。

template<class T>
chain<T>& chain<T>::binSort(int range)
{
	//对链表中的节点排序
	//创建并初始化箱子
	chainNode<T> **bottom,**top;
	bottom = new chainNode<T>* [range + 1];
	top = new chainNode<T>* [range + 1];
	for(int b = 0; b <= range; b++)
	{
		bottom[b] = NULL;
	}

	//把链表的节点分配到箱子
	//for 循环把输入链表的节点逐个插人相应的箱子顶
	for(;firstNode != NULL; firstNode = firstNode->next)
	{
		//把首节点 firstNode 加到箱子中
		//元素类型转换到整型 int
		int theBin = firstNode->element;
		//箱子为空
		if(bottom[theBin] == NULL)
		{
			bottom[theBin] = top[theBin] = firstNode;
		}
		else
		{
			//箱子不空
			top[theBin]->next = firstNode;
			top[theBin] = firstNode;
		}
	}
	//把箱子中的节点收集到有序链表
	// for 循环从第 0 个箱子开始,把非空的箱子依次链接起来,形成一个有序链表。
	chainNode<T> *y = NULL;
	for(int theBin = 0; theBin <= range; theBin++)
	{
		if(bottom[theBin] != NULL)
		{
			//箱子不空
			//第一个非空箱子
			if(y == NULL)
			{
				firstNode = bottom[theBin];
			}
			else //不是第一个非空箱子
			{
				y->next = bottom[theBin];
			}
			y = top[theBin];
		}
		if(y != NULL)
		{
			y->next = NULL;
		}
	}
	delete[] bottom;
	delete[] top;
	return *this;
}

//根据 棋牌的组号 样式 面值排序
template<class T>
chain<T>& chain<T>::binSort(int range,int (*func)(chainNode<T>*))
{
	//对链表中的节点排序
	//创建并初始化箱子
	chainNode<T> **bottom,**top;
	bottom = new chainNode<T>* [range + 1];
	top = new chainNode<T>* [range + 1];
	for(int b = 0; b <= range; b++)
	{
		bottom[b] = NULL;
	}

	//把链表的节点分配到箱子
	//for 循环把输入链表的节点逐个插人相应的箱子顶
	for(;firstNode != NULL; firstNode = firstNode->next)
	{
		//把首节点 firstNode 加到箱子中
		//元素类型转换到整型 int
		int theBin;
		if(!func)
		{
			theBin = firstNode->element;
		}
		else
		{
			//根据 组号 样式 面值排序
			theBin = func(firstNode);
		}
		//箱子为空
		if(bottom[theBin] == NULL)
		{
			bottom[theBin] = top[theBin] = firstNode;
		}
		else
		{
			//箱子不空
			top[theBin]->next = firstNode;
			top[theBin] = firstNode;
		}
	}
	//把箱子中的节点收集到有序链表
	// for 循环从第 0 个箱子开始,把非空的箱子依次链接起来,形成一个有序链表。
	chainNode<T> *y = NULL;
	for(int theBin = 0; theBin <= range; theBin++)
	{
		if(bottom[theBin] != NULL)
		{
			//箱子不空
			//第一个非空箱子
			if(y == NULL)
			{
				firstNode = bottom[theBin];
			}
			else //不是第一个非空箱子
			{
				y->next = bottom[theBin];
			}
			y = top[theBin];
		}
		if(y != NULL)
		{
			y->next = NULL;
		}
	}
	delete[] bottom;
	delete[] top;
	return *this;	
}

//例 6-1 假定对0 ~ 999 之间的 10 个整数进行排序。如果使用 range=1000 的箱子排序方
//法,那么箱子链表的初始化需要 1000 个执行步,节点分配需要 10 个执行步,从箱子中收集
//节点需要 1000 个执行步,总的执行步数为 2010。而基数排序方法是:
//1 ) 利用箱子排序方法,根据最低位数字 ( 即个位数字 ),对 10 个数进行排序。因为每个
//数字都在0 ~ 9之间,所以range=10。图 6-9a 是 10 个数的输入链表,图 6-9b 是按最低位数
//字排序后的链表。
//216->521->425->116->91->515->124->34->96->24 //a输入链表
//521->91->124->34->24->425->515->216->116->96 //b按照最后一位数字进行排序
//515->216->116->521->124->24->425->34->91->96 //c按照第二位数字进行排序
//24->34->91->96->116->124->216->425->515->521 //d按照最高位进行排序
//2 ) 利用箱子排序方法, 对 1 ) 的结果按次低位数字 ( 即十位数字 ) 进行排序。同样有
//range=10。因为箱子排序是稳定排序,所以次低位数字相同的节点,按最低位数字排序所得
//到的次序保持不变。因此,现在的链表是按照最后两位数字进行排序的。图 6-9c 是相应的排
//序结果
//3 ) 利用箱子排序方法,对 2 ) 的结果按第三位数字 ( 即百位数字 ) 进行排序。小于 100
//的数,第三位数字为 0。因为按第三位数字排序是稳定排序, 所以第三位数字相同的节点,
//按最后两位数字排序所得到的次序保持不变。因此,现在的链表是按照后三位数字进行排序
//的。图 6-9d 是相应的排序结果。
//上述基数排序方法是以 10 为基数,把数分解为十进制数字进行排序。因为每个数至少有
//三位数字,所以要进行三次排序,每次排序都使用 range=10 个箱子排序。每次箱子的初始化
//需要 10 个执行步,节点分配需要 10 个执行步,从箱子中收集节点需要 10 个执行步,总的执
//行步数为 90,比使用 range=1000 的箱子排序要少得多。单个箱子排序 (一数一个箱子的 ) 实
//际上等价于 一1000 的基数排序。                                                                   


//64. 设计一个方法,它应用基数排序思想给链表排序。
//1 ) 编写方法 chain<T>::radixSort(cd),它使用基数排序思想,按递增顺序给链表排序。方
//法的输入为 : 基数r、按基数r分解的数字的个数d。假设定义了从类型T到int的类
//型转换。方法的复杂度应为 O(d(r+n))。证明这个复杂度成立。
//2 ) 使用自己设计的测试数据来测试方法的正确性。
//3 ) 比较你的方法和基于链表的插入排序方法的性能。
//为此可使用m=100, 1000, 10 000;r=10 和 d=3。
template<class T>
chain<T>& chain<T>::radixSort(int r,int d)
{
	//基数r、按基数r分解的数字的个数d。
	//假设定义了从类型T到int的类型转换。
	chainNode<T>** bottom = nullptr;
	chainNode<T>** top = nullptr;
	bottom = new chainNode<T>* [r + 1];
	top = new chainNode<T>* [r + 1];

	for(int i = 0; i < d; i++)
	{
		for(int j = 0; j <= r; j++)
		{
			bottom[j] = nullptr;
		}
	
		while(firstNode)
		{
			int num = firstNode->element;
			for(int k = 0;k < i;k++)
				num /= r;
			num %= r;

			if(bottom[num] == nullptr)
			{
				bottom[num] = top[num] = firstNode;
			}
			else
			{
				top[num]->next = firstNode;
				top[num] = firstNode;
			}
			firstNode = firstNode->next;
		}
		chainNode<T>* end = nullptr;
		for(int j = 0; j <= r; j++)
		{
			if(bottom[j] == nullptr)
				continue;

			if(end == nullptr)
			{
				firstNode = bottom[j];
			}
			else
			{
				end->next = bottom[j];
			}
			end = top[j];
		}
		if(end != nullptr)
		{
			end->next = nullptr;
		}
	}
	delete[] bottom;
	delete[] top;
	return *this;
}


//ex66. 一堆部组卡片。每张卡片有三个域 : 卡片的组号、卡片的样式及卡片的面值。每组最多有
//52 张卡片(因为每一组可能有丢失的卡片 ),因此该堆卡片最多有 522 张。可以假设每组
//卡片至少有一张,因此卡片总数至少为n
//1 ) 按照组号对卡片进行排序,对组号相同的卡片按样式排序,对样式也相同的卡片按其
//面值排序。应该采用三次箱子排序过程来完成这种排序。
//2 ) 编写一段程序,输入为上和一个卡片堆,输出为有序的卡片。把卡片堆描述为一个链
//表,链表节点包含如下域 : deck、suit、face 和link。程序的复杂度应为 O(n),证明这
//个复杂度成立。
//3 ) 测试程序的正确性。
ostream& operator<<(ostream& out,const Card& x)
{
	out<<x.deck<<' '<<x.suit<<' '<<x.face<<endl;
	return out;
}

int getDeck(chainNode<Card>* node)
{
	return node->element.deck;
}

int getSuit(chainNode<Card>* node)
{
	return node->element.suit;
}

int getFace(chainNode<Card>* node)
{
	return node->element.face - 2;
}

chain<Card>& constructCards()
{
	constexpr int deckNum = 10;
	constexpr int suitNum = 4;
	constexpr int faceNum = 13;
	constexpr int cardNum = suitNum * faceNum;
	const int total = deckNum * cardNum;
	chain<Card>* card_chain = new chain<Card>();

	//創建排序好的牌
	for(int i = 0; i < deckNum;i++)
	{
		for(int j = 0; j < cardNum; j++)
		{
			int deck = i;
			Card::Suit suit = (Card::Suit)(j / faceNum);
			Card::Face face = (Card::Face)(j % faceNum + 2);
			Card card(deck,suit,face);
			card_chain->push_back(card);
		}
	}

	//隨機種子
	srand(time(0));

	//洗牌
	for(int i = 0; i < total; i++)
	{
		unsigned int from = rand() % total;
		unsigned int to = rand() % total;
		Card card = card_chain->get(from);
		card_chain->erase(from);
		card_chain->insert(to,card);
 	}

 	for(int i = 0;i < total / 10; i++)
 	{
 		unsigned int index = rand() % (total - i);
 		card_chain->erase(index);
 	}
 	return *card_chain;
}

int main()
{
	cout<<"----------------------size start--------------------"<<endl;
	chain<double> list1;
	cout<<list1.size()<<endl;
	cout<<"----------------------size end--------------------"<<endl;
	cout<<"----------------------insert start--------------------"<<endl;
	for(int i = 0;i < 10;i++)
	{
		list1.insert(0,i);
	}
	cout<<list1.size()<<endl;
	cout<<list1<<endl;
	cout<<"----------------------insert end--------------------"<<endl;
	cout<<"----------------------get start--------------------"<<endl;
	for(int i = 0;i < 10;i++)
	{
		cout<<list1.get(i)<<" ";
	}
	cout<<endl;
	cout<<"----------------------get end--------------------"<<endl;
	cout<<"----------------------erase start--------------------"<<endl;
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	for(int i = 0;i < 6;i++)
	{
		list1.erase(0);
	}
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	cout<<"----------------------erase end--------------------"<<endl;
	cout<<"----------------------push_back start--------------------"<<endl;
	cout<<list1<<endl;
	for(int i = 0;i < 10;i++)
	{
		list1.insert(3,i);
	}
	cout<<list1<<endl;
	cout<<list1.size()<<endl;

	for(int i = 0;i < 3;i++)
	{
		list1.push_back(1000);
	}
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	cout<<"----------------------push_back end--------------------"<<endl;
	cout<<"----------------------iterator start--------------------"<<endl;
	chain<double>::iterator iter;
	for(iter = list1.begin();iter != list1.end();iter++)
	{
		cout<<*iter<<" ";
	}
	cout<<endl;
	cout<<"----------------------iterator end--------------------"<<endl;
	cout<<"----------------------setSize start--------------------"<<endl;
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	list1.setSize(2);
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	cout<<"----------------------setSize end--------------------"<<endl;
	cout<<"----------------------set start--------------------"<<endl;
	for(int i = 0;i < 5;i++)
	{
		list1.push_back(i);
	}
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	list1.set(2,10000);
	cout<<list1.get(2)<<endl;
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	cout<<"----------------------set end--------------------"<<endl;
	cout<<"----------------------removeRange start--------------------"<<endl;
	cout<<list1.size()<<endl;
	cout<<list1<<endl;
	list1.removeRange(2,5);
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	cout<<"lastNode = "<<list1.getLastNode()<<endl;
	cout<<"----------------------removeRange end--------------------"<<endl;
	cout<<"----------------------lastIndexOf start--------------------"<<endl;
	cout<<list1.size()<<endl;
	cout<<list1<<endl;
	cout<<list1.lastIndexOf(3)<<endl;
	cout<<list1.size()<<endl;
	cout<<list1<<endl;
	cout<<"----------------------lastIndexOf end--------------------"<<endl;
	cout<<"----------------------operator[] start--------------------"<<endl;
	cout<<list1<<endl;
	cout<<list1.size()<<endl;
	for(int i = 0;i < list1.size();i++)
	{
		cout<<list1[i].element<<" "; 
	}
	cout<<endl;
	cout<<list1.size()<<endl;
	cout<<list1[0].element<<" "; 
	cout<<list1[1].element<<" "; 
	cout<<list1[2].element<<" "; 
	cout<<list1[3].element<<" "; 

	cout<<endl;
	for(int i = 0;i < list1.size();i++)
	{
		list1[i].element = i; 
	}
	cout<<list1<<endl;
	cout<<"----------------------operator[] end--------------------"<<endl;
	cout<<"----------------------operator== start--------------------"<<endl;
	chain<double> list2;
	for(int i = 0;i < 4;i++)
	{
		list2.push_back(i);
	}
	cout<<list2<<endl;
	cout<<(list2==list1)<<endl;
	cout<<"----------------------operator== end--------------------"<<endl;
	cout<<"----------------------operator!= start--------------------"<<endl;
	cout<<(list2!=list1)<<endl;
	cout<<"----------------------operator!= end--------------------"<<endl;
	cout<<"----------------------leftShift start--------------------"<<endl;
	cout<<list1<<endl;
	list1.leftShift(2);
	cout<<list1<<endl;
	cout<<"----------------------leftShift end--------------------"<<endl;
	cout<<"----------------------reverse start--------------------"<<endl;
	cout<<list2<<endl;
	cout<<list2.getLastNode()<<endl;
	list2.reverse();
	cout<<list2<<endl;
	cout<<list2.getLastNode()<<endl;
	cout<<"----------------------reverse end--------------------"<<endl;
	cout<<"----------------------meld1 start--------------------"<<endl;
	cout<<list1<<endl;
	cout<<list2<<endl;
	chain<double> list3; 
	list3.meld1(list1,list2);
	cout<<list3<<endl;
	cout<<list3.getLastNode()<<endl;
	cout<<list3.size()<<endl;
	cout<<"----------------------meld1 end--------------------"<<endl;
	cout<<"----------------------meld2 start--------------------"<<endl;
	cout<<list1<<endl;
	cout<<list2<<endl;
	chain<double> list4; 
	list4.meld2(list1,list2);
	cout<<list1<<endl;
	cout<<list2<<endl;
	cout<<list4<<endl;
	cout<<list4.getLastNode()<<endl;
	cout<<list4.size()<<endl;
	cout<<"----------------------meld2 end--------------------"<<endl;
	cout<<"----------------------split start--------------------"<<endl;
	cout<<list1<<endl;
	cout<<list2<<endl;
	cout<<list4<<endl;
	list4.split(list1,list2);
	cout<<list1<<endl;
	cout<<list2<<endl;
	cout<<list4<<endl;	
	cout<<"----------------------split end--------------------"<<endl;
	cout<<"----------------------circularShift start--------------------"<<endl;
	for(int i = 0;i < 10;i++)
	{
		list4.push_back(i);
	}
	cout<<list4<<endl;
	list4.circularShift(4);
	cout<<list4<<endl;
	cout<<"----------------------circularShift end--------------------"<<endl;
	cout<<"----------------------insertionSort start--------------------"<<endl;
	cout<<list4<<endl;
	cout<<list4.size()<<endl;
	list4.insertionSort();
	cout<<list4.size()<<endl;
	cout<<list4<<endl;	
	cout<<"----------------------bubbleSort start--------------------"<<endl;
	list4.circularShift(4);
	cout<<list4.size()<<endl;
	cout<<list4<<endl;
	list4.bubbleSort();
	cout<<list4<<endl;	
	cout<<"----------------------bubbleSort end--------------------"<<endl;
	cout<<"----------------------selectionSort start--------------------"<<endl;
	list4.circularShift(4);
	cout<<list4.size()<<endl;
	cout<<list4<<endl;
	list4.selectionSort();
	cout<<list4<<endl;
	cout<<list4.getLastNode();
	cout<<"----------------------selectionSort end--------------------"<<endl;
	cout<<"----------------------binSort start--------------------"<<endl;
	list4.circularShift(4);
	cout<<list4.size()<<endl;
	cout<<list4<<endl;
	list4.binSort(10);
	cout<<list4<<endl;
	cout<<list4.getLastNode();
	cout<<"----------------------binSort end--------------------"<<endl;
	cout<<"----------------------radixSort start--------------------"<<endl;
	list4.circularShift(4);
	list4.insert(3,216);
	list4.insert(3,521);
	list4.insert(3,425);
	list4.insert(3,116);
	list4.insert(3,91);
	list4.insert(3,515);
	list4.insert(3,124);
	list4.insert(3,34);
	list4.insert(3,96);
	list4.insert(3,24);
	cout<<list4.size()<<endl;
	cout<<list4<<endl;
	list4.radixSort(10,1);
	cout<<list4<<endl;
	list4.radixSort(10,2);
	cout<<list4<<endl;
	list4.radixSort(10,3);
	cout<<list4<<endl;
	list4.radixSort(10,4);
	cout<<list4<<endl;
	cout<<list4.getLastNode();
	cout<<"----------------------radixSort end--------------------"<<endl;
	cout<<"----------------------card start--------------------"<<endl;
	chain<Card> card_chain = constructCards();
	cout<<card_chain<<endl;
	//根据 组号 样式 面值排序
	card_chain.binSort(13,getFace);
	card_chain.binSort(4,getSuit);
	card_chain.binSort(10,getDeck);
	cout<<card_chain<<endl;
	cout<<"----------------------card end--------------------"<<endl;


	return 0;
}

测试输出

xz@xiaqiu:~/study/algorithm/c++/1/build$ ./test 
----------------------size start--------------------
0
----------------------size end--------------------
----------------------insert start--------------------
10
9 8 7 6 5 4 3 2 1 0 
----------------------insert end--------------------
----------------------get start--------------------
9 8 7 6 5 4 3 2 1 0 
----------------------get end--------------------
----------------------erase start--------------------
9 8 7 6 5 4 3 2 1 0 
10
3 2 1 0 
4
----------------------erase end--------------------
----------------------push_back start--------------------
3 2 1 0 
3 2 1 9 8 7 6 5 4 3 2 1 0 0 
14
3 2 1 9 8 7 6 5 4 3 2 1 0 0 1000 1000 1000 
17
----------------------push_back end--------------------
----------------------iterator start--------------------
3 2 1 9 8 7 6 5 4 3 2 1 0 0 1000 1000 1000 
----------------------iterator end--------------------
----------------------setSize start--------------------
3 2 1 9 8 7 6 5 4 3 2 1 0 0 1000 1000 1000 
17
3 2 
2
----------------------setSize end--------------------
----------------------set start--------------------
3 2 0 1 2 3 4 
7
10000
3 2 10000 1 2 3 4 
7
----------------------set end--------------------
----------------------removeRange start--------------------
7
3 2 10000 1 2 3 4 
3 2 3 4 
4
lastNode = 4
----------------------removeRange end--------------------
----------------------lastIndexOf start--------------------
4
3 2 3 4 
2
4
3 2 3 4 
----------------------lastIndexOf end--------------------
----------------------operator[] start--------------------
3 2 3 4 
4
3 2 3 4 
4
3 2 3 4 
0 1 2 3 
----------------------operator[] end--------------------
----------------------operator== start--------------------
0 1 2 3 
1
----------------------operator== end--------------------
----------------------operator!= start--------------------
0
----------------------operator!= end--------------------
----------------------leftShift start--------------------
0 1 2 3 
2 3 
----------------------leftShift end--------------------
----------------------reverse start--------------------
0 1 2 3 
3
3 2 1 0 
0
----------------------reverse end--------------------
----------------------meld1 start--------------------
2 3 
3 2 1 0 
2 3 3 2 1 0 
0
6
----------------------meld1 end--------------------
----------------------meld2 start--------------------
2 3 
3 2 1 0 


2 3 3 2 1 0 
0
6
----------------------meld2 end--------------------
----------------------split start--------------------


2 3 3 2 1 0 
2 3 1 
3 2 0 

----------------------split end--------------------
----------------------circularShift start--------------------
0 1 2 3 4 5 6 7 8 9 
4 5 6 7 8 9 0 1 2 3 
----------------------circularShift end--------------------
----------------------insertionSort start--------------------
4 5 6 7 8 9 0 1 2 3 
10
10
0 1 2 3 4 5 6 7 8 9 
----------------------bubbleSort start--------------------
10
4 5 6 7 8 9 0 1 2 3 
0 1 2 3 4 5 6 7 8 9 
----------------------bubbleSort end--------------------
----------------------selectionSort start--------------------
10
4 5 6 7 8 9 0 1 2 3 
0 1 2 3 4 5 6 7 8 9 
9----------------------selectionSort end--------------------
----------------------binSort start--------------------
10
4 5 6 7 8 9 0 1 2 3 
0 1 2 3 4 5 6 7 8 9 
3----------------------binSort end--------------------
----------------------radixSort start--------------------
20
0 1 2 24 96 34 124 515 91 116 425 521 216 3 
0 1 91 521 2 3 24 34 124 515 425 96 116 216 
0 1 2 3 515 116 216 521 24 124 425 34 91 96 
0 1 2 3 24 34 91 96 116 124 216 425 515 521 
0 1 2 3 24 34 91 96 116 124 216 425 515 521 
3----------------------radixSort end--------------------
----------------------card start--------------------
2 2 13
 0 0 2
 2 2 11
 0 2 7
 8 3 8
 0 0 5
 0 0 8
 4 3 4
 3 1 8
 6 0 6
 7 0 14
 1 1 4
 8 3 3
 0 0 12
 0 0 13
 0 1 3
 4 2 6
 9 3 14
 7 0 12
 8 3 9
 3 0 9
 1 0 7
 7 2 13
 0 2 2
 0 2 4
 8 2 11
 8 1 14
 6 3 6
 0 2 5
 0 2 6
 1 2 4
 8 0 8
 4 2 12
 8 0 9
 8 0 13
 6 0 7
 0 2 12
 4 0 2
 0 3 5
 0 3 8
 7 3 11
 3 1 5
 8 1 10
 9 1 12
 0 3 10
 1 0 2
 7 2 8
 9 1 10
 1 0 5
 1 0 8
 3 2 8
 6 2 14
 0 3 11
 1 0 9
 1 0 10
 5 0 14
 4 1 2
 1 0 13
 5 0 4
 1 1 2
 6 0 14
 1 2 2
 1 1 5
 1 2 5
 1 2 6
 1 2 7
 7 1 13
 9 1 5
 1 2 8
 1 2 9
 1 2 12
 4 1 5
 6 0 9
 6 0 2
 2 3 9
 1 3 2
 1 3 4
 1 1 9
 1 3 5
 1 3 8
 5 0 2
 2 2 9
 9 2 6
 4 3 13
 8 1 3
 1 3 11
 2 3 3
 9 3 8
 2 0 2
 3 2 6
 2 0 4
 5 3 12
 3 3 5
 2 0 5
 3 3 2
 2 0 7
 4 3 9
 2 0 9
 2 0 11
 7 2 9
 2 3 13
 2 0 13
 2 0 14
 0 2 8
 4 0 9
 3 2 13
 1 1 11
 9 0 6
 2 1 6
 5 3 5
 3 1 9
 7 3 2
 2 1 7
 2 1 8
 2 1 9
 0 3 9
 2 1 10
 8 3 5
 2 1 13
 1 3 14
 2 2 2
 2 2 3
 2 2 4
 2 2 5
 3 1 4
 8 0 14
 2 1 12
 7 1 4
 0 2 13
 9 2 2
 9 3 6
 1 3 3
 2 3 2
 2 3 5
 2 1 5
 0 0 14
 4 0 11
 5 1 5
 5 0 11
 4 3 3
 4 1 11
 3 2 5
 4 0 13
 4 0 10
 2 3 11
 3 0 11
 0 2 3
 1 0 12
 2 0 8
 0 3 7
 3 0 2
 3 0 3
 7 2 5
 3 0 4
 0 1 14
 3 3 11
 5 1 10
 0 3 12
 4 3 11
 1 1 6
 3 0 12
 8 1 11
 3 1 7
 9 1 2
 1 0 6
 8 1 9
 6 1 11
 0 1 6
 3 1 14
 4 0 8
 8 0 11
 3 2 2
 9 2 13
 9 1 4
 1 0 4
 3 2 4
 4 3 10
 6 2 7
 0 0 3
 0 0 9
 0 1 10
 3 2 11
 8 1 6
 4 0 14
 7 3 3
 3 3 3
 7 3 9
 3 3 14
 3 3 4
 2 1 11
 1 1 14
 3 3 6
 6 0 12
 8 2 12
 7 2 12
 1 3 9
 3 0 14
 0 1 5
 3 3 10
 3 3 13
 4 0 3
 5 0 3
 1 0 14
 4 0 4
 4 1 3
 4 1 4
 1 3 6
 7 2 14
 9 0 12
 3 2 9
 4 1 6
 3 3 9
 4 1 10
 4 1 13
 4 1 14
 5 3 14
 5 2 5
 4 2 3
 4 1 7
 4 2 4
 4 2 5
 6 1 2
 9 3 7
 4 2 7
 4 2 9
 2 2 6
 4 2 10
 1 1 8
 4 2 11
 7 3 5
 4 2 13
 0 1 7
 9 0 5
 1 0 11
 4 3 5
 0 1 12
 2 2 12
 5 2 12
 6 1 14
 4 3 14
 5 1 4
 5 0 5
 5 0 6
 3 2 3
 2 1 3
 5 0 12
 4 3 2
 6 3 3
 5 1 7
 5 1 9
 2 1 2
 1 2 13
 4 1 12
 4 1 8
 5 2 6
 5 2 7
 0 1 2
 5 2 9
 1 1 7
 5 2 11
 2 3 8
 6 3 5
 8 2 5
 5 2 13
 5 3 9
 5 2 14
 3 3 12
 3 0 7
 5 3 3
 5 3 4
 7 0 2
 7 2 6
 5 3 8
 6 3 8
 5 3 13
 5 0 8
 0 3 2
 7 1 12
 7 1 3
 4 0 5
 6 2 3
 9 0 11
 6 3 4
 5 2 2
 6 0 10
 8 0 2
 6 0 11
 6 0 4
 3 3 8
 2 3 10
 0 3 14
 6 3 2
 5 0 7
 6 1 3
 8 2 2
 0 0 4
 5 1 13
 2 2 14
 3 2 14
 6 1 7
 6 3 13
 6 1 8
 6 1 12
 0 1 4
 7 1 9
 0 3 3
 9 2 8
 6 2 5
 8 0 12
 6 2 9
 5 1 2
 6 2 10
 1 1 13
 3 1 13
 8 2 3
 6 2 12
 6 3 7
 6 2 8
 6 3 11
 6 3 12
 7 0 3
 6 0 5
 7 0 6
 2 3 4
 7 0 8
 7 0 5
 9 0 3
 7 0 10
 9 3 11
 6 2 6
 6 2 11
 7 0 11
 6 1 6
 7 0 13
 0 2 9
 5 3 10
 7 1 2
 6 0 13
 1 2 11
 7 1 6
 6 3 10
 7 3 8
 7 1 7
 6 1 9
 7 1 5
 7 1 8
 7 1 10
 4 2 8
 3 2 12
 3 3 7
 3 1 11
 0 0 7
 7 0 4
 3 0 10
 6 1 13
 7 2 7
 1 3 7
 7 2 10
 8 3 4
 7 2 11
 5 0 13
 6 3 14
 1 3 10
 1 2 3
 7 3 4
 2 0 10
 1 2 10
 0 2 10
 7 3 6
 7 3 7
 9 1 7
 2 2 8
 5 3 11
 1 2 14
 7 3 10
 7 3 12
 5 3 7
 7 3 14
 6 2 13
 8 0 3
 6 0 3
 8 0 7
 7 0 9
 8 0 10
 2 3 7
 8 1 2
 9 2 3
 8 1 5
 8 1 8
 8 0 6
 9 1 6
 8 1 13
 8 2 4
 3 1 3
 8 2 6
 1 1 12
 8 2 7
 8 0 4
 8 2 8
 5 3 2
 1 0 3
 2 3 6
 0 1 13
 8 2 10
 8 2 13
 8 2 14
 8 3 2
 9 3 2
 5 1 12
 2 1 14
 8 3 11
 0 1 8
 8 3 6
 8 3 7
 8 3 12
 9 1 3
 0 3 6
 8 3 13
 8 3 14
 7 1 14
 9 2 14
 5 2 8
 9 0 2
 9 0 7
 5 1 14
 9 0 10
 2 0 6
 6 1 4
 9 0 13
 7 2 3
 3 1 12
 0 1 9
 0 3 4
 0 0 10
 1 1 10
 3 0 8
 4 0 6
 2 3 12
 9 1 9
 7 3 13
 3 0 5
 3 2 10
 9 3 3
 9 1 13
 5 1 6
 9 2 7
 9 1 14
 3 0 13
 3 1 6
 8 0 5
 9 2 5
 3 0 6
 9 0 4
 4 3 7
 9 2 9
 8 3 10
 9 2 11
 9 2 12
 9 0 8
 2 2 7
 9 3 4
 9 3 5
 9 3 12
 5 1 8
 9 3 13
 0 2 11
 5 0 9
 5 1 11
 
0 0 2
 0 0 3
 0 0 4
 0 0 5
 0 0 7
 0 0 8
 0 0 9
 0 0 10
 0 0 12
 0 0 13
 0 0 14
 0 1 2
 0 1 3
 0 1 4
 0 1 5
 0 1 6
 0 1 7
 0 1 8
 0 1 9
 0 1 10
 0 1 12
 0 1 13
 0 1 14
 0 2 2
 0 2 3
 0 2 4
 0 2 5
 0 2 6
 0 2 7
 0 2 8
 0 2 9
 0 2 10
 0 2 11
 0 2 12
 0 2 13
 0 3 2
 0 3 3
 0 3 4
 0 3 5
 0 3 6
 0 3 7
 0 3 8
 0 3 9
 0 3 10
 0 3 11
 0 3 12
 0 3 14
 1 0 2
 1 0 3
 1 0 4
 1 0 5
 1 0 6
 1 0 7
 1 0 8
 1 0 9
 1 0 10
 1 0 11
 1 0 12
 1 0 13
 1 0 14
 1 1 2
 1 1 4
 1 1 5
 1 1 6
 1 1 7
 1 1 8
 1 1 9
 1 1 10
 1 1 11
 1 1 12
 1 1 13
 1 1 14
 1 2 2
 1 2 3
 1 2 4
 1 2 5
 1 2 6
 1 2 7
 1 2 8
 1 2 9
 1 2 10
 1 2 11
 1 2 12
 1 2 13
 1 2 14
 1 3 2
 1 3 3
 1 3 4
 1 3 5
 1 3 6
 1 3 7
 1 3 8
 1 3 9
 1 3 10
 1 3 11
 1 3 14
 2 0 2
 2 0 4
 2 0 5
 2 0 6
 2 0 7
 2 0 8
 2 0 9
 2 0 10
 2 0 11
 2 0 13
 2 0 14
 2 1 2
 2 1 3
 2 1 5
 2 1 6
 2 1 7
 2 1 8
 2 1 9
 2 1 10
 2 1 11
 2 1 12
 2 1 13
 2 1 14
 2 2 2
 2 2 3
 2 2 4
 2 2 5
 2 2 6
 2 2 7
 2 2 8
 2 2 9
 2 2 11
 2 2 12
 2 2 13
 2 2 14
 2 3 2
 2 3 3
 2 3 4
 2 3 5
 2 3 6
 2 3 7
 2 3 8
 2 3 9
 2 3 10
 2 3 11
 2 3 12
 2 3 13
 3 0 2
 3 0 3
 3 0 4
 3 0 5
 3 0 6
 3 0 7
 3 0 8
 3 0 9
 3 0 10
 3 0 11
 3 0 12
 3 0 13
 3 0 14
 3 1 3
 3 1 4
 3 1 5
 3 1 6
 3 1 7
 3 1 8
 3 1 9
 3 1 11
 3 1 12
 3 1 13
 3 1 14
 3 2 2
 3 2 3
 3 2 4
 3 2 5
 3 2 6
 3 2 8
 3 2 9
 3 2 10
 3 2 11
 3 2 12
 3 2 13
 3 2 14
 3 3 2
 3 3 3
 3 3 4
 3 3 5
 3 3 6
 3 3 7
 3 3 8
 3 3 9
 3 3 10
 3 3 11
 3 3 12
 3 3 13
 3 3 14
 4 0 2
 4 0 3
 4 0 4
 4 0 5
 4 0 6
 4 0 8
 4 0 9
 4 0 10
 4 0 11
 4 0 13
 4 0 14
 4 1 2
 4 1 3
 4 1 4
 4 1 5
 4 1 6
 4 1 7
 4 1 8
 4 1 10
 4 1 11
 4 1 12
 4 1 13
 4 1 14
 4 2 3
 4 2 4
 4 2 5
 4 2 6
 4 2 7
 4 2 8
 4 2 9
 4 2 10
 4 2 11
 4 2 12
 4 2 13
 4 3 2
 4 3 3
 4 3 4
 4 3 5
 4 3 7
 4 3 9
 4 3 10
 4 3 11
 4 3 13
 4 3 14
 5 0 2
 5 0 3
 5 0 4
 5 0 5
 5 0 6
 5 0 7
 5 0 8
 5 0 9
 5 0 11
 5 0 12
 5 0 13
 5 0 14
 5 1 2
 5 1 4
 5 1 5
 5 1 6
 5 1 7
 5 1 8
 5 1 9
 5 1 10
 5 1 11
 5 1 12
 5 1 13
 5 1 14
 5 2 2
 5 2 5
 5 2 6
 5 2 7
 5 2 8
 5 2 9
 5 2 11
 5 2 12
 5 2 13
 5 2 14
 5 3 2
 5 3 3
 5 3 4
 5 3 5
 5 3 7
 5 3 8
 5 3 9
 5 3 10
 5 3 11
 5 3 12
 5 3 13
 5 3 14
 6 0 2
 6 0 3
 6 0 4
 6 0 5
 6 0 6
 6 0 7
 6 0 9
 6 0 10
 6 0 11
 6 0 12
 6 0 13
 6 0 14
 6 1 2
 6 1 3
 6 1 4
 6 1 6
 6 1 7
 6 1 8
 6 1 9
 6 1 11
 6 1 12
 6 1 13
 6 1 14
 6 2 3
 6 2 5
 6 2 6
 6 2 7
 6 2 8
 6 2 9
 6 2 10
 6 2 11
 6 2 12
 6 2 13
 6 2 14
 6 3 2
 6 3 3
 6 3 4
 6 3 5
 6 3 6
 6 3 7
 6 3 8
 6 3 10
 6 3 11
 6 3 12
 6 3 13
 6 3 14
 7 0 2
 7 0 3
 7 0 4
 7 0 5
 7 0 6
 7 0 8
 7 0 9
 7 0 10
 7 0 11
 7 0 12
 7 0 13
 7 0 14
 7 1 2
 7 1 3
 7 1 4
 7 1 5
 7 1 6
 7 1 7
 7 1 8
 7 1 9
 7 1 10
 7 1 12
 7 1 13
 7 1 14
 7 2 3
 7 2 5
 7 2 6
 7 2 7
 7 2 8
 7 2 9
 7 2 10
 7 2 11
 7 2 12
 7 2 13
 7 2 14
 7 3 2
 7 3 3
 7 3 4
 7 3 5
 7 3 6
 7 3 7
 7 3 8
 7 3 9
 7 3 10
 7 3 11
 7 3 12
 7 3 13
 7 3 14
 8 0 2
 8 0 3
 8 0 4
 8 0 5
 8 0 6
 8 0 7
 8 0 8
 8 0 9
 8 0 10
 8 0 11
 8 0 12
 8 0 13
 8 0 14
 8 1 2
 8 1 3
 8 1 5
 8 1 6
 8 1 8
 8 1 9
 8 1 10
 8 1 11
 8 1 13
 8 1 14
 8 2 2
 8 2 3
 8 2 4
 8 2 5
 8 2 6
 8 2 7
 8 2 8
 8 2 10
 8 2 11
 8 2 12
 8 2 13
 8 2 14
 8 3 2
 8 3 3
 8 3 4
 8 3 5
 8 3 6
 8 3 7
 8 3 8
 8 3 9
 8 3 10
 8 3 11
 8 3 12
 8 3 13
 8 3 14
 9 0 2
 9 0 3
 9 0 4
 9 0 5
 9 0 6
 9 0 7
 9 0 8
 9 0 10
 9 0 11
 9 0 12
 9 0 13
 9 1 2
 9 1 3
 9 1 4
 9 1 5
 9 1 6
 9 1 7
 9 1 9
 9 1 10
 9 1 12
 9 1 13
 9 1 14
 9 2 2
 9 2 3
 9 2 5
 9 2 6
 9 2 7
 9 2 8
 9 2 9
 9 2 11
 9 2 12
 9 2 13
 9 2 14
 9 3 2
 9 3 3
 9 3 4
 9 3 5
 9 3 6
 9 3 7
 9 3 8
 9 3 11
 9 3 12
 9 3 13
 9 3 14
 
----------------------card end--------------------
xz@xiaqiu:~/study/algorithm/c++/1/build$ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值