【蓝桥杯软件赛 零基础备赛20周】第7周——二叉树

文章目录

  • 1 二叉树概念
  • 2 二叉树的存储和编码
    • 2.1 二叉树的存储方法
    • 2.2 二叉树存储的编码实现
    • 2.3 二叉树的极简存储方法
  • 3 例题
  • 4 习题

前面介绍的数据结构数组、队列、栈,都是线性的,它们存储数据的方式是把相同类型的数据按顺序一个接一个串在一起。简单的形态使线性表难以实现高效率的操作。

二叉树是一种层次化的、高度组织性的数据结构。二叉树的形态使得它有天然的优势,在二叉树上做查询、插入、删除、修改、区间等操作极为高效,基于二叉树的算法也很容易实现高效率的计算。

1 二叉树概念

二叉树的每个节点最多有两个子节点,分别称为左孩子、右孩子,以它们为根的子树称为左子树、右子树。二叉树的每一层以2的倍数递增,所以二叉树的第k层最多有 2 k − 1 2^{k-1} 2k1 个节点。根据每一层的节点分布情况,有以下常见的二叉树。

(1)满二叉树

特征是每一层的节点数都是满的。第一层只有1个节点,编号为1;第二层有2个节点,编号2、3;第三层有4个节点,编号4、5、6、7;…;第k层有 2 k − 1 2^{k-1} 2k1 个节点,编号 2 k − 1 2^{k-1} 2k1 2 k − 1 + 1 2^{k-1}+1 2k1+1、…、 2 k − 1 2^k-1 2k1

一棵n层的满二叉树,节点一共有 1 + 2 + 4 + . . . + 2 n − 1 = 2 n − 1 1+2+4+...+2^{n-1} = 2^{n-1} 1+2+4+...+2n1=2n1 个。

在这里插入图片描述

(2)完全二叉树

如果满二叉树只在最后一层有缺失,并且缺失的节点都在最后,称为完全二叉树。上图演示了一棵满二叉树和一棵完全二叉树。

(3)平衡二叉树

任意左子树和右子树的高度差不大于1,称为平衡二叉树。若只有少部分子树的高度差超过1,这是一棵接近平衡的二叉树。

在这里插入图片描述

(4)退化二叉树

如果树上每个节点都只有1个孩子,称为退化二叉树。退化二叉树实际上已经变成了一根链表。如果绝大部分节点只有1个孩子,少数有2个孩子,也看成退化二叉树。

二叉树之所以应用广泛,得益于它的形态。高级数据结构大部分和二叉树有关,下面列举二叉树的一些优势。

(1)在二叉树上能进行极高效率的访问。一棵平衡的二叉树,例如满二叉树或完全二叉树,每一层的节点数量约是上一层数量的2倍,也就是说,一棵有N个节点的满二叉树,树的高度是O(logN)。从根节点到叶子节点,只需要走logN步,例如N = 100万,树的高度仅有logN = 20,只需要20步就能到达100万个节点中的任意一个。但是,如果二叉树不是满的,而且很不平衡,甚至在极端情况下变成退化二叉树,访问效率会打折扣。维护二叉树的平衡是高级数据结构的主要任务之一。

(2)二叉树很适合做从整体到局部、从局部到整体的操作。二叉树内的一棵子树可以看成整棵树的一个子区间,求区间最值、区间和、区间翻转、区间合并、区间分裂等,用二叉树都很快捷。

(3)基于二叉树的算法容易设计和实现。例如二叉树用BFS和DFS搜索处理都极为简便。二叉树可以一层一层地搜索,这是BFS。二叉树的任意一个子节点,是以它为根的一棵二叉树,这是一种递归的结构,用DFS访问二叉树极容易编码。

2 二叉树的存储和编码

2.1 二叉树的存储方法

要使用二叉树,首先得定义和存储它的节点。

二叉树的一个节点包括三个值:节点的值、指向左孩子的指针、指向右孩子的指针。需要用一个结构体来定义二叉树。

