单链表属于基本数据结构中的线性结构,它应用于很多数据结构中,例如栈,队列等,作用广泛。在数据结构中我们习惯把关于单链表的一系列操作制作为一个ADT(Abstract Data Type)使用,在C++中把它封装为一个类,使用起来更是方便。
关于单链表的操作包括插入(包括头插和尾插)、删除、查找、检查是否有环、环的长度、环的入口等操作。以下就是我的代码实现:
每个结点的数据类型应该是一个结构体类型:
struct Node { Node(int data=0) { _data = data; _pnext = NULL; } int _data; Node *_pnext;
单链表类的定义:
int main()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; };
{
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了。*^_^*