LeetCode 解题笔记链表
Posted 火山上的企鹅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 解题笔记链表相关的知识,希望对你有一定的参考价值。
文章目录
总目录: LeetCode 解题笔记(一)总
一、总结
● 链表的简介:
如果你还不太熟悉链表,下面有关于列表的概要讲述。
有两种常用的列表实现,分别为数组列表和链表。如果我们想在列表中存储值,它们是如何实现的呢?
数组 列表底层是使用数组存储值,我们可以通过索引在 O(1)
的时间访问列表任何位置的值,这是由基于内存寻址的方式。
链表 存储的是称为节点的对象,每个节点保存一个值和指向下一个节点的指针。访问某个特定索引的节点需要O(n)
的时间,因为要通过指针获取到下一个位置的节点。
● 链表中方法总结:
① 常用的方法:
● 暴力破解,常用手段
● 栈stack:先进后出, 适合链表
● 哈希表:用 unordered_map 或 unordered_set
● 双指针(快慢指针)
● 递归:有重复公式的,先进后出
② 增加哑结点。考虑如果需要删除头节点的话,这时需要新增一个哑节点dummy ,它下一个节点为 head;
增加: ListNode *dummy = new ListNode(0, head);
删除: delete dummy;
③ 链表中循环 while 比 for 好用
二、题目
237. 删除链表中的节点(2022/03/10)
链接: 237. 删除链表中的节点
标签:链表
题目:
class Solution
public:
void deleteNode(ListNode* node)
//4 5 1 9 变为 4 1 9
//本来要删除第二个,现只需要删除第三个就好,把三个的值要复制给第二个
node->val = node->next->val;
node->next = node->next->next;
;
19. 删除链表的倒数第 N 个结点(2022/03/11)
题目:
● 方法一:计算链表长度(我的错误答案:)
class Solution
public:
ListNode* removeNthFromEnd(ListNode* head, int n)
//只有一个的
if(head->next == nullptr)
return nullptr;
//遍历求n个节点
int sum = 0;
for(ListNode * node = head; node->next != nullptr; node = node->next)
sum++;
sum++; //2
ListNode * node2 = head;
int i = 1;
while(i < sum-n) //sum = 5 n=2 //0 1 2 3
node2 = node2->next;
i++; //加三次就去了4那里了 只需要加两次
if(i == sum)
node2->next = nullptr;
else
node2->next = node2->next->next;
return head;
;
思路是没有问题的,以下几点得注意:
① 考虑如果删除了头节点,所以需要新增一个哑节点dummy ,它下一个节点为 head;
② 判断链表长度的时候应判断,当前链表 node,而不是 node->next(); 用 while 比 for 好
③ 删除哑结点可以直接:delete dummy;
看了参考答案后,修改上面的:
class Solution
public:
ListNode* removeNthFromEnd(ListNode* head, int n)
//只有一个的
if(head->next == nullptr) return nullptr;
//遍历求n个节点
int sum = 0;
for(ListNode * node = head; node != nullptr; node = node->next)
sum++; //4
//新增一个空的节点,dummy 指向head
ListNode *dummy = new ListNode(0, head);
ListNode * node = dummy; //当前节点
int i = 0;
while(i < sum-n) //sum=5 n=1 i<4 //0 1 2 3 4 可以执行四次
node = node->next;
i++;
node->next = node->next->next; //如果为最后一个,那么 node->next->next = null 不需要额外判断了
return dummy->next;
;
● 方法二:栈 stack; 先进后出
class Solution
public:
ListNode* removeNthFromEnd(ListNode* head, int n)
ListNode* dummy = new ListNode(0, head);
stack<ListNode *> stk;
ListNode *cur = dummy;
while(cur)
stk.push(cur);
cur = cur->next;
for(int i=0; i<n; i++)
stk.pop();
ListNode* prev = stk.top();
prev->next = prev->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
;
● 方法三:双指针
快指针比慢指针快了 2 个! 画图一目了然
class Solution
public:
ListNode* removeNthFromEnd(ListNode* head, int n)
ListNode* first = head;
ListNode* dummy = new ListNode(0, head);
ListNode* second = dummy;
for(int i=0; i<n; i++)
first = first->next;
while(first)
first = first->next;
second = second->next;
second->next = second->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
;
206. 反转链表(2022/03/18)
● 链接: 206. 反转链表
● 标签: 链表、递归
● 题目:
● 方法一:官方答案
假设链表为 1→2→3→∅,我们想要把它改成 ∅←1←2←3。
class Solution
public:
ListNode* reverseList(ListNode* head)
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr)
//先修改 curr->next; 并且先要保存curr->next, 很奇妙
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
return prev;
;
21. 合并两个有序链表(2022/03/19)
● 链接: 21. 合并两个有序链表
● 标签: 链表、递归
● 题目:
● 方法一:通用野蛮法
/**
* 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* list1, ListNode* list2)
// ListNode* dummy = new ListNode(0, list1);
ListNode* listHead = new ListNode(-1);
ListNode* cur = listHead;
while(list1 || list2)
//处理空指针
if(list1 == nullptr)
cur->next = list2;
break;
if(list2 == nullptr)
cur->next = list1;
break;
if(list1->val <= list2->val)
cur->next = list1;
list1 = list1->next;
else
cur->next = list2;
list2 = list2->next;
cur = cur->next; //这个是关键
ListNode* ans = listHead->next;
delete listHead;
return ans;
;
● 方法二:递归高级法
递归就是先进后出,和栈一样!
class Solution
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)
//1.递归
if(list1 == nullptr) return list2;
if(list2 == nullptr) return list1;
if(list1->val < list2->val)
list1->next = mergeTwoLists(list1->next, list2);
return list1;
else
list2->next = mergeTwoLists(list2->next, list1);
return list2;
;
234.回文链表(2022/03/20)
● 链接: 234. 回文链表
● 标签: 递归、链表、双指针
● 题目:
● 我的答案:计算个数,压栈 + 头和栈尾比较
(内存消耗有点大)
/**
* 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:
bool isPalindrome(ListNode* head)
///计算个数
int n = 0;
ListNode *cur = head;
stack<ListNode *> stk;
while(cur)
n++;
stk.push(cur);
cur = cur->next;
//判断回文
for(int i=0; i<n/2; i++)
if(head->val == stk.top()->val)
stk.pop(); //移除
head = head->next;
else
return false;
return true;
;
● 官方答案一:将值复制到数组中后用双指针法
class Solution
public:
bool isPalindrome(ListNode* head)
vector<int> vals;
while (head != nullptr)
vals.emplace_back(head->val);
head = head->next;
for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j)
if (vals[i] != vals[j])
return false;
return true;
;
141. 环形链表(2022/03/21)
● 链接: 141. 环形链表
● 标签: 哈希、链表、双指针
● 题目:
● 我的答案(一)哈希 unordered_set
利用 unordered_set,存入每一个链表,并且遍历,有重复就是环形
class Solution
public:
bool hasCycle(ListNode *head)
//哈希
unordered_set<ListNode* > _set;
while(head)
//官方用的 _set.count(head) 似乎更加便捷
if(_set.find(head) != _set.end())
return true;
_set.insert(head);
head = head->next;
return false;
;
● 我的答案(二)快慢指针
关键点是考虑临界值!
class Solution
public:
bool hasCycle(ListNode *head)
//快慢指针
if(head == nullptr || head->next == nullptr) return false;
ListNode *fast = head->next;
ListNode *slow = head;
//官方的 if 和 while 的条件反过来了,并且不需要判断 slow
while(fast && fast->next && slow)
if(fast == slow)
return true;
fast = fast->next->next;
slow = slow->next;
return false;
;
以上是关于LeetCode 解题笔记链表的主要内容,如果未能解决你的问题,请参考以下文章