Qwen3-VL-8B-Instruct推理测试transformer+sglang双版本

Qwen3-VL-8B-Instruct推理测试transformer+sglang双版本

契机

Qwen3-VL-8B发布了,结合官方demo+github仓库issue,在h20显卡服务器上跑了一下,相当于qwen2.5-vl-8b还是有较大的提升。对官方demo进行了加强,输出了推理速度+显存占用+token统计等,跑是跑通了,有点小问题,小问题的处理应该也不麻烦,先记录下来。

说明

环境搭建

bash 复制代码
#driver+cuda略,多显卡遇到cuda识别不了的问题,可以翻我上一篇博客

#模型下载略,可参考我另外一篇《多模态视频大模型Aria在Docker部署》中的下载脚本

#创建conda环境,期间最好有梯子,涉及到github下载源码安装
conda create --name qwen3 python=3.11.14
pip install git+https://github.com/huggingface/transformers
pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple/ && pip install vllm  qwen-vl-utils --no-build-isolation -i https://pypi.tuna.tsinghua.edu.cn/simple/ 
pip install -U flash-attn --no-build-isolation -i https://pypi.tuna.tsinghua.edu.cn/simple/ 
pip install sglang -i https://pypi.tuna.tsinghua.edu.cn/simple/ 

#输出我自己conda环境(忽略)
conda env export --no-builds | grep -v "prefix" > qwen3_environment.yml

