C++进阶:二叉搜索树介绍、模拟实现(递归迭代两版本)及其应用

news2024/11/24 4:20:15

上次介绍完多态后:C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)

也是要开始继续学习了


文章目录

  • 1.二叉搜索树
    • 1.1概念
    • 1.2二叉搜索树特性
    • 1.3 二叉搜索树的操作
  • 2.模拟实现
    • 2.1项目文件规划
    • 2.2基本结构
    • 2.3各种接口、功能、以及基本结构的补充
      • 2.3.1 Find() 查找 (迭代/循环版本)
      • 2.3.2 Insert() 插入(迭代/循环版本)
        • 写出中序遍历来进行验证(中序遍历有序)
      • 2.3.3 Erase() 删除(迭代/循环版本)
        • 验证
      • 2.3.4FindR() 查找 (递归版本)
      • 2.3.5Insert() 插入 (递归版本)
      • 2.3.6 EraseR() 删除 (迭代版本)
      • 2.3.7 补全拷贝构造函数
      • 2.3.8 补全析构函数
  • 3.二叉搜索树应用
  • 4.性能分析


1.二叉搜索树

1.1概念

二叉搜索树(Binary Search Tree,BST)是一种二叉树,其中每个节点都具有以下性质:

  • 节点的左子树中的所有节点的值都小于该节点的值。
  • 节点的右子树中的所有节点的值都大于该节点的值。
  • 左右子树也分别为二叉搜索树。

假设我们插入以下元素:5, 3, 7, 1, 4, 6, 8,可以构建如下的二叉搜索树(BST):

      5
     / \
    3   7
   / \ / \
  1  4 6  8

1.2二叉搜索树特性

  • 中序遍历二叉搜索树得到的序列是有序的。
  • 查找、插入和删除操作的平均时间复杂度为O(log N),其中N为树中节点的数量

1.3 二叉搜索树的操作

  1. 插入操作

    • 树为空,则直接新增节点,赋值给root指针

    • 树不空,按二叉搜索树性质查找插入位置,插入新节点

  2. 删除操作

    首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

    • 如果要删除的节点没有孩子结点,那么可以直接删除它。

    • 如果要删除的节点只有左孩子结点,可以直接删除该节点,并使其父节点指向其左孩子。

    • 如果要删除的节点只有右孩子结点,同样可以直接删除该节点,并使其父节点指向其右孩子。

    • 如果要删除的节点有左、右孩子结点,可以在其右子树中找到中序遍历下的第一个结点(右子树里最小),将其值替换到要删除的节点中,再递归删除右子树中的那个中序遍历下的第一个结点。(这个可以直接删)


2.模拟实现

2.1项目文件规划

在这里插入图片描述

头文件BSTree.h:进行模拟的编写

源文件test.cpp:进行测试,检查代码逻辑是否满足期望

2.2基本结构

namespace key
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode* _left;//左指针
		BSTreeNode* _right;//右指针
		K _key;//存数据的

		BSTreeNode(const K& key)//构造函数
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}
	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	public:
		BSTree() = default;//强制生成默认构造函数

		BSTree(const BSTree<K>& t)//拷贝构造函数
		{
			//....
		}

		~BSTree()//析构函数
		{
			//...
		}

	private:
		Node* _root = nullptr;//头结点
	};
}

2.3各种接口、功能、以及基本结构的补充

2.3.1 Find() 查找 (迭代/循环版本)

		bool Find(const K& key)
		{
			Node* cur = _root;//创建临时节点
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return true;//这是找到啦
				}
			}
			return false;//没找到才会出循环
		}

这里的思路很简单:该key< cur的_key,就进入到左子树;反之进入右子树

2.3.2 Insert() 插入(迭代/循环版本)

bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
                return true;
			}
			else
			{
				Node* cur = _root;
				Node* parent = nullptr;//这里存父亲节点,方便后续链接上
				while (cur)
				{
					if (key < cur->_key)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (key > cur->_key)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return false;//搜索二叉树里不能有相同 
					}
				}//出来后cur是nullptr,parent是叶子节点
				cur = new Node(key);//重新利用
				if (parent->_key < key)
				{
					parent->_right = cur;
				}
				else
				{
					parent->_left = cur;
				}

				return true;
			}
		}
  1. 如果二叉搜索树为空(即 _root == nullptr),则创建一个新节点 _root,其键值为 key,并将其作为根节点。
  2. 如果二叉搜索树不为空,则从根节点开始,沿着树的路径向下搜索应该插入新节点的位置。
  3. 在搜索过程中,如果发现要插入的键值 key 小于当前节点的键值,则继续在当前节点的左子树中搜索;如果大于当前节点的键值,则继续在右子树中搜索。
  4. 如果搜索到某个节点的键值与要插入的键值相等,则说明二叉搜索树中不能有相同的节点,直接返回 false
  5. 当搜索到空节点时,表示找到了要插入新节点的位置,此时创建一个新节点 cur,其键值为 key
  6. 最后,将新节点 cur 插入到父节点 parent 的左子树或右子树上,具体取决于新节点的键值与父节点的键值的大小关系
