⭐链表与数组的相爱相杀
线性表有两种存储方式—链表和数组
数组,所有的元素都连续的存储于一段内存中,且每个元素占用的内存大小相同,所以数组具备了通过索引能够快速访问元素的本领。但连续存储的缺点也很明显,增加容量时,当前数组的最后一个元素的后面没有足够的空间开辟时,我们需要重新申请一块内存,将原有的值重新拷贝到新的数组中,最后释放原来所占的内存。
数组删除元素时,要将被删除元素之后的所有元素都向前移动,以保证数组能够连续,时间复杂度为O(n)。增加元素时需要将插入位置后面的所有元素向后移动,时间复杂度为O(n),当容量不足时,会出现增容现象。
链表,由若干个节点构成,每个节点由数据域和指针域构成,在物理内存上不连续。由于它独特的存储方式,所以可以高效的在指定元素的后面进行插入和删除,时间复杂度均为O(1)。
那么重点来了,因为链表无法高效的获取长度,无法快速的访问元素,所以诸如“判断链表中是否有环”,“返回链表开始入环的第一个节点”,“两个单链表相交的起始节点”,“删除链表的倒数第 n 个节点”,这类问题变成了我们需要掌握理解的一类知识点。
解决这类问题最好的办法就是采用“双指针”技巧
⭐双指针技巧
一、环形链表
题目描述:给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例一:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例二:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
代码展示:(c++)
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL)
return false;
ListNode *fast=head;
ListNode *slow=head;
while(fast && fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(slow==fast)
return true;
}
return false;
}
};
😊该题思路:
采用快慢指针解决问题,快指针每次走两步,慢指针每次走一步。当该链表存在环,则快指针和慢指针一定会相遇,它们都从head出发,在环中进行追击问题,快指针