---恢复内容开始---
我是一个c++和数据结构的初学者,本文主要是把清华大学出版社的数据结构(用面向对象方法与c++语言描述)(第2版)这本书中第二章线性表的源码抄下来,在学习的过程中有助于加深印象,书中代码很全,介绍的很仔细。
因为模板类的函数定义好像不能放在源文件中,所以我将代码全部放在一个头文件中(linearList.h),因为使用的少,不好验证代码的可行性,里面可能存在很多错误,如果有朋友发现,希望指出,我好进一步改正。
整个头文件逻辑结构如图
因为关于线性表的理论知识比较简单,百度一下一大片,下面就直接上代码,虽然我把它们都放在了同一个头文件中,但是为了后面可以看的更舒服,这里面按照模块把它们拆开
1.线性表类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//线性表抽象类 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; //查 virtual int Locate(int i)const = 0; virtual bool getData(int i, T& x)const = 0; virtual void setData(int i, T& x) = 0; //改 virtual bool Insert(int i, T&x) = 0; //增 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; };
里面似乎少了一个删除操作,不过这只是个父类,在各子类里面都有对应的删除操作,感觉这段代码实际上没什么大用,主要就是向读者说明一般的线性表类应该具备哪些操作。
2.顺序表(顺序表就是个数组,用起来局限性很大)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//顺序表类 const int defaultSize = 100; template<class T> class SeqList: public LinearList<T>{ protected: T* data; int maxSize; int last; void reSize(int newSize); 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; int Locate(int i)const; bool getData(int i, T&x)const { if (i > 0 && i < last + 1) { x = data[i - 1]; return true; } else return false; } void setData(int i, T&x) { if (i > 0 && i <= last + 1) data[i - 1] = x; } bool Insert(int i, T&x); bool Remove(int i, T&x); bool IsEmpty() { return(last == -1 ? true : false;) } bool IsFull() { return (last == maxSize - 1) ? true : false; } void input(); void output(); SeqList<T> operator=(SeqList<T>&L); }; //顺序表(SeqList)类定义 //构造函数 template<class T> SeqList<T>::SeqList(int sz) { if (sz > 0) { maxSize = sz; last = -1; data = new T[maxSize]; if (data = NULL) { cerr << "存储分配错误!" << endl; exit(1); } } }; //复制构造函数 template<class T> SeqList<T>::SeqList(SeqList<T>& L) { maxSize = L.Size; last = L.length() - 1; T value; data = new T[maxSize]; if(data = NULL) { cerr << "存储分配错误!" << endl; exit(1); } for (int i = 1; i <= last + 1; i++) { L.getData(i, value); data[i - 1] = value; } }; template<class T> void SeqList<T>::reSize(int newSize) { if (newSize <= 0) { cerr << "无效的数组大小" << endl; return; } if (newSize != maxSize) { T* newarray = new T[newSize]; if (newarray == NULL) { cerr << "存储分配错误!" << endl; exit(1); } int n = last + 1; T* srcptr = data; T* destptr = newarray; while (n--) *destptr++ = *srcptr++; delete[]data; data = newarray; maxSize = newSize; } }; //搜索操作 template<class T> int SeqList<T>::Search(T& x)const { for (int i = 0; i <= last; i++) { if (data[i] == x)return x + 1; } return 0; }; //定位操作 template<class T> int SeqList<T>::Locate(int i)const { if (i >= 1 && i <= last + 1)return i; else return 0; }; //插入操作 template<class T> bool SeqList<T>::Insert(int i, T& x) { if (last == maxSize - 1)return false; if (i < 0 || i>last + 1)return false; for (int j = last; j >= i; j--) data[j + 1] = data[j]; data[i] = x; last++; return true; }; //删除操作 template<class T> bool SeqList<T>::Remove(int i, T&x) //删除data[i-1],并将x赋值data[i-1] { if (last == -1)return false; if (i<1!!i>last + 1)return false; x = data[i - 1]; for (int j = i; j <= last; j++) { data[j - 1] = data[j]; } last--; return true; }; //输入 template<class T> void SeqList<T>::input() { cout << "开始建立顺序表,请输入表中元素个数:"; while (1) { cin >> last; last--; if (last <= maxSize - 1)break; cout << "表元素个数输入有误,范围不能超过" << maxSize - 1 << ":"; } for (int i = 0; i <= last; i++) { cin >> data[i]; cout << i + 1 << endl; } }; //输出 template<class T> void SeqList<T>::output() { cout << "顺序表当前元素的最后位置(last)为:" << last << endl; for (int i = 0; i <= last; i++) { cout << "#" << i + 1: << ":" << data[i] << endl; } }; //赋值 template<class T> SeqList<T> SeqList<T>::operator=(SeqList<T>&L) { maxSize = L.Size; last = L.length() - 1; T value; data = new T[maxSize]; if (data = NULL) { cerr << "存储分配错误!" << endl; exit(1); } for (int i = 1; i <= last + 1; i++) { L.getData(i, value); data[i - 1] = value; } }; //顺序表抽象类定义结束 //顺序表求并集 template<class T> void SeqList_union(SeqList<T>& LA, SeqList<T>& LB) //求顺序表的并集,并集结果为存储在LA { int n = LA.length(), m = LB.length(), i, k, x; for (i = 1; i <= m; i++) { LB.getData(i, x); k = LA.Search(x); if (k == 0) { LA.Insert(n, x); n++; } } }; //顺序表求交集 template<class T> void SeqList_intersection(SeqList<T>& LA, SeqList<T>& LB) //求顺序表的交集,交集结果存储在LA { int n = LA.Length(), m = LB.Length(), i = 1, k, x; while (i <= n) { LA.getData(i, x); k = LB.Search(x); if (k == 0) { LA.Remove(i, x); n--; } else i++; } };
3.带附加头结点的单链表(我最喜欢的,感觉最有用的)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//单链表类 //带附加头结点的单链表类定义 //结点类定义 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 LinearList<T> { public: List() { first = new LinkNode<T>; } List(const T& x) { first = new LinkNode<T>(x); } List(List<T>& L); ~List() { makeEmpty(); } void makeEmpty(); int Length() const; LinkNode<T>* getHead()const { return first; } LinkNode<T>* Search(T& x)const; LinkNode<T>* Locate(int i)const; bool getData(int i, T& x)const; void setData(int i, T&x); bool Insert(int i, T&x); bool Remove(int i, T&x); bool IsEmpty()const { return first->link == Null ? true : false; } bool IsFull()const { return false; } //void Sort(); //void input(); void output(); List<T>& operator= (List<T>&L); void inputFront(T endTag); void inputRear(T endTag); protected: LinkNode<T> *first; }; //单链表类(List)定义 //复制构造函数 template<class T > List<T>::List(List<T>&L) { T value; LinkNode<T>*srcptr = L.getHead(); LinkNode<T>*destptr = first = new LinkNode<T>; while (srcptr->link != NULL) { value = srcptr->link->data; destptr->link = new LinkNode<T>(value); destptr = destptr->link; srcptr = srcptr->link; } destptr->link = NULL; }; //将链表置空 template<class T> void List<T>::makeEmpty() { LinkNode<T> *q; while (first->link != NULL) { q = first->link; first->link = q->link; delete q; } }; //计算单链表长度 template<class T> int List<T>::Length()const { LinkNode<T>*p = first->link; int count = 0; while (p != NULL) { p = p->link; count++; } return count; } //搜索操作 template<class T> LinkNode<T> *List<T>::Search(T& x)const { LinkNode<T>*current = first->link; while (current != NULL) { if (current->data == x) break; else current = current->link; } return current; }; //定位操作 template<class T> LinkNode<T>* List<T>::Locate(int i)const { if (i < 0) return NULL; LinkNode<T>*current = first; int k = 0; while (current != NULL&&k < i) { current = current->link; k++; } return current; }; //取值操作 template<class T> bool List<T>::getData(int i, T&x)const { if (i <= 0) return NULL; LinkNode<T>*current = Locate(i); if (current == NULL)return false; else { x = current->data; return true; } }; //修改操作 template<class T> void List<T>::setData(int i, T& x) { if (i <= 0) return; LinkNode<T>* current = Locate(i); if (current == NULL) return; else current->data = x; }; //插入操作 template<class T> bool List<T>::Insert(int i, T& x) { LinkNode<T>* current = Locate(i); if (current == NULL) return false; LinkNode<T>* newNode = new LinkNode<T>(x); if (newNode == NULL) { cerr << "存储分配错误!" << endl; exit(1); } newNode->link = current->link; current->data = newNode; return true; }; //删除操作 template<class T> bool List<T>::Remove(int i, T&x) { LinkNode<T>* current = Locate(i - 1); if (current == NULL || current->link == NULL) return false; LinkNode<T>*del = current->link; current->link = del->link; x = del->data; delete del; return true; }; //输出 template<class T> void List<T>::output() { LinkNode<T> * current = first->link; while (current != NULL) { cout << current->data << endl; current = current->link; } }; //赋值操作 template<class T> List<T>& List<T>::operator=(List<T>& L) { T value; LinkNode<T>* srcptr = L.getHead(); LinkNode<T>* destptr = first = new LinkNode<T>; while (srcptr->link != NULL) { value = srcptr->link->data; destptr->link = new LinkNode<T>(value); destptr = destptr->link; srcptr = srcptr->link; } destptr->link = NULL; return *this; }; //前插法建立单链表 template<class T> void List<T>::inputFront(T endTag) //endTag为输入序列结束标志 { LinkNode<T>* newNode; T val; makeEmpty(); cin >> val; while (val != endTag) { newNode = new LinkNode<T>(val); if (newNode == NULL) { cerr << "存储分配错误!" << endl; exit(1); } newNode->link = first->link; first->link = newNode; cin >> val; } }; //后插法建立单链表 template<class T> void List<T>::inputRear(T endTag) { LinkNode<T>* newNode, *last; T val; makeEmpty(); cin >> val; last = first; while (val != endTag) { newNode = new LinkNode<T>(val); if (newNode == NULL) { cerr << "内存分配错误!" << endl; exit(1); } last->link = newNode; last = newNode; cin >> val; } last->link = NULL; }
4.循环链表(循环链表书中只有类的声明,具体的函数实现是自己写的,所以可能存在错误)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//循环链表类定义 template<class T> struct CircLinkNode //链表结点类定义 { T data; CircLinkNode<T> *link; CircLinkNode(CircLinkNode<T> *next = NULL) :link(next) {} CircLinkNode(T d, CircLinkNode<T> *next = NULL) :data(d), link(next) {} }; template<class T> class CircList //链表类定义 { public: CircList(); CircList(CircList<T>& L); ~CircList(); int Length()const; bool IsEmpty() { return first->link == first ? true : false; } CircLinkNode<T> *getHead() const {return first}; void setHead(CircLinkNode<T> *p); CircLinkNode<T> *Search(T x); CircLinkNode<T> *Locate(int i); T* getData(int i); void setData(int i, T&x); bool Insert(int i, T&x); bool Remove(int i, T&x); CircLinkNode<T> *first, *last; }; //循环链表 template<class T> CircList<T>::CircList() { first = new CircLinkNode<T>; if (first == NULL) { cerr << "内存分配错误!" << endl; exit(1); } first->link = first; last = first->link; } template<class T> CircList<T>::CircList(CircList<T>& L) { T value; CircLinkNode<T> *srcptr0 = L.getHead(); CircLinkNode<T> *srcptr = srcptr0->link; CircLinkNode<T> *destptr = first = new CircLinkNode<T>; if(first==NULL) { cerr << "内存分配错误!" << endl; exit(1); } while (srcptr->link != srcptr0->link) { value = srcptr->data destptr->link = new CircLinkNode<T>(value); if (destptr->link == NULL) { cerr << "内存分配错误!" << endl; exit(1); } srcptr = srcptr->link; destptr = destptr->link; } last= destptr->link; last->link = first->link; } template<class T> CircList<T>::~CircList() { CircLinkNode<T> *q; while (first->link != NULL) { q = first->link; if (q->link != q) { first->link = q->link; delete q; } else { delete q; first->link = NULL; } } } template<class T> int CircList<T>::Length()const { CircLinkNode<T> *q = first->link; int count = 1; while (q->link != first->link) { q = q->link; count++; } return count; } template<class T> void CircList<T>::setHead(CircLinkNode<T> *p) { p->link = first->link; first = p; } template<class T> CircLinkNode<T> *CircList<T>::Search(T x) { CircLinkNode<T>* current = first->link; if (current->data == x) return current; current = current->link; while (current != first->link) { if (current->data == x) { return current; } else current = current->link; } return NULL; } template<class T> CircLinkNode<T> *CircList<T>::Locate(int i) { if (i < 0) return NULL; CircLinkNode<T>* current = first; int k = 0; while (k < i) { current = current->link; k++; } return current; } template<class T> T* CircList<T>::getData(int i) { if (i <= 0) return NULL; CircLinkNode<T>* current = Locate(i); return current->data; } template<class T> void CircList<T>::setData(int i, T&x) { if (i <= 0) return NULL; CircLinkNode<T>* current = Locate(i); current->data = x; } template<class T> bool CircList<T>::Insert(int i, T&x) { if (i < 0) return NULL; else if (i == 0) { CircLinkNode<T>* current = first; if (current == NULL) return false; CircLinkNode<T> *newNode = new CircLinkNode<T>(x); if (newNode == NULL) { cerr << "内存分配错误!" << endl; exit(1); } current->link = newNode; newNode->link = newNode; if (current == last) last = newNode; return true; } else { CircLinkNode<T>* current = Locate(i); if (current == NULL) return false; CircLinkNode<T> *newNode = new CircLinkNode<T>(x); if (newNode == NULL) { cerr << "内存分配错误!" << endl; exit(1); } newNode->link = current->link; current->link = newNode; if (current == last) last = newNode; return true; } } template<class T> bool CircList<T>::Remove(int i, T&x) { if (i <= 0) return NULL; CircLinkNode<T>* current = Locate(i-1); if (current == NULL) return false; CircLinkNode<T> *del = current->link; current->link = del->link; if (del == last) last = current; x = del->data; delete del; return true; }
书中带附加头结点的循环链表是这样的
个人不太喜欢循环链表带附加头结点,因为在求解约瑟夫问题(后面有该问题的代码测试)时感觉这个附加头结点很烦人,又奈何书中在循环链表类的定义中又有与附加头结点相关的操作,所以我把循环链表设计成了
反正只是为了学习,真正的好不好用不需要太在意,根本用不上。
求解约瑟夫问题:一个旅行社要从n个旅客中选出一名旅客,为他提供免费的环球旅行服务。旅行社安排这些旅客围成一个圆圈,从帽子中取出一张纸条,用上面写的正整数m(<n)作为报数值。游戏进行时,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数,报m的人被淘汰出列,然后下一个人重新报数,如此下去直到圆圈中只剩下一个人,这个人为胜利者。假设n=8,m=3,则出列顺序为3,6,1,5,2,8,4,最后7号胜出。
约瑟夫函数
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//求解Josephus问题 template<class T> void Josephus(CircList<T>& Js, int n, int m) { CircLinkNode<T> *p = Js.Locate(1), *pre = NULL; for (int i = 0; i < n - 1; i++) { for (int j = 1; j < m; j++) { pre = p; p = p->link; } cout << "出列的人是" << p->data << endl; pre->link = p->link; delete p; p = pre->link; } Js.first->link = Js.last = p; }
约瑟夫问题调试
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include"linearList.h" using namespace std; int main() { CircList<int> clist; int i, n, m; cout << "输入游戏者人数和报数间隔:" << endl; cin >> n >> m; for (i = 1; i <= n; i++) clist.Insert(i-1, i); Josephus(clist, n, m); system("pause"); return 0; }
5.双向链表
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//双向链表类定义(带附加头结点) //双向链表结点类定义 template<class T> struct DblNode { T data; DblNode<T>* lLink, *rLink; //链表前驱后继指针 DblNode(DblNode<T>* left = NULL, DblNode<T>* right = NULL) :lLink(left), rLink(right) {} DblNode(T value, DblNode<T>* left = NULL, DblNode<T>* right = NULL):data(value,)lLink(left), rLink(right) {} }; //双向链表类定义 template<class T> class DblList :public LinearList<T> { public: DblList(T uniqueVal); //构造函数,建立附加头结点 //~DblList(); //析构函数 int Length() const; bool IsEmpty() { return first->rlink == first; } //判断链表是否为空 DblNode<T>* getHead() const { return first; } void setHead(DblNode<T>* ptr) { first = ptr; } //设置附加头结点地址;书中是这么定义的,但是这样岂不是把链表丢弃了? DblNode<T>* Search(const T& x); //在链表中沿后继方向寻找等于给定值x的结点 DblNode<T>* Locate(int i, int d); //在链表中定位序号为i(>=0)的结点,d=0按前驱方向,d!=0按后继方向 bool Insert(int i, const T& x, int d); //在第i个结点后插入一个包含有值x的新结点,d=0按前驱方向,d!=0按后继方向 bool Reomve(int i, T&x, int d); //删除第i个结点,x返回其值,d=0按前驱方向,d!=0按后继方向 private: DblNode<T>* first; }; template<class T> DblList<T>::DblList(T uniqueVal) { first = new DblNode<T>(uniqueVal); if (first == NULL) { cerr << "内存分配错误!" << endl; exit(1); } first->rLink = first->lLink = first; } template<class T> int DblList<T>::Length()const { DblNode<T>* current = first->rLink; int count = 0; while (current != first) { current = current->rLink; count++; } return count; } template<class T> DblNode<T>* DblList<T>::Search(const T& x) { DblNode<T> *current = first->rLink; while (current != first && current->data != x) current = current->rLink; if (current != first) return current; //搜索成功 else return NULL; //搜索失败 } template<class T> DblNode<T>* DblList<T>::Locate(int i, int d) { if (first->rLink == first || i == 0) return first; DblNode<T>* current; if (d == 0) current = first->lLink; else current = first->rLink; for (int j = 1; j < i; j++) { if (current == first) break; //链表太短 else if (d == 0) current = first->lLink; else current = first->rLink; } if (current != first) return current; //搜索成功 else return NULL; //搜索失败 } template<class T> bool DblList<T>::Insert(int i, const T&x, int d) { DblNode<T>* current = Locate(i, d); if (current == NULL) return false; DblNode<T>* newNode = new DblNode<T>(x); if (newNode == NULL) { cerr << "内存分配错误!" << endl; exit(1); } if (d == 0) { newNode->lLink = current->lLink; current->lLink = newNode; newNode->lLink->rLink = newNode; newNode->rLink = current; } else { newNode->rLink = current->rLink; current->rLink = newNode; newNode->rLink->lLink = newNode; newNode->lLink = current; } } template<class T> bool DblList<T>::Reomve(int i, T& x, int d) { DblNode<T>* current = Locate(i, d); if (current == NULL) return false; current->rLink->lLink = current->lLink; current->lLink->rLink = current->rLink; x = current->data; delete current; return true; }
感觉整个线性表这一章,单链表让人最舒服,其它几类虽然看上去不那么自然,但是主要目的是学习其中的逻辑结构。