leetcode链表相关题目

news2024/11/24 13:03:44

文章目录

  • 1.移除链表元素
    • 方法1:
    • 方法2
  • 2.合并两个有序链表
  • 3.链表的中间节点
    • 方法1
    • 方法2
  • 4.反转单链表
    • 方法1
    • 方法2
  • 5.分割链表
  • 6.链表中的倒数第k个节点
    • 方法1:
    • 方法2:
  • 7.环形链表的约瑟夫问题
  • 8.链表的回文结构
  • 9.相交链表
    • 方法1
    • 方法2:
  • 10.环形链表
  • 11.环形链表Ⅱ
  • 12.随机链表的复制

在这里插入图片描述
链表学习完以后,来做点相关题目吧

1.移除链表元素

在这里插入图片描述

方法1:

在原链表的基础上直接删除指定元素

  • 若当前节点要删除的节点,则将其前驱节点指向当前节点的下一个节点
  • 若当前节点不是要删除的节点,前驱节点指向当前节点,当前节点后移
  • 特殊情况:
    • 循环判断,若头节点是要删除的节点,则将头节点后移
    • 头节点不为空
struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* cur = head;
    struct ListNode* prev = head;
    //判断头节点
    while(head && head->val == val)
    {
        head = head->next;
    }
    //链表为空
    if(head == NULL)
    {
        return NULL;
    }
    //正常情况
    while(cur)
    {
        if(cur->val == val)
        {
            prev->next = cur->next;
        }
        else
        {
            prev = cur;
        }
        cur =  cur->next;
    }
    return head;
}

方法2

创建一个新的链表存放未删除的元素

  • 先创建一个虚拟的头节点,指向新链表,同时记录该链表的尾
  • 若当前节点要删除的元素,直接后移
  • 若当前节点不是要删除的元素,连接到新链表的尾后
  • 将新链表尾节点的next置为空(断开与原链表的连接)
struct ListNode* removeElements(struct ListNode* head, int val) {
    //设置新链表的头
    struct ListNode* newhead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newhead->val = -1;
    newhead->next = NULL;
    struct ListNode* cur = head;
    struct ListNode* tail = newhead;
    while(cur)
    {
        if(cur->val == val)
        {
            cur = cur->next;
        }
        else
        {
            tail->next = cur;
            tail = tail->next;
            cur = cur->next;
        }
    }
    //将新链表与原链表断开
    tail->next = NULL;
    struct ListNode* ret = newhead->next;
    free(newhead);
    newhead = tail = NULL;
    return ret;
}

2.合并两个有序链表

在这里插入图片描述

创建一个新的链表

  • 两个指针分别指向两个链表
  • 将两指针所指向的元素的较小值连接到新链表的尾
  • 若有一个指针走到空,则将另一个指针连接到新链表的尾
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    //定义新链表的头
    struct ListNode* newhead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newhead->val = -1;
    newhead->next = NULL;

    struct ListNode* tail = newhead;
    //比较链表元素
    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
            tail->next = list1;
            tail = tail->next;
            list1 = list1->next;
        }
        else
        {
            tail->next = list2;
            tail = tail->next;
            list2 = list2->next;
        }
    }
    //若有一个链表为空,则连接另一个
    if(list1)
    {
        tail->next = list1;
    }
    else
    {
        tail->next = list2;
    }
    struct ListNode* ret = newhead->next;
    free(newhead);
    newhead = tail = NULL;
    return ret;
}

3.链表的中间节点

在这里插入图片描述

方法1

统计链表长度,计算出中间位置;再寻找中间位置

struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* cur = head;
    int len = 0;
    while(cur)
    {
        len++;
        cur = cur->next;
    }
    int count = 0;
    cur = head;
    while(count < (len / 2))
    {
        count++;
        cur = cur->next;
    }
    return cur;
}

方法2

快慢指针法:一个指针一次走一步,一个指针一次走两步。当快指针尾空或者快指针的next为空,那么慢指针所指向的元素就是中间元素。
在这里插入图片描述

struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

4.反转单链表

在这里插入图片描述

方法1

