单链表属于基本数据结构中的线性结构,它应用于很多数据结构中,例如栈,队列等,作用广泛。在数据结构中我们习惯把关于单链表的一系列操作制作为一个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了。*^_^*