二叉搜索树BST的学习

文章目录

  • 二叉搜索树BST
    • 什么是BST?
    • 用BST做什么?
    • 一、BST的特性
      • BST的特性是什么?
      • 1.[230. 二叉搜索树中第K小的元素](https://leetcode.cn/problems/kth-smallest-element-in-a-bst/)
      • 2.[538. 把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree/)
    • 二、BST的基础操作
      • BST的基础操作有哪些?
      • 1.判断BST的合法性
        • [98. 验证二叉搜索树](https://leetcode.cn/problems/validate-binary-search-tree/)
          • 总结所出现的问题就是:
          • 那如何解决这个问题呢?
      • 2.在BTS中搜索元素
        • [700. 二叉搜索树中的搜索](https://leetcode.cn/problems/search-in-a-binary-search-tree/)
      • 3.在BST中插入一个数
        • [701. 二叉搜索树中的插入操作](https://leetcode.cn/problems/insert-into-a-binary-search-tree/)
      • 4.在BST中删除一个数
          • 但是找到目标节点,如何删除是一个难点!
            • 情况1:
            • 情况2:
            • 情况3:
      • 5.总结
    • 三、BST的构造(如何计算所有合法BST)
      • 1.[96. 不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees/)
        • A.那用什么方式能够正确地穷举合法BST的数量呢?
        • B.那对于每个根节点又会有几种不同的BST呢?
        • C.那我们如何得到组合的结果呢?
        • D.为什么base case返回1?
        • E.如何解决时间复杂度高问题?即如何消除重叠子问题?
      • 2.[95. 不同的二叉搜索树 II](https://leetcode.cn/problems/unique-binary-search-trees-ii/)

二叉搜索树BST

什么是BST?

  1. 对于 BST 的每一个节点 node,左子树节点的值都比 node 的值要小,右子树节点的值都比 node 的值大。
  2. 对于 BST 的每一个节点 node,它的左侧子树和右侧子树都是 BST。

二叉搜索树并不算复杂,但我觉得它可以算是数据结构领域的半壁江山,直接基于 BST 的数据结构有 AVL 树,红黑树等等,拥有了自平衡性质,可以提供 logN 级别的增删查改效率;还有 B+ 树,线段树等结构都是基于 BST 的思想来设计的。

用BST做什么?

要么利用 BST 左小右大的特性提升算法效率,要么利用中序遍历的特性满足题目的要求

一、BST的特性

BST的特性是什么?

从做算法题的角度来看 BST,除了它的定义,还有一个重要的性质:BST 的中序遍历结果是有序的(升序)

1.230. 二叉搜索树中第K小的元素

首先找一堆元素中的第k小的元素,直接的思路就是将这些元素按升序排,dik个就是满足条件的元素

根据BST的性质,所以我们就在遍历的时候找到符合条件的元素

写法1:

List<Integer> result=new ArrayList<>();
    public void traverse(TreeNode root){
        if(root==null) return;
        traverse(root.left);
        result.add(root.val);
        traverse(root.right);
    }
    public int kthSmallest(TreeNode root, int k) {
        traverse(root);
        int n=result.get(k-1);
        return n;
    }

写法2:

int kthSmallest(TreeNode root, int k) {
    // 利用 BST 的中序遍历特性
    traverse(root, k);
    return res;
}

// 记录结果
int res = 0;
// 记录当前元素的排名
int rank = 0;
void traverse(TreeNode root, int k) {
    if (root == null) {
        return;
    }
    traverse(root.left, k);
    /* 中序遍历代码位置 */
    rank++;
    if (k == rank) {
        // 找到第 k 小的元素
        res = root.val;
        return;
    }
    /*****************/
    traverse(root.right, k);
}

但是这样的做法并不是最高效的解法

1.为什么呢?

因为按照刚刚的方法,每次寻找第 k 小的元素都要中序遍历一次,最坏的时间复杂度是 O(N)N 是 BST 的节点个数。

要知道 BST 性质是非常牛逼的,像红黑树这种改良的自平衡 BST,增删查改都是 O(logN) 的复杂度,让你算一个第 k 小元素,时间复杂度竟然要 O(N),有点低效了。

所以说,计算第 k 小元素,最好的算法肯定也是对数级别的复杂度,不过这个依赖于 BST 节点记录的信息有多少。

2.为什么BST能在对数级别的复杂度内搜索某一个元素呢?

根本原因还是在 BST 的定义里,左子树小右子树大嘛,所以每个节点都可以通过对比自身的值判断去左子树还是右子树搜索目标值,从而避免了全树遍历,达到对数级复杂度。

3.如何实现?

关键也在于每个节点得知道他自己排第几。

比如说你让我查找排名为 k 的元素,当前节点知道自己排名第 m,那么我可以比较 mk 的大小:

  • 如果 m == k,显然就是找到了第 k 个元素,返回当前节点就行了。
  • 如果 k < m,那说明排名第 k 的元素在左子树,所以可以去左子树搜索第 k 个元素。
  • 如果 k > m,那说明排名第 k 的元素在右子树,所以可以去右子树搜索第 k - m - 1 个元素。

这样就可以将时间复杂度降到 O(logN) 了。

4.如何让每一个节点知道自己的排名呢?

这就是我们之前说的,需要在二叉树节点中维护额外信息。每个节点需要记录,以自己为根的这棵二叉树有多少个节点

也就是说,我们 TreeNode 中的字段应该如下:

class TreeNode {
 int val;
 // 以该节点为根的树的节点总数
 int size;
 TreeNode left;
 TreeNode right;
}

有了 size 字段,外加 BST 节点左小右大的性质,对于每个节点 node 就可以通过 node.left 推导出 node 的排名,从而做到我们刚才说到的对数级算法。

当然,size 字段需要在增删元素的时候需要被正确维护,力扣提供的 TreeNode 是没有 size 这个字段的,所以我们这道题就只能利用 BST 中序遍历的特性实现了,但是我们上面说到的优化思路是 BST 的常见操作,还是有必要理解的。

2.538. 把二叉搜索树转换为累加树

我们可以降序打印 BST 节点的值,通过维护一个外部累加变量 sum,然后把 sum 赋值给 BST 中的每一个节点,就将 BST 转化成累加树了。

这道题的核心还是 BST 的中序遍历特性,只不过我们修改了递归顺序,降序遍历 BST 的元素值,从而契合题目累加树的要求。

二、BST的基础操作

BST的基础操作有哪些?

判断 BST 的合法性、增、删、查。其中「删」和「判断合法性」略微复杂。

BST 的基础操作主要依赖「左小右大」的特性,可以在二叉树中做类似二分搜索的操作,寻找一个元素的效率很高。

对于 BST 相关的问题,你可能会经常看到类似下面这样的代码逻辑:

void BST(TreeNode root, int target) {
    if (root.val == target)
        // 找到目标,做点什么
    if (root.val < target) 
        BST(root.right, target);
    if (root.val > target)
        BST(root.left, target);
}

1.判断BST的合法性

以以下例题作讲解:

98. 验证二叉搜索树

判断合法性我们直接就会想到按照BST左小右大的特性,每个节点判断自己和自己的左右孩子是否满足左小右大。

因此很容易得出:

boolean isValidBST(TreeNode root) {
    if (root == null) return true;
    // root 的左边应该更小
    if (root.left != null && root.left.val >= root.val)
        return false;
    // root 的右边应该更大
    if (root.right != null && root.right.val <= root.val)
        return false;

    return isValidBST(root.left)
        && isValidBST(root.right);
}

但是这个算法是有问题的,因为BST 的每个节点应该要小于右边子树的所有节点

所以就有可能出现以下这样的二叉树我们也判定为是二叉搜索树(节点10的右子树中有一个节点6,显然这就不是二叉搜索树)

总结所出现的问题就是:

对于每一个节点 root,代码值检查了它的左右孩子节点是否符合左小右大的原则;

但是根据 BST 的定义,root 的整个左子树都要小于 root.val,整个右子树都要大于 root.val

那如何解决这个问题呢?

对于某一个节点 root,他只能管得了自己的左右子节点,怎么把 root 的约束传递给左右子树呢?

解决方法是我们通过使用辅助函数增加函数参数列表,在参数中携带额外信息,将这种约束传递给子树的所有节点,这也是二叉树算法的一个小技巧

public boolean isValidBST(TreeNode root) {
        return isValidBST(root,null,null);
    }
    public boolean isValidBST(TreeNode root,TreeNode min,TreeNode max){
        if(root==null) return true;
        if(min!=null&&root.val<=min.val) return false;
        if(max!=null&&root.val>=max.val) return false;
        return isValidBST(root.left,min,root)&&isValidBST(root.right,root,max);
    }

2.在BTS中搜索元素

700. 二叉搜索树中的搜索

如果在一棵普通的二叉树中寻找,可以这样写代码:

TreeNode searchTree(TreeNode root, int target);
    if (root == null) return null;
    if (root.val == target) return root;
    // 当前节点没找到就递归地去左右子树寻找
    TreeNode left = searchTree(root.left, target);
    TreeNode right = searchTree(root.right, target);

    return left != null ? left : right;
}

这样写就相当于穷举了所有节点,适用于所有二叉树

既然BST具有特殊性,那我们是否考虑将其特性用在搜索上,方便我们更快地找到目标元素?

根据BST左小右大的特性,我们不需要递归地搜索两边,类似二分查找思想,根据 targetroot.val 的大小比较,就能排除一边。

public TreeNode searchBST(TreeNode root, int val) {
        if (root == null) return null;
        int target=val;
        if(root.val==target) return root;
        if(root.val<target) return searchBST(root.right,target);
        if(root.val>target) return searchBST(root.left,target);
        return null;
    }

3.在BST中插入一个数

对数据结构的操作无非遍历 + 访问,遍历就是找,访问就是改。具体到这个问题,插入一个数,就是先找到插入位置,然后进行插入操作。

我们已经直到在BST中找一个数是怎样的框架,因此我们就在此框架上完成在BST中插入一个数。

在此之前我们要知道,一旦涉及改,就类似二叉树的构造问题,函数要返回 TreeNode 类型,并且要对递归调用的返回值进行接收

701. 二叉搜索树中的插入操作

public TreeNode insertIntoBST(TreeNode root, int val) {
        // 找到空位置插入新节点
        if(root==null) return new TreeNode(val);
         // if (root.val == val)
         // BST 中一般不会插入已存在元素
        if(root.val<val) {
            //对于递归调用的返回值进行接收
            root.right=insertIntoBST(root.right,val);
        }
        if(root.val>val){
            //对于递归调用的返回值进行接收
            root.left=insertIntoBST(root.left,val);
        }
        return root;
    }

4.在BST中删除一个数

这个问题稍微复杂,跟插入操作类似,先找再改,也是基于插入的框架

TreeNode deleteNode(TreeNode root, int key) {
    if (root.val == key) {
        // 找到啦,进行删除
    } else if (root.val > key) {
        // 去左子树找
        root.left = deleteNode(root.left, key);
    } else if (root.val < key) {
        // 去右子树找
        root.right = deleteNode(root.right, key);
    }
    return root;
}
但是找到目标节点,如何删除是一个难点!

因为删除目标节点A可能有三种情况并且删除之后不能破坏BST的性质

情况1:

A恰好是末端节点,两个子节点都为空,那就可以直接删除

在这里插入图片描述

if (root.left == null && root.right == null)
    return null;
情况2:

A只有一个非空节点,那么就让这个节点接替自己的位置

在这里插入图片描述

// 排除了情况 1 之后
if (root.left == null) return root.right;
if (root.right == null) return root.left;
情况3:

A有两个子节点,为了不破坏BST的性质,A必须找到左子树中最大的那个节点或者右子树中最小的那个节点来接替自己,然后就变成删除右子树最小节点

在这里插入图片描述

if (root.left != null && root.right != null) {
    // 找到右子树的最小节点
    TreeNode minNode = getMin(root.right);
    // 把 root 改成 minNode
    root.val = minNode.val;
    // 转而去删除 minNode
    //为什么这里是minNode.val,因为我们只是将最小节点的值给了目标节点,而没有将最小节点进行替换,可能会有疑问,为什么不替换呢,这样就还是删除key。但其实这一步是没必要的,因为即使替换,还是这个位置上的节点被删除的,所以就不替换,当作删除最小节点
    root.right = deleteNode(root.right, minNode.val);
}

但是我们一般不会通过修改节点内部的值来交换节点。因为在实际应用中,BST 节点内部的数据域是用户自定义的,可以非常复杂,而 BST 作为数据结构(一个工具人),其操作应该和内部存储的数据域解耦,所以我们更倾向于使用指针操作来交换节点,根本没必要关心内部数据。

所以接替行为变成这样

// 处理情况 3
// 获得右子树最小的节点
TreeNode minNode = getMin(root.right);
// 删除右子树最小的节点
root.right = deleteNode(root.right, minNode.val);
// 用右子树最小的节点替换 root 节点
minNode.left = root.left;
minNode.right = root.right;
root = minNode;

完整代码如下:

public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) return null;
        //1.
        if(root.val==key){
            //符合条件,删除
            //以下两个if包括了第一种叶子情况和第二种只有一个孩子的节点情况
            // if(root.left==null&&root.right==null) return null;
            if(root.left==null){
                return root.right;
            }
            if(root.right==null){
                return root.left;
            }
            if(root.left!=null&&root.right!=null){
                /*
                // 找到右子树的最小节点
                TreeNode minNode = getMin(root.right);
                // 把 root 改成 minNode
                root.val = minNode.val;
                // 转而去删除 minNode
                root.right = deleteNode(root.right, minNode.val);
                */
                // 处理情况 3
                // 获得右子树最小的节点
                TreeNode minNode = getMin(root.right);
                // 删除右子树最小的节点
                root.right = deleteNode(root.right, minNode.val);
                // 用右子树最小的节点替换 root 节点
                minNode.left = root.left;
                minNode.right = root.right;
                root = minNode;
            }
        
        }
        //2.
        if(root.val<key){
            root.right=deleteNode(root.right,key);
        }
        //3.
        if(root.val>key){
            root.left=deleteNode(root.left,key);
        }
        return root;
    }
    TreeNode getMin(TreeNode node) {
        // BST 最左边的就是最小的
        while (node.left != null) node = node.left;
        return node;
    }

5.总结

1、如果当前节点会对下面的子节点有整体影响,可以通过辅助函数增长参数列表,借助参数传递信息。

2、在二叉树递归框架之上,扩展出一套 BST 代码框架:

void BST(TreeNode root, int target) {
    if (root.val == target)
        // 找到目标,做点什么
    if (root.val < target) 
        BST(root.right, target);
    if (root.val > target)
        BST(root.left, target);
}

3、根据代码框架掌握了 BST 的增删查改操作

三、BST的构造(如何计算所有合法BST)

1.96. 不同的二叉搜索树

在这里插入图片描述

由上图的例子我们很容易的出这就是一个穷举问题

A.那用什么方式能够正确地穷举合法BST的数量呢?

在前面学习过二叉树之后我们都知道二叉树算法的关键就在于明确根节点需要做什么,其实 BST 作为一种特殊的二叉树,核心思路也是一样的。

例如:输入n = 5,也就是说用{1,2,3,4,5}这些数字去构造 BST。

显然就有基本的5种情况,因为每个数字都可以作为根节点。

B.那对于每个根节点又会有几种不同的BST呢?

例如我们固定3作为根节点

左子树节点就是{1,2}的组合,右子树就是{4,5}的组合

显然左子树的组合数和右子树的组合数乘积就是3作为根节点时的 BST 个数。

在这里插入图片描述

显然这对于其他节点也是一样的

C.那我们如何得到组合的结果呢?

显然这需要用递归实现

// 定义:闭区间 [lo, hi] 的数字能组成 count(lo, hi) 种 BST
int count(int lo, int hi);

结合上面的分析我们可以得到以下代码:

    public int numTrees(int n) {
    // 计算闭区间 [1, n] 组成的 BST 个数
        return count(1,n);
    }
/* 计算闭区间 [lo, hi] 组成的 BST 个数 */
    public int count(int low,int high){
        //base case
        if(low>high) return 1;
        int res=0;
        for(int i=low;i<=high;i++){
            //i作为根节点
            int left=count(low,i-1);
            int right=count(i+1,high);
            // 左右子树的组合数乘积是 BST 的总数
            res+=left*right;
        }
        return res;
    }

D.为什么base case返回1?

显然当lo > hi闭区间[lo, hi]肯定是个空区间,也就对应着空节点 null,虽然是空节点,但是也是一种情况,所以要返回 1 而不能返回 0。

但是这个代码时间复杂度很高,因为存在重叠子问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d55vqqIk-1680230716781)(D:\Development\Typora\img\image-20230331094114121.png)]

