基于FPGA实现正弦插值算法

news2024/11/25 18:48:06

1、正弦插值的算法分析

1.1 信号在时域与频域的映射关系

        在进行正弦算法分析之前,我们回顾一下《数字信号处理》课程中,对于信号在时域与频域之间的映射关系,如下图。

 

        对于上图中的原始信号x(t),使用ADC对信号进行采样,即实现了时域信号的离散化,得到x[k]。根据时域与频域之间的映射关系:时域的离散化对应着频域的周期化,即x[k]的频域响应为X(e^{j\Omega })

        那么离散化的x[k]如何还原为原来的x(t)呢?时域上分析较为复杂,我们可以从频域上进行分析,即如何将频域响应X(e^{j\Omega })还原成X(jw)。这样就比较直观了,只需要截取X(e^{j\Omega })一个周期的信号,就可以还原成X(jw),示例如下图。

 1.2 sinc函数

         上面用到的窗函数,实际上是一个理想的低通滤波器,其时域形态是什么样子的呢?如下图。

        其时域形态公式:sinc(t)=\frac{sin(\pi t))}{\pi t} ,sinc函数在频域上是一个矩形的方窗,如下图。

 1.3 使用sinc函数实现正弦插值的算法分析

        综上所述,对于我们可以使用sinc函数实现离散信号还原为连续信号,即使用sinc函数实现离散信号的无限插值。

        而频域的乘积对应着时域的卷积,因此我们可以将离散的采样信号与sinc函数进行卷积运算,从而获得插值后的信号。

        y(n)=\sum x(\imath)h(n-\imath )

        其中,y(n)为插值后信号;x(n)为待插值的离散信号;h(n)为sinc函数,也称为插值核或者插值因子。

        假设有一个离散信号x(n),含有8个离散点,x(n)={-10,0,10,0,-10,0,10,0};如下图:

         我们使用前后共计8个原始采样点,来求插值点x(0.5)的数值。套用上面描述的公式y(n)=\sum x(\imath)h(n-\imath ),即x(0.5)=x(-3)h(0.5-(-3)) +  x(-2)h(0.5-(-2)) + x(-1)h(0.5-(-1)) + x(0)h(0.5-(0)) + x(1)h(0.5-(1)) + x(2)h(0.5-(2)) + x(3)h(0.5-(3)) =-6.3056,即求得插值点的数值。

         根据这种思想,我们可以无限的求得每个插值点的数值。那么如何在工程也能够中使用sinc函数实现信号的插值,是我们接下来要讲述的。

2、使用matlab计算插值核

        在使用FPGA实现插值算法时,由于sinc函数的计算实现起来比较消耗硬件资源,所以一般预先计算好插值核存放在ROM或者寄存器中,直接进行调用。

        假设我们要使用前后8个点计算插值,插值倍数为10,那么使用matlab计算插值核的代码如下:

interp_mul = 10; %插值倍数为10
orig_point_num = 8;%原始点数8个

coe_group = zeros(interp_mul,orig_point_num); %定义系数组
for group = 1:1:interp_mul %计算interp_mul组系数
    for Hn = 1:1:orig_point_num %计算每组系数的orig_point_num个加权值
        if(group == 1)
            if(Hn==4)
                coe_group(group,Hn) = 1; %原始点保持数据不变[0,0,0,1,0,0,0,0]
            else
                coe_group(group,Hn) = 0; %原始点保持数据不变[0,0,0,1,0,0,0,0]
            end
        else
            coe_group(group,Hn) = sinc(4- Hn + 0.1*(group-1));%找规律映射
        end
    end
end

        运行结果保存在coe_group数组中,结果如下:

         上述算法计算得到的插值核为小数,而FPGA计算小数运算时,需要现将数值左移相应位数进行放大,方便进行计算,输出结果时,截取高bit即可。由于后续FPGA实现卷积和时使用18bits的插值核进行运算,因此实际工程中的matlab代码如下:

interp_mul = 10; %插值倍数为10
orig_point_num = 8;%原始点数8个

