Python图像处理【7】采样、卷积与离散傅里叶变换

news2025/1/18 11:48:14

采样、卷积与离散傅里叶变换

    • 0. 前言
    • 1. 图像傅里叶变换
      • 1.1 傅里叶变换基础
      • 1.2 傅里叶变换应用
      • 1.3 逆傅里叶变换应用
    • 2. 利用采样改变图像分辨率
      • 2.1 上采样
      • 2.2 下采样
    • 小结
    • 系列链接

0. 前言

采样 (Sampling) 是用于选择/丢弃图像像素的空间操作,通常用于增加/减小图像大小;而卷积是一种局部数学运算,通过将像素及其相邻像素的强度值乘以卷积核(通常是一个尺寸较小的窗口矩阵)实现;使用不同核执行图像卷积会在输出图像中产生不同的效果(例如,模糊、锐化、边缘提取等)。离散傅里叶变换 (Discrete Fourier Transform, DFT) 的基本思想是将图像视为二维函数,该函数可以表示为二维正弦和余弦(傅里叶基/系数)的加权和。DFT 可以用于将图像从空域变换到频域,因为卷积等操作在频域中可以更快地执行。在本节中,我们将使用常用 Python 库利用采样、卷积和 DFT 定理解决图像处理问题。

1. 图像傅里叶变换

在本节中,我们将介绍离散傅里叶变换 (Discrete Fourier Transform, DFT) 相关基础概念,并使用 Numpyfft 模块在图像中应用 DFT,也可以使用 Scipyfftpack 模块实现 DFT

1.1 傅里叶变换基础

我们首先介绍 2D 傅里叶变换及 2D 逆离散傅里叶变换。(灰度)图像可以被定义为 2D 函数 f ( x , y ) f(x,y) f(x,y),其中 ( x , y ) ∈ { 0 , . . . , M − 1 } × { 0 , . . . , N − 1 } (x,y)∈\{0,...,M-1\}×\{0,...,N-1\} (x,y){0,...,M1}×{0,...,N1}DFT 可以将图像从其空间表示 f ( x , y ) f(x,y) f(x,y) 改变为频域表示 f ( u , v ) f(u,v) f(u,v),其中 ( u , v ) ∈ { 0 , . . . , M − 1 } × { 0 , . . . , N − 1 } (u,v)∈\{0,...,M-1\}×\{0,...,N-1\} (u,v){0,...,M1}×{0,...,N1} 表示频率分量/傅里叶基向量:

离散傅里叶变换

1.2 傅里叶变换应用

(1) 首先,导入所有必需的库:

import numpy as np
import numpy.fft as fp
from skimage.io import imread
from skimage.color import rgb2gray
from skimage.metrics import peak_signal_noise_ratio
import matplotlib.pyplot as plt

(2) 定义函数 plot_image(),使用 matplotlib.pyplotimshow() 函数绘制图像:

def plot_image(im, title):
    plt.imshow(im, cmap='gray')
    plt.axis('off')
    plt.title(title, size=10)

(3) 接下来,定义函数 plot_freq_spectrum() 以绘制图像的频(功率)谱,该函数可以通过可选参数控制是否显示颜色映射图和轴刻度。我们需要使用 numpy.fft.fftshift() 函数将零功率谱系数 (0,0) 移动到功率谱中心,然后可视化功率谱:

numpy.fft.fftshift(x, axes=None)

傅里叶变换会返回一复数数组;复数部分对应于相位,实数部分对应于我们感兴趣的幅度:

def plot_freq_spectrum(F, title, cmap=plt.cm.gray, show_axis=True, colorbar=False):
    plt.imshow((20*np.log10(0.1 + fp.fftshift(F))).real.astype(int), cmap=cmap)
    if not show_axis:
        plt.axis('off')
    if colorbar:
        plt.colorbar()
    plt.title(title, size=10)

(4) 接下来,创建一些简单图像并获取 DFT 的功率谱。首先分别生成一对大小为 100x100 且具有(等间距)水平和垂直条纹的周期图像:

h, w = 100, 100
images = list()
im = np.zeros((h,w))
for x in range(h):
    im[x,:] = np.sin(x)
images.append(im)
im = np.zeros((h,w))
for y in range(w):
    im[:,y] = np.sin(y)
