一起刷算法与数据结构-链表篇1

大家好,我是工藤学编程 大二在读
作业侠系列最新文章 Java实现聊天程序
一起备战ccf-csp与蓝桥杯系列最新文章 一起备战蓝桥杯与CCF-CSP之大模拟炉石传说
一起刷算法与数据结构最新文章 一起刷算法与数据结构-链表篇1

一起刷算法与数据结构专栏说明:

由于博主数据结构与算法能力处于普通水平,就和大多数同学一样,本专栏会按照每个知识点整理题目,同时,为了节约大家的时间,为大家去掉了很简单的题,整理出了适合我们思考的难度的题目,题目来源均为Acwing或者lc,如果大家对想刷数据结构和算法但又不知道重合下手,那大家可以按照我为大家整理的题目来,每个题目都为大家准备了通俗易懂解题思路和参考代码,准备面试什么的非常适合

接下来进入的开始篇:
一起刷算法与数据结构-链表篇1

一起刷算法与数据结构-链表篇1_第1张图片

题目1.在O(1)时间删除链表节点
题目2.删除链表中重复的节点
题目3.链表中环的入口节点
题目4.反转链表
题目5.两个链表的第一个公共结点
题目6.奇偶链表
题目7.链表随机节点
题目8.合并两个链表
题目9.删除链表的倒数第N个元素
题目10.从链表中删去总和值为0的连续节点

题目1.在O(1)时间删除链表节点
一起刷算法与数据结构-链表篇1_第2张图片解题思路:

由于是单链表,我们不能找到前驱节点,所以我们不能按常规方法将该节点删除。
但是我们可以换一种思路,将下一个节点的值复制到当前节点,然后将下一个节点删除即可(因为题目保证了删除的节点一定不是尾节点)。

参考代码(c++):

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        node->val = node->next->val;
        node->next = node->next->next;
    }
};

题目2.删除链表中重复的节点

一起刷算法与数据结构-链表篇1_第3张图片解题思路:

通常可能会删除首元节点的情况,我们都会通过定义一个虚拟头节点 指向链表首元节点。
对于此题,定义好头节点之后,从前往后扫描整个链表,每次扫描元素相同的一段,如果这段中的元素个数多于1个,则将整段元素直接删除即可。

拓展:头指针和头节点的区别

头指针:
(1)头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针
(2)无论链表是否为空,头指针均不为空。头指针是链表的必要元素
头结点:
(1)头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(也可存放链表的长度)
(2)有了头结点,对第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了
(3)头结点不一定是链表必须要素

参考代码(c++):

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplication(ListNode* head) {
        auto dummy = new ListNode(-1);
        dummy->next = head;

        auto p = dummy;
        while (p->next) {
            auto q = p->next;
            while (q && p->next->val == q->val) q = q->next;

            if (p->next->next == q) p = p->next;
            else p->next = q;
        }

        return dummy->next;
    }
};


题目3.链表中环的入口节点

一起刷算法与数据结构-链表篇1_第4张图片解题思路:

1.判断有无环常用方法:set,快慢指针
2.通过快慢指针如何去判断入口节点:看y总咋说就对了
一起刷算法与数据结构-链表篇1_第5张图片

参考代码(c++)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *entryNodeOfLoop(ListNode *head) {
        if(head==NULL || head->next==NULL) return NULL;
        auto first = head,second = head;
        while(first && second){
            first = first->next;
            second=second->next;
            if(second) second=second->next;//巧妙
            if(first == second){
                first = head;
                while(first!=second){
                    first=first->next;
                    second=second->next;
                }
                return first;
            }
        }
        return NULL;
    }
};

题目4.反转链表
一起刷算法与数据结构-链表篇1_第6张图片
解题思路:

翻转即将所有节点的next指针指向前驱节点。
由于是单链表,我们在迭代时不能直接找到前驱节点,所以我们需要一个额外的指针保存前驱节点。同时在改变当前节点的next指针前,不要忘记保存它的后继节点。

参考代码(c++循环版):

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head || !head->next) return head;
        auto per = head,cur = per->next;
        while(cur){
            auto temp=cur->next;
            cur->next=per;
            per=cur;
            cur=temp;
        }
        head->next=nullptr;
        return per;
    }
};

递归版(来自y总):

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode *tail = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return tail;
    }
};

作者:yxc
链接:https://www.acwing.com/solution/content/743/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

题目5.两个链表的第一个公共结点

一起刷算法与数据结构-链表篇1_第7张图片
解题思路:

一起刷算法与数据结构-链表篇1_第8张图片
图片来源:https://www.acwing.com/solution/content/39358/

参考代码(c++):

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
       auto p=headA,q=headB;
       while(p!=q)
       {
           if(p) p=p->next;
           else p=headB;
           if(q) q=q->next;
           else q=headA;
       }
       return p;
    }
};

