二叉树和堆(优先队列)

前言:

        本章会讲解二叉树及其一些相关练习题,和堆是什么。

二叉树:

二叉树的一些概念:

        一棵二叉树是有限节点的集合,该集合可能为空。二叉树的特点是每一个节点最多有两个子树,即二叉树不存在度大于2的节点。且二叉树子树有左右之分,子树顺序不能颠倒。

        还有两种特殊的二叉树,完全二叉树和满二叉树。

        满二叉树是就是没有度为1的节点。所以当有k层时,它有2^k -1个节点。

        完全二叉树有度为1的节点且是连续的。

        所以我们可以根据节点的个数计算树的高度。

二叉树的性质: 

        若规定根节点层数是1,则一颗非空二叉树第i层上最多有2^(i-1)个节点。

        若规定根节点层数是1,则深度为h的二叉树的最大节点数为2^h-1个节点。

        对任何一颗二叉树如果度为0的节点数是n0,度为2的节点数是n2,则有n0=n2+1。

        若规定根节点层数为1,则有n个节点的满二叉树深度为h=LogN。

         在具有2n个节点的完全二叉树中,叶子结点个数为n。

练习题:

二叉树的最大深度:

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        思路:整棵树的高度 = (左子树的高度 + 右子树的高度)的最大值 + 1。

        其实也就是求树的高度,这里我们利用递归来实现:

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }

        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

判断是否为平衡二叉树: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        这里需要用到二叉树的最大深度来完成:

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }

        return treeHeigth(root) >= 0;

        //时间复杂度:O(n)
    }
    public int treeHeigth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        int leftHeigth = treeHeigth(root.left);
        if (leftHeigth < 0) {
            return -1;
        }
        int rightHeigth = treeHeigth(root.right);

        if (leftHeigth >= 0 && rightHeigth >= 0
        && Math.abs(leftHeigth - rightHeigth) <= 1) {
            return Math.max(leftHeigth, rightHeigth) + 1;
        } else {
            return -1;
        }
    }
}

相同的树: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q != null || p != null && q == null) {
            return false;
        }

        //此时,要么两个都为空 要么两个都不为空
        if (p == null && q == null) {
            return true;
        }

        //此时两个都不为空
        if (p.val != q.val) {
            return false;
        }

        //p != null && q != null && p.val == q.val
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);

        //时间复杂度为min(n,m)
    }
}

另一棵树的子树: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        这里我们需要用到判断两树是否相同的代码:

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        //可能会有空的情况
        if (root == null || subRoot == null) {
            return false;
        }
        //类似于BF算法
        //1.是不是和根节点相同
        if (isSameTree(root, subRoot)) {
            return true;
        }

        //2.判断是不是root的左子树
        if (isSubtree(root.left, subRoot)){
            return true;
        }

        //3.右子树
        if (isSubtree(root.right, subRoot)) {
            return true;
        }
        
        //4.返回
        return false;
        //时间复杂度 O(M * N)
    }
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q != null || p != null && q == null) {
            return false;
        }

        //此时,要么两个都为空 要么两个都不为空
        if (p == null && q == null) {
            return true;
        }

        //此时两个都不为空
        if (p.val != q.val) {
            return false;
        }

        //p != null && q != null && p.val == q.val
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);

        //时间复杂度为min(n,m)
    }
}

翻转二叉树: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        invertTree(root.left);
        invertTree(root.right);

        return root;
    }
}

对称二叉树: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public boolean isSymmetric(TreeNode root) {
        //根的值一样
        //1.左树的左 和 右树的右
        //2.左树的右 和 右树的左
        if (root == null) {
            return true;
        }

        return isSymmetricChild(root.left, root.right);
    }
    
    private boolean isSymmetricChild(TreeNode leftTree, TreeNode rightTree) {
        if (leftTree == null && rightTree != null || leftTree != null && rightTree == null) {
            return false;
        }

        if (leftTree == null && rightTree == null) {
            return true;
        }
        if (leftTree.val != rightTree.val) {
            return false;
        }
        
        return isSymmetricChild(leftTree.left, rightTree.right) 
        && isSymmetricChild(leftTree.right, rightTree.left);
    }
}