定义一个新的头节点,然后遍历链表,采取头插
在这里插入图片描述

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = -1;
    newHead->next = NULL;

    while(head)
    {
        //先记录后继节点
        struct ListNode* next = head->next;
        //头插
        head->next = newHead->next;
        newHead->next = head;
        //节点后移
        head = next;
    }
    return newHead->next;
}

方法2

原地直接反转

  • 先记录当前节点的后继节点,以便节点后移
  • 当前节点指向其前驱节点
  • 前驱节点后移
  • 当前节点后移
  • prev即为反转后新的头

在这里插入图片描述

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* next = NULL;
    struct ListNode* prev = NULL;
    struct ListNode* cur = head;

    while(cur)
    {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

5.分割链表

在这里插入图片描述
思路:

  • 给定两个新的链表,一个放小于X的元素,一个放大于X的元素
  • 元素尾插至新链表
  • 将两个新链表相连

在这里插入图片描述

struct ListNode* partition(struct ListNode* head, int x){
    struct ListNode* minHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* maxHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* minTail = minHead;
    struct ListNode* maxTail = maxHead;
    struct ListNode* cur = head;

    while(cur)
    {
        if(cur->val < x)
        {
            minTail->next = cur;
            minTail = minTail->next;
        }
        else
        {
            maxTail->next = cur;
            maxTail = maxTail->next;
        }
        cur = cur->next;
    }
    //防止成环
    maxTail->next = NULL;
    minTail->next = maxHead->next;
    struct ListNode* ret = minHead->next;
    free(minHead);
    free(maxHead);
    return ret;
}

6.链表中的倒数第k个节点

在这里插入图片描述

方法1:

思路:倒数第k就是正数第n-k+1个(n为链表长度)
特殊情况:

  • k应该小于链表长度
  • k不能为0
  • 链表不能为空
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k) {
    struct ListNode* cur = pListHead;
    int len = 0;
    //求链表的长度
    while (cur)
    {
        len++;
        cur = cur->next;
    }
    //特殊情况判断
    //1.k不能大于链表长度
    //2.k不能为0
    //3.链表不为空
    if (k > len || k == 0 || pListHead == NULL)
    {
        return NULL;
    }
    //倒数第k个就是正数第n-k+1个
    int n = 1;
    len = len - k + 1;
    cur = pListHead;
    while (n != len)
    {
        n++;
        cur = cur->next;
    }
    return cur;
}

方法2:

快慢指针:快指针先走k步,然后快慢一起走
在这里插入图片描述

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast = pListHead;
    struct ListNode* slow = pListHead;
    //快指针先走k步
    while(k--)
    {
        //fast不能走出链表(k符合)
        if(fast)
        {
            fast = fast->next;
        }
        else 
        {
            return NULL;
        }
    }
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

7.环形链表的约瑟夫问题

在这里插入图片描述
解题思路:

  • 构建环形链表,给每个节点编号
  • 逢m就删除节点,再从新报数

在这里插入图片描述

typedef struct ListNode  ListNode;

//创建节点
ListNode* CreatNode(int x)
{
    ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
    newnode->val = x;
    newnode->next = NULL;
    return newnode;
}

//构建环
ListNode* CreatCircle(int n)
{
    ListNode* head = CreatNode(1);
    ListNode* tail = head;
    //连续创建节点
    for(int i=2; i<=n; i++)
    {
        ListNode* newnode = CreatNode(i);
        //连接节点
        tail->next = newnode;
        tail = tail->next;
    }
    //成环
    tail->next = head;
    //返回尾节点的目的是:防止第一个元素就是要删除的元素
    return tail;
}

int ysf(int n, int m ) {
    ListNode* prev = CreatCircle(n);
    ListNode* cur = prev->next;
    int count = 1;
    //有多个节点继续报数,直到剩下一个节点
    while(cur->next != cur)
    {
        //逢m就删除
        if(count == m)
        {
            prev->next = cur->next;
            free(cur);
            cur = prev->next;
            count = 1;
        }
        else 
        {
            prev = cur;
            cur = cur->next;
            count++;
        }
    }
    return cur->val;
}

8.链表的回文结构

在这里插入图片描述
思路:从链表的中间节点逆置后半段,然后比较前半段与后半段是否相等。

  • 快慢指针找链表的中间节点
  • 从中间节点反转单链表
  • 节点比较

