整理一下刷题过程中的思路,在这里进行一下总结与分享。
github地址:https://github.com/lvjian0706/Leetcode-solutions
github项目是刚刚新建的,陆续会将整理的代码以及思路上传上去,代码是基于C++与python的。同时会将基础的排序算法等也一并进行整理上传。
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
* 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 node(0);
ListNode* new_head = &node;
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;
}
while(l1){
new_head->next = l1;
l1 = l1->next;
new_head = new_head->next;
}
while(l2){
new_head->next = l2;
l2 = l2->next;
new_head = new_head->next;
}
return node.next;
}
};
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:
输入: 1->1->1->2->3
输出: 2->3
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
删除重复节点,只保留没有重复出现的数字:
1. 为了避免头节点重复需要删除,新建一个头节点指向原来的头节点,最后返回新建节点的next;
2. 定义last和fast节点用来遍历链表,并找出重复节点:
2.1 last和fast初始化为head节点,循环判断last和fast的next是否相等,如果相等,fast前进一位;
2.2 如果fast发生了变化,也就是last和fast不是同一个节点时,说明从last到fast之间是重复元素,new_head的next指向fast的下一个元素;
2.3 如果last和fast是同一个节点时,说明该元素不是重复元素,new_head前进到该元素;
2.4 last和fast前进一位,保证永远在new_head前面一位;
*/
ListNode* deleteDuplicates(ListNode* head) {
ListNode node(0);
ListNode* new_head = &node;
new_head->next = head;
ListNode* last = head;
ListNode* fast = head;
while(fast && fast->next){
while(fast->next && last->val == fast->next->val){
fast = fast->next;
}
if(last==fast){
new_head = last;
}
else{
new_head->next = fast->next;
}
last = new_head->next;
fast = new_head->next;
}
return node.next;
}
};
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
使用一个指针进行遍历:
1. 如果没有元素或者只有1个元素,直接返回;
2. 遍历链表,当下一个元素与当前元素相等时,指针的next指向next的next,用以删除重复的元素,直到下一个元素与当前元素不相等为止;
3. 指针指向下一个元素(与当前元素不相等的第一个元素);
*/
ListNode* deleteDuplicates(ListNode* head) {
if(!head || !head->next) return head;
ListNode* newHead = head;
while(newHead && newHead->next){
if(newHead->next->val==newHead->val){
newHead->next = newHead->next->next;
}
else{
newHead = newHead->next;
}
}
return head;
}
};
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
分隔链表:
定义两个节点small_head和large_head,前者作为头节点存储小于x的节点,后者作为头节点存储大于等于x的节点,最后进行拼接;
注意结果链表的尾部应该为null;
*/
ListNode* partition(ListNode* head, int x) {
ListNode node1(0);
ListNode* small_head = &node1;
ListNode node2(0);
ListNode* large_head = &node2;
while(head){
if(head->val<x){
small_head->next = head;
small_head = small_head->next;
}
else{
large_head->next = head;
large_head = large_head->next;
}
head = head->next;
}
small_head->next = node2.next;
large_head->next = NULL;
return node1.next;
}
};
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
反转从m到n的链表:迭代法
使用first指针指向位置m,first_pre指向m-1,temp设为null,last指向位置m+1,last_next指向位置n+1;
使用fist,last以及temp将位置m到n进行反转,接着将first_pre指向反转后的子链表头,反转后的子链表尾指向n+1;
*/
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode* first = head;
ListNode node(0);
ListNode* first_pre = &node;
first_pre->next = head;
/*
将first指针指向位置m,first_pre指向位置m-1;
*/
for(int i=0; i<m-1; i++){
first = first->next;
first_pre = first_pre->next;
}
ListNode* last = first->next;
ListNode* temp = NULL;
/*
使用fist,last以及temp将位置m到n进行反转,此时temp指向位置m,first指向位置m+1;
*/
for(int i=m; i<n+1; i++){
first->next = temp;
temp = first;
first = last;
if(last) last = last->next;
}
first_pre->next->next = first;
first_pre->next = temp;
return node.next;
}
};
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 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:
/*
复制带随机指针的链表:深拷贝
1. 循环遍历链表,一边遍历一边新建节点,将新节点赋值为与该节点一样的值,然后插入到该节点后边;
2. 再次循环遍历新链表,步长为2,一边遍历一边将后边的节点(1中新建的节点)的random赋值为与前边节点(原始节点)一致;
3. 将新链表拆分
*/
Node* copyRandomList(Node* head) {
Node* insert_head = head;
while(insert_head){
Node* temp_node = new Node(insert_head->val);
temp_node->next = insert_head->next;
insert_head->next = temp_node;
insert_head = insert_head->next->next;
}
Node* random_head = head;
while(random_head && random_head->next){
if(random_head->random) random_head->next->random = random_head->random->next;
else random_head->next->random = NULL;
random_head = random_head->next->next;
}
Node node(0);
Node* copy_head = &node;
Node* new_head = head;
while(new_head && new_head->next){
copy_head->next = new_head->next;
new_head->next = new_head->next->next;
new_head = new_head->next;
copy_head = copy_head->next;
}
copy_head->next = NULL;
return node.next;
}
};
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
/**
* 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) {
if(!head || !head->next) return false;
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast==slow) return true;
}
return false;
}
};
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
循环链表:
1. 首先判断是否是环,使用快慢指针,快指针每次走2步,慢指针每次走1步,看是否相遇,不相遇则返回null;
2. 如果相遇,设慢指针走了s,则快指针已经走了2s,假设从head到环的入口为a,环的长度为b,则s=nb,快指针比慢指针多走了nb;
3. 想要计算环的入口a,可以使用两个指针,一个在head处走a步,一个在相遇处走a步,此时,一个指针的位置为a,一个为a+nb,因为有环,所以a=a+nb;
4. 最终的相遇位置为环的入口;
*/
ListNode *detectCycle(ListNode *head) {
if(!head || !head->next) return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast==slow){
slow = head;
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
}
};
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->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){
if(!head || !head->next) return head;
ListNode* new_head = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return new_head;
}
/*
重排列链表:将后半部分的链表倒序插入到前半部分链表中;
1. 使用快慢指针找到后半部分链表;
2. 反转后半部分链表;
3. 将右半部分链表插入到左半部分链表中;
*/
void reorderList(ListNode* head) {
if(!head || !head->next) return;
ListNode* fast = head->next;
ListNode* slow = head;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* right_half = reverseList(slow->next);
slow->next = nullptr;
ListNode* new_head = head;
while(new_head && right_half){
ListNode* temp = right_half->next;
right_half->next = new_head->next;
new_head->next = right_half;
right_half = temp;
new_head = new_head->next->next;
}
/*
当右半部分链表还有一个元素没有插入进去时,单独处理;
*/
if(right_half){
right_half->next = new_head->next;
new_head->next = right_half;
}
}
};
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
新建头节点,进行归并排序;
*/
ListNode* mergeSort(ListNode* left, ListNode* right){
ListNode node(0);
ListNode* new_head = &node;
while(left && right){
if(left->val<right->val){
new_head->next = left;
left = left->next;
}
else{
new_head->next = right;
right = right->next;
}
new_head = new_head->next;
}
while(left){
new_head->next = left;
left = left->next;
new_head = new_head->next;
}
while(right){
new_head->next = right;
right = right->next;
new_head = new_head->next;
}
return node.next;
}
/*
链表排序:
O(nlogn) 时间复杂度以及常数级空间复杂度,考虑归并排序
使用快慢指针找链表的中点,将链表分为前半部分和后半部分,递归的对前半部分以及后半部分进行排序,最后对两部分进行总的排序
其中,在分割时,需要将前半部分的尾指针next赋为null,将链表切断,否则会无限递归;后半部分的起始点则为slow->next;
*/
ListNode* sortList(ListNode* head) {
if(!head || !head->next) return head;
ListNode* fast = head->next;
ListNode* slow = head;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* half = slow->next;
slow->next = NULL;
return mergeSort(sortList(head), sortList(half));
}
};
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
原地反转链表:方法1.递归
1. 递归结束条件:如果head为空或者链表只有1个节点,不需要反转,直接返回;
2. 当head->next之后的节点已经反转好了,将新的头结点保存为new_head;
3. 此时,头节点指向新链表的尾节点,需要将新链表的尾节点指向头节点(反转head和head->next),head->next->next = head;
4. 将头节点的next赋为空;
5. 返回新链表的头节点;
*/
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head;
ListNode* new_head = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return new_head;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
原地反转链表:方法2.迭代
1. 递归结束条件:如果head为空或者链表只有1个节点,不需要反转,直接返回;
2. 使用三个指针,每次将当前指针指向前一个位置的指针,然后将三个指针同时向后移动一位;
*/
ListNode* reverseList(ListNode* head) {
ListNode* new_head = NULL;
while(head){
ListNode* head_next = head->next;
head->next = new_head;
new_head = head;
head = head_next;
}
return new_head;
}
};
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/*
递归反转链表
*/
ListNode* reverseList(ListNode* head){
if(!head || !head->next) return head;
ListNode* new_head = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return new_head;
}
/*
判断是否是回文链表:
1. 使用快慢指针找到链表中点,将链表断开;
2. 将后半部分链表反转;
3. 比较两个链表的相同位置的值是否一致;
*/
bool isPalindrome(ListNode* head) {
if(!head || !head->next) return true;
ListNode* fast = head->next;
ListNode* slow = head;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* right = reverseList(slow->next);
slow->next = NULL;
while(head && right){
if(head->val != right->val) return false;
head = head->next;
right = right->next;
}
return true;
}
};