写出中序遍历来进行验证(中序遍历有序)
void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

	private:
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}

这里因为要传入根节点,所以正常无法在外部调用。我们采用子函数的方式来解决这个问题。

顺便把子函数放在private里,这样更安全

#include<iostream>
using namespace std;

#include"BSTree.h"

int main()
{
	key::BSTree<int> t;
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	
	t.InOrder();
}

在这里插入图片描述


2.3.3 Erase() 删除(迭代/循环版本)

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)//先找到在哪里删除
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else//进来就说明找到啦,就是现在的cur
				{
					if (cur->_left == nullptr)//左为空,就把右给父亲节点(为空也无妨)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (cur == parent->_right)//cur在parent右,那就接到右侧
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}

						delete cur;
						return true;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_right)//cur在parent右,那就接到右侧
							{
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}

						delete cur;
						return true;
					}
					else//左右都不是空,使用替换法,这里用右子树最小来换
					{
						Node* rightMinParent = cur;//右子树最小的父亲节点
						Node* rightMin = cur->_right;//右子树最小的节点

						while (rightMin->_left)//开始找
						{
							rightMinParent = rightMin;
							rightMin = rightMin->_left;
						}

						cur->_key = rightMin->_key;//把值一给,现在删rightMin就行了

						if (rightMin == rightMinParent->_left)
							rightMinParent->_left = rightMin->_right;
						else
							rightMinParent->_right = rightMin->_right;

						delete rightMin;
						return true;
					}
				}
			}
		}
  1. 首先,定义了两个指针 parentcur,分别用来记录当前节点的父节点和当前节点,初始时 cur 指向根节点 _root
  2. while 循环中,不断遍历二叉搜索树,直到找到要删除的节点或遍历完整棵树
  3. 在循环中,通过比较要删除的键值 key 与当前节点的键值 cur->_key 的大小关系,来确定是向左子树还是右子树继续遍历。
  4. 如果找到了要删除的节点,根据不同情况进行处理:
  • 如果要删除的节点没有左子树,直接将其右子树接到父节点的相应位置,然后删除该节点。
  • 如果要删除的节点没有右子树,类似地将其左子树接到父节点的相应位置,然后删除该节点。
  • 如果要删除的节点既有左子树又有右子树,采用替换法,找到右子树中最小的节点(即右子树中最左边的节点),将其键值替换到要删除的节点上,然后删除右子树中最小的节点。
  1. 删除节点后,返回 true 表示删除成功
验证
int main()
{
	key::BSTree<int> t;
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	t.InOrder();

	t.Erase(8);
	t.InOrder();

	t.Erase(14);
	t.InOrder();

	t.Erase(4);
	t.InOrder();

	t.Erase(6);
	t.InOrder();
	return 0;
}

在这里插入图片描述


2.3.4FindR() 查找 (递归版本)

		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}

	private:
		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)//到最后没找到
			{
				return false;
			}

			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}
  1. FindR 函数是对外提供的接口函数,用于从根节点开始递归查找指定键值的节点。
  2. _FindR 函数中,首先判断当前节点是否为空,如果为空则表示在当前路径上没有找到指定键值的节点,返回 false
  3. 如果当前节点的键值小于要查找的键值 key,则递归调用 _FindR 函数查找右子树。
  4. 如果当前节点的键值大于要查找的键值 key,则递归调用 _FindR 函数查找左子树。
  5. 如果当前节点的键值等于要查找的键值 key,则表示找到了目标节点,返回 true
  6. 通过递归的方式不断在左右子树中查找,直到找到目标节点或者遍历完整棵树

2.3.5Insert() 插入 (递归版本)