coe_group = zeros(interp_mul,orig_point_num); %定义系数组
for group = 1:1:interp_mul %计算interp_mul组系数
    for Hn = 1:1:orig_point_num %计算每组系数的orig_point_num个加权值
        if(group == 1)
            if(Hn==4)
                coe_group(group,Hn) = 1; %原始点保持数据不变[0,0,0,1,0,0,0,0]
            else
                coe_group(group,Hn) = 0; %原始点保持数据不变[0,0,0,1,0,0,0,0]
            end
        else
            coe_group(group,Hn) = sinc(4- Hn + 0.1*(group-1)) *2^17;%找规律映射,小数放大2^17倍
        end
    end
end

结果如下:

 3、使用FPGA实现离散采样点的插值

         本文中仅使用一个测试程序,说明FPGA实现插值的原理,结构相对简单,架构图如下:

         离散采样RAM模块代码如下:

module disc_samp_gen(
    input clk,
    input disc_rd,//读待插值数据标志
    output [7:0] disc_samp  //离散采样点输出 有符号数据
    
    );
reg [1:0]addra=0;

always@(posedge clk)
begin
    if(disc_rd)
        addra <= addra + 1'b1;
end


DataSrc_ROM DataSrc_ROM (
  .clka(clk),    // input wire clka
  .addra(addra),  // input wire [1 : 0] addra
  .douta(disc_samp)  // output wire [7 : 0] douta
);
    
endmodule

         插值处理模块,主要通过乘法器实现卷积和,如下:

module sinc_process(
    input clk,
    input sinc_en,//插值使能标志
    input [7:0]disc_samp,//待插值数据,有符号数
    output disc_rd,//读取插值数据标志
    
    output sinc_valid,//插值数据有效标志
    output [7:0]sinc_data//插值后数据
    );
//插值倍数10,使用8个离散采样点进行插值处理
第0组插值核  
//localparam [17:0] group0_coe[7:0] = {18'h0,18'h0,18'h0,18'h0,
//                                     18'h1FFFF,18'h0,18'h0,18'h0};//临时粗略将18'h1FFFF视为1
第1组插值核  
//localparam [17:0] group1_coe[7:0] = {18'h3F317,18'h0115D,18'h3E57F,18'h037F5,
//                                     18'h1F79E,18'h3D238,18'h017FB,18'h3EFC2};
第2组插值核  
//localparam [17:0] group2_coe[7:0] = {18'h3E6CB,18'h02236,18'h3CAC8,18'h077BE,
//                                     18'h1DEF8,18'h3B02C,18'h02B8A,18'h3E211};
第3组插值核  
//localparam [17:0] group3_coe[7:0] = {18'h3DC5E,18'h030D5,18'h3B272,18'h0BC5B,
//                                     18'h1B77F,18'h39A94,18'h03953,18'h3D80C};
第4组插值核  
//localparam [17:0] group4_coe[7:0] = {18'h3D4F2,18'h03B9D,18'h39F21,18'h10254,
//                                     18'h1837E,18'h3914A,18'h04095,18'h3D26A};
第5组插值核  
//localparam [17:0] group5_coe[7:0] = {18'h3D170,18'h04130,18'h3935A,18'h145F3,
//                                     18'h145F3,18'h3935A,18'h04130,18'h3D170};
第6组插值核  
//localparam [17:0] group6_coe[7:0] = {18'h3D26A,18'h04095,18'h3914A,18'h1837E,
//                                     18'h10254,18'h39F21,18'h03B9D,18'h3D4F2};
第7组插值核  
//localparam [17:0] group7_coe[7:0] = {18'h3D80C,18'h03953,18'h39A94,18'h1B77F,
//                                     18'h0BC5B,18'h3B272,18'h030D5,18'h3DC5E};
第8组插值核  
//localparam [17:0] group8_coe[7:0] = {18'h3E211,18'h02B8A,18'h3B02C,18'h1DEF8,
//                                     18'h077BE,18'h3CAC8,18'h02236,18'h3E6CB};
第9组插值核  
//localparam [17:0] group9_coe[7:0] = {18'h3EFC2,18'h017FB,18'h3D238,18'h1F79E,
//                                     18'h037F5,18'h3E57F,18'h0115D,18'h3F317};

