每日一题——C++、Python实现牛客网CM11 链表分割(举一反三+思想解读+逐步优化)

news2024/9/21 16:37:50


一个认为一切根源都是“自己不够强”的INTJ

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

题目链接

目录

我的写法

C嘎嘎

​编辑

Python

代码点评

代码点评

时间复杂度分析

空间复杂度分析

改进建议

我要更强

哲学和编程思想

举一反三

1. 模块化思维

2. 抽象与封装

3. 迭代与增量开发

4. 避免副作用

5. 优化与性能


我的写法

C嘎嘎

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // 创建两个链表的头指针,分别用于存储小于x和大于等于x的节点
        ListNode* listLessThanX = pHead, *listEqualOrGreaterThanX = pHead;


        // 1. 找到listLessThanX和listEqualOrGreaterThanX的首元素,cur1,cur2将始终分别指向前两者当前最后一个元素
        for (; listLessThanX;) {
            if (listLessThanX->val < x) {
                break; // 找到小于x的节点,跳出循环
            } else {
                listLessThanX = listLessThanX->next; // 继续寻找
            }
        }
        for (; listEqualOrGreaterThanX;) {
            if (listEqualOrGreaterThanX->val >= x) {
                break; // 找到大于等于x的节点,跳出循环
            } else {
                listEqualOrGreaterThanX = listEqualOrGreaterThanX->next; // 继续寻找
            }
        }
        // 检查是否找到了小于x和大于等于x的节点
        if (listLessThanX == NULL && listEqualOrGreaterThanX)
            return pHead; // 如果只有大于等于x的节点,返回原链表
        else if (listLessThanX && listEqualOrGreaterThanX == NULL)
            return pHead; // 如果只有小于x的节点,返回原链表


        ListNode* cur1 = listLessThanX, *cur2 = listEqualOrGreaterThanX;


        // 2. pHead遍历原链表,根据题意将当前元素接到cur1或cur2后,pHead以及cur1或cur2再指向新接入的元素
        for (; pHead;) {
            if (pHead == cur1 || pHead == cur2)
                pHead = pHead->next; // 如果pHead指向的是已经处理过的节点,跳过
            else {
                if (pHead->val < x) {
                    cur1->next = pHead; // 将小于x的节点接到cur1后面
                    cur1 = cur1->next; // cur1指向新添加的节点
                    pHead = pHead->next; // 移动pHead到下一个节点
                } else {
                    cur2->next = pHead; // 将大于等于x的节点接到cur2后面
                    cur2 = cur2->next; // cur2指向新添加的节点
                    pHead = pHead->next; // 移动pHead到下一个节点
                }
            }
        }
        cur2->next = NULL; // 确保链表的末尾指向NULL,防止出现环
        cur1->next = listEqualOrGreaterThanX; // 将两个链表连接起来
        return listLessThanX; // 返回小于x的链表头,即整个分区后的链表头
    }
};

Python

class Partition:
    def partition(self, pHead, x):
        # 创建两个链表的头指针,分别用于存储小于x和大于等于x的节点
        listLessThanX = pHead
        listEqualOrGreaterThanX = pHead

        # 1. 找到listLessThanX和listEqualOrGreaterThanX的首元素,cur1,cur2将始终分别指向前两者当前最后一个元素
        while listLessThanX:
            if listLessThanX.val < x:
                break  # 找到小于x的节点,跳出循环
            listLessThanX = listLessThanX.next  # 继续寻找

        while listEqualOrGreaterThanX:
            if listEqualOrGreaterThanX.val >= x:
                break  # 找到大于等于x的节点,跳出循环
            listEqualOrGreaterThanX = listEqualOrGreaterThanX.next  # 继续寻找

        # 检查是否找到了小于x和大于等于x的节点
        if not listLessThanX and listEqualOrGreaterThanX:
            return pHead  # 如果只有大于等于x的节点,返回原链表
        elif listLessThanX and not listEqualOrGreaterThanX:
            return pHead  # 如果只有小于x的节点,返回原链表

        cur1 = listLessThanX
        cur2 = listEqualOrGreaterThanX

        # 2. pHead遍历原链表,根据题意将当前元素接到cur1或cur2后,pHead以及cur1或cur2再指向新接入的元素
        while pHead:
            if pHead == cur1 or pHead == cur2:
                pHead = pHead.next  # 如果pHead指向的是已经处理过的节点,跳过
            else:
                if pHead.val < x:
                    cur1.next = pHead  # 将小于x的节点接到cur1后面
                    cur1 = cur1.next  # cur1指向新添加的节点
                    pHead = pHead.next  # 移动pHead到下一个节点
                else:
                    cur2.next = pHead  # 将大于等于x的节点接到cur2后面
                    cur2 = cur2.next  # cur2指向新添加的节点
                    pHead = pHead.next  # 移动pHead到下一个节点

        cur2.next = None  # 确保链表的末尾指向NULL,防止出现环
        cur1.next = listEqualOrGreaterThanX  # 将两个链表连接起来
        return listLessThanX  # 返回小于x的链表头,即整个分区后的链表头

