链表习题-力扣oj (附加思路版)

返回倒数第k个值  

LCR 140. 训练计划 IIicon-default.png?t=N7T8https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

        给定一个头节点为 head 的链表用于记录一系列核心肌群训练项目编号,请查找并返回倒数第 cnt 个训练项目编号。

思路:

        双指针,快指针先走cnt步然后快慢指针同时向前走,当快指针为空时慢指针所在位置即为倒是第cnt个编号

class Solution {
public:
    ListNode* trainingPlan(ListNode* head, int cnt) {
       ListNode *fast=head,*slow=head;
       for(int i=1;i<=cnt;i++)
       fast=fast->next;
       while(fast)
       {
           fast=fast->next;
           slow =slow->next;
       }
       return slow;
    }
};

判断是否构成环  

141. 环形链表icon-default.png?t=N7T8https://leetcode.cn/problems/linked-list-cycle/

        如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。如果链表中存在环 ,则返回 true 。 否则,返回 false 。

思路:

        双指针,一个快指针一个慢指针,快指针每次走两步(有风险:快指针可能为要特殊处理一下)慢指针走一步。如果两个指针能相遇则有环,如果不能相遇则没有环

class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *fast=head,*slow=head;
      while(fast&&fast->next){
          fast=fast->next->next;
          slow=slow->next;
          if(fast==slow)
            return 1;
      }
      return false;
    }
};

 删除链表中重复元素 

26. 删除有序数组中的重复项icon-default.png?t=N7T8https://leetcode.cn/problems/remove-duplicates-from-sorted-array/

        给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

思想:

        双指针,快指针如果与慢指针相等快指针++,当快指针不等于慢指针时,慢指针++快慢赋值给慢指针,然后快指针++。直到快指针遍历完数组结束。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
      int fast=1,slow=0;
     if(nums.size()==1)
        return 1;
     while(fast<nums.size())
     {
         if(nums[fast]==nums[slow])
            fast++;
         else
         {
            slow++;
            nums[slow]=nums[fast];
            fast++;
         }
     }
     return slow+1;
    }
};

82. 删除排序链表中的重复元素 IIicon-default.png?t=N7T8https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/

        给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

 思路

        创建一个虚头节点,前驱节点pre和当前节点cur。如果当前节点的值等于下一个节点的值,就找一个临时变量存下,如果当前节点的值等于临时变量,则开始删除。否则当前节点和前驱节点都移动,最后返回虚头节点的下一个节点。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode *Head=new ListNode(0,head);
        ListNode*pre=Head,*cur=head;
        while(cur&&cur->next)
        {
            if(cur->val==cur->next->val)
            {
                int temp=cur->val;//记录要删除的节点
                    while(cur && cur->val==temp)
                    {
                    pre->next=cur->next;   
                    cur=cur->next;
                    }
            }
            else
            {
                 cur=cur->next;
                 pre=pre->next;
            }           
        }
        return Head->next;
    }
};

最大子数组和  

53. 最大子数组和icon-default.png?t=N7T8https://leetcode.cn/problems/maximum-subarray/

        给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。

思路:

        将数组和与最大和比较,如果数组和大于最大和则更新,否则最大和加下一位数组元素判断有没有当前最大和大。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int max=INT_MIN;
        int sum=0;
        for(int i=0;i<nums.size();i++)
        {
            sum+=nums[i];
            if(sum>max)max=sum;
            if(sum<0)sum=0;
        }
        return max;
    }
};

链表的中间节点  

876. 链表的中间结点icon-default.png?t=N7T8https://leetcode.cn/problems/middle-of-the-linked-list/

        给你单链表的头结点 head ,请你找出并返回链表的中间结点。

        如果有两个中间结点,则返回第二个中间结点。

思路:快指针走两步慢指针走一步

class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* fast=head,*slow =head;
        while(fast&& fast->next)
        {
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
    }
};

合并两个有序链表 

21. 合并两个有序链表icon-default.png?t=N7T8https://leetcode.cn/problems/merge-two-sorted-lists/

        将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

思路:

        当头节点和中间节点的处理方式不同时手动添加一个虚头节点

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        //创建虚头节点
         ListNode* Head = new ListNode();
         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;
         }
        //判断剩余
         if(l1) Tail->next=l1;
         if(l2) Tail->next=l2;
         return Head->next;
    }
};

反转链表

206. 反转链表icon-default.png?t=N7T8https://leetcode.cn/problems/reverse-linked-list/

        给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

思路:

        定义一个cur代表当前要反转的节点 ,定义pre为空,让cur->next指向pre,此时需要一个next找到后面