bool InsertR(const K& key)//难点在于,如何与父亲节点进行链接
		{
			return _InsertR(_root, key);
		}

	private:
		bool _InsertR(Node*& root,const K& key)//为了解决这个问题,我们用&引用来解决
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}

			if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else
			{
				return false;
			}
		}
  1. InsertR 函数是对外提供的接口函数,用于从根节点开始递归插入新节点。
  2. _InsertR 函数中,参数 root 是一个指向节点指针的引用,这样可以在递归过程中改变节点指针的指向,从而实现与父节点的链接
  3. 首先判断当前节点是否为空,如果为空则表示可以在该位置插入新节点,创建一个新节点并将其指针赋给 root,然后返回 true
  4. 如果当前节点的键值小于要插入的键值 key,则递归调用 _InsertR 函数插入到右子树。
  5. 如果当前节点的键值大于要插入的键值 key,则递归调用 _InsertR 函数插入到左子树。
  6. 如果当前节点的键值等于要插入的键值 key,则表示树中已经存在相同键值的节点,返回 false
  7. 通过递归的方式在左右子树中寻找合适的插入位置,并不断更新父节点的指针,直到插入新节点或者确认新节点已存在

2.3.6 EraseR() 删除 (迭代版本)

		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

	private:
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;//说明没找到,就返回空
			}

			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else//找到啦
			{
				Node* del = root;//保存一下,过会要delete的

				//这里便是引用开始发挥的作用,可以直接进行链接
				if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else//还是使用替换法,不过这次,直接交换后,再去右子树里递归
				{
					Node* rightMin = root->_right;
					while (rightMin->_left)
					{
						rightMin = rightMin->_left;
					}

					std::swap(root->_key, rightMin->_key);

					return _EraseR(root->_right, key);
				}

				delete del;
				return true;
			}
		}
  1. EraseR 函数是对外提供的接口函数,用于从根节点开始递归删除指定键值的节点。
  2. _EraseR 函数中,参数 root 是一个指向节点指针的引用,这样可以在递归过程中改变节点指针的指向,从而实现删除操作
  3. 首先判断当前节点是否为空,如果为空则表示在当前路径上没有找到指定键值的节点,返回 false
  4. 如果当前节点的键值小于要删除的键值 key,则递归调用 _EraseR 函数在右子树中删除。
  5. 如果当前节点的键值大于要删除的键值 key,则递归调用 _EraseR 函数在左子树中删除。
  6. 如果当前节点的键值等于要删除的键值 key,则表示找到了目标节点,进行删除操作。
  7. 如果目标节点只有一个子节点或者没有子节点,直接用子节点替换目标节点即可。
  8. 如果目标节点有两个子节点,找到右子树中最小的节点(即右子树中最左边的节点),将其键值与目标节点键值交换,然后在右子树中递归删除这个最小节点。
  9. 最后删除目标节点,并返回 true 表示删除成功

2.3.7 补全拷贝构造函数

		BSTree(const BSTree<K>& t)//拷贝构造函数
		{
			_root = copy(_root);
		}

		Node* copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;

			Node* newnode = new Node(root->_key);
			if (root->_left)
			{
				newnode->_left = copy(root->_left);
			}
			if (root->_right)
			{
				newnode->_right = copy(root->_right);
			}
			return newnode;
		}
  1. 在拷贝构造函数 BSTree(const BSTree<K>& t) 中,调用了 copy 函数来复制传入二叉搜索树 t 的根节点及其所有子节点。
  2. copy 函数接收一个节点指针 root,用于递归复制以该节点为根的子树。
  3. 首先判断当前节点是否为空,如果为空则返回 nullptr
  4. 创建一个新节点 newnode,键值为当前节点 root 的键值,表示复制当前节点。
  5. 如果当前节点有左子节点,则递归复制左子树,并将复制得到的左子树根节点赋值给新节点的左指针 _left
  6. 如果当前节点有右子节点,则递归复制右子树,并将复制得到的右子树根节点赋值给新节点的右指针 _right
  7. 返回新节点 newnode,表示复制当前节点及其所有子节点。(也是起到链接的作用)
  8. 在拷贝构造函数中,调用 copy 函数复制传入二叉搜索树的根节点,从而完成整棵树的复制。

2.3.8 补全析构函数

		~BSTree()//析构函数
		{
			Destory(_root);
		}

		void Destory(Node* root)//走个后序
		{
			if (root == nullptr)
				return;

			Destory(root->_left);
			Destory(root->_right);
			delete root;
		}

