141. Linked List Cycle
题目地址
解题思路:
- 快慢指针,创建指针p每次前进一格,创建指针q每次前进二格,步长过大会增加pq相遇的时间。
- p, q的初始位置不同,原因是当只存在一个节点时,while循环会因为q的下一节点为NULL跳出循环,返回true值(当然也可以在while中做判断)
- whlie中
q != NULL && q->next != NULL
条件是为了防止q指向空指针。
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL) return false;
ListNode *p = head, *q = head->next;
while(q != NULL && q->next != NULL && q != p){
p = p->next;
q = q->next->next;
}
return q==p;
}
};
142. Linked List Cycle II
题目地址
解题思路:
基于快慢指针的思想,假设当q的步长是p的两倍时,head节点到 循环链表起点 距离为a个节点:
- 如下图所示,当p到达循环起点时,q已经在循环链表中走了a个节点,设q到p的距离为x,则循环链表周长为 a+x
- 当p走了x,q就走了2x,此时两节点相遇,注意此时p和q到循环起点的距离为a,等于head到链表起点的距离。
- 如图所示,重新让q指向head节点,当p, q再次相遇时,就会位于循环起始点。
代码实现:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == NULL) return NULL;
ListNode *p = head, *q = head;
while(q && q->next){
p = p->next;
q = q->next->next;
//if判断语句放在循环内仍然是为了考虑单个节点
if(q == p){
q = head;
while(q != p){
q = q->next;
p = p->next;
}
return p;
}
}
return NULL;
}
};
202. Happy Number
题目地址
解题思路:
如图所示,快乐数的推导过程可以看作是由自身节点推出下一个节点,最后输出相同的值时即为进入循环链表(快乐数会进入1循环,数字2会进入20循环)。所以仍然可以使用快慢指针判断。
class Solution {
public:
int getNext(int x){
int sum = 0;
while(n){
sum += (n % 10)*(n % 10);
n = n/10;
}
}
bool isHappy(int n) {
int p = n, q = n;
do{
p = getNext(p);
q = getNext(getNext(q));
}while(q != p && q != 1)
return q == 1;
}
};
206.reverse linked list
题目地址
解题思路:
创建三个指针:pre, cur, next
指针操作步骤:
- 定义 pre 为空指针
- cur 指向 pre 指向的节点
- pre 指向 cur 指向的节点
- cur 指向 next 指向的节点
- next 指向 next 的下一个节点
- 重复 2~6 直到 cur 指向最后的空节点
下图显示了上面描述的指针操作过程
上图的代码实现
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head-> next == nullptr) return head;
ListNode *pre = nullptr, *cur = head, *p = head -> next;
while(cur){
cur -> next = pre;
pre = cur;
//这里是为了防止最后 p节点为空,会出现错误
(cur = p) && (p = p -> next);
}
return pre;
}
};
另一种思路是使用递归
如下图所示,交换 head节点 和 tail节点 的位置
代码实现为
head -> next = tail -> next;
tail -> next = head;
将上述思路通过递归的方式,从最后的节点开始逐对反转
代码实现:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode *tail = head -> next, *p = reverseList(head -> next);//p 是最后的结点,也就是反转后的头结点
head -> next = tail -> next;
tail -> next = head;
return p;
}
};
92.reverse linked list II
解法1:使用上面的常规的三指针操作,记录起始反转位置(left)的前一个结点 head 和终止反转位置(rigtht)的后一个结点 pre。
在这里使用了 ret结点 作为头结点的前置结点,目的是便于找到反转链表的起始点。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if(head == nullptr || head -> next == nullptr || left == right) return head;
ListNode ret(0,head), *hair = &ret, *pre = head;
int m = left, n = right, cnt = n - m + 1;
while(--m) hair = hair -> next;
while(n){
pre = pre -> next;
n--;
}
ListNode *cur = hair -> next, *p = hair -> next -> next;
while(cnt){
cur -> next = pre;
pre = cur;
(cur = p) && (p = p -> next);
cnt--;
}
hair -> next = pre;
return ret.next;
}
};
解法2:只需要调用上面的递归函数,并稍作修改。原函数的递归最底层为最后一个结点,现在为反转链表范围的最后结点
class Solution {
public:
ListNode* reverseList(ListNode* head, int n) {
if(n == 1) return head;
ListNode *tail = head -> next, *p = reverseList(head -> next, --n);
head -> next = tail -> next;
tail -> next = head;
return p;
}
ListNode* reverseBetween(ListNode* head, int left, int right) {
if(head == nullptr || head -> next == nullptr || left == right) return head;
ListNode ret(0,head), *hair = &ret;
int m = left, n = right, cnt = n - m + 1;
while(--m) hair = hair -> next;
hair -> next = reverseList(hair -> next, cnt);
return ret.next;
}
};
25. Reverse Nodes in K-Group
题目地址
完全是基于上边已有的代码,非常简单
reverseK() 函数中有一个小技巧,如果传入的链表长度小于规定的长度,直接返回原链表,然后 reverseKGroup()中通过判断返回的链表头结点是否为原来的结点来决定是否跳出循环
class Solution {
public:
ListNode* reverseList(ListNode* head, int n) {
if(n == 1) return head;
ListNode *tail = head -> next, *p = reverseList(head -> next, --n);
head -> next = tail -> next;
tail -> next = head;
return p;
}
ListNode* reverseK(ListNode* head, int k) {
ListNode *p = head;
int cnt = k;
while(--k && p) p = p -> next;
if(p == nullptr) return head;
return reverseList(head, cnt);
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode ret(0, head), *p = &ret, *q = p -> next;
while((p -> next = reverseK(q, k)) != q) {
p = q;
q = q -> next;
}
return ret.next;
}
};
61. Rotate List
题目地址
解题思路:
将链表首尾连接,寻找断点并断开
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head == nullptr || head->next == nullptr) return head;
int n = 1;
ListNode*p = head;
while(p->next){
p = p->next, n++;
}
p->next = head;
k %= n, k = n - k;
while(k--) p = p->next;
head = p->next;
p->next = nullptr;
return head;
}
};
19. Remove Nth Node From End of List
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode ret(0, head), *p = &ret, *q = head;
while(n--) q = q->next;
while(q != nullptr) p = p->next, q = q->next;
p->next = p->next->next;
return ret.next;
}
};
83. Remove Duplicates from Sorted List
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head == nullptr) return nullptr;
ListNode *p = head;
while(p->next){
if(p->val == p->next->val){
p->next = p->next->next;
}else{
p = p->next;
}
}
return head;
}
};
82. Remove Duplicates from Sorted List II
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode ret(0,head), *p = &ret, *q;
while(p->next){
if(p->next->next && p->next->val == p->next->next->val){
q = p->next->next;
while(q && q->val == p->next->val) q = q->next;
p->next = q;
}else{
p = p->next;
}
}
return ret.next;
}
};
24. Swap Nodes in Pairs
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode ret(0, head), *q = &ret, *p = head;
while(p && p->next){
q->next = p->next;
p->next = p->next->next;
q->next->next = p;
q = q->next->next;
p = p->next;
}
return ret.next;
}
};
86. Partition List
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
if(head == nullptr || head->next == nullptr) return head;
ListNode ret(0, head), *p = &ret, *q = head, *m;
while(q->next && p->next){
while(p->next && p->next->val < x) p = p->next;
q = p;
while(q->next && q->next->val >= x) q = q->next;
if(p->next && q->next){
m = q->next;
q->next = m->next;
m->next = p->next;
p->next = m;
}
}
return ret.next;
}
};
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode h1, h2, *p1 = &h1, *p2 = &h2;
for(ListNode *p = head, *q; p != nullptr; p = q){
q = p->next;
p->next = nullptr;
if(p->val < x){
p1->next = p;
p1 = p;
}else{
p2->next = p;
p2 = p;
}
}
p1->next = h2.next;
return h1.next;
}
};
138. Copy List with Random Pointer
地址
复制原链表,将原链表的random赋给random
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return head;
Node *p = head, *q, *new_head;
while(p){
q = new Node(p->val);
q->next = p->next;
q->random = p->random;
p->next = q;
p = q->next;
}
p = head->next;
while(p){
if(p->random != NULL){
p->random = p->random->next;
}
p = p->next;
if(p) p = p->next;
}
new_head = head->next;
p = head;
while(p){
q = p->next;
p->next = q->next;
if(p->next) q->next = p->next->next;
p = p->next;
}
return new_head;
}
};