二叉树的节点有动态和静态两种存储方法,竞赛中一般采用静态方法。

(1)动态存储二叉树。例如写c代码,数据结构的教科书一般这样定义二叉树的节点:

struct Node{
    int value;           //节点的值,可以定义多个值
    Node *lson, *rson;   //指针,分别指向左右子节点    
};

其中value是这个节点的值,lson和rson是指向两个孩子的指针。动态新建一个Node时,用new运算符动态申请一个节点。使用完毕后,需要用delete释放它,否则会内存泄漏。动态二叉树的优点是不浪费空间,缺点是需要管理,不小心会出错,竞赛中一般不这样用。

(2)用静态数组存储二叉树。在算法竞赛中,为了编码简单,加快速度,一般用静态数组来实现二叉树。下面定义一个大小为N的结构体数组。N的值根据题目要求设定,有时节点多,例如N=100万,那么tree[N]使用的内存是12M字节,不算大。

struct Node{                //静态二叉树
   int value;               //可以把value简写为v
   int lson, rson;          //左右孩子,可以把lson、rson简写为ls、rs
}tree[N];                   //可以把tree简写为t

tree[i]表示这个节点存储在结构体数组的第i个位置,lson是它的左孩子在结构体数组的位置,rson是它的右孩子在结构体数组的位置。lson和rson指向孩子的位置,也可以称为指针。

下图演示了一棵二叉树的存储,圆圈内的字母是这个节点的value值。根节点存储在tree[5]上,它的左孩子lson=7,表示左孩子存储在tree[7]上,右孩子rson=3,存储在tree[3]。
在这里插入图片描述

编码时一般不用tree[0],因为0常常被用来表示空节点,例如叶子节点tree[2]没有子节点,就把它的子节点赋值为lson = rson = 0。

2.2 二叉树存储的编码实现

下面写代码演示上图中二叉树的建立,并输出二叉树。

(1)C++代码。第16~21行建立二叉树,然后用print_tree()输出二叉树。

#include <bits/stdc++.h>
using namespace std;
const int N=100;               //注意const不能少
struct Node{                   //定义静态二叉树结构体
   char v;                     //把value简写为v
   int ls, rs;                 //左右孩子,把lson、rson简写为ls、rs
}t[N];                         //把tree简写为t
void print_tree(int u){        //打印二叉树
    if(u){
        cout<<t[u].v<<' ';     //打印节点u的值
        print_tree(t[u].ls);   //继续搜左孩子
        print_tree(t[u].rs);   //继续搜右孩子
    }
}
int main(){
    t[5].v='A'; t[5].ls=7; t[5].rs=3;
    t[7].v='B'; t[7].ls=2; t[7].rs=0;
    t[3].v='C'; t[3].ls=9; t[3].rs=6;
    t[2].v='D';    // t[2].ls=0; t[2].rs=0; 可以不写,因为t[]是全局变量,已初始化为0
    t[9].v='E';    // t[9].ls=0; t[9].rs=0; 可以不写
    t[6].v='F';    // t[6].ls=0; t[6].rs=0; 可以不写
    int root = 5;  //根是tree[5]
    print_tree(5); //输出: A B D C E F
    return 0;
}

初学者可能看不懂print_tree()是怎么工作的。它是一个递归函数,先打印这个节点的值t[u].v,然后继续搜它的左右孩子。上图的打印结果是”A B D C E F”,步骤如下:
  
  (1)首先打印根节点A;
  (2)然后搜左孩子,是B,打印出来;
  (3)继续搜B的左孩子,是D,打印出来;
  (4)D没有孩子,回到B,B发现也没有右孩子,继续回到A;
  (5)A有右孩子C,打印出来;
  (6)打印C的左右孩子E、F。

这个递归函数执行的步骤称为“先序遍历”,先输出父节点,然后再搜左右孩子并输出。还有“中序遍历”和“后序遍历”,将在后面讲解。

(2)Java代码

