一、带表头结点的单向链表如下:
结构:表头无数据,或者说是垃圾数据,表头结点在整个链表中起到领头的作用,最后的指针指向了NULL指针,我们可以对其进行的操作会更加的简便,比如说在遍历链表的时候,可以不用像没有表头结点链表那样考虑头结点是不是空,在删除和中间插入的时候都给编码人员带来了方便。
二、带表头的单向循环链表
是单向链表的进一步改进方式,链表尾指针重新指向了表头;在删除和插入的时候可以不用考虑尾指针是否为空,因为他们的结构是循环的,为它操作的代码量可以更为精简。
三、本例采用带表头的单向循环链表
并使用C++模板类实现链表的增、删、改、查和排序,因为是循环链表,所以不存在逆置(Reverse)的说法,无法让一个有数据的尾结点代替垃圾值的表头结点, 可能是可以,就是把表头结点排开,让后面的所有结点进行逆转,最后表头结点指向的是原来的尾结点。
Link.h
类模版的声明和实现尽量放在一个文件中,在多文件编程中,定义和声明放在不同文件中可能会引发意想不到的结果。
/***************************************************** copyright (C), 2019-2020, Lighting Studio. Co., Ltd. File name: Author:xozofunny Version:0.1 Date: Description: Funcion List: *****************************************************/ #ifndef _LINK_H_ #define _LINK_H_ #include <iostream> using namespace std; /***************************************************** 以后创建新结点就是实例化这个类 *****************************************************/ template <class T> class Node{ public: T data; Node<T> *next; }; /**************************************************** 用来完成链表的基本操作 ****************************************************/ template <class T> class List : public Node<T>{//继承结点类使其拥有相应权限 protected: Node<T> *head;//链表头结点 Node<T> *new_node;//链表新结点 //Node<T> *next;//链表指针域 int count;//记录结点的个数(不算表头结点) public: List();//构造1--创建链表基类型 List(const T i);//构造2--給当前链表赋值一次 //List(const List& obj);//拷贝构造 void Create_Space(Node<T> **new_node);//封装new分配 bool Is_Empty();//判断链表是否已清空,一般不会清空 bool Insert_Tail(int cont); bool Delete_Node(const T& loc); void Sort_Link(void);//链表排序 //void Reverse_Link(void);//链表转置 double Get_Rand(int Min, int Max);//获取一个随机的浮点数 //全局重载输出运算符,用来输出整个链表 //因为友元函数是类外函数,所以得另外定义一个函数模板 template <typename U> friend ostream& operator<<(ostream &out, List<U>& obj); bool Clear();//清空链表,但保持其结构 ~List();//析构,释放链表 }; /************************************************** 功能:封装new分配函数 入参:新结点二级地址 规则:至少要为结点分配一次,一直不成功则仅分配十次 C++没有二级引用,只有二级指针 **************************************************/ template <class T> void List<T>::Create_Space(Node<T> **new_node) { int count = 10; do { *new_node = new Node<T>; count--; } while (*new_node == NULL && count); } /************************************************* 功能:判断链表是否为空,(因为我是带表头的) 空表示基类型仍然存在,但不带任何数据 入参:无 返回值:bool *************************************************/ template <class T> bool List<T>::Is_Empty() { if(head->next == head) { //cout<<"List was Empty!"<<endl; return 1; } else return 0; } /************************************************* 默认构造创建链表基类型,并创建表头结点 且为循环链表 *************************************************/ template <class T> List<T>::List() { cout<<"Default constructor was called"<<endl; Create_Space(&head);//当前对象的二级地址 head->next = head; count = 0; } /*************************************************** 功能:重载构造函数,为当前链表主动添加一个试水值 主调方:无 被调方:创建对象的时候 入参:一个模版类型的值 返回值:无 **************************************************/ template <class T> List<T>::List(const T i) { cout<<"Costom constructor was called"<<endl; Create_Space(&head); Create_Space(&new_node); head->next = new_node; new_node->data = i; new_node->next = head; count = 1; } /***************************************************** 全局函数用于生成指定的随机数可以是整形,也可以是浮点型 *****************************************************/ template <class T> double List<T>::Get_Rand(int Min, int Max) { //计算 0,1之间的随机小数,得到的值域近似为(0,1),两个数没有它义,只是为了让其精度高一点 double m1=(double)(rand()%101)/101; Min++;//将 区间变为(min+1,max), double m2=(double)((rand()%(Max-Min+1))+Min);//计算 min+1,max 之间的随机整数,得到的值域为[min+1,max] m2=m2-1;//令值域为[min,max-1] return m1+m2;//返回值域为(min,max),为所求随机浮点数 } /***************************************************** 函数模版用于排序,排序方法---冒泡 返回值:无(带表头的模板类) 入参:无 *****************************************************/ template <class T>//链表插(mao)入(pao)排序 void List<T>::Sort_Link(void) { T tmp; Node<T> *p; p = head->next; #if 0 while(p != head && i < count) { j = i - 1; tmp = p->data;//偏移一个结点的距离 q = p; p = p->next; while(j >= 0 && q->data > tmp) { q->next->data = q->data; j--; q -= seek; } if(j != i - 1) { q->next->data = tmp; } i++; } #endif for(int i = 0; i < count -1; i++) { p = head->next; int isSorted = 1; for(int j = 0; j < count - 1 - i; j++) { if(p->data > p->next->data) { tmp = p->data; p->data = p->next->data; p->next->data = tmp; isSorted = 0; } p = p->next; } if(isSorted) break; } } /***************************************************** 删除某一单链循环表的结点 返回值:bool 入参:欲删除的位置 *****************************************************/ template <class T> bool List<T>::Delete_Node(const T& loc) { int t = count; if(Is_Empty()){ cout<<"The Link was Empty!"<<endl; return 0; } else{ Node<T> *p,*q; p = q = head->next; while(p->next != head) { if(p->next->data == loc) { q = p->next; p->next = q->next; delete q; t--;count--; break; } p = p->next; } } if(t == count) return 1; else return 0; } /************************************************** 功能:为链表进行尾插工作 入参:描述创建的结点个数 返回值:bool *************************************************/ template <class T> bool List<T>::Insert_Tail(int cont) { Node<T> *p; T j = 0; if(Is_Empty())//如果为空 { p = head->next; for(int i = 0; i < cont; i++) { Create_Space(&new_node);//先创建结点 j = (T)Get_Rand(1, 10); cout<<"You will create rand number:"<<j<<" for new_node value"<<endl; new_node->data = j;//生成1-10的随机数 p->next = new_node; new_node->next = head; p = p->next; count++; } } else//不为空,先便历至空 { p = head->next; while(p->next != head) { p = p->next; } for(int i = 0; i < cont; i++) { Create_Space(&new_node);//先创建结点 j = (T)Get_Rand(1, 10); cout<<"You will create rand number:"<<j<<" for new_node value"<<endl; new_node->data = j; p->next = new_node; new_node->next = head; p = p->next; count++; } } if(Is_Empty()) { cout<<"Insert fail"<<endl; return 1; } else{ cout<<"Insert success"<<endl; return 0; } } /***************************************************** 重载运算符<<将链表作为对象输出整个链表数据 因为友元函数是类外函数,所以得另外定义一个函数模板 ******************************************************/ template <typename U> ostream& operator<<(ostream& out, List<U>& obj) { Node<U> *p = obj.head->next; //int i = 1; if(obj.Is_Empty()) { out<<"The Link was Empty!"<<endl; } else{ out<<"You have "<<obj.count<<" Node in there"<<endl;; out<<"Link head->"; while(p != obj.head) { out<</*i<<"st:"<<*/p->data<<"->"; p = p->next; //i++; } cout<<"NULL"; } return out; } /*********************************************** 函数名:Clear() 主调方:无 被调方:对象 功能:清空链表,但保留其逻辑结构 入参:无 返回值:bool类型 **********************************************/ template <class T> bool List<T>::Clear() { Node<T> *p; if(!Is_Empty()) { while(head->next != head) { p = head->next; head->next = p->next; delete p; } if(head->next == head) return 1; else return 0; } return 0; } /******************************************* 功能: 析构函数,用于释放链表 主调方:清空链表Clear() 被调方:对象消亡时主动调用 入参:无 返回值:无 ******************************************/ template <class T> List<T>::~List() { Node<T> *p; Clear(); p = head; head = NULL; delete p; cout<<"Destructor was called successfully!"<<endl; } #endif
main.cpp
#include "link.h" int main() { srand((unsigned)time(NULL));//seed List<float> obj; obj.Insert_Tail(10); cout<<obj<<endl; cout<<"- - - - - - - - - - - - - - Sort Link - - - - - - - - - - - - - - - -"<<endl; obj.Sort_Link(); cout<<obj<<endl; List<int> *pobj = new List<int>(5); pobj->Insert_Tail(12); cout<<*pobj<<endl; cout<<"- - - - - - - - - - - - - - Sort Link - - - - - - - - - - - - - - - -"<<endl; pobj->Sort_Link(); cout<<*pobj<<endl; cout<<"- - - - - - - - - - - - - - Delete Node - - - - - - - - - - - - - - -"<<endl; int loc; cout<<"input your data>>"<<endl; cin>>loc; pobj->Delete_Node(loc); cout<<*pobj<<endl; delete pobj; return 0; }
makefile:
.PHONY : clean cc = g++ target = Link CPPFLAGS = -I ./ CFLAGS = -Wall -g obj = main.o $(target):$(obj) $(cc) -o $@ $^ %.o:%.cpp $(cc) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ clean: rm -f $(obj) rm -i $(target)
编译,运行:
最后析构函数用于释放链表。。
233