【基础算法】单链表的OJ练习(4) # 分割链表 # 回文链表 #

news2024/11/15 13:38:04

文章目录

  • 前言
  • 分割链表
  • 回文链表
  • 写在最后

前言

  • 本章的OJ练习相对前面的难度加大了,但是换汤不换药,还是围绕单链表的性质来出题的。我相信,能够过了前面的OJ练习,本章的OJ也是轻轻松松。

  • 对于OJ练习(3)-> 传送门 <- ,着重需要理解的是相交链表那道题的双指针思路,明白为什么可以这样,这样为什么可行。后面遇到类似的题目我还会做么?

  • 我们每做一道题目,都要深挖他的题目结构,明白为什么可以这样做。我相信如果你这样去做了,并且不断地练习,到后面,每遇到一个题目,你都会有所印象,并能够很快的指出解这道题的思路。

分割链表

  • 题目链接:-> 传送门 <-

  • 题目描述:现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

  • 这里的不能改变原来的数据顺序,也就是比x小的节点放在其余节点之前时,这些节点的顺序应该与没有放之前是一样的。

  • 例如: 2 4 8 5 9 3 5 ,给定 x = 8,通过本题目的描述最终的答案应该为:2 4 5 3 5 8 9

图解描述:(给定x = 3
在这里插入图片描述

解题思路:

  • 根据题目的意思以及不能改变原来的数据顺序这一特性,我们可以以一种反归并的思想来想这道题。

  • 我们遍历原链表并创建两个新链表(这里的新链表表示的是在原链表基础上通过某一条件新连接的一串节点),第一个链表用来尾插小于x的节点,第二个链表用来尾插大于等于x的节点,最后再将第一个链表的尾与第二个链表的头相连接,再返回连接后的整个链表的头,便ok啦。整个解题思路大致是这样,后面,来谈谈其中的细节。

  • 对于创建的两个新链表,都需要哨兵位的头节点来维护,什么是哨兵位的头节点呢?我们可以把哨兵位的头节点当作真实头节点的前驱,它是一个不在答案范围内的节点,但它却可以有效的控制真实的链表。其里面的值可以是随机的,它的next初始指向NULL,直到有节点尾插,它的next就指向这个节点,而这个节点就是新连接的链表的真实的头。当最后连接完成时,我们想要返回这个链表,应该返回哨兵位的next,因为哨兵位的next才是有效的真实的头节点。

  • 如果说不用哨兵位的头节点来维护,也是可以的。不过这样会有很多不必要的处理。比如给的原链表是NULL,或者两个新链表连接完后其中有一个为空,这些都有可能造成解题出问题,因此需要额外的处理。所以,为了减少这些麻烦,用哨兵位的头节点来维护这两个新链表,上述的这些情况都能够有效避之(上手写代码就有深刻的体会)。

  • 有了上面的铺垫我们只需要按照题目要求依次在原链表取节点,然后在对应哨兵位节点指向的链表中尾插,最后将这两个链表连接即可。

在这里插入图片描述

下面是代码实现:

这里没学过c++也不用担心,代码纯都是C语言写的。

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
    	// 创建两个哨兵位的头节点
    	// nhead作为连接比x小的节点
    	// rhead作为连接大于等于x的节点
        struct ListNode* nhead = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* rhead = (struct ListNode*)malloc(sizeof(struct ListNode));
		
		// 操控连接的指针
        nhead->next = NULL, rhead->next = NULL;
        struct ListNode* cur = pHead, * cur1 = nhead, * cur2 = rhead;
		
		// 遍历原链表
        while (cur)
        {
        	// 如果小于x就尾插到nhead
            if (cur->val < x) 
            {
                cur1->next = cur;
                cur1 = cur1->next;
            }
            else   // 如果 >= x 就尾插到rhead
            {
                cur2->next = cur;
                cur2 = cur2->next;
            }
            cur = cur->next;
        }
		
		// 两个带哨兵位头节点的链表的连接
		// 第一个的尾连接第二个的头
        cur1->next = rhead->next;
        // 将第二个的尾置为NULL
        cur2->next = NULL;
        // 保存连接好的整个链表的头节点(哨兵位头节点的下一个)
        struct ListNode* newhead = nhead->next;
		
		// 分别释放创建的两个哨兵位头节点
        free(nhead);
        free(rhead);
		
		// 返回新链表的头
        return newhead;
    }
};

回文链表

  • 题目链接:-> 传送门 <-

  • 题目描述:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

  • 回文结构,相信大家已不陌生,类似于123211233216789876这样的数字,都可称之为回文数。而回文链表也是一样,它每个节点的值组成的数就是一个回文数,例如下面这些链表:

在这里插入图片描述

