链表初始知识
链表种类:单链表,双链表,循环链表
链表初始化
struct ListNode{
int val;
ListNode* next;
ListNode(int x): val(x),next(nullptr) {}
};
//初始化
ListNode* head = new ListNode(5);
删除节点、添加节点
考虑的边界
head==nullptr || head->next ==nullptr
处理链表的输入输出
// 本地测试代码 (main.cpp)
#include <iostream>
using namespace std;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) {}
};// 粘贴修正后的Solution类
int main() {
// 创建链表 1->2->3->4->5
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(3);
head->next->next->next = new ListNode(4);
head->next->next->next->next = new ListNode(5);
Solution sol;
ListNode* newHead = sol.reverseList(head);
// 打印结果: 5->4->3->2->1
while (newHead) {
cout << newHead->val << " ";
newHead = newHead->next;
}
return 0;
}
ListNode vs ListNode*
ListNode node; // 创建默认节点 (val=0, next=nullptr)
ListNode node2(5); // 创建 val=5 的节点
ListNode node3(10, &node); // 创建 val=10 并指向 node 的节点
ListNode* ptr = new ListNode(); // 动态创建节点
ListNode* ptr2 = new ListNode(20); // 动态创建 val=20 的节点
1.相交链表【没想明白】6/31h
160. 相交链表 - 力扣(LeetCode)
问题:没看懂其实找的是指针相同/地址相同,而不是数值相同
法一:哈希表,找b中指针在不在a里
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {//好奇怎么样处理输入
//请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
//暴力解forfor
//我才看懂这个是地址相同不是val相同
ListNode*a = headA;
ListNode*b = headB;
unordered_set<ListNode*>set;
while(a!=nullptr){
set.insert(a);
a = a->next;
}
while(b!=nullptr){
if(set.find(b)!=set.end()){
return b;
}
b = b->next;
}
return nullptr;
}
};
法二:类似数学发现
不相交:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {//好奇怎么样处理输入
//请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
//暴力解forfor
//我才看懂这个是地址相同不是val相同
ListNode*a = headA;
ListNode*b = headB;
while(!(a==nullptr && b==nullptr)){
if(a == b){
return a;
}
if(a!=nullptr){
a = a->next;
}else{
a = headB;
}
if(b!=nullptr){
b = b->next;
}else{
b = headA;
}
}
return nullptr;
}
};
2.反转链表【30min】
206. 反转链表 - 力扣(LeetCode)
注意:qpr返回的是哪个,如何初始化
/**
* 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 == nullptr || head->next == nullptr){
return head;
}
ListNode * p = head;
ListNode * q = nullptr;
ListNode * r;
//q,p,r
while(p!=nullptr){
r = p->next;
p->next = q;
q = p;
p = r;
}
return q;
}
};
法二:递归【没咋看】
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
3回文链表
234. 回文链表 - 力扣(LeetCode)
问题:原来想先把链表反转,然后遍历,看值是否相同。
但是其实反转之后,链表被覆盖了
所以可以找到链表中点,反转一半,然后遍历比较值
/**
* 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* reverse(ListNode*head){
if(head == nullptr || head->next == nullptr){
return head;
}
ListNode* p = head;
ListNode*q = nullptr;
ListNode*r;
//qpr
while(p != nullptr){//p != nullptr
r = p->next;
p->next = q;
q = p;
p = r;
}
return q;//返回的有问题
}
bool isPalindrome(ListNode* head) {
//哈希表有问题,不是按照顺序判断的
ListNode *p = head;
int size = 0;
while(p!=nullptr){
p = p->next;
size++;
}
int half = size/2;
size = 0;
p = head;
while(size !=half){
p = p->next;
size++;
}
ListNode * head1 = reverse(p);//链表改变
size = 0;
while(size!=half){
if(head->val != head1->val){
return false;
}
size++;
head = head->next;//忘记处理一般情况了
head1 = head1->next;
}
return true;
}
};
4.环形链表
141. 环形链表 - 力扣(LeetCode)
法一:哈希表
问题:while(head!=nullptr && set.find(head)==set.end()){条件写错了:没到终点且元素没出现
/**
* 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 == nullptr){
return false;
}
unordered_set<ListNode *>set;
while(head!=nullptr && set.find(head)==set.end()){//set.find(head)==set.end()没找见
set.insert(head);
head = head->next;
}
if(head==nullptr){
return false;
}else{
cout<<head->val;
return true;
}
}
};
法二:数学发现/双指针
每次移动差一个,若有圈,绕n圈后必然相见
灵神一语点醒梦中人:
这时用「相对速度」思考,乌龟不动,兔子相对乌龟每次只走一步,这样就可以看出兔子一定会和乌龟相遇了。
/**
* 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 == nullptr ){
return false;
}
ListNode* slow = head;
ListNode* fast = head->next;
while (slow != fast) {
if (fast == nullptr || fast->next == nullptr) {
return false;
}
slow = slow->next;
fast = fast->next->next;
}
return true;
}
};
5.环形链表2
142. 环形链表 II - 力扣(LeetCode)
法一哈希表
法二数学发现/双指针[floyd判圈算法]
快的走了:a+d+kc
慢的走了:a+d
首先,k= 1,
a+d+c = 2(a+d)
c = a+d
求a,
关键在于:c-d = a
/**
* 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) {
if(head == nullptr || head->next ==nullptr){
return nullptr;
}
ListNode *low = head->next;
ListNode* fast = head->next->next;//同一起点
//数组中第 k 个最大的元素
//求a
while(fast!= nullptr && low!=fast){//初始都是head,不合适
low = low->next;
fast = fast->next ? fast->next->next:nullptr;//写反了
}
if(low == fast){
while(head!=low){
head = head->next;
low = low->next;
}
return low;
}else{
return nullptr;
}
}
};