最近公共祖先: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        //利用链表相交的道理
        //之后利用栈来完成
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        getPath(root, p, stack1);
        getPath(root, q, stack2);

        while (stack1.size() != stack2.size()) {
            if (stack1.size() > stack2.size()) {
                stack1.pop();
            } else {
                stack2.pop();
            }
        }

        while (stack1.peek() != stack2.peek()) {
            stack1.pop();
            stack2.pop();
        }
        return stack1.peek();

    }

    private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
        if (root == null || node == null) {
            return false;
        }
        stack.push(root);

        if (root == node) {
            return true;
        }

        boolean flag1 = getPath(root.left, node, stack);
        if (flag1 == true) {
            return true;
        }

        boolean flag2 = getPath(root.right, node, stack);
        if (flag2 == true) {
            return true;
        }

        stack.pop();
        return false;
    }
}

求树的第K层节点个数:

        求树的第k层节点个数,如果不用层序遍历,我们可以使用递归。

        思路:整棵树第k层多少个节点 = 左子树的第k-1层节点 + 右子树的第k-1层节点。

        A的第3层 = A左树的第2层 + A右树的第2层

int CountLevel(TreeNode root, int k) {
    if (root == null) {
        return 0;
    }
    if (k == 1) {
        return 1;
    }
    return CountLevel(root.left, k - 1) + CountLevel(root.right, k - 1);
} 

找节点:

        我们要找一个节点的位置,找到返回它的地址,否则返回null。

TreeNode find(TreeNode root, char val) {
    if (root == null) {
        return null;
    }
    if (root.val = val) {
        return root;
    }

    TreeNode ret1 = find(root.left, val);
    if (ret1 != null) {
        return ret1;//不去右边了,因为找到了
    }

    TreeNode ret2 = find(root.right, val);
    if (ret2 != null) {
        return ret2;
    }
    return null;
}

根据树的前序遍历构建一棵树:

        oj链接:二叉树遍历__牛客网

class TreeNode {
    public char val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(char val) {
        this.val = val;
    }
}

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static int i = 0;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String str = in.nextLine();
            TreeNode root = creatNode(str);
            inorder(root);

            
        }
    }

    public static TreeNode creatNode(String str) {
            
        //1.遍历str
        // for (int i = 0; i < str.length(); i++) {
        //     char ch = str.charAt(i);
        // }

        TreeNode root = null;
        if (str.charAt(i) != '#') {
            root = new TreeNode(str.charAt(i));
            i++;

            root.left = creatNode(str);
            root.right = creatNode(str);

        } else {
            i++;
        }

        //2.根据字符串创建二叉树

        //3.返回根节点
        return root;
    }

    public static void inorder(TreeNode root) {
        if (root == null) {
            return ;
        }
        inorder(root.left);
        System.out.print(root.val + " ");
        inorder(root.right);
    }
    
}

判断是否为完全二叉树: 

        12节(2:44)。

        我们利用层序遍历,每次都把所有节点加入队列,包括null。之后遇到null就跳出,之后再判断(此时如果是完全二叉树,则队列中所有元素为null;否则则不是完全二叉树)。

boolean isCompleteTree(TreeNode root) {
    if (root == null) {
        return true;
    }
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        TreeNode cur = queue.poll();
        if (cur != null) {
            queue.offer(cur.left);
            queue.offer(cur.right);
        } else {
            break;
        }
    }
    
    //判断队列中是否有非空元素
    while (!queue.isEmpty()) {
        TreeNode cur = queue.peek();
        if (cur == null) {
            queue.poll();
        } else {
            return false;
        }
    }
    
    return true;
}

        这里我们使用队列的性质来完成。 

层序遍历:

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        
        //利用队列
        List<List<Integer>> link = new ArrayList<>();

        Queue<TreeNode> queue = new ArrayDeque<>();
        if (root != null) {
            queue.offer(root);
        }
        while(!queue.isEmpty()) {
            int n = queue.size();
            List<Integer> level = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                TreeNode node = queue.poll();
                level.add(node.val);
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
            link.add(level);
        }
        return link;
    }
}

根据前序遍历和中序遍历构建二叉树:

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public int preIndex;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeChild(preorder, inorder, 0, inorder.length - 1);

    }
    private TreeNode buildTreeChild(int[] preorder, int [] inorder, int inbegin, int inend) {
        if (inbegin > inend) {
            return null;
        }

        TreeNode root = new TreeNode(preorder[preIndex]);
        int rootIndex = findIndexRoot(inorder, inbegin, inend, preorder[preIndex]);
        if (rootIndex == -1) {
            return null;
        }
        preIndex++;

        root.left = buildTreeChild(preorder, inorder, inbegin, rootIndex - 1);

        root.right = buildTreeChild(preorder, inorder, rootIndex + 1, inend);

        return root;
    }

    private int findIndexRoot(int[] inorder, int inbegin, int inend, int target) {
        while (inbegin <= inend) {
            if (inorder[inbegin] == target) {
                return inbegin;
            }
            inbegin++;
        }
        return -1;
    }
}

