/**
* 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) {}
* };
*/
head->···->pre->cur->next1->next2->···->final
上面所示的是一个三个节点的链表,pre、cur、next分别代表了指向三个节点的指针
我们现在要做的就是删除cur节点,做法有两种,一种是真正意义上的删除,另一种则是寻找一个替死鬼
方法一所面向的是知道链表的头节点,并且给出要删除的节点,我们直接进行删除即可;方法步骤就是找到pre、cur、next1三个节点,然后使得pre->next=next1即可;
ListNode* deleteNode(ListNode* head,ListNode* cur)
{
if(!head) return NULL;
//先构造一个虚拟头节点指向head,因为删除的节点可能是head,
//我们通过构造虚拟头节点来进行避免删除head之后找不到头节点的情况
auto dummy=new ListNode(-1);
dummy->next=head;
//遍历链表找到要删除的节点cur
auto pre=dummy,p=dummy->next;
while(p && p!=cur)
{
p=p->next;
pre=pre->next;
}
pre->next=p->next;
return dummy->next;
}
方法二讲述的其实是一个假删除,实际上是采用了替身的方法
由于是只把关键代码或者核心思想进行记录,一些具体的函数形式就没必要在意了
void deleteNode(ListNode* cur)
{
//这种情况是只给出了要删除的节点,并没有给出头节点,所以我们无法找到pre这个节点,
//并且遇到这种解法时,一般不会让你删除final节点,也就是尾节点
auto p=cur->next;
cur->val=p->val;
cur->next=p->next;
}
增加节点其实是与删除节点的进行的相反的操作
head->···->pre->next1->next2->···->final
上面所示的是一个三个节点的链表,pre、cur、next分别代表了指向三个节点的指针
我们现在给出一个cur节点,要求其加在pre节点与next1节点之间,或者说加在pre节点后面
//我们只需要经典三步
auto p=pre->next;//将p->next用p进行保存
pre->next=cur;//添加新的节点
cur->next=p;//将其进行链接
当然还有一些其他比较实用和常考的一些方法,这些将在下面的题目中进行表现
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
代码如下
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
auto dummy=new ListNode(-1);
dummy->next=head;
auto first=dummy,second=dummy;
while(n--) first=first->next;//将first与second的间距设为N
while(first->next)
{
first=first->next;
second=second->next;
}
second->next=second->next->next;
return dummy->next;
}
};
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
对于只有两个节点的链表,例如left->right->nullptr
我们在进行交换操作时,首先需要一个虚拟头节点,因为交换操作会改变head,加上dummy后,链表变为dummy->left->right->nullptr
其次我们只要进行交换left,right就可以了,最终返回的为dummy->next;交换的代码如下:dummy->next=right; left->next=right->next; right->next=left;
代码如下
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
auto dummy=new ListNode(-1);
dummy->next=head;
auto left=head,right=head,dum=dummy;//dum为临时虚拟头节点
while(left && left->next)
{
right=left->next;
dum->next=right;
left->next=right->next;
right->next=left;
dum=left;
left=left->next;
}
return dummy->next;
}
};
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
下面我们来看看具体的步骤是什么:
对于每一步其实可以在进行细分
代码如下
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head || !head->next) return head;
int len=1;
auto p=head;
//求len,并且将单链表变为环形链表
while(p->next)
{
len++;
p=p->next;
}
p->next=head;//将单链表成环
k=len-k%len;//计算要走多少步
while(k--) head=head->next,p=p->next;
p->next=NULL;//将环形链表断开,返回head即可
return head;
}
};
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
代码如下
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
auto cur=head;
while(cur)
{
if(cur->next && cur->next->val==cur->val)
cur->next=cur->next->next;
else
cur=cur->next;
}
return head;
}
};
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
本题给出了反转链表的两大方法,1、递归,2、就地反转
初始链表 head->l1->l2->l3->···->final->nullptr
递归之后 head<->l1<-l2<-l3<-···<-final
结合代码解释
代码如下
class Solution {
public:
ListNode* ans;//设置一个公共变量用来返回结果
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head; //如果是空链表或者链表只有一个节点,则不需要进行反转,直接返回head就行
recur(head); //否则进行递归处理
head->next=NULL;
return ans;
}
//递归函数
void recur(ListNode* head)
{
//找到尾节点,将尾节点给ans
if(!head || !head->next)
{
ans=head;
return;
}
//否则继续向下递归
recur(head->next);
//将next进行反转
head->next->next=head;
return;
}
};
代码如下
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head;
auto dummy=new ListNode(-1);
dummy->next=head;
auto p=head,pnext=head;
while(p->next)
{
pnext=p->next;
p->next=pnext->next;
pnext->next=dummy->next;
dummy->next=pnext;
}
return dummy->next;
}
};
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
代码如下
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if(left==right) return head;
auto dummy=new ListNode(-1);
dummy->next=head;
auto p=dummy,q=dummy,qnext=dummy;
right=right-left;
while(--left) p=p->next;
q=p->next;
while(right--)
{
qnext=q->next;
q->next=qnext->next;
qnext->next=p->next;
p->next=qnext;
}
return dummy->next;
}
};
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
- 你是否可以使用 O(1) 空间解决此题?
快慢指针的典型例题
代码如下
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head) return NULL;
auto dummy=new ListNode(-1);
dummy->next=head;
auto fast=dummy,slow=dummy;
int n=0;
while(n==0 || fast!=slow)
{
if(!fast->next || !fast->next->next) return NULL;
fast=fast->next->next;
slow=slow->next;
n++;
}
fast=dummy;
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
};
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
典型的替身攻击问题
代码如下
class Solution {
public:
void deleteNode(ListNode* node) {
*(node)=*(node->next);//替身攻击
}
};
编写一个程序,找到两个单链表相交的起始节点。
注意:
- 如果两个链表没有交点,返回 null.
- 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
- 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
代码如下
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
auto pA=headA,pB=headB;
while(pA!=pB)
{
if(!pA) pA=headB;
else pA=pA->next;
if(!pB) pB=headA;
else pB=pB->next;
}
return pA;
}
};
给你链表的头结点 head ,请将其按升序排列并返回排序后的链表。
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
暴力解法
归并排序
代码如下
class Solution {
public:
ListNode* sortList(ListNode* head) {
int n=0;
for(auto p=head;p;p=p->next) n++;
auto dummy=new ListNode(-1);
dummy->next=head;
for(int i=1;i<n;i*=2)
{
auto cur=dummy;
for(int j=0;j<n;j+=(i*2))
{
auto L=cur->next,R=L;
for(int k=0;k<i*2;++k)
{
if(L->val>R->val)
{
cur->next=R;
L->next=R->next;
R->next=L;
}
}
}
}
}
};
原文:https://www.cnblogs.com/hyhuang/p/14687595.html