力扣刷题笔记

「新」动计划 · 编程入门

两整数相加

notion image
在这段代码中,我们来解释两个关键字:objectself

1. object

object 是 Python 中所有类的基类。换句话说,Python 中所有的类都默认继承自 object,不论你有没有明确写出来。这样做的好处是让所有类都具备了 object 类的一些基础方法,比如 __init____str__ 等。这也是 Python 面向对象设计的一个基本原则——万物皆对象。
在这段代码中,class Solution(object): 的意思是 Solution 类是继承自 object 类的。虽然 Python 3 中写 class Solution:class Solution(object): 是一样的,但在 Python 2 中,只有写成 class Solution(object): 才能用到 Python 的新式类机制(新式类引入了一些更强大的功能)。
所以,这里的 object 是告诉 Python,Solution 类是基于 object 类创建的。

2. self

self 是 Python 类方法中的一个“传统”参数。它代表类的实例本身,或者说是对象自身。每当你在类中定义一个方法时,Python 会默认把调用该方法的对象作为第一个参数传递给方法,这就是 self
比如你在代码中定义了一个 sum 方法,传入 num1num2。当你用一个 Solution 类的实例去调用这个方法时,Python 会自动把这个实例作为 self 传递进去。所以即使你不显式传入 self,Python 也会在幕后帮你传递。
举个例子:
s = Solution() s.sum(1, 2)
在调用 s.sum(1, 2) 的时候,Python 实际上相当于执行了 Solution.sum(s, 1, 2),其中 s 就是 self,指代 Solution 类的实例。
 
为什么 Python 要把调用方法的对象作为第一个参数传递给方法呢?为什么一定要有 self? 这个问题其实可以从 Python 的设计哲学和面向对象编程(OOP)的角度来理解。

1. 明确的 "自我":self 让代码更清晰

Python 是一门非常强调“清晰”、“明确”的语言,它有一个哲学——“明确优于隐式”(Explicit is better than implicit)。通过显式地把 self 作为方法的第一个参数,我们能够非常清楚地知道这个方法是与某个对象绑定的,也就是属于某个实例的,而不是一个普通的函数。
举个例子,如果没有 self,方法内部使用对象的属性时,可能会让代码变得不够明确:
def sum(num1, num2): # 假设内部用了实例的属性,代码不容易理解 return num1 + self.num2
在 Python 里,每次定义类方法时都显式地传入 self,这样一眼就能看出这个方法依赖于实例对象,不需要靠猜。

2. 灵活控制:self 让你更自由

通过 self,我们可以随时访问当前实例的属性和方法,这带来了更大的灵活性。你可以定义一个类,类中的方法可以动态地操作实例自身的数据。self 让我们在方法中能够直接操作这个对象的状态,也就是能随时修改、访问和更新对象的属性。
比如:
class Person: def __init__(self, name): self.name = name def greet(self): print(f"Hello, my name is {self.name}")
每个 Person 实例可以有不同的 name 属性,self 让方法能够随时访问到这个 name,而且不同的实例使用这个方法时,输出的内容也会不同。

3. 这是 OOP 的标准做法

在面向对象编程中,类的实例方法默认就是作用于当前对象本身,这样的设计使得对象能够拥有自己的状态(属性)和行为(方法)。Python 通过 self 实现了这一机制,而许多其他编程语言(如 C++、Java)则采用了类似的机制,只是它们的 self 是隐式的,由编译器自动处理,而不是像 Python 这样显式写出来。
举个对比的例子: 在 Java 里,你可能会看到类似的代码:
class Person { String name; void greet() { System.out.println("Hello, my name is " + this.name); } }
在这里,this 和 Python 的 self 是类似的,但在 Java 中,它是可以省略的,而 Python 则要求显式写出 self,因为这符合 Python 的哲学:明确优于隐式。

4. 避免混淆