#可以参考下面我的yml安装
conda env create -f qwen3_environment.yml --name qwen3  # 确保名称与原环境一致
yaml 复制代码
name: qwen3
channels:
  - conda-forge
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
dependencies:
  - _libgcc_mutex=0.1
  - _openmp_mutex=4.5
  - bzip2=1.0.8
  - ca-certificates=2025.10.5
  - conda-pack=0.8.1
  - ld_impl_linux-64=2.44
  - libexpat=2.7.1
  - libffi=3.4.6
  - libgcc=15.2.0
  - libgcc-ng=15.2.0
  - libgomp=15.2.0
  - liblzma=5.8.1
  - libnsl=2.0.1
  - libsqlite=3.50.4
  - libuuid=2.41.2
  - libxcrypt=4.4.36
  - libzlib=1.3.1
  - ncurses=6.5
  - openssl=3.5.4
  - pip=25.2
  - python=3.11.14
  - readline=8.2
  - setuptools=80.9.0
  - tk=8.6.13
  - wheel=0.45.1
  - pip:
      - accelerate==1.10.1
      - aiohappyeyeballs==2.6.1
      - aiohttp==3.13.0
      - aiosignal==1.4.0
      - airportsdata==20250909
      - annotated-types==0.7.0
      - anthropic==0.71.0
      - anyio==4.11.0
      - apache-tvm-ffi==0.1.0b15
      - astor==0.8.1
      - asttokens==3.0.0
      - attrs==25.4.0
      - av==16.0.1
      - blake3==1.0.8
      - blobfile==3.0.0
      - build==1.3.0
      - cachetools==6.2.1
      - cbor2==5.7.0
      - certifi==2025.10.5
      - cffi==2.0.0
      - charset-normalizer==3.4.4
      - click==8.2.1
      - cloudpickle==3.1.1
      - compressed-tensors==0.11.0
      - cuda-bindings==13.0.2
      - cuda-pathfinder==1.3.1
      - cuda-python==13.0.2
      - cupy-cuda12x==13.6.0
      - datasets==4.2.0
      - decorator==5.2.1
      - depyf==0.19.0
      - dill==0.4.0
      - diskcache==5.6.3
      - distro==1.9.0
      - dnspython==2.8.0
      - docstring-parser==0.17.0
      - einops==0.8.1
      - email-validator==2.3.0
      - executing==2.2.1
      - fastapi==0.119.0
      - fastapi-cli==0.0.13
      - fastapi-cloud-cli==0.3.1
      - fastrlock==0.8.3
      - filelock==3.20.0
      - flash-attn==2.8.3
      - flashinfer-python==0.4.0
      - frozendict==2.4.6
      - frozenlist==1.8.0
      - fsspec==2025.9.0
      - gguf==0.17.1
      - grpcio==1.75.1
      - grpcio-reflection==1.75.1
      - grpcio-tools==1.75.1
      - h11==0.16.0
      - hf-transfer==0.1.9
      - hf-xet==1.1.10
      - httpcore==1.0.9
      - httptools==0.7.1
      - httpx==0.28.1
      - huggingface-hub==0.35.3
      - idna==3.11
      - interegular==0.3.3
      - ipython==9.6.0
      - ipython-pygments-lexers==1.1.1
      - jedi==0.19.2
      - jinja2==3.1.6
      - jiter==0.11.0
      - jsonschema==4.25.1
      - jsonschema-specifications==2025.9.1
      - lark==1.2.2
      - llguidance==0.7.30
      - llvmlite==0.44.0
      - lm-format-enforcer==0.11.3
      - lxml==6.0.2
      - markdown-it-py==4.0.0
      - markupsafe==3.0.3
      - matplotlib-inline==0.1.7
      - mdurl==0.1.2
      - mistral-common==1.8.5
      - modelscope==1.31.0
      - mpmath==1.3.0
      - msgpack==1.1.2
      - msgspec==0.19.0
      - multidict==6.7.0
      - multiprocess==0.70.16
      - nest-asyncio==1.6.0
      - networkx==3.5
      - ninja==1.13.0
      - numba==0.61.2
      - numpy==2.2.6
      - nvidia-cublas-cu12==12.8.4.1
      - nvidia-cuda-cupti-cu12==12.8.90
      - nvidia-cuda-nvrtc-cu12==12.8.93
      - nvidia-cuda-runtime-cu12==12.8.90
      - nvidia-cudnn-cu12==9.10.2.21
      - nvidia-cudnn-frontend==1.15.0
      - nvidia-cufft-cu12==11.3.3.83
      - nvidia-cufile-cu12==1.13.1.3
      - nvidia-curand-cu12==10.3.9.90
      - nvidia-cusolver-cu12==11.7.3.90
      - nvidia-cusparse-cu12==12.5.8.93
      - nvidia-cusparselt-cu12==0.7.1
      - nvidia-cutlass-dsl==4.2.1
      - nvidia-ml-py==13.580.82
      - nvidia-nccl-cu12==2.27.3
      - nvidia-nvjitlink-cu12==12.8.93
      - nvidia-nvshmem-cu12==3.3.20
      - nvidia-nvtx-cu12==12.8.90
      - openai==1.99.1
      - openai-harmony==0.0.4
      - opencv-python-headless==4.12.0.88
      - orjson==3.11.3
      - outlines==0.1.11
      - outlines-core==0.1.26
      - packaging==25.0
      - pandas==2.3.3
      - parso==0.8.5
      - partial-json-parser==0.2.1.1.post6
      - pexpect==4.9.0
      - pillow==12.0.0
      - prometheus-client==0.23.1
      - prometheus-fastapi-instrumentator==7.1.0
      - prompt-toolkit==3.0.52
      - propcache==0.4.1
      - protobuf==6.33.0
      - psutil==7.1.0
      - ptyprocess==0.7.0
      - pure-eval==0.2.3
      - py-cpuinfo==9.0.0
      - py-spy==0.4.1
      - pyarrow==21.0.0
      - pybase64==1.4.2
      - pycountry==24.6.1
      - pycparser==2.23
      - pycryptodomex==3.23.0
      - pydantic==2.12.2
      - pydantic-core==2.41.4
      - pydantic-extra-types==2.10.6
      - pygments==2.19.2
      - pyproject-hooks==1.2.0
      - python-dateutil==2.9.0.post0
      - python-dotenv==1.1.1
      - python-json-logger==4.0.0
      - python-multipart==0.0.20
      - pytz==2025.2
      - pyyaml==6.0.3
      - pyzmq==27.1.0
      - qwen-vl-utils==0.0.14
      - ray==2.50.0
      - referencing==0.37.0
      - regex==2025.9.18
      - requests==2.32.5
      - rich==14.2.0
      - rich-toolkit==0.15.1
      - rignore==0.7.1
      - rpds-py==0.27.1
      - safetensors==0.6.2
      - scipy==1.16.2
      - sentencepiece==0.2.1
      - sentry-sdk==2.42.0
      - setproctitle==1.3.7
      - sgl-kernel==0.3.15
      - sglang==0.5.3.post3
      - shellingham==1.5.4
      - six==1.17.0
      - sniffio==1.3.1
      - soundfile==0.13.1
      - soxr==1.0.0
      - stack-data==0.6.3
      - starlette==0.48.0
      - sympy==1.14.0
      - tabulate==0.9.0
      - tiktoken==0.12.0
      - timm==1.0.16
      - tokenizers==0.22.1
      - torch==2.8.0
      - torch-memory-saver==0.0.9rc2
      - torchao==0.9.0
      - torchaudio==2.8.0
      - torchvision==0.23.0
      - tqdm==4.67.1
      - traitlets==5.14.3
      - transformers==4.57.1
      - triton==3.4.0
      - typer==0.19.2
      - typer-slim==0.19.2
      - typing-extensions==4.15.0
      - typing-inspection==0.4.2
      - tzdata==2025.2
      - urllib3==2.5.0
      - uvicorn==0.37.0
      - uvloop==0.22.1
      - vllm==0.11.0
      - watchfiles==1.1.1
      - wcwidth==0.2.14
      - websockets==15.0.1
      - xformers==0.0.32.post1
      - xgrammar==0.1.25
      - xxhash==3.6.0
      - yarl==1.22.0

