leetcode每日一练:链表OJ题

news2024/12/24 0:39:28

链表经典算法OJ题

1.1 移除链表元素

题目要求:

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

解题思路:

思路1:

定义两个指针,一个指向当前节点,一个指向当前节点的下一个节点,如果下一个节点是要删除的节点就将当前节点的next存储下一个节点的再下一个节点的地址,然后free那个节点。知道遍历完原链表,该思路是改变原链表。

思路2:

建立一个新的链表,遍历原链表,将原链表中的非删除节点给新的链表,遍历结束后新链表中没有要删除节点。

struct ListNode{
    int val;
    struct ListNode *next;
};
struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* Newhead, * NewTail;//一个头链表,一个链表尾部
    Newhead = NewTail = NULL;
    //遍历原链表
    struct ListNode* pcur = head;
    while (pcur){
        if (pcur->val != val){
            if (Newhead == NULL){
                Newhead = NewTail = pcur;
            }
            else{
                NewTail->next = pcur;
                NewTail = NewTail->next;
            }
        }
        pcur = pcur->next;
    }
    //如果要删除值在原链表为节点,新链表的尾节点就不会指向NULL,所以这里要判断
    if (NewTail){
        NewTail->next = NULL;
    }
    return Newhead;
}

1.2 反转链表

题目要求:

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

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解题思路:

可以定义三个新的节点类型的指针变量n1、n2、n3。然后就可以使用这三个指针来反转链表。首先n1初始化为NULL,n2初始化为head链表头结点,n3则是head头结点的下一个节点。然后循环n2->next = n1, n1 = n2, n2 = n3,n3 = n3->next。循环往复就完成了反转链表的操作。

typedef struct ListNode{
    int val;
    struct ListNode *next;
}*pList;

//反转链表函数实现
pList reverselist(pList head){
    if (head == NULL){
        return NULL;
    }
    pList n1, n2, n3;
    n1 = NULL, n2 = head, n3 = head->next;
    while (n2){
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)//如果n3为NULL就不再向后访问了
           n3 = n3->next;
    }
    return n1;
}

//以下调用上面函数的main是我自己写的,可以供大家参考
int main()
{
    //创建链表
    struct ListNode* phead, *pTail, *p;
    phead = pTail = NULL;
    int n = 0;
    printf("请输入要创建链表节点个数:>");
    scanf("%d", &n);
    int i = 0;
    for (i = 0; i < n; i++)
    {
        p = (pList)malloc(sizeof(struct ListNode));
        printf("请输入第%d个节点的值:>", i + 1);
        scanf("%d", &(p->val));
        p->next = NULL;
        if (phead == NULL)
        {
            phead = p;
            pTail = phead;
        }
        else
        {
            pTail->next = p;
            pTail = pTail->next;
        }
    }
    //调用反转链表函数
    pList Newhead = reverselist(phead);
    if (Newhead == NULL)
    {
        perror("reverselist");
        return;
    }
    phead = Newhead;
    //打印链表节点数据
    while (Newhead)
    {
        printf("%d->", Newhead->val);
        Newhead = Newhead->next;
    }
    printf("NULL\n");
    i = 1;
    Newhead = phead;
    //销毁链表
    while (Newhead)
    {
        phead = phead->next;
        free(Newhead);
        Newhead = phead;
        printf("已释放第%d条节点\n", i);
        i++;
    }
    return 0;
}

1.3 合并两个有序链表

题目要求:

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

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

解题思路:

定义两个新的节点指针,一个头节点,一个尾结点。比较两个原链表当前节点的数据大小,然后插入新的链表,知道插完为止

