一,链表整体逆序
方法一:就地逆置
思路:
- 代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode*new_head=NULL;
while(head)
{
ListNode*next=head->next;
head->next=new_head;
new_head=head;
head=next;
}
return new_head;
}
};
- leetcode链接
206.反转链表
方法二:头插法
1.空间复杂度和常量个数没有关系
2.头节点和头指针的初始化
3.链表连接的顺序【重点】
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode temp(0);
while(head)
{
ListNode*next=head->next;
//temp.next=head;//注意先后顺序
head->next=temp.next;
temp.next=head;
head=next;
}
return temp.next;
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/lian-biao-ni-zhi-tou-cha-fa-by-xia-mu-la-ktoi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二,链表指定位置逆序
(https://leetcode.com/problems/intersection-of-two-linked-lists/description/)
- 思路
1.找到开始逆序的结点,记录它的前驱,用于逆序后的拼接
2.开始逆置,多少个,从哪开始。
3.拼接,注意特殊情况,边界情况
head为什么到5了,而不是2.【看代码】
- 代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
int chang_len=right-left+1;//计算交换的节点个数
ListNode*pre_head=NULL;//前驱
ListNode*result=head;
while(head&&--left)
{
pre_head=head;
head=head->next; //head移动到交换的节点
}
ListNode*tail=head;
ListNode*new_head=NULL;
while(head&&chang_len)//链表逆置
{
ListNode*next=head->next;
head->next=new_head;
new_head=head;
head=next;
chang_len--;
}
tail->next=head;//连接
if(pre_head)
{
pre_head->next=new_head;
}
else
{
result=new_head;//注意left=1的情况
}
return result;
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/lian-biao-ji-qiao-xiang-guan-by-xia-mu-l-klpk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
三,求两个链表的交点
方法一,使用set求交集
在a中找到和b第一个重复的地址,利用了set的特性
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
#include<set>
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
std::set<ListNode*>set_NODE;
while(headA)
{
set_NODE.insert(headA);
headA=headA->next;
}
while(headB)
{
if(set_NODE.find(headB)!=set_NODE.end())
return headB;
else
headB=headB->next;
}
return NULL;
}
};
方法二,空间复杂度O(1)
看图思路很直白
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
int getLen(ListNode*head)
{
int i=0;
while(head)
{
i++;
head=head->next;
}
return i;
}
ListNode* forwardLog(int long_len,int short_len,ListNode*head)
{
int del=long_len-short_len;
while(head&&del)//while(del)空指针?
{
head=head->next;
del--;
}
return head;
}
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int a,b;
a=getLen(headA);
b=getLen(headB);
if(a>b)
{
headA=forwardLog(a,b,headA);
}
else
{
headB=forwardLog(b,a,headB);
}
while(headA&&headB)
{
if(headA==headB)
{
return headA;
}
headA=headA->next;
headB=headB->next;
}
return NULL;
}
};
四,链表求环
方法一,使用set求环起始节点
方法二,快慢指针赛跑 【重点】
理解a=c是什么意思
看图中结论
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode*fast;
ListNode*slow;
fast=slow=head;
while(fast)
{
fast=fast->next;
slow=slow->next;
if(!fast)
{
return false;
}
fast=fast->next;
if(fast==slow)
{
return true;
}
}
return false;
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/lian-biao-qiu-huan-by-xia-mu-lao-zhang-r-1lva/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
五,复杂的链表的深度拷贝
1.map存储的是【原来的】链表地址和序号,使用map是方便后面取出对应地址的序号
2. node_map[ptr]是一个地址
3.node_map[ptr->random]是另一个地址
4.new Node(ptr->val)
看注释,这是构造函数,
Node(int _val) {
val = _val;//他没有,所以要初始化赋值
next = NULL;//他俩都有默认值
random = NULL;
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
vector<Node*>node_vec;
map<Node*,int>node_map;
Node*ptr=head;//从头开始
int i=0;
while(ptr)
{
node_vec.push_back(new Node(ptr->val));//初始化吗
node_map[ptr]=i;//键值对,序号和地址
ptr=ptr->next;
i++;
}
node_vec.push_back(0);//尾结点置空
ptr=head;//再遍历一遍
i=0;
while(ptr)
{
node_vec[i]->next=node_vec[i+1];
if(ptr->random)
{
int id=node_map[ptr->random];
node_vec[i]->random=node_vec[id];
}
ptr=ptr->next;
i++;
}
return node_vec[0];
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer/solution/fu-za-de-lian-biao-de-shen-du-kao-bei-by-v8lz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
当成图进行深度遍历,哈希标记,回溯
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
unordered_map<Node*,Node*>visited;
Node* copyRandomList(Node* head) {
if(head==NULL){
return NULL;
}
if(visited.find(head)!=visited.end()){
return visited[head];//当前节点已被访问并创造过了,返回它对应的新节点即可
}
Node*node=new Node(head->val);
visited[head]=node;
node->next=copyRandomList(head->next);
node->random=copyRandomList(head->random);
return node;
}
};
六,排序链表的合并(2个)
设置临时头结点的原因【老问题了】
备份头结点,然后往下跑,最后返回头结点
为什么临时头结点不是指针【语法问题】
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode temp_head(0);
// new_head->next=nullptr;
ListNode*new_head=&temp_head;
while(l1&&l2)
{
if(l1->val<l2->val)
{
new_head->next=l1;
l1=l1->next;
}
else
{
new_head->next=l2;
l2=l2->next;
}
new_head=new_head->next;
}
if(l1)
{
new_head->next=l1;
}
else if(l2)
{
new_head->next=l2;
}
return temp_head.next;
}
};
七,排序链表的合并(多个)
LeetCode 23. Merge k Sorted Lists
方法一,暴力合并
方法二,排序后合并【有老问题】
- 注意边界条件 for(int i=1;i<node_vec.size();i++)
- size和下标差1,下标是从0开始计数,size是从1开始计数
- 这是老问题了
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
#include<algorithm>
bool cmp(const ListNode*a,const ListNode*b)
{
return a->val<b->val;
}
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
vector<ListNode*>node_vec;
for(int i=0;i<lists.size();i++)
{
ListNode*head=lists[i];
while(head)
{
node_vec.push_back(head);
head=head->next;
}
}
if(node_vec.size()==0)
{
return NULL;
}
sort(node_vec.begin(),node_vec.end(),cmp);
for(int i=1;i<node_vec.size();i++)//不懂边界条件设置原因
{
node_vec[i-1]->next=node_vec[i];
}
node_vec[node_vec.size()-1]->next=NULL;
return node_vec[0];
}
};
方法三,分治后相连【重点】
1.分治思想
2.临时头结点的设立【老问题了】
3.return temp.next;//不返回下一个节点,就多了一个头结点了,后面的合并没法算
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
ListNode*mergeTwoLists(ListNode*l1,ListNode*l2)
{
ListNode temp(0);
ListNode*head=&temp;
while(l1&&l2)
{
if(l1->val<l2->val)
{
head->next=l1;
l1=l1->next;
}
else
{
head->next=l2;
l2=l2->next;
}
head=head->next;
}
if(l1)
{
head->next=l1;
}
if(l2)
{
head->next=l2;
}
return temp.next;//不返回下一个节点,就多了一个头结点了,后面的合并没法算
}
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size()==0)
{
return NULL;
}
if(lists.size()==1)
{
return lists[0];
}
if(lists.size()==2)
{
return mergeTwoLists(lists[0],lists[1]);
}
int mid=lists.size()/2;
vector<ListNode*>sub1_list;
vector<ListNode*>sub2_list;
for(int i=0;i<mid;i++)
{
sub1_list.push_back(lists[i]);
}
for(int i=mid;i<lists.size();i++)
{
sub2_list.push_back(lists[i]);
}
ListNode*l1=mergeKLists(sub1_list);
ListNode*l2=mergeKLists(sub2_list);
return mergeTwoLists(l1,l2);//收口
}
};
八,链表划分
1.临时结点的创建【经常犯的语法错误】
设置临时头结点问题
没有初始化,导致后面无法判断空指针
2.后期调用临时结点的成员用什么符号
3.临时结点是空节点,注意尾部处理
4.结点向后移动
ListNode* partition(ListNode* head, int x) {
ListNode*more_head;
ListNode*temp=more_head;//把空给空了
ListNode*less_head;
ListNode*pre=less_head;
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode more_head(0);
ListNode less_head(0);
ListNode *more_ptr=&more_head;
ListNode*less_ptr=&less_head;
while(head)
{
if(x>head->val)
{
less_ptr->next=head;
less_ptr=head;
}
else
{
more_ptr->next=head;
more_ptr=head;//more_ptr=more_next;
}
head=head->next;
}
more_ptr->next=NULL;//注意置空
less_ptr->next=more_head.next;//这两个都是空节点
return less_head.next;
}
};
九,重排链表
方法一,快慢指针
- 快满指针找到中间节点
- 拆分链表,并反转中间节点之后的链表
- 合并两个链表
class Solution {
public:
void reorderList(ListNode *head) {
if(head==NULL||head->next==NULL)
return;
ListNode*fast=head;
ListNode*slow=head;
while(fast->next!=NULL&&fast->next->next!=NULL){
fast=fast->next->next;
slow=slow->next;
}
ListNode*after=slow->next;
ListNode*pre=nullptr;
slow->next=nullptr;
while(after){
ListNode*temp=after->next;
after->next=pre;
pre=after;
after=temp;
}
ListNode*first=head;
after=pre;//复用了变量,自己再写一个也行
while(first&&after){
ListNode*f_temp=first->next;
ListNode*a_temp=after->next;
first->next=after;
first=f_temp;
after->next=first;
after=a_temp;
}
}
};
方法二 ,双端队列
- 头拿一个,尾拿一个。O(N)时间,O(1)空间
- 语法问题,初始化一个新节点?
- 保存结点
class Solution {
public:
void reorderList(ListNode *head) {
deque<ListNode*>que;
ListNode*tmp=head;
while(tmp){
que.push_back(tmp);
tmp=tmp->next;
}
ListNode nhead(0);
ListNode*ntail=&nhead;//这里变成一静一动两个节点了?????
//ListNode *nhead=new ListNode(0);和上面等效
// ListNode*ntail=nhead;
while(!que.empty()){
ListNode*h=que.front();
ListNode*t=que.back();
if(h==t){
que.pop_back();
h->next=nullptr;
ntail->next=h;
}
else{
que.pop_front();
que.pop_back();
h->next=t;
t->next=nullptr;
ntail->next=h;
ntail=t;
}
head=nhead.next;
}
}
};