E.如何解决时间复杂度高问题?即如何消除重叠子问题?

这就想起动态规划消除重叠子问题的方法,就是加一个备忘录

备忘录记录的就是子问题,当再次遇到相同子问题的时候,把第一次遇到时得出的结果(在备忘录存着)返回就可以了

    //备忘录
    int[][] memory;
    public int numTrees(int n) {
        //备忘录初始化
        memory=new int[n+1][n+1];
        return count(1,n);
    }
    public int count(int low,int high){
        //base case
        if(low>high) return 1;
        if(memory[low][high]!=0){
            return memory[low][high];
        }
        int res=0;
        for(int i=low;i<=high;i++){
            //i作为根节点
            int left=count(low,i-1);
            int right=count(i+1,high);
            res+=left*right;
        }
        memory[low][high]=res;
        return res;
    }

2.95. 不同的二叉搜索树 II

做法和上一题很像

1、穷举root节点的所有可能。

2、递归构造出左右子树的所有合法 BST。

3、给root节点穷举所有左右子树的组合

public List<TreeNode> generateTrees(int n) {
        if(n==0) return new LinkedList<>();
        return build(1,n);
    }
    public List<TreeNode> build(int low,int high){
        //为什么在这里,这样每棵BST的节点才会分开分开来,可能会有一个疑问,那在递归调用左右子树的时候不就总是重新初始化吗?其实这个是没有关系的,因为在递归构造完左右子树之前result中都没有节点。
        List<TreeNode> result=new LinkedList<>();
        if(low>high){
            result.add(null);
            return result;
        } 
        for(int i=low;i<=high;i++){
            //递归生成左右子树的节点列表
            List<TreeNode> leftTree=build(low,i-1);
            List<TreeNode> rightTree=build(i+1,high);
            //给root节点穷举所有左右子树组合
            for(TreeNode left:leftTree){
                for(TreeNode right:rightTree){
                    TreeNode root=new TreeNode(i);
                    root.left=left;
                    root.right=right;
                    result.add(root);
                }
            }
        }
         return result;
    }

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

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