代码点评

代码点评

这段代码实现了链表的分割功能,根据给定的值 x 将链表分割成两部分:小于 x 的节点和大于等于 x 的节点。以下是对代码的点评:

  1. 逻辑清晰:代码逻辑清晰,通过两个指针 cur1 和 cur2 分别管理小于 x 和大于等于 x 的节点,有效地实现了链表的分割。
  2. 边界处理:代码在开始时处理了链表可能为空或所有节点值都小于或大于等于 x 的情况,确保了算法的健壮性。
  3. 指针操作:使用指针操作来移动和连接节点,这是链表操作中常见的技巧,有效地避免了额外的数据结构开销。
  4. 结尾处理:在分割完成后,代码正确地设置了 cur2->next 为 NULL,防止链表出现环,这是一个重要的细节处理。
  5. 代码可读性:虽然功能实现正确,但代码中的条件判断和循环可以进一步优化,以提高代码的可读性和简洁性。

时间复杂度分析

  • 遍历链表:代码中有一个主要的循环,用于遍历链表中的每个节点,并对每个节点进行判断和移动。因此,时间复杂度为 O(n),其中 n 是链表的长度。

空间复杂度分析

  • 额外空间:代码没有使用额外的数据结构(如数组或额外的链表)来存储节点,只是使用了几个指针变量。因此,空间复杂度为 O(1),即常数级空间复杂度。

改进建议

  • 代码优化:可以考虑简化代码中的条件判断,例如通过直接比较 pHead 和 cur1 或 cur2 的值来决定是否跳过当前节点,而不是通过 pHead == cur1 || pHead == cur2 这样的判断。
  • 错误处理:虽然代码已经考虑了一些边界情况,但可以进一步完善错误处理,例如处理输入链表为空的情况。
  • 可读性提升:可以通过添加更多的注释和使用更清晰的变量命名来提高代码的可读性。

总体来说,这段代码是一个有效的链表分割实现,具有较好的时间和空间效率,但仍有改进空间以提高代码质量和可维护性。


我要更强

为了优化时间复杂度和空间复杂度,可以考虑使用更直接的方法来处理链表分割。基本思路是创建两个新的链表头,然后遍历原始链表,将节点根据其值与 x 的关系分别连接到两个新的链表中。最后,将这两个链表连接起来。这种方法避免了在原始链表上进行复杂的指针操作,使得代码更加简洁和直观。

以下是优化后的代码实现:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // 创建两个新的链表头
        ListNode* lessHead = new ListNode(0); // 用于存储小于x的节点
        ListNode* greaterEqualHead = new ListNode(0); // 用于存储大于等于x的节点

        // 分别指向两个链表的当前尾节点
        ListNode* lessTail = lessHead;
        ListNode* greaterEqualTail = greaterEqualHead;

        // 遍历原始链表
        while (pHead) {
            if (pHead->val < x) {
                // 如果节点值小于x,将其添加到less链表
                lessTail->next = pHead;
                lessTail = pHead;
            } else {
                // 如果节点值大于等于x,将其添加到greaterEqual链表
                greaterEqualTail->next = pHead;
                greaterEqualTail = pHead;
            }
            // 移动到下一个节点
            pHead = pHead->next;
        }

        // 将大于等于x的链表连接到小于x的链表后面
        lessTail->next = greaterEqualHead->next;
        // 确保greaterEqual链表的末尾指向NULL,防止出现环
        greaterEqualTail->next = NULL;

        // 返回less链表的头节点,即整个分割后的链表头
        ListNode* result = lessHead->next;
        // 清理创建的临时头节点
        delete lessHead;
        delete greaterEqualHead;

        return result;
    }
};