//第0组插值核  
localparam [17:0] group0_coe[7:0] = {18'h0,18'h0,18'h0,18'h1FFFF,18'h0,18'h0,18'h0,18'h0};//临时粗略将18'h1FFFF视为1
//第1组插值核  
localparam [17:0] group1_coe[7:0] = {18'h3EFC2,18'h017FB,18'h3D238,18'h1F79E,18'h037F5,18'h3E57F,18'h0115D,18'h3F317};
//第2组插值核  
localparam [17:0] group2_coe[7:0] = {18'h3E211,18'h02B8A,18'h3B02C,18'h1DEF8,18'h077BE,18'h3CAC8,18'h02236,18'h3E6CB};
//第3组插值核  
localparam [17:0] group3_coe[7:0] = {18'h3D80C,18'h03953,18'h39A94,18'h1B77F,18'h0BC5B,18'h3B272,18'h030D5,18'h3DC5E};
//第4组插值核  
localparam [17:0] group4_coe[7:0] = {18'h3D26A,18'h04095,18'h3914A,18'h1837E,18'h10254,18'h39F21,18'h03B9D,18'h3D4F2};
//第5组插值核  
localparam [17:0] group5_coe[7:0] = {18'h3D170,18'h04130,18'h3935A,18'h145F3,18'h145F3,18'h3935A,18'h04130,18'h3D170};
//第6组插值核  
localparam [17:0] group6_coe[7:0] = {18'h3D4F2,18'h03B9D,18'h39F21,18'h10254,18'h1837E,18'h3914A,18'h04095,18'h3D26A};
//第7组插值核  
localparam [17:0] group7_coe[7:0] = {18'h3DC5E,18'h030D5,18'h3B272,18'h0BC5B,18'h1B77F,18'h39A94,18'h03953,18'h3D80C};
//第8组插值核  
localparam [17:0] group8_coe[7:0] = {18'h3E6CB,18'h02236,18'h3CAC8,18'h077BE,18'h1DEF8,18'h3B02C,18'h02B8A,18'h3E211};
//第9组插值核  
localparam [17:0] group9_coe[7:0] = {18'h3F317,18'h0115D,18'h3E57F,18'h037F5,18'h1F79E,18'h3D238,18'h017FB,18'h3EFC2};


reg [17:0] sinc_coe [7:0];//对应插值点使用的插值核
reg [3:0] sinc_cnt = 0;//插值计数,0时为原始点,1~9为插值点,其余数值保留
reg [7:0] samp_data [7:0];//每次从RAM读取数据时进行锁存更新


//8个乘法结果
reg [25:0] mult_data[7:0];

assign disc_rd = (sinc_cnt == 4'd8) ? 1'b1:1'b0; //插值第8个点时,读取新的数据

//插值倍数10,插值点计数
always@(posedge clk)
begin
    if(sinc_en == 1'b1) begin
        if(sinc_cnt == 4'd9) 
            sinc_cnt <= 4'd0;
        else
            sinc_cnt <= sinc_cnt + 1;
    end
    else
        sinc_cnt <= 4'd0;
end
//原始点锁存
always@(posedge clk)
begin
    if(sinc_en == 1'b1 && sinc_cnt == 4'd0) begin //插值使能
        samp_data[0] <= disc_samp;//新输入的点
        samp_data[1] <= samp_data[0];
        samp_data[2] <= samp_data[1];
        samp_data[3] <= samp_data[2];
        samp_data[4] <= samp_data[3];
        samp_data[5] <= samp_data[4];
        samp_data[6] <= samp_data[5];
        samp_data[7] <= samp_data[6];
    end
end

//插值核系数选择
always@(posedge clk)
begin
    case(sinc_cnt)
        4'd0:sinc_coe <= group0_coe;//原始点系数
        4'd1:sinc_coe <= group1_coe;//第1个插值点系数
        4'd2:sinc_coe <= group2_coe;//第2个插值点系数
        4'd3:sinc_coe <= group3_coe;//第3个插值点系数
        4'd4:sinc_coe <= group4_coe;//第4个插值点系数
        4'd5:sinc_coe <= group5_coe;//第5个插值点系数
        4'd6:sinc_coe <= group6_coe;//第6个插值点系数
        4'd7:sinc_coe <= group7_coe;//第7个插值点系数
        4'd8:sinc_coe <= group8_coe;//第8个插值点系数
        4'd9:sinc_coe <= group9_coe;//第9个插值点系数
        default:;
    endcase
end


//乘法器0
mult_gen mult_gen_0 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[0]),      // input wire [7 : 0] A
  .B(sinc_coe[0]),      // input wire [17 : 0] B
  .P(mult_data[0])      // output wire [25 : 0] P
);

//乘法器1
mult_gen mult_gen_1 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[1]),      // input wire [7 : 0] A
  .B(sinc_coe[1]),      // input wire [17 : 0] B
  .P(mult_data[1])      // output wire [25 : 0] P
);

