线性表是最常用也是最简单的数据结构,几种常用的线性表的类模板C++描述描述如下:
1.顺序表
顺序表是将所有元素按逻辑顺序存储在一组地址连续的存储空间中的简单数据结构:
1 const int MAXSIZE = 1000; 2 template<class T> 3 class SeqList 4 { 5 public: 6 SeqList(){ lenght = 0; } // 无参构造函数 7 SeqList(const T a[], int n); // 带参构造函数,并用数组初始化 8 9 int GetLenght() const{ return lenght; }// 返回顺序表长度 10 void PrintList()const; // 依次遍历顺序表各个数据元素 11 void Insert(int i, T x); // 指定位置插入数据元素 12 T Delete(int i); // 删除指定位置的数据元素,并返回这个元素 13 T Get(int i) const; // 获取指定位置的数据元素 14 int Locate(T x) const; // 查找指定元素并返回其位置 15 private: 16 T data[MAXSIZE]; // 存储顺序表中各个数据元素 17 int lenght; // 顺序表长度 18 }; 19 20 21 // 构造函数 22 template<class T> 23 SeqList<T>::SeqList(const T a[], int n) 24 { 25 if (n > MAXSIZE) 26 throw"数组长度超出顺序表最大长度"; 27 for (int i = 0; i < n; i++) 28 data[i] = a[i]; 29 lenght = n; 30 } 31 32 // 遍历顺序表 33 template<class T> 34 void SeqList<T>::PrintList()const 35 { 36 std::cout << "顺序表中元素依次如下所示:" << endl; 37 for (int i = 0; i < lenght; i++) 38 std::cout << data[i] << " "; 39 std::cout << std::endl; 40 } 41 42 // 插入元素 43 template<class T> 44 void SeqList<T>::Insert(int i, T x) 45 { 46 if (lenght >= MAXSIZE) 47 throw"上溢异常"; 48 if (i<1 || i>lenght) 49 throw"插入位置不合法"; 50 for (int j = lenght; j >= i; j--) 51 data[j] = data[j - 1]; 52 data[i - 1] = x; 53 lenght++; 54 } 55 56 // 删除元素 57 template<class T> 58 T SeqList<T>::Delete(int i) 59 { 60 if (0 == lenght) 61 throw"下溢异常"; 62 if (i<1 || i>lenght) 63 throw"删除位置不合法"; 64 T X = data[i - 1]; 65 for (int j = i; j < lenght; j++) 66 data[j - 1] = data[j]; 67 lenght--; 68 return X; 69 } 70 71 // 查找元素 72 template<class T> 73 T SeqList<T>::Get(int i)const 74 { 75 if (i<1 || i>lenght) 76 throw"查找位置不合法"; 77 78 return data[i - 1]; 79 } 80 81 // 定位元素 82 template<class T> 83 int SeqList<T>::Locate(T x)const 84 { 85 for (int i = 0; i < lenght; i++) 86 { 87 if (x == data[i]) 88 return i + 1; 89 } 90 91 throw"查找的元素不存在顺序表中"; 92 return 0; 93 }
2.单链表
单链表与顺序表不同的地方是存放的元素地址是零散的,可能连续也可能不连续,保证彼此之间的联系,因此,在存储元素的时候需要存储下一个元素的地址:
1 template<class T> 2 struct Node 3 { 4 T data; 5 Node*next; 6 }; 7 8 9 template<class T> 10 class LinkList 11 { 12 public: 13 LinkList(){ front = new Node<T>; front->next = NULL; }// 无参构造函数 14 LinkList(T a[], int n); // 带参构造函数,用数组初始化链表 15 ~LinkList(); // 析构函数 16 void PrintList()const; // 依次遍历链表元素 17 int GetLenght()const; // 返回链表长度 18 Node<T>*Get(int i)const; // 返回指定位置的数据元素 19 int Locate(T x)const; // 查找指定元素,并返回其位置 20 void Insert(int i, T x); // 指定位置插入指定元素 21 T Delete(int i); // 删除指定位置的元素 22 private: 23 Node<T>*front; // 头指针 24 }; 25 26 27 // 头插法 28 template<class T> 29 LinkList<T>::LinkList(T a[], int n) 30 { 31 front = new Node<T>; 32 front->next = NULL; // 构造空链表 33 for (int i = n - 1; i => 0;i--) 34 { 35 Node<T>*s = new Node<T>; // 建立新结点 36 s->data = a[i]; // 将值写入结点数据域 37 s->next = front->next; // 修改新节点指针域 38 front->next = s; // 修改头结点指针域 39 } 40 } 41 42 // 尾插法 43 template<class T> 44 LinkList<T>::LinkList(T a[], int n) 45 { 46 front = new Node<T>; 47 Node<T>*r = front; 48 for (int i = 0; i < n;i++) 49 { 50 Node<T>*s = new Node<T>; // 建立新结点 51 s->data = a[i]; // 将值写入新结点数据域 52 r->next = s; // 将新结点加入链表 53 r = s; // 修改尾指针 54 } 55 r->next = NULL; 56 } 57 58 template<class T> 59 LinkList<T>::~LinkList() 60 { 61 Node<T>*p = front; // 初始化工作指针 62 while (p) 63 { 64 front = p; // 暂存工作指针 65 p = p->next; // 移动工作指针到下一个结点 66 delete front; // 释放结点 67 } 68 } 69 70 template<class T> 71 Node<T>*LinkList<T>::Get(int i)const 72 { 73 Node<T>*p = front->next; // 初始化工作指针 74 int j = 1; // 初始化计数器 75 while (p&&j!=i) 76 { 77 p = p->next; // 移动工作指针 78 j++; // 计数器递增 79 } 80 return p; // 返回查找结果 81 } 82 83 template<class T> 84 int LinkList<T>::Locate(T x)const 85 { 86 Node<T>*p = front->next; // 初始化工作指针 87 int j = 1; // 初始化计数器 88 while (p) 89 { 90 if (x == p->data) // 找到指定元素就返回位置 91 return j; 92 p = p->next; // 移动工作指针 93 j++; // 递增计时器 94 } 95 return -1; // 若找不到,返回-1 96 } 97 98 template<class T> 99 void LinkList<T>::Insert(int i, T x) 100 { 101 Node<T>*p = front; // 初始化工作指针 102 103 if (i != 1) 104 p = Get(i - 1); // 返回插入位置前一个元素的位置 105 if (p) 106 { 107 Node<T>*s = new Node<T>; 108 s->data = x; // 插入元素数据区域赋值 109 s->next = p->next; // 插入元素指针区域赋值 110 p->next = s; // 插入元素接入链表 111 } 112 else 113 throw"插入位置错误!"; 114 } 115 116 template<class T> 117 T LinkList<T>::Delete(int i) 118 { 119 Node<T>*p = front; // 初始化工作指针 120 if (i != 1) 121 p = Get(i - 1); // 获取第i-1个元素的地址 122 Node<T>*q = p->next; // 获取第i个元素的地址 123 p->next = q->next; // 将第i+1个元素与第i个元素链接 124 T x = q->data; // 保存被删除元素 125 delete q; // 删除指定位置元素 126 return x; // 返回被删除的元素 127 } 128 129 template<class T> 130 void LinkList<T>::PrintList()const 131 { 132 Node<T>*p = front->next; 133 std::cout << "链表元素依次为:"; 134 while (p) 135 { 136 std::cout << (p->data) << " "; 137 p = p->next; 138 } 139 std::cout << std::endl; 140 } 141 142 template<class T> 143 int LinkList<T>::GetLenght()const 144 { 145 Node<T>*p = front->next; 146 int j = 1; 147 while (p) 148 { 149 p = p->next; 150 j++; 151 } 152 153 return j; 154 }
3.单循环链表
单循环链表与单链表没有太大区别,为了访问链表的首尾段,将之前的头指针换成了尾指针,尾指针指向链表尾部,尾指针指针域指向链表第一个元素:
1 template<class T> 2 struct Node 3 { 4 T data; // 数据域 5 Node*next; // 指针域 6 } 7 8 template<class T> 9 class CLinkList 10 { 11 public: 12 CLinkList(){ rear = new Node<T>; rear->next = rear; } // 无参构造函数 13 CLinkList(T a[], int n); // 用数组初始化单循环链表 14 15 ~CLinkList(); // 析构函数 16 int GetLenght()const; // 返回链表长度 17 Node<T>*Get(int i)const; // 返回指定位置元素数据 18 int Locate(T x)const; // 查找指定元素,并返回其位置 19 void Insert(int i, T x); // 指定位置插入元素 20 T Delete(int i); // 删除指定位置的元素,并返回其值 21 void PrintList()const; // 依次遍历链表元素 22 void Connect(CLinkList<T>&B); // 将链表B链接到表尾 23 24 private: 25 Node<T>*rear; // 尾指针(指向最后一个元素) 26 }; 27 28 29 template<class T> 30 CLinkList<T>::CLinkList(T a[], int n) // 尾插法 31 { 32 rear = new Node<T>; 33 rear->next = rear; // 初始化尾指针 34 for (int i = 0; i < n; i++) 35 { 36 Node <T>*s = new Node <T>; 37 s->data = a[i]; // 数据域赋值 38 s->next = rear->next;// 新增结点指针指向头结点 39 rear->next = s; // 新增结点添加到链表中 40 rear = s; // 尾指针指向尾结点 41 } 42 } 43 44 45 template<class T> 46 void CLinkList<T>::PrintList()const 47 { 48 std::cout << "单循环链表各元素如下:"; 49 Node<T>*p = rear->next; // 初始化工作指针指向头结点 50 do 51 { 52 p = p->next; // 修改工作指针 53 std::cout << p->data << " "; // 打印 54 } while (p != rear); 55 56 std::cout << std::endl; 57 } 58 59 template<class T> 60 CLinkList<T>::~CLinkList() 61 { 62 Node<T>*p = rear->next; // 初始化工作指针 63 Node<T>*q = rear->next; // 初始化工作指针 64 while (p!=rear) 65 { 66 q = p; 67 p = p->next; 68 delete q; 69 } 70 delete p; // 释放尾结点 71 } 72 73 74 template<class T> 75 int CLinkList<T>::GetLenght()const 76 { 77 Node<T>*p = rear->next; // 初始化工作指针 78 int j = 0; // 元素计数器 79 while (p != rear) 80 { 81 p = p->next; // 移动工作指针 82 j++; 83 } 84 return j; 85 } 86 87 template<class T> 88 Node<T>*CLinkList<T>::Get(int i)const 89 { 90 Node<T>*p = rear->next; // 初始化工作指针 91 int j = 0; // 计数器 92 while ((p != rear) && (j != i)) 93 { 94 p = p->next; // 移动工作指针 95 j++; 96 } 97 return p; 98 } 99 100 template<class T> 101 int CLinkList<T>::Locate(T x)const 102 { 103 Node<T>*p = rear->next; // 初始化工作指针 104 int j = 0; // 计数器 105 while (p != rear) 106 { 107 if (p->data == x) 108 return j; 109 p = p->next; // 移动工作指针 110 j++; 111 } 112 return -1; 113 } 114 115 template<class T> 116 T CLinkList<T>::Delete(int i) 117 { 118 Node<T>*P = rear; // 初始化工作指针 119 if (i != 1) 120 P = Get(i - 1); // 获取第i-1个元素的地址 121 Node<T>*q = P->next; // 获取第i个元素的地址 122 P->next = q->next; // 将第i+1个元素与第i个元素链接 123 T x = q->data; // 保存被删除元素 124 delete q; // 删除指定位置元素 125 return x; // 返回被删除的元素 126 } 127 128 template<class T> 129 void CLinkList<T>::Insert(int i, T x) 130 { 131 Node<T>*p = rear; 132 133 if (i != 1) 134 p = Get(i - 1); // 获取第i-1个元素的地址 135 if (p) 136 { 137 Node<T>*s = new Node<T>; // 新建结点数据 138 s->data = x; // 新增结点数据域赋值 139 s->next = p->next; // 新增结点指针域赋值 140 p->next = s; // 连接链表 141 } 142 else 143 throw"插入位置错误!"; 144 } 145 146 template<class T> 147 void CLinkList<T>::Connect(CLinkList<T>&B) 148 { 149 if (B.rear == B.rear->next) 150 return; // 如果B为空表,无需链接 151 Node<T>*p = B.rear->next; // 暂存头结点 152 B.rear->next = rear->next; 153 rear->next = p->next; 154 rear = B.rear; 155 156 B.rear->next = B.rear = p; 157 }
4.双向链表
相比单向链表,就是多了一个前驱结点指针,方便访问相连的前后结点:
1 template<class T> 2 struct DNode 3 { 4 T data; // 数据域 5 DNode*prior; // 指针域,指向前一个结点 6 DNode*next; // 指针域,指向后一个结点 7 }; 8 9 template<class T> 10 class DLinkList 11 { 12 public: 13 DLinkList(){ front = new DNode<T>; front->next = front; front->prior = front; } // 无参构造函数 14 DLinkList(T a[], int n); // 带参构造函数,数组初始化链表 15 ~DLinkList(); // 析构函数 16 17 void Insert(int i, T X); // 指定位置插入指定元素 18 T Delete(int i); // 删除指定位置的元素,并返回这个元素 19 int GetLenght()const; // 返回链表长度 20 DNode<T>*Get(int i)const; // 查找指定位置元素 21 int Locate(T X)const; // 查找指定元素,并返回其位置 22 void PrintList()const; // 遍历链表 23 private: 24 DNode<T>*front; 25 }; 26 27 template<class T> 28 DLinkList<T>::DLinkList(T a[], int n) 29 { 30 front = new DNode<T>; // 构造头结点 31 DNode<T>*p = front; // 存储头指针 32 front->next = front; // 初始化头结点 33 front->prior = front; // 初始化头结点 34 35 for (int i = 0; i < n;i++) 36 { 37 DNode<T>*s = new DNode<T>; // 构造新结点数据结构 38 s->data = a[i]; // 给新结点数据域赋值 39 s->next = front->next; // 新结点后驱指针指向头结点 40 front->next = s; // 新结点加入后驱链中 41 s->prior = front; // 新结点前驱指针指向头结点 42 p->prior = s; // 修改头结点前驱指针指向新结点 43 44 front = s; // 传递尾结点地址到下一个结点 45 } 46 front = p; // 修改头指针指向头结点 47 } 48 49 template<class T> 50 DLinkList<T>::~DLinkList() 51 { 52 DNode<T>*p = front; // 初始化工作结点 53 DNode<T>*q = front; // 初始化工作结点 54 do 55 { 56 p = p->next; // 移动工作结点 57 q = p; // 暂存待释放结点 58 delete q; 59 } while (p!=front->prior); 60 61 delete front; // 释放头结点 62 } 63 64 template<class T> 65 void DLinkList<T>::PrintList()const 66 { 67 DNode<T>*p = front; // 初始化工作链表 68 std::cout << "按后驱指针遍历链表:"; 69 while(p!=front->prior) 70 { 71 p = p->next; // 移动工作指针 72 std::cout << p->data << " "; 73 } 74 std::cout << std::endl; 75 76 DNode<T>*q = front->prior; // 初始化工作链表 77 std::cout << "按前驱指针遍历链表:"; 78 while (p != front) 79 { 80 std::cout << p->data << " "; 81 p = p->prior; // 移动工作指针 82 } 83 std::cout << std::endl; 84 } 85 86 template<class T> 87 T DLinkList<T>::Delete(int i) 88 { 89 DNode<T>*p = Get(i); // 获取待删除结点地址 90 DNode<T>*p1 = p->prior; // 获取待删除结点的前驱结点位置 91 DNode<T>*p2 = p->next; // 获取待删除结点的后驱结点位置 92 T x = p->data; // 暂存待删除结点数据域 93 p1->next = p2; // 修改后驱链 94 p2->prior = p1; // 修改前驱链 95 96 delete p; // 删除结点 97 98 return x; 99 } 100 101 template<class T> 102 void DLinkList<T>::Insert(int i, T X) 103 { 104 DNode<T>*p = Get(i); // 获取待插入位置地址 105 if (p) 106 { 107 DNode<T>*s = new DNode<T>; // 构造新结点数据存储结构 108 DNode<T>*q = p->prior; // 获取待插入位置的前驱结点位置 109 110 s->data = X; // 插入的新结点数据域赋值 111 s->next = p; // 插入结点的后驱指针赋值 112 q->next = s; // 插入结点的前一结点的后驱指针赋值 113 s->prior = q; // 插入结点的前驱指针赋值 114 p->prior = s; // 插入结点的后一节点的前驱指针赋值 115 } 116 else throw"插入位置错误"; 117 } 118 119 template<class T> 120 int DLinkList<T>::GetLenght()const 121 { 122 DNode<T>*p = front; // 初始化工作指针 123 int j = 0; // 定义一个计数器 124 while (p!=front->prior) 125 { 126 p = p->next; // 移动工作指针 127 j++; 128 } 129 return j; 130 } 131 132 template<class T> 133 DNode<T>*DLinkList<T>::Get(int i)const 134 { 135 DNode<T>*p = front; // 初始化工作指针 136 int j = 0; // 定义一个计数器 137 while ((p!=front->prior)&&j!=i) 138 { 139 p = p->next; // 移动工作指针 140 j++; 141 } 142 return p; // 找到返回地址 143 } 144 145 template<class T> 146 int DLinkList<T>::Locate(T X)const 147 { 148 DNode<T>*p = front; // 初始化工作结点 149 int j = 0; 150 while (p!=front->prior) 151 { 152 p = p->next; // 移动工作指针 153 j++; 154 if (p->data == X) 155 return j; // 找到元素,返回位置 156 } 157 return -1; // 找不到返回-1 158 }
5.静态链表
静态链表主要是为了满足那些没有“指针”这一类数据类型的语言(JAVA,BASIC等),采用数组来模拟链表,数组的下标来模拟指针:
1 template<class T> 2 struct StaticNode 3 { 4 T data; // 数据域 5 int next; // 游标(模拟指针) 6 }; 7 8 const int SLISTMAXSIZE = 100; 9 10 template<class T> 11 class StaticLinkList 12 { 13 public: 14 StaticLinkList(); // 无参构造函数 15 StaticLinkList(T a[], int n); // 带参构造函数,数组初始化链表 16 void Insert(int i, T X); // 指定位置插入指定元素 17 T Delete(int i); // 删除指定位置元素,并返回该元素 18 int Get(int i)const; // 按位查找,返回下标 19 int Locate(T X)const; // 按值查找,返回下标 20 int GetLenght()const; // 返回链表长度 21 int NewNode(); // 申请结点空间 22 void DeleteNode(int i); // 释放指定结点 23 void PrintList()const; // 遍历链表 24 private: 25 int front; // 数据域起始下标 26 int tail; // 空闲域起始下标 27 StaticNode<T> Sarray[SLISTMAXSIZE]; // 数据表 28 }; 29 30 31 template<class T> 32 StaticLinkList<T>::StaticLinkList() 33 { 34 for (int i = 0; i < SLISTMAXSIZE - 1; i++) 35 Sarray[i].next = i + 1; // 链表每个游标都是指向下一个数组元素 36 Sarray[SLISTMAXSIZE - 1].next = -1; // 将最后一个元素的游标指向-1 37 tail = 0; // 显然空链表的头结点下标就是0 38 front = -1; // 空链表的头结点游标定义为-1 39 } 40 41 template<class T> 42 StaticLinkList<T>::StaticLinkList(T a[], int n) 43 { 44 // 初始化数组 45 if (n > SLISTMAXSIZE) 46 throw"数据溢出"; 47 for (int i = 0; i < SLISTMAXSIZE - 1; i++) 48 Sarray[i].next = i + 1; // 链表中每个游标指向下一个数组元素 49 Sarray[SLISTMAXSIZE - 1].next = -1; // 将最后一个元素的游标指向-1 50 for (int i = 0; i < n; i++) 51 Sarray[i].data = a[i]; // 给链表数据域赋值 52 front = 0; // front指向可用数据域的第一个元素的下标 53 tail = Sarray[n - 1].next; // tail指向未分配空间的第一个元素 54 Sarray[n - 1].next = -1; // 将可用数据域最有一个元素的游标指向-1 55 } 56 57 template<class T> 58 int StaticLinkList<T>::NewNode() 59 { 60 if (-1 == tail) // 判断是否还有未分配空间 61 throw"内存不足"; 62 int pos=tail; // 未分配空间第一个结点分配给新结点 63 tail = Sarray[tail].next; // 未分配空间起点游标后移 64 Sarray[tail].next = -1; // 修改数据域最后一个元素指向-1 65 return pos; // 返回新结点的下标 66 } 67 68 template<class T> 69 void StaticLinkList<T>::DeleteNode(int i) 70 { 71 if (i<0 || i>SLISTMAXSIZE - 1 || -1 == front) 72 throw"释放空间错误"; 73 int p = Get(i); // 记录第i个元素下标 74 int q = Get(i - 1);; // 记录第i-1个元素的下标 75 if (p==front) // 单独处理删除点为头结点的情况 76 { 77 Sarray[front].next = tail; 78 tail = front; // 删除的结点插入到未使用空间的头结点位置 79 front = Sarray[front].next; // 更改数据域的起始下标为原来的下一个位置 80 } 81 else if (-1 == Sarray[p].next) // 单独处理待删除点为尾结点的情况 82 { 83 Sarray[p].next = tail; 84 tail = p; 85 Sarray[q].next = -1; 86 } 87 else // 一般情况处理 88 { 89 Sarray[q].next = Sarray[p].next; 90 Sarray[p].next = tail; 91 tail = p; 92 } 93 } 94 95 template<class T> 96 T StaticLinkList<T>::Delete(int i) 97 { 98 T x = Sarray[Get(i)].data; // 将待删除元素暂存 99 DeleteNode(i); // 删除元素 100 101 return x; 102 } 103 104 template<class T> 105 void StaticLinkList<T>::Insert(int i, T X) 106 { 107 int p = Get(i); // 记录第i个元素的下标 108 if (p < 0) 109 throw"插入位置错误"; 110 111 if (p==front) // 单独处理在头结点插入的情况 112 { 113 Sarray[tail].data = X; // 从未分配空间第一个元素开始使用 114 Sarray[tail].next = front; // 新结点与后一结点链接 115 front = tail; // 更新头结点 116 tail = Sarray[tail].next; // 未分配空间的头结点顺移 117 } 118 else 119 { 120 int q = Get(i - 1); // 记录第i-1个元素下标 121 122 Sarray[tail].data = X; // 从未分配空间第一个元素开始使用 123 Sarray[q].next = tail; // 新结点与前一结点链接 124 Sarray[tail].next = p; // 新结点与后一结点链接 125 tail = Sarray[tail].next; // 未分配空间的头结点顺移 126 } 127 } 128 129 template<class T> 130 int StaticLinkList<T>::Get(int i)const 131 { 132 if (i<0 || i>GetLenght()) 133 return -1; // 找不到就返回错误-1 134 int p = front; // 初始化工作指针 135 int j = 1; // 计数器 136 while (p!= -1) 137 { 138 if (j == i) 139 return p; 140 p = Sarray[p].next; // 移动工作指针 141 j++; 142 } 143 return p; 144 } 145 146 template<class T> 147 int StaticLinkList<T>::GetLenght()const 148 { 149 if (-1 == front) 150 throw"长度为0"; 151 152 int j = 0; // 计数器 153 int k = front; // 工作指针 154 while (k!=-1) 155 { 156 k = Sarray[k].next; // 移动工作指针 157 j++; 158 } 159 return j; 160 } 161 162 template<class T> 163 int StaticLinkList<T>::Locate(T X)const 164 { 165 int p = front; // 工作指针 166 while (p!=-1) 167 { 168 if (X == Sarray[p].data) // 找到就返回下标 169 return p; 170 p = Sarray[p].next; 171 } 172 return -1; 173 } 174 175 template<class T> 176 void StaticLinkList<T>::PrintList()const 177 { 178 int p = front; // 工作游标 179 std::cout << "链表元素依次如下:"; 180 while (p!=-1) 181 { 182 std::cout << Sarray[p].data << " "; 183 p = Sarray[p].next; // 移动工作游标 184 } 185 std::cout << std::endl; 186 }