在这里插入图片描述
在这里插入图片描述

	//找中间节点
    ListNode* FindMid(ListNode* A)
    {
        ListNode* fast = A;
        ListNode* slow = A;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;   
    }
	//反转
    ListNode* Reverse(ListNode* mid)
    {
        ListNode* cur = mid;
        ListNode* prev = NULL;
        ListNode* next = NULL;
        while(cur)
        {
            next = cur->next;
            cur->next = prev;
            prev = cur;
            cur = next;
        }
        return prev;
    }

    bool chkPalindrome(ListNode* A) {
        ListNode* mid = FindMid(A);
        ListNode* midhead = Reverse(mid);

        while(midhead)
        {
            if(A->val == midhead->val)
            {
                A = A->next;
                midhead = midhead->next;
            }
            else 
            {
                return false;
            }
        }
        return true;
    }

9.相交链表

在这里插入图片描述

方法1

将链表A中的每一个节点与链表B中的每一个节点比较,看是否相等。

  • 若相等,则返回相等的节点
  • 若所有节点都不相等,则链表不相交。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA = headA;
    struct ListNode* curB = headB;
    while(curA)
    {
        curB = headB;
        while(curB)
        {
            //若节点相等,直接返回相等节点
            if(curA == curB)
            {
                return curA;
            }
            curB = curB->next;
        }
        curA = curA->next;
    }
    //A中每一个节点都与B比较完毕,仍无交点
    return NULL;
}

方法2:

分别计算两链表长度,长的先走长度差步,然后再一起走,判断是否相等。

  • 再求链表长度的同时,若两链表的最后一个节点相等,则二者一定相交
struct ListNode* getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA = headA;
    struct ListNode* curB = headB;
    int lenA = 0;
    int lenB = 0;
    //先计算链表各自的长度,都少计算一个
    while(curA->next)
    {
        lenA++;
        curA = curA->next;
    }
    while(curB->next)
    {
        lenB++;
        curB = curB->next;
    }
    //若不相交,直接返回空
    if(curA != curB)
    {
        return NULL;
    }
    //算长度差
    int  gap = abs(lenA - lenB);
    struct ListNode* longList = headA;
    struct ListNode* shortList = headB;
    if(lenA < lenB)
    {
        longList = headB;
        shortList = headA;
    }
    //长的先走长度差步
    while(gap--)
    {
        longList = longList->next;
    }
    //同时走,找交点
    while(longList && shortList)
    {
        if(longList == shortList)
        {
            return shortList;
        }
        longList = longList->next;
        shortList = shortList->next;
    }
    return NULL;
}

10.环形链表

在这里插入图片描述
思路:快慢指针。

  • 一个指针一次走一步,一个指针一次走两步
  • 若快指针走到了空,则不带环
  • 若快指针与慢指针相遇,则带环
