【高阶数据结构】搜索二叉树 经典习题讲解

news2024/9/30 11:24:55

🌈欢迎来到数据结构专栏~~搜索二叉树


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    请添加图片描述

请添加图片描述

二叉搜索树

  • 🌈欢迎来到数据结构专栏~~搜索二叉树
    • 一. 概念
    • 二. 基本操作
      • 🌈查找元素 Search
      • 🌈插入 Insert
      • 🌈删除 Delete
    • 三. 进阶操作 ~ 递归写法
      • 🥑递归查找 Search
      • 🥑递归插入 Insert
      • 🥑递归删除 Delete
    • 四. 性能分析
    • 五. 二叉搜索树的应用
      • 💦Key模型
      • 💦Key- Value模型
    • 附源码
      • `BinarySearchTree.h`
      • `test.c`
    • 二叉树习题大全
      • 1️⃣根据二叉树创建字符串
      • 2️⃣二叉树的层序遍历
      • 3️⃣二叉树的层序遍历 II
      • 4️⃣二叉树的最近公共祖先
      • 5️⃣ 二叉搜索树与双向链表
      • 6️⃣从前序与中序遍历序列构造二叉树
      • 8️⃣
  • 📢写在最后

请添加图片描述

一. 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则 左子树上所有节点的值都小于根节点的值

  • 若它的左子树不为空,则 右子树上所有节点的值都大于根节点的值

  • 它的左右子树也分别为二叉搜索树

在这里插入图片描述

二. 基本操作

🌈查找元素 Search

在这里插入图片描述

  • 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找
  • 最多查找高度次,走到到空,还没找到,这个值不存在
	//查找
	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->_right;
			}
			else if (cur->_key < key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
	}

🌈插入 Insert

思路如下:

  1. 树为空,则直接新增节点,直接插入root指针即可
  2. 因为不能插入重复的元素,并且每次插入都是要定位到空节点的位置;定义一个 cur 从root开始,插入的元素比当前位置元素小就往左走,比当前位置元素大就往右走,直到为空;
  3. 再定义一个parent记录 cur的前一个位置,最后判断cur是parent的左子树or右子树

动画演示:

请添加图片描述

    //相同的
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{ 
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		//链接节点
		cur = new Node(key);
		if(parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;
	}

🌈删除 Delete

删除有三种情况:

  1. 无牵无挂型:如果是叶子结点,直接置空并链接到空指针
  2. 独生子女型:只有一个子节点,删除自己本身,并链接子节点和父节点
  3. 替罪羊型:寻找出右子树最小节点左子树最大节点,其节点可以作为交换节点和删除结点进行交换,交换后删除交换节点、交换节点要么没有孩子,要么只有一个孩子可以直接删除

在这里插入图片描述

其实在代码层面第一种情况可以归类到第二种情况(没有左孩子,就链接上右孩子)

在代码层面实际上是四种:

  1. 左为空
  2. 右为空
  3. 左右都为空(细节很多看下图)
  4. cur是跟节点位置(特殊情况移动root

在这里插入图片描述

	//删除
	bool Erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//找到了,开始删除,有三种情况
				//1、左为空
				//2、右为空
				//3、左右都不为空
				//4、删除跟root 要移动root(特殊情况)
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
					cur = nullptr;
				}
				else if (cur->_right == nullptr)
				{
					if (_root == cur)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
					cur = nullptr;
				}
				else
				{
					//左右都不为空  ——  替换法删除
					//找到右树最小节点进行替换
					Node* min = cur->_right;
					Node* minparent = nullptr;
					while (min->_left)
					{
						minparent = min;
						min = min->_left;
					}
					swap(cur->_key, min->_key);

					//注意min的右子树还有连接节点的可能
					//和判断min在哪边的问题?
					if (minparent->left = min)
					{
						minparent->_left = min->_right;
					}
					else
					{
						minparent->_right = min->_right;
					}
					delete min;
				}
				return true;
			}
		}
		return false;
	}