题目6.奇偶链表
一起刷算法与数据结构-链表篇1_第9张图片
解题思路:

如果链表为空,则直接返回链表。
对于原始链表,每个节点都是奇数节点或偶数节点。头节点是奇数节点,头节点的后一个节点是偶数节点,相邻节点的奇偶性不同。因此可以将奇数节点和偶数节点分离成奇数链表和偶数链表,然后将偶数链表连接在奇数链表之后,合并后的链表即为结果链表

注意需要有一个链表代替偶数节点去遍历,不然遍历之后你就失去他了

参考代码(c++)

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if(!head || !head->next) return head;
           auto enve = head,unodd1=head->next;
           auto unodd=unodd1;
           while(enve && enve->next && unodd->next)
           {
               enve->next=unodd->next;
               enve=enve->next;
               unodd->next=enve->next;
               unodd=unodd->next;
           }
           enve->next=unodd1;
           return head;
    }
};

题目7.链表随机节点

一起刷算法与数据结构-链表篇1_第10张图片
解题思路:

方法1:用一个数组记录链表中的所有元素,这样随机选择链表的一个节点,就变成在数组中随机选择一个元素
方法2:蓄水池抽样算法
由于真的不好理解,视频推荐:蓄水池抽样算法

参考代码:
方法1:

class Solution {
    vector<int> arr;

public:
    Solution(ListNode *head) {
        while (head) {
            arr.emplace_back(head->val);
            head = head->next;
        }
    }

    int getRandom() {
        return arr[rand() % arr.size()];
    }
};

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/linked-list-random-node/solution/lian-biao-sui-ji-jie-dian-by-leetcode-so-x6it/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法2:

class Solution {
    ListNode *head;

public:
    Solution(ListNode *head) {
        this->head = head;
    }

    int getRandom() {
        int i = 1, ans = 0;
        for (auto node = head; node; node = node->next) {
            if (rand() % i == 0) { // 1/i 的概率选中(替换为答案)
                ans = node->val;
            }
            ++i;
        }
        return ans;
    }
};

题目8.合并两个链表
一起刷算法与数据结构-链表篇1_第11张图片
需要注意:

不要想着直接返回left,而因返回list1,因为left只是list1的一部分

参考代码(c++):

class Solution {
public:
    ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
        
        auto left=new ListNode(),right=new ListNode(),tail=new ListNode();
        auto dummy = new ListNode(-1);
        auto q=dummy;
        int i=0;
        for(auto p=list1;p;p=p->next)
        {
            i++;
            if(i==a) left=p;
        }
        i=0;
        for(auto p=list1;p;p=p->next)
        {
            i++;
            if(i==b+2) right=p;
        }
        for(tail=list2;tail->next;tail=tail->next)
        {
             
        }
        left->next=list2;
        tail->next=right;
        return list1;
           
    }
};

题目9.删除链表的倒数第N个元素
一起刷算法与数据结构-链表篇1_第12张图片
思路:

一边扫描的方法:先让一个指针走n步,然后再用另一个指针,直到第一个指针走到空节点,这时即可走到倒数第N个节点上,为了方变,我们在第二指针上定义一个头节点,这样我们更容易删除倒数第n个节点

参考代码(c++)

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
  auto dummy = new ListNode(-1);
         dummy->next = head;
        auto first = head;
        auto second = dummy;
        for(int i=0;i<n;i++)
        {
            first=first->next;
        }
        while(first)
        {
            first=first->next;
            second=second->next;
        }
        second->next=second->next->next;
        return dummy->next;
    }
};

题目10.从链表中删去总和值为0的连续节点
一起刷算法与数据结构-链表篇1_第13张图片
参考大佬解法:
一起刷算法与数据结构-链表篇1_第14张图片
大佬代码:

  unordered_map<int, ListNode*> map;
        auto* dummyhead = new ListNode(0, head);
        int sum = 0;
        for (auto* cur = dummyhead; cur != nullptr; cur = cur->next)
        {
            /*
            求出每个结点的前缀和 存到一个哈希表中
            如果有相同的前缀和 那么自动覆盖为最新的节点
            */
            sum += cur->val;
            map[sum] = cur;
        }
        sum = 0;
        for (auto* e = dummyhead; e != nullptr; e = e->next)
        {
            /*
            如果两个结点的前缀和相等 说明它们中间的节点(包括右边那个结点)的和是0
            由于不能delete头结点 我们直接让这个节点的next等于右边结点的next
            如果这两个结点是同一个结点 这步相当于没任何操作
            */
            sum += e->val;
            e->next = map[sum]->next;
        }
        head = dummyhead->next;
        delete dummyhead;
        return head;

作者:Sleeping-Router
链接:https://leetcode.cn/problems/remove-zero-sum-consecutive-nodes-from-linked-list/solution/bao-li-jie-fa-he-ha-xi-biao-jie-fa-by-mo-bqb3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(算法,链表,数据结构)