3.二叉搜索树应用

  1. K模型

    • 结构:在K模型中,每个节点只包含一个关键码(key),不包含值。节点的左子树中的所有节点都小于当前节点的关键码,右子树中的所有节点都大于当前节点的关键码。
    • 操作
      • 插入:将新的关键码插入到二叉搜索树中的合适位置,保持树的有序性。
      • 搜索:通过比较关键码,可以快速判断给定的值是否在树中存在。
    • 应用场景:适用于需要快速判断特定值是否存在的场景,如拼写检查、查找特定单词等。
  2. KV模型

    • 结构:在KV模型中,每个节点包含一个键值对(<Key, Value>),其中Key为关键码,Value为对应的值。节点的左子树中的所有节点的关键码小于当前节点的关键码,右子树中的所有节点的关键码大于当前节点的关键码。
    • 操作
      • 插入:插入新的键值对到二叉搜索树中,保持树的有序性。
      • 搜索:通过比较关键码,可以快速查找对应的值。
    • 应用场景
      • 英汉词典:以英文单词为关键码,对应的中文翻译为值,可以通过英文单词快速查找对应的中文翻译。
      • 统计单词次数:以单词为关键码,出现次数为值,可以方便地查找给定单词的出现次数。

4.性能分析

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

在这里插入图片描述

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

为了改进这种情况,可以使用自平衡二叉搜索树,如AVL树和红黑树。这些树在插入和删除操作时会通过旋转和重新平衡操作来保持树的平衡,从而确保树的高度较低,提高了搜索效率。

我后面也会继续学习,分享给大家!!!

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

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

相关文章

【C语言】—— 指针三 : 参透数组传参的本质

【C语言】—— 指针三 &#xff1a; 参透数组传参的本质 一、数组名的理解二、使用指针访问数组2.1、指针访问数组2.2、[ ] 的深入理解2.3、数组与指针的区别 三、一维数组的传参本质四、数组指针变量4.1、数组指针变量是什么4.2、 数组指针的初始化 五、二维数组传参的本质 一…

DML - 增删改(insert into,delete,update)

引言&#xff1a;对比DB / 表结构 : create , drop , alter 本次记录 数据操作 语言&#xff1a; 1.进入 hive 数据库&#xff0c;再打开 ryx1 表 2. insert select 3. update select 4. delete select

外卖项目:使用AOP切面编程实现增删改查的操作日志记录(debug断点调试)

文章目录 一、问题描述二、问题分析三、断掉调试四、代码展示 一、问题描述 需求&#xff1a;将项目中增、删、改相关接口的操作日志记录到数据库表中。 操作日志信息包含&#xff1a; 操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时…

武汉星起航:专业团队引领,经验与创新共铸跨境电商新辉煌

在竞争激烈的跨境电商市场中&#xff0c;武汉星起航电商公司凭借其专业的运营团队和多年的行业经验&#xff0c;成功脱颖而出。这支拥有丰富经验的团队&#xff0c;不仅深刻了解跨境电商市场的动态&#xff0c;更通过持续创新和个性化解决方案&#xff0c;为合作伙伴提供了强有…

STM32CubeMX学习笔记26---FreeRTOS互斥量

一、互斥量简介 1、互斥量用于互锁&#xff0c;可以充当资源保护的令牌&#xff0c;当一个任务希望访问某个资源时&#xff0c;它必须先获取令牌&#xff0c;当任务使用完资源后&#xff0c;必须返还令牌&#xff0c;以便其他任务可以访问该资源。 2、互斥量一般用于临界资源…

GPT-4引领AI新纪元,Claude3、Gemini、Sora能否跟上步伐?

【最新增加Claude3、Gemini、Sora、GPTs讲解及AI领域中的集中大模型的最新技术】 2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚…

【XXL-JOB】分布式任务调度解决方案,XXL-JOB入门

目录 1 概念2 快速入门2.1 启动xxl-job-admin2.2 创建一个新的定时任务2.3 在调度中心新增定时任务 3 详细介绍3.1 新建执行器3.2 创建任务3.3 其他概念3.3.1 路由策略3.3.2 任务运行模式(BEAN、GLUE)3.3.3 阻塞处理策略3.3.4 子任务3.3.5 任务超时时间 3.4 高级任务用法3.4.1 …

源码编译部署LAMP

编译部署LAMP 配置apache [rootzyq ~]#: wget https://downloads.apache.org/apr/apr-1.7.4.tar.gz --2023-12-11 14:35:57-- https://downloads.apache.org/apr/apr-1.7.4.tar.gz Resolving downloads.apache.org (downloads.apache.org)... 88.99.95.219, 135.181.214.104…