//乘法器2
mult_gen mult_gen_2 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[2]),      // input wire [7 : 0] A
  .B(sinc_coe[2]),      // input wire [17 : 0] B
  .P(mult_data[2])      // output wire [25 : 0] P
);

//乘法器3
mult_gen mult_gen_3 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[3]),      // input wire [7 : 0] A
  .B(sinc_coe[3]),      // input wire [17 : 0] B
  .P(mult_data[3])      // output wire [25 : 0] P
);

//乘法器4
mult_gen mult_gen_4 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[4]),      // input wire [7 : 0] A
  .B(sinc_coe[4]),      // input wire [17 : 0] B
  .P(mult_data[4])      // output wire [25 : 0] P
);

//乘法器5
mult_gen mult_gen_5 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[5]),      // input wire [7 : 0] A
  .B(sinc_coe[5]),      // input wire [17 : 0] B
  .P(mult_data[5])      // output wire [25 : 0] P
);

//乘法器6
mult_gen mult_gen_6 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[6]),      // input wire [7 : 0] A
  .B(sinc_coe[6]),      // input wire [17 : 0] B
  .P(mult_data[6])      // output wire [25 : 0] P
);

//乘法器7
mult_gen mult_gen_7 (
  .CLK(clk),  // input wire CLK
  .A(samp_data[7]),      // input wire [7 : 0] A
  .B(sinc_coe[7]),      // input wire [17 : 0] B
  .P(mult_data[7])      // output wire [25 : 0] P
);


//使用加法器实现有符号数相加

//二点相加
wire [25:0] add2_0;
wire [25:0] add2_1;
wire [25:0] add2_2;
wire [25:0] add2_3;
adder2 adder2_inst0 (
  .A(mult_data[0]),      // input wire [25 : 0] A
  .B(mult_data[1]),      // input wire [25 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add2_0)      // output wire [26 : 0] S
);

adder2 adder2_inst1 (
  .A(mult_data[2]),      // input wire [25 : 0] A
  .B(mult_data[3]),      // input wire [25 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add2_1)      // output wire [26 : 0] S
);

adder2 adder2_inst2 (
  .A(mult_data[4]),      // input wire [25 : 0] A
  .B(mult_data[5]),      // input wire [25 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add2_2)      // output wire [26 : 0] S
);

adder2 adder2_inst3 (
  .A(mult_data[5]),      // input wire [25 : 0] A
  .B(mult_data[6]),      // input wire [25 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add2_3)      // output wire [26 : 0] S
);

//四点相加
reg [25:0] add4_0;
reg [25:0] add4_1;
adder4 adder4_inst0 (
  .A(add2_0),      // input wire [26 : 0] A
  .B(add2_1),      // input wire [26 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add4_0)      // output wire [27 : 0] S
);

adder4 adder4_inst1 (
  .A(add2_2),      // input wire [26 : 0] A
  .B(add2_3),      // input wire [26 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add4_1)      // output wire [27 : 0] S
);

//八点相加
wire [25:0] add8;
adder8 adder8 (
  .A(add4_0),      // input wire [26 : 0] A
  .B(add4_1),      // input wire [26 : 0] B
  .CLK(clk),  // input wire CLK
  .S(add8)      // output wire [27 : 0] S
);


assign sinc_valid = 1'b1;
assign sinc_data = add8[25:18];
endmodule

        编写testbench,对插值模块进行仿真验证,testbench代码如下:

module testbench(

    );
reg clk = 0; //100M时钟

always 
begin
    # 5
    clk <= ~clk;
end

wire disc_rd;//采样点有效标志
wire [7:0] disc_samp;  //离散采样点输出
disc_samp_gen disc_samp_gen(
    .clk(clk),
    .disc_rd(disc_rd),//采样点有效标志
    .disc_samp(disc_samp)//离散采样点输出
    );
    
