【数据结构(九)】线索化二叉树(3)

news2024/11/20 9:14:25

文章目录

  • 1. 前言——问题引出
  • 2. 线索二叉树的基本介绍
  • 3. 线索二叉树的应用案例
    • 3.1. 思路分析
    • 3.2. 代码实现
  • 4. 遍历线索化二叉树
    • 4.1. 代码实现


1. 前言——问题引出

    

问题:
    将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. (n+1=7个空指针域)

在这里插入图片描述
    

问题分析:
1.当对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 }
2.但是 6 的 右指针,8、10、14 这几个节点的 左右指针,并没有完全的利用上(共7个空指针域)
3.如果希望充分的利用 各个节点的左右指针,让各个节点可以指向自己的前后节点,怎么办?
    
解决方案:线索二叉树

2. 线索二叉树的基本介绍

在这里插入图片描述

  1. n n n 个结点的二叉链表中含有 n + 1 n+1 n+1 【公式 2 n − ( n − 1 ) = n + 1 2n-(n-1)=n+1 2n(n1)=n+1】 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱后继结点的指针(这种附加的指针称为"线索")。
  2. 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树中序线索二叉树后序线索二叉树三种。
  3. 一个结点的前一个结点,称为前驱结点
  4. 一个结点的后一个结点,称为后继结点

3. 线索二叉树的应用案例

应用案例说明:
    将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8, 3, 10, 1, 14, 6}

在这里插入图片描述

3.1. 思路分析

中序遍历的结果:{8, 3, 10, 1, 14, 6}

在这里插入图片描述

说明:
当线索化二叉树后,Node节点的 属性 leftright ,有如下情况:
(1)left指向的是左子树,也可能是指向的前驱节点,比如 “1节点” 的left 指向的左子树, 而 “10节点” 的 left 指向的就是前驱节点.
(2)right指向的是右子树,也可能是指向后继节点,比如 “1节点” 的right 指向的是右子树,而 “10节点” 的right 指向的是后继节点.

3.2. 代码实现

package tree.threadedbinarytree;

//import tree.HeroNode;

public class ThreadedBinaryTreeDemo {

	public static void main(String[] args) {

		HeroNode root = new HeroNode(1, "tom");
		HeroNode node2 = new HeroNode(3, "jack");
		HeroNode node3 = new HeroNode(6, "smith");
		HeroNode node4 = new HeroNode(8, "mary");
		HeroNode node5 = new HeroNode(10, "king");
		HeroNode node6 = new HeroNode(14, "dim");

		// 二叉树,这里手动创建方法比较低级,后面要学习递归创建
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(node6);

		// 测试线索化
		ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
		threadedBinaryTree.setRoot(root);
		threadedBinaryTree.threadedNodes();

		// 测试,以10节点测试
		HeroNode leftNode = node5.getLeft();
		System.out.println("10号节点的前驱节点是:" + leftNode);

		HeroNode rightNode = node5.getRight();
		System.out.println("10号节点的前驱节点是:" + rightNode);

	}

}

//
//编写一个ArrayBinaryTree,实现顺序存储二叉树遍历
//定义一个ThreadedBinaryTree,实现了线索化功能的二叉树
class ThreadedBinaryTree {
	private HeroNode root;

	// 为了实现线索化,需要创建要指向当前节点的前驱节点的指针
	// 在递归进行线索化时,pre 总是保留前一个节点
	private HeroNode pre = null;

	public void setRoot(HeroNode root) {
		this.root = root;
	}

	// 重载
	public void threadedNodes() {
		this.threadedNodes(root);
	}