三. 进阶操作 ~ 递归写法

🥑递归查找 Search

唤起递归的记忆吧

	void _FindR(Node* root, const K& key)
	{
		//根为空的情况下
		if (root == nullptr)
		{
			return false;
		}

		if (root->_key < key)
		{
			return _FindR(root->_right);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left);
		}
		else
		{
			return true;
		}
	}

🥑递归插入 Insert

要注意,这里有“神之一手

在这里插入图片描述

在这里插入图片描述

	void _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;
		}
	}

🥑递归删除 Delete

神之操作:root = root->_left

在这里插入图片描述

在这里插入图片描述

	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;
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				//找右树的最小节点 - 替换删除
				Node* min = root->_right;
				while (min->_left)
				{
					min = min->_left;
				}
				swap(min->_key, root->_key);
				return _Erase(root->_right)
			}

			delete del;
			return true;
		}
	}

四. 性能分析

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

在这里插入图片描述

五. 二叉搜索树的应用

💦Key模型

key的搜索模型,判断关键字在不在

  • 刷卡进宿舍楼
  • 检测一篇英文文档中单词拼写是否正确

以上都是把全部相关的资料都插入到一颗搜索树中,然后开始寻找,判断在不在

💦Key- Value模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对

  • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对
  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对
// 改造二叉搜索树为KV结构
template<class K, class V>
struct BSTNode
{
	BSTNode(const K& key = K(), const V& value = V())
		: _pLeft(nullptr), _pRight(nullptr), _key(key), _Value(value)
	{}
	BSTNode<T>* _pLeft;
	BSTNode<T>* _pRight;
	K _key;
	V _value
};
template<class K, class V>
class BSTree
{
	typedef BSTNode<K, V> Node;
	typedef Node* PNode;
public:
	BSTree() : _pRoot(nullptr) {}
	PNode Find(const K& key);
	bool Insert(const K& key, const V& value)
		bool Erase(const K& key)
private:
	PNode _pRoot;
}

void TestBSTree3()
{
	// 输入单词,查找单词对应的中文翻译
	BSTree<string, string> dict;
	dict.Insert("string", "字符串");
	dict.Insert("tree", "树");
	dict.Insert("left", "左边、剩余");
	dict.Insert("right", "右边");
	dict.Insert("sort", "排序");
	// 插入词库中所有单词
	string str;
	while (cin >> str)
	{
		BSTreeNode<string, string>* ret = dict.Find(str);
		if (ret == nullptr)
		{
			cout << "单词拼写错误,词库中没有这个单词:" << str << endl;
		}
		else
		{
			cout << str << "中文翻译:" << ret->_value << endl;
		}
	}
}

附源码

BinarySearchTree.h

#pragma once

#include<iostream>
using namespace std;