在这里插入图片描述

  • 那么我们该如何判断一个链表是否为回文链表呢?

解题思路一:

  • 我们可以在不破坏原链表的情况下另外创建一个链表,通过遍历原链表依次将原链表上的节点复制尾插到新链表。

  • 然后两个链表从头开始比较,如果有一个节点的值不相等就返回false。两个链表都遍历结束并且最后遍历的指针都同时指向NULL,此时返回true

下面是代码实现:

bool isPalindrome(struct ListNode* head){
    struct ListNode* rhead = NULL;
    struct ListNode* cur = head;

    // 尾插到新链表
    while (cur)
    {
        if (rhead == NULL)
        {
            rhead = (struct ListNode*)malloc(sizeof(struct ListNode));
            rhead->val = cur->val;
            rhead->next = NULL;
        }
        else 
        {
            struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
            newnode->val = cur->val;
            newnode->next = rhead;
            rhead = newnode;
        }
        cur = cur->next;
    }

    struct ListNode* cur1 = head, * cur2 = rhead;
    while (cur1 && cur2)
    {
        // 有不同直接返回false
        if (cur1->val != cur2->val) return false;
        
        cur1 = cur1->next;
        cur2 = cur2->next;
    }

    // 如果结束且同时指向NULL,说明回文
    if (cur1 == NULL && cur2 == NULL) return true;
    else return false;
}

该思路可以说是暴力解法,效率相对较低,并且开了额外的空间,如果限制不能开额外的空间呢?

解题思路二:

  • 上一个思路创建了额外的空间,而本思路将不创建新的空间。

  • 首先,找到链表的中间节点,然后从这个中间节点开始,将后面的节点逆转,最后从链表的头节点和逆转后返回的节点分别开始遍历判断。

  • 如下图解析:

在这里插入图片描述

在这里插入图片描述

  • 可以看到,整个判断过程是一个循环,如果rhead指向空就结束循环,说明是回文链表;如果在循环期间,发现有不一样的值的节点,就直接返回false

  • 对于 -> 找中间节点 <--> 反转链表 <- ,在前面已经讲解过了,在这里,我们直接 ctrl + c , ctrl + v 就好。

下面是代码实现:

// 寻找中间节点函数
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* fast = head, * slow = head;

    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }

    return slow;
}

// 反转链表函数
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur = head;
    struct ListNode* prev =  NULL;
    
    while (cur)
    {
        struct ListNode* tmp = cur->next;
        cur->next = prev;
        prev = cur;
        cur = tmp;
    }

    return prev;
}

bool isPalindrome(struct ListNode* head){
    // 找中间节点
    struct ListNode* mid = middleNode(head);
    // 从中间节点开始反转链表
    struct ListNode* rlist = reverseList(mid);
	
	// 判断
    while (rlist)
    {
        if (head->val != rlist->val) return false;

        rlist = rlist->next;
        head = head->next;
    }       

    return true;
}

写在最后

对于单链表的题目练习,最重要的是思路,我们在数据结构阶段要养成画图的习惯,因为它能帮助我们更好的理解。后续还会有单链表相关的题目练习。

感谢阅读本小白的博客,错误的地方请严厉指出噢!

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

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

相关文章

华为OD机试题,用 Java 解【单词反转】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

在 Docker 安装 Oracle12

说明 单独在Linux上安装Oracle很繁琐&#xff0c;首先需要安装各种依赖&#xff0c;其次安装如果失败了&#xff0c;重新配置安装也挺麻烦&#xff0c;所以如果是开发或者测试的时候使用Docker来进行安装会非常的方便。 搜索了很多的oracle相关镜像&#xff0c;选择一个适合自…

【Linux】-- 基本指令

目录 用户管理 adduser passwd userdel pwd ls指令 -l -a -d -F -r -t -R -1 which alias ll ls -n cd cd - cd ~ touch -d stat mkdir -p rmdir rm -r -f man cp ​编辑 -r -f mv cat -n tac more less -N head tail | 管道 dat…

Cookies与Session

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

JavaScript基础六、函数

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

Deploy Workshop|DIY部署环境,让OceanBase跑起来

2023 年 3 月 25 日&#xff0c;我们将在北京开启首次 OceanBase 开发者大会&#xff0c;与开发者共同探讨单机分布式、云原生、HTAP 等数据库前沿趋势&#xff0c;分享全新的产品 Roadmap&#xff0c;交流场景探索和最佳实践&#xff0c;此外&#xff0c;OceanBase 开源技术全…

Qt多线程文件查找器