根据中序遍历和后序遍历构建二叉树: 

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public int endIndex;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        endIndex = postorder.length - 1;
        return buildTreeChild(inorder, postorder, 0, postorder.length - 1);
    }
    private TreeNode buildTreeChild(int[] inorder, int[] postorder, int begin, int end) {
        if (begin > end) {
            return null;
        }
        TreeNode root = new TreeNode(postorder[endIndex]);

        int rootIndex = findTreeNode(inorder, begin, end, postorder[endIndex]);
        if (rootIndex < 0) {
            return null;
        }
        endIndex--;

        //这里要先创建右树
        root.right = buildTreeChild(inorder, postorder, rootIndex + 1, end);
        root.left = buildTreeChild(inorder, postorder, begin, rootIndex - 1);

        return root;
    }
    private int findTreeNode(int[] inorder, int begin, int end, int key) {
        while (begin <= end) {
            if (inorder[begin] == key) {
                return begin;
            }
            begin++;
        }
        return -1;
    }
}

前序遍历非递归:

        此时我们借助栈来完成。

void preOrderNor(TreeNode root) {
    if (root == null) {
        return;
    }
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        while (cur != null) {
            stack.push(cur);
            System.out.print(cur.val + " ");
            cur = cur.left;
        }
        TreeNode top = stack.pop();
        cur = top.right;
    } 
}

后序遍历非递归:

void postOrderNor(TreeNode root) {
    if (root == null) {
        return;
    }
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }
        TreeNode top = stack.peek();
        TreeNode prev = null;//方便记录
        
        if (top.right == null || top.right == prev) {
            System.out.print(top.val + " ");
            stack.pop();
            prev = top;
        } else {
            cur = top.right;
        }
    }
}

堆(优先级队列):

堆的概念:

        我们可以将数组想成一个二叉树,堆的逻辑结构是一颗完全二叉树,物理结构是一个数组。我们可以得出左右孩子和父节点的数学关系。

        建立堆,可以分为两种,一种建立小堆,一种建立大堆。我们利用向下调整算法来建立堆。

向下调整算法: 

        我们可以将数组想象成二叉树,但是向下调整算法必须保证左右树必须已经建好堆,所以我们从数组的最后开始建堆,也就是从最后一颗子树开始,根据公式,最后一棵树的位置(下标)就是(n - 1 - 1) / 2,之后逐个向下调整并建好堆。接下来给出该算法:

public class TestHeap {
    public  int[] elem;
    public  int usedSize;

    public TestHeap() {
        this.elem = new int[10];
    }

    public void initElem(int[] array) {
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
    }

    public void createHeap() {
        for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {
            AdjustDown(parent, usedSize);
        }
    }

    private void AdjustDown(int parent, int len) {
        int child = parent * 2 + 1;
        //建大堆
        while (child < len) {
            if (elem[child] < elem[child + 1] && child + 1 < len) {
                child++;
            }
            if (elem[parent] < elem[child]) {
                //交换
                swap(parent, child);
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }
    }

    private void swap(int a, int b) {
        int tmp = elem[a];
        elem[a] = elem[b];
        elem[b] = tmp;
    }

}

优先级队列(PriorityQueue):

        其实就是堆,但是我们还是要先了解一下什么是优先级队列。

        优先级队列,有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列。此时一般的队列显然不合适。比如玩手机时,有人打来电话,系统就应该优先处理打来的电话。

        这种数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这就被称为优先级队列,优先级队列底层是一个完全二叉树。

        这里优先级队列底层是使用数组实现的,操作规则是使用队列来完成的。 

        不能插入null对象,可以插入任意多个元素,内部可以实现自动扩容。

        当我们进行删除优先级队列元素时,需要从队列头部开始删除,如果从尾部开始删除,则相当于向上建堆,向上调整建堆时间复杂度会很大,所以我们进行头删。

public static void main(String[] args) {
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
    //堆
    priorityQueue.offer(10);
    priorityQueue.offer(5);
    priorityQueue.offer(6);

    System.out.println(priorityQueue.peek());
    //当我们实例化一个 priorityQueue 之后,默认是一个小根堆
}

        此时队头元素为5,可以发现默认是小堆。 所以我们如何将其改为大堆呢?

构建大堆(Comparable接口): 