(pre是反转要指向的节点 cur是当前要反转的节点 next记录下一个要反转的节点

即cur->next=pre pre=cur cur=next next=next->next)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head)return head;
        ListNode *cur=head,*pre=nullptr,*next=head->next;
        while(cur)
        {
            cur->next=pre;
            pre=cur;
            cur=next;
            if(next)
            next=next->next;
        }
        return pre;
    }
};

相交链表

160. 相交链表icon-default.png?t=N7T8https://leetcode.cn/problems/intersection-of-two-linked-lists/

        给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

思路

        两个指针,a指针先走链表1然后走链表2,b指针先走链表2在走链表1如果在都走完之前两指针会相遇则两链表相交否则不相交

(

A=A?A->next:headB; 这行代码的意思是,如果A非空,即A指向的节点不为空,则将A指针移向下一个节点,即A=A->next;如果A为空,即已,经达到了链表末尾则让A重新指向headB,即A=headB。这样保证了A指针遍历完链表A后可以继续遍历链表B中的节点。

B=B?B->next:headA; 同理。

)

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode*a=headA,*b=headB;
        while(a!=b)
        {
          if(a)
          a=a->next;
          else
          a=headB;  
          b=b?b->next:headA;        
        }        
         return a;
    }   
};


回文链表

LCR 027. 回文链表icon-default.png?t=N7T8https://leetcode.cn/problems/aMhZSa/

        给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

思路

        先找到中间节点(快指针走两步慢指针走一步),将后半部分也就是4-1进行反转,比较pre和head链表 

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode*f=head,*s=head;
        while(f&&f->next)
        {
            f=f->next->next;
            s=s->next;
        }
        ListNode *pre = nullptr, *cur = s, *next = cur->next;
        while(cur) {
            cur->next = pre;
            pre = cur;
            cur = next;
            if (next) {
                next = next->next;
            }
        }
        while(head&&pre)
        {
            if(head->val!=pre->val)
            {
               return false;
            }
            else
                {
                head=head->next;
                pre=pre->next;
                }
        }
        return true;
    }
};

删除倒数第k个节点 

19. 删除链表的倒数第 N 个结点icon-default.png?t=N7T8https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

        给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

思路

        当头节点和非头节点出现差异时设虚头,最后返回虚头->next。

创建虚头的语句 List Node *Head=new ListNode(0,head);

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode*HEAD=new ListNode(0,head);
        ListNode *fast=head,*slow =HEAD;
        if(!head)return head;
        for(int i=0;i<n;i++)
        {
            fast=fast->next;
        }
        while(fast)
        {
            fast=fast->next;
            slow=slow->next;
        }
       slow->next=slow->next->next;
       return HEAD->next;
    }
};

 旋转链表 

61. 旋转链表icon-default.png?t=N7T8https://leetcode.cn/problems/rotate-list/

        给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

思路

        :k可能大于链表的长度

        先求出链表的长度,然后因为k可能大于链表长度这里对k取余,再将链表头尾节点拼接,最后断链。

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head || !head->next) return head; // 处理链表为空或只有一个节点的情况
        ListNode *p = head;
        int n = 1;
        while (p->next) { // 找到链表长度n
            p = p->next;
            n++;
        }
        p->next = head; // 将链表首尾相连形成环状链表

        k = n - k % n; // 找到需要断开的位置
        for (int i = 0; i < k; i++) {
            p = p->next;
        }

        head = p->next; // 更新新的头节点
        p->next = nullptr; // 断开环状链表

        return head;
    }
};

链表中元素的和

2. 两数相加icon-default.png?t=N7T8https://leetcode.cn/problems/add-two-numbers/

        给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

        请你将两个数相加,并以相同形式返回一个表示和的链表。

        你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

思路

       本题首先要考虑两个问题:一是两个链表长度不相同,二是进位问题。 新建一个链表存储两个链表的和,利用%10和/10取出个位和十位,将对齐位数的和的个位存入链表十位进位,最后遍历新建的链表即为所求。

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        /*
        
        */
        ListNode*head=new ListNode();
        ListNode*tail=head;
        int carry=0;
        while(l1||l2||carry)
        {
            int n1=l1?l1->val:0;
            int n2=l2?l2->val:0;
            int sum=n1+n2+carry;
            carry=sum/10;//存进位的数字
            tail->next=new ListNode(sum%10);
            tail=tail->next;
            if(l1)
                l1=l1->next;
            if(l2)
                l2=l2->next;
        }
        return head->next;
    }
};

  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
