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) {}
* };
*/
203.移除链表元素
题目:给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
思路:数据结构基操。题目中的头节点说法不准确,害我浪费不少时间,实际是首元节点。
通过代码:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head == nullptr)
return head;
ListNode *h = new ListNode{0, head};
ListNode *prior, *ptr = h;
while(ptr != nullptr)
{
prior = ptr;
ptr = ptr -> next;
while(ptr != nullptr && ptr -> val == val)
{
prior -> next = ptr -> next;
ptr = ptr -> next;
}
}
return h -> next;
}
};
707.设计链表
题目:你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
思路:题目出的不好啊,看题解才知道Node节点明明有写好的,却不给提示,非要我自己写一个。
通过代码:
struct Node
{
int val;
Node *next;
};
class MyLinkedList {
Node *head;
public:
MyLinkedList() {
head = new Node{0, nullptr};
}
int get(int index) {
if(index < 0)
return -1;
int cnt = -1;
Node *ptr = head;
while(ptr != nullptr && cnt < index)
{
cnt++;
ptr = ptr -> next;
}
if(ptr == nullptr)
return -1;
else
return ptr -> val;
}
void addAtHead(int val) {
Node *node = new Node{val, head -> next};
head -> next = node;
}
void addAtTail(int val) {
Node *node = new Node{val, nullptr};
Node *ptr = head;
while(ptr -> next != nullptr)
ptr = ptr -> next;
ptr -> next = node;
}
void addAtIndex(int index, int val) {
if(index < 0)
return;
Node *node = new Node{val, nullptr};
int cnt = -1;
Node *ptr = head;
while(ptr != nullptr && cnt < index -1)
{
ptr = ptr ->next;
cnt++;
}
if(ptr != nullptr)
{
node -> next = ptr -> next;
ptr -> next = node;
}
}
void deleteAtIndex(int index) {
if(index < 0)
return;
int cnt = -1;
Node *ptr = head;
while(ptr != nullptr && cnt < index - 1)
{
ptr = ptr -> next;
cnt++;
}
if(ptr != nullptr && ptr -> next != nullptr)
{
Node *tmp = ptr -> next;
ptr -> next = tmp -> next;
delete tmp;
}
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
206.反转链表
题目:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
思路:双指针(或者说单链表的头插法),注意对调的时候需要用一个变量保存下一个节点的地址,防止地址丢失。
通过代码:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head -> next == nullptr)
return head;
ListNode *ptr = head -> next;
head -> next = nullptr;
while(ptr != nullptr)
{
ListNode *tmp = ptr -> next;
ptr -> next = head;
head = ptr;
ptr = tmp;
}
return head;
}
};
24. 两两交换链表中的节点
题目:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
思路一:双指针迭代,添加一个头节点
通过代码:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head -> next == nullptr)
return head;
ListNode *newhead = new ListNode{0, head};
ListNode *left = newhead, *right = head;
while(right != nullptr && right -> next != nullptr)
{
left -> next = right -> next;
right -> next = left -> next -> next;
left -> next -> next = right;
left = right;
right = left -> next;
}
return newhead -> next;
}
};
思路二:递归
19.删除链表的倒数第N个节点
题目:给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
思路:双指针,两个指针直接保持n的距离,一起滑到最后即可。其实这和遍历两边没啥区别,真想遍历一遍应该把所有节点的地址按顺序存下来。
通过代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head == nullptr)
return head;
ListNode *newhead = new ListNode{0, head};
ListNode *left = newhead, *right = newhead;
int cnt = 0;
while(right != nullptr && cnt < n)
{
right = right -> next;
cnt++;
}
while(right -> next != nullptr)
{
left = left -> next;
right = right -> next;
}
ListNode *tmp = left -> next;
left -> next = left -> next -> next;
delete tmp;
return newhead -> next;
}
};
面试题 02.07. 链表相交
题目:给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
思路一:哈希集合
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA || !headB)
return NULL;
unordered_set<ListNode *> u;
ListNode *pa = headA;
while(pa)
{
u.insert(pa);
pa = pa -> next;
}
ListNode *pb = headB;
while(pb)
{
if(u.find(pb) != u.end())
return pb;
pb = pb -> next;
}
return NULL;
}
};
思路二:双指针,我的想法是做减法使得长度一样。先遍历一遍得到长度差,让长的那个链表的指针先走这么多,然后长度就一样了,就可以放在一个while里遍历。官方题解是做加法,很巧妙,最后也能实现长度一样的效果。本质上两种解法指针走过的路一样长。
通过代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *newa = new ListNode{0, headA};
ListNode *newb = new ListNode{0, headB};
ListNode *pa = newa, *pb = newb;
int lena = 0 ,lenb = 0;
while(pa -> next)
{
pa = pa -> next;
lena++;
}
while(pb -> next)
{
pb = pb -> next;
lenb++;
}
if(!lena || !lenb || pa != pb)
return NULL;
int cnt = 0;
pa = newa;
pb = newb;
if(lena > lenb)
{
while(cnt < lena - lenb)
{
pa = pa -> next;
cnt++;
}
}
else
{
while(cnt < lenb - lena)
{
pb = pb -> next;
cnt ++;
}
}
while(pa -> next != pb -> next)
{
pa = pa -> next;
pb = pb -> next;
}
return pa -> next;
}
};
142.环形链表II
题目:给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
思路一:哈希表(集合),简单粗暴,空间复杂度O(n)。
通过代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head)
return NULL;
unordered_set<ListNode*> u;
ListNode *ptr = head;
while(ptr)
{
if(u.find(ptr) != u.end())
return ptr;
else
u.insert(ptr);
ptr = ptr -> next;
}
return NULL;
}
};
思路二:快慢指针,空间复杂度O(1)。快指针每次走2,慢指针每次走1。首先,如果存在环,快慢指针一定在环中相遇。其次,相遇的时候慢指针一定还在走第一圈。
如上如所示,由于快指针的速度是慢指针的两倍,所以快指针会多走一个慢指针的路程,即x+y,而这正是环长度的整数倍。
通过代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head)
return NULL;
ListNode *fast = head, *slow = head;
while(fast && fast -> next)
{
fast = fast -> next -> next;
slow = slow -> next;
if(fast == slow)
{
ListNode *index1 = head, *index2 = fast;
while(index1 != index2)
{
index1 = index1 -> next;
index2 = index2 -> next;
}
return index1;
}
}
return NULL;
}
};