        我们不能随意向其中插入数据,因为我们其实会进行比较。举个例子:

class Student {
    public int age;
    public String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

public class Test2 {
    public static void main(String[] args) {
        PriorityQueue<Student> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(new Student(22, "wowo"));
        priorityQueue.offer(new Student(21, "wda"));
    }
}

        此时报错,因为没有指定类型去建堆。 所以我们其实可以想到可能其中使用了Comparable接口。

         所以可以发现当我们使用无参构造器时,默认优先队列的容量是11。而且可以发现其使用了比较器。

        看一看出,里面重载了构造方法,所以我们可以传入比较器来完成效果。比如此时我们是一个小堆,第一个元素是10,之后插入5:

        我们再观察Integer中的Comparable接口中的compareTo方法。 

        也就是说,此时我们将返回值改变即可将小根堆改为大根堆。 

public static void main(String[] args) {
    Imp imp = new Imp();
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(imp);
    //使用自己的比较器

    //堆
    priorityQueue.offer(10);
    priorityQueue.offer(5);
}
class  Imp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1.compareTo(o2);
    }
}

        所以我们可以通过自己实现的比较器来构建大根堆。

观察源码:

         可以看到,当数组容量小于64时,每次增加2;当容量大于64时,每次扩容1.5倍。

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

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

相关文章

红包封面的流量是真的大啊

你好&#xff0c;我是小生&#xff0c;一个程序员转型做自媒体副业中~ 昨天&#xff0c;是农历 2023 年最后一天&#xff0c;到下午饭后的时候&#xff0c;红包封面流量推上了高峰期&#xff0c;非常的狂暴&#xff0c;有要烟花封面的、有要金龙封面的、有要表白封面的等等。 小…

在计算机/移动设备上恢复已删除视频的 10 个数据恢复工具

视频在网上疯传&#xff0c;我们都观看或创建视频&#xff0c;并将我们最喜欢的视频保存在硬盘上。如果我们丢失了一些重要的视频&#xff0c;那将是非常令人心碎的。但是今天&#xff0c;恢复已删除的视频变得更加容易。删除的视频在被新数据覆盖之前并没有真正从您的存储驱动…

单片机在物联网中的应用

单片机&#xff0c;这个小巧的电子设备&#xff0c;可能听起来有点技术性&#xff0c;但它实际上是物联网世界中的一个超级英雄。简单来说&#xff0c;单片机就像是各种智能设备的大脑&#xff0c;它能让设备“思考”和“行动”。由于其体积小、成本低、功耗低、易于编程等特点…

Web前端-移动web开发_rem布局

文章目录 移动web开发之rem布局1.0 rem基础1.1 rem单位(重点)1.2 em单位(了解)1.3 媒体查询什么是媒体查询媒体查询语法规范 1.4 less 基础维护css弊端Less 介绍Less安装Less 使用之变量使用node编译less的指令Less 编译 vocode Less 插件Less 嵌套Less 运算Less中的Mixin混入L…

使用 MinIO 超级充电 TileDB 引擎

MinIO 是一个强大的主要 TileDB 后端&#xff0c;因为两者都是为性能和规模而构建的。MinIO 是一个单一的 Go 二进制文件&#xff0c;可以在许多不同类型的云和本地环境中启动。它非常轻量级&#xff0c;但也具有复制和加密等功能&#xff0c;并且提供与各种应用程序的集成。Mi…

这MySQL错误日志异常也太猛了吧

作者&#xff1a;田逸&#xff08;formyz&#xff09; 一台核心业务数据库&#xff0c;版本为MySQL 8.34 社区服务器版。从上线以来&#xff0c;这个数据库服务器的错误日志增增加非常迅猛&#xff08;如下图所示&#xff09;&#xff0c;每24小时能增加到10多个G的容量。 因为…

前后端分离nodejs+vue动态网站的图书借阅管理系统35ih5

读者模块 1)注册&#xff1a;读者输入账号、密码、确认密码、姓名、手机、身份证、邮箱&#xff0c;点击注册按钮&#xff0c;完成注册。 2)登录&#xff1a;普通读者成功输入读者账号和密码&#xff0c;点击登录按钮。 3)读者主页面&#xff1a;读者登录成功后&#xff0c;选择…

C++重新入门-循环

目录 1.循环类型 while循环&#xff1a; for循环 基于范围的for循环(C11) do...while 循环 2.循环控制语句 3.无限循环 有的时候&#xff0c;可能需要多次执行同一块代码。一般情况下&#xff0c;语句是顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着…

使用Flash download tool进行ESP32固件烧录