namespace Key
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;

		BSTreeNode(const K& key)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}
	};

	//class BinarySearchTree
	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;

	public:
		//插入
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}

			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			//链接节点
			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}

			return true;
		}

		//查找
		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					cur = cur->_right;
				}
				else if (cur->_key < key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
		}

		//删除
		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//找到了,开始删除,有三种情况
					//1、左为空
					//2、右为空
					//3、左右都不为空
					//4、删除跟root 要移动root(特殊情况)
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else if (cur->_right == nullptr)
					{
						if (_root == cur)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else
					{
						//左右都不为空  ——  替换法删除
						//找到右树最小节点进行替换
						Node* min = cur->_right;
						Node* minparent = nullptr;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);

						//注意min的右子树还有连接节点的可能
						//和判断min在哪边的问题?
						if (minparent->_left = min)
						{
							minparent->_left = min->_right;
						}
						else
						{
							minparent->_right = min->_right;
						}
						delete min;
					}
					return true;
				}
			}
			return false;
		}


		//这样就能避免this指针问题,因为递归必须显示给参数
		void InOrder()
		{
			_InOrder(_root);//这样就可以使用_root
			cout << endl;
		}


		/// //
		/// 递归写法

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

		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}

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

		~BSTree()
		{
			_Destory(_root);
		}

		//BSTree()
		//{}

		//C++11的用法:强制编译器生成默认构造
		BSTree() = default;


		//拷贝构造
		BSTree(const BSTree<K>& t)
		{
			_root = _Copy(t._root);
		}

		//t2 = t1
		BSTree<K>& operator = (BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}

	private:
		Node* _Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}

			Node* copyRoot = new Node(root->_key);
			copyRoot->_left = _Copy(root->_left);
			copyRoot->_right = _Copy(root->_right);
			return copyRoot;
		}

		void _Destory(Node*& root)
		{
			if (root == nullptr)
			{
				return;
			}

			_Destory(root->_left);
			_Destory(root->_right);
			delete root;
			root = nullptr;
		}

		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;
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else
				{
					//找右树的最小节点 - 替换删除
					Node* min = root->_right;
					while (min->_left)
					{
						min = min->_left;
					}
					swap(min->_key, root->_key);
					return _Erase(root->_right);//防止找不到key的情况
				}

				delete del;
				return true;
			}
		}

		void _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;
			}
		}

		void _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;
			}
		}

		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}

	private:
		Node* _root = nullptr;
	};

	void TestBSTree1()
	{
		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(3);
		t.InOrder();

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


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

		BSTree<int> copy = t;
		copy.InOrder();
		t.InOrder();
	}
}

test.c

#include "BinarySearchTree.h"

int main()
{
	//TestBSTree1();
	/*TestBSTree2();*/
	TestBSTree3();

	return 0;
}

二叉树习题大全

1️⃣根据二叉树创建字符串

题目地址:传送

在这里插入图片描述

思路:

  1. 左括号为空,右括号不为空,不可以省略
  2. 左右括号为空,省略
class Solution {
public:
    string tree2str(TreeNode* root) {
        if(root == nullptr)
            return string();

        string str;
        str += to_string(root->val);
        
        //左边不为空or左变为空,右变不为空 不可以省略
        if(root->left || root->right)
        {
            str += '(';
            str += tree2str(root->left);
            str += ')';
        }

        //右为空的都可以省略
        if(root->right)
        {
            str += '(';
            str += tree2str(root->right);
            str += ')';
        }
        return str;
    }
};

2️⃣二叉树的层序遍历

题目地址:传送

在这里插入图片描述

解题思路:

  1. 要控制一层一层的出,定义一个levelsize,每次的队列的个数就是levelsize的大小
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q;
        size_t levelsize = 0;
        if(root)
        {
            q.push(root);
            levelsize = 1;
        }

        vector<vector<int>> vv;
        while(!q.empty())
        {
            //控制一层一层的出
            vector<int> v;
            for(size_t i = 0; i < levelsize; i++)
            {
                TreeNode* front = q.front();
                q.pop();
                v.push_back(front->val);

                if(front->left)
                    q.push(front->left);

                if(front->right)
                    q.push(front->right);
            }
            vv.push_back(v);
            //当前层出完了,下一层都进队列了,队列的size就是下一层的数据个数
            levelsize = q.size();
        }
        return vv;
    }
};

3️⃣二叉树的层序遍历 II

在这里插入图片描述

要求反过来输出
其实只要在上面题目的基础上倒置一下二维数组即可

4️⃣二叉树的最近公共祖先

题目链接:传送

在这里插入图片描述

在这里插入图片描述

  • 规则:一个是左子树中节点,一个是右子树节点,那么它就是最近公共祖先
  • 此处函数的起名很重要
class Solution {
public:
    bool Find(TreeNode* sub, TreeNode* x)
    {
        if(sub == nullptr)
            return false;
        
        if(sub == x)
            return true;

        return Find(sub->left, x)
            || Find(sub->right, x);
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr)
            return nullptr;