时间复杂度分析

  • 遍历链表:代码中有一个循环,用于遍历链表中的每个节点,并对每个节点进行判断和移动。因此,时间复杂度为 O(n),其中 n 是链表的长度。

空间复杂度分析

  • 额外空间:代码使用了两个额外的链表头节点和几个指针变量。链表头节点在逻辑上是必要的,因为它们帮助我们管理新创建的链表。因此,空间复杂度为 O(1),即常数级空间复杂度。

这种优化方法保持了时间复杂度为 O(n),同时通过避免在原始链表上进行复杂的指针操作,简化了代码逻辑,提高了代码的可读性和维护性。


哲学和编程思想

这种方法体现了几个重要的哲学和编程思想:

  1. 模块化思维:通过创建两个独立的链表来分别处理小于和大于等于 x 的节点,这种方法体现了模块化思维。每个链表都是一个独立的模块,负责处理特定类型的数据。这种模块化的设计使得代码更加清晰,易于理解和维护。
  2. 抽象与封装:代码中创建了两个新的链表头,并将原始链表的节点根据其值与 x 的关系分别添加到这两个链表中。这种做法抽象了链表分割的过程,封装了分割的细节,使得外部只需要关注分割的结果,而不需要了解分割的具体实现。
  3. 迭代与增量开发:代码通过迭代的方式遍历原始链表,并在每次迭代中决定节点的去向。这种迭代的方法体现了增量开发的哲学,即逐步构建解决方案,每次只处理一个问题或一个步骤。
  4. 避免副作用:通过创建新的链表来存储分割后的节点,而不是直接在原始链表上操作,这种方法避免了副作用。在原始链表上直接操作可能会导致不可预测的结果,而创建新的链表则确保了原始数据的完整性。
  5. 优化与性能:虽然这种方法在空间上稍微增加了一些开销(创建了两个新的链表头),但它简化了时间复杂度,保持了线性时间复杂度。这种权衡体现了优化与性能的思想,即在保证算法效率的同时,也考虑代码的简洁性和可读性。
  6. 面向对象编程:如果这段代码是在一个面向对象的编程环境中编写的,那么它还体现了面向对象编程的原则,如封装和继承。链表节点可以被视为对象,而链表的操作则可以被封装在类的方法中。

这些哲学和编程思想不仅适用于链表分割这个问题,也适用于许多其他编程问题和软件开发场景。通过理解和应用这些思想,可以提高代码的质量和开发效率。


举一反三

根据上述提到的哲学和编程思想,以下是一些技巧和示例代码,帮助你举一反三,应用到其他编程场景中:

1. 模块化思维

将复杂问题分解为更小、更易于管理的部分。例如,如果你需要实现一个功能来处理不同类型的数据,可以创建不同的函数或类来处理每种数据类型。

def process_integer(data):
    # 处理整数数据的逻辑
    pass

def process_string(data):
    # 处理字符串数据的逻辑
    pass

# 使用示例
data_list = [10, "hello", 20, "world"]
for data in data_list:
    if isinstance(data, int):
        process_integer(data)
    elif isinstance(data, str):
        process_string(data)

2. 抽象与封装

创建抽象的数据结构或类来隐藏复杂性。例如,设计一个栈类,封装栈操作的细节。

class Stack:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def is_empty(self):
        return not bool(self.items)

# 使用示例
stack = Stack()
stack.push(1)
stack.push(2)
print(stack.pop())  # 输出: 2

3. 迭代与增量开发

逐步构建解决方案,每次只处理一个问题或一个步骤。例如,实现一个排序算法,可以先实现基本框架,然后逐步添加排序逻辑。

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]

# 使用示例
data = [64, 34, 25, 12, 22, 11, 90]
bubble_sort(data)
print(data)  # 输出: [11, 12, 22, 25, 34, 64, 90]

4. 避免副作用

