单链表(Singly Linked List)
1. 单链表的概念
1.1 单链表的定义
- 单链表是线性表的链式存储表示。
1.2 单链表的结点结构
- 单链表的结点包括两个部分:数据域和指针域。
(1)数据域(data),用于存储该结点的数据元素,数据元素类型由应用问题决定。
(2)指针域(link),用于存放一个指针,该指针指向下一个结点的开始存储地址。 - 单链表的结点结构示意图:
1.3 单链表中各结点的链接方式
- 单链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
- 单链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
- 为了表示每个数据元素与其直接后继数据元素之间的逻辑关系,对数据元素来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。由这两部分信息组成一个“结点”,表示线性表中一个数据元素。
- 单链表要找一个数,必须要从表头开始找起,十分麻烦。
- 单链表(带附加头结点的)中各结点的链接方式示意图:
2. 单链表的实现
2.1 静态链表
- 静态链表:链表中所有结点的数据内存中的分布都是在编译时就确定好,程序运行后不能进行改动。
静态链表的实现——例1
#include <iostream> using namespace std; template <class T> struct LinkNodeSimple { T data; //数据域(data),用于存储该结点的数据元素,数据元素类型由应用问题决定。 LinkNodeSimple<T> *link; //指针域(link),用于存放一个指针,该指针指向下一个结点的开始存储地址。 }; int main() { LinkNodeSimple<int> node1, node2, node3, *head; node1.data = 1; node2.data = 2; node3.data = 3; head = &node1; //将node1变量结构的开始地址作为一个结点赋给头指针head node1.link = &node2; //将node2变量结构的开始地址作为一个结点赋给node1变量结构的link指针成员 node2.link = &node3; //将node3变量结构的开始地址作为一个结点赋给node2变量结构的link指针成员 node3.link = NULL; //将node3变量结构的link指针成员赋为空,即表示该结点是个尾结点 //打印链表每一个结点的data值 int count = 1; LinkNodeSimple<int> *p; p = head;//将p指针指向头结点head while(NULL != p) { cout<<"#"<<count<<" num = "<<p->data<<endl; p = p->link;//将p指针指向下一个结点 count++; } system("pause"); return 0; }
静态链表的实现——例2
#include <iostream> #include <string> using namespace std; template <class T1, class T2> struct LinkNode { T1 data1; //数据域(data),用于存储该结点的数据元素,数据元素类型由应用问题决定。 T2 data2; //数据域(data),用于存储该结点的数据元素,数据元素类型由应用问题决定。 LinkNode<T1, T2> *link; //指针域(link),用于存放一个指针,该指针指向下一个结点的开始存储地址。 }; int main() { LinkNode<int, string> node1, node2, node3, *head; node1.data1 = 1; node1.data2 = "one"; node2.data1 = 2; node2.data2 = "two"; node3.data1 = 3; node3.data2 = "three"; head = &node1; //将node1变量结构的开始地址作为一个结点赋给头指针head node1.link = &node2; //将node2变量结构的开始地址作为一个结点赋给node1变量结构的link指针成员 node2.link = &node3; //将node3变量结构的开始地址作为一个结点赋给node2变量结构的link指针成员 node3.link = NULL; //将node3变量结构的link指针成员赋为空,即表示该结点是个尾结点 //打印链表每一个结点的data值 int count = 1; LinkNode<int, string> *p; p = head;//将p指针指向头结点head while(NULL != p) { cout<<"#"<<count<<" num = "<<p->data1<<", name = "<<p->data2<<endl; p = p->link;//将p指针指向下一个结点 count++; } system("pause"); return 0; }
2.2 动态链表
- 动态链表:链表结构也可以是动态地分配存储的,即在需要时才开辟结点的存储空间。
动态链表的实现:
(1)链表的结点结构定义:LinkNode.h#ifndef LINK_NODE_H_ #define LINK_NODE_H_ #include <iostream> #include <string> #include <strstream> using namespace std; template <class T> struct LinkNode //链表结点类的定义 { T data; //数据域 LinkNode<T> *link; //指针域——后继指针 //仅初始化指针成员的构造函数 LinkNode(LinkNode<T>* ptr = NULL){ link = ptr; } //初始化数据与指针成员的构造函数 LinkNode(const T& value, LinkNode<T>* ptr = NULL){ data = value; link = ptr; } }; #endif /* LINK_NODE_H_ */
(2)单链表的类定义及其操作的实现(带附加头结点):LinkedList1.h
#ifndef LINKED_LIST1_H_ #define LINKED_LIST1_H_ #include "LinkNode.h" template <class T> class LinkedList//带附加头结点 { public: LinkedList(); //构造函数 LinkedList(const LinkedList<T>& L); //拷贝构造函数 ~LinkedList(); //析构函数 public: int Length()const; //计算链表的结点总数(除附加头结点) LinkNode<T>* Search(const T& x)const; //搜索数据值为x的结点并返回 LinkNode<T>* Locate(int i)const; //获取第i个结点并返回 bool GetData(int i, T& x)const; //获取第i个结点的数据值保存至x,并返回获取成功与否 bool SetData(int i, const T& x); //修改第i个结点的数据值为x bool Insert(int i, const T& x); //在第i个结点后插入数据值为x的新结点 bool Remove(int i, T& x); //删除第i个结点,并将被删结点的数据值保存至x bool IsEmpty()const; //判断表是否为空 bool IsFull()const; //判断表是否为满 void Sort(); //表排序——冒泡排序 void InputFront(const T& endTag); //前插法建立链表 void InputRear(const T& endTag); //后插法建立链表 void Output()const; //输出所有结点的数据值 void Reverse(); //链表逆置 void MakeEmpty(); //清空链表(保留附加头结点) LinkedList<T>& operator=(const LinkedList<T>& L); //链表间赋值操作——重载等号运算符 public: LinkNode<T>* GetHead()const; //返回附加头结点的地址 T get_value(string& s_value); //返回输入的结点数据值 public: static bool IsNumber(const string& s_num); //判断输入的字符串每个字符是否都是数值0~9 static T StrToTtype(const string& s_num); //类型转换——将string型转为模板类型T private: LinkNode<T> *first; //链表的头结点 }; //构造函数 template<class T> LinkedList<T>::LinkedList() : first(new LinkNode<T>) { cout << "$ 执行构造函数" << endl; } //拷贝构造函数 template<class T> LinkedList<T>::LinkedList(const LinkedList<T>& L) { cout << "$ 执行拷贝构造函数" << endl; LinkNode<T> *srcptr = L.GetHead(); LinkNode<T> *destptr = first = new LinkNode<T>; while (NULL != srcptr->link) { srcptr = srcptr->link; destptr->link = new LinkNode<T>(srcptr->data); destptr = destptr->link; } } //析构函数 template<class T> LinkedList<T>::~LinkedList() { cout << "$ 执行析构函数" << endl; MakeEmpty(); } //计算链表的结点总数(除附加头结点) template<class T> int LinkedList<T>::Length()const { int count = 0; LinkNode<T> *curNode = first->link; while (NULL != curNode) { curNode = curNode->link; count++; } return count; } //搜索数据值为x的结点并返回 template<class T> LinkNode<T>* LinkedList<T>::Search(const T& x)const { LinkNode<T> *curNode = first->link; while ((NULL != curNode) && (x != curNode->data)) { curNode = curNode->link; } return curNode; } //获取第i个结点并返回 template<class T> LinkNode<T>* LinkedList<T>::Locate(int i)const { if (i < 0) { return NULL; } if (0 == i) { return first; } int location = 1; LinkNode<T> *curNode = first->link; while ((location < i) && (NULL != curNode)) { curNode = curNode->link; location++; } return curNode; } //获取第i个结点的数据值保存至x,并返回获取成功与否 template<class T> bool LinkedList<T>::GetData(int i, T& x)const { LinkNode<T> *curNode = Locate(i); if ((NULL == curNode) || (first == curNode)) { return false; } x = curNode->data; return true; } //修改第i个结点的数据值为x template<class T> bool LinkedList<T>::SetData(int i, const T& x) { LinkNode<T> *curNode = Locate(i); if ((NULL == curNode) || (first == curNode)) { return false; } curNode->data = x; return true; } //在第i个结点后插入数据值为x的新结点 template<class T> bool LinkedList<T>::Insert(int i, const T& x) { LinkNode<T> *curNode = Locate(i); if (NULL == curNode) { return false; } LinkNode<T> *newNode = new LinkNode<T>(x); if (NULL == newNode) { cout << "* 存储分配错误" << endl; return false; } newNode->link = curNode->link; curNode->link = newNode; return true; } //删除第i个结点,并将被删结点的数据值保存至x template<class T> bool LinkedList<T>::Remove(int i, T& x) { LinkNode<T> *preNode = Locate(i - 1); if (NULL == preNode) { return false; } LinkNode<T> *delNode = preNode->link; if ((NULL == delNode) || (first == delNode)) { return false; } preNode->link = delNode->link; x = delNode->data; delete delNode; return true; } //判断表是否为空 template<class T> bool LinkedList<T>::IsEmpty()const { return (NULL == first->link) ? true : false; } //判断表是否为满 template<class T> bool LinkedList<T>::IsFull()const { return false; } //表排序——冒泡排序 template<class T> void LinkedList<T>::Sort() { LinkNode<T> *curNode = first->link; while (NULL != curNode) { LinkNode<T> *nextNode = curNode->link; while (NULL != nextNode) { if (curNode->data < nextNode->data) { T temp = curNode->data; curNode->data = nextNode->data; nextNode->data = temp; } nextNode = nextNode->link; } curNode = curNode->link; } } //前插法建立链表 //endTag是约定的输入序列结束的标志。如果输入序列是正整数,endTag可以是0或负数; //如果输入序列是字符,endTag可以是字符集中不会出现的字符,如“\0”。 template<class T> void LinkedList<T>::InputFront(const T& endTag) { MakeEmpty(); string s_value; T value = get_value(s_value); while (endTag != value) { LinkNode<T> *newNode = new LinkNode<T>(value); //创建新结点 if (NULL == newNode) { cout << "* 存储分配错误" << endl; break; } newNode->link = first->link; //新结点中指针域的指针指向链表的第一个结点 first->link = newNode; //附加头结点中指针域的指针指向新结点 value = get_value(s_value); } } //后插法建立链表 //endTag是约定的输入序列结束的标志。如果输入序列是正整数,endTag可以是0或负数; //如果输入序列是字符,endTag可以是字符集中不会出现的字符,如“\0”。 template<class T> void LinkedList<T>::InputRear(const T& endTag) { MakeEmpty(); LinkNode<T> *lastNode = first; //设置链表的最后一个结点为附加头结点 string s_value; T value = get_value(s_value); while (endTag != value) { LinkNode<T> *newNode = new LinkNode<T>(value); //创建新结点 if (NULL == newNode) { cout << "* 存储分配错误" << endl; break; } lastNode->link = newNode; //最后一个结点中指针域的指针指向新结点 lastNode = newNode; //设置最后一个结点为新结点 value = get_value(s_value); } } //输出所有结点的数据值 template<class T> void LinkedList<T>::Output()const { LinkNode<T> *curNode = first->link; while (NULL != curNode) { cout << "curNode->data = " << curNode->data << endl; curNode = curNode->link; } } //链表逆置 template<class T> void LinkedList<T>::Reverse() { LinkNode<T> *prevNode = NULL; LinkNode<T> *curNode = first->link; LinkNode<T> *nextNode = NULL; while (NULL != curNode) { nextNode = curNode->link; curNode->link = prevNode; prevNode = curNode; curNode = nextNode; } first->link = prevNode; } //清空链表(保留附加头结点) template<class T> void LinkedList<T>::MakeEmpty() { LinkNode<T> *curNode = NULL; while (NULL != first->link) //当链表不为空时,删去链表中所有结点 { curNode = first->link; //保存被删结点 first->link = curNode->link; //将链表附加头结点中指针域的指针指向被删结点的下一个结点 delete curNode; //从链表上摘下被删结点 } } //链表间赋值操作——重载等号运算符 template<class T> LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& L) { cout << "$ 执行赋值操作函数" << endl; LinkNode<T> *srcptr = L.GetHead(); LinkNode<T> *destptr = first = new LinkNode<T>; while (NULL != srcptr->link) { srcptr = srcptr->link; destptr->link = new LinkNode<T>(srcptr->data); destptr = destptr->link; } return *this; } //返回附加头结点的地址 template<class T> LinkNode<T>* LinkedList<T>::GetHead()const { return first; } //返回输入的结点数据值 template <class T> T LinkedList<T>::get_value(string& s_value) { cout << "newNode->data = "; cin >> s_value; return StrToTtype(s_value); } //判断输入的字符串每个字符是否都是数值0~9 template <class T> bool LinkedList<T>::IsNumber(const string& s_num) { for (size_t i = 0; i < s_num.size(); i++) { if ((s_num[i] < '0') || (s_num[i] > '9')) { return false; } } return true; } //类型转换——将string型转为模板类型T template <class T> T LinkedList<T>::StrToTtype(const string& s_num) { T n_num; strstream ss_num; ss_num << s_num; ss_num >> n_num; return n_num; } #endif /* LINKED_LIST1_H_ */
(3)单链表的类定义及其操作的实现(不带附加头结点):LinkedList2.h
#ifndef LINKED_LIST2_H_ #define LINKED_LIST2_H_ #include "LinkNode.h" template <class T> class LinkedList//不带附加头结点 { public: LinkedList(); //构造函数 LinkedList(const LinkedList<T>& L); //拷贝构造函数 ~LinkedList(); //析构函数 public: int Length()const; //计算链表的结点总数 LinkNode<T>* Search(const T& x)const; //搜索数据值为x的结点并返回 LinkNode<T>* Locate(int i)const; //获取第i个结点并返回 bool GetData(int i, T& x)const; //获取第i个结点的数据值保存至x,并返回获取成功与否 bool SetData(int i, const T& x); //修改第i个结点的数据值为x bool Insert(int i, const T& x); //在第i个结点后插入数据值为x的新结点 bool Remove(int i, T& x); //删除第i个结点,并将被删结点的数据值保存至x bool IsEmpty()const; //判断表是否为空 bool IsFull()const; //判断表是否为满 void Sort(); //表排序——冒泡排序 void InputFront(const T& endTag); //前插法建立链表 void InputRear(const T& endTag); //后插法建立链表 void Output()const; //输出所有结点的数据值 void Reverse(); //链表逆置 void MakeEmpty(); //清空链表 LinkedList<T>& operator=(const LinkedList<T>& L); //链表间赋值操作——重载等号运算符 public: LinkNode<T>* GetHead()const; //返回头结点的地址 T get_value(string& s_value); //返回输入的结点数据值 public: static bool IsNumber(const string& s_num); //判断输入的字符串每个字符是否都是数值0~9 static T StrToTtype(const string& s_num); //类型转换——将string型转为模板类型T private: LinkNode<T> *first; //链表的头结点 }; //构造函数 template<class T> LinkedList<T>::LinkedList() : first(NULL) { cout << "$ 执行构造函数" << endl; } //拷贝构造函数 template<class T> LinkedList<T>::LinkedList(const LinkedList<T>& L) { cout << "$ 执行拷贝构造函数" << endl; LinkNode<T> *srcptr = L.GetHead(); LinkNode<T> *destptr = first = NULL; if (NULL != srcptr) { destptr = first = new LinkNode<T>(srcptr->data); while (NULL != srcptr->link) { srcptr = srcptr->link; destptr->link = new LinkNode<T>(srcptr->data); destptr = destptr->link; } } } //析构函数 template<class T> LinkedList<T>::~LinkedList() { cout << "$ 执行析构函数" << endl; MakeEmpty(); } //计算链表的结点总数 template<class T> int LinkedList<T>::Length()const { int count = 0; LinkNode<T> *curNode = first; while (NULL != curNode) { curNode = curNode->link; count++; } return count; } //搜索数据值为x的结点并返回 template<class T> LinkNode<T>* LinkedList<T>::Search(const T& x)const { LinkNode<T> *curNode = first; while ((NULL != curNode) && (x != curNode->data)) { curNode = curNode->link; } return curNode; } //获取第i个结点并返回 template<class T> LinkNode<T>* LinkedList<T>::Locate(int i)const { if (i <= 0) { return NULL; } int location = 1; LinkNode<T> *curNode = first; while ((location < i) && (NULL != curNode)) { curNode = curNode->link; location++; } return curNode; } //获取第i个结点的数据值保存至x,并返回获取成功与否 template<class T> bool LinkedList<T>::GetData(int i, T& x)const { LinkNode<T> *curNode = Locate(i); if (NULL == curNode) { return false; } x = curNode->data; return true; } //修改第i个结点的数据值为x template<class T> bool LinkedList<T>::SetData(int i, const T& x) { LinkNode<T> *curNode = Locate(i); if (NULL == curNode) { return false; } curNode->data = x; return true; } //在第i个结点后插入数据值为x的新结点 template<class T> bool LinkedList<T>::Insert(int i, const T& x) { LinkNode<T> *newNode = new LinkNode<T>(x); if (NULL == newNode) { cout << "* 存储分配错误" << endl; return false; } if (0 == i) { newNode->link = first; first = newNode; return true; } LinkNode<T> *curNode = Locate(i); if (NULL != curNode) { newNode->link = curNode->link; curNode->link = newNode; return true; } delete newNode; return false; } //删除第i个结点,并将被删结点的数据值保存至x template<class T> bool LinkedList<T>::Remove(int i, T& x) { if (NULL == first) { return false; } LinkNode<T> *delNode = NULL; if (1 == i) { delNode = first; first = delNode->link; } else { LinkNode<T> *preNode = Locate(i - 1); if ((NULL == preNode) || (NULL == preNode->link)) { return false; } delNode = preNode->link; preNode->link = delNode->link; } x = delNode->data; delete delNode; return true; } //判断表是否为空 template<class T> bool LinkedList<T>::IsEmpty()const { return (NULL == first) ? true : false; } //判断表是否为满 template<class T> bool LinkedList<T>::IsFull()const { return false; } //表排序——冒泡排序 template<class T> void LinkedList<T>::Sort() { LinkNode<T> *curNode = first; while (NULL != curNode) { LinkNode<T> *nextNode = curNode->link; while (NULL != nextNode) { if (curNode->data < nextNode->data) { T temp = curNode->data; curNode->data = nextNode->data; nextNode->data = temp; } nextNode = nextNode->link; } curNode = curNode->link; } } //前插法建立链表 //endTag是约定的输入序列结束的标志。如果输入序列是正整数,endTag可以是0或负数; //如果输入序列是字符,endTag可以是字符集中不会出现的字符,如“\0”。 template<class T> void LinkedList<T>::InputFront(const T& endTag) { MakeEmpty(); string s_value; T value = get_value(s_value); while (endTag != value) { LinkNode<T> *newNode = new LinkNode<T>(value); //创建新结点 if (NULL == newNode) { cout << "* 存储分配错误" << endl; break; } newNode->link = first; //新结点中指针域的指针指向链表的第一个结点 first = newNode; //新结点成为头结点 value = get_value(s_value); } } //后插法建立链表 //endTag是约定的输入序列结束的标志。如果输入序列是正整数,endTag可以是0或负数; //如果输入序列是字符,endTag可以是字符集中不会出现的字符,如“\0”。 template<class T> void LinkedList<T>::InputRear(const T& endTag) { MakeEmpty(); LinkNode<T> *lastNode = first; //设置链表的最后一个结点为头结点; string s_value; T value = get_value(s_value); while (endTag != value) { LinkNode<T> *newNode = new LinkNode<T>(value); //创建新结点 if (NULL == newNode) { cout << "* 存储分配错误" << endl; break; } if(NULL == lastNode) { first = newNode; //当链表为空时,新结点成为头结点 } else { lastNode->link = newNode; //链表的最后一个结点中指针域的指针指向新结点 } lastNode = newNode; //新结点成为链表的最后一个结点 value = get_value(s_value); } } //输出所有结点的数据值 template<class T> void LinkedList<T>::Output()const { LinkNode<T> *curNode = first; while (NULL != curNode) { cout << "curNode->data = " << curNode->data << endl; curNode = curNode->link; } } //链表逆置 template<class T> void LinkedList<T>::Reverse() { LinkNode<T> *prevNode = NULL; LinkNode<T> *curNode = first; LinkNode<T> *nextNode = NULL; while (NULL != curNode) { nextNode = curNode->link; curNode->link = prevNode; prevNode = curNode; curNode = nextNode; } first = prevNode; } //清空链表 template<class T> void LinkedList<T>::MakeEmpty() { LinkNode<T> *curNode = NULL; while (NULL != first) //当链表不为空时,删去链表中所有结点 { curNode = first; //保存被删结点 first = curNode->link; //被删结点的下一个结点成为头结点 delete curNode; //从链表上摘下被删结点 } } //链表间赋值操作——重载等号运算符 template<class T> LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& L) { cout << "$ 执行赋值操作函数" << endl; LinkNode<T> *srcptr = L.GetHead(); LinkNode<T> *destptr = first = NULL; if (NULL != srcptr) { destptr = first = new LinkNode<T>(srcptr->data); while (NULL != srcptr->link) { srcptr = srcptr->link; destptr->link = new LinkNode<T>(srcptr->data); destptr = destptr->link; } } return *this; } //返回头结点的地址 template<class T> LinkNode<T>* LinkedList<T>::GetHead()const { return first; } //返回输入的结点数据值 template <class T> T LinkedList<T>::get_value(string& s_value) { cout << "newNode->data = "; cin >> s_value; return StrToTtype(s_value); } //判断输入的字符串每个字符是否都是数值0~9 template <class T> bool LinkedList<T>::IsNumber(const string& s_num) { for (size_t i = 0; i < s_num.size(); i++) { if ((s_num[i] < '0') || (s_num[i] > '9')) { return false; } } return true; } //类型转换——将string型转为模板类型T template <class T> T LinkedList<T>::StrToTtype(const string& s_num) { T n_num; strstream ss_num; ss_num << s_num; ss_num >> n_num; return n_num; } #endif /* LINKED_LIST2_H_ */
(4)主函数(main函数)的实现:main.cpp
#include "LinkedList1.h" //带附加头结点 //#include "LinkedList2.h" //不带附加头结点 #define EXIT 0 //退出 #define CONSTRUCT_COPY 1 //拷贝构造链表 #define LENGTH 2 //计算链表的结点总数 #define SEARCH 3 //搜索数据值为x的结点并返回 #define LOCATE 4 //获取第i个结点并返回 #define GETDATA 5 //获取第i个结点的数据值保存至x,并返回获取成功与否 #define SETDATA 6 //修改第i个结点的数据值为x #define INSERT 7 //在第i个结点后插入数据值为x的新结点 #define REMOVE 8 //删除第i个结点,并将被删结点的数据值保存至x #define ISEMPTY 9 //判断表是否为空 #define ISFULL 10 //判断表是否为满 #define SORT 11 //表排序——冒泡排序 #define INPUTFRONT 12 //前插法建立链表 #define INPUTREAR 13 //后插法建立链表 #define OUTPUT 14 //输出所有结点的数据值 #define REVERSE 15 //链表逆置 #define MAKEEMPTY 16 //清空链表 #define OPERATOR_COPY 17 //链表间赋值操作——重载等号运算符 #define GETHEAD 18 //返回头结点的地址 void print_description() { cout << "------------------------------>单链表<------------------------------" << endl; cout << "功能选项说明:" << endl; cout << "#0: 退出" << endl; cout << "#1: 拷贝构造链表" << endl; cout << "#2: 计算链表的结点总数" << endl; cout << "#3: 搜索数据值为x的结点并返回" << endl; cout << "#4: 获取第i个结点并返回" << endl; cout << "#5: 获取第i个结点的数据值保存至x,并返回获取成功与否" << endl; cout << "#6: 修改第i个结点的数据值为x" << endl; cout << "#7: 在第i个结点后插入数据值为x的新结点" << endl; cout << "#8: 删除第i个结点,并将被删结点的数据值保存至x" << endl; cout << "#9: 判断表是否为空" << endl; cout << "#10:判断表是否为满" << endl; cout << "#11:表排序——冒泡排序" << endl; cout << "#12:前插法建立链表" << endl; cout << "#13:后插法建立链表" << endl; cout << "#14:输出所有结点的数据值" << endl; cout << "#15:链表逆置" << endl; cout << "#16:清空链表" << endl; cout << "#17:链表间赋值操作——重载等号运算符" << endl; cout << "#18:返回头结点的地址" << endl; cout << "--------------------------------------------------------------------" << endl; } //输入结点编号 template <class T> int get_item() { cout << "> 请输入结点编号,item = "; string s_item; cin >> s_item; while (false == LinkedList<T>::IsNumber(s_item)) { cout << "* 输入有误,请重新输入:"; cin >> s_item; } return atoi(s_item.c_str()); } //输入数据值 template <class T> T get_data() { cout << "> 请输入数据值,data = "; string s_data; cin >> s_data; return LinkedList<T>::StrToTtype(s_data); } //输入结束标志位 template <class T> T get_endtag() { cout << "请输入结束标志值,endTag = "; string s_endTag; cin >> s_endTag; return LinkedList<T>::StrToTtype(s_endTag); } //构造单链表 template <class T> LinkedList<T>* construct_linkedlist() { cout << "\n==> 创建单链表" << endl; LinkedList<T> *linkedList = new LinkedList<T>; return linkedList; } //析构单链表 template <class T> void destory_linkedlist(LinkedList<T>* linkedList) { cout << "\n==> 释放单链表在堆中申请的空间,并将指向该空间的指针变量置为空" << endl; delete linkedList; linkedList = NULL; } //拷贝构造链表 template <class T> void construct_copy(LinkedList<T>* linkedList) { cout << "$ 执行拷贝构造链表函数" << endl; LinkedList<T> cpy_linkedlist = *linkedList; cpy_linkedlist.Output(); } //计算链表的结点总数 template <class T> void length(LinkedList<T>* linkedList) { cout << "$ 执行计算链表的结点总数函数,Length = " << linkedList->Length() << endl; } //搜索数据值为x的结点并返回 template <class T> void search(LinkedList<T>* linkedList) { cout << "$ 执行搜索数据值为x的结点并返回函数" << endl; T data = get_data<T>(); LinkNode<T> *node = linkedList->Search(data); if (NULL == node) { cout << "* 搜索失败" << endl; return; } cout << "* 搜索成功,data = " << node->data << endl; } //获取第i个结点并返回 template <class T> void locate(LinkedList<T>* linkedList) { cout << "$ 执行获取第i个结点并返回函数" << endl; int n_item = get_item<T>(); LinkNode<T> *node = linkedList->Locate(n_item); if ((NULL == node) || (linkedList->GetHead() == node)) { cout << "* 获取失败" << endl; return; } cout << "* 获取成功,item = " << n_item << ",data = " << node->data << endl; } //获取第i个结点的数据值保存至x,并返回获取成功与否 template <class T> void getdata(LinkedList<T>* linkedList) { cout << "$ 执行获取第i个结点的数据值保存至x并返回获取成功与否函数" << endl; T data; int n_item = get_item<T>(); if (false == linkedList->GetData(n_item, data)) { cout << "* 获取失败" << endl; return; } cout << "* 获取成功,item = " << n_item << ",data = " << data << endl; } //修改第i个结点的数据值为x template <class T> void setdata(LinkedList<T>* linkedList) { cout << "$ 执行修改第i个结点的数据值为x函数" << endl; int n_item = get_item<T>(); T data = get_data<T>(); if (false == linkedList->SetData(n_item, data)) { cout << "* 修改失败" << endl; return; } cout << "* 修改成功,item = " << n_item << ",data = " << data << endl; } //在第i个结点后插入数据值为x的新结点 template <class T> void insert(LinkedList<T>* linkedList) { cout << "$ 执行在第i个结点后插入数据值为x的新结点函数" << endl; int n_item = get_item<T>(); T data = get_data<T>(); if (false == linkedList->Insert(n_item, data)) { cout << "* 插入失败" << endl; return; } cout << "* 插入成功,item+1 = " << n_item+1 << ",data = " << data << endl; } //删除第i个结点,并将被删结点的数据值保存至x template <class T> void remove(LinkedList<T>* linkedList) { cout << "$ 执行删除第i个结点并将被删结点的数据值保存至x函数" << endl; T data; int n_item = get_item<T>(); if (false == linkedList->Remove(n_item, data)) { cout << "* 删除失败" << endl; return; } cout << "* 删除成功,item = " << n_item << ",data = " << data << endl; } //判断表是否为空 template <class T> void isempty(LinkedList<T>* linkedList) { cout << "$ 执行判断表是否为空函数,IsEmpty = " << linkedList->IsEmpty() << endl; } //判断表是否为满 template <class T> void isfull(LinkedList<T>* linkedList) { cout << "$ 执行判断表是否为满函数,IsFull = " << linkedList->IsFull() << endl; } //表排序——冒泡排序 template <class T> void sort(LinkedList<T>* linkedList) { cout << "$ 执行表排序——冒泡排序函数" << endl; linkedList->Sort(); } //前插法建立链表 template <class T> void inputfront(LinkedList<T>* linkedList) { cout << "$ 执行前插法建立链表函数" << endl; T endTag = get_endtag<T>(); linkedList->InputFront(endTag); } //后插法建立链表 template <class T> void inputrear(LinkedList<T>* linkedList) { cout << "$ 执行后插法建立链表函数" << endl; T endTag = get_endtag<T>(); linkedList->InputRear(endTag); } //输出所有结点的数据值 template <class T> void output(LinkedList<T>* linkedList) { cout << "$ 执行输出所有结点的数据值函数" << endl; linkedList->Output(); } //链表逆置 template <class T> void reverse(LinkedList<T>* linkedList) { cout << "$ 执行链表逆置函数" << endl; linkedList->Reverse(); } //清空链表 template <class T> void make_empty(LinkedList<T>* linkedList) { cout << "$ 执行清空链表函数" << endl; linkedList->MakeEmpty(); } //链表间赋值操作——重载等号运算符 template <class T> void operator_copy(LinkedList<T>* linkedList) { cout << "$ 执行链表间赋值操作——重载等号运算符函数" << endl; LinkedList<T> cpy_linkedlist; cpy_linkedlist = *linkedList;//或cpy_linkedlist.operator=(*linkedList); cpy_linkedlist.Output(); } //返回头结点的地址 template <class T> void gethead(LinkedList<T>* linkedList) { LinkNode<T> *head = linkedList->GetHead(); cout << "$ 执行返回头结点的地址函数,&first = " << &head << endl; } //单链表操作选择 template <class T> void select_operation(LinkedList<T>* linkedList) { if (NULL == linkedList) { cout << "* 没有构造单链表,请先构造单链表。" << endl; return; } string s_operation; while (s_operation != "0") { cout << "\n==> 请输入功能选项编号(按\"0\"退出程序):"; cin >> s_operation; while (false == LinkedList<T>::IsNumber(s_operation)) { cout << "* 输入有误,请重新输入:"; cin >> s_operation; } int n_operation = atoi(s_operation.c_str()); switch (n_operation) { case EXIT://退出 { cout << "$ 退出程序" << endl; break; } case CONSTRUCT_COPY://拷贝构造链表 { construct_copy(linkedList); break; } case LENGTH://计算链表的结点总数 { length(linkedList); break; } case SEARCH://搜索数据值为x的结点并返回 { search(linkedList); break; } case LOCATE://获取第i个结点并返回 { locate(linkedList); break; } case GETDATA://获取第i个结点的数据值保存至x,并返回获取成功与否 { getdata(linkedList); break; } case SETDATA://修改第i个结点的数据值为x { setdata(linkedList); break; } case INSERT://在第i个结点后插入数据值为x的新结点 { insert(linkedList); break; } case REMOVE://删除第i个结点,并将被删结点的数据值保存至x { remove(linkedList); break; } case ISEMPTY://判断表是否为空 { isempty(linkedList); break; } case ISFULL://判断表是否为满 { isfull(linkedList); break; } case SORT://表排序——冒泡排序 { sort(linkedList); break; } case INPUTFRONT://前插法建立链表 { inputfront(linkedList); break; } case INPUTREAR://后插法建立链表 { inputrear(linkedList); break; } case OUTPUT://输出所有结点的数据值 { output(linkedList); break; } case REVERSE://链表逆置 { reverse(linkedList); break; } case MAKEEMPTY://清空链表 { make_empty(linkedList); break; } case OPERATOR_COPY://链表间赋值操作——重载等号运算符 { operator_copy(linkedList); break; } case GETHEAD://返回头结点的地址 { gethead(linkedList); break; } default: { cout << "* 请输入正确的功能选项编号" << endl; break; } } } } int main(int argc, char* argv[]) { print_description(); LinkedList<int> *linkedList = construct_linkedlist<int>(); select_operation(linkedList); destory_linkedlist(linkedList); system("pause"); return 0; }
3. 单链表的优缺点
3.1 优点
- 没有空间限制,存储元素的个数无上限,基本只与内存空间大小有关。
- 插入和删除元素速率高。
3.2 缺点
- 占用额外的空间以存储指针(浪费空间)。
- 随机存取元素速率低。
- 单链表要找一个数,必须要从表头开始找起,十分麻烦。
3.3 单链表的适用情况
- 适用于需要进行大量增加和删除元素操作而对访问元素无要求的,及预先无法确定表的大小的程序。
参考文献:
[1]《数据结构(用面向对象方法与C++语言描述)(第2版)》殷人昆——第二章
[2]《C/C++常用算法手册》秦姣华、向旭宇——第二章
[3] 百度搜索关键字:单链表的优缺点