        //根是我
        if(root == q || root == p)
            return root;

        bool qInLeft, qInRight, pInLeft, pInRight;
        pInLeft = Find(root->left, p);
        pInRight = !pInLeft;

        qInLeft = Find(root->left, q);
        qInRight = !qInLeft;

        //1、一个在左一个在右,root就是最近公共祖先
        //2、如果都在左,就递归去左边
        //3、如果都在右,就递归去右边
        if((pInLeft && qInRight) || (qInLeft && pInRight))
        {
            return root;
        }
        else if (qInLeft && pInLeft)
        {
            return lowestCommonAncestor(root->left, p, q);
        }
        else if (qInRight && pInRight)
        {
            return lowestCommonAncestor(root->right, p, q);
        }
        else
        {
            return nullptr;
        }
    }
};

思路2:存储路径

  1. 定义一个stack来存储路径,通过递归来找到p和q的具体路径
  2. 大的路径先走,直到两个路径相同,比较两个路径的top(),不相等的同样pop掉,最后返回的肯定是相等的

在这里插入图片描述

class Solution {
public:
    bool FindPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
    {
        if(root == nullptr)
            return false;

        path.push(root);
        if(root == x)
            return true;

        if(FindPath(root->left, x,path))
            return true;
        if(FindPath(root->right, x, path))  
            return true;

        path.pop();
        return false;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath, qPath;
        FindPath(root, p, pPath);
        FindPath(root, q, qPath);

        //类似链表相交 —— 大的先走 直到两个相等
        while(pPath.size() != qPath.size())
        {
            if(pPath.size() > qPath.size())
                pPath.pop();
            else
                qPath.pop();
        }

        //两个值不相等一起pop
        while(pPath.top() != qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }

        //最后肯定相等,随便返回一个即可
        return pPath.top();
    }
};

5️⃣ 二叉搜索树与双向链表

题目地址:传送

在这里插入图片描述

思路:

  1. left指向中序的前一个;right指向中序的后一个
  2. 中序遍历:cur的前一个是prev, cur移动后,prev的后一个是cur ;构成双向链接
class Solution {
public:
	void InOderConvert(TreeNode* cur, TreeNode*& prev)
	{
		if(cur == nullptr)
			return ;
		
		InOderConvert(cur->left, prev);
		cur->left = prev;
		if(prev)
			prev->right = cur;
		
		prev = cur;

		InOderConvert(cur->right, prev);
	}

    TreeNode* Convert(TreeNode* pRootOfTree) {
        TreeNode* prev = nullptr;
		InOderConvert(pRootOfTree, prev);

		TreeNode* head = pRootOfTree;
		while(head && head->left)
			head = head->left;

		return head;
    }
};

6️⃣从前序与中序遍历序列构造二叉树

题目链接:传送

在这里插入图片描述

相信大家都做过这道选择题吧 哈哈哈

思路:

  1. 前序创建树,中序分割左右子树
  2. 子树区间确认是否继续递归创建子树,不存在区间则空树
class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inbegin, int inend) {
        if(inbegin > inend)
            return nullptr;

        TreeNode* root = new TreeNode(preorder[prei++]);
        //分割中序
        int ini = inbegin;
        while(ini <= inend)
        {
            if(inorder[ini] == root->val)
                break;
            else
                ini++;
        }

        //[inbegin, ini-1] ini [ini+1, inend]
        root->left = _buildTree(preorder, inorder, prei, inbegin, ini-1);
        root->right = _buildTree(preorder, inorder, prei, ini+1,  inend);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int i = 0;
        return _buildTree(preorder,inorder, i, 0, inorder.size()-1);

    }
};

8️⃣

📢写在最后

多多更新

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

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

相关文章

【OpenFOAM】-olaFlow-算例6- waveFloatingObject