transformer推理

图片推理

python 复制代码
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
import torch
import time  # 用于计时

# 加载模型和处理器
model = Qwen3VLForConditionalGeneration.from_pretrained(
    "/xxx/Qwen3-VL-8B-Instruct",
    dtype=torch.bfloat16,
    attn_implementation="flash_attention_2",
    device_map="auto",
)
processor = AutoProcessor.from_pretrained("/xxx/Qwen3-VL-8B-Instruct")

# 输入消息
messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg",
            },
            {"type": "text", "text": "描述图片"},
        ],
    }
]

# 处理输入(转为模型可接受的格式)
inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt"
)
inputs = inputs.to(model.device)

# 计算输入token数量(batch_size=1,取第一个样本的长度)
input_token_count = inputs.input_ids.size(1)
print(f"输入token数量: {input_token_count}")

# 热身推理(避免首次推理包含编译等额外耗时)
with torch.no_grad():
    model.generate(** inputs, max_new_tokens=10)

# 重置CUDA显存统计(确保测量的是推理阶段的显存)
torch.cuda.reset_peak_memory_stats()

# 记录推理开始时间
start_time = time.time()

# 执行推理
with torch.no_grad():
    generated_ids = model.generate(**inputs, max_new_tokens=128)

# 记录推理结束时间
end_time = time.time()
inference_duration = end_time - start_time  # 推理耗时(秒)