在函数内部避免修改外部状态,确保函数的纯度。例如,设计一个函数,它接受输入并返回处理后的结果,但不修改任何外部变量。

def add_one(x):
    return x + 1

# 使用示例
original_value = 5
new_value = add_one(original_value)
print(new_value)  # 输出: 6
print(original_value)  # 输出: 5,原始值未被修改

5. 优化与性能

在设计算法时,考虑时间和空间的平衡。例如,使用哈希表来优化查找操作。

def find_element(arr, target):
    seen = set()
    for element in arr:
        if target - element in seen:
            return True
        seen.add(element)
    return False

# 使用示例
data = [1, 2, 3, 4, 5]
print(find_element(data, 7))  # 输出: True

通过这些示例,可以看到如何将这些哲学和编程思想应用到不同的编程场景中,从而提高代码的质量和效率。


感谢阅读。

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

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

相关文章

Wpf 使用 Prism 实战开发Day22

客户端添加IDialogService 弹窗服务 在首页点击添加备忘录或待办事项按钮的时候&#xff0c;希望有一个弹窗&#xff0c;进行相对应的内容添加操作。 一.在Views文件夹中&#xff0c;再创建一个Dialog 文件夹&#xff0c;用于放置备忘录和待办事项的弹窗界面。 1.1 备忘录&…

Unity3D输入事件

文章目录 前言一、全局事件二、射线三、点选3D模型四、点击地面控制人物移动总结 前言 Unity输入事件分为两类&#xff0c;全局触发和监听式触发。全局触发通常是运行在update在每帧进行检测&#xff0c;而监听式触发是被动的输入事件。 一、全局事件 在最新的unity中有新和旧…

el-table 实现嵌套表格的思路及完整功能代码

要实现的需求是这样的&#xff1a; 本来我是用 el-table 的 :span-method 方法实现的&#xff0c;但发现合并起来有问题&#xff0c;跟我的需求差距有些大&#xff0c;于是我想到了嵌套表格。但是嵌套完之后的样子也是很奇怪&#xff1a; 不要气馁&#xff0c;思路还是对的&a…

《QT实用小工具·六十四》QT实现仿Windows消息通知控件可交互

1、概述 源码放在文章末尾 该项目实现了仿Windows消息通知功能&#xff0c;包含多个通知显示定时消失支持出现/消失动画等功能 允许两种使用方式&#xff1a; 局部通知&#xff0c;通过信号槽和 Lambda 直接获取通知的操作方式 全部通知&#xff0c;触发信号给其他控件使用 另…

C++_C++11的学习

1. 统一的列表初始化 1.1&#xff5b;&#xff5d;初始化 在C98 中&#xff0c;标准就已经允许使用花括号 {} 对数组或者结构体元素进行统一的列表初始值设定。而到了C11&#xff0c;标准扩大了用大括号括起的列表 ( 初始化列表 )的使用范围&#xff0c;使其能适用于所有的内…

思科模拟器--03.RIP协议路由--24.5.17

1.首先&#xff0c;先创建两个个人电脑:PC0和PC1和三个路由器:R1&#xff0c;R2和R3. (诀窍:建议用文本框标注一下重要简短的内容; 目的:降低失误概率,提高成功率!) 第0步:(个人电脑的IP,子网掩码和默认网关配置) 接着&#xff0c;可以先将个人电脑的IP和网关先配置一下…

虹科Pico汽车示波器 | 免拆诊断案例 | 2017款奔驰E300L车行驶中发动机偶尔无法加速

故障现象 一辆2017款奔驰E300L车&#xff0c;搭载274 920发动机&#xff0c;累计行驶里程约为21万km。车主反映&#xff0c;该车行驶中发动机偶尔无法加速&#xff0c;且车辆发闯。 故障诊断 用故障检测仪检测&#xff0c;发动机控制单元&#xff08;N3/10&#xff09;中存储…

由于下列错误 luafv服务启动失败的解决办法

主要是电脑近期总有问题&#xff0c;经常使用中就死机&#xff0c;无任何反应只能按重启按钮。 一天最少也要有一次&#xff0c;然后查看死机前的系统日志发现主要错误为 “由于下列错误&#xff0c;luafv 服务启动失败:此驱动程序被阻止加载” 该错误在每天都会出现&#x…

