Leetcode_2【链表】

文章链接: 代码随想录

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.设计链表  

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点

题目链接:707. 设计链表 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

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

题目链接:206. 反转链表 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

方法一:双指针

 // 基础数据结构操作
 // 双指针;递归
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就行,如果快慢指针会相交那么一定存在环。

总结

代码随想录

2. 两数相加

值得再刷一下

注意怎么新建一个链表节点

注意怎么处理进位

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;
    }
};

143. 重排链表

美团面试,我那容易出错的脆弱程序:

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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值