images.append(im)

(5) 接下来,我们生成对角线周期图像,然后使用半径为 10 的圆形掩码(以图像的中心为圆心),从根据此图像中创建另一图像:

im = np.zeros((h,w))
for x in range(h):
    for y in range(w):
        im[x,y] = np.sin(x + y)
images.append(im)
im = np.zeros((h,w))
for x in range(h):
    for y in range(w):
        if (x-h/2)**2 + (y-w/2)**2 < 100:
            im[x,y] = np.sin(x + y)
images.append(im)

(7) 最后,生成另一对图像,第一个图像带有一个实心圆,第二个带有以图像中心为中心的实心正方形:

im = np.zeros((h,w))
for x in range(h):
    for y in range(w):
        if (x-h/2)**2 + (y-w/2)**2 < 25:
            im[x,y] = 1
images.append(im)
im = np.zeros((h,w))
im[h//2 -5:h//2 + 5, w//2 -5:w//2 + 5] = 1
images.append(im)

(8) 我们将所有图像都存储在列表中,使用 numpy.fftfft2() 函数使用快速傅里叶变换算法来计算 DFT,并绘制生成的每个输入图像的输出功率谱:

numpy.fft.fft2(a, s=None, axes=(-2, -1), norm=None)

下图显示了相应图像及其 DFT

plt.figure(figsize=(25,10))
i = 1
for im in images:
    plt.subplot(2,6,i), plot_image(im, 'image {}'.format(i))
    plt.subplot(2,6,i+6), plot_freq_spectrum(fp.fft2(im), 'DFT {}'.format(i), show_axis=False)
    i += 1
plt.tight_layout()
plt.show()

结果图像
理想情况下,对于图像 12,水平和垂直周期模式的 DFT 应分别为垂直和水平对齐的点(对应于频率)。但是,如上图所示,由于边缘效应,结果为一条垂直和水平线(线上的亮点对应于相应的频率)。同样,对于图像 3,周期对角线模式应在垂直对角线方向上产生两个对角线点,但由于边缘效应,得到了其他许多线。如果使用二值圆形掩码处理图像 3 中心的圆,则会降低边缘效应,我们可以看到沿主对角线方向的亮点,对应于频率分量。

1.3 逆傅里叶变换应用

我们已经了解不同频率对图像的不同作用,如果我们在功率谱上应用逆傅里叶变换 (Inverse Discrete Fourier Transform ,IDFT`),可以在数值精度内恢复原始图像,下图展示了如何在图像上应用傅立叶变换:

傅里叶变换
如果我们从功率谱中删除一部分频率并应用 IDFT 以重建图像时会发生什么?接下来,我们使用函数 numpy.fft.ifft2() 执行逆傅立叶变换,函数调用方式如下:

numpy.fft.ifft2(a, s=None, axes=(-2, -1), norm=None)

(1) 读取输入图像并将其转换为灰度图像,然后计算图像的 DFT,并移动频率中心:

im = rgb2gray(imread("1.png"))
h, w = im.shape
F = fp.fft2(im)
F_shifted = fp.fftshift(F)

(2) 为了观察消除频率分量对 IDFT 输出图像的影响,我们仅选择几个具有较小(绝对)值的频率,即位于 (0,0) 周围的频率,并过滤其他频率。换句话说,只允许介于 (−k1, − k2)(+k1, +k2) 之间的频率,即允许频率 (Fx, Fy),其中 |Fx|, |Fy|≤(k1,k2),并且在利用 IDFT 变换回空间域之前过滤该区间之外的其他频率:

xs = list(map(int, np.linspace(1, h//5, 10)))
ys = list(map(int, np.linspace(1, w//5, 10)))
plt.figure(figsize=(20,8))
plt.gray()
for i in range(10):
    F_mask = np.zeros((h, w))
    F_mask[h//2-xs[i]:h//2+xs[i]+1, w//2-ys[i]:w//2+ys[i]+1] = 1 
    F1 = F_shifted*F_mask
    im_out =  fp.ifft2(fp.ifftshift(F1)).real #np.abs()
    plt.subplot(2,5,i+1), plt.imshow(im_out), plt.axis('off')
    plt.title('{}x{},PSNR={}'.format(2*xs[i]+1, 2*ys[i]+1, round(peak_signal_noise_ratio(im, im_out),2)), size=10)
plt.suptitle('Fourier reconstruction by keeping first few frequency basis vectors', size=15)
plt.show()

逆傅里叶变换
从上图可以看出,与较低频率相对应的系数值保留了均值信息,并且随着频率的增加,可以捕获图像中的更多边缘和细节,并且 PSNR 也随之增加。从 143x213 频率开始,我们可以较好的重建图像而不会导致明显的细节丢失。

2. 利用采样改变图像分辨率

采样 (Sampling) 可以通过选择/丢弃图像像素来增加/减少图像的分辨率。在本节中,我们将解决两个问题,包括上采样 (Upsampling) 与下采样 (Downsampling)。

2.1 上采样

在本小节,我们将学习如何使用离散傅里叶变换 (Discrete Fourier Transform, DFT) 执行上采样并增加图像分辨率。上采样通常在空域中进行,通过使用最近邻、双线性或双立方插值来推测新增的像素值。但在这里,我们将尝试使用 DFT 实现上采样,并在频域中应用低通滤波器( Low Pass Filter, LPF)。

2.1.1 离散傅里叶变换与低通滤波器

为了充分理解离散傅里叶变换与低通滤波器,我们首先了解如何在空域和频域中使用卷积实现 LPF。我们已经知道,滤波是指改变像素强度值以发现某些图像特征,例如平滑或锐化等。
LPF 仅允许通过来自图像频域表示的低频(使用 DFT 获得)部分,并过滤所有超出截止值的高频部分。可以在空间域中使用合适的核(例如高斯核)执行卷积实现 LPF,起始时核窗口位于(灰度)图像左上角,核的尺寸大小不能超过图像尺寸。输出图像中的像素值是通过在输入图像中滑动核窗口进行计算的:

空间卷积

接下来,我们将在频域中实现 LPF,根据卷积定理,我们只需要进行元素乘法操作,因此算法运算速度很快:

f ( x , y ) ∗ h ( x , y ) ⇔ F ( u , v ) ⋅ H ( u , v ) f(x,y)*h(x,y)⇔F(u,v)\cdot H(u,v) f(x,y)h(x,y)F(u,v)H(u,v)

根据卷积定理,我们可以将空间卷积转换为频域逐元素乘法操作。

2.1.2 使用离散傅里叶变换和低通滤波器对图像执行上采样

接下来,我们将使用尺寸为 400x600 的输入图像,并计算得到两倍尺寸的图像 (800x1200)。

(1) 首先,读取图像,将其转换为灰度图像,并在每个交替的行/列上填充零,从而令图像的尺寸大小扩大两倍:

import numpy as np
import numpy.fft as fp
from skimage.io import imread
from skimage.color import rgb2gray
import matplotlib.pyplot as plt

im = 255*rgb2gray(imread('1.png'))
im1 = np.zeros((2*im.shape[0], 2*im.shape[1]))
print(im.shape, im1.shape)
for i in range(im.shape[0]):
    for j in range(im.shape[1]):
        im1[2*i,2*j] = im[i,j]

def plot_image(im, title):
    plt.imshow(im, cmap='gray')
    plt.axis('off')
    plt.title(title, size=10)
def plot_freq_spectrum(F, title, cmap=plt.cm.gray, show_axis=True, colorbar=False):
    plt.imshow((20*np.log10(0.1 + fp.fftshift(F))).real.astype(int), cmap=cmap)
    if not show_axis:
        plt.axis('off')
    if colorbar:
        plt.colorbar()
    plt.title(title, size=10)

(2) 我们将使用以下 LPF 核,可以看出,它的中心权重最高,而远离中心的权重较小:

kernel = [[0.25, 0.5, 0.25], [0.5, 1, 0.5], [0.25, 0.5, 0.25]]

(3) 要在频域中实现滤波器,我们需要将图像乘以核,为此,核的形状需要完全等于图像的形状。为了使用 NumPy 库的函数 pad(),我们首先定义填充函数 pad_with_zeros() 用值零填充核:

def pad_with_zeros(vector, pad_width, iaxis, kwargs):
    vector[:pad_width[0]] = 0
    vector[-pad_width[1]:] = 0
    return vector

kernel = np.pad(kernel, (((im1.shape[0]-3)//2,(im1.shape[0]-3)//2+1), ((im1.shape[1]-3)//2,(im1.shape[1]-3)//2+1)), pad_with_zeros)

(4) 接下来,计算输入图像和扩展核的功率谱。由于核已经居中,因此,在应用 DFT 之前,我们需要应用 fftshift() 的逆函数 ifftshift()

freq = fp.fft2(im1)
freq_kernel = fp.fft2(fp.ifftshift(kernel))

(5) 通过频域中的逐元素乘法计算 LPF

freq_LPF = freq*freq_kernel # 卷积理论

(6) 最后,使用逆 DFT 获得输出图像,我们需要提取复数输出中的实数部分:

im2 = fp.ifft2(freq_LPF).real

(7) 绘制输入、核和输出图像以及它们的功率谱:

plt.figure(figsize=(15,10))
plt.gray()
cmap = 'nipy_spectral'
plt.subplot(231), plot_image(im, 'Original Input Image')
plt.subplot(232), plot_image(im1, 'Padded Input Image')
plt.subplot(233), plot_freq_spectrum(freq, 'Original Image Spectrum', cmap=cmap)
plt.subplot(234), plot_freq_spectrum(freq_kernel, 'Image Spectrum of the LPF', cmap=cmap)
plt.subplot(235), plot_freq_spectrum(fp.fft2(im2), 'Image Spectrum after LPF', cmap=cmap)
plt.subplot(236), plot_image(im2.astype(np.uint8), 'Output Image')
plt.show()

上采样

2.2 下采样

为了减少图像的尺寸大小,我们需要对图像执行下样本。得到的尺寸较小的新图像中的每个像素对应于原始大图像中的多个像素。直接从原始图像中删除像素有助于减少图像的分辨率,但是它会引入空间混叠效应(例如摩尔纹),并且导致输出图像质量较差。为了防止这种情况,需要使用抗混叠滤波器(在下采样之前)以过滤图像高频分量。

2.2.1 使用高斯滤波器实现抗锯齿下采样

在本节中,我们将学习一种简单的抗锯齿技术,该方法通过在下采样前应用低通滤波器( LPF )来实现。

(1) 导入所需的库,读取输入图像并将其转换为 float 类型(使所有像素值均在 01 之间),我们的目标是通过将高度和宽度减少三倍,将图像大小减少九倍:

from skimage.filters import gaussian
from skimage import img_as_float
import numpy as np
from skimage.io import imread
import matplotlib.pyplot as plt

im = img_as_float(imread('1.png'))
print(im.shape)

(2) 在下采样之前,应用高斯滤波器作为预处理步骤,以使图像更加平滑。使用 skimage.filters.gaussian() 函数将高斯模糊应用于图像,等价的,我们也可以使用 scipy.ndimage.gaussian_filter() 函数:

im_blurred = gaussian(im, sigma=1.25, multichannel=True)

(3) 从原始图像中在 XY 轴方向的每隔三个像素保留一个像素,以获取尺寸较小的图像。观察没有使用高斯滤波器(带有空间混叠)通过下采样获得的输出图像的质量:

n = 3 # 缩放图像倍数
h, w = im.shape[0] // n, im.shape[1] // n
im_small = np.zeros((h, w, 3))
for i in range(h):
   for j in range(w):
      im_small[i,j] = im[n*i, n*j]
im_small_aa = np.zeros((h, w, 3))
for i in range(h):
   for j in range(w):
      im_small_aa[i,j] = im_blurred[n*i, n*j]

(4) 绘制原始输入图像:

plt.figure(figsize=(15,15))
plt.imshow(im), plt.title('Original Image', size=10)
plt.show()

原始图像

(5) 绘制没有使用高斯滤波器进行下采样的图像:

plt.figure(figsize=(15,15))
plt.imshow(im_small), plt.title('Resized Image (without Anti-aliasing)', size=10)
plt.show()

未使用高斯滤波的下采样
(6) 绘制使用抗锯齿技术得到的下采样图像:

plt.figure(figsize=(15,15))
plt.imshow(im_small_aa), plt.title('Resized Image (with Anti-aliasing)', size=10)
plt.show()

具有抗锯齿的下采样图像
正如上图所示,没有抗锯齿的下采样图像中存在空间混叠现象,但是具有抗锯齿操作的图像中几乎不存在这种现象。由于高斯模糊是 LPF,因此从原始输入图像中移除了高频部分。因此,可以避免空间混叠现象。

小结

在本节中,我们介绍了频域图像处理概念以及相关问题,包括采样、卷积和离散傅里叶变换。讲解了这些频域图像处理的基本概念,并利用相关技术解决了图像处理问题,重点介绍了如何使用离散傅里叶变换/低通滤波器对图像执行上采样以及使用高斯滤波器实现抗锯齿下采样。

系列链接

Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲/逆扭曲
Python图像处理【6】通过哈希查找重复和类似的图像

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

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

相关文章

(byte)1658385462>>16=-40,怎么算的?

正文 在 Github 项目mongo-java-driver有一个类ObjectId.java&#xff0c;它的作用是生成唯一 id 的&#xff0c;它的核心实现是下面这样一段代码 [1]&#xff1a; public void putToByteBuffer(final ByteBuffer buffer) {notNull("buffer", buffer);isTrueArgume…

【数据结构Java版】树与二叉树的相关知识全解

目录 一、树型结构 &#xff08;1&#xff09;树的定义 &#xff08;2&#xff09;树的基本术语 &#xff08;3&#xff09;树的存储结构 二、二叉树 &#xff08;1&#xff09;二叉树的定义 &#xff08;2&#xff09;两种特殊二叉树 1.满二叉树 2.完全二叉树 &…

CSS中你可能不知道的Selectors特性

CSS中你可能不知道的Selectors特性本文作者为奇舞团前端开发工程师引言最近看了2022年全球CSS调查报告&#xff0c;发现了一些不常见的伪类和伪元素。伪类:has()html<div><h1>H1</h1><h2>H2</h2><p>h1{margin: 0 0 0.25rem 0}</p> &…

设置访问SSH为密钥访问

1.制作密钥对 ssh-keygen输入会问两个问题 设置公私钥名称&#xff08;可以留白&#xff0c;直接回车&#xff09;设置公私钥密码&#xff08;可以留白&#xff0c;直接回车&#xff09; 第一次输入第二次确认 留空确认的话&#xff0c;生成公私钥。共有两个文件 # 私钥 id…

Rxjava源码分析实践(三)【RxJava基本原理分析之订阅流】

本节,我们从Rxjava使用代码入手,去结合自己已有的知识体系,加查阅部分源码验证的方式,来一起探索一下Rxjava实现的基本原理。 为了本文原理分析环节,可以被更多的人理解、学习,所以小编从初学者的角度,从使用入手,一点点的分析了其中的源码细节、思想,建议大家随着本文…

NCMMSC论文介绍 | 探索语音自监督模型的高效融合算法

本文介绍了清华大学语音与音频技术实验室&#xff08;SATLab&#xff09;与上海交通大学跨媒体语言智能实验室&#xff08;X-LANCE&#xff09;合作的NCMMSC录用论文&#xff1a;Exploring Effective Fusion Algorithms for Speech Based Self-Supervised Learning Models。该论…

动态列合并更新

【问题】 I have one query, would be great if anyone can help me out on this. In SQL, I have two tables with same column names. Want to query if there is any difference in the column values and if yes will update the values(in the first table) else if the…

【工具类】后台Mock工具类

目录一、介绍二、使用方法1. Controller层定义接口2. 编写json文件3. 开启AOP4. 调用接口验证三、源码一、介绍 Controller层定义完接口后&#xff0c;不需要写业务逻辑。编写Json文件&#xff0c;调用接口时返回json文件的数据。 优点&#xff1a; 设计阶段即可定义好接口&…

Centos 图形化yum管理工具 - yum Extender

文章目录背景安装开启yum-GUI工具 - yumexyum list installed列出软件包的依赖yum cleam背景 作为一个yum工程师&#xff0c;长期备受yum 命令的煎熬。 难道yum就乜有一个GUI管理界面吗&#xff1f; yum Extender (简称 yumex ) , 是 yum 的图形化操作界面。可以通过 yumex 方…

ActiveMQ高级特性和大厂面试常考重点

目录 一、引入消息队列之后该如何保证其高可用性 二、异步投递Async Sends 三、延迟投递和定时投递 四、ActiveMQ消费重试机制 五、死信队列 六、如何保证消息不被重复消费呢?幂等性问题你谈谈 一、引入消息队列之后该如何保证其高可用性 ActiveMQ集群模式_zoeil的博客-…

【机器学习】KNN 算法介绍

文章目录一、KNN 简介二、KNN 核心思想实例分析&#xff1a;K 值的影响三、KNN 的关键1. 距离计算1. 闵可夫斯基距离2. 曼哈顿距离3. 欧氏距离4. 切比雪夫距离5. 余弦距离总结2. K值选择四、KNN 的改进&#xff1a;KDTree五、KNN 回归算法参考链接一、KNN 简介 KNN 算法&#…

想在微信上使用chatGPT?小程序?公众号?企业微信,最终还是选择了企业微信版本的chatgpt

chatgpt的接口现在都可以正常用了&#xff0c;但是怎么把这个功能放在手机上随用随开呢&#xff1f;微信个人聊天版本小程序版本公众号版本企业微信版本逻辑实现方式微信个人聊天版本 网上很多微信机器人版本的&#xff0c;但是原理是网页版微信&#xff0c;很多账号都不能登陆…

golang指针

指针 区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 要搞明白Go语言中的指针需要先知道3个概念&#xff1a;指针地址、指针类型和指针取值。 1.1. Go语言中的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想要修改某个变…

Linux中如何理解线程?线程ID到底是什么?

朋友们好&#xff0c;这里简要介绍了进程和线程的区别以及对LINUX中线程ID的理解&#xff0c;本人目前理解尚浅&#xff0c;若文中有表述不当的地方还望理解并指正&#xff0c;谢谢大家&#xff01; 文章目录一&#xff1a;进程和线程二&#xff1a;线程ID和进程地址空间布局一…

5 项目部署

5.1 Linux-项目部署 5.1.1 环境 5.1.1.1 开发环境&#xff08;dev&#xff09; 外部用户无法访问&#xff0c;开发人员使用&#xff0c;版本变动很大 平时大家大多是在Windows或者Mac操作系统下去编写代码进行开发. 在开发环境中安装大量的软件&#xff0c;这样会导致环境的稳…

2022 年度盘点 | 更成熟的 AI,更破圈的技术狂欢

By 超神经内容一览&#xff1a;2022 年 AI 领域发展不断提速&#xff0c;新技术成果纷纷落地&#xff0c;模型迭代加速升级。本文总结了 2022 年 AI 领域各大公司的技术成就。关键词&#xff1a;年终盘点 大厂 技术创新2022 年在此起彼伏的咳嗽声中接近尾声&#xff0c;这一…

onCreate、onSaveInstanceState、onRestoreInstance一个参数和两个参数

Android Studio移动应用开发——onCreate、onSaveInstanceState、onRestoreInstance一个参数和两个参数_dear_jing的博客-程序员宅基地 - 程序员宅基地 在做Android生命周期实验过程中&#xff0c;把 Log.i(TAG, "(1) onCreate()") 写到了含有两个参数的函数 onSave…

HTML5 元素拖放

文章目录HTML5 元素拖放概述触发事件实现元素拖放功能dataTransfer元素拖动效果垃圾箱效果HTML5 元素拖放 概述 在HTML5中&#xff0c;我们只需要给元素添加一个draggable属性&#xff0c;然后设置该属性值为true&#xff0c;就能实现元素的拖放。 拖放&#xff0c;指的是“…

【Python】Numpy分布函数总结

文章目录总表均匀分布和三角分布幂分布与正态分布相关的分布与Gamma相关的分布极值分布总表 np.random中提供了一系列的分布函数&#xff0c;用以生成符合某种分布的随机数。下表中&#xff0c;如未作特殊说明&#xff0c;均有一个size参数&#xff0c;用以描述生成数组的尺寸…

【综合笔试题】难度 1.5/5,常规二叉树爆搜题

题目描述 这是 LeetCode 上的 95. 不同的二叉搜索树 II &#xff0c;难度为 中等。 Tag : 「树」、「二叉搜索树」、「BST」、「DFS」、「递归」、「爆搜」 给你一个整数 n&#xff0c;请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以…