当前位置: 首页 > news >正文

uniapp b树

# #43 

## 今日计划

### 1.python 爬虫

  ~~~~ 学习看视频2h 了解回顾熟悉python~~语法~~

 ~~尝试写一个python爬虫demo~~ 

~~:看到p8正则表达式了~~

### 2.uniapp

     完善昨天写的demo ~~大学生家教服务项目~~:bug太多不好改 

​    ~~看完uniapp3h的速成课~~  : 密码的 没密码用 还得看一个vue3 ts速成课

​    ~~小兔鲜儿项目 day2 day3 ppt~~

​    3h

### 3.java算法 

​    ~~开始写B树~~

​    ~~1.5h~~ 

 [讲解](https://www.bilibili.com/video/BV1tJ4m1w7yR/?spm_id_from=333.337.search-card.all.click&vd_source=3e5b8596fdf7a1e637fb85e996addeda)

### 4.技术栈

​    maven视频课

​    ssm框架视频课

​    ~~看了一个mysql索引优化~~  :看了等于没看 没密码用

​    

### 5.web3

​    了解

:思考什么时候学习mysql调优 以及 设计模式 等技术栈 

:项目后端什么时候写 

## 今日总结

```

```

```

```

```

```

## 今日源码

### python高级语法

~~~markdown
好的,下面为你介绍 Python 的一些高级语法,掌握这些内容能够让你写出更加简洁、高效且专业的代码。

### 1. 列表/字典/集合推导式
借助推导式,你可以简洁地创建列表、字典和集合,无需编写传统的循环代码。
```python
# 列表推导式
squares = [x**2 for x in range(10) if x % 2 == 0]  # 结果: [0, 4, 16, 36, 64]

# 字典推导式
squares_dict = {x: x**2 for x in range(5)}  # 结果: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 集合推导式
s = {x for x in [1, 2, 2, 3, 3, 3]}  # 结果: {1, 2, 3}
```

### 2. 生成器(Generator)
生成器采用惰性计算的方式,在需要时才生成数值,这有助于节省内存。
```python
# 创建生成器的方式一:使用生成器表达式
gen = (x**2 for x in range(5))  # 使用圆括号
for num in gen:
print(num)  # 依次输出 0, 1, 4, 9, 16

# 创建生成器的方式二:使用 yield 关键字
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
```

### 3. 装饰器(Decorator)
装饰器能够在不修改原函数代码的前提下,为函数增添额外的功能。
```python
def timer_decorator(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 运行耗时: {end - start} 秒")
return result
return wrapper

@timer_decorator
def calculate_sum(n):
return sum(range(n + 1))

# 调用方式:
# calculate_sum(1000000)  # 会输出函数运行时间
```

### 4. 上下文管理器(Context Manager)
上下文管理器可用于自动管理资源,像文件操作、数据库连接等。
```python
# 使用 with 语句操作文件(这是 Python 内置的上下文管理器)
with open('example.txt', 'r') as f:
content = f.read()

# 自定义上下文管理器
class MyContextManager:
def __enter__(self):
print("进入上下文")
return self  # 返回的对象会被 as 后的变量接收
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出上下文")
# 可以在这里进行资源清理等操作
```

### 5. 迭代器(Iterator)
迭代器是实现了 `__iter__()` 和 `__next__()` 方法的对象。
```python
class MyRange:
def __init__(self, start, end):
self.start = start
self.end = end
self.current = start
def __iter__(self):
return self  # 返回迭代器自身
def __next__(self):
if self.current < self.end:
value = self.current
self.current += 1
return value
else:
raise StopIteration  # 结束迭代

# 使用示例
for num in MyRange(0, 3):
print(num)  # 输出 0, 1, 2
```

### 6. 多线程与多进程
利用多线程和多进程能够实现并行处理任务。
```python
# 多线程示例
import threading

def print_numbers():
for i in range(5):
print(f"线程1: {i}")

def print_letters():
for letter in ['a', 'b', 'c', 'd', 'e']:
print(f"线程2: {letter}")

t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)

t1.start()
t2.start()
t1.join()  # 等待线程1结束
t2.join()  # 等待线程2结束

# 多进程示例(与多线程代码结构相似)
from multiprocessing import Process

p1 = Process(target=print_numbers)
p2 = Process(target=print_letters)
# 后续使用方法与线程类似
```

### 7. 异常处理进阶
在异常处理中,可以使用 `else` 和 `finally` 子句。
```python
try:
result = 10 / 0  # 会触发 ZeroDivisionError
except ZeroDivisionError:
print("除数不能为零!")
else:
print("没有发生异常时执行此代码")  # 若 try 中无异常则执行
finally:
print("无论是否有异常都会执行此代码")  # 总会执行
```

### 8. 元类(Metaclass)
元类是创建类的类,在需要动态创建类时会用到。
```python
class MyMeta(type):
def __new__(cls, name, bases, attrs):
# 可以在这里修改类的属性或方法
attrs['custom_method'] = lambda self: f"来自 {name} 的自定义方法"
return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=MyMeta):
pass

obj = MyClass()
print(obj.custom_method())  # 输出: "来自 MyClass 的自定义方法"
```

### 9. 魔法方法(Dunder Methods)
魔法方法能够让你自定义类的行为,例如实现运算符重载。
```python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)  # 输出: Vector(4, 6)
```

### 10. 函数式编程工具
Python 提供了 `map`、`filter`、`reduce` 等函数式编程工具。
```python
# map 函数:对可迭代对象的每个元素应用函数
numbers = [1, 2, 3, 4]
squares = list(map(lambda x: x**2, numbers))  # 结果: [1, 4, 9, 16]

# filter 函数:过滤可迭代对象中的元素
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))  # 结果: [2, 4]

# reduce 函数(需要从 functools 导入):累积计算
from functools import reduce
total = reduce(lambda x, y: x + y, numbers)  # 结果: 10 (1+2+3+4)
```

以上就是 Python 高级语法的核心内容,掌握这些知识会让你的代码更具专业性和高效性。
~~~

### studentmanangement

~~~markdown
下面我将分模块详细讲解这个学生成绩管理系统,每个模块都会用简单的语言和示例说明,帮助你逐步理解整个系统的设计和实现。


### **1. 基础概念:类与对象**
类是创建对象的蓝图,就像“学生”是一个类,而“小明”是这个类的一个具体对象。

```python
class Student:
def __init__(self, name, score):
self.name = name  # 学生姓名
self.score = score  # 学生成绩

# 创建对象
student1 = Student("小明", 90)
student2 = Student("小红", 85)

print(student1.name)  # 输出: 小明
print(student2.score)  # 输出: 85
```


### **2. 装饰器:增强函数功能**
装饰器就像“包装器”,可以在不修改原函数的情况下,为其添加额外功能(如验证、日志)。

```python
# 定义一个简单的装饰器:打印函数执行时间
def timer_decorator(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)  # 执行原函数
end = time.time()
print(f"{func.__name__} 运行耗时: {end - start} 秒")
return result
return wrapper

@timer_decorator  # 使用装饰器
def calculate_sum(n):
return sum(range(n + 1))

print(calculate_sum(1000000))  # 输出结果并显示耗时
```


### **3. 上下文管理器:自动管理资源**
上下文管理器用于自动处理资源的分配和释放(如打开/关闭文件),通常与 `with` 语句一起使用。

```python
# 自定义一个简单的计时器上下文管理器
class Timer:
def __enter__(self):
import time
self.start = time.time()
print("开始计时")
return self

def __exit__(self, exc_type, exc_val, exc_tb):
end = time.time()
print(f"计时结束,耗时: {end - self.start} 秒")

# 使用上下文管理器
with Timer():
sum = 0
for i in range(1000000):
sum += i
```


### **4. 数据模型:学生类**
这是系统的核心数据结构,用于表示每个学生的信息。

```python
class Student:
def __init__(self, name, score, **kwargs):
self.name = name  # 学生姓名
self.score = score  # 学生成绩
self.attributes = kwargs  # 其他属性(如年龄、班级)

    def to_dict(self):
# 将学生对象转换为字典(用于保存到文件)
return {
'name': self.name,
'score': self.score,
**self.attributes
}

# 创建带额外属性的学生
student = Student("小李", 92, age=18, class_="高三一班")
print(student.to_dict())  # 输出: {'name': '小李', 'score': 92, 'age': 18, 'class_': '高三一班'}
```


### **5. 成绩管理器:核心功能**
这个类负责管理所有学生数据,提供增删改查等功能。

```python
class GradeManager:
def __init__(self, filename="grades.json"):
self.filename = filename  # 数据存储文件
self.students = []  # 学生列表
self._load_data()  # 从文件加载数据

    def _load_data(self):
# 从JSON文件加载数据
try:
with open(self.filename, 'r') as f:
self.students = [Student(**data) for data in json.load(f)]
except (FileNotFoundError, json.JSONDecodeError):
self.students = []  # 文件不存在或数据错误时初始化为空

    def add_student(self, name, score, **kwargs):
# 添加学生
student = Student(name, score, **kwargs)
self.students.append(student)
self._save_data()  # 保存到文件
return student

    def _save_data(self):
# 保存数据到JSON文件
with open(self.filename, 'w') as f:
json.dump([student.to_dict() for student in self.students], f)

# 使用示例
manager = GradeManager()
manager.add_student("小王", 88, age=17)
print(len(manager.students))  # 输出: 1
```


### **6. 统计功能:计算平均分、最高分等**
使用不同的处理器计算成绩统计信息。

```python
class BasicStatsProcessor:
def process(self, grades):
if not grades:
return {'average': 0, 'max': 0, 'min': 0}
return {
'average': sum(grades) / len(grades),
'max': max(grades),
'min': min(grades)
}

# 使用处理器
processor = BasicStatsProcessor()
stats = processor.process([90, 85, 92])
print(stats)  # 输出: {'average': 89.0, 'max': 92, 'min': 85}
```


### **7. 命令行界面:用户交互**
提供简单的命令行操作,让用户可以通过输入命令管理学生数据。

```python
class CLI:
def __init__(self, manager):
self.manager = manager
self.commands = {
'add': self.add_student,
'list': self.list_students,
'exit': self.exit
}

    def run(self):
print("欢迎使用成绩管理系统!")
while True:
command = input("\n> ").strip().lower()
if command in self.commands:
self.commands[command]()
else:
print("未知命令,请重试!")

    def add_student(self):
name = input("请输入姓名: ")
score = float(input("请输入成绩: "))
self.manager.add_student(name, score)
print(f"已添加学生: {name}")

    def list_students(self):
for student in self.manager.students:
print(f"{student.name}: {student.score}")

    def exit(self):
print("再见!")
exit()

# 启动命令行界面
cli = CLI(manager)
cli.run()
```


### **完整系统工作流程**
1. **初始化**:从文件加载学生数据。
2. **用户操作**:通过命令行界面接收用户指令(添加、查询等)。
3. **数据处理**:调用 `GradeManager` 处理数据,并自动保存到文件。
4. **统计分析**:使用处理器计算成绩统计信息。

这个系统的每个模块都有明确的职责,通过组合这些模块,实现了一个功能完整、结构清晰的成绩管理系统。

~~~

### 多进程

~~~markdown
好的,我将按模块详细讲解 Python 多进程的核心概念和使用方法,并结合示例代码说明。以下是分模块的深度解析:


### 一、基础模块:`multiprocessing.Process`
这是最基本的进程创建方式,类似线程的 `threading.Thread`。

#### 核心功能:
- 创建独立的进程
- 支持参数传递
- 提供进程生命周期管理(启动、等待、终止)

#### 关键方法:
- `start()`:启动进程执行
- `join([timeout])`:阻塞主进程,等待子进程结束(可设置超时)
- `terminate()`:强制终止进程
- `is_alive()`:检查进程是否运行中
- `name`/`pid`:进程名称和进程ID

#### 示例代码:
```python
from multiprocessing import Process
import os

def worker_function(name, delay):
print(f"进程 {name} (PID: {os.getpid()}) 启动")
time.sleep(delay)
print(f"进程 {name} 完成")

if __name__ == "__main__":
# 创建进程对象
p1 = Process(target=worker_function, args=("任务A", 2))
p2 = Process(target=worker_function, args=("任务B", 1))

# 启动进程
p1.start()
p2.start()

# 等待进程结束(按顺序)
p1.join()  # 等待p1完成
print("p1已结束")
p2.join()  # 等待p2完成
print("p2已结束")

# 检查进程状态
print(f"p1状态: {'运行中' if p1.is_alive() else '已结束'}")
```

#### 执行流程:
1. 主进程创建两个子进程 `p1` 和 `p2`
2. `p1` 和 `p2` 并行启动(实际取决于CPU核心数)
3. 主进程按顺序调用 `join()` 等待子进程结束
4. 输出进程状态信息


### 二、进程间通信(IPC):`multiprocessing.Queue`
进程间无法直接共享内存,`Queue` 提供了线程和进程安全的通信方式。

#### 核心特点:
- 基于管道和锁实现
- 支持 `put()` 和 `get()` 操作
- 可设置阻塞/非阻塞模式
- 自动处理序列化(pickle)

#### 示例代码:
```python
from multiprocessing import Process, Queue

def producer(q):
for i in range(5):
q.put(f"数据-{i}")
print(f"生产者放入: {i}")
time.sleep(0.5)

def consumer(q):
while True:
data = q.get()  # 阻塞等待
if data is None:  # 结束信号
break
print(f"消费者取出: {data}")

if __name__ == "__main__":
q = Queue()

# 创建生产者和消费者进程
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))

p1.start()
p2.start()

# 等待生产者完成
p1.join()

# 发送结束信号给消费者
q.put(None)
p2.join()
```

#### 关键方法:
- `q.put(obj[, block[, timeout]])`:放入数据(可设置超时)
- `q.get([block[, timeout]])`:获取数据(可设置超时)
- `q.empty()`/`q.full()`:检查队列状态
- `q.qsize()`:获取队列中元素数量(不可靠)


### 三、进程池:`multiprocessing.Pool`
适用于批量任务处理,自动管理进程生命周期。

#### 核心参数:
- `processes`:进程池大小(默认使用 CPU 核心数)
- `initializer`:进程初始化函数
- `maxtasksperchild`:每个进程最大任务数(超过后重启进程)

#### 主要方法:
- `map(func, iterable[, chunksize])`:批量执行任务(阻塞式)
- `imap()`:返回迭代器(非阻塞)
- `apply_async(func[, args[, kwds[, callback]]])`:异步执行单个任务
- `close()`:关闭进程池,不再接受新任务
- `terminate()`:立即终止所有进程
- `join()`:等待所有进程完成(需先调用 `close()`)

#### 示例代码:
```python
from multiprocessing import Pool

def square(x):
print(f"处理 {x} (PID: {os.getpid()})")
return x * x

if __name__ == "__main__":
with Pool(processes=4) as pool:
# 方式1:map (阻塞直到所有结果完成)
results = pool.map(square, range(10))
print("map结果:", results)

# 方式2:imap (返回迭代器,可逐个处理结果)
for result in pool.imap(square, range(10)):
print(f"获取结果: {result}")

# 方式3:异步apply (适用于不确定参数的任务)
async_results = [pool.apply_async(square, args=(i,)) for i in range(10)]

# 获取异步结果
async_results = [res.get() for res in async_results]
print("异步结果:", async_results)
```

#### 执行流程:
1. 创建包含 4 个进程的进程池
2. `map()` 方法将任务分发给进程池中的进程
3. 进程池自动管理进程的创建、复用和销毁
4. 不同方法适用于不同场景(批量处理 vs 实时处理)


### 四、共享内存:`multiprocessing.Manager`
用于创建可在进程间共享的对象(如字典、列表)。

#### 核心机制:
- 通过后台服务器进程管理共享对象
- 客户端进程通过代理对象访问共享数据
- 内部使用 `pickle` 序列化和反序列化数据

#### 支持的共享对象类型:
- `Manager().list()`:共享列表
- `Manager().dict()`:共享字典
- `Manager().Namespace()`:共享命名空间
- `Manager().Lock()`/`Manager().Semaphore()`:共享同步原语

#### 示例代码:
```python
from multiprocessing import Process, Manager

def update_shared_data(d, l, n):
# 修改共享字典
d[n] = os.getpid()

# 修改共享列表
l.append(n)

# 修改共享命名空间
n.value += 1

if __name__ == "__main__":
with Manager() as manager:
# 创建共享对象
shared_dict = manager.dict()
shared_list = manager.list()
shared_ns = manager.Namespace()
shared_ns.value = 0  # 初始值

# 创建多个进程修改共享数据
processes = []
for i in range(5):
p = Process(target=update_shared_data, args=(shared_dict, shared_list, shared_ns))
processes.append(p)
p.start()

# 等待所有进程完成
for p in processes:
p.join()

# 查看共享数据的最终状态
print("共享字典:", shared_dict)
print("共享列表:", shared_list)
print("共享计数器:", shared_ns.value)
```

#### 注意事项:
- 共享对象的操作比本地对象慢(涉及进程间通信)
- 避免在共享对象中存储大对象(如大型数组)
- 复杂对象可能需要自定义序列化方法


### 五、同步原语:锁、信号量等
用于控制对共享资源的访问,避免竞态条件。

#### 常用同步工具:
- `Lock()`:互斥锁(一次只允许一个进程访问)
- `RLock()`:可重入锁(允许同一进程多次获取)
- `Semaphore(value)`:信号量(控制同时访问的进程数量)
- `Barrier(n)`:屏障(等待 n 个进程到达后继续执行)

#### 示例代码:
```python
from multiprocessing import Process, Manager, Lock
import time

def worker_with_lock(lock, shared_list):
with lock:  # 获取锁
print(f"进程 {os.getpid()} 获取锁")
shared_list.append(os.getpid())
time.sleep(1)  # 模拟耗时操作
print(f"进程 {os.getpid()} 释放锁")

if __name__ == "__main__":
with Manager() as manager:
shared_list = manager.list()
lock = Lock()

processes = []
for _ in range(3):
p = Process(target=worker_with_lock, args=(lock, shared_list))
processes.append(p)
p.start()

for p in processes:
p.join()

print("最终结果:", shared_list)
```

#### 执行流程:
1. 三个进程尝试访问共享列表
2. 通过 `Lock` 确保每次只有一个进程修改列表
3. 所有进程按顺序执行,避免数据竞争
4. 最终共享列表包含所有进程的 PID


### 六、进程间高级通信:`Pipe`
`Pipe` 提供了双向通信通道,返回一对连接对象。

#### 核心特点:
- 基于操作系统的管道实现
- 返回两个连接对象 `(conn1, conn2)`
- 默认是双向的(也可设置为单向)
- 适用于简单的进程间一对一通信

#### 示例代码:
```python
from multiprocessing import Process, Pipe

def sender(conn):
messages = ["你好", "世界", "再见"]
for msg in messages:
conn.send(msg)  # 发送消息
print(f"发送: {msg}")
time.sleep(0.5)
conn.close()  # 关闭连接

def receiver(conn):
while True:
msg = conn.recv()  # 接收消息(阻塞)
if not msg:  # 连接关闭时返回空
break
print(f"接收: {msg}")

if __name__ == "__main__":
# 创建管道,返回两个连接对象
parent_conn, child_conn = Pipe()

# 创建发送者和接收者进程
p1 = Process(target=sender, args=(child_conn,))
p2 = Process(target=receiver, args=(parent_conn,))

p1.start()
p2.start()

p1.join()  # 等待发送者完成
parent_conn.close()  # 关闭父连接
p2.join()  # 等待接收者完成
```

#### 关键方法:
- `conn.send(obj)`:发送对象(需可序列化)
- `conn.recv()`:接收对象(阻塞直到有数据)
- `conn.close()`:关闭连接
- `conn.poll([timeout])`:检查是否有数据可接收


### 七、应用场景与最佳实践

#### 1. CPU密集型任务
适合使用多进程充分利用多核CPU:
```python
from multiprocessing import Pool
import math

def compute_prime(n):
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True

if __name__ == "__main__":
with Pool() as pool:
results = pool.map(compute_prime, range(1, 100000))
print(f"质数数量: {sum(results)}")
```

#### 2. 并行IO任务
适用于同时处理多个IO操作:
```python
from multiprocessing import Process
import requests

def download(url):
response = requests.get(url)
print(f"下载完成: {url}, 大小: {len(response.content)}")

if __name__ == "__main__":
urls = [
"https://example.com/file1",
"https://example.com/file2",
"https://example.com/file3"
]

processes = [Process(target=download, args=(url,)) for url in urls]

for p in processes:
p.start()

for p in processes:
p.join()
```

#### 3. 长时间运行的服务
使用进程池管理常驻服务:
```python
from multiprocessing import Pool

def server_worker(client_id):
print(f"处理客户端 {client_id}")
# 模拟长时间服务
time.sleep(10)
print(f"客户端 {client_id} 处理完成")

if __name__ == "__main__":
with Pool(processes=5) as pool:
# 模拟接收10个客户端请求
for i in range(10):
pool.apply_async(server_worker, args=(i,))

# 保持服务运行
pool.close()  # 关闭进程池,不再接受新任务
pool.join()   # 等待所有任务完成
```


### 八、注意事项与常见问题

1. **Windows系统限制**:
- 必须在 `if __name__ == "__main__":` 内启动进程
- 避免使用全局变量(Windows无法正确复制)

2. **资源管理**:
- 长时间运行的进程池可能导致内存泄漏
- 使用 `with` 语句自动管理进程池生命周期

3. **调试困难**:
- 多进程程序调试比单进程复杂
- 建议先测试单进程版本,再扩展到多进程

4. **性能考量**:
- 进程创建和销毁开销较大,避免频繁创建
- 进程数量超过CPU核心数可能导致性能下降

5. **数据序列化**:
- 进程间传递的对象必须可序列化(可被 `pickle` 处理)
- 避免传递大型数据结构(通过共享内存或文件)


### 总结
Python 的 `multiprocessing` 模块提供了丰富的工具来创建和管理多进程:
- **基础模块**:`Process` 类创建独立进程
- **通信机制**:`Queue` 和 `Pipe` 实现进程间数据传输
- **资源共享**:`Manager` 创建可共享的对象
- **批量处理**:`Pool` 高效管理进程池
- **同步控制**:锁、信号量等避免资源竞争

合理使用多进程可以显著提升程序性能,尤其适合 CPU 密集型任务和需要隔离的操作。
~~~

### request基础

~~~markdown
下面我将分模块详细讲解这个基于 `requests` 库的 HTTP 请求示例代码,帮助你理解每个部分的功能和用法。


### **1. 导入模块**
```python
import requests
```
- `requests` 是 Python 中最流行的 HTTP 客户端库,用于发送各种 HTTP 请求(GET、POST 等)。
- 使用前需要先安装:`pip install requests`。


### **2. 基本 GET 请求**
```python
def example1_basic_get():
response = requests.get('https://api.github.com/users/octocat')

if response.status_code == 200:
data = response.json()  # 解析 JSON 响应
print(f"用户名: {data['login']}")
else:
print(f"请求失败,状态码: {response.status_code}")
```
- **关键点**:
- `requests.get(url)` 发送 GET 请求。
- `response.status_code` 检查响应状态(200 表示成功)。
- `response.json()` 解析 JSON 数据为 Python 字典。


### **3. 带参数的 GET 请求**
```python
def example2_get_with_params():
params = {
'q': 'python',
'page': 1,
'per_page': 20
}
response = requests.get('https://api.github.com/search/repositories', params=params)

if response.status_code == 200:
data = response.json()
print(f"搜索结果总数: {data['total_count']}")
```
- **关键点**:
- `params` 参数接收字典,自动将其编码为 URL 参数(如 `?q=python&page=1`)。
- 适用于需要传递查询参数的 API(如搜索、分页)。


### **4. 基本 POST 请求**
```python
def example3_basic_post():
data = {
'username': 'test_user',
'password': 'test_password'
}
response = requests.post('https://httpbin.org/post', data=data)

if response.status_code == 200:
print(response.json()['form'])  # httpbin 返回我们发送的数据
```
- **关键点**:
- `requests.post(url, data=...)` 发送 POST 请求。
- `data` 参数默认编码为表单数据(`application/x-www-form-urlencoded`)。


### **5. JSON 格式的 POST 请求**
```python
def example4_post_json():
json_data = {
'name': 'John Doe',
'age': 30
}
response = requests.post('https://httpbin.org/post', json=json_data)

if response.status_code == 200:
print(response.json()['json'])  # 服务器返回的 JSON
```
- **关键点**:
- `json` 参数直接发送 JSON 数据(自动设置 `Content-Type: application/json`)。
- 现代 API 更喜欢使用 JSON 格式。


### **6. 设置请求头(Headers)**
```python
def example5_request_headers():
headers = {
'User-Agent': 'Custom User Agent',
'Accept': 'application/json'
}
response = requests.get('https://api.github.com/user', headers=headers)
```
- **关键点**:
- `headers` 参数接收字典,用于设置请求头。
- 常见用途:伪装浏览器(User-Agent)、指定响应格式(Accept)。


### **7. 处理响应内容**
```python
def example6_response_content():
response = requests.get('https://www.baidu.com')

print(f"文本内容长度: {len(response.text)}")  # 文本内容
# binary_content = response.content  # 二进制内容(适用于图片、文件)

print("响应头:")
for key, value in response.headers.items():
print(f"  {key}: {value}")
```
- **关键点**:
- `response.text`:获取解码后的文本内容(自动处理编码)。
- `response.content`:获取原始二进制内容(用于下载文件、图片)。
- `response.headers`:获取响应头(如 `Content-Type`、`Server`)。


### **8. 设置超时和错误处理**
```python
def example7_timeout_and_error_handling():
try:
response = requests.get('https://httpbin.org/delay/2', timeout=(3, 5))

except requests.exceptions.Timeout:
print("请求超时")
except requests.exceptions.RequestException as e:
print(f"网络错误: {e}")
```
- **关键点**:
- `timeout=(连接超时, 读取超时)`:避免程序无限等待。
- 异常处理:捕获 `Timeout`、`RequestException` 等错误。


### **9. 会话(Session)的使用**
```python
def example8_session_usage():
session = requests.Session()
session.headers.update({'User-Agent': 'Custom User Agent'})

# 第一次请求(设置 Cookies)
response1 = session.get('https://httpbin.org/cookies/set?name=value')
print(f"第一次请求Cookies: {response1.cookies}")

# 第二次请求(自动携带 Cookies)
response2 = session.get('https://httpbin.org/cookies')
print(f"第二次请求Cookies: {response2.json()['cookies']}")

session.close()  # 释放资源
```
- **关键点**:
- `Session` 对象会自动保持 Cookies 和会话状态。
- 适用于需要登录或保持状态的场景(如爬虫)。


### **总结:HTTP 请求核心概念**
1. **请求方法**:
- `GET`:获取资源(最常用)。
- `POST`:提交数据(如表单、JSON)。
- 其他:`PUT`(更新)、`DELETE`(删除)等。

2. **请求参数**:
- `params`:GET 请求的查询参数。
- `data`:POST 请求的表单数据。
- `json`:POST 请求的 JSON 数据。
- `headers`:自定义请求头。

3. **响应处理**:
- `status_code`:检查请求状态。
- `json()`:解析 JSON 响应。
- `text`:获取文本内容。
- `content`:获取二进制内容。

4. **高级特性**:
- `Session`:保持会话状态(如 Cookies)。
- `timeout`:设置请求超时。
- 异常处理:确保程序健壮性。


### **常见应用场景**
- **API 调用**:获取或提交数据到 Web API。
- **网络爬虫**:抓取网页内容。
- **文件下载**:通过 `content` 下载二进制文件。
- **自动化测试**:模拟用户请求测试 Web 服务。

通过这些示例,你可以掌握使用 `requests` 库进行各种 HTTP 请求的基本方法,以及如何处理响应和异常。

~~~

### request高级

~~~markdown
下面我将分模块详细讲解这份基于 `requests` 库的高级 HTTP 请求示例代码,每个模块都会聚焦核心功能和使用场景,帮助你理解进阶用法。


### **1. Session 会话管理(example1_session_management)**
**核心功能**:创建持久会话,自动保持 Cookies 和请求状态,适用于需要连续操作的场景(如登录后访问多个页面)。

```python
# 创建会话对象(类似浏览器的"会话"概念)
session = requests.Session()

# 会话级配置(所有请求共享)
session.headers.update({'User-Agent': '自定义浏览器标识'})  # 统一设置请求头

# 第一次请求:登录(会自动保存 Cookies)
login_response = session.post('https://httpbin.org/post', data={'username': 'test'})

# 第二次请求:同一会话自动携带 Cookies
protected_response = session.get('https://httpbin.org/cookies')

session.close()  # 结束会话,释放资源
```

**关键知识点**:
- `Session` 解决了多次请求时重复传递 Cookies、请求头的问题。
- 常用于模拟用户登录后的连续操作(如电商网站下单流程)。
- 会话会自动管理连接池,减少重复建立连接的开销。


### **2. 代理设置(example2_proxy_usage)**
**核心功能**:通过代理服务器发送请求,用于隐藏真实 IP 或访问受限资源。

```python
# 代理配置字典(键为协议,值为代理地址)
proxies = {
'http': 'http://代理IP:端口',  # HTTP代理
'https': 'http://用户名:密码@代理IP:端口'  # 带认证的HTTPS代理
}

# 使用代理发送请求
response = requests.get('https://httpbin.org/ip', proxies=proxies, timeout=5)
```

**关键知识点**:
- 代理格式:`协议://[用户名:密码@]代理IP:端口`。
- 用途:爬虫反反爬(隐藏真实 IP)、访问地区限制资源(如国外网站)。
- 注意:免费代理稳定性差,生产环境建议使用付费代理。


### **3. SSL 证书验证(example3_ssl_verification)**
**核心功能**:处理 HTTPS 证书验证,解决自签名证书或过期证书的访问问题。

```python
# 1. 默认行为:验证证书(推荐生产环境使用)
response = requests.get('https://github.com')  # 正规网站证书有效

# 2. 禁用证书验证(不推荐,仅测试用)
response = requests.get('https://自签名证书网站', verify=False)

# 3. 指定自定义证书(推荐处理自签名证书的方式)
response = requests.get('https://自签名证书网站', verify='本地证书路径.pem')
```

**关键知识点**:
- HTTPS 网站依赖 SSL 证书确保通信安全,`requests` 默认会验证证书有效性。
- 访问自签名证书网站时,需通过 `verify=False` 禁用验证(会有警告),或通过 `verify` 指定证书文件。
- 生产环境中禁用验证存在安全风险,可能遭受中间人攻击。


### **4. 请求重试机制(example4_retry_mechanism)**
**核心功能**:自动重试失败的请求(如服务器临时错误),提高请求稳定性。

```python
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

# 创建会话
session = requests.Session()

# 配置重试策略
retry_strategy = Retry(
total=3,  # 最大重试3次
backoff_factor=1,  # 重试间隔:1秒 → 2秒 → 4秒(指数退避)
status_forcelist=[500, 502, 503, 504],  # 对这些状态码重试(服务器错误)
allowed_methods=["GET", "POST"]  # 允许重试的请求方法
)

# 将重试策略绑定到适配器
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)  # 对HTTPS请求生效
session.mount("http://", adapter)   # 对HTTP请求生效

# 使用带重试的会话发送请求
response = session.get('https://可能临时出错的网站')
```

**关键知识点**:
- 适用场景:服务器偶尔抽风(5xx 错误)、网络波动导致的请求失败。
- 退避策略(`backoff_factor`):避免重试请求集中冲击服务器,间隔随重试次数递增。
- 注意:`POST` 等有副作用的请求重试需谨慎(可能导致重复提交)。


### **5. 流式请求(example5_streaming_request)**
**核心功能**:分块接收响应内容,用于下载大文件(避免内存溢出)。

```python
url = 'https://example.com/大文件.zip'

# 开启流式模式(stream=True)
with requests.get(url, stream=True, timeout=10) as response:
response.raise_for_status()  # 检查请求是否成功

# 写入文件(每次读取8KB)
with open('本地文件名.zip', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):  # 分块迭代内容
if chunk:  # 过滤空块
f.write(chunk)
```

**关键知识点**:
- `stream=True` 让 `requests` 不一次性加载全部内容到内存,而是按需读取。
- `iter_content(chunk_size)` 分块获取内容,`chunk_size` 建议设为 8KB~1MB。
- 适用于下载大文件(如视频、安装包),避免内存不足报错。


### **6. HTTP 基本认证(example6_http_basic_auth)**
**核心功能**:处理采用 HTTP Basic Auth 认证的网站或 API。

```python
from requests.auth import HTTPBasicAuth

# 方式1:使用 HTTPBasicAuth 类
auth = HTTPBasicAuth('用户名', '密码')
response = requests.get('https://需要认证的网站', auth=auth)

# 方式2:简化写法(直接传元组)
response = requests.get('https://需要认证的网站', auth=('用户名', '密码'))
```

**关键知识点**:
- HTTP 基本认证是最简单的认证方式,用户名和密码会经过 Base64 编码(可解密,安全性低)。
- 适用于内部系统或简单 API 的身份验证。
- 生产环境建议使用更安全的认证方式(如 Token、OAuth2)。


### **7. 自定义请求钩子(example7_custom_hooks)**
**核心功能**:在请求发送前或响应返回后插入自定义逻辑(如日志、监控)。

```python
# 定义请求钩子(发送前执行)
def log_request(r, *args, **kwargs):
print(f"发送请求到: {r.url}")

# 定义响应钩子(接收后执行)
def log_response(r, *args, **kwargs):
print(f"收到响应,状态码: {r.status_code}")

# 注册钩子并发送请求
response = requests.get(
'https://httpbin.org/get',
hooks={
'request': [log_request],  # 请求钩子列表
'response': [log_response]  # 响应钩子列表
}
)
```

**关键知识点**:
- 钩子是一种"回调函数"机制,用于在请求生命周期的特定节点执行额外操作。
- 常见用途:记录请求日志、计算响应时间、自动重试(替代重试机制)、统一错误处理。
- 钩子可以是多个函数的列表,按顺序执行。


### **8. 处理重定向(example8_handling_redirects)**
**核心功能**:控制请求是否自动跟随重定向(3xx 状态码)。

```python
# 方式1:默认自动跟随重定向(allow_redirects=True)
response = requests.get('https://httpbin.org/redirect/3')  # 会自动跳转3次
print("最终URL:", response.url)  # 重定向后的最终地址
print("重定向历史:", [r.status_code for r in response.history])  # 记录跳转过程

# 方式2:禁止自动重定向
response = requests.get('https://httpbin.org/redirect/3', allow_redirects=False)
print("状态码:", response.status_code)  # 会返回302(重定向)而不跳转
```

**关键知识点**:
- 重定向是服务器让客户端访问新地址的机制(状态码:301 永久重定向、302 临时重定向)。
- `response.history` 记录了重定向的历史(按顺序)。
- 禁止重定向常用于分析重定向链,或手动控制跳转逻辑。


### **总结:requests 高级功能适用场景**
| 功能               | 适用场景                                  | 核心价值                     |
|--------------------|-------------------------------------------|------------------------------|
| Session 会话       | 登录后连续操作、保持状态                  | 自动管理 Cookies 和连接池   |
| 代理设置           | 隐藏 IP、访问受限资源                     | 突破网络限制                 |
| SSL 证书验证       | 访问自签名证书网站                        | 平衡安全性和可用性           |
| 请求重试机制       | 网络不稳定、服务器偶尔出错                | 提高请求成功率               |
| 流式请求           | 下载大文件                                | 避免内存溢出                 |
| HTTP 基本认证      | 简单的身份验证                            | 快速实现认证逻辑             |
| 自定义钩子         | 日志记录、性能监控、统一处理逻辑          | 增强代码复用性和可维护性     |
| 处理重定向         | 分析跳转链、手动控制跳转                  | 灵活处理页面跳转逻辑         |

这些功能是 `requests` 库的进阶用法,掌握后可以应对更复杂的网络请求场景(如爬虫、API 集成、自动化测试等)。实际使用时,需根据具体需求组合这些功能,同时注意异常处理和资源释放(如关闭会话)。
~~~

### B树java算法

```Java
package com.itheima.BTree;

import java.util.ArrayList;
import java.util.List;

/**
* B树实现类 - 一种自平衡的多路搜索树,适用于大量数据的存储与高效检索
* 主要应用场景:数据库索引、文件系统等需要频繁磁盘I/O的场景

* B树的核心特性(基于阶数m):
* 1. 每个节点最多有m个子节点(即最多存储m-1个关键字)
* 2. 非根节点至少有⌈m/2⌉个子节点(即至少存储⌈m/2⌉-1个关键字)
* 3. 根节点至少有2个子节点(除非它是叶子节点,此时可以只有0个或1个关键字)
* 4. 所有叶子节点处于同一层,保证查询效率稳定
* 5. 节点内的关键字按升序排列,子节点范围由父节点关键字界定
*/
public class BTree<K extends Comparable<K>> {
// B树的阶数(m表示每个节点最多能有m个子节点)
// 静态变量:所有B树实例共享阶数定义(此处按类设计,也可改为实例变量)
private static int m = 0;
// B树的根节点(整个树的入口)
private Node<K> root;

    /**
* 构造函数 - 初始化B树的阶数
* @param m 阶数(必须≥3,否则无法满足B树的平衡性要求)
*/
public BTree(int m) {
// 校验阶数合法性:B树阶数至少为3(保证节点分裂/合并时有足够空间)
if (m < 3) {
throw new IllegalArgumentException("B树的阶数必须 >= 3(当前输入:" + m + ")");
}
this.m = m;
// 初始化根节点为叶子节点(空树的根节点是叶子节点)
this.root = new Node<>(true);
}

    /**
* 节点内部类 - 表示B树中的一个节点,存储关键字和子节点引用
*/
private static class Node<K extends Comparable<K>> {
// 标记当前节点是否为叶子节点(叶子节点无子孙节点)
boolean isLeaf;
// 存储关键字的列表(按升序排列)
List<K> keys;
// 存储子节点引用的列表(仅非叶子节点使用)
List<Node<K>> children;

        /**
* 节点构造函数
* @param isLeaf 是否为叶子节点
*/
public Node(boolean isLeaf) {
this.isLeaf = isLeaf;
this.keys = new ArrayList<>(); // 初始化关键字列表
this.children = new ArrayList<>(); // 初始化子节点列表(非叶子节点会用到)
}

        /**
* 判断节点是否已满(关键字数量达到上限)
* @return 已满返回true,否则返回false
*/
public boolean isFull() {
// 节点最多存储m-1个关键字(因为子节点数量=关键字数量+1,最多m个子节点)
return keys.size() == m - 1;
}
}

    /**
* 向B树中插入关键字
* @param key 要插入的关键字(必须实现Comparable接口,用于比较大小)
*/
public void insert(K key) {
Node<K> rootNode = root; // 获取当前根节点

        // 特殊情况:如果根节点已满,需要先分裂根节点(树高+1)
if (rootNode.isFull()) {
// 创建新的根节点(非叶子节点,因为它将包含原根节点作为子节点)
Node<K> newRoot = new Node<>(false);
// 更新树的根节点为新节点
root = newRoot;
// 将原根节点添加为新根的第一个子节点
newRoot.children.add(rootNode);
// 分裂原根节点(此时新根节点只有一个子节点,分裂后变为两个)
splitChild(newRoot, 0, rootNode);
// 向新根节点插入关键字(此时新根节点未满)
insertNonFull(newRoot, key);
} else {
// 根节点未满,直接插入
insertNonFull(rootNode, key);
}
}

    /**
* 向非满节点中插入关键字(核心插入逻辑)
* 前提:目标节点当前未满(关键字数量 < m-1)
* @param node 目标节点
* @param key 要插入的关键字
*/
private void insertNonFull(Node<K> node, K key) {
// 从节点的最后一个关键字开始向前遍历
int i = node.keys.size() - 1;

        // 情况1:如果是叶子节点,直接插入关键字(找到位置后添加)
if (node.isLeaf) {
// 向前找到第一个比key小的关键字位置(保持升序)
// 例如:节点关键字为[3,5,7],插入4时,i会停在3的位置(索引0)
while (i >= 0 && key.compareTo(node.keys.get(i)) < 0) {
i--; // 关键字比当前位置大,继续向前找
}
// 在i+1位置插入新关键字(此时i+1是第一个大于key的位置)
node.keys.add(i + 1, key);

// 情况2:如果是非叶子节点,需要找到对应的子节点递归插入
else {
// 向前找到子节点的索引(子节点i对应关键字i的左侧范围)
while (i >= 0 && key.compareTo(node.keys.get(i)) < 0) {
i--;
}
// 子节点索引为i+1(例如:关键字[3,5]对应子节点[0,1,2],key=4时i=0,子节点索引1)
i++;
Node<K> child = node.children.get(i); // 获取目标子节点

            // 如果子节点已满,先分裂子节点(保证插入时子节点未满)
if (child.isFull()) {
splitChild(node, i, child); // 分裂子节点
// 分裂后,原关键字会有一个提升到父节点,需要重新判断插入哪个子节点
if (key.compareTo(node.keys.get(i)) > 0) {
i++; // key大于提升的关键字,插入右侧新子节点
}
}
// 递归向子节点插入关键字(此时子节点已确保未满)
insertNonFull(node.children.get(i), key);
}
}

    /**
* 分裂子节点(当子节点已满时调用)
* 分裂逻辑:将满节点从中间分为两个节点,中间关键字提升到父节点
* @param parent 父节点(接收提升的关键字)
* @param index 子节点在父节点children列表中的索引
* @param child 要分裂的子节点(已满)
*/
private void splitChild(Node<K> parent, int index, Node<K> child) {
// 创建新节点(与原节点同为叶子/非叶子节点)
Node<K> newNode = new Node<>(child.isLeaf);
// 计算中间位置:m为阶数,中间索引为(m+1)/2 - 1(例如m=3时,中间索引为1)
int t = (m + 1) / 2; // 分裂点(原节点保留前t-1个关键字,新节点保留剩余的)

        // 步骤1:移动关键字到新节点
// 原节点的关键字从索引t开始(即中间关键字之后的)移到新节点
for (int i = t; i < child.keys.size(); i++) {
newNode.keys.add(child.keys.get(i));
}
// 从原节点中删除已移走的关键字
for (int i = child.keys.size() - 1; i >= t; i--) {
child.keys.remove(i);
}

        // 步骤2:如果是非叶子节点,还需要移动子节点
if (!child.isLeaf) {
// 原节点的子节点从索引t开始移到新节点
for (int i = t; i < child.children.size(); i++) {
newNode.children.add(child.children.get(i));
}
// 从原节点中删除已移走的子节点
for (int i = child.children.size() - 1; i >= t; i--) {
child.children.remove(i);
}
}

        // 步骤3:将中间关键字提升到父节点
parent.keys.add(index, child.keys.get(t - 1)); // 中间关键字是原节点的第t-1个
child.keys.remove(t - 1); // 从原节点删除中间关键字

        // 步骤4:将新节点添加到父节点的子节点列表(位于原节点右侧)
parent.children.add(index + 1, newNode);
}

    /**
* 从B树中删除关键字(对外接口)
* @param key 要删除的关键字
*/
public void delete(K key) {
// 如果树为空(根节点无关键字),直接返回
if (root.keys.isEmpty()) {
return;
}
// 调用内部删除方法,从根节点开始查找并删除
delete(root, key);

        // 特殊情况:如果根节点无关键字且非叶子节点,更新根节点为其唯一子节点
if (root.keys.isEmpty() && !root.isLeaf) {
root = root.children.get(0);
}
}

    /**
* 从指定节点开始删除关键字(核心删除逻辑)
* @param node 当前节点
* @param key 要删除的关键字
*/
private void delete(Node<K> node, K key) {
// 查找key在当前节点中的位置(返回第一个≥key的索引)
int index = findKeyIndex(node, key);

        // 情况A:关键字在当前节点中(index位置的关键字等于key)
if (index < node.keys.size() && key.compareTo(node.keys.get(index)) == 0) {
// 子情况A1:当前节点是叶子节点 → 直接删除关键字
if (node.isLeaf) {
node.keys.remove(index);

// 子情况A2:当前节点是非叶子节点 → 需要处理子节点
else {
// 获取左子节点(关键字index左侧的子节点)
Node<K> predecessorChild = node.children.get(index);
// 获取右子节点(关键字index右侧的子节点)
Node<K> successorChild = node.children.get(index + 1);

                // A2a:左子节点有足够的关键字(≥⌈m/2⌉-1)→ 用前驱关键字替换并删除前驱
if (predecessorChild.keys.size() >= (m + 1) / 2) {
// 找到左子树中最大的关键字(前驱)
K predecessor = getPredecessor(predecessorChild);
// 用前驱替换当前节点的关键字
node.keys.set(index, predecessor);
// 在左子节点中删除前驱关键字
delete(predecessorChild, predecessor);
}
// A2b:右子节点有足够的关键字 → 用后继关键字替换并删除后继
else if (successorChild.keys.size() >= (m + 1) / 2) {
// 找到右子树中最小的关键字(后继)
K successor = getSuccessor(successorChild);
// 用后继替换当前节点的关键字
node.keys.set(index, successor);
// 在右子节点中删除后继关键字
delete(successorChild, successor);
}
// A2c:左右子节点都不足 → 合并左右子节点,再删除
else {
// 合并左子节点、当前关键字、右子节点
merge(node, index, predecessorChild, successorChild);
// 在合并后的节点中删除关键字(此时关键字已在合并后的节点中)
delete(predecessorChild, key);
}
}

// 情况B:关键字不在当前节点中 → 需要到子节点中删除
else {
// 如果是叶子节点,说明关键字不存在(直接返回)
if (node.isLeaf) {
return;
}

            // 确定要搜索的子节点索引(index即为子节点索引)
int childIndex = index;
Node<K> child = node.children.get(childIndex);

            // 确保子节点有足够的关键字(≥⌈m/2⌉-1),否则先修复
if (child.keys.size() < (m + 1) / 2) {
fixShortage(node, childIndex, child);
}

            // 递归在子节点中删除关键字
delete(child, key);
}
}

    /**
* 查找关键字在节点中的索引(辅助方法)
* @param node 目标节点
* @param key 要查找的关键字
* @return 第一个≥key的关键字索引(如果都小于key,返回keys.size())
*/
private int findKeyIndex(Node<K> node, K key) {
int index = 0;
// 遍历关键字,找到第一个不小于key的位置
while (index < node.keys.size() && key.compareTo(node.keys.get(index)) > 0) {
index++;
}
return index;
}

    /**
* 获取指定节点的前驱关键字(左子树中的最大关键字)
* @param node 目标节点
* @return 前驱关键字
*/
private K getPredecessor(Node<K> node) {
// 一直向右子节点遍历,直到叶子节点(叶子节点的最后一个关键字即为最大)
while (!node.isLeaf) {
node = node.children.get(node.children.size() - 1);
}
return node.keys.get(node.keys.size() - 1);
}

    /**
* 获取指定节点的后继关键字(右子树中的最小关键字)
* @param node 目标节点
* @return 后继关键字
*/
private K getSuccessor(Node<K> node) {
// 一直向左子节点遍历,直到叶子节点(叶子节点的第一个关键字即为最小)
while (!node.isLeaf) {
node = node.children.get(0);
}
return node.keys.get(0);
}

    /**
* 修复子节点关键字不足的问题(当子节点关键字数量 < ⌈m/2⌉-1时)
* @param parent 父节点
* @param index 子节点在父节点中的索引
* @param child 关键字不足的子节点
*/
private void fixShortage(Node<K> parent, int index, Node<K> child) {
// 方案1:左兄弟有足够的关键字 → 从左兄弟借一个
if (index > 0 && parent.children.get(index - 1).keys.size() >= (m + 1) / 2) {
borrowFromLeftSibling(parent, index, child);
}
// 方案2:右兄弟有足够的关键字 → 从右兄弟借一个
else if (index < parent.children.size() - 1 && parent.children.get(index + 1).keys.size() >= (m + 1) / 2) {
borrowFromRightSibling(parent, index, child);
}
// 方案3:左右兄弟都不足 → 合并子节点与兄弟节点
else {
if (index > 0) {
// 与左兄弟合并
merge(parent, index - 1, parent.children.get(index - 1), child);
} else {
// 与右兄弟合并
merge(parent, index, child, parent.children.get(index + 1));
}
}
}

    /**
* 从左兄弟节点借一个关键字(修复子节点不足)
* @param parent 父节点
* @param index 子节点在父节点中的索引
* @param child 关键字不足的子节点
*/
private void borrowFromLeftSibling(Node<K> parent, int index, Node<K> child) {
Node<K> leftSibling = parent.children.get(index - 1); // 获取左兄弟

        // 步骤1:父节点中index-1位置的关键字下移到子节点的最前面
child.keys.add(0, parent.keys.get(index - 1));
// 步骤2:左兄弟的最后一个关键字上移到父节点的index-1位置
parent.keys.set(index - 1, leftSibling.keys.get(leftSibling.keys.size() - 1));
leftSibling.keys.remove(leftSibling.keys.size() - 1); // 左兄弟删除该关键字

        // 如果是非叶子节点,还需要移动子节点(保持子节点与关键字的对应关系)
if (!child.isLeaf) {
// 左兄弟的最后一个子节点移到当前子节点的最前面
child.children.add(0, leftSibling.children.get(leftSibling.children.size() - 1));
leftSibling.children.remove(leftSibling.children.size() - 1); // 左兄弟删除该子节点
}
}

    /**
* 从右兄弟节点借一个关键字(修复子节点不足)
* @param parent 父节点
* @param index 子节点在父节点中的索引
* @param child 关键字不足的子节点
*/
private void borrowFromRightSibling(Node<K> parent, int index, Node<K> child) {
Node<K> rightSibling = parent.children.get(index + 1); // 获取右兄弟

        // 步骤1:父节点中index位置的关键字下移到子节点的最后面
child.keys.add(parent.keys.get(index));
// 步骤2:右兄弟的第一个关键字上移到父节点的index位置
parent.keys.set(index, rightSibling.keys.get(0));
rightSibling.keys.remove(0); // 右兄弟删除该关键字

        // 如果是非叶子节点,还需要移动子节点
if (!child.isLeaf) {
// 右兄弟的第一个子节点移到当前子节点的最后面
child.children.add(rightSibling.children.get(0));
rightSibling.children.remove(0); // 右兄弟删除该子节点
}
}

    /**
* 合并两个子节点(当两者关键字都不足时)
* @param parent 父节点
* @param index 父节点中连接两个子节点的关键字索引
* @param left 左子节点
* @param right 右子节点
*/
private void merge(Node<K> parent, int index, Node<K> left, Node<K> right) {
// 步骤1:父节点中index位置的关键字下移到左子节点的最后
left.keys.add(parent.keys.get(index));
parent.keys.remove(index); // 父节点删除该关键字

        // 步骤2:右子节点的所有关键字合并到左子节点
left.keys.addAll(right.keys);
// 步骤3:右子节点的所有子节点合并到左子节点(非叶子节点时)
left.children.addAll(right.children);

        // 步骤4:父节点删除右子节点(此时左子节点已包含右子节点的所有内容)
parent.children.remove(index + 1);
}

    /**
* 搜索关键字是否存在于B树中(对外接口)
* @param key 要搜索的关键字
* @return 存在返回true,否则返回false
*/
public boolean search(K key) {
// 从根节点开始递归搜索
return search(root, key);
}

    /**
* 从指定节点开始搜索关键字(核心搜索逻辑)
* @param node 当前节点
* @param key 要搜索的关键字
* @return 存在返回true,否则返回false
*/
private boolean search(Node<K> node, K key) {
int i = 0;
// 找到第一个不小于key的关键字索引
while (i < node.keys.size() && key.compareTo(node.keys.get(i)) > 0) {
i++;
}

        // 情况1:找到匹配的关键字
if (i < node.keys.size() && key.compareTo(node.keys.get(i)) == 0) {
return true;
}

        // 情况2:已到达叶子节点但未找到 → 关键字不存在
if (node.isLeaf) {
return false;
}

        // 情况3:非叶子节点 → 递归搜索对应的子节点
return search(node.children.get(i), key);
}

    /**
* 打印B树结构(用于调试和可视化)
*/
public void printTree() {
// 从根节点开始递归打印,根节点层级为0
printTree(root, 0);
}

    /**
* 递归打印B树(辅助方法)
* @param node 当前节点
* @param level 节点所在层级(根节点为0,子节点+1)
*/
private void printTree(Node<K> node, int level) {
// 生成缩进(层级越深,缩进越多,方便查看树结构)
StringBuilder indent = new StringBuilder();
for (int i = 0; i < level; i++) {
indent.append("    "); // 每层缩进4个空格
}
// 打印当前节点的层级和关键字
System.out.print(indent);
System.out.print("Level " + level + ": ");
for (K key : node.keys) {
System.out.print(key + " ");
}
System.out.println(); // 换行

        // 如果是非叶子节点,递归打印所有子节点(层级+1)
if (!node.isLeaf) {
for (Node<K> child : node.children) {
printTree(child, level + 1);
}
}
}

    /**
* 测试B树的示例代码
*/
public static void main(String[] args) {
// 创建一个阶数为3的B树(即2-3树,每个节点最多2个关键字、3个子节点)
BTree<Integer> bTree = new BTree<>(3);

        // 插入测试数据(1-10),观察树结构变化
System.out.println("=== 插入测试 ===");
for (int i = 1; i <= 10; i++) {
bTree.insert(i);
System.out.println("插入 " + i + " 后的B树结构:");
bTree.printTree();
System.out.println(); // 空行分隔
}

        // 搜索功能测试
System.out.println("=== 搜索测试 ===");
System.out.println("搜索 5: " + bTree.search(5));  // 存在 → true
System.out.println("搜索 15: " + bTree.search(15)); // 不存在 → false

        // 删除功能测试
System.out.println("\n=== 删除测试 ===");
bTree.delete(3);
System.out.println("删除 3 后的B树结构:");
bTree.printTree();

        bTree.delete(7);
System.out.println("\n删除 7 后的B树结构:");
bTree.printTree();
}
}
```

```

```

```

```

```

```

```

```

```

```

```

```

```


```

http://www.dtcms.com/a/270093.html

相关文章:

  • C++笔记之使用bitset对uint32_t类型变量对位状态判断
  • 2025年深圳杉川机器人性格测评和Verify测评SHL题库高分攻略
  • 论文略读:Parameter-efficient transfer learning for NLP
  • InstructBLIP:迈向具备指令微调能力的通用视觉语言模型
  • Go语言标识符命名规则详解:工程化实践
  • Spring的依赖注入(xml)
  • RISC-V:开源芯浪潮下的技术突围与职业新赛道 (一)为什么RISC-V是颠覆性创新?
  • 安装 asciidoctor-vscode 最新版
  • 针对 SSD 固态硬盘的安全擦除 Secure Erase
  • Kotlin协程中的Job详解
  • 如何用Python编程计算权重?
  • Anolis OS 23 架构支持家族新成员:Anolis OS 23.3 版本及 RISC-V 预览版发布
  • 数据库设计精要:完整性和范式理论
  • 去掉长按遥控器power键后提示关机、飞行模式的弹窗
  • 数据提取之lxml模块与xpath工具
  • 基于Java+SpringBoot 协同过滤算法私人诊所管理系统
  • 系统架构设计师论文分享-论系统安全设计
  • IoTDB:专为物联网场景设计的高性能时序数据库
  • 把word中表格转成excle文件
  • 基于GeoTools的根据Shp文件生成完全包围格网实战
  • Oracle 存储过程、函数与触发器
  • AI标注平台label-studio之二添加机器学习后端模型辅助标注
  • vue3官方文档学习心得
  • SpringCloud系列 - Gateway 网关功能(五)
  • 人体坐姿检测系统开发实战(YOLOv8+PyTorch+可视化)
  • 本地部署 R 语言环境运行软件 RStudio Server 并实现外部访问
  • 玩具语音方案选型决策OTP vs Flash 的成本功耗与灵活性
  • window wsl 环境下编译openharmony,HarmonyOS 三方库 FFmpeg
  • VLLM 调用有哪些超参数; clean_up_tokenization_spaces是什么
  • ubuntu24.04安装NFS网络文件系统/ARM开发板NFS挂载