CMU硕士101-我与链表的故事
链表的基本操作
翻转一个链表
有迭代法和递归法,这两个都要画图,手撕。
/*
思路:
两个都要画图
链表的题目还是要画图.
方法一:迭代法
1->2->3->nullptr 转换成 nullpter->1->2->3
在遍历链表时,将curr节点的next指向pre,所以要先保存next节点,然后跳到之前的next,之前的next指向之前的curr
1,保存next节点
2,curr->next指向pre
3,pre变成curr,curr变成next
方法二:递归法
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
};
/*
[先画图用1->2来理解,](https://blog.csdn.net/w605283073/article/details/86653745?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163251905716780357275848%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163251905716780357275848&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-86653745.pc_search_insert_js_new&utm_term=%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8%E9%80%92%E5%BD%92&spm=1018.2226.3001.4187)
*/
struct ListNode* reverseList(struct ListNode* head)
{
if((head == NULL )||(head->next==NULL))
return head;
struct ListNode *new_Head = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return new_Head;
}
合并链表:两个有序链表,合并成一个有序链表
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* l1, ListNode* l2) {
ListNode *ret = new ListNode(0), *node = ret;
// 非递归法
while (l1 && l2) {
if (l1->val <= l2->val) {
node->next = l1;
l1 = l1->next;
} else {
node->next = l2;
l2 = l2->next;
}
node = node->next;
}
node->next = l1?l1:l2;
return ret->next;
}
};
24 两两交换链表中的节点*
这题要学会怎样交换链表中的两个节点,画个图更清晰
参考链接:
class Solution {
public:
void swapTwo(ListNode* start){
ListNode* a=start->next;
ListNode* b=start->next->next;
start->next=b;
a->next=b->next;
b->next=a;
}
ListNode* swapPairs(ListNode* head) {
ListNode* start=new ListNode(0);
start->next=head;
//哨兵节点 ans,用于记录链表的开头,方便输出
ListNode* ans=start;
while(start->next!=nullptr && start->next->next!=nullptr){
swapTwo(start);
//不能忘了更新 start
start=start->next->next;
}
return ans->next;
}
};
160 判断链表能否交于一点*
/*
思路:
如果要返回相交点位置, 我们必须让两个head从距离末尾相等的地方遍历. 但两个链表的长度有可能是不同的,
所以我们应该选取短链表的头节点位置,所以问题转换成 “如何消除两个链表的长度差?”
solution:两个head同时从头部遍历,到达尾部的那个节点,指向另一个链表的头结点。这样就消除了链表差。
如果链表相交,会交于一个非空点。如果不会相交,最后相聚的就是空节点。
如果两个链表相交,相交之后的长度是相同的。
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
要参考:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/tu-jie-xiang-jiao-lian-biao-by-user7208t/
错在, pA = (pA == nullptr) ? headB:pA->next;
如果pA走到了末尾,将pA指向另一个链表的头结点。
如果没有走到末尾,指向pA的next
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == nullptr || headB == nullptr) { // 链表一般都要判空
return nullptr;
}
ListNode *pA = headA, *pB = headB;
while(pA != pB) {
pA = (pA == nullptr) ? headB:pA->next;
pB = (pB == nullptr) ? headA:pB->next;
}
return pA;
}
};
234 判断链表是否回文
/*
一个链表是不是回文链表
方法一:将链表中的数据都提取到vector中,转换成vec,比较机械。不建议。
方法二:找到链中间的点,然后翻转后半段链表,让两段链表逐个比较
前半段和后半段逐个比较,要保存头节点和中间节点。
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
bool isPalindrome(ListNode* head) {
ListNode *pHead = head;
ListNode *slow = head, *fast = head;
// 找到中间节点
while(fast->next && fast->next->next) { // 这里为什么找到 while(slow->next && fast->next->next) 就会判断空指针。因为对的要更严格一些。
slow = slow->next;
fast = fast->next->next;
}
// 保存中间节点,奇偶怎样考虑(画图ok)
ListNode *mid = slow;
// 翻转mid之后的链表
ListNode *reverseAfterMid = reverseList(mid);
// 判断前半段和后半段是否相等
while(pHead && reverseAfterMid) {
if(pHead->val != reverseAfterMid->val) {
return false;
}
pHead = pHead->next;
reverseAfterMid = reverseAfterMid->next;
}
return true;
}
};
83 . 删除排序链表中的重复元素
// 思路:按照升序排列, 保存头结点,遍历数组,next和当前相等的,next = next->next
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (head == nullptr) { // 判空
return head;
}
ListNode *pHead = head; // 保存头结点
while(head && head->next) {
if (head->next->val == head->val) { // 当下一个值与head val相同时,一直到让next等于next->next,遇到不相等的,让head指向下一个next
head->next = head->next->next;
} else {
head = head->next; // 这里纠结了一下,考虑{1,1,1}的场景
}
}
return pHead;
}
};
328. 奇偶链表
/*
思路:分开两条路一奇一偶,然后让奇数链表最后挂接偶数链表,最终返回奇数链表的头部
*/
class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
// 判空
if (head == nullptr || head->next == nullptr) {
return head;
}
// 创建奇数链表和偶数链表,并保存哨兵节点
ListNode *oddList = head, *evenList = head->next;
ListNode *oddHead = oddList, *evenHead = evenList;
// 奇偶链表分别向后延伸两个节点,一直到当前next的节点为空
while(oddList->next && evenList->next) {
oddList->next = oddList->next->next;
evenList->next = evenList->next->next;
oddList = oddList->next;
evenList = evenList->next;
}
// 奇数链表尾部挂接偶数链表
oddList->next = evenHead;
// 最终返回奇数链表
return oddHead;
}
};
19. 删除链表的倒数第 N 个结点
****
// 思路,找到head后面的n个节点作为post,head和post一起向后移动,当post的next为空时,head的next等于next->next,然后返回哨兵节点。我们可以找到中间的节点,就可以找到第n个点,也可以找到倒数第n个点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if (head == nullptr || head->next == nullptr) {
return nullptr;
}
ListNode *pHead = head, *post = head;
ListNode *ret = pHead;
while(n--) {
post = post->next; // 找到第n个节点
}
if (post == nullptr) { // 如果是要删除第一个节点, 直接返回head->next, 这个有点打补丁
return head->next;
}
while(post && post->next) { //
post = post->next;
pHead = pHead->next;
}
pHead->next = pHead->next->next;
return ret;
}
};
148 排序链表
/*
思路:这题归根结底是排序,对数组排序可以用归并法,对链表排序其实也是可以用的。
1,找到链表的中点,将链表拆分成两个子链表。// 可以使用快慢指针法,返回slow的时候要保证无人指向slow,mid本身就是一个开始
2,对两个链表进行排序
3,合并 治法
延伸,分治法的思想
*/
// https://leetcode-cn.com/problems/sort-list/solution/c-an-bu-jiu-ban-qing-xi-de-gui-bing-pai-0cu85/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;
ListNode* mid = middleNode(head);
ListNode* left = sortList(head);
ListNode* right = sortList(mid);
return mergeTwoLists(left, right);
}
ListNode* middleNode(ListNode* head) {
ListNode* fast = head, *slow = head;
ListNode* prev = slow;
while (fast && fast->next) {
prev = slow;
fast = fast->next->next;
slow = slow->next;
}
prev->next = nullptr; // 让slow和head断了关系,slow在这里就是一个独立的节点了,无人指向mid
return slow;
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* head = new ListNode(0);
ListNode* tail = head;
while (l1 && l2) {
if (l1->val <= l2->val) {
tail->next = l1;
l1 = l1->next;
} else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
tail->next = l1 ? l1 : l2;
return head->next;
}
};
141 判断是不是环形链表
class Solution {
public:
bool hasCycle(ListNode *head) {
auto slow = head, fast = head;
while(fast && fast->next) // 保证非空
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast) return true;
}
return false; // 能走到这里说明其中一个走到了空
}
};