相关文章

Git Commit Message 应该怎么写?

原文链接&#xff1a; Git Commit Message 应该怎么写&#xff1f; 最近被同事吐槽了&#xff0c;说我代码提交说明写的太差。其实都不用他吐槽&#xff0c;我自己心里也非常清楚。毕竟很多时候犯懒&#xff0c;都是直接一个 -m "fix" 就提交上去了。 这样做是非常…

C语言变量

C 变量 变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型&#xff0c;类型决定了变量存储的大小和布局&#xff0c;该范围内的值都可以存储在内存中&#xff0c;运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下…

机器学习-卷积神经网络CNN中的单通道和多通道图片差异

背景 最近在使用CNN的场景中&#xff0c;既有单通道的图片输入需求&#xff0c;也有多通道的图片输入需求&#xff0c;因此又整理回顾了一下单通道或者多通道卷积的差别&#xff0c;这里记录一下探索过程。 结论 直接给出结论&#xff0c;单通道图片和多通道图片在经历了第一…

【hello C语言】结构体(下)

目录 1.结构体的声明 1.1 结构的声明 1.2 特殊声明&#xff1a;匿名结构体 1.3 结构的自引用 2. 结构体的定义和初始化 3. 结构体的内存对齐 3.1 内存对齐规则 3.2 内存对齐存在的原因 3.3 修改默认对其数 4. 结构体传参 C语言&#x1f6f4; 1.结构体的声明 结构体便是描述复杂…