import java.util.*;
class Main {
    static class Node {
        char v;
        int ls, rs;
    }
    static final int N = 100;
    static Node[] t = new Node[N];
    static void print_tree(int u) {
        if (u != 0) {
            System.out.print(t[u].v + " ");
            print_tree(t[u].ls);
            print_tree(t[u].rs);
        }
    }
    public static void main(String[] args) {
        t[5] = new Node(); t[5].v = 'A'; t[5].ls = 7; t[5].rs = 3;
        t[7] = new Node(); t[7].v = 'B'; t[7].ls = 2; t[7].rs = 0;
        t[3] = new Node(); t[3].v = 'C'; t[3].ls = 9; t[3].rs = 6;
        t[2] = new Node(); t[2].v = 'D';
        t[9] = new Node(); t[9].v = 'E';
        t[6] = new Node(); t[6].v = 'F';
        int root = 5;
        print_tree(5); // 输出: A B D C E F
    }
}

(3)Python代码

N = 100
class Node:   # 定义静态二叉树结构体
    def __init__(self):
        self.v = ''              # 把value简写为v
        self.ls = 0              # 左右孩子,把lson、rson简写为ls、rs
        self.rs = 0
t = [Node() for i in range(N)]   # 把tree简写为t
def print_tree(u):
    if u:
        print(t[u].v, end=' ')   # 打印节点u的值
        print_tree(t[u].ls)
        print_tree(t[u].rs)
t[5].v, t[5].ls, t[5].rs = 'A', 7, 3
t[7].v, t[7].ls, t[7].rs = 'B', 2, 0
t[3].v, t[3].ls, t[3].rs = 'C', 9, 6
t[2].v = 'D'    # t[2].ls=0; t[2].rs=0; 可以不写,因为t[]已初始化为0
t[9].v = 'E'    # t[9].ls=0; t[9].rs=0; 可以不写
t[6].v = 'F'    # t[6].ls=0; t[6].rs=0; 可以不写
root = 5        # 根是tree[5]
print_tree(5)   # 输出: A B D C E F

2.3 二叉树的极简存储方法

如果是满二叉树或者完全二叉树,有更简单的编码方法,连lson、rson都不需要定义,因为可以用数组的下标定位左右孩子。

一棵节点总数量为k的完全二叉树,设1号点为根节点,有以下性质:

(1) p > 1 p > 1 p>1的节点,其父节点是 ⌊ p / 2 ⌋ \lfloor p/2 \rfloor p/2。例如 p = 4 p=4 p=4,父亲是 4 / 2 = 2 4/2=2 4/2=2 p = 5 p=5 p=5,父亲是 5 / 2 = 2 5/2=2 5/2=2
(2)如果 2 × p > k 2×p> k 2×p>k,那么 p p p没有孩子;如果 2 × p + 1 > k 2×p+1 > k 2×p+1>k,那么 p p p没有右孩子。例如 k = 11 k=11 k=11 p = 6 p=6 p=6的节点没有孩子; k = 12 k=12 k=12 p = 6 p=6 p=6的节点没有右孩子。
(3)如果节点 p p p有孩子,那么它的左孩子是 2 × p 2×p 2×p,右孩子是 2 × p + 1 2×p+1 2×p+1
在这里插入图片描述

图中圆圈内是节点的值,圆圈外数字是节点存储位置。

(1)C++代码。

l s ( p ) ls(p) ls(p)找p的左孩子,用 r s ( p ) rs(p) rs(p)找p的右孩子。 l s ( p ) ls(p) ls(p)中把 p ∗ 2 p*2 p2写成 p < < 1 p<<1 p<<1,用了位运算。

#include <bits/stdc++.h>
using namespace std;
const int N=100;                   //注意const不能少
char t[N];                         //简单地用一个数组定义二叉树
int ls(int p){return p<<1;}        //定位左孩子,也可以写成 p*2
int rs(int p){return p<<1 | 1;}    //定位右孩子,也可以写成 p*2+1
int main(){
    t[1]='A';  t[2]='B';  t[3]='C';
    t[4]='D';  t[5]='E';  t[6]='F';  t[7]='G';
    t[8]='H';  t[9]='I';  t[10]='J'; t[11]='K';
    cout<<t[1]<<":lson="<<t[ls(1)]<<" rson="<<t[rs(1)]; //输出  A:lson=B rson=C
    cout<<endl;
    cout<<t[5]<<":lson="<<t[ls(5)]<<" rson="<<t[rs(5)]; //输出  E:lson=J rson=K
    return 0;
}