算例路径&#xff1a; olaFlow\tutorials\waveFloatingObject 算例描述&#xff1a; 波浪作用下的浮体的刚体运动&#xff0c;属于流固耦合&#xff08;FSI&#xff09;问题 学习目标&#xff1a; 动网格设置和使用&#xff0c;网格变形控制&#xff0c;浮体的物理参数设置&…

23种设计模式(二)——享元模式【对象性能】

文章目录意图什么时候使用享元享元模式的实现内部状态和外部状态享元模式的优缺点与其他模式的关系亦称&#xff1a; 缓存、Cache、Flyweight 意图 享元模式是一种结构型设计模式&#xff0c; 它摒弃了在每个对象中保存所有数据的方式&#xff0c; 通过共享多个对象所共有的相…

数图互通房产管理系统架构分析

数图互通高校房产管理系统V5.0 使用JAVA、Canvas、H5等技术开发的图形数据交互技术架构平台&#xff1b;本系统满足XX大学房屋管理系统需求&#xff0c;高校房产综合管理信息系统平台V5.0遵循高校房产“分级授权、分类管理、网络化、图形化、精细化、流程化”的管理理念&#x…

关于新冠的几点总结

关于新冠的几点总结一、前言:二、病程阶段1. 第一阶段 反复发热2. 第二极端 退烧虚弱3. 第三阶段 咳嗽嗜睡三、处置措施:1. 思想准备2. 药/物准备3. 退烧方式4. 保持体温5. 通则不痛&#xff0c;痛则不通6. 营养补充7. 恢复关键期写在最后一、前言: 所写内容&#xff0c;为个人…

磊科路由器后门蜜罐捕获的事件分布情况

重点物联网 漏洞利用情况本节我们选取了两个漏洞进行分析。UPnP 相关的漏洞我们将在 4.4.3 进行分析&#xff0c;除去 UPnP 相关漏 洞外&#xff0c;被利用最多的是 Eir D1000 路由器的一个漏洞 [44]&#xff08;CVE-2016-10372&#xff09;&#xff0c;我们将对其进行分析。 …

Apollo浅解2

目录 用户、角色、权限 三者间的关系 权限Permission 新增一个应用时 新增一个命名空间时 角色Role 新增一个应用时 新增一个命名空间时 第三方应用 用户、角色、权限 三者间的关系 apollo也采用经典的三层权限设计&#xff0c;用户关联角色&#xff0c;角色关联权限…

DOM基础

一、DOM的概念 文档对象模型(DOM,Document Object Module)是W3C组织推荐的处理可扩展标志语言的标准编程接口&#xff0c;它允许程序和脚本动态的访问和更新文档的内容、结构和样式。 HTML的DOM操作是将文档里所有的内容(包括标签、标签里的内容、标签属性甚至注释等)都当做一…

51单片机入门 第一篇:LED灯

文章目录前言一、LED原理图二、创建keil5工程三、代码的编写四、程序的烧录总结前言 本篇文章讲正式带大家开始学习51单片机&#xff0c;希望这些文章能够很好的帮助到大家学习51单片机。 一、LED原理图 一般的51单片机上都带有8个LED灯&#xff0c;这里8个LED灯分别接到了板…

JS逆向——工信部ICP、IP、域名信息备案管理平台

问题&#xff1a;&#xff08;1&#xff09;数据列表接口token参数验证&#xff08;2&#xff09;authKey参数加密生成 1、页面中请求接口&#xff0c;观察请求头可发现&#xff0c;校验参数token为加密的字符串&#xff0c;根据该字符串并不能直观得到所用的加密方式是什么。 …

数据库大小写不敏感后,值也不敏感了

现象&#xff1a;我有一个账号admin&#xff0c;结果莫名多了一个ADMIN、Admin、AdMin等一些列账号&#xff1b;细品你的密码就算密文签名&#xff0c;是不是在你不知情的情况下也有很多。 原因&#xff1a;数据库安装的时候设置的大小写不敏感导致 解决&#xff1a;建议第三…

Spark 的学习笔记