	// 编写对二叉树进行中序线索化的方法
	/**
	 * 
	 * @param node 就是当前需要线索化的节点
	 */
	public void threadedNodes(HeroNode node) {

		// 如果node==null,不能线索化
		if (node == null) {
			return;
		}

		// 1. 先线索化左子树
		threadedNodes(node.getLeft());
		// 2. 然后线索化当前节点[有难度]

		// 先处理当前节点的前驱节点
		// 以8节点来理解
		// 8节点的.left=null, 8节点的.leftType=1
		if (node.getLeft() == null) {
			// 让当前节点的左指针指向前驱节点
			node.setLeft(pre);
			// 修改当前节点的左指针的类型
			node.setLeftType(1);
		}

		// 处理后继结点
		if (pre != null && pre.getRight() == null) {
			// 让前驱节点的有指针指向当前节点
			pre.setRight(node);
			// 修改前驱节点的有指针类型
			pre.setRightType(1);
		}

		// !!!每处理一个节点后,让当前节点是下一个节点的前驱节点
		pre = node;

		// 3. 再线索化右子树
		threadedNodes(node.getRight());
	}

	// 删除节点
	public void delNode(int no) {
		if (root != null) {
			// 如果只有一个root节点,这里立即判断root是不是就是要删除的节点
			if (root.getNo() == no) {
				root = null;
			} else {
				// 递归删除
				root.delNode(no);
			}
		} else {
			System.out.println("空数,不能删除~");
		}
	}

	// 前序遍历
	public void preOrder() {
		if (this.root != null) {
			this.root.preOrder();
		} else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (this.root != null) {
			this.root.infixOrder();
		} else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

	// 后序遍历
	public void postOrder() {
		if (this.root != null) {
			this.root.postOrder();
		} else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

	// 前序遍历查找
	public HeroNode preOrderSearch(int no) {
		if (root != null) {
			return root.preOrderSearch(no);
		} else {
			return null;
		}
	}

	// 中序遍历
	public HeroNode infixOrderSearch(int no) {
		if (root != null) {
			return root.infixOrderSearch(no);
		} else {
			return null;
		}
	}

	// 后序遍历
	public HeroNode postOrderSearch(int no) {
		if (root != null) {
			return this.root.postOrderSearch(no);
		} else {
			return null;
		}
	}
}

//创建HeroNode
//先创建HeroNode节点
class HeroNode {
	private int no;
	private String name;
	private HeroNode left;// 默认null
	private HeroNode right;// 默认null
	// 说明
	// 1. 如果leftType == 0 表示指向的是左子树,如果1 则表示指向前驱节点
	// 2. 如果rightType == 0 表示指向的是右子树,如果1 则表示指向后继结点

	private int leftType;
	private int rightType;

	public int getLeftType() {
		return leftType;
	}

	public void setLeftType(int leftType) {
		this.leftType = leftType;
	}

	public int getRightType() {
		return rightType;
	}

	public void setRightType(int rightType) {
		this.rightType = rightType;
	}

	public HeroNode(int no, String name) {

		super();
		this.no = no;
		this.name = name;
	}

	public int getNo() {
		return this.no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public HeroNode getLeft() {
		return this.left;
	}

	public void setLeft(HeroNode left) {
		this.left = left;
	}

	public HeroNode getRight() {
		return this.right;
	}

	public void setRight(HeroNode right) {
		this.right = right;
	}

	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + "]";
	}

	// 递归删除节点
	// 1. 如果删除的节点是叶子节点,则删除该节点
	// 2. 如果删除的节点是非叶子节点,则删除该子树
	public void delNode(int no) {
		// 思路
		/*
		 * 1.因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除结点,而不能去判断当前这个节点是不是需要删除节点。
		 * 2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)
		 * 3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
		 * 4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除 5.如果第4步也没有删除节点,则应当向右子树进行递归删除
		 */
		// 2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)
		if (this.left != null && this.left.no == no) {
			this.left = null;
			return;
		}
		// 3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
		if (this.right != null && this.right.no == no) {
			this.right = null;
			return;
		}
		// 4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除
		if (this.left != null) {
			this.left.delNode(no);
		}

		// 5.如果第4步也没有删除节点,则应当向右子树进行递归删除
		if (this.right != null) {
			this.right.delNode(no);
		}

	}

	// 编写前序遍历的方法
	public void preOrder() {
		System.out.println(this);// 先输出父节点
		// 递归向左子树前序遍历
		if (this.left != null) {
			this.left.preOrder();
		}
		// 递归向右子树前序遍历
		if (this.right != null) {
			this.right.preOrder();
		}
	}

	// 编写中序遍历的方法
	public void infixOrder() {
		// 递归向左子树中序遍历
		if (this.left != null) {
			this.left.infixOrder();
		}

		// 输出父节点
		System.out.println(this);

		// 递归向右子树中序遍历
		if (this.right != null) {
			this.right.infixOrder();
		}
	}

	// 编写后序遍历的方法
	public void postOrder() {
		if (this.left != null) {
			this.left.postOrder();
		}

		if (this.right != null) {
			this.right.postOrder();
		}

		System.out.println(this);
	}

	// 前序遍历查找
	/**
	 * 
	 * @param no 查找的编号
	 * @return 如果找到就返回该Node,如果没有找到就返回null
	 */
	public HeroNode preOrderSearch(int no) {
		System.out.println("进入了前序查找一次~~");
		// 比较当前节点是不是
		if (this.no == no) {
			return this;
		}

		// 1. 判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
		// 2. 如果左递归前序查找,找到节点,则返回
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.preOrderSearch(no);
		}

		if (resNode != null) {// 说明我们左子树找到
			return resNode;
		}

		// 1. 左递归前序查找,找到节点,则返回,否继续判断,
		// 2.当前的节点的右子节点是否为空,如果不空,则继续向右递归前序查找
		if (this.right != null) {
			resNode = this.right.preOrderSearch(no);
		}
		return resNode;
	}

	// 中序遍历查找
	public HeroNode infixOrderSearch(int no) {

		// 判断当前节点的左子节点是否为空,如果不为空,则递归中序查找
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.infixOrderSearch(no);
		}

		if (resNode != null) {
			return resNode;
		}
		System.out.println("进入了中序查找一次~~");
		// 如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前节点
		if (this.no == no) {
			return this;
		}

		// 否则继续进行右递归的中序查找
		if (this.right != null) {
			resNode = this.right.infixOrderSearch(no);
		}

		return resNode;
	}