# 计算输出token数量(去掉输入部分后的生成token长度)
generated_ids_trimmed = [
    out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_token_count = len(generated_ids_trimmed[0])  # batch_size=1,取第一个样本
print(f"输出token数量: {output_token_count}")

# 计算推理速度(token/秒)
inference_speed = output_token_count / inference_duration if inference_duration > 0 else 0
print(f"推理时间: {inference_duration:.4f}秒")
print(f"推理速度: {inference_speed:.2f} token/秒")

# 获取最大显存占用(仅GPU有效,单位:GB)
if torch.cuda.is_available():
    max_memory_allocated = torch.cuda.max_memory_allocated() / (1024**3)  # 转为GB
    max_memory_reserved = torch.cuda.max_memory_reserved() / (1024**3)
    print(f"最大显存占用(已分配): {max_memory_allocated:.2f} GB")
    print(f"最大显存占用(已预留): {max_memory_reserved:.2f} GB")
else:
    print("未使用GPU,无法统计显存占用")

# 打印模型输出
output_text = processor.batch_decode(
    generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print("\n模型输出:", output_text[0])

输出如下:


输入token数量: 2764 输出token数量: 128 推理时间: 5.6309秒 推理速度: 22.73 token/秒 最大显存占用(已分配): 17.18 GB 最大显存占用(已预留): 17.38 GB

模型输出: 这是一张充满温馨与宁静氛围的海滩照片,捕捉了人与宠物之间亲密互动的瞬间。

画面主体是一位年轻女性和一只金毛犬,他们正坐在沙滩上。女性侧身对着镜头,面带灿烂的笑容,她身穿一件蓝白相间的格子衬衫和深色裤子,左手腕上戴着一块白色手表。她正与狗狗互动,右手伸出,似乎在给狗狗一个高五或是在奖励它,左手则拿着一小块食物。

金毛犬温顺地坐在她面前,前爪抬起,与女性的手相触,姿态乖巧。它身上


可以看到输出是不完整的,视频版本的一样,但是在sglang版本里面输出是完整的

视频推理

python 复制代码
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
import torch
import time  # 用于计时

# 加载模型和处理器
model = Qwen3VLForConditionalGeneration.from_pretrained(
    "/xxx/Qwen3-VL-8B-Instruct",
    dtype=torch.bfloat16,
    attn_implementation="flash_attention_2",
    device_map="auto",
)
processor = AutoProcessor.from_pretrained("/xxx/Qwen3-VL-8B-Instruct")

# 输入消息
messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "video",
                "video": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-VL/space_woaudio.mp4",
            },
            {"type": "text", "text": "描述视频"},
        ],
    }
]

# 处理输入(转为模型可接受的格式)
inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt"
)
inputs = inputs.to(model.device)

# 计算输入token数量(batch_size=1,取第一个样本的长度)
input_token_count = inputs.input_ids.size(1)
print(f"输入token数量: {input_token_count}")

# 热身推理(避免首次推理包含编译等额外耗时)
with torch.no_grad():
    model.generate(** inputs, max_new_tokens=10)

# 重置CUDA显存统计(确保测量的是推理阶段的显存)
torch.cuda.reset_peak_memory_stats()

# 记录推理开始时间
start_time = time.time()

# 执行推理
with torch.no_grad():
    generated_ids = model.generate(**inputs, max_new_tokens=128)

# 记录推理结束时间
end_time = time.time()
inference_duration = end_time - start_time  # 推理耗时(秒)