一种适合容器化部署的雪花算法ID生成器

雪花算法简介 SnowFlake 中文意思为雪花&#xff0c;故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。 雪花算法有以下几个优点&#xff1a; 高并发分布式环境下生成不重复 id&#xff0c;每秒可生成百万个不重复 id。基于时间戳&#xff0c;以及同…

零编程经验,通过 GPT-4 十分钟开发了一个浏览器插件,并成功运行,实现了需求目标!

大佬蓝鸟ID: sundyme零编程经验&#xff0c;通过 GPT-4 十分钟开发了一个浏览器插件&#xff0c;并成功运行&#xff0c;实现了需求目标&#xff01;太不可思意了&#xff0c;真正体会到了自然语言编程的魅力&#xff01; 下一步是利用Pinterest 的 API 接口实现自动发图&#…

No.026<软考>《(高项)备考大全》【第10章】项目沟通和干系人管理(第2部分-干系人管理)

1 干系人管理部分相关 1.1 干系人ITO 1.2 干系人管理 过程过程的定义过程的作用识别干系人识别能影响项目决策、活动或结果的个人、群体或组织&#xff0c;以及被项目决策、活动或者结果影响的个人、群体或者组织&#xff0c;并分析和记录他们的相关信息的过程帮助项目经理建…

此战成硕,我成功上岸西南交通大学了~~~

