203.移除链表元素
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解::代码随想录
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点并赋值;ATTENTION:new是用来开辟内存空间
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while (cur->next != NULL) {//or nullptr
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
707.设计链表
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
文章讲解/视频讲解:代码随想录
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val): val(val),next(nullptr){}//类构造函数的赋值
};
// 初始化
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 虚拟头结点,这里默认_dummyHead就是head
_size = 0;//通过函数动态变化的
}
// 获取第n个节点的值,0代表是的head
int get(int index) {
if(index < 0 || index > _size - 1) {
return -1;
}
// _dummyHead = head;
LinkedNode* curNode = _dummyHead->next;
while(index--){
curNode = curNode->next;
}
return curNode->val;
}
void addAtHead(int val) {
LinkedNode* addNode = new LinkedNode(val);
addNode->next = _dummyHead->next;
_dummyHead->next = addNode;
// head = addNode; 它是怎么知道谁是第一个节点的?
_size ++;
}
void addAtTail(int val) {
LinkedNode* curNode = _dummyHead;
LinkedNode* addNode = new LinkedNode(val);//默认next是null
while(curNode->next != nullptr){
curNode = curNode->next;
}//curNode指向尾部节点
curNode->next = addNode;
_size ++;
}
void addAtIndex(int index, int val) {
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* curNode = _dummyHead;
LinkedNode* addNode = new LinkedNode(val);//默认next是null
while(index--){//ATTENTION:第0个节点指的是head,也就是_dummyHead->next
curNode = curNode->next;
// index --;
}
addNode ->next = curNode -> next ;
curNode -> next = addNode ;
_size ++ ;
}
void deleteAtIndex(int index) {
if(index >=_size || index < 0) return;
// if(index < 0) index = 0;
LinkedNode* curNode = _dummyHead;
while(index--){
curNode = curNode->next;
}
LinkedNode* tmpNode = curNode->next;
curNode->next = curNode->next->next;
delete tmpNode;
tmpNode = nullptr;
_size --;
}
private:
int _size;
LinkedNode* _dummyHead;
};
/**
* 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.反转链表
建议先看视频讲解,视频讲解中对反转链表需要注意的点讲的很清晰
面试考了*
注意pre初始化是nullptr,三个指针的顺序是pre、cur、temp,最后cur会是nullptr所以要返回的是pre
文章讲解/视频讲解:代码随想录
方法一:双指针
// 基础数据结构操作
// 双指针;递归
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// ListNode* pre = new ListNode(); //ATTENTION : else [5,4,3,2,1,0]
ListNode* pre = NULL;//NULL本身就是一个节点
ListNode* cur = head;
ListNode* tmp;
while(cur != NULL){//ATTENTION the conditions
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
//递归
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL) return pre;
ListNode* temp = cur->next;
cur->next = pre;
// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
// pre = cur;
// cur = temp;
return reverse(cur,temp);
}
ListNode* reverseList(ListNode* head) {
// 和双指针法初始化是一样的逻辑
// ListNode* cur = head;
// ListNode* pre = NULL;
return reverse(NULL, head);
}
};
ONT4 链表内指定区间反转
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
//加个表头
ListNode* res = new ListNode(-1);
res->next = head;
//前序节点
ListNode* pre = res;
//当前节点
ListNode* cur = head;
//找到m
for(int i = 1; i < m; i++){
pre = cur;
cur = cur->next;
}
//从m反转到n
for(int i = m; i < n; i++){
ListNode* temp = cur->next;
cur->next = temp->next;
temp->next = pre->next;
pre->next = temp;
}
//返回去掉表头
return res->next;
}
};
92. 反转链表 II
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = dummy;
left--;
for(int i = 0; i < left && cur != nullptr; i++){
cur = cur->next;
}
ListNode* p1 = cur;
cur = cur->next;
ListNode* p2 = cur;
ListNode* pre = nullptr;
for(int i = 0; i < (right - left) && cur != nullptr; i++){
ListNode* temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
p1->next = pre;
p2->next = cur;
return dummy->next;
}
};
24. 两两交换链表中的节点
先看视频:为什么需要temp保存临时节点。用虚拟头结点会方便很多。 此时一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解: 代码随想录
为了最后能索引到head还是需要虚拟头节点的!
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr ){//奇数,偶数,注意顺序不要反了,要不会报错
ListNode* tmp = cur->next;
ListNode* tmp1 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = tmp;
cur->next->next->next = tmp1;
cur = cur->next->next;
}
return dummyHead->next;
}
};
二刷
cur是所交换的两个节点的前面那个节点,这一点不好想到。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
if(head == nullptr) return head;
while(cur != nullptr && cur->next != nullptr && cur->next->next !=nullptr){
ListNode* temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = temp1->next;
cur->next->next = temp1;
temp1->next = temp2;
cur = temp1;
}
return dummyHead->next;
}
};
三刷:虽然这个用的中间节点多但是好想
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = dummy;
while(cur != nullptr && cur->next != nullptr && cur->next->next != nullptr){
ListNode* temp1 = cur->next;
ListNode* temp2 = cur->next->next;
ListNode* temp3 = cur->next->next->next;
cur->next = temp2;
temp2->next = temp1;
temp1->next = temp3;
cur = temp1;
}
return dummy->next;
}
};
19.删除链表的倒数第N个节点【双指针】
双指针的操作,要注意,删除第N个节点,那么我们当前遍历的指针一定要指向第N个节点的前一个节点,先看视频。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解:代码随想录
fast = fast->next那一步很重要哦
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0) ;
dummyHead->next = head ;
ListNode* fast = dummyHead ;
ListNode* slow = dummyHead ;
for(int i = 0; i < n && fast != nullptr; i++){
fast = fast->next;
}
// while(n-- && fast != NULL) {
// fast = fast->next;
// }//都可以
fast = fast->next;//ATTENTION: 因为需要让slow指向删除节点的上一个节点,fast再提前走一步
while(fast != NULL){
fast = fast->next;
slow = slow->next;
}
// slow->next = nullptr;//删除所有后面的元素了
slow->next = slow->next->next;
return dummyHead->next;
//ATTENTION: return head是不对的因为有可能head被清除了
}
};
面试题 02.07. 链表相交
数值相同,不代表指针相同。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解:代码随想录
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
我的
/**
* 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) {
int distA = 0, distB = 0;
if(headA == NULL || headB == NULL) return NULL;
ListNode *curA = headA, *curB = headB;
while(curA != NULL){
curA = curA->next;
distA ++;
}
while(curB != NULL){
curB = curB->next;
distB ++;
}
int diff = abs(distB - distA);
curA = headA;
curB = headB;
if(distA > distB){
while(diff--){
curA = curA->next;
}
}else{
while(diff--){
curB = curB->next;
}
}
while(curA != NULL && curB != NULL){
if(curA == curB) return curA;
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
21. 合并两个有序链表
最优美的解法出现了:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
while(list1 != nullptr && list2 != nullptr){
if(list1->val > list2->val){
cur->next = list2;
list2 = list2->next;
}else{
cur->next = list1;
list1 = list1->next;
}
cur = cur->next;
}
if(list1 != nullptr) cur->next = list1;
if(list2 != nullptr) cur->next = list2;
return dummy->next;
}
};
一刷:我做的不算简单
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(0);
ListNode* cur2 = nullptr;
if(list1 == nullptr && list2 == nullptr) return nullptr;
if(list1 != nullptr && list2 == nullptr) return list1;
if(list1 == nullptr && list2 != nullptr) return list2;
if(list1->val <= list2->val){
dummy->next = list1;
cur2 = list2;
}else{
dummy->next = list2;
cur2 = list1;
}
ListNode* cur = dummy->next;
while(cur != nullptr && cur2 != nullptr){
cout << cur->val << endl;
ListNode* temp = cur->next;
ListNode* temp2 = cur2->next;
if(cur2->val >= cur->val && (temp == nullptr || cur2->val < temp->val)){
cur->next = cur2;
cur2->next = temp;
cur2 = temp2;
}
cur = cur->next;
}
return dummy->next;
}
};
二刷这么做的,list2向list1中插入:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(INT_MIN);
dummy->next = list1;
ListNode* cur1 = dummy;
ListNode* cur2 = list2;
while(cur1 != nullptr && cur1->next != nullptr && cur2 != nullptr){
if(cur1->val <= cur2->val && cur1->next->val >= cur2->val){
ListNode* temp1 = cur1->next;
ListNode* temp2 = cur2->next;
cur1->next = cur2;
cur2->next = temp1;
cur2 = temp2;
cur1 = cur1->next;
}else{
cur1 = cur1->next;
}
}
if(cur1->next == nullptr && cur2 != nullptr){
cur1->next = cur2;
}
return dummy->next;
}
};
148. 排序链表【难题,需要递归】
class Solution {
public:
ListNode* sortList(ListNode* head) {
// return condition
if(head == nullptr || head->next == nullptr) return head;
// split
ListNode* head1 = head;
ListNode* head2 = split(head);// midpoint
// sort - iter
head1 = sortList(head1);
head2 = sortList(head2);
//merge - sort logic
return merge(head1, head2);
}
//分割出后一个,前一个需要断尾
ListNode* split(ListNode* node){
ListNode* fast = node, *slow = node;
fast = fast->next;
while(fast != nullptr && fast->next != nullptr){
slow = slow->next;
fast = fast->next->next;
}
ListNode* mid = slow->next;
slow->next = nullptr;
return mid;
}
// merge node2 in node1 , return node1
ListNode* merge(ListNode* node1, ListNode* node2){
ListNode* dummy = new ListNode(INT_MIN);
dummy->next = node1;
ListNode* cur1 = dummy, *cur2 = node2;
while(cur1 != nullptr && cur1->next != nullptr && cur2 != nullptr){
if(cur1->val <= cur2->val && cur1->next->val > cur2->val){
ListNode* temp1 = cur1->next;
ListNode* temp2 = cur2->next;
cur1->next = cur2;
cur2->next = temp1;
cur2 = temp2;
}
cur1 = cur1->next;
}
if(cur1->next == nullptr && cur2 != nullptr){
cur1->next = cur2;
}
return dummy->next;
}
};
142.环形链表II 【妙啊】
算是链表比较有难度的题目,需要多花点时间理解 确定环和找环入口,先看视频。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解:代码随想录
注意里面有两个while,第一个while为了找到fast和slow相交的节点;第二个while为了从相遇的点和起点出发的两个点,找到它们相遇的点就是入口了。
注意第一个while的条件,为了保证fast = fast->next->next不报错
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast!=nullptr && fast->next!=nullptr){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
ListNode* index1 = head;
ListNode* index2 = slow;
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return nullptr;
}
};
141. 环形链表
此题用来判断是否存在环,只需上面的第一个while就行,如果快慢指针会相交那么一定存在环。
总结
值得再刷一下
注意怎么新建一个链表节点
注意怎么处理进位
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {//l1 l2就是head..
ListNode* myList = new ListNode(0);
ListNode* dummy = new ListNode(0);
dummy->next = myList;
ListNode* cur1 = l1;
ListNode* cur2 = l2;
int plus = 0;
int val;
while(cur1 != NULL || cur2 != NULL){
if(cur1 != NULL && cur2 != NULL) {
val = cur1->val + cur2->val + plus;
if(myList!=nullptr) myList->next = new ListNode(val % 10);
plus = val / 10;
else plus = 0;
cur1 = cur1->next;
cur2 = cur2->next;
myList = myList->next;
}else if(cur1 != NULL && cur2 == NULL){
val = cur1->val + plus;
if(myList!=nullptr) myList->next = new ListNode(val % 10);
plus = val / 10;
else plus = 0;
cur1 = cur1->next;
myList = myList->next;
}
else{
val = cur2->val + plus;
if(myList!=nullptr) myList->next = new ListNode(val % 10);
plus = val / 10;
else plus = 0;
cur2 = cur2->next;
myList = myList->next;
}
}
if(plus != 0)
{
myList->next = new ListNode(plus);
}
return dummy->next->next;
}
};
美团面试,我那容易出错的脆弱程序:
class Solution {
public:
void reorderList(ListNode* head) {
ListNode* cur1 = head;
int count = 0;
while(cur1 != nullptr){
cur1 = cur1->next;
count++;
}
if(count < 3) return;
cur1 = head;
for(int i = 0; i < count/2 - 1; i++){
cur1 = cur1->next;
}
ListNode *list2 = swap(cur1->next);
ListNode *cur2 = list2;
while(cur2 != nullptr ){
cur2 = cur2->next;
}
ListNode* cur = head;
while(count % 2 == 0 && cur != cur1 || count % 2 == 1 && cur != cur1->next && cur != nullptr ){
ListNode* temp1 = cur->next;
ListNode* temp2 = list2->next;
cur->next = list2;
list2->next = temp1;
cur = temp1;
list2 = temp2;
}
}
ListNode* swap(ListNode* node){
ListNode *cur = node, *pre = nullptr;
//pre->next = cur;
ListNode *temp;
while(cur!=nullptr){
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
这个方法好!双端队列!
/**
* 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:
void reorderList(ListNode* head) {
deque<ListNode*> que;
ListNode* cur = head->next;
while(cur != nullptr){
que.push_back(cur);
cur = cur->next;
}
cur = head;
int count = 0;
while(!que.empty()){
if(count % 2 == 0){
cout << "back"<<count << que.back()->val << endl;
cur->next = que.back();
que.pop_back();
}else{
cout <<"back"<< count <<que.front()->val << endl;
cur->next = que.front();
que.pop_front();
}
cur = cur->next;
count ++;
}
cur->next = nullptr;
}
};