ChatGPT移动应用收入在GPT-4o发布后迎来最大涨幅

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

几年前写的一个小工具

几年前写的一个工具&#xff0c;开发工具 是Delphi7 UniDAC FastReport2.53 &#xff0c;开发时间不到8小时&#xff08;同时还在处理其他事情&#xff09;。 其实把这个翻出来&#xff0c;是想说说俺的一个同事。他是俺这几年遇到的最优秀的人之一。他负责售后维护部&#x…

Harmony学习笔记一——项目创建及配置

文章基于Harmony Next Preview2 进行学习&#xff0c;其他版本可能会稍有不同 准备工作 由于目前Harmony Next仅有Preview版本&#xff0c;想要进行Harmony Next开发需要向华为申请权限&#xff0c;具体操作参考: https://developer.huawei.com/consumer/cn/forum/topic/02081…

一文搞懂HashSet类的底层实现原理

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

Flask多线程开发指南

文章目录 1. 什么是多线程&#xff1f;2. Flask中的多线程3. 注意事项结论 在Web应用程序开发中&#xff0c;有时候需要处理一些耗时的任务&#xff0c;例如与数据库交互、发送网络请求或执行计算密集型的操作。为了保持用户体验的流畅性&#xff0c;我们可以使用多线程来处理这…

【开源可视化报表设计器】借力实现高效率流程化办公!

进行数字化转型、实现流程化办公&#xff0c;这些应该是目前很多企业都想要实现的目标吧。那么&#xff0c;利用什么样的软件平台可以实现&#xff1f;低代码技术平台拥有可视化界面、灵活操作、好维护等众多优势特点&#xff0c;可以借助低代码技术平台、开源可视化报表设计器…

Hidedump:dumplsass加密免杀工具

文章目录 前记hook WriteAllduplication其他思路SilentProcessExitminidumpCallback 后记referencereference 前记 思路&#xff1a;直接dumplsass原文会被杀软删掉&#xff0c;通过hook WriteAll对dump的内容先加密再保存到磁盘并离线解密 项目已开源&#xff0c;该项目采用…

【git】开发提交规范(feat、fix、perf)

这段时间收到的需求很多&#xff0c;可能是临近两周一次的大版本灰度上线&#xff0c;这次产生了一个关于git的思考&#xff0c;就是各个版本之间怎么管理的问题&#xff0c;这里做出我自己的一些方法。 首先&#xff0c;既然已经明确了remote分支中的release分支为主分支&…

为什么说 Redis 是单线程的?——Java全栈知识(25)

为什么说 Redis 是单线程的&#xff1f; 我们常说的 Redis 是单线程的&#xff0c;但是我前面在讲持久化机制的时候又说 RDB 的持久化是通过主进程 fork 出一个子进程来实现 RDB 持久化。那么 Redis 到底是多线程还是单线程的呢&#xff1f; Redis 的网络 IO 和键值的读写是单…

爬虫学习--11.MySQL数据库的基本操作(上)

MySQL数据库的基本操作 创建数据库 我们可以在登陆 MySQL 服务后&#xff0c;使用命令创建数据库&#xff0c;语法如下: CREATE DATABASE 数据库名; 显示所有的数据库 show databases; 删除数据库 使用普通用户登陆 MySQL 服务器&#xff0c;你可能需要特定的权限来创建或者删…

java中的StringBuffer类和StringBuildet类

一、StringBuffer类 1、特点 底层是不被final修饰的char数组value,数组地址可以发生改变&#xff0c;当StringBuffer类对象的值发生改变时不用创建新的对象 2、构造方法 3、扩容规则 StringBuffer的底层数组value在扩容时为当前数组的长度2倍加2或者当前数组长度加上要追加…

CVE-2020-7982 OpenWrt 远程命令执行漏洞学习(更新中)

OpenWrt是一款应用于嵌入式设备如路由器等的Linux操作系统。类似于kali等linux系统中的apt-get等&#xff0c;该系统中下载应用使用的是opgk工具&#xff0c;其通过非加密的HTTP连接来下载应用。但是其下载的应用使用了SHA256sum哈希值来进行检验&#xff0c;所以将下载到的数据…