bool hasCycle(struct ListNode *head) {
    struct ListNode * fast = head;
    struct ListNode * slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

为什么这样可以呢?
在这里插入图片描述

为什么不是一个走1步一个走3步?一个走1步一个走4步?一个走1步一个走6步?…

在这里插入图片描述
在这里插入图片描述

若N为奇数,C-1也为奇数则永远追不上(存在这种情况吗?)

在这里插入图片描述
所以,一步两步走是最保险也是最简单的方式,不会错过。

11.环形链表Ⅱ

在这里插入图片描述
思路:快慢指针,找到二者的相遇点。再使用两指针,从相遇点和链表头开始走,二者相遇点就是入环点

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;

    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            struct ListNode* meet = slow;
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

原理

在这里插入图片描述

12.随机链表的复制

在这里插入图片描述
在这里插入图片描述
本题的难点在于random的指向难以找到
思路:

  • 在原链表每个节点的后面尾插一个新的节点
  • 根据原链表,找到新链表的random指向
  • 将新链表的节点与原链表分离,恢复原链表

第一步:连接新节点
在这里插入图片描述

第二步:搞清random指向
新链表random就是原链表random的next!!!
在这里插入图片描述
第三步:将新链表从原链表上摘下来
在这里插入图片描述

struct Node* copyRandomList(struct Node* head) {
	struct Node* cur = head;
    //插入新节点
    while(cur)
    {
        struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
        //连接新节点
        newnode->val = cur->val;
        newnode->next = cur->next;
        cur->next = newnode;
        //原链表后移
        cur = cur->next->next;
    }
    //random
    cur = head;
    while(cur)
    {
        struct Node* newnode = cur->next;
        if(cur->random == NULL)
        {
            newnode->random = NULL;
        }
        else
        {
            newnode->random = cur->random->next;
        }
        cur = cur->next->next;
    }
    //摘新链表
    struct Node* newhead = (struct Node*)malloc(sizeof(struct Node));
    newhead->next = NULL;
    newhead->random = NULL;
    struct Node* tail = newhead;
    cur = head;
    while(cur)
    {
        //尾插新链表
        struct Node* newnode = cur->next;
        tail->next = newnode;
        tail = tail->next;
        //恢复原链表
        cur->next = newnode->next;

        cur = cur->next;
    }
    return newhead->next;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1445560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JMM(Java内存模型)

Java内存模型&#xff08;Java Memory Model&#xff0c;简称JMM&#xff09;是Java语言规范中定义的一个抽象概念&#xff0c;它描述了程序中各个变量&#xff08;包括实例字段、静态字段和构成数组对象的元素&#xff09;在并发环境下的访问规则和一致性保证。JMM的主要目标是…

2024年低代码平台概览:国内主流平台一览

低代码开发是一项革命性的技术&#xff0c;主要目的是尽量避免程序研发的复杂性&#xff0c;让外行开发者也能加入到应用程序的搭建中。低代码平台的核心概念和构成部分通常包括用户界面和拖拽设计、预构件和模块、自动化工作内容与数据库集成和扩展应用&#xff0c;应用低代码…

73. 矩阵置零(Java)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 输入&#xff1a; matrix [[1,1,1],[1,0,…

C语言学习记录

牛牛的第二个整数_牛客题霸_牛客网 (nowcoder.com) 注意输入的格式&#xff0c;%d空格%d空格%d

《统计学简易速速上手小册》第7章:时间序列分析(2024 最新版)

文章目录 7.1 时间序列数据的特点7.1.1 基础知识7.1.2 主要案例&#xff1a;股票市场分析7.1.3 拓展案例 1&#xff1a;电商销售预测7.1.4 拓展案例 2&#xff1a;能源消耗趋势分析 7.2 时间序列模型7.2.1 基础知识7.2.2 主要案例&#xff1a;股价预测7.2.3 拓展案例 1&#xf…

第72讲后台管理Container布局实现

新建layout目录 登录成功后&#xff0c;跳转layout布局容器页面 login页面&#xff1a; 导入router import router from "/router";登录成功&#xff0c;跳转后台管理页面 选用布局容器&#xff1a; <template><div class"common-layout">…

【Linux】基础命令 第二篇

目录 echo 输出重定向:(本质都是写入) 输入重定向cat more 指令 && less指令 head && tail && 管道初步使用 grep&#xff1a;行文本过滤工具&#xff08;文本按行搜索&#xff09; date&#xff1a;获取时间 date 命令用于 显示 或 设置系统的…

HCIA-HarmonyOS设备开发认证V2.0-3.2.轻量系统内核基础-中断管理

目录 一、中断基础概念二、中断管理使用说明三、中断管理模块接口四、代码分析&#xff08;待续...&#xff09; 一、中断基础概念 在程序运行过程中&#xff0c;出现需要由 CPU 立即处理的事务时&#xff0c;CPU 暂时中止当前程序的执行转而处理这个事务&#xff0c;这个过程…

【数学建模】【2024年】【第40届】【MCM/ICM】【F题 减少非法野生动物贸易】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 ICM Problem F: Reducing Illegal Wildlife Trade Illegal wildlife trade negatively impacts our environment and threatens global biodiversity. It is estimated to involve up to 26.5 billion US dollars per y…

C#,卢卡斯数(Lucas Number)的算法与源代码

1 卢卡斯数&#xff08;Lucas Number&#xff09; 卢卡斯数&#xff08;Lucas Number&#xff09;是一个以数学家爱德华卢卡斯&#xff08;Edward Lucas&#xff09;命名的整数序列。爱德华卢卡斯既研究了这个数列&#xff0c;也研究了有密切关系的斐波那契数&#xff08;两个…

一些AI工具的初探和使用

0. 前言 目前我自己对于AI的应用还不成熟&#xff0c;先记录一下常用的AI工具&#xff0c;后续再进行探索。 目前AI发展的速度已经超出想象了。可能最开始我对ai的应用 还停留在回答问题以及自己领域的可以生成cursor,还有阿里家通义灵码。都还是程序员的范畴。 然后对于文字…

黑马程序员——html css基础——day10day11day12——小兔鲜儿

目录&#xff1a; 底部盒子制作 底部服务模块制作底部帮助模块底部版权模块banner-轮播图 HTML结构CSS样式banner图片模块 htmlcssbanner小圆点制作 htmlcssbanner-侧导航 HTML结构CSS样式新鲜好物-hd HTML结构CSS样式新鲜好物-bd HTML结构CSS样式溢出文字显示省略号人气推荐 …

专业课145+总分410+华南理工大学811信号与系统考研经验华工电子信息与通信,真题,大纲,参考书。

大家好&#xff0c;今年考研顺利上岸华南理工大学&#xff0c;专业课811信号与系统145&#xff08;只差一点满分&#xff0c;有点遗憾&#xff0c;专业我跟着Jenny老师复习投入时间和精力和数学差不多&#xff0c;华工专业课难度中等&#xff0c;是一个总分提高很好的突破口&am…

备战蓝桥杯---数学基础2

学了常见的筛法&#xff0c;让我们看个题&#xff1a; 首先&#xff0c;我们知道欧拉筛复杂度为nlognlogn,这题可以承受&#xff0c;但是空间上存不了&#xff0c;而如果我们枚举1--n^1/2&#xff0c;复杂度不允许。 其实在枚举的方法中&#xff0c;我们只需找出有无在【2&…

集合进阶(双列集合、HashMap、LinkedHashMap、TreeMap、Collections)

目录 一、双列集合 1、双列集合的特点 2、双列集合的常见API 3、Map的遍历方式 3.1第一种遍历方式&#xff1a;键找值&#xff08;keySet&#xff09; 3.2第二种遍历方式&#xff1a;键值对&#xff08;entrySet&#xff09;Entry&#xff1a;键值对对象 3.3第三种遍历方…

数据结构——6.3 图的遍历

6.3 图的遍历 一、概念 图的广度优先遍历 树的广度优先遍历&#xff08;层序遍历&#xff09;&#xff1a;不存在“回路”&#xff0c;搜索相邻的结点时&#xff0c;不可能搜到已经访问过的结点&#xff1a; 若树非空&#xff0c;则根节点入队 若队列非空&#xff0c;队头元素…

如何写出别人写不出的内容(译)

&#xff08;译者序&#xff1a;这篇文章不只是写作&#xff0c;对信息获取、阅读也都有启发。随着社交媒体和 AI 的发展&#xff0c;人们越来越被动的接收海量信息&#xff0c;如何主动查找与整理对自己有用的内容&#xff0c;将是一个不可或缺的能力。&#xff09; 原文&…

雨云裸金属服务器

雨云服务器与裸金属服务器&#xff1a;云端与实体的完美交融 随着信息技术的迅猛发展&#xff0c;云服务已经成为企业和个人数据处理与存储的重要选择。其中&#xff0c;雨云服务器和裸金属服务器作为两种截然不同的服务形式&#xff0c;各自拥有独特的优势和应用场景。本文将深…

图灵日记--MapSet字符串常量池反射枚举Lambda表达式泛型

目录 搜索树概念实现性能分析和 java 类集的关系 搜索概念及场景模型 Map的使用Map常用方法 Set的说明常见方法说明 哈希表冲突-避免-负载因子调节冲突-解决-闭散列冲突-解决-开散列/哈希桶冲突严重时的解决办法 实现和 java 类集的关系 字符串常量池String对象创建intern方法 …

IT行业含金量高的证书-软考

软考全称计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;软考既是职业资格考试&#xff0c;又是职称资格考试。2021年12月2号发布新版的国家职业资格目录&#xff0c;软考是在计算机技术领域中的唯一的国家职业资格。 一、好处 软考是一个神奇又特…