(2)Java代码。

import java.util.Arrays;
public class Main {
    static int ls(int p){ return p<<1;}
    static int rs(int p){ return p<<1 | 1;}
    public static void main(String[] args) {
        final int N = 100;
        char[] t = new char[N];
        t[1]='A';  t[2]='B';  t[3]='C';
        t[4]='D';  t[5]='E';  t[6]='F';  t[7]='G';
        t[8]='H';  t[9]='I';  t[10]='J'; t[11]='K';
        System.out.print(t[1]+":lson="+t[ls(1)]+" rson="+t[rs(1)]);//输出A:lson=B rson=C
        System.out.println();
        System.out.print(t[5]+":lson="+t[ls(5)]+" rson="+t[rs(5)]);//输出E:lson=J rson=K
    }
}

(3)Python代码。

N = 100
t = [''] * N
def ls(p):  return p << 1
def rs(p):  return (p << 1) | 1

t[1] = 'A'; t[2] = 'B'; t[3] = 'C'
t[4] = 'D'; t[5] = 'E'; t[6] = 'F'; t[7] = 'G'
t[8] = 'H'; t[9] = 'I'; t[10] = 'J'; t[11] = 'K'

print(t[1] + ':lson=' + t[ls(1)] + ' rson=' + t[rs(1)]) # 输出  A:lson=B rson=C
print(t[5] + ':lson=' + t[ls(5)] + ' rson=' + t[rs(5)]) # 输出  E:lson=J rson=K

其实,即使二叉树不是完全二叉树,而是普通二叉树,也可以用这种简单方法来存储。如果某个节点没有值,那就空着这个节点不用,方法是把它赋值为一个不该出现的值,例如赋值为0或无穷大INF。这样会浪费一些空间,好处是编程非常简单。

3 例题

二叉树是很基本的数据结构,大量算法、高级数据结构都是基于二叉树的。二叉树有很多操作,最基础的操作是搜索(遍历)二叉树的每个节点,有先序遍历、中序遍历、后序遍历。这3种遍历都用到了递归函数,二叉树的形态天然适合用递归来编程。
在这里插入图片描述

(1)先(父)序遍历,父节点在最前面输出。先输出父节点,再访问左孩子,最后访问右孩子。上图的先序遍历结果是ABDCEF。为什么?把结果分解为:A-BD-CEF。父亲是A,然后是左孩子B和它带领的子树BD,最后是右孩子C和它带领的子树CEF。这是一个递归的过程,每个子树也满足先序遍历,例如CEF,父亲是C,然后是左孩子E,最后是右孩子F。

(2)中(父)序遍历,父节点在中间输出。先访问左孩子,然后输出父节点,最后访问右孩子。上图的中序遍历结果是DBAECF。为什么?把结果分解为:DB-A-ECF。DB是左子树,然后是父亲A,最后是右子树ECF。每个子树也满足中序遍历,例如ECF,先左孩子E,然后是父亲C,最后是右孩子F。

(3)后(父)序遍历,父节点在最后输出。先访问左孩子,然后访问右孩子,最后输出父节点。上图的后序遍历结果是DBEFCA。为什么?把结果分解为:DB-EFC-A。DB是左子树,然后是右子树EFC,最后是父亲A。每个子树也满足后序遍历,例如EFC,先左孩子E,然后是右孩子F,最后是父亲C。

这三种遍历,中序遍历是最有用的,它是二叉查找树的核心。

例题 二叉树的遍历

