C++实现带头结点单链表

来源:互联网 发布:ie浏览器java插件下载 编辑:程序博客网 时间:2024/05/23 00:02

单链表属于基本数据结构中的线性结构,它应用于很多数据结构中,例如栈,队列等,作用广泛。在数据结构中我们习惯把关于单链表的一系列操作制作为一个ADT(Abstract Data Type)使用,在C++中把它封装为一个类,使用起来更是方便。

关于单链表的操作包括插入(包括头插和尾插)、删除、查找、检查是否有环、环的长度、环的入口等操作。以下就是我的代码实现:
每个结点的数据类型应该是一个结构体类型:
struct Node{Node(int data=0){_data = data;_pnext = NULL;}int _data;Node *_pnext;
单链表类的定义:
class LinkList{public:LinkList(){_head = new Node;cout<<"LinkList()"<<endl;}~LinkList(){if (_head == NULL){return;}if (isLoop()){Node *tmp = _head->_pnext;Node *p = NULL;while (tmp != getFirstLoopNode()){tmp = tmp->_pnext;}tmp->_pnext = NULL;}Node *tmp = _head->_pnext;Node *p = NULL;while (tmp != NULL){p = tmp;tmp = tmp->_pnext;delete p;}delete _head;cout<<"~LinkList()"<<endl;}void insertHead(int data){Node *tmp = new Node(data);tmp->_pnext = _head->_pnext;_head->_pnext = tmp;}void insertTail(int data){Node *tmp = _head->_pnext;while (tmp->_pnext != NULL){tmp = tmp->_pnext;}tmp->_pnext = new Node(data);tmp->_pnext->_pnext = NULL;}void deleteData(int data){Node *tmp = _head->_pnext;Node *p = _head;while (tmp->_data != data && tmp->_pnext != NULL){p = tmp;tmp = tmp->_pnext;}p->_pnext = tmp->_pnext;delete tmp;}void show(){Node *tmp = _head->_pnext;while (tmp != NULL){cout<<tmp->_data<<" ";tmp = tmp->_pnext;}cout<<endl;}//是否有环bool isLoop(){Node*fast = _head;Node*slow = _head;while (fast != NULL && fast->_pnext != NULL){fast = fast->_pnext->_pnext;slow = slow->_pnext;if (fast == slow){return true;}}return false;}void creat_loop()//创建有环链表{Node *tmp = _head->_pnext;while (tmp->_pnext != NULL){tmp = tmp->_pnext;}tmp->_pnext = _head->_pnext;}//获取单链表环的第一个节点Node* getFirstLoopNode(){Node *fast = _head;Node *slow = _head;while (fast != NULL && fast->_pnext != NULL)//快指针走的步数是慢指针走的步数的两倍{fast = fast->_pnext->_pnext;slow = slow->_pnext;if (fast == slow){break;}}slow = _head;while (slow != fast){slow = slow->_pnext;}return slow;}//获取环的长度int getLoopLength(){Node *fast = _head;Node *slow = _head;while (fast != NULL && fast->_pnext != NULL){fast = fast->_pnext->_pnext;slow = slow->_pnext;if (fast == slow){break;}}slow = slow->_pnext;int count = 1;while (slow != fast){slow = slow->_pnext;count++;}return count;}private:Node *_head;};
int main()
{
LinkList list;
for (int i=0;i<4;i++)
{
list.insertHead(rand()%10);
}
list.show();
for (int i=1;i<6;i++)
{
list.insertTail(i*10);
}
list.deleteData(0);
list.show();
list.creat_loop();
if (list.isLoop())
{
cout<<"have a loop"<<endl;
cout<<"loop entrance:"<<list.getFirstLoopNode()->_data<<endl;
cout<<"loop length:"<<list.getLoopLength()<<endl;
}
return 0;
}      
判断链表是否有环,通常的办法就是使用快慢指针,快慢指针初始时指向同一个位置,这里指向头结点(fast = _head;slow = _head);快指针每次向前走两步(fast = fast->_pnext->_pnext;),慢指针每次走一步( slow = slow->_pnext;);当快指针遇到慢指针时,说明该链表有环。
      计算环的长度,在环上相遇后,记录第一次相遇点为fast=slow,添加计数器count=0;fast不动,slow每次走一步,count++,等到fast==slow时,count的值就是环的长度。
      判断环的入口,假设头结点距离环的入口的距离是len,在环上相遇时,假设slow走了S步,则fast走了2S步,并且2S=S+len,即S=len,所以当fast和slow相遇时,slow从头结点出发,当遇到fast时的位置就是环的入口结点。
      上述代码中由于创建了有环的链表,所以在析构函数中添加了有环链表的析构过程,相对来说,这个链表还不算完整,比如对有环链表的插入结点、删除结点以及打印结点信息的方法都未实现,原理上与析构的过程其实是一致的,基本原理懂了,一切都so easy了。*^_^*