typedef struct ListNode {
    int val;
    struct ListNode* next;
}*pList;
//合并两个有序链表函数实现
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    if (list1 == NULL){
        return list2;
    }
    if (list2 == NULL){
        return list1;
    }
    struct ListNode* cur1, *cur2;
    cur1 = list1, cur2 = list2;
    struct ListNode* phead, * pTail;
    phead = pTail = (struct ListNode*)malloc(sizeof(struct ListNode));
    while (cur1 && cur2) {
        if (cur1->val < cur2->val){
            pTail->next = cur1;
            pTail = pTail->next;
            cur1 = cur1->next;
        }
        else{
            pTail->next = cur2;
            pTail = pTail->next;
            cur2 = cur2->next;
        }
    }
    if (cur1) {
        pTail->next = cur1;
    }
    if (cur2) {
        pTail->next = cur2;
    }
    struct ListNode* retList = phead->next;
    free(phead);
    return retList;
}
//以下创建链表调用函数的代码是自己写的,只为调用上面函数,不是链表规范创建。仅供参考
int main()
{
    struct ListNode* phead1, pTail1, p1;
    struct ListNode* phead2, pTail2, p2;
    phead1 = pTail1 = NULL;
    phead2 = pTail2 = NULL;
    int n = 0;
    printf("请输入要创建p1和p2链表节点个数:>");
    scanf("%d", &n);
    int i = 0;
    for (i = 0; i < n; i++)
    {
        p1 = (pList)malloc(sizeof(struct ListNode));
        p2 = (pList)malloc(sizeof(struct ListNode));
        printf("请输入p1链表第%d个节点的值:>", i + 1);
        scanf("%d", &(p1->val));
        printf("请输入p2链表第%d个节点的值:>", i + 1);
        scanf("%d", &(p2->val));
        p1->next = NULL;
        p2->next = NULL;
        if (phead1 == NULL && phead2 == NULL)
        {
            phead1 = p1;
            pTail1 = phead1;
            phead2 = p2;
            pTail2 = phead2;
        }
        else
        {
            pTail1->next = p1;
            pTail1 = pTail1->next;
            pTail2->next = p2;
            pTail2 = pTail2->next;
        }
    }
    pList Newhead = mergeTwoLists(phead1,phead2);
    if (Newhead == NULL)
    {
        perror("reverselist");
        return;
    }
    pList phead = Newhead;
    while (Newhead)
    {
        printf("%d->", Newhead->val);
        Newhead = Newhead->next;
    }
    printf("NULL\n");
    i = 1;
    Newhead = phead;
    while (Newhead)
    {
        phead = phead->next;
        free(Newhead);
        Newhead = phead;
        printf("已释放第%d条节点\n", i);
        i++;
    }
    return 0;
}

1.4 链表的中间节点

题目要求:

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

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

示例 1:

输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。

示例 2:

输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

解题思路:

快慢指针:使用快慢指针来遍历链表,慢指针每次走一步,快指针每次走两步,当快指针走到最后慢指针刚好走到中间节点。因为快指针是慢指针的2倍。所以快指针从起点到终点时慢指针就是这个路程的一半,是中点。

