给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
链接:https://leetcode-cn.com/problems/rotate-list/
/**
基本思想:先把单链表改成循环链表,然后头指针一直走loop的长度,最后改成单链表返回头
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head==NULL || k==0)
return head;
ListNode *h = head; //头指针
ListNode *f = NULL; //尾指针
int length = 1;
while(h->next!=NULL)
{
length++;
h = h->next;
}
int loop = length - (k%length);//循环次数
f = h;
h->next = head;
h = head;
for(int i=0;i<loop;i++)
{
h = h->next;
f = f->next;
}
f->next = NULL; //改成单链表
return h;
}
};
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs/
/*递归思想:按照正常思路交换两个节点为子问题,返回值连接前面的应该正确连接的节点
*/
/*非递归思想:设置三个指针,h指向要交换的第一个节点,f指向要交换的第二个节点,p指向第一个之前的一个节点
交换的时候:p->next指向f
h->next指向
f->nextf->next指向h
注意循环时,p,h,f的移动(指针是随着交换移动的,不是单纯的值的移动)
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*ListNode* swapPairs(ListNode* head) {
if(head == NULL || head->next == NULL)
return head;
ListNode *next = head->next;
head->next = swapPairs(next->next);
next->next = head;
return next;
}*/
ListNode* swapPairs(ListNode* head) {
if(head == NULL || head->next == NULL)
return head;
ListNode *p =new ListNode(0); //设置前面一个虚拟节点
p->next = head;
ListNode *h = head;
ListNode *f = head->next;
ListNode *newNode = f;
while(h!=NULL)
{
p->next = f;
h->next = f->next;
f->next = h;
p = h;
h = h -> next;
if(h==NULL || h->next == NULL)
break;
f = h->next;
}
return newNode;
}
};
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
链接:https://leetcode-cn.com/problems/sort-list/
/*
基本思想:归并排序,找中点,左右排序合并
链表的中点用两个指针,一个每次走两步,一个每次走一步,快指针到达最后,则慢指针的位置就是中点
左右分别归并,注意左边排序时,中点的next要设置为NULL
左右排序之后合并
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode * getMiddleList(ListNode* L)
{
ListNode *L1=L;
ListNode *L2=L->next;
while(L2!=NULL && L2->next != NULL)
{
L1 = L1->next;
L2 = L2->next->next;
}
return L1;
}
ListNode* MergeList(ListNode*L1,ListNode* L2)
{
if(L1 == NULL)
return L2;
if(L2 == NULL)
return L1;
ListNode* result = new ListNode(0);
ListNode* head = result;
while(L1!=NULL && L2!=NULL){
if(L1->val <= L2->val)
{
result->next = L1;
L1 = L1->next;
}
else
{
result->next = L2;
L2 = L2->next;
}
result = result->next;
}
if(L1==NULL)
{
result->next = L2;
}
if(L2==NULL)
result->next = L1;
return head->next;
}
ListNode *sortList(ListNode *head) {
if(head==NULL || head->next == NULL)
return head;
ListNode *middle = getMiddleList(head);
cout<<middle->val<<endl;
ListNode *right = sortList(middle->next);
middle->next = NULL;
ListNode *left = sortList(head);
return MergeList(left,right);
}
};
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/
/*
基本思想:把要反转的部分截取出来,进行反转,然后再拼接
注意,对于链表操作的时候,指向的链表的指针对链表的修改就是整个链表的变动
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode *ltail;
ListNode *rhead;
ListNode *dummyHead = new ListNode(0);
ListNode *cur = dummyHead;
ListNode *reversenode; //保存要反转的部分的头
dummyHead->next = head;
int i=0;
for(i=0;i<=n;i++)
{
if(i==m-1)
{
ltail = cur; //前面保留的尾
reversenode = cur->next; //要反转的部分的头
}
if(i==n)
{
rhead = cur->next; //后面要保留的部分的头
cur->next = NULL;
}
cur = cur->next;
}
ListNode* pre = NULL;
cur =reversenode;
while (cur != NULL) { //进行反转
ListNode* nextTemp = cur->next;
cur->next = pre;
pre = cur;
cur = nextTemp;
}
ltail->next = pre; //连接剩余不变的部分
reversenode->next = rhead;
return dummyHead->next;
}
};
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
链接:https://leetcode-cn.com/problems/partition-list/
/*基本思想:先找到第一个大于或等于x的位置,从这个位置开始向后找小于x的,找到就遍历前面的第一个大于或等于x的位置,找到该位置进行链表分开和拼接
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* cur;
ListNode* dummpNode = new ListNode(0);
dummpNode->next = head;
ListNode* lhead = dummpNode;
ListNode* list = dummpNode->next;
int min;
if(head==NULL)
return head;
while(list!=NULL)
{
if(list->val >= x )
{
cur = list;
break;
}
list = list->next;
}
ListNode *pre = cur;
while(cur!=NULL)
{
cout<<"c"<<cur->val;
if(cur->val <x){
while(lhead!=NULL&&lhead->next!=NULL)
{
cout<<"l"<<lhead->val;
if(lhead->next->val >= x)
{
ListNode* tmp = cur;
if(tmp!=NULL)
{
pre->next = cur->next;
tmp->next = lhead->next;
lhead->next = tmp;
}
lhead = lhead->next;
break;
}
lhead = lhead->next;
}
}
pre = cur;
cur = cur->next;
}
return dummpNode->next;
}
};
//另一种思路:维护两个链表,一个比x大,一个比x小最后两个链表拼接
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头
链接:https://leetcode-cn.com/problems/add-two-numbers
/*基本思想:就是两个数字对应相加,如果两个链表长度不一样,那短的那个算0来相加。最后都遍历完,考虑最后多出的进位新建一个链表添加在后面
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
ListNode *res = new ListNode(0);
ListNode *r = res;
ListNode *numlist1 = l1;
ListNode *numlist2 = l2;
int num1 ;
int num2 ;
int rest = 0;
while(numlist1!=NULL || numlist2!=NULL)
{
ListNode *node = new ListNode(0);
if(numlist1==NULL)
num1 = 0;
else
num1 = numlist1->val;
if(numlist2==NULL)
num2 = 0;
else
num2 = numlist2->val;
int num = (num1+num2+rest)%10 ;
rest = (num1+num2+rest)/10;
node->val = num;
res->next = node;
res = node;
if(numlist1==NULL)
numlist1=NULL;
else
numlist1 = numlist1->next;
if(numlist2==NULL)
numlist2=NULL;
else
numlist2 = numlist2->next;
}
if(rest!=0)
{
ListNode *node = new ListNode(rest);
res->next = node;
}
return r->next;
}
};
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
链接:https://leetcode-cn.com/problems/reorder-list
/*基本思想:分三步,先利用快慢指针找到中点,然后分成左部分和右部分,对右部分反转,然后两个部分合并
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void reorderList(ListNode* head) {
if(head == NULL)
return;
//快慢指针找中点
ListNode *quick = head;
ListNode *low = head->next;
while(low!=NULL && low->next!=NULL)
{
quick = quick->next;
low = low->next->next;
}
//反转右部分
ListNode *Rhead = quick->next;
quick->next = NULL;
ListNode* pre = NULL;
ListNode *cur = Rhead;
while (cur != NULL) {
ListNode* nextTemp = cur->next;
cur->next = pre;
pre = cur;
cur = nextTemp;
}
Rhead = pre;
//合并链表
ListNode *res = new ListNode(0);
ListNode *returnh = res;
int i=1;
while(head!=NULL && Rhead!=NULL)
{
ListNode*l = head->next; //记住下一个要找的位置
ListNode*r = Rhead->next;
if(i%2==0){
res->next = Rhead;
Rhead = r;
}
else
{
res->next = head;
head = l;
}
res = res->next;
i++;
}
if(head == NULL)
res->next = Rhead;
if(Rhead == NULL)
res->next = head;
head = returnh->next;
}
};
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
/*基本思想:主要是要找到要删除的节点,三个指针,一个指要删除的节点的之前的节点,快指针先走n步,然后慢指针和快指针同时出发,知道快指针到达末尾,慢指针的位置就是要删除的位置
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* newhead = new ListNode(0);
newhead->next = head;
ListNode* l1=newhead;
ListNode* l2=newhead;
ListNode* pre=newhead;
while(n>0 && l2!=NULL)
{
l2=l2->next;
n--;
}
while(l2!=NULL)
{
pre = l1;
l1=l1->next;
l2=l2->next;
}
pre->next = l1->next;
return newhead->next;
}
};
给定一个链表,判断链表中是否有环。
链接:https://leetcode-cn.com/problems/linked-list-cycle/
/*基本思想:利用快慢指针,如果两个指针相遇表示有环,否则没有环
*/
/**
* 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=head;
ListNode* slow=head;
while(fast!=NULL && fast->next!=NULL)
{
fast = fast->next->next;
slow=slow->next;
if(fast==slow)
return true;
}
return false;
}
};
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/
基本思想:先利用快慢指针找到第一次相遇的位置,然后一个指针从头开始出发,一个指针从相遇位置开始,再次相遇的点就是环的入口
原理:
X,Y,Z分别为链表起始位置,环开始位置和两指针相遇位置。
由于快指针速度为慢指针速度的两倍,那么这个慢指针最多走圆的一圈(这里想象极端情况,就是整个链表就是一个环,那么两个指针从圆的同一个地方出发,那么此时何时相遇呢?必然是慢指针正好才走一圈的时候,快指针走两圈追上来了)。
所以这里假设就是在Z相遇的,那么慢指针走的距离是a+b,很好计算。而快指针走的距离是2(a+b),此时我们想象,假设慢指针走到了X和Z的中间的时候,快指针已经到Z了,那么下面再走的话,就是快指针从Z点出发围着圆绕几圈之后恰好在Z点和X相遇,因此快指针走过的距离是:
2(a+b) = a+b+n*圆的周长 = a+b+n(b+c)
此时a为:
a = (n - 1) * b + n * c = (n - 1)(b + c) +c
从公式上看,当一个指针从X出出发,走完a的距离之后,那么另一个指针从相遇点Z出发就会走(n-1)圈的环再加一个C的距离,此时正好在Y点相遇。
因此,一个指针从头出发,一个指针从相遇点出发,速度相同,相遇点就是环的入口节点。
*/
/*基本思想:先利用快慢指针找到第一次相遇的位置,然后一个指针从头开始出发,一个指针从相遇位置开始,再次相遇的点就是环的入口
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast=head;
ListNode *slow=head;
if( fast== NULL || fast->next == NULL)
return NULL;
while(fast!=NULL && fast->next!=NULL)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
break;
}
}
while(fast!=NULL && fast!=head )
{
fast = fast->next;
head = head->next;
}
return fast;
}
};
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
/*基本思想:对于每一个链表节点,while循环向后找第一个和他不一样的接到他后面
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode* p=head;
while(p!=NULL && p->next!=NULL)
{
while(p->next!=NULL && p->next->val == p->val)
{
ListNode *next = p->next->next;
p->next = next;
}
p =p->next;
}
return head;
}
};
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/
/*基本思想: 用一个新链表存储只出现一次的链表节点,和之前删除重复元素类似,先找到第一个和他不相等的,不过此时要有一个计数,当总数等于1的时候,才连接,否则不连接,注意最后一个当为空的时候要把新链表最后置为NULL(否则会连接以前存在的)
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode *newhead = new ListNode(0);
ListNode *res = newhead;
ListNode *p = head;
while(p!=NULL)
{
int count = 1;
while(p->next!=NULL && p->next->val == p->val )
{
count++;
p->next = p->next->next;
}
if(count==1)
{
cout<<newhead->val<<endl;
newhead->next = p;
newhead=newhead->next;
}
if(p->next==NULL)
{
newhead->next =NULL;
}
p=p->next;
}
return res->next;
}
};
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node
/*
基本思路:主要是理解题,对于一个根节点,他的左孩子的next指向右孩子,对于右孩子的next指向根的next的左孩子
所以只需要对于每一个根节点进行这样的操作就可以,递归处理每一个节点,体中初始的next已经是NULL
*/
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() {}
Node(int _val, Node* _left, Node* _right, Node* _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if(root==NULL)
return root;
if(root->left!=NULL)
root->left->next = root->right;
if(root->right!=NULL && root->next)
root->right->next = root->next->left;
connect(root->left);
connect(root->right);
return root;
}
};
给定一个二叉树,不是完全二叉树
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii
/*基本思想:和上一个思想一样,也是递归,这个要注意不是完全二叉树的时候,对于next指针可能要找到上一层的next的左右孩子,存在指向,都不存在才为NULL
所以当树大的时候,一个节点的next可能指向的离他比较远,所以要遍历确定指向,同时注意要先确定root-right的next,root->left的next需要依赖于right的 next往后找
*/
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() {}
Node(int _val, Node* _left, Node* _right, Node* _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if(root==NULL)
return root;
if(root->left!=NULL){
if(root->right!=NULL)
root->left->next = root->right;
else
{
Node* next=root->next;
while(next) {
if(next->left!=NULL){
root->left->next = next->left;
break;
}
if(next->right!=NULL)
{
root->left->next = next->right;
break;
}
next =next->next;
}
}
}
if(root->right!=NULL )
{
Node* next =root->next;
while(next) {
if(next->left!=NULL) {
root->right->next = next->left;
break;
}
if(next->right!=NULL)
{
root->right->next = next->right;
break;
}
next= next->next;
}
}
connect(root->right);
//先确保 root->right 下的节点的已完全连接,因 root->left 下的节点的连接
// 需要 root->left.next 下的节点的信息,若 root.right 下的节点未完全连
// 接(即先对 root.left 递归),则 root->left.next 下的信息链不完整,将
// 返回错误的信息。可能出现的错误情况如下图所示。此时,底层最左边节点将无
// 法获得正确的 next 信息:
// o root
// / \
// root.left o —— o root.right
// / / \
// o —— o o
// / / \
// o o o
connect(root->left);
return root;
}
};