友友们&#xff0c;好久不见&#xff0c;很长时间没有更一个正式点的文章了&#xff01; 是因为我在去年年底忙着准备初试&#xff0c;今年年初在准备复试&#xff0c;直到3月底拟录取后&#xff0c;终于可以写下这篇上岸贴&#xff0c;和大家分享一下考研至上岸的一个过程 文章…

游戏算法-游戏AI行为树,python实现

参考文章&#xff1a;Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念&#xff0c;用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计&#xff0c;使得游…

电子拣货标签9代系统简介

CK_Label_v9一、产品参数 产品型号 CK_Label_v9 LED 3&#xff08;红&黄&绿&#xff09;独立可控 供电方式 DC 24V 0.2A 通信方式 无线通信 蜂鸣器 支持 尺寸 D60 H307mm 二、革新点 配合标签拣货使用三个灯&#xff08;红黄绿&#xff09;都可以被独立控…

基于MATALB编程的BP神经网络手臂血管分类识别,基于BP神经网络的图像分类

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 数据 神经网络参数 基于BP神经网络手臂血管识别的MATLAB代码 效果图 结果分析 展望 背影 随着人工智能的发展&#xff0c;智…

贪心算法-删数问题C++

目录 一、题目 二&#xff1a;思路 代码 运行结果 一、题目 有一个长度为n&#xff08;n < 240&#xff09;的正整数&#xff0c;从中取出k&#xff08;k < n&#xff09;个数&#xff0c;使剩余的数保持原来的次序不变&#xff0c;求这个正整数经过删数之后最小是多…