如果没有 self,Python 就得靠某种“隐式”机制来知道哪个对象在调用某个方法。这样可能会引起混乱,特别是如果你不小心定义了两个相同名字的方法或者变量,而没有明确指明哪个是实例的属性、哪个是局部变量的话,调试起来会非常困难。
所以,self 就是为了解决这种混乱问题而设计的,通过 self,你可以明确区分“对象的属性”和“方法内的局部变量”,这样代码阅读起来更直观,调试起来也更容易。
class Person: def __init__(self, name, age): self.name = name # 这是对象的属性 self.age = age # 这是对象的属性 def celebrate_birthday(self): age = 18 # 这是方法内的局部变量,仅在方法内部生效 self.age += 1 # 通过 self 访问对象的属性,并修改它 print(f"{self.name} is now {self.age} years old!")

python的代码特点,包括object 类、函数的参数self

温度转换

notion image
notion image

python简直太方便了

python直接列表返回,一看就懂

python变量名通常使用小写字母

解释下c,与python的区别

局部变量只在函数执行期间有效,此为栈内存
堆内存在函数结束后依然有效,直到手动 free()
在代码中,result 是一个指向 double 类型的指针,指向了一块通过 malloc 分配的堆内存。由于这块内存是动态分配的,它不会随着函数结束而被自动回收。指针 result 保存了这块堆内存的地址,因此即使函数结束,调用者依然可以通过这个指针访问到有效的数据,直到明确调用 free() 来释放内存 通过 *returnSize = 2;,调用者可以知道返回的数组有 2 个元素。换句话说,returnSize 告诉了调用者,result 数组中存储了 2 个温度值(开尔文和华氏温度)。在实际使用中,调用者就可以通过这个信息去正确地遍历或处理返回的数组数据。
c语言返回数组的大小还要我自己设置啊,它不能自己得知吗?没我想的智能啊
你提出的这个问题非常有趣!在 C 语言中,确实不像 Python 等高级语言那样智能,需要开发者手动管理很多事情,包括内存分配和数组的大小。这也是 C 语言的特性之一,它接近底层,给了开发者对内存和数据的精确控制,但同时也带来了更多的管理工作。

为什么 C 语言不能自动知道数组的大小?

C 语言中,数组在函数间传递时,只能传递数组的首地址(也就是指针),它不会携带有关数组大小的信息。也就是说,数组本身并不包含长度的元数据。当你传递一个数组或动态分配的内存时,函数只知道这是某块内存的起始位置,而不知道这块内存有多大。
在 C 语言中,指针和数组有以下特点:
  • 数组名在函数调用中退化为指针,指向数组的第一个元素。
  • 指针本身只是一个地址,并不包含指向的数据有多大。
  • 动态分配的内存(如使用 malloc 分配的内存)不会自动附带大小信息,函数必须通过其他方式知道内存块的大小。

C 语言的设计理念

C 语言是一种底层的编程语言,它更关注性能和内存管理,而不是自动化处理。例如,当你使用 malloc 动态分配内存时,C 语言只会返回一块内存的首地址,内存管理完全由程序员控制。相比之下,像 Python、Java 这样的高级语言,会自动管理内存,并且它们的数组或列表自带长度信息。
在 C 语言中,你要明确告诉程序内存有多大:
  1. 当你动态分配内存时,你需要自己指定需要多少字节的内存。
  1. 当函数需要返回一个数组或处理一个数组时,调用者通常需要提供数组的大小,或者通过其他方式告诉函数数组有多少元素。

高级语言与 C 语言的差异

在 Python 这样的语言中,列表对象会自动存储它们的大小,因此当你传递一个列表时,Python 知道列表有多少元素:
my_list = [1, 2, 3] len(my_list) # Python 会自动知道列表的大小
但在 C 语言中,数组传递时仅仅传递了首地址,没有内置的方法可以知道数组的长度。所以必须通过额外的机制告诉函数数组的大小。
例如,在 C 语言中常见的做法是:
  • 通过额外的参数传递数组的大小
  • 返回时通过指针传递数组的大小(像你代码中的 returnSize)。
  • 使用结构体打包数组和它的大小一起传递。