A:好的,下面是K - 双向链表习题的解答。 题目描述: 设计一个数据结构,要求实现双向链表的基本操作:插入、删除、反转等,并实现一个方法向链表中添加一个元素。同时,该数据结构需要支持以下操作: 1. get(k):返回链表中第 k 个节点的值。如果 k 小于 1 或大于链表的长度,则返回 -1。 2. addAtHead(val):在链表的头部插入一个值为 val 的节点。 3. addAtTail(val):在链表的尾部插入一个值为 val 的节点。 4. addAtIndex(index,val):在链表中的第 index 个节点前插入一个值为 val 的节点。如果 index 等于链表的长度,则在链表的末尾添加该节点。如果 index 大于链表的长度,则不会插入。 5. deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。 解题思路: 题目要求我们设计一个双向链表数据结构,并提供选定的一些操作。这是一个经典的链表问题,我们需要先设计链表节点的结构体,并在节点中保留前驱和后继指针,便于双向操作。然后,我们可以定义一个链表类,在该类中定义一些方法来实现链表的基本操作。 本题需要实现的方法包括: 1. get(k):遍历链表,找到第 k 个节点并返回该节点的值。 2. addAtHead(val):创建一个新节点,将该节点作为头节点,并更新头节点的前驱指针。 3. addAtTail(val):创建一个新节点,将该节点作为尾节点,并更新尾节点的后继指针。 4. addAtIndex(index,val):遍历链表,找到第 index - 1 个节点,创建一个新节点,并将其插入到该节点的后面。如果 index 为零,则将新节点插入到头部。如果 index 等于链表的长度,则将新节点插入到末尾。 5. deleteAtIndex(index):遍历链表,找到第 index - 1 个节点,并将其后继指针指向第 index + 1 个节点。如果 index 为零,则更新头节点。如果 index 等于链表的长度 - 1,则更新尾节点。 代码实现: 下面是基于C++的实现代码,其中Node是一个链表节点的结构体,List是链表类的定义: ```cpp #include<iostream> using namespace std; // 链表节点结构体 struct Node { int val; // 节点的值 Node* pre; // 前驱指针 Node* nxt; // 后继指针 Node(int _val):val(_val),pre(nullptr),nxt(nullptr){} // 构造函数 }; // 链表类 class List{ private: Node* head; // 头节点 Node* tail; // 尾节点 int size; // 链表长度 public: List():head(nullptr),tail(nullptr),size(0){} // 构造函数 int get(int k){ if(k < 1 || k > size) // 判断k是否合法 return -1; Node* p = head; for(int i=1; i<k; i++) // 遍历链表,找到第k个节点 p = p->nxt; return p->val; // 返回节点的值 } void addAtHead(int val){ Node* p = new Node(val); // 创建新节点 if(size == 0){ // 链表为空的情况 head = p; tail = p; }else{ // 链表非空的情况 p->nxt = head; // 插入节点 head->pre = p; head = p; } size++; // 更新链表长度 } void addAtTail(int val){ Node* p = new Node(val); // 创建新节点 if(size == 0){ // 链表为空的情况 head = p; tail = p; }else{ // 链表非空的情况 tail->nxt = p; // 插入节点 p->pre = tail; tail = p; } size++; // 更新链表长度 } void addAtIndex(int index, int val){ if(index > size) // index不合法,不插入 return; if(index <= 0) // 如果index小于等于0,插入到头部 addAtHead(val); else if(index == size) // 如果index等于size,插入到尾部 addAtTail(val); else{ // 如果index在链表中 Node* p = head; for(int i=1; i<index; i++) // 找到第index-1个节点 p = p->nxt; Node* q = new Node(val); // 创建新节点 q->nxt = p->nxt; // 插入节点 p->nxt->pre = q; p->nxt = q; q->pre = p; size++; // 更新链表长度 } } void deleteAtIndex(int index){ if(index < 0 || index >= size) // index不合法,不删除 return; if(index == 0){ // 如果要删除的是头节点 head = head->nxt; // 更新头节点 if(head == nullptr) // 如果链表为空,尾节点也需要更新 tail = nullptr; else head->pre = nullptr; }else if(index == size-1){ // 如果要删除的是尾节点 tail = tail->pre; // 更新尾节点 tail->nxt = nullptr; }else{ // 如果要删除的是中间节点 Node* p = head; for(int i=1; i<index; i++) // 找到第index-1个节点 p = p->nxt; p->nxt = p->nxt->nxt; // 删除节点 p->nxt->pre = p; } size--; // 更新链表长度 } }; int main(){ List l; l.addAtHead(1); l.addAtTail(3); l.addAtIndex(1,2); // 链表变为[1,2,3] cout<<l.get(1)<<" "; // 返回2 l.deleteAtIndex(1); // 现在链表是[1,3] cout<<l.get(1)<<" "; // 返回3 return 0; } ``` 总结: 双向链表实现相对较多的语言,相对单向链表更适合一些场景;比如说LUR Cache。对于双向链表的实现,我们需要注意节点间指针的指向关系,以及头节点和尾节点的处理。感兴趣的读者可以继续尝试其他链表问题,如链表的分割、链表的反转等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

了一li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值