sinc_process sinc_process(
    .clk(clk),
    
    .sinc_en(1'b1),
    
    .disc_rd(disc_rd),//采样点有效标志
    .disc_samp(disc_samp),//离散采样点输出
    
    .sinc_valid(),//插值数据有效标志
    .sinc_data()//插值后数据
    );
    
    
endmodule

仿真验证结果如下:

         可以看到,图中黄色部分为原始采样点的直线连接波形,紫色部分为正弦插值后的波形。正弦插值仿真成功。

vivado工程以及学习sinc插值的过程文件(主要是为了存档,方便后续自己使用):https://download.csdn.net/download/yindq1220/87557975?spm=1001.2014.3001.5501

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

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

相关文章

【操作系统】进程句柄

进程句柄句柄是什么为什么需要句柄作用句柄是什么 先给结论&#xff0c;句柄&#xff08;handle&#xff09;实际上是一个指向指针的指针。 它指向进程所要访问的进程对象的地址&#xff0c;是用来找到目标进程的索引&#xff0c;当我们想要访问对象进程时&#xff0c;就要利…

从一道面试题看 TCP 的吞吐极限

分享一个 TCP 面试题&#xff1a;单条 TCP 流如何打满香港到旧金山的 320Gbps 专线&#xff1f;(补充&#xff0c;写成 400Gbps 更具迷惑性&#xff0c;但预测大多数人都会跑偏&#xff0c;320Gbps 也就白给了) 这个题目是上周帮一个朋友想的&#xff0c;建议他别问三次握手&a…

C#:Krypton控件使用方法详解(第十六讲) ——kryptonCheckedListBox

今天介绍的Krypton控件中的kryptonCheckedListBox。下面介绍控件的外观属性如下图所示&#xff1a;Cursor属性&#xff1a;表示鼠标移动过该控件的时候&#xff0c;鼠标显示的形状。属性值如下图所示&#xff1a;UseWaitCursor属性&#xff1a;表示鼠标在控件中等待时&#xff…

问ChatGPT:零基础如何学好.Net Core?

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 ChatGPT横空出世&#xff0c;一下子让全球互联网企业都慌了&#xff0c;纷纷表示&#xff1a;马上跟进发布ChatGPT&#xff0c;媒体纷纷报道大有改变教培行业。 下面我们问问ChatGPT&#xff1a;零基础如何学好…

EasyCVR视频融合平台开放插件功能:支持EasyNTS与EasyShark抓包

EasyCVR视频融合平台基于云边端一体化架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;平台支持海量视频汇聚管理&#xff0c;能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅…

GPS/GPRS车载定位系统智能终端设计μC/OS-Ⅱ调度液晶显示汽车行驶记录仪电路

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;gps电路 免费下载完整无水印论文报告&#xff08;包含主板电路图和采集板电路图&#xff09; 文章目录一、绪论二、车载智能终端三、车载智能终端的硬件结构及设计四、车载智能终端的软件结构及设计五、总结和展望附录一 汽…

小i机器人登陆纳斯达克:市值4.2亿美元,与苹果打了10年专利侵权官司

‍数据智能产业创新服务媒体——聚焦数智 改变商业要问当前科技圈里最靓的仔是谁&#xff1f;那当然是非 ChatGPT莫属。当下谁能推出真正意义上的中国版ChatGPT&#xff0c;并且在这轮AI浪潮竞争白热化阶段中笑到最后&#xff0c;已经成为人们关注的焦点。美东时间3月9日&…

使用chatgpt写6.5分作文范文

其实使用chatgpt最大的背单词好处就是你可以看到真正的外国人的思维到底是如何的。而且&#xff0c;你也可以看到chatgpt这个模型&#xff0c;如果是编写代码的话&#xff0c;你如果使用中文&#xff0c;它编写的效果是没有英文输入的好的&#xff0c;为什么呢&#xff1f;因为…

Vector - CAPL - log回放模块函数

Replay log回放模块作为我们常见的问题分析小工具,是大部分车载圈老人的必备工具,不过自从CANoe软件11.0之后的版本变动依然无法阻挡每一个车载人使用的热情,除了我们常见的手动回放log外,我们工作中还有一部分低概率以及极低概率出现的问题,这时候我们就需要通过自动化对…

python趣味编程-2048游戏

在上一期我们用Python实现了一个盒子追逐者的游戏&#xff0c;这一期我们继续使用Python实现一个简单的2048游戏&#xff0c;让我们开始今天的旅程吧~ 在 Python 免费源代码中使用 Tkinter 的简单 2048 游戏 使用 Tkinter 的简单 2048 游戏是一个用Python编程语言编码的桌面游…

阅读笔记DeepAR: Probabilistic Forecasting with Autoregressive Recurrent Networks

zi,t∈Rz_{i,t}\in \mathbb{R}zi,t​∈R表示时间序列iii在ttt时刻的值。给一个连续时间段t∈[1,T]t\in [1, T]t∈[1,T]&#xff0c;将其划分为context window[1,t0)[1,t_0)[1,t0​)和prediction window[t0,T][t_0,T][t0​,T]。用context window的时间序列预测prediction window…

关于异常控制流和系统级 I/O:进程

&#x1f4ad; 写在前面&#xff1a;本文将学习《深入理解计算机系统》的第六章 - 关于异常控制流和系统级 I/O 的 进程部分。CSAPP 是计算机科学经典教材《Computer Systems: A Programmers Perspective》的缩写&#xff0c;该教材由Randal E. Bryant和David R. OHallaron 合著…

C 去除字符串中重复字母(LeetCode)

&#x1f31f;前言摆烂太久&#xff0c;好久没有更文了&#xff0c;小九和大家一起看看题写写题找回手感吧&#xff0c;也希望这篇文章可以帮助正在寻找解题答案的朋友&#xff0c;你们的支持就是我最大的动力&#xff01;求三连&#xff01;求关注呀&#xff01;&#x1f31f;…

信息系统项目管理师第4版教材的变化:PMBOK第六版和第七版的叠加

昨天下午&#xff0c;软考官方网站突然发布了第4版考纲和教材&#xff0c;这让很多在准备今年上半年信息系统项目管理师考试的考生们有些紧张。那么这次教材改版主要的变化是什么&#xff0c;5月份的考试是否会按照新版的教材来考呢&#xff1f;让我们逐个章节进行分析。信息化…

Spark RDD编程基本操作

RDD是Spark的核心概念&#xff0c;它是一个只读的、可分区的分布式数据集&#xff0c;这个数据集的全部或部分可以缓存在内存中&#xff0c;可在多次计算间重用。Spark用Scala语言实现了RDD的API&#xff0c;程序员可以通过调用API实现对RDD的各种操作&#xff0c;从而实现各种…

数据结构刷题(二十三):47全排列II、51N皇后、37解数独

1.全排列II题目链接思路&#xff1a;回溯之排列问题并且有数组排序标记数组。 回溯三部曲同46. 全排列。过程图&#xff1a;https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html#%E6%80%9D%E8%B7%AF注意&#xff1a;本题的去重操作主要在树层上。就是说同一树…

2.JVM常识之 运行时数据区

1.JVM核心组成 2.JVM 运行时数据区&#xff08;jdk8&#xff09; 程序计数器&#xff1a;线程私有&#xff0c;当前线程所执行字节码的行号指示器 jvm栈&#xff1a;线程私有&#xff0c;Java 虚拟机栈为 JVM 执行 Java 方法服务 本地方法栈&#xff1a;线程私有&#xff0c;本…

SpringBoot接口 - 如何统一异常处理

SpringBoot接口如何对异常进行统一封装&#xff0c;并统一返回呢&#xff1f;以上文的参数校验为例&#xff0c;如何优雅的将参数校验的错误信息统一处理并封装返回呢&#xff1f;为什么要优雅的处理异常如果我们不统一的处理异常&#xff0c;经常会在controller层有大量的异常…

【Java】初识Java

Java和C语言有许多类似之处&#xff0c;这里就只挑不一样的点来说&#xff0c;所以会比较杂乱哈~ 目录 1.数据类型 2.输入与输出 2.1三种输出 2.2输入 2.3循环输入输出 //猜数字小游戏 //打印乘法口诀表 3.方法 //交换两个数&#xff08;数组的应用&#xff09; //模…

栈的应用-算数

本题要求你为初学数据结构的小伙伴设计一款简单的利用堆栈执行的计算器。如上图所示&#xff0c;计算器由两个堆栈组成&#xff0c;一个堆栈 S1​ 存放数字&#xff0c;另一个堆栈 S2​ 存放运算符。计算器的最下方有一个等号键&#xff0c;每次按下这个键&#xff0c;计算器就…