	// 后序遍历查找
	public HeroNode postOrderSearch(int no) {
		// 判断当前节点的左节点是否为空,如果不为空,则递归后序查找
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.postOrderSearch(no);
		}
		if (resNode != null) {// 说明在左子树找到
			return resNode;
		}

		// 如果左子树没有找到,则向右子树递归进行后序遍历查找
		if (this.right != null) {
			resNode = this.right.postOrderSearch(no);
		}

		if (resNode != null) {
			return resNode;
		}
		System.out.println("进入了后序查找一次~~");
		// 如果左右子树都没有找到,就比较当前节点是不是
		if (this.no == no) {
			return this;
		}
		return resNode;
	}

}

运行结果:

在这里插入图片描述

4. 遍历线索化二叉树

    

问题:
    对前面的中序线索化的二叉树, 进行遍历
在这里插入图片描述

分析:
    因为线索化后,各个结点指向有变化,因此原来的遍历方式不能使用,这时需要使用新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。 遍历的次序应当和中序遍历保持一致

4.1. 代码实现

package tree.threadedbinarytree;

//import tree.HeroNode;

public class ThreadedBinaryTreeDemo {

	public static void main(String[] args) {

		HeroNode root = new HeroNode(1, "tom");
		HeroNode node2 = new HeroNode(3, "jack");
		HeroNode node3 = new HeroNode(6, "smith");
		HeroNode node4 = new HeroNode(8, "mary");
		HeroNode node5 = new HeroNode(10, "king");
		HeroNode node6 = new HeroNode(14, "dim");

		// 二叉树,这里手动创建方法比较低级,后面要学习递归创建
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(node6);

		// 测试线索化
		ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
		threadedBinaryTree.setRoot(root);
		threadedBinaryTree.threadedNodes();

		// 测试,以10节点测试
		HeroNode leftNode = node5.getLeft();
		System.out.println("10号节点的前驱节点是:" + leftNode);

		HeroNode rightNode = node5.getRight();
		System.out.println("10号节点的前驱节点是:" + rightNode);

		// 当线索化二叉树后,不能再使用原来的遍历方法(threadedBinaryTree.infixOrder();)
		System.out.println("使用线索化的方式遍历 线索化二叉树");
		threadedBinaryTree.threadedList();

	}

}