(1)C++代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Node{
    int v; int ls, rs;
}t[N];                          //tree[0]不用,0表示空结点
void preorder (int p){          //求先序序列
    if(p != 0){
        cout << t[p].v <<" ";   //先序输出
        preorder (t[p].ls);
        preorder (t[p].rs);
    }
}
void midorder (int p){          //求中序序列
    if(p != 0){
        midorder (t[p].ls);
        cout << t[p].v <<" ";   //中序输出
        midorder (t[p].rs);
    }
}
void postorder (int p){         //求后序序列
    if(p != 0){
        postorder (t[p].ls);
        postorder (t[p].rs);
        cout << t[p].v <<" ";    //后序输出
    }
}
int main() {
	int n;	cin >> n;
	for (int i = 1; i <= n; i++) {
		int a, b; cin >> a >> b;
		t[i].v = i;
		t[i].ls = a;
		t[i].rs = b;
	}
	preorder(1);  cout << endl;
	midorder(1);  cout << endl;
    postorder(1); cout << endl;
}

(2)Java代码

下面的Java代码和上面的C++代码略有不同。例如在preorder()中没有直接打印节点的值,而是用joiner.add()先记录下来,遍历结束后一起打印,这样快一些。本题 n = 1 0 6 n=10^6 n=106 ,规模大,时间紧张。

import java.util.Scanner;
import java.util.StringJoiner;
class Main {
    static class Node {
        int v, ls, rs;
        Node(int v, int ls, int rs) {
            this.v = v;
            this.ls = ls;
            this.rs = rs;
        }
    }
    static final int N = 100005;
    static Node[] t = new Node[N];                     //tree[0]不用,0表示空结点
    static void preorder(int p, StringJoiner joiner) { //求先序序列
        if (p != 0) {
        	  joiner.add(t[p].v + "");   //不是直接打印,而是先记录下来
            preorder(t[p].ls,joiner);
            preorder(t[p].rs,joiner);
        }
    }
    static void midorder(int p, StringJoiner joiner) { //求中序序列
        if (p != 0) {
            midorder(t[p].ls,joiner);
            joiner.add(t[p].v + "");//中序输出
            midorder(t[p].rs,joiner);
        }
    }
    static void postorder(int p, StringJoiner joiner) { //求后序序列
        if (p != 0) {
            postorder(t[p].ls,joiner);
            postorder(t[p].rs,joiner);
            joiner.add(t[p].v + ""); //后序输出
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            int a = sc.nextInt(), b = sc.nextInt();
            t[i] = new Node(i, a, b);
        }
        StringJoiner joiner = new StringJoiner(" ");        
        preorder(1, joiner);        System.out.println(joiner);        
        joiner = new StringJoiner(" ");
        midorder(1, joiner);        System.out.println(joiner);
        joiner = new StringJoiner(" ");
        postorder(1, joiner);       System.out.println(joiner);
    }
}

(3)Python代码

N = 100005
t = [0] * N  # tree[0]不用,0表示空结点
class Node:
    def __init__(self, v, ls, rs):
        self.v = v
        self.ls = ls
        self.rs = rs

def preorder(p):  # 求先序序列
    if p != 0:
        print(t[p].v, end=' ')  # 先序输出
        preorder(t[p].ls)
        preorder(t[p].rs)

def midorder(p):  # 求中序序列
    if p != 0:
        midorder(t[p].ls)
        print(t[p].v, end=' ')  # 中序输出
        midorder(t[p].rs)

def postorder(p):  # 求后序序列
    if p != 0:
        postorder(t[p].ls)
        postorder(t[p].rs)
        print(t[p].v, end=' ')  # 后序输出

n = int(input())
for i in range(1, n+1):
    a, b = map(int, input().split())
    t[i] = Node(i, a, b)

preorder(1);  print()
midorder(1);  print()
postorder(1); print()

4 习题

完全二叉树的权值

FBI树

American Heritage

求先序排列

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

污水处理成套设备如何选择

污水处理是现代社会中不可或缺的一个重要环节&#xff0c;它涉及到环保领域&#xff0c;与人们的生活和健康息息相关。而污水处理成套设备的选择则显得尤为重要&#xff0c;因为合适的设备能够有效地解决水污染问题&#xff0c;提高环境质量。 在选择污水处理成套设备时&#x…