用Python绘制六种可视化图表,简直太好用了

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python资料、源码、教程: 点击此处跳转文末名片获取 可视化图表&#xff0c;有相当多种&#xff0c;但常见的也就下面几种&#xff0c;其他比较复杂一点&#xff0c;大都也是基于如下几种进行组合&#xff0c;变换出来的。 …

记录--CSS 如何实现羽化效果?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 最近碰到这样一个问题&#xff0c;在一张封面上直接显示书名&#xff0c;可能会存在书名看不太清楚的情况(容易受到背景干扰)&#xff0c;如下 为了解决这个问题&#xff0c;设计师提了一个“究极”方…

算法 - 希尔排序

原理 首先将数组两两分组&#xff0c;分成n组数组&#xff0c;每组数组内部进行排序。再分成n/2组数组&#xff0c;每组数组内部进行排序。直至分成只剩一组&#xff0c;最后进行排序得到最后的数组。 代码 public static int[] shell(int[] arr) {int temp;for (int gra …

计算机图形学13:三维图形的几何变换

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、三维图形的几…

MongoDB综述【入门指南】

写这篇博客,正好是2023年4月5日15:29:31,是清明节,放假一天,我坐在我的小小租房室内,思考着没思考到啥,哈哈哈,感觉好着急啊!看完了一本《城南旧事》,但是就是不踏实,好吧~我来写一篇最近在学的一个技术 为了更优秀的自己~奥利给!! 首先,我们从最初级小白开始(因为自己也是小白…

卷麻了,00后测试用例写的比我还好,简直无地自容.....

前言 作为一个测试新人&#xff0c;刚开始接触测试&#xff0c;对于怎么写测试用例很头疼&#xff0c;无法接触需求&#xff0c;只能根据站在用户的角度去做测试&#xff0c;但是这样情况会导致不能全方位的测试APP&#xff0c;这种情况就需要一份测试用例了&#xff0c;但是不…

倾斜实景三维建模与BIM模型处理技术

倾斜实景三维建模与BIM模型处理技术 一、研究背景 ➢ 倾斜模型 ✓ 真实纹理&#xff0c;高分辨率 ✓ 真实坐标&#xff0c;高空间精度 ✓ 快速建模&#xff0c;低建模成本 ➢ BIM设计 BIM 技术作为建筑施工行业中的新技术&#xff0c;存在于建筑的全生命周期&#xff0c;服务…

机器学习算法:K近邻(k-nearest neighbors)初探

KNN的介绍和应用 KNN&#xff08;K-Nearest Neighbor&#xff09;算法是一种基于实例的学习算法&#xff0c;也是一种常见的分类算法。 如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别&#xff0c;则该样本也属于这个类别。 示例 &…