⭐️我叫恒心&#xff0c;一名喜欢书写博客的研究生在读生。 原创不易~转载麻烦注明出处&#xff0c;并告知作者&#xff0c;谢谢&#xff01;&#xff01;&#xff01; 这是一篇近期会不断更新的博客欧~~~ 有什么问题的小伙伴 欢迎留言提问欧。 Qt多线程文件查找器 前言 最近…

JUC并发编程——wait-notify

目录一、wait / notify1.1 wait / notify 原理1.2 wait / notify API介绍二、wait VS sleep三、wait / notify —代码改进一、wait / notify 1.1 wait / notify 原理 ● Owner线程发现条件不满足&#xff0c;调用wait( )方法即可进入WaitSet变为 WAITING状态 ● BLOCKED 和 W…

AI的简单介绍

什么是AI&#xff1f; AI 是 Artificial Intelligent 的缩写&#xff0c;是我们通常意义上说的人工智能。 简单来说就是让机器能够模拟人类的思维能力&#xff0c;让它能够像人一样感知、思考甚至决策。 为什么要开发AI&#xff1f; 因为在过去&#xff0c;都是我们学习机器…

408 计算机基础复试笔记 —— 更新中

计算机组成原理 计算机系统概述 问题一、冯诺依曼机基本思想 存储程序&#xff1a;程序和数据都存储在同一个内存中&#xff0c;计算机可以根据指令集执行存储在内存中的程序。这使得程序具有高度灵活性和可重用性。指令流水线&#xff1a;将指令分成若干阶段&#xff0c;每…

opencv学习(一)图像的基本操作

数据的读取cv2.IMREAD_COLOR:彩色图像cv2.IMREAD_GRAYSCALE:灰度图像import cv2 img cv2.imread(E:/opencv/open-cv/2-7/cat.jpg,1)cv2.imshow("img", img) cv2.waitKey(0) cv2.destroyAllWindows() cv2.imread()读取图片&#xff0c;当括号里面是1时&#xff…

华为OD机试题,用 Java 解【水仙花数】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

抓包技术(浏览器APP小程序PC应用)

P1 抓包工具 01. Fidder 首先第一个Fiddler它的优势&#xff0c;独立运行&#xff0c;第二个支持移动设备&#xff08;是否能抓移动APP的包&#xff0c;&#xff09;在这一块的话wireshark、httpwatch就不支持&#xff0c;因此在这一块就可以排除掉前连个&#xff0c;因为我们…

SpringCloudGateway--基于redis实现令牌桶算法

目录 一、限流算法 1、计数器算法 2、漏桶算法 3、令牌桶算法 二、Gateway中的限流 一、限流算法 1、计数器算法 计数器算法是指从第一个请求开始&#xff0c;每多一个请求就加1&#xff0c;假设设置每秒限流100&#xff0c;当在一秒钟前500ms已经达到100&#xff0c;后面的5…

华为云计算HCIA学习笔记-第1章 云计算基础概念

1.1 云计算课程安排说明 &#xff08;IA-虚拟化-FC / IP-Linux OpenStack 桌面云/IE-备份容灾迁移&#xff09; 1.2 为什么云计算IA讲虚拟化&#xff1f; 提前告知学员&#xff0c;为什么IA课程要重点讲解虚拟化&#xff1f;云计算基于OpenStack&#xff0c;其底层虚拟化技术…

赞!中原银行|古风金融产品体验运营数字驾驶舱大屏!

本期「V友故事」&#xff1a;中原银行设计师作品——古风金融产品体验运营大屏 金融行业数字化转型是近年来提出的一个关键词&#xff0c;它不仅在改变金融服务的结构、形式和方式&#xff0c;而且也在影响中国金融行业的扩张规模&#xff0c;基于此行业趋势&#xff0c;金融从…

加密图像的脆弱水印及应用

原文题目&#xff1a;《A self-embedding secure fragile watermarking scheme with high quality recovery》 学习笔记&#xff1a; 应用场景 为了确保图像在传输过程中不被损坏&#xff0c;在将原始图像发送到云端之前&#xff0c;将用于篡改检测和恢复的水印嵌入到原始图像…

docker逃逸复现--pid=host模式下的逃逸

漏洞原理当docker以--pidhost模式启动时&#xff0c;你可以通过在容器进程中注入一些shellcode进行逃逸。相当于给了docker Linux中的CAP_SYS_PTRACE权限--pidhost&#xff1a;意味着宿主机与容器公享一套pid&#xff0c;如此做容器就可以访问并跟踪宿主机的进程Linux中的CAP_S…

【微信小程序】-- 页面导航 -- 导航传参(二十四)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

MyBatis框架快速入门 - 基本使用

文章目录MyBatisMyBatis基本介绍MyBaits快速入门Mapper代理开发MyBatis配置文件MyBatis MyBatis基本介绍 什么是MyBatis? MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software found…