DRF-源码解析-3-权限流程:drf的权限源码解析,drf的权限流程。

一、代码的准备 视图&#xff1a; class TestAPIView(APIView):authentication_classes[MyJWTAuthentication]permission_classes [AdminPermission,]def get(self,request)return Respponse({code:200,msg:测试通过})路由&#xff1a; path(test/,views.TestAPIView.as_vi…

Weblogic安全漫谈(三)

本篇介绍coherence.jar中的漏洞利用链及后续绕过。 经历2015到2018的3年迭代后&#xff0c;Weblogic的黑名单逐渐完善&#xff0c;废掉CC反序列化更是釜底抽薪。另一方面也促使研究员去挖掘新组件新利用链&#xff0c;这篇介绍的就是testbnull在发现Spring写文件链后[1]&#…

spring boot支付宝沙箱环境测试支付功能

目录 一、安装支付宝支付demo 二、配置demo信息 三、配置回调地址和异步地址 四、内网穿透 &#xff08;一&#xff09;简介 &#xff08;二&#xff09;使用场景 &#xff08;三&#xff09;内网穿透的几个常用软件 &#xff08;四&#xff09;使用natapp 一、安装支付…

ckplayer如何设置键盘的方向左和方向右是快退或快进多少秒?

默认是20秒&#xff0c;那怎么按照自定义的配置呢&#xff1f; 打开文件&#xff1a;“.\ckplayer\js\ckplayer.js” 然后在下面的函数中修改就可以了&#xff1a; 下面的代码我已经修改为了按一次方向左键为快退3秒&#xff0c;按一次方向右键为快进5秒。 /** fastBack* 功能&…

第03章_运算符与流程控制

第03章_运算符与流程控制 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题脉络 1. 运算符&#xff08;Operator&#xff09; 运算符是一种特殊的符号&#xff0c;用以表示数据的运算、赋…

DevOps搭建(十五)-kubernetes部署项目详细步骤

1、k8s简介 k8s官网地址 https://kubernetes.io/zh-cn/docs/home/ 2、安装kuboard 详细步骤可参考官网 https://kuboard.cn/install/install-k8s.html 2.1、环境准备 至少 2 台 2核4G 的服务器。 选择v1.19&#xff0c;因为高版本的已经把docker给舍弃掉了。 https://k…

Vue3插件开发教程:步步指导如何编写Vue3插件

关注⬆️⬆️⬆️⬆️ 专栏后期更新更多前端内容 文章目录 Vue3 插件插件注册形式插件主要的场景使用插件Vue3 插件 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。 插件注册形式 一个插件可以是一个拥有 install() 方法的对象,也可以直接是一个安装函数本身。 i…

Transformer从菜鸟到新手(三)

引言 这是Transformer的第三篇文章&#xff0c;上篇文章中我们了解了多头注意力和位置编码&#xff0c;本文我们继续了解Transformer中剩下的其他组件。 下篇文章会介绍完整的训练过程。 层归一化 层归一化想要解决一个问题&#xff0c;这个问题在Batch Normalization的论文…

【JaveWeb教程】(1)Web前端基础:HTML+CSS入门不再难:一篇文章教你轻松搞定HTML与CSS!

目录 1. 前端开发介绍2. HTML & CSS2.1 HTML快速入门2.1.1 操作2.1.2 总结 2.2 开发工具2.3 基础标签 & 样式2.3.1 新浪新闻-标题实现2.3.1.1 标题排版2.3.1.1.1 分析2.3.1.1.2 标签2.3.1.1.2 实现 2.3.1.2 标题样式2.3.1.2.1 CSS引入方式2.3.1.2.2 颜色表示2.3.1.2.3 …

书生·浦语大模型趣味 Demo笔记及作业