Spark 的学习笔记 文章目录Spark 的学习笔记1. 概述Spark 优势及特点优秀的数据模型和丰富计算抽象Spark 生态圈Spark 特点Spark 与 HadoopSpark与MRSpark Streaming与StormSpark SQL与HiveSpark 运行模式2. 快速入门使用 Spark Shell 进行交互式分析基础Dataset 上的更多操作缓…

【机器学习】LDA算法原理

问题 线性判别分析&#xff08;Linear Discriminant Analysis&#xff0c;LDA&#xff09;是机器学习中常用的降维方法之一&#xff0c;本文旨在介绍LDA算法的思想&#xff0c;其数学推导过程可能会稍作简化。 LDA的思想 ● LDA是一种线性的、有监督的降维方法&#xff0c;即…

销售流程标准化重要吗?

各行各业都存在销售&#xff0c;但并不是每个销售都可以成为优秀的销售&#xff0c;优秀的销售往往有一套完整的销售流程&#xff0c;为了保证销售新人销售工作的顺利进行&#xff0c;销售流程标准化很有必要。 前言 各行各业都存在销售&#xff0c;但并不是每个销售都可以成为…

前端面试题之计算机网络篇--WebSocket基本使用

WebSocket 普通的包的请求和响应过程 1. 对 WebSocket 的理解 WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术&#xff0c;属于应用层协议。它基于TCP传输协议&#xff0c;并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手&#xff0c;两者之间…

HDLBits练习汇总-13-时序逻辑设计测试--状态机(一)

简单摩尔状态机1&#xff08;Fsm1&#xff09; 是一个摩尔状态机&#xff0c;具有两种状态&#xff0c;一种输入&#xff0c;一种输出。实现此状态机。请注意&#xff0c;重置状态为 B。使用异步复位。 模块声明 module top_module(input clk,input areset, // Asynchrono…

基础数学复习(3)——曲线拟合

文章目录基础概念曲线拟合的流程极小化损失函数线性最小二乘超定方程组的最小二乘解&#xff08;必考&#xff09;例题&#xff08;必考&#xff09;使用法方程计算拟合方程使用最小二乘法求解总结基础概念 曲线拟合的流程 选取函数类选取参数的准则&#xff1a;极小化损失函…

实习-------数据库基础

检索数据 1、如果使用DISTINCT关键字&#xff0c;它必须直接放在列名的前面。不能部分使用DISTINCT&#xff0c;DISTINCT关键字应用于所有列而不仅是前置它的列 例如&#xff1a;SELECT DISTINCT vend_id告诉MySQL只返回不同&#xff08;唯一&#xff09;的vend_id行 2、带一…

(六)devops持续集成开发——jenkins的全局工具配置之node环境安装及配置

前言 本节内容主要是关于jenkins集成node组件&#xff0c;从而实现前端node项目的流水线CICD发布功能。我们需要先安装好前端组件node,并在jenkins中配置好node组件&#xff0c;这样就可以流水线发布一个前端工程了。 正文 安装node组件①上传node安装包 ②解压node安装包 t…

用纯python脚本玩转UU加速器

1. 前言 之前几期内容&#xff0c;我们出过纯py形式的Android自动化脚本。同学们一直让再出一下纯py形式的Windows脚本&#xff0c;今天我们以UU加速器为例&#xff0c;给大家出一个简单的学习demo。 2. UU加速器的自动化demo 今天的练习demo也非常简单&#xff0c;大致内容…

张勇用最严厉的内部信,敲打阿里云,也在提振阿里士气

“「客户第一」的价值观&#xff0c;从来都不是高高挂在公司墙上的标语&#xff0c;而是支撑我们每一天获得成长的基石”。这应该是阿里巴巴董事局主席兼CEO张勇&#xff0c;自2015年来最严厉的一封内部信。信件里&#xff0c;张勇一改往日温情形象&#xff0c;措辞严厉的批评了…