你提到的智能与手动设置大小的区别

  1. 高级语言(如 Python):列表或数组本身会自动记录长度信息。当你传递列表或数组时,语言的运行时系统会跟踪它的长度,因此你不需要显式地管理数组的大小。
  1. C 语言:C 语言没有自动的内存管理或长度跟踪机制。当你传递数组或动态分配的内存时,程序只知道数组的起始地址,不知道它的大小。这是因为 C 语言设计初衷是提供高效、低开销的内存操作,而不是在运行时增加额外的元数据管理。

举个例子:

假设我们需要返回一个数组,并且告诉调用者数组的大小,常见的做法是通过 returnSize 参数返回数组大小:
#include <stdlib.h> double* convertTemperature(double celsius, int* returnSize) { double* result = (double*)malloc(2 * sizeof(double)); // 分配2个double大小的数组 result[0] = celsius + 273.15; // 开尔文 result[1] = celsius * 1.8 + 32; // 华氏 *returnSize = 2; // 告诉调用者数组的大小是2 return result; // 返回数组的地址 }

如果没有 returnSize,会发生什么?

如果你不通过 returnSize 告诉调用者数组的大小,调用者就不知道返回的数组里有多少个元素。在这种情况下,调用者可能错误地遍历数组,导致越界访问,产生不可预期的行为或程序崩溃。

总结:

  • C 语言设计:C 语言没有内置机制记录数组大小,因为它主要面向高性能和内存控制,这也导致你需要手动管理数组的大小。
  • 为什么不能自动得知数组大小:因为数组名传递的是指针,指针仅仅指向数组的首地址,没有长度信息。因此,程序员必须显式传递数组的大小。
  • 如何处理:通过额外的参数(如 returnSize)或使用结构体来打包数组和长度信息,是 C 语言中常见的做法。
虽然这种手动管理看似麻烦,但它让你对内存有更多的控制,同时减少了高级语言中的运行时开销(如自动内存管理、垃圾回收等)。这也是为什么 C 语言被广泛用于操作系统、嵌入式系统等性能要求极高的场景。

对比python和c的一点区别
c的内存管理逻辑
python超方便的列表

最小偶倍数

python3 类型注解
python3 类型注解
notion image

求最小偶倍数的解法
以后用python3、c两种语言解题
python3类型注解

判断根节点是否等于子节点之和

 
notion image
notion image

二叉树的定义
树的解引用操作
检查输入 的思想

数组异或操作

异或?

位运算,相同为0,不同为1。
运用:交换两个数、找出出现过单数次数的数、加密解密
题目这样定义数组的好处:等差数列,训练对公式的敏感度;方便异或运算(虽然解的时候不用考虑);优化空间使用,按需生成数组元素
题目这样定义数组的好处:等差数列,训练对公式的敏感度;方便异或运算(虽然解的时候不用考虑);优化空间使用,按需生成数组元素
在 Python 中,range() 函数可以根据传入的参数生成一个从起始值到结束值(不包括结束值)的整数序列。
notion image
notion image

对异或的学习
两种语言循环的写法
循环+增量赋值运算符

好数对的数目

  • range(stop):生成从 0stop-1 的整数序列。
  • range(start, stop):生成从 startstop-1 的整数序列。(这题很适合:)
  • range(start, stop, step):生成从 start 开始,每次步长为 step,直到 stop-1 的整数序列。
  • range() 通常用于循环控制,特别是在 for 循环中。
notion image
notion image

(我又找回考试的感觉了🤣)
双重循环及其中一个技巧:j从i+1开始

统计好三元组

notion image
python用and
abs() 是一个内置函数,专门用于计算一个数的绝对值
notion image

跟上一道题很像
abs()的运用

寻找用户推荐人

notion image
考虑NULL
OR
IS NULL
补集法
补集法
NOT IN

MySQL基本查询语句
两种解法,一种正向且带有对NULL的考虑,一种是补集法
💡
中途写作业去了,用的c++。现在决定把c替换成c++,并且只用c++
并且简单的题目不附上

**258

%=10
/=10