typedef struct ListNode{
    int val;
    struct ListNode* next;
}*pList;
//函数实现
struct ListNode* middleNode(struct ListNode* head) {
    if (head == NULL){
        return NULL;
    }
    struct ListNode* slow, *fast;
    slow = fast = head;
    //节点数可能是奇数个也可能是偶数个,所以要两种判断
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

1.5 环形链表的约瑟夫问题

著名的Josephus问题

据说著名的历史学家 Josephus有过以下的故事:故事据说发生在古罗马时期,在罗马人占领乔塔帕特后,39个犹太人与约瑟夫及他的朋友躲到一个洞中,他们宁愿死也不要被敌人抓到,于是约定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下1个人重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

题目要求:

编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。

下一个人继续从 1 开始报数。

n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?

数据范围: 1≤𝑛,𝑚≤100001≤n,m≤10000

进阶:空间复杂度 𝑂(1)O(1),时间复杂度 𝑂(𝑛)O(n)

示例1

输入:

5,2     

复制返回值:

3    

复制说明:

开始5个人 1,2,3,4,5 ,从1开始报数,1->1,2->2编号为2的人离开
1,3,4,5,从3开始报数,3->1,4->2编号为4的人离开
1,3,5,从5开始报数,5->1,1->2编号为1的人离开
3,5,从3开始报数,3->1,5->2编号为5的人离开
最后留下人的编号是3      

示例2

输入:

1,1

返回值:

1
typedef struct ListNode ListNode;

创建一个节点并返回
ListNode* ListBuyNode(int val)
{
    ListNode* ret = (ListNode*)malloc(sizeof(ListNode));
    if(ret==NULL)
    {
        perror("malloc");
        return NULL;
    }
    ret->val = val;
    ret->next = NULL;
    return ret;
}
//创建一个环形链表并返回
ListNode* CreatNode(int n)
{
    ListNode* phead = ListBuyNode(1);
    ListNode* pTail = phead;
    int i = 0;
    for(i=2;i<=n;i++)
    {
        pTail->next = ListBuyNode(i);
        pTail = pTail->next;
    }
    pTail->next = phead;//将链表循环
    return pTail;
}

int ysf(int n, int m ) {
    // write code here
    ListNode* prev = CreatNode(n);
    ListNode* cur = prev->next;
    int count = 1;
    //遍历判断
    while(cur->next!=cur){
        if(count == m){
            prev->next = cur->next;
            free(cur);
            cur = prev->next;
            count = 1;
        }
        else{
            prev = cur;
            cur = cur->next;
            count++;
        }
    }
    return cur->val;
}

1.6 分隔链表

题目要求:

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

解题思路:

大小链表:我们可以创建两个新的链表,为大小链表。遍历原链表。大于等于x的节点连接在大链表,小于x的节点连接在小链表。最后将大链表的头结点连接在小链表的尾结点。返回小链表的头结点

typedef struct ListNode {
    int val;
    struct ListNode* next;
}*pList;

//分隔链表的函数实现
struct ListNode* partition(struct ListNode* head, int x) {
    if (head == NULL){
        return NULL;
    }
    struct ListNode* lessHead, * lessTail;
    struct ListNode* greaterHead, * greaterTail;
    //定义哨兵位
    lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
    greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* pcur = head;//用来遍历原链表
    //分隔操作
    while (pcur){
        if (pcur->val >= x){
            greaterTail->next = pcur;
            greaterTail = greaterTail->next;
        }
        else{
            lessTail->next = pcur;
            lessTail = lessTail->next;
        }
        pcur = pcur->next;
    }
    //将大小链表连接起来
    lessTail->next = greaterHead->next;
    if(greaterTail)
       greaterTail->next = NULL;
    //释放哨兵位
    free(greaterHead);
    struct ListNode* retList = lessHead->next;
    free(lessHead);
    //返回头结点
    return retList;
}
int main()
{
    pList phead, pTail, p;
    phead = pTail = NULL;
    int n = 0;
    printf("请输入要创建链表节点个数:>");
    scanf("%d", &n);
    int i = 0;
    for (i = 0; i < n; i++)
    {
        p = (pList)malloc(sizeof(struct ListNode));
        printf("请输入第%d个节点的值:>", i + 1);
        scanf("%d", &(p->val));
        p->next = NULL;
        if (phead == NULL)
        {
            phead = p;
            pTail = phead;
        }
        else
        {
            pTail->next = p;
            pTail = pTail->next;
        }
    }
    pList Newhead = partition(phead, 3);
    if (Newhead == NULL)
    {
        perror("reverselist");
        return;
    }
    phead = Newhead;
    while (Newhead)
    {
        printf("%d->", Newhead->val);
        Newhead = Newhead->next;
    }
    printf("NULL\n");
    i = 1;
    Newhead = phead;
    while (Newhead)
    {
        phead = phead->next;
        free(Newhead);
        Newhead = phead;
        printf("已释放第%d条节点\n", i);
        i++;
    }
    return 0;
}

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

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

相关文章

绝区零 Mac 下载安装详细教程(MacOS IPA 砸壳包 playCover 完美运行)

绝区零 7.4 号开始公测&#xff0c;但刚刚就可以开始下载了&#xff0c;我也是第一时间就迫不及待的安装到了我的 Mac 电脑上&#xff0c;感兴趣的朋友可以跟我一起安装试试 我这里是通过 playCover 的形式在 Mac 上安装运行的&#xff0c;根据之前原神的经验所以这次还是同样…

Linux——shell原理和文件权限

1.shell原理 在我们使用云服务器时&#xff0c;需要通过shell进行使用&#xff0c;而shell则是一种外壳程序。 我们提到过&#xff0c;大部分的指令实际上就是文件&#xff0c;当用户需要执行某种功能时&#xff0c;由于用户不擅长和操作系统直接交互&#xff08;操作复杂&…

事务的影子拷贝-系统架构师(二十)

1、&#xff08;重点&#xff09;企业信息集成按照组织范围分为企业内部的信息集成和外部信息集成。在企业内部信息集成中&#xff0c;&#xff08;&#xff09;实现了不同系统之间的互操作&#xff0c;使的不同系统之间能够实现数据和方法的共享。&#xff08;&#xff09;实现…

2024 年江西省研究生数学建模竞赛A题:交通信号灯管理问题分析、数学模型及VISSIM仿真实现

2024 年江西省研究生数学建模竞赛题目交通信号灯管理 1 题目 交通信号灯是指挥车辆通行的重要标志&#xff0c;由红灯、绿灯、 黄灯组成。红灯停、绿灯行&#xff0c;而黄灯则起到警示作用。交通 信号灯分为机动车信号灯、非机动车信号灯、人行横道信号 灯、方向指示灯等。 一…

Sentinel限流算法总结

文章目录 一、线程隔离二、滑动窗口算法三、令牌桶算法四、漏桶算法 一、线程隔离 线程隔离有两种方式实现&#xff1a; 线程池隔离&#xff1a;给每个服务调用业务分配一个线程池&#xff0c;利用线程池本身实现隔离效果信号量隔离&#xff1a;不创建线程池&#xff0c;而是…

用AI帮助量子计算?行业巨头IBM又在整什么新花活

内容来源&#xff1a;IBM——Quantum System Two模块化量子计算平台 文丨沛贤/浪味仙 排版丨沛贤 深度好文&#xff1a;1200字丨6分钟阅读 摘要&#xff1a;IBM正在利用其Watsonx平台和Granite AI模型,将人工智能技术与量子计算进行结合&#xff0c;以增强量子计算能力并加速…

【读点论文】Gray level thresholding in badly illuminated image,光照不均匀的二值化

Gray level thresholding in badly illuminated image Abstract 大多数灰度阈值化方法在原始栅格图像中光照梯度规律且不大的情况下能够产生非常好的结果。在其他情况下&#xff0c;比如光照变化幅度较大时&#xff0c;就无法生成令人满意的二值图像。一种方法是首先定位目标像…

Linux系统之部署RSS阅读器yarr

Linux系统之部署RSS阅读器yarr 一、yarr介绍1.1 yarr简介1.2 yarr使用场景 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、部署Node.js 环境4.1 下载Node.js安装包4.2 解压Node.js安装包4.3 创建软链接…

FreeRTOS的任务理论

文章目录 2 FreeRTOS的任务理论2.1 任务及任务优先级2.2 任务状态理论2.2.1 任务状态的转换2.2.2 任务状态改变相关函数2.2.3 调度器相关函数 2.3 FreeRTOS延时2.3.1 vTaskDelay延时2.3.2 vTaskDelayUntil延时2.3.3 pdMS_TO_TICKS&#xff08;x&#xff09;宏 2.4 TCB任务控制块…

安卓微商大师V3.4.0/高级版一键群发僵尸粉检测

一款高效获取客源&#xff0c;备受好评的微商工具&#xff0c;资源丰富&#xff0c;秒速获得客源&#xff0c;大量群客源&#xff0c;都是散客&#xff0c;携手创业&#xff0c;是做微商生意的首选工具。打开即是黑钻高级会员 赶快体验吧 很强大 链接&#xff1a;https://pan.…

【实践总结】Python使用Pandas 读取Excel文件,将其中的值转换为字符串的方法

假设你的Excel的列有一行是这个样子的; 如果直接解析就会按照float字段处理&#xff0c;所以现在需要将他们按照字符串去读取出来。正确的做法如下说生意 import pandas as pddf pd.read_excel(ExcelPath, sheet_nameSHEET,dtype{Version: str})在这里我们使用的方法就是dtyp…

专门看电视的app有什么?9款免费看片追剧神器,尽享电视乐趣!(亲测有效)

今天又来跟大家聊聊那些让人眼前一亮的追剧app和电视直播软件&#xff0c;宅家必备呀&#xff01; 咱们这回不聊那些老掉牙的&#xff0c;来点新鲜的&#xff0c;让咱们的看剧电视屏幕也能跟上潮流&#xff0c;享受一下科技带来的便利和乐趣。 首先&#xff0c;得提一提央视频…

配电智能网关赋能电力系统智能化运行维护

随着智能电网和物联网技术的不断发展&#xff0c;两者之间的融合应用成为电力行业的重要趋势。配电智能网关作为连接两者的关键设备&#xff0c;在智能电网的物联网应用中发挥着重要作用。 配电智能网关能够实现对电力系统的实时监控、数据采集、远程控制等功能&#xff0c;为…

注意!!2024《信息系统监理师》易混淆知识点来了,赶紧收藏

宝子们&#xff0c;在复习软考信息系统监理师中&#xff0c;是不是觉得有很多知识点含义比较相近&#xff0c;很多友友刚看的时候估计会像我一样迷迷糊糊的&#xff0c;作为一个软考老鸟&#xff0c;在这里给大家整理了信息系统监理师学习过程中易混淆的知识点&#xff0c;大家…

山东益康,聚焦绿葆医院场景媒体,用爱服务人类健康

山东益康集团创建于1983年&#xff0c;发展成为集药品研发生产、销售、特医功能食品、精细化工、医疗防护产品等多产业经营为一体的省级企业集团。益康集团紧跟国家发展战略&#xff0c;满足民众日益增长的健康需求&#xff0c;将食品生产向特医保健功能食品转型升级&#xff0…

Windows PowerShell 添加新配置文件(打开对应的目录,并执行命令)

%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe ./redis-server.exe %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe yarn dev 人工智能学习网站 https://chat.xutongbao.top

【C语言】刷题笔记 Day1

多刷题 多思考 【题目1】 实现字母的大小写转换&#xff0c;实现多组输入输出 1. getchar 为输入函数&#xff0c;EOF&#xff08;end of file&#xff09;为文件结束标志&#xff0c;通常为文件结束的末尾。 2. 题目中要求实现多组输入输出&#xff0c;那我们用 while 循…

2024 年江西省研究生数学建模竞赛题目 B题投标中的竞争策略问题---完整文章分享(仅供学习)

问题&#xff1a; 招投标问题是企业运营过程中必须面对的基本问题之一。现有的招投标平台有国家级的&#xff0c;也有地方性的。在招投标过程中&#xff0c;企业需要全面了解招标公告中的相关信息&#xff0c;在遵守招投标各种规范和制度的基础上&#xff0c;选择有效的竞争策…

武汉凯迪正大—冲击电流成套试验设备 雷电冲击电流发生器 成套雷电冲击装置

适用范围 本发生器适用于36kV及以下避雷器大电流试验、避雷器雷电流残压试验。 技术指标 1. 系统技术参数 1.标称电压&#xff1a;300kV 2.额定级电压&#xff1a;150kV 3.额定能量&#xff1a;45kJ 4.冲击电容量&#xff1a;75k/2uF*8台 5.总级数&#xff1a;2级 6.输…

指哪打哪,重绘神器!我已出手…

最近AI界又爆出了一个大新闻&#xff0c;阿里巴巴、香港大学和蚂蚁集团的研究人员联合推出了一款超厉害的AI工具——MimicBrush&#xff0c;它的问世&#xff0c;无疑给图像编辑领域带来了一场革命&#xff0c;它就像魔法师手中的魔杖&#xff0c;轻轻一挥&#xff0c;就能让图…