与带环链表相关的一些操作
带环链表的概念
显然,环在链表中不能出现在头部和中部,因为这样的话用来实现连接的结点需要两个指针(一个指向后继结点,另一个指向前方的某个结点来形成环),这样会破坏链表原有的结构。所以环只能出现在链表的尾部,只需要将尾结点与之前的任意结点相连即可。为了表示链表的状态,我们用整数pos表示为尾结点连接到链表的位置(索引为0),若pos=-1,则表示链表不带环。
如何处理不带环链表使它变成带环链表
我们所进行的处理要达到这样一个效果,使一个不带环的链表变为一个带环的链表,并且可以指定尾结点连接到链表的位置(pos)。
//思路:先找到尾结点,在找到索引为pos的结点,将二者相连
void ChangeToCircle(LinkList L,int pos)
{
//pos为-1则不做操作
if (pos == -1) return;
Node* s, * r;
int i;
//先找到链表的尾结点
for (s = L->next; s->next; s = s->next);
//再找到索引为pos的结点
for (r = L, i = 0; i <= pos; i++) r = r->next;
//将链表尾结点的尾指针指向索引为pos的结点
s->next = r;
}
如何判断链表是否带环
若带环,则返回true,否则返回false。
//思路:快慢指针法,快指针一次走两个结点,慢指针一次走一个结点,若链表带环,那么两者一定会相遇,否则快指针会先一步走完
bool CircleJudge(LinkList L)
{
Node* fast, * slow;
//循环条件:不确定链表结点数的奇偶,所以fast和fast->next都有可能是NULL但只要出现一种情况,就说明链表没有环
for (fast = slow = L; fast != NULL && fast->next != NULL;) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) return true;
}
return false;
}
如何计算链表环的长度
//思路:当快慢指针相遇时,快指针所走的路程是慢指针所走路程的两倍,并且快指针比慢指针多走一个环的路程,因此慢指针所走的路程就是链表的长度
int CircleLen(LinkList L)
{
Node* fast, * slow;
int len = 0;
for (fast = slow = L; fast != NULL && fast->next != NULL;) {
fast = fast->next->next;
slow = slow->next;
len++;
if (fast == slow) return len;
}
return -1;
}
如何寻找链表环的入口结点
设从链表头走到环入口需要a步,从环入口走到相遇点需要b步,再从相遇点走回环入口需要c步,那么第一次相遇时慢指针走过(a+b)步,快指针走过(a+b+c+b)步。由a+b+c+b=2(a+b)得a=c。
//思路: 当快慢指针相遇后,将快指针的速度调整到与慢指针一致,然后让任意一个指针从开头跑,另一个指针从相遇点跑,他们会在环的入口点相遇
Node* CirEntrFind(LinkList L)
{
Node* fast, * slow;
for (fast = slow = L; fast != NULL && fast->next->next != NULL;) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) {
fast = L;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return NULL;
}