背景 为方便分发固件&#xff0c;可在任意电脑上安装烧录软件&#xff0c;直接将固件烧录进 烧录内容 查看vscode上platformio的烧录过程 Writing at 0x00000000... (100 %) Wrote 15104 bytes (10401 compressed) at 0x00000000 in 0.4 seconds (effective 281.3 kbit/s).…

【Spring源码分析】Spring的启动流程源码解析

阅读此需阅读下面这些博客先【Spring源码分析】Bean的元数据和一些Spring的工具【Spring源码分析】BeanFactory系列接口解读【Spring源码分析】执行流程之非懒加载单例Bean的实例化逻辑【Spring源码分析】从源码角度去熟悉依赖注入&#xff08;一&#xff09;【Spring源码分析】…

【Linux环境基础开发工具的使用(yum、vim、gcc、g++、gdb、make/Makefile)】

Linux环境基础开发工具的使用yum、vim、gcc、g、gdb、make/Makefile Linux软件包管理器- yumLinux下安装软件的方式认识yum查找软件包安装软件如何实现本地机器和云服务器之间的文件互传卸载软件 Linux编辑器 - vimvim的基本概念vim下各模式的切换vim命令模式各命令汇总vim底行…

openresty (nginx)快速开始

文章目录 一、什么是openresty&#xff1f;二、openresty编译安装1. 编译安装命令1.1 编译完成后路径1.2 常用编译选项解释 2. nginx配置文件配置2.1 nginx.conf模板 3. nginx常见配置一个站点配置多个域名nginx配置中location匹配规则 三、OpenResty工作原理OpenResty工作原理…

实现自定义标记

实现自定义标记 问题陈述 New Tech Book的高级管理层决定在其用JSP设计的应用程序的所有页面上显示版权信息。它们还要去如何向应用程序中添加JSP页面,可以重用显示版本信息的代码。公司的软件开发人员Jerry Smith决定用自定义标记来创建应用程序的这一部分。 解决方案 要解…

【数学建模】【2024年】【第40届】【MCM/ICM】【D题 五大湖的水位控制问题】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 ICM Problem D: Great Lakes Water Problem Background The Great Lakes of the United States and Canada are the largest group of freshwater lakes in the world. The five lakes and connecting waterways const…

社区团购系统-UMLChina建模知识竞赛第5赛季第6轮

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 参考潘加宇在《软件方法》和UMLChina公众号文章中发表的内容作答。在本文下留言回答。 只要最先答对前3题&#xff0c;即可获得本轮优胜。 如果有第4题&#xff0c;第4题为附加题&am…

SpringBoot3整合Knife4j

前置&#xff1a; 官网&#xff1a;快速开始 | Knife4j gitee&#xff1a;swagger-bootstrap-ui-demo: knife4j 以及swagger-bootstrap-ui 集成框架示例项目 - Gitee.com 1.依赖引入&#xff1a; ps&#xff1a;json处理需要引入相关包 <dependency><groupId>c…

sqli-labs-master靶场训练笔记(54-65|决战紫禁之巓)

2024.2.5 level-54 1、先判断类型和闭合类型 ?id1/0 #正常 证明不是数字型 ?id1 #错误 ?id1 -- a #正常 判断是 闭合 2、 判断列数 这里需要运气&#xff0c;但是根据前面50多关的经验直接猜测是3列 ?id-1 union select 1,2,3 -- a 3、爆表名&#xf…

leetcode:131.分割回文串

树形结构&#xff1a; 切割到字符串的尾部&#xff0c;就是叶子节点。 回溯算法三部曲&#xff1a; 1.递归的参数和返回值&#xff1a; 参数字符串s和startIndex切割线 2.确定终止条件&#xff1a; 当分割线到字符串末尾时到叶子节点&#xff0c;一种方案出现 3.单层搜索…

真、开源LLM-OLMo

论文&#xff1a;https://arxiv.org/pdf/2402.00838.pdf Weights https://huggingface.co/allenai/OLMo-7B Code https://github.com/allenai/OLMo Data https://huggingface.co/datasets/allenai/dolma Evaluation https://github.com/allenai/OLMo-Eval Adaptation http…

行业科普应用分享 | 用于安全和安保的仪器仪表

【前言】 物联网带来了对安全和安保的新要求。利用物联网&#xff0c;运营商可以从复杂和分布式的装置中获益。此外&#xff0c;自主系统在现代工业的运作中正变得越来越重要。 从制造业到农业&#xff0c;这些远程操作需要仪器提供持续监测&#xff0c;以提供安全和保障。这…