链表题的分类
1. 链表题中很多需要靠快慢指针去定位,快指针先走,慢指针负责后勤,万一快指针执行了某种节点删除工作,慢指针还是能够弥补回来的:比如jz18_删除链表的节点,慢指针越过快指针将其删除;
2. 快慢指针还可以用于定位,jz22_链表中倒数第k个节点,先让快指针走k步,慢指针和快指针再同时出发隔了k个位置,当快指针到达最后一个有val节点时,这时慢指针就到达倒数第k个指针了,这时对其进行操作;
3.
160.相交链表(中智行)
// 题解: 先遍历自己再遍历对方
// 时间复杂度O(m + n) 空间复杂度O(1)
ListNOde* getIntersectionNode(ListNode* headA, ListNode* headB){
ListNode* p = headA, *q = headB;
while(p!=q){
// p=q 包含空指针的情况,也可以返回
// if用if(p)而不是if(p->next),当面临两个/// 链表不相交时,也可以返回相交节点
if(p) p=p->next;
else p=headB;
if(q) q=q->next;
else q=headA;
}
return p;
}
// hashset方法
// 使用一个hashset,遍历一个链表,hashset中存放其所有指针
// 遍历另一个链表,去hashset中找相同指针
// 时间复杂度O(m + n) 空间复杂度O(m) 或 O(n)
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB)
{
std::unordered_set<ListNode*> set;
ListNode* cur_a = headA;
// 先建立A链表的hash_set
while (cur_a)
{
set.insert(cur_a);
cur_a = cur_A->next;
}
ListNode* cur_b = headB;
// 再在A链表中去搜索
while (cur_b)
{
if (set.find(cur_b) != set.end()) // 找到了
{
return cur_b;
}
cur_b = cur_b->next;
}
return nullptr;
}
142.环形链表2
// 思路主要是快慢指针
// 快指针速度为2,慢指针为1,当在环中相遇时,快指针是慢指针路程的两倍
// 表示为:2(x+y)=x+y+z+y,x=z
ListNode* detectCycle(ListNode* head)
{
ListNode* fast = head, *slow = head;
while(fast)
{
fast = fast->next;
slow = slow->next;
if(fast) fast = fast->next;
else break; // 第一次fast->next为null的情况
// 第一次相遇,slow回到原点
if(fast == slow)
{
slow = head;
while(fast!=slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
}
21.合并两个有序链表
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
{
ListNode* pHead = new ListNode*(-1);
ListNode* prev = pHead;
while(l1!=nullptr && l2!=nullptr)
{
if(l1->val<l2->val)
{
prev->next=l1;
l1=l1->next;
}
else
{
prev->next=l2;
l2=l2->next;
}
prev=prev->next;
}
prev->next= l1==nullptr?l2:l1;
return pHead->next;
}
61.旋转链表
ListNode* rotateRight(ListNode* head, int k)
{
ListNode* pHead = head;
List
int n = 0;
while(pHead)
{
n++;
pHead =pHead->next;
}
//for(auto p=head;p;p=p->next) n++;
k%=n;
ListNode* first, *second = head;
while(k--) first=first->next;
while(first)
{
first = first->next;
second = second->next;
}
first->next=Head;
Head=second->next;
second->next = null;
return Head;
}
jz35.复制复杂链表
class Solution
{
public:
Node* copyRandomList(Nodes* head)
{
if(!head) return nullptr;
unordered_map<Node*, Node*> mp;
Node* t = head;
while(t)
{
//刚开始的深拷贝,将原先的节点深拷贝后的儿子放入map与其父亲一一对应
mp[t] = t;
t = t->next;
}
t = head;
while(t)
{
// next与random指针的复制对应关系
if(t->next) mp[t]->next = mp[t->next];
if(t->random) mp[t]->random = mp[t->random];
t = t->next;
}
return mp[head];
}
}
// 参考:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/unordered_mapha-xi-biao-shi-xian-fu-za-lian-biao-s/
反转链表
涉及到链表的操作,一定要在纸上把过程先画出来,再写程序
主要思路:
定义一个cur和pre指针,随着遍历而反转
纸上画图!!!
双指针的方法,cur找一个前驱节点pre,每一次cur与前驱节点交换值的时候,要临时记录cur的next
ListNode* ReverseList(ListNode* pHead){
// 找到前驱节点
ListNode* pre=nullptr;
ListNode* cur = pHead;
while(cur){
// 1. cur指向后驱节点
ListNode* tmp = cur->next; // 提前保存
cur->next=pre;
// 2. 位置往前移动
pre=cur;
cur=tmp;
#cur = cur->next; // 因为cur->next已经指向了指向了前向节点
}
return pre;
}
反转链表二
主要思路:
参考官方题解迭代法
链表的题先画图,然后结合图去理解程序
class Solution{
ListNode reverseBetween(ListNode head, int m, int n){
// Empth list
if(head == null){
return null;
}
// 1. move the two pointers until they reach the proper starting point in the List
// 已到要反转的子链位置上(第二段)
ListNode cur=head, prev=null; // 初始化节点cur,head
while(m>1){
prev = cur;
cur=cur.next;
m--;
n--;
}
// 2.the two pointers that will fix the final connections
// 初始化con tail 用于中间段与首段与第三段的连接
ListNode con = perv, tail=cur;
// 3.Iteratively reverse the nodes until n becones 0.
// 反转中间段
ListNode tmp = null; // 初始化tmp
while(n>0){
//tmp;
tmp = cur.next;
cur.next=prev;
prev=cur; // 前进
cur=tmp; // 前进
n--
}
// 4. Adjust the final connections as explained in the algorithm
// 完成中间段与首尾两段的连接,靠con,tail指针完成
if(con != null){
con.next=prev;
}
else{
head=prev; // 只有一个节点的情况
}
tail.next=cur;
return head;
}
};