# 计算输出token数量(去掉输入部分后的生成token长度)
generated_ids_trimmed = [
    out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_token_count = len(generated_ids_trimmed[0])  # batch_size=1,取第一个样本
print(f"输出token数量: {output_token_count}")

# 计算推理速度(token/秒)
inference_speed = output_token_count / inference_duration if inference_duration > 0 else 0
print(f"推理时间: {inference_duration:.4f}秒")
print(f"推理速度: {inference_speed:.2f} token/秒")

# 获取最大显存占用(仅GPU有效,单位:GB)
if torch.cuda.is_available():
    max_memory_allocated = torch.cuda.max_memory_allocated() / (1024**3)  # 转为GB
    max_memory_reserved = torch.cuda.max_memory_reserved() / (1024**3)
    print(f"最大显存占用(已分配): {max_memory_allocated:.2f} GB")
    print(f"最大显存占用(已预留): {max_memory_reserved:.2f} GB")
else:
    print("未使用GPU,无法统计显存占用")

# 打印模型输出
output_text = processor.batch_decode(
    generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print("\n模型输出:", output_text[0])

输出如下:


输入token数量: 13428 输出token数量: 128 推理时间: 7.6084秒 推理速度: 16.82 token/秒 最大显存占用(已分配): 20.18 GB 最大显存占用(已预留): 21.05 GB

模型输出: 视频开始于一个控制室,一名穿着深色Polo衫和卡其色裤子的男子站在一个大型屏幕前,屏幕上显示着地球的图像和一些数据。他正在向观众讲解,手势生动,似乎在介绍某个项目或任务。背景中可以看到多个显示屏和控制面板,显示出这是一个高科技的环境。

接下来,画面切换到国际空间站(ISS)的外部图像,显示了其复杂的结构和太阳能电池板。随后,镜头转到空间站内部,两名宇航员站在一个充满设备和仪器的房间里,他们穿着宇航服,手持麦克风,似乎在


sglang版本

python 复制代码
#期间解决的error-1:升级cuda-12.3
Exception: Capture cuda graph failed: Assertion error (/sgl-kernel/build/_deps/repo-deepgemm-src/csrc/apis/../jit_kernels/impls/../../jit/compiler.hpp:142): (major > 12 or (major == 12 and minor >= 3)) and "NVCC version should be >= 12.3"

#期间解决的error-2:修改事件循环
RuntimeError: There is no current event loop in thread 'MainThread'.

#期间解决的error-3:设置单张显卡+参数
ValueError: For qwen3-vl-fp8 models, please make sure (text_config.moe_intermediate_size=768 // (self.tp_size=8 // self.moe_ep_size=1)) % weight_block_size_n=128 == 0. You can fix this by using arguments such as `--tp-size 8 --ep-size 8`
python 复制代码
import os
import time
import requests
import torch
from PIL import Image
from sglang import Engine
from qwen_vl_utils import process_vision_info
from transformers import AutoProcessor

# 指定使用0号GPU(需在导入torch前设置)
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

if __name__ == "__main__":
    checkpoint_path = "/mnt/tos-tommi-algorithm/lzy_workspace/Qwen3-VL-8B-Instruct"
    processor = AutoProcessor.from_pretrained(checkpoint_path)
    tokenizer = processor.tokenizer  # 提取tokenizer

    # 图像URL
    image_url = "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"
    
    # 构建对话消息
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": image_url},
                {"type": "text", "text": "描述图片"},
            ],
        }
    ]

    # 生成对话模板文本
    text = processor.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True
    )

    # 处理图像信息
    image_inputs, _ = process_vision_info(messages, image_patch_size=processor.image_processor.patch_size)

    # 初始化引擎(加载模型前清理显存)
    torch.cuda.empty_cache()
    llm = Engine(
        model_path=checkpoint_path,
        enable_multimodal=True,
        mem_fraction_static=0.7,
        tp_size=1,
        ep_size=1,
        attention_backend="fa3"
    )

    # 初始化事件循环
    import asyncio
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    # --------------------------
    # 计算输入token数量(含图像token)
    # --------------------------
    response_img = requests.get(image_url, stream=True)
    image = Image.open(response_img.raw)
    width, height = image.size
    
    patch_size = processor.image_processor.patch_size
    patch_size = patch_size[0] if isinstance(patch_size, tuple) else patch_size
    image_token_count = (height // patch_size) * (width // patch_size)
    
    text_encoded = tokenizer.encode(text, add_special_tokens=False)
    text_token_count_with_placeholder = len(text_encoded)
    input_token_count = (text_token_count_with_placeholder - 1) + image_token_count

    # --------------------------
    # 执行推理并记录时间
    # --------------------------
    start_time = time.time()
    sampling_params = {"max_new_tokens": 1024}
    response = llm.generate(prompt=text, image_data=image_inputs, sampling_params=sampling_params)
    end_time = time.time()
    inference_time = end_time - start_time

    # --------------------------
    # 计算输出token数量及推理速度
    # --------------------------
    output_text = response['text']
    output_token_count = len(tokenizer.encode(output_text, add_special_tokens=False))
    inference_speed = output_token_count / inference_time  # tokens per second

    # --------------------------
    # 精确获取最大显存占用(关键修改)
    # --------------------------
    # 获取当前设备完整显存统计
    mem_stats = torch.cuda.memory_stats()
    # 最大已分配显存(GB):模型张量实际占用
    max_allocated = torch.cuda.max_memory_allocated() / (1024 **3)
    # 最大预留显存(GB):框架为模型预留的总显存峰值
    max_reserved = torch.cuda.max_memory_reserved() / (1024** 3)
    # 当前活跃显存(GB)
    active = torch.cuda.memory_allocated() / (1024 **3)

    # --------------------------
    # 打印所有统计信息
    # --------------------------
    print(f"=== 推理性能统计 ===")
    print(f"推理时间: {inference_time:.2f}秒")
    print(f"输入token数: {input_token_count} (文本token + 图像patch token)")
    print(f"输出token数: {output_token_count}")
    print(f"推理速度: {inference_speed:.2f} tokens/秒")
    print(f"=== 显存占用(GB) ===")
    print(f"最大已分配显存: {max_allocated:.2f}")
    print(f"最大预留显存(推荐参考): {max_reserved:.2f}")
    print(f"当前活跃显存: {active:.2f}")
    print(f"\n生成文本: {output_text}")

输出如下:


=== 推理性能统计 === 推理时间: 5.97秒 输入token数: 10892 (文本token + 图像patch token) 输出token数: 349 推理速度: 58.45 tokens/秒 === 显存占用(GB) === 最大已分配显存: 0.24 最大预留显存(推荐参考): 0.31 当前活跃显存: 0.00

生成文本: 这是一张充满温馨与宁静氛围的海滩照片,捕捉了一个女孩与她的狗在日落时分互动的幸福瞬间。

  • 主体人物与宠物

    • 一位年轻女子坐在沙滩上,身体微微前倾,面带灿烂的笑容,眼神温柔地注视着她面前的狗狗。她留着棕色长发,身穿一件蓝黑相间的格子衬衫和深色裤子,左手腕上戴着一块白色手表,右手正拿着一小块食物。
    • 一只金黄色的拉布拉多犬(或类似品种)坐在她对面,前爪抬起,似乎正在与女孩击掌或请求食物,姿态乖巧而专注。狗狗身上戴着一条带有彩色小花图案的蓝色胸背带,红色的牵引绳放在旁边的沙滩上。
  • 场景与环境

    • 场景位于一片广阔的沙滩上,背景是波光粼粼的大海,近处有一道正在破碎的海浪,远处海天相接处泛着温暖的金色光芒。
    • 画面整体沐浴在柔和的金色夕阳中,光线从右后方照射过来,为女子的发丝和画面边缘镀上了一层金边,营造出温暖、梦幻的氛围。天空大部分是明亮的白色,与海面的金色形成对比。
  • 构图与氛围

    • 构图将人与狗置于画面中央偏右的位置,前景是细腻的沙滩纹理,富有层次感。
    • 整体色调偏暖,以金色和蓝色为主,强调了夕阳下的宁静与喜悦,传达出人与宠物之间深厚的情感联系和共享美好时光的幸福感。

显存这里统计有问题,我实际在nvidia-smi看到大约在71912MiB左右

总结

  • sglang需要12.3的cuda
  • transformer输出不完整,sglang完整
  • sglang没咋研究过,感觉显存占用太大了,应该有参数可以压

写到最后

相关推荐
尤超宇3 小时前
YOLOv3 目标检测算法核心技术
算法·yolo·目标检测
cyclel4 小时前
散列表的小想法
算法
Code小翊4 小时前
堆的基础操作,C语言示例
java·数据结构·算法
余俊晖4 小时前
如何让多模态大模型学会“自动思考”-R-4B训练框架核心设计与训练方法
人工智能·算法·机器学习
Emilia486.4 小时前
【Leetcode&nowcode&数据结构】顺序表的应用
数据结构·算法·leetcode
一水鉴天4 小时前
整体设计 逻辑系统程序 之27 拼语言整体设计 9 套程序架构优化与核心组件(CNN 改造框架 / Slave/Supervisor/ 数学工具)协同设计
人工智能·算法
小年糕是糕手4 小时前
【数据结构】双向链表“0”基础知识讲解 + 实战演练
c语言·开发语言·数据结构·c++·学习·算法·链表
PyHaVolask5 小时前
数据结构与算法分析
数据结构·算法·图论
小王C语言5 小时前
封装红黑树实现mymap和myset
linux·服务器·算法