//编写一个ArrayBinaryTree,实现顺序存储二叉树遍历
//定义一个ThreadedBinaryTree,实现了线索化功能的二叉树
class ThreadedBinaryTree {
	private HeroNode root;

	// 为了实现线索化,需要创建要指向当前节点的前驱节点的指针
	// 在递归进行线索化时,pre 总是保留前一个节点
	private HeroNode pre = null;

	public void setRoot(HeroNode root) {
		this.root = root;
	}

	// 重载
	public void threadedNodes() {
		this.threadedNodes(root);
	}

	// 遍历线索化二叉树的方法
	public void threadedList() {
		// 定义一个变量,存储当前遍历的节点,从root开始
		HeroNode node = root;
		while (node != null) {
			// 循环的找到leftType==1的节点,第一个找到的就是8节点
			// 后面随着遍历而变化,因为当leftType==1时,说明该节点是按照线索化处理后的有效节点
			while (node.getLeftType() == 0) {
				node = node.getLeft();
			}

			// 打印当前这个节点
			System.out.println(node);

			// 如果当前节点的右指针指向的是后继节点,就一直输出
			while (node.getRightType() == 1) {
				// 获取到当前节点的后继节点
				node = node.getRight();
				System.out.println(node);
			}

			// 替换遍历的节点
			node = node.getRight();
		}
	}

	// 编写对二叉树进行中序线索化的方法
	/**
	 * 
	 * @param node 就是当前需要线索化的节点
	 */
	public void threadedNodes(HeroNode node) {

		// 如果node==null,不能线索化
		if (node == null) {
			return;
		}

		// 1. 先线索化左子树
		threadedNodes(node.getLeft());
		// 2. 然后线索化当前节点[有难度]

		// 先处理当前节点的前驱节点
		// 以8节点来理解
		// 8节点的.left=null, 8节点的.leftType=1
		if (node.getLeft() == null) {
			// 让当前节点的左指针指向前驱节点
			node.setLeft(pre);
			// 修改当前节点的左指针的类型
			node.setLeftType(1);
		}

		// 处理后继结点
		if (pre != null && pre.getRight() == null) {
			// 让前驱节点的有指针指向当前节点
			pre.setRight(node);
			// 修改前驱节点的有指针类型
			pre.setRightType(1);
		}

		// !!!每处理一个节点后,让当前节点是下一个节点的前驱节点
		pre = node;

		// 3. 再线索化右子树
		threadedNodes(node.getRight());
	}

	// 删除节点
	public void delNode(int no) {
		if (root != null) {
			// 如果只有一个root节点,这里立即判断root是不是就是要删除的节点
			if (root.getNo() == no) {
				root = null;
			} else {
				// 递归删除
				root.delNode(no);
			}
		} else {
			System.out.println("空数,不能删除~");
		}
	}

