判断一条链表是否是环形链表的两种方法

环形链表的示意图如下
判断一条链表是否是环形链表的两种方法_第1张图片
我们假设定义的单链表如下

struct ListNode {
     int val;
     ListNode *next;
     ListNode(int x) : val(x), next(NULL) {}
};

方法一:哈希表法
这里我们采用set这一关联容器。设置set的下标为ListNode*,遍历该链表的每一个结点,将未出现过的链表结点存储至set中,会出现两种情况:

  1. 发现指向某一结点的指针已经存储过了,就说明这个链表是环形的。
  2. 发现指向某一结点的指针是NULL,则说明这条链表有尾巴,不是环形的。
bool IsCycle(ListNode* head){
	set<ListNode*> Node;
	while(head){
		if(Node.find(head)==Node.end()){
			Node.insert(head);//如果未找到,则将该指针存储到set容器中
		}else return true;//如果找到了,则属于第一种情况
		head=head->next;//移动指针指向下一个元素
	}
	return false;//如果跳出循环,则说明head是NULL,属于第二种情况
}

该算法时间复杂度为 O(n),空间复杂度为 O(n)

方法二:快慢指针法
我们可以思考一个问题:两位运动员在环形跑道上什么情况下会相遇?
是当他们速度一致吗?显然不是,应该是当他们有速度差的时候,快的会逐渐追上慢的。
在这里我们只是将运动员换成了指针,将赛道换成了环形链表。
于是有了下面的代码:

bool IsCycle(ListNode* head){
	ListNode *fast, *slow;
	//首先要排除该链表为空或只有一个节点,两种情况均不可能是循环链表
	//排除的其他原因是链表为空时,slow和fast指针均会失效
	//当只有一个节点时,fast则会失效
	if(head==NULL || head->next==NULL) return false;
	fast=head->next;//从起点开始,fast就比slow快一步
	slow=head;
	while(fast!=slow){
		//如果fast或fast->next为空:则该链表不是循环链表
		if(fast==NULL || fast->next==NULL)  return false;
		//在“跑步”过程中,fast每跑两步,slow只跑一步
		fast=fast->next->next;
		slow=slow->next;
	}
	return true;//跳出循环也就意味着fast追上了slow,该链表是循环链表
}

该算法的时间复杂度为 O(n),空间复杂度为 O(1)

你可能感兴趣的:(数据结构,单链表,指针,算法,数据结构,leetcode)