本文记录的是刷题过程中的重要概念和笔记。如有侵权,请联系删除。
234.回文链表
力扣题目链接(opens new window)
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
思路
数组模拟
最直接的想法,就是把链表装成数组,然后再判断是否回文。
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode* cur = head;
int length = 0;
while (cur) {
length++;
cur = cur->next;
}
vector<int> vec(length, 0); // 给定vector的初始长度,这样避免vector每次添加节点重新开辟空间
cur = head;
int index = 0;
while (cur) {
vec[index++] = cur->val;
cur = cur->next;
}
// 比较数组回文
for (int i = 0, j = vec.size() - 1; i < j; i++, j--) {
if (vec[i] != vec[j]) return false;
}
return true;
}
};
反转后半部分链表
分为如下几步:
- 用快慢指针,快指针有两步,慢指针走一步,快指针遇到终止位置时,慢指针就在链表中间位置
- 同时用pre记录慢指针指向节点的前一个节点,用来分割链表
- 将链表分为前后均等两部分,如果链表长度是奇数,那么后半部分多一个节点
- 将后半部分反转 ,得cur2,前半部分为cur1
- 按照cur1的长度,一次比较cur1和cur2的节点数值
需要分类讨论链表节点数为奇数和偶数的情况。
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (head == nullptr || head->next == nullptr) return true;
ListNode* slow = head; // 慢指针,找到链表中间分位置,作为分割
ListNode* fast = head;
ListNode* pre = head; // 记录慢指针的前一个节点,用来分割链表
while (fast && fast->next) {
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = nullptr; // 分割链表
ListNode* cur1 = head; // 前半部分
ListNode* cur2 = reverseList(slow); // 反转后半部分,总链表长度如果是奇数,cur2比cur1多一个节点
// 开始两个链表的比较
while (cur1) {
if (cur1->val != cur2->val) return false;
cur1 = cur1->next;
cur2 = cur2->next;
}
return true;
}
// 反转链表
ListNode* reverseList(ListNode* head) {
ListNode* temp; // 保存cur的下一个节点
ListNode* cur = head;
ListNode* pre = nullptr;
while(cur) {
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre;
}
};
------------------------------------------------------------
143.重排链表 *
力扣题目链接(opens new window)
思路
三种C++实现的方法
数组模拟
双向队列模拟
直接分割链表
数组模拟
把链表放进数组中,然后通过双指针法,一前一后,来遍历数组,构造链表。
class Solution {
public:
void reorderList(ListNode* head) {
vector<ListNode*> vec;
ListNode* cur = head;
if (cur == nullptr) return;
while(cur != nullptr) {
vec.push_back(cur);
cur = cur->next;
}
cur = head;
int i = 1;
int j = vec.size() - 1; // i j为之前前后的双指针
int count = 0; // 计数,偶数去后面,奇数取前面
while (i <= j) {
if (count % 2 == 0) {
cur->next = vec[j];
j--;
} else {
cur->next = vec[i];
i++;
}
cur = cur->next;
count++;
}
cur->next = nullptr; // 注意结尾
}
};
双向队列模拟
把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了
class Solution {
public:
void reorderList(ListNode* head) {
deque<ListNode*> que;
ListNode* cur = head;
if (cur == nullptr) return;
while(cur->next != nullptr) {
que.push_back(cur->next);
cur = cur->next;
}
cur = head;
int count = 0; // 计数,偶数去后面,奇数取前面
ListNode* node;
while(que.size()) {
if (count % 2 == 0) {
node = que.back();
que.pop_back();
} else {
node = que.front();
que.pop_front();
}
count++;
cur->next = node;
cur = cur->next;
}
cur->next = nullptr; // 注意结尾
}
};
直接分割链表
将链表分割成两个链表,然后把第二个链表反转,之后在通过两个链表拼接成新的链表。
class Solution {
private:
// 反转链表
ListNode* reverseList(ListNode* head) {
ListNode* temp; // 保存cur的下一个节点
ListNode* cur = head;
ListNode* pre = NULL;
while(cur) {
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre;
}
public:
void reorderList(ListNode* head) {
if (head == nullptr) return;
// 使用快慢指针法,将链表分成长度均等的两个链表head1和head2
// 如果总链表长度为奇数,则head1相对head2多一个节点
ListNode* fast = head;
ListNode* slow = head;
while (fast && fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
}
ListNode* head1 = head;
ListNode* head2;
head2 = slow->next; // 因为没有留下pre,所以才需要这样
slow->next = nullptr;
// 对head2进行翻转
head2 = reverseList(head2);
// 将head1和head2交替生成新的链表head
ListNode* cur1 = head1;
ListNode* cur2 = head2;
ListNode* cur = head;
cur1 = cur1->next;
int count = 0; // 偶数取head2的元素,奇数取head1的元素
while (cur1 && cur2) {
if (count % 2 == 0) {
cur->next = cur2;
cur2 = cur2->next;
} else {
cur->next = cur1;
cur1 = cur1->next;
}
count++;
cur = cur->next;
}
if (cur2 != nullptr) { // 处理结尾
cur->next = cur2;
}
if (cur1 != nullptr) {
cur->next = cur1;
}
}
};
------------------------------------------------------------
141. 环形链表
力扣题目链接(opens new window)
给定一个链表,判断链表中是否有环.
如果链表中存在环,则返回 true 。 否则,返回 false 。
思路
已经做过,快慢指针是否重逢。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,说明有环
if (slow == fast) return true;
}
return false;
}
};