	// 前序遍历
	public void preOrder() {
		if (this.root != null) {
			this.root.preOrder();
		} else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (this.root != null) {
			this.root.infixOrder();
		} else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

	// 后序遍历
	public void postOrder() {
		if (this.root != null) {
			this.root.postOrder();
		} else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

	// 前序遍历查找
	public HeroNode preOrderSearch(int no) {
		if (root != null) {
			return root.preOrderSearch(no);
		} else {
			return null;
		}
	}

	// 中序遍历
	public HeroNode infixOrderSearch(int no) {
		if (root != null) {
			return root.infixOrderSearch(no);
		} else {
			return null;
		}
	}

	// 后序遍历
	public HeroNode postOrderSearch(int no) {
		if (root != null) {
			return this.root.postOrderSearch(no);
		} else {
			return null;
		}
	}
}

//创建HeroNode
//先创建HeroNode节点
class HeroNode {
	private int no;
	private String name;
	private HeroNode left;// 默认null
	private HeroNode right;// 默认null
	// 说明
	// 1. 如果leftType == 0 表示指向的是左子树,如果1 则表示指向前驱节点
	// 2. 如果rightType == 0 表示指向的是右子树,如果1 则表示指向后继结点

	private int leftType;
	private int rightType;

	public int getLeftType() {
		return leftType;
	}

	public void setLeftType(int leftType) {
		this.leftType = leftType;
	}

	public int getRightType() {
		return rightType;
	}

	public void setRightType(int rightType) {
		this.rightType = rightType;
	}

	public HeroNode(int no, String name) {

		super();
		this.no = no;
		this.name = name;
	}

	public int getNo() {
		return this.no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public HeroNode getLeft() {
		return this.left;
	}

	public void setLeft(HeroNode left) {
		this.left = left;
	}

	public HeroNode getRight() {
		return this.right;
	}

	public void setRight(HeroNode right) {
		this.right = right;
	}

	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + "]";
	}

	// 递归删除节点
	// 1. 如果删除的节点是叶子节点,则删除该节点
	// 2. 如果删除的节点是非叶子节点,则删除该子树
	public void delNode(int no) {
		// 思路
		/*
		 * 1.因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除结点,而不能去判断当前这个节点是不是需要删除节点。
		 * 2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)
		 * 3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
		 * 4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除 5.如果第4步也没有删除节点,则应当向右子树进行递归删除
		 */
		// 2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)
		if (this.left != null && this.left.no == no) {
			this.left = null;
			return;
		}
		// 3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
		if (this.right != null && this.right.no == no) {
			this.right = null;
			return;
		}
		// 4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除
		if (this.left != null) {
			this.left.delNode(no);
		}

		// 5.如果第4步也没有删除节点,则应当向右子树进行递归删除
		if (this.right != null) {
			this.right.delNode(no);
		}

	}

	// 编写前序遍历的方法
	public void preOrder() {
		System.out.println(this);// 先输出父节点
		// 递归向左子树前序遍历
		if (this.left != null) {
			this.left.preOrder();
		}
		// 递归向右子树前序遍历
		if (this.right != null) {
			this.right.preOrder();
		}
	}

	// 编写中序遍历的方法
	public void infixOrder() {
		// 递归向左子树中序遍历
		if (this.left != null) {
			this.left.infixOrder();
		}

		// 输出父节点
		System.out.println(this);

		// 递归向右子树中序遍历
		if (this.right != null) {
			this.right.infixOrder();
		}
	}

	// 编写后序遍历的方法
	public void postOrder() {
		if (this.left != null) {
			this.left.postOrder();
		}

		if (this.right != null) {
			this.right.postOrder();
		}

		System.out.println(this);
	}

	// 前序遍历查找
	/**
	 * 
	 * @param no 查找的编号
	 * @return 如果找到就返回该Node,如果没有找到就返回null
	 */
	public HeroNode preOrderSearch(int no) {
		System.out.println("进入了前序查找一次~~");
		// 比较当前节点是不是
		if (this.no == no) {
			return this;
		}

		// 1. 判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
		// 2. 如果左递归前序查找,找到节点,则返回
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.preOrderSearch(no);
		}

		if (resNode != null) {// 说明我们左子树找到
			return resNode;
		}

		// 1. 左递归前序查找,找到节点,则返回,否继续判断,
		// 2.当前的节点的右子节点是否为空,如果不空,则继续向右递归前序查找
		if (this.right != null) {
			resNode = this.right.preOrderSearch(no);
		}
		return resNode;
	}

	// 中序遍历查找
	public HeroNode infixOrderSearch(int no) {

		// 判断当前节点的左子节点是否为空,如果不为空,则递归中序查找
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.infixOrderSearch(no);
		}

		if (resNode != null) {
			return resNode;
		}
		System.out.println("进入了中序查找一次~~");
		// 如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前节点
		if (this.no == no) {
			return this;
		}

		// 否则继续进行右递归的中序查找
		if (this.right != null) {
			resNode = this.right.infixOrderSearch(no);
		}

		return resNode;
	}