如何用 C++ 部署深度学习模型?

深度学习模型通常在诸如Python这样的高级语言中训练和验证&#xff0c;但在实际生产环境部署时&#xff0c;往往需要更高的执行效率和更低的资源占用。C作为一款性能卓越、低级别的编程语言&#xff0c;是部署深度学习模型的理想选择之一。本文将详细介绍如何在C环境下加载和运…

AI智能客服系统的费用

实现智能客服所需的费用取决于多个因素&#xff0c;包括项目的规模、所选择的技术和服务提供商、数据的获取和处理方式等。以下是一些可能影响费用的因素&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作…

使用stream流合并多个List(根据实体类特定属性合并)

开发情景 现有多个List集合,其中都是一样的实体类,这里我想根据实体类的特定属性将它们合并在一起,形成一个最终的List集合。 这里主要用到了Stream流的flatMap方法与reduce方法。 flatMap:可以将多个Stream流合并在一起,形成一个Stream流。 reduce:可以将Stram流中的元…

MySQL的概述与安装

一、数据库的基本概念&#xff1a; 1.1 数据&#xff1a; 1&#xff09; 描述事物的符号记录称为数据&#xff08;Data&#xff09;。数字、文字、图形、图像、声音、档案记录等 都是数据。 2&#xff09;数据是以“记录”的形式按照统一的格式进行存储的&#xff0c;而不是…

ASA方舟生存飞升计划1.5重置版服务器搭建教程

ASA方舟生存飞升计划1.5重置版服务器搭建教程 大家好我是艾西一个做服务器租用的网络安全工程人员&#xff0c;以前有给大家分享过方舟生存进化的搭建架设教程。方舟这游戏出的时间也很久了&#xff0c;随着时间的推移官方有出新的版本命名为飞升计划&#xff0c;不少的玩家都…

数据库系统概论-第4章 数据库安全性

4.1 数据库安全性概述 4.2 数据库安全性控制 4.3 视图机制 4.4 审计 4.5 数据加密 4.6 其他安全性保护 4.7 小结

SVN修改已提交版本的注释

目录 一、需求分析 二、问题分析 三、解决办法 一、需求分析 ​开发过程中&#xff0c;在SVN提交文件后&#xff0c;发现注释写的不完整或不够明确&#xff0c;想再修改之前的注释文字​。 使用环境&#xff1a; SVN服务器操作系统&#xff1a;Ubuntu 20.04.6 LTS SVN版本&…

linux网络服务学习(2):vsftp

1.什么是vsftp vsftp是linux服务器上的一款使用ftp协议的软件&#xff0c;是linux上使用最广泛的ftp服务端软件 ftp协议是使用明文传输的&#xff0c;很不安全&#xff0c;一般用于局域网内的文件上传、下载 2.vsftp连接类型 ftp连接要用到2个端口&#xff1a;21、20端口。…

Visual Studio 2013 - 重置窗口布局

Visual Studio 2013 - 重置窗口布局 1. Microsoft Visual Studio 2013 - 重置窗口布局References 1. Microsoft Visual Studio 2013 - 重置窗口布局 窗口 -> 重置窗口布局 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

HarmonyOS定时器与定时任务

HarmonyOS 的 ArkTS 说白了 就是 TS和JS混合 加了一些新特性的语言 定时任务 就还是用 js代码就OK了 我们代码这样写 Entry Component struct Twox {build() {Row() {Column(){Button("触发定时任务").onClick(()>{setTimeout(()> {console.log(执行)},2000…

Halcon 凹坑检测案例

* 使用元组的方法 ImageFile:[] ImageFile[0]:D:/Halcon/产品上的凹坑检测/1.bmp ImageFile[1]:D:/Halcon/产品上的凹坑检测/2.bmp for Index : 0 to |ImageFile|-1 by 1read_image (Image, ImageFile[Index])* 二值化threshold (Image, Region, 100, 255)* 连通性connection (…

C语言实现流水灯

头文件 #ifndef __LED_H__ #define __LED_H__ #define RCC (*(unsigned int *)0X50000A28) #define GPIOE_MODER (*(unsigned int *)0X50006000) #define GPIOF_MODER (*(unsigned int *)0X50007000) #define GPIOE_OTYPER (*(unsigned int *)0X50006004) #define GPIOE_OSPE…