文章目录 笔记作业基础作业&#xff1a;进阶作业&#xff1a; 笔记 书生浦语大模型InternLM-Chat-7B 智能对话 Demo&#xff1a;https://blog.csdn.net/m0_49289284/article/details/135412067书生浦语大模型Lagent 智能体工具调用 Demo&#xff1a;https://blog.csdn.net/m0_…

大模型日报-20240108

M(2)UGen&#xff1a;利用 LLM 理解和生成音乐 https://github.com/shansongliu/M2UGen M (2) UGen 模型是一种音乐理解和生成模型&#xff0c;能够从文本、图像、视频和音频中进行音乐问答和音乐生成&#xff0c;以及音乐编辑。该模型利用编码器&#xff0c;如用于音乐理解的…

代码随想录day60:贪心算法|84.柱状图中最大的矩形

84. Largest Rectangle in Histogram 进行优化&#xff0c;如果我们想获得left就给他left即可&#xff0c;我们只需要在求宽度的时候用到left,而没必要修改原数组。 所以给栈插入一个虚拟索引-1 思考过程&#xff1a; left应该为多少呢&#xff1f; 首先确定left是什么&#…

吴飞教授 人工智能 模型与算法 启发式搜索课件发散分析

一、文章介绍 本文是针对吴飞教授在MOOC课程 &#xff1a;《人工智能&#xff1a;模型与算法》 2.1节 启发式搜索的课前发散 在课程2.1节 启发式搜索章节中&#xff0c;吴飞教授以如何计算城市地图两点之间最短路径为例&#xff0c;重点讲授了贪婪最佳优先搜索和A*搜索算法&a…

Materialise Mimics各版本安装指南

Materialise Mimics下载链接 https://pan.baidu.com/s/1GYnAuXfbgk_n-OXLNSOt6w?pwd0531 1.鼠标右击【Materialise Mimics21】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 Materialise Mimics21】。 2.打开解压后的文件夹&#xff0c;鼠…

网页设计与制作web前端设计html+css+js成品。电脑网站制作代开发。vscodeDrea 【企业公司宣传网站(HTML静态网页项目实战)附源码】

网页设计与制作web前端设计htmlcssjs成品。电脑网站制作代开发。vscodeDrea 【企业公司宣传网站&#xff08;HTML静态网页项目实战&#xff09;附源码】 https://www.bilibili.com/video/BV1Hp4y1o7RY/?share_sourcecopy_web&vd_sourced43766e8ddfffd1f1a1165a3e72d7605

大学物理-实验篇——用拉伸法测定金属丝的杨氏(弹性)模量(胡克定律、杨氏模量、平面反射镜、三角函数、螺旋测微器)

目录 预备知识 力学&#xff1a;胡克定律&#xff08;Hookes law&#xff09; 材料力学&#xff1a;杨氏模量 光学&#xff1a;平面反射镜 数学&#xff1a;三角函数 螺旋测微器 实验目的 实验仪器 实验原理 1.拉伸法测杨氏弹性模量 2.光杠杆放大法测量微小伸长量 …

Mac M1 Parallels CentOS7.9 Install Parallels Tools

一、挂载parallels-tools安装包 mkdir /media/cdrom/ mount /dev/cdrom /media/cdrom/ mount: /dev/sr0 写保护&#xff0c;将以只读方式挂载二、GCC升级 yum install -y centos-release-scl yum install -y devtoolset-8-gcc*# 切换当前会话中gcc版本为8 scl enable devtool…

HarmonyOS应用开发学习笔记 UIAbility组件与UI的数据同步 EventHub、globalThis

1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器&#xff1a;定义组件重用样式 Extend装饰器&#xff1a;定义扩展组件样式 5、HarmonyOS 应用开发…

重磅!图扑软件获评国家级专精特新“小巨人”企业

2023 年 7 月&#xff0c;工业和信息化部审核并公布了第五批国家级专精特新“小巨人”企业&#xff0c;图扑软件成功入选&#xff0c;荣膺国家级专精特新“小巨人”企业称号。 此次荣获国家级专精特新“小巨人”企业称号&#xff0c;不仅是对图扑软件在可视化和数字孪生领域专业…