	// 后序遍历查找
	public HeroNode postOrderSearch(int no) {
		// 判断当前节点的左节点是否为空,如果不为空,则递归后序查找
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.postOrderSearch(no);
		}
		if (resNode != null) {// 说明在左子树找到
			return resNode;
		}

		// 如果左子树没有找到,则向右子树递归进行后序遍历查找
		if (this.right != null) {
			resNode = this.right.postOrderSearch(no);
		}

		if (resNode != null) {
			return resNode;
		}
		System.out.println("进入了后序查找一次~~");
		// 如果左右子树都没有找到,就比较当前节点是不是
		if (this.no == no) {
			return this;
		}
		return resNode;
	}

}

运行结果:

在这里插入图片描述

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

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

相关文章

软件测试:Selenium三大等待(详解版)

一、强制等待 1.设置完等待后不管有没有找到元素,都会执行等待,等待结束后才会执行下一步 2.实例: driver webdriver.Chrome()driver.get("https://www.baidu.com")time.sleep(3) # 设置强制等待driver.quit() 二、隐性等待 …

【数据挖掘】国科大苏桂平老师数据库新技术课程作业 —— 第三次作业

part 1 设计一个学籍管理小系统。系统包含以下信息: 学号、学生姓名、性别、出生日、学生所在系名、学生所在系号、课程名、课程号、课程类型(必修、选修、任选)、学分、任课教师姓名、教师编号、教师职称、教师所属系名、系号、学生所选课…

【GEE笔记】随机森林特征重要性计算并排序

随机森林是一种基于多个决策树的集成学习方法,可以用于分类和回归问题。在gee中可以使用ee.Classifier.smileRandomForest()函数来创建一个随机森林分类器,并用它来对影像进行分类。 随机森林分类器有一个重要的属性,就是可以计算每个特征&a…

计算机毕业设计springboot+ssm停车场车位预约系统java

管理员不可以注册账号 停车位包括车位所在楼层、车位编号、车位类型(全时间开放/高峰期开放)、预定状态等 用户预约时要求支付预约时间段的停车费用 违规行为:1.停车超过预约时间段 2.预约未使用 于系统的基本要求 (1)功能要求&am…

class067 二维动态规划【算法】

class067 二维动态规划 code1 64. 最小路径和 // 最小路径和 // 给定一个包含非负整数的 m x n 网格 grid // 请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 // 说明:每次只能向下或者向右移动一步。 // 测试链接 : https://leetcode…

【腾讯云 HAI域探秘】StableDiffusionWebUI 让我找到了宫崎骏动漫里的夏天

目录 前言一、HAI二、应用场景三、构建 Stable Diffusion 模型1、新建HAI应用2、StableDiffusionWebUI(1)功能介绍(2)页面转中文(3)AI绘图① 正向提示词语② 反向提示词③ “” 、“ AND”、“|” 用法④ 权…

网上下载的pdf文件,为什么不能复制文字?

不知道大家有没有到过这种情况?在网上下载的PDF文件打开之后,发现选中文字之后无法复制。甚至其他功能也都无法使用,这是怎么回事?该怎么办? 当我们发现文件打开之后,编辑功能无法使用,很可能是…

基于MATLAB的BPSK调制解调仿真(仿真图超多,结果超清晰)

基于MATLAB的BPSK调制解调仿真(包含中间各个环节的时域波形、功率谱、频谱图、抽样后的星座图和眼图、理论与仿真的误码率曲线) 目录 前言 一、BPSK基本原理 二、BPSK调制解调过程 三、仿真结果 1、码元信噪比eb/n0-10dB时 1、双极性不归零基带信号 2、成型滤波(根升余弦…

uni-app应用设置 可以根据手机屏幕旋转进行 (横/竖) 屏切换

首先 我们打开项目的 pages.json 在左侧导航栏中找到 源码视图 然后找到 app-plus 配置 在下面加上 "orientation": [//竖屏正方向"portrait-primary",//竖屏反方向"portrait-secondary",//横屏正方向"landscape-primary",//横屏反方…

Other -- ChatGPT 原理

本文为个人理解,帮助小白(本人就是)了解正在创建新时代的 AI 产品,如文中理解有误欢迎留言。 [参考链接--](https://baijiahao.baidu.com/s?id1765556782543603120&wfrspider&forpc) 1. 了解一些基本概念 大语言模型&a…

安卓MediaRecorder(2)录制源码分析

文章目录 前言JAVA new MediaRecorder() 源码分析android_media_MediaRecorder.cpp native_init()MediaRecorder.java postEventFromNativeandroid_media_MediaRecorder.cpp native_setup() MediaRecorder 参数设置MediaRecorder.prepare 分析MediaRecorder.start 分析MediaRec…

[面试题~Docker] 云原生必问基础篇

文章目录 基础相关1. Docker 是什么?2. 镜像是什么3. 容器是什么4. 数据卷是什么5. Docker 和虚拟机的区别?6. Docker 常用命令有哪些? 原理相关1. docker 有几种网络模式host 模式container模式none模式bridge模式 2. docker 网络实现在Linu…

AGILE-SCRUM

一个复杂的汽车ECU开发。当时开发队伍遍布全球7个国家,10多个地区,需要同时为多款车型定制不同的软件,头疼的地方是: 涉及到多方人员协调,多模块集成和管理不同软件团队使用的设计工具、验证工具,数据、工…

C语言数据结构-基于单链表实现通讯录

文章目录 1 基础要求2 通讯录功能2.1 引入单链表的文件2.2 定义联系人数据结构2.3 打开通讯录2.4 保存数据后销毁通讯录2.5 添加联系人2.6 删除联系人2.7 修改联系人2.8 查找联系人2.9 查看通讯录 3 通讯录代码展示3.1 SeqList_copy.h3.2 SeqList_copy.c3.3 Contact.h3.4 Conta…

【论文阅读】Reachability and distance queries via 2-hop labels

Cohen E, Halperin E, Kaplan H, et al. Reachability and distance queries via 2-hop labels[J]. SIAM Journal on Computing, 2003, 32(5): 1338-1355. Abstract 图中的可达性和距离查询是许多应用的基础,从地理导航系统到互联网路由。其中一些应用程序涉及到巨…

免费开源-数字孪生城市污水处理平台

智慧城市污水处理平台,基于污水厂三维模型可视化场景,结合物联网IOT、视频监控以及综合运营数据,增加污水处理厂的掌控力度。飞渡科技利用数字孪生技术结合物联网IOT技术,直观展现各污水处理站点的整体建设规模,以及污…

pytorch:YOLOV1的pytorch实现

pytorch:YOLOV1的pytorch实现 注:本篇仅为学习记录、学习笔记,请谨慎参考,如果错误请评论指出。 参考: 动手学习深度学习pytorch版——从零开始实现YOLOv1 目标检测模型YOLO-V1损失函数详解 3.1 YOLO系列理论合集(YOL…

Windows Service Name重复问题

Windows Service Name重复问题 1,问题 2,打开命令提示符,管理员身份运行 3,输入命令:sc delete MYSQL57 4,验证一下,可以看见已经没有感叹号啦 ,可以看见已经没有感叹号啦

基于Qt的Live2D模型显示以及控制

基于Qt的Live2D模型显示以及控制 基本说明 Live2D官方提供有控制Live2D模型的SDK,而且还提供了一个基于OpenGL的C项目Example,我们可以基于该项目改成Qt的项目,做一个桌面端的Live2D桌宠程序。 官方例子 经过改造效果如下图所示。 官方项目配置 下载官方提供的SD…

视觉检测系统在半导体行业的应用

一、半导体产业链概述 半导体产业链是现代电子工业的核心组成部分,涵盖了从原材料到最终产品的整个生产过程。这个产业链主要分为以下几个环节: 1.原材料供应:半导体行业的基石是半导体材料,如硅片、化合物半导体等。这些材料需要…