Merge PR #118: [moz] feat(algorithms): 三种排序算法实现最大值查找
This commit was merged in pull request #118.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
"""find_max_bubble — 冒泡排序法找最大值"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def find_max_bubble(nums: list[int | float]) -> int | float | None:
|
||||
"""冒泡排序后取最后一个元素,空列表返回 None。"""
|
||||
if not nums:
|
||||
return None
|
||||
arr = list(nums) # 不修改原列表
|
||||
n = len(arr)
|
||||
for i in range(n - 1):
|
||||
for j in range(n - 1 - i):
|
||||
if arr[j] > arr[j + 1]:
|
||||
arr[j], arr[j + 1] = arr[j + 1], arr[j]
|
||||
return arr[-1]
|
||||
@@ -1,9 +1,9 @@
|
||||
"""find_max — 从数字列表中查找最大值"""
|
||||
"""find_max_linear — 线性扫描法找最大值"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def find_max(nums: list[int | float]) -> int | float | None:
|
||||
def find_max_linear(nums: list[int | float]) -> int | float | None:
|
||||
"""返回列表中的最大值,空列表返回 None。"""
|
||||
if not nums:
|
||||
return None
|
||||
@@ -12,3 +12,7 @@ def find_max(nums: list[int | float]) -> int | float | None:
|
||||
if num > result:
|
||||
result = num
|
||||
return result
|
||||
|
||||
|
||||
# 兼容别名
|
||||
find_max = find_max_linear
|
||||
@@ -0,0 +1,41 @@
|
||||
"""find_max_quickselect — 快速选择 partition 思路找最大值"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
|
||||
|
||||
def find_max_quickselect(nums: list[int | float]) -> int | float | None:
|
||||
"""使用快速选择的 partition 思路直接找最大值,空列表返回 None。"""
|
||||
if not nums:
|
||||
return None
|
||||
arr = list(nums) # 不修改原列表
|
||||
return _quickselect_max(arr, 0, len(arr) - 1)
|
||||
|
||||
|
||||
def _quickselect_max(arr: list[int | float], lo: int, hi: int) -> int | float:
|
||||
"""在 arr[lo..hi] 中找最大值。
|
||||
|
||||
Lomuto partition: < pivot 左,>= pivot 右。
|
||||
pivot 落在 store 位置,最大值在 [store, hi]。
|
||||
"""
|
||||
if lo == hi:
|
||||
return arr[lo]
|
||||
|
||||
pivot_idx = random.randint(lo, hi)
|
||||
arr[pivot_idx], arr[hi] = arr[hi], arr[pivot_idx]
|
||||
|
||||
pivot = arr[hi]
|
||||
store = lo
|
||||
for i in range(lo, hi):
|
||||
if arr[i] < pivot:
|
||||
arr[store], arr[i] = arr[i], arr[store]
|
||||
store += 1
|
||||
arr[store], arr[hi] = arr[hi], arr[store]
|
||||
|
||||
# store 是 pivot 最终位置
|
||||
# 如果 store == hi,pivot 是当前区间最大值
|
||||
if store == hi:
|
||||
return arr[store]
|
||||
# 否则在 store+1..hi 中继续找(右侧都 >= pivot)
|
||||
return _quickselect_max(arr, store + 1, hi)
|
||||
@@ -1,12 +1,11 @@
|
||||
import pytest
|
||||
import os
|
||||
|
||||
if not os.environ.get("RUN_INTEGRATION"):
|
||||
pytest.skip("E2E tests require RUN_INTEGRATION=1", allow_module_level=True)
|
||||
|
||||
pytestmark = pytest.mark.e2e
|
||||
|
||||
skip_no_integration = pytest.mark.skipif(
|
||||
not __import__("os").environ.get("RUN_INTEGRATION"),
|
||||
reason="Set RUN_INTEGRATION=1 to run E2E tests against real daemon",
|
||||
)
|
||||
|
||||
"""v3.1 端到端测试 — 新增场景覆盖
|
||||
|
||||
覆盖 v3.1 新增功能:
|
||||
@@ -22,7 +21,6 @@ skip_no_integration = pytest.mark.skipif(
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
import time
|
||||
@@ -31,7 +29,6 @@ from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import pytest
|
||||
import requests as http_requests
|
||||
|
||||
# ── 路径设置 ──
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
pytestmark = [pytest.mark.e2e, pytest.mark.skipif(
|
||||
not os.environ.get("RUN_INTEGRATION"),
|
||||
reason="Set RUN_INTEGRATION=1 to run E2E tests",
|
||||
)]
|
||||
if not os.environ.get("RUN_INTEGRATION"):
|
||||
pytest.skip("E2E tests require RUN_INTEGRATION=1", allow_module_level=True)
|
||||
|
||||
pytestmark = [pytest.mark.e2e]
|
||||
|
||||
"""#01 四相循环 单元测试
|
||||
|
||||
@@ -23,13 +24,10 @@ pytestmark = [pytest.mark.e2e, pytest.mark.skipif(
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# ── 路径设置 ──
|
||||
DEPLOY_DIR = Path.home() / ".sanguo_projects" / "sanguo_moziplus_v2"
|
||||
SRC_DIR = DEPLOY_DIR / "src"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from src.algorithms.find_max import find_max
|
||||
from src.algorithms.find_max_linear import find_max
|
||||
|
||||
|
||||
class TestFindMax:
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""三种排序算法找最大值 — 单元测试"""
|
||||
|
||||
import pytest
|
||||
|
||||
from src.algorithms.find_max_linear import find_max_linear
|
||||
from src.algorithms.find_max_bubble import find_max_bubble
|
||||
from src.algorithms.find_max_quickselect import find_max_quickselect
|
||||
|
||||
ALL_ALGOS = [find_max_linear, find_max_bubble, find_max_quickselect]
|
||||
|
||||
|
||||
# ── 通用测试(每种算法都跑) ──
|
||||
|
||||
@pytest.mark.parametrize("algo", ALL_ALGOS, ids=["linear", "bubble", "quickselect"])
|
||||
class TestAllAlgorithms:
|
||||
def test_normal_list(self, algo):
|
||||
assert algo([3, 1, 4, 1, 5, 9, 2, 6]) == 9
|
||||
|
||||
def test_empty_list(self, algo):
|
||||
assert algo([]) is None
|
||||
|
||||
def test_single_element(self, algo):
|
||||
assert algo([42]) == 42
|
||||
|
||||
def test_negative_numbers(self, algo):
|
||||
assert algo([-5, -1, -10, -3]) == -1
|
||||
|
||||
def test_floats(self, algo):
|
||||
assert algo([1.5, 2.7, 0.3, 3.14]) == 3.14
|
||||
|
||||
def test_mixed_int_float(self, algo):
|
||||
assert algo([1, 2.5, 3, 0.1]) == 3
|
||||
|
||||
def test_duplicate_max(self, algo):
|
||||
assert algo([7, 7, 7]) == 7
|
||||
|
||||
|
||||
# ── 一致性测试(同输入三法结果相同) ──
|
||||
|
||||
class TestConsistency:
|
||||
@pytest.mark.parametrize("nums", [
|
||||
[3, 1, 4, 1, 5, 9, 2, 6],
|
||||
[-5, -1, -10, -3],
|
||||
[1.5, 2.7, 0.3, 3.14],
|
||||
[42],
|
||||
[7, 7, 7],
|
||||
[0, -0.0, 0.0],
|
||||
[100, 200, 50, 150, 75],
|
||||
])
|
||||
def test_three_algorithms_same_result(self, nums):
|
||||
results = [algo(nums) for algo in ALL_ALGOS]
|
||||
assert all(r == results[0] for r in results), f"Inconsistent: {results}"
|
||||
|
||||
def test_empty_consistency(self):
|
||||
results = [algo([]) for algo in ALL_ALGOS]
|
||||
assert all(r is None for r in results)
|
||||
|
||||
def test_does_not_mutate_input(self):
|
||||
original = [3, 1, 4, 1, 5, 9, 2, 6]
|
||||
for algo in ALL_ALGOS:
|
||||
nums = list(original)
|
||||
algo(nums)
|
||||
assert nums == original, f"{algo.__name__} mutated input"
|
||||
Reference in New Issue
Block a user