<C++>二叉树进阶

news2024/10/5 3:18:45

文章目录

  • 为什么要学这一节
  • 1. 二叉搜索树
    • 1.1 二叉搜索树概念
    • 1.2 二叉搜索树操作
    • 1.3 二叉搜索树的实现
    • 1.4 二叉搜索树的应用
    • 1.5 二叉搜索树的性能分析
  • 2. 经典题目
    • 2.1 最近公共祖先
    • 2.2 从前序与中序遍历序列构造二叉树
    • 2.3 二叉树的前序遍历(非递归)

为什么要学这一节

二叉树在前面C数据结构阶段已经讲过,本节取名二叉树进阶是因为:

  1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构
  2. 二叉搜索树的特性了解,有助于更好的理解map和set的特性
  3. 二叉树中部分面试题稍微有点难度,在前面讲解大家不容易接受,且时间长容易忘
  4. 有些OJ题使用C语言方式实现比较麻烦,比如有些地方要返回动态开辟的二维数组,非常麻烦。

因此本节借二叉树搜索树,对二叉树部分进行收尾总结。

1. 二叉搜索树

1.1 二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

image-20220818094927219

1.2 二叉搜索树操作

  1. 二叉搜索树的查找
    a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
    b、最多查找高度次,走到到空,还没找到,这个值不存在。
  2. 二叉搜索树的插入
    插入的具体过程如下:
    a. 树为空,则直接新增节点,赋值给root指针
    b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

image-20220824100136750

  1. 二叉搜索树的删除
  • 首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
    a. 要删除的结点无孩子结点
    b. 要删除的结点只有左孩子结点
    c. 要删除的结点只有右孩子结点
    d. 要删除的结点有左、右孩子结点
  • 看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
    情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
    情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
    情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除

image-20220824100146652

1.3 二叉搜索树的实现

#pragma once
#include <iostream>
#include <string.h>
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)
		{}
	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	private:
		void destoryTree(Node* root)
		{
			if (root == nullptr)
				return;

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

		Node* CopyTree(const Node* root)// 前序遍历递归
		{
			if (root = nullptr)
				return nullptr;

			Node* copyNode = new Node(root->_key);
			copyNode->_left = CopyTree(root->_left);
			copyNode->_right = CopyTree(root->_right);

			return copyNode;
		}
	public:
		//自己写了拷贝构造函数,编译器就不会默认生成构造函数
		//default用于强制编译器自己生成构造(C++11)
		BSTree() = default;

		BSTree(const BSTree<K>& t)
		{
			_root = CopyTree(t._root);
		}

		~BSTree()
		{
			destoryTree(_root);
			_root = nullptr;
		}

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

		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			cur = new Node(key);
			if (key > parent->_key)
			{
				parent->_right = cur;
			}
			else if (key < parent->_key)
			{
				parent->_left = cur;
			}
			return true;
		}

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

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					// 一个孩子、没有孩子:托孤
					// 两个孩子:替代法
					if (cur->_left == nullptr)
					{
						// 删根节点
						// if (parent == nullptr)
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (cur == parent->_right)
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}
						delete cur;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					//两个孩子:替代法。用左子树的最右节点 或 右树的最左节点
					else
					{
						//用右树的最左节点替代
						Node* minParent = cur;// 存父亲,不能给nullptr,因为右子树的根可能就是minRight
						Node* minRight = cur->_right;// 存
						while (minRight->_left)// 找右树的最左
						{
							minParent = minRight;
							minRight = minRight->_left;
						}
						swap(cur->_key, minRight->_key);
						// 开始托孤
						// return erase(key);交换后不符合二叉搜索树了。找不到key了。
						if (minRight = minParent->_left)
						{
							minParent->_left = minRight->_right;
						}
						else
						{
							minParent->_right = minRight->_right;
						}
						delete minRight;
					}
					return true;
				}
			}
			return false;// cur为空,找不到要删的
		}

		void InOrder()// 在类外传递root很麻烦,所以再套一层
		{
			_InOrder(_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);
		}
	private:
		bool _EraseR(Node*& root, const K& key)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
		{
			if (root == nullptr)
				return false;

			if (key > root->_key)
				return _EraseR(root->_right, key);
			else if (key < root->_key)
				return _EraseR(root->_left, key);
			else
			{
				Node* del = root;
				if (root->_left == nullptr)
				{
					root = root->_right;// root是父节点孩子的别名。
					return true;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
					return true;
				}
				else
				{
					Node* minRight = root->_right;
					while (minRight->_left)
					{
						minRight = minRight->_left;
					}

					swap(minRight->_key, root->_key);

					return _EraseR(root->_right, key);// 递归删除,删除左为空的节点
				}
			}
		}
		bool _InsertR(Node*& root, const K& key)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (key > root->_key)
				return _InsertR(root->_right, key);
			else if (key < root->_key)
				return _InsertR(root->_left, key);
			else
				return false;
		}

		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;

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

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

			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
		Node* _root = nullptr;
	};

	void test1()
	{
		BSTree<int> t;
		int a[] = { 14,1,76,42,13,5 };
		for (auto e : a)
		{
			t.InsertR(e);
		}
		t.InOrder();

		for (auto e : a)
		{
			t.EraseR(e);
		}
		t.InOrder();

	}
}

1.4 二叉搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

    • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树。
    • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:

    • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
    • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对
namespace key_value
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;

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

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		void InOrder()// 在类外传递root很麻烦,所以再套一层
		{
			_InOrder(_root);
			cout << endl;
		}

		// 递归
		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}

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

		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		bool _EraseR(Node*& root, const K& key)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
		{
			if (root == nullptr)
				return false;

			if (key > root->_key)
				return _EraseR(root->_right, key);
			else if (key < root->_key)
				return _EraseR(root->_left, key);
			else
			{
				Node* del = root;
				if (root->_left == nullptr)
				{
					root = root->_right;// root是父节点孩子的别名。
					return true;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
					return true;
				}
				else
				{
					Node* minRight = root->_right;
					while (minRight->_left)
					{
						minRight = minRight->_left;
					}

					swap(minRight->_key, root->_key);

					return _EraseR(root->_right, key);// 递归删除,删除左为空的节点
				}
			}
		}
		bool _InsertR(Node*& root, const K& key, const V& value)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
		{
			if (root == nullptr)
			{
				root = new Node(key, value);
				return true;
			}
			if (key > root->_key)
				return _InsertR(root->_right, key, value);
			else if (key < root->_key)
				return _InsertR(root->_left, key, value);
			else
				return false;
		}

		Node* _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return nullptr;

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

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

			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
		Node* _root = nullptr;
	};

	void Test1()
	{
		BSTree<string, string> ECDict;
		ECDict.InsertR("root", "根");
		ECDict.InsertR("string", "字符串");
		ECDict.InsertR("left", "左边");
		ECDict.InsertR("insert", "插入");
		//...
		string str;
		while (cin >> str)  //while (scanf() != EOF)
		{
			//BSTreeNode<string, string>* ret = ECDict.FindR(str);
			auto ret = ECDict.FindR(str);
			if (ret != nullptr)
			{
				cout << "对应的中文:" << ret->_value << endl;
				//ret->_key = "";
				ret->_value = "";
			}
			else
			{
				cout << "无此单词,请重新输入" << endl;
			}
		}
	}
}

1.5 二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

image-20220824100525556

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

问题:如果退化成单支树,二叉搜索树的性能就失去了。

那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上场了

2. 经典题目

2.1 最近公共祖先

二叉树的最近公共祖先

class Solution {
public:
    bool isInSubTree(TreeNode* tree, TreeNode* x)
    {
        if(tree == nullptr)
            return false;

        if(tree == x)
            return true;

        return isInSubTree(tree->left, x)
        || isInSubTree(tree->right, x);
    }

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

        if(root == p || root == q)
            return root;

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

        if(pInLeft && qInLeft)
        {
            return lowestCommonAncestor(root->left, p, q);
        }
        else if(pInRight && qInRight)
        {
            return lowestCommonAncestor(root->right, p, q);
        }
        else
        {
            return root;
        }
    }
};

要一个个找,效率无法保证。把题目转化为相交链表求交点,把它的路径求出来,在找交点,交点即为最近公共祖先。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool Route(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
    {
        if(root == nullptr)
            return false;

        path.push(root);

        if(root == x)
            return true;
        else if(Route(root->left, x, path))
            return true;
        else if(Route(root->right, x, path))
            return true;
        else
        {
            path.pop();
            return false;
        }

    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath, qPath;
        Route(root, p, pPath);
        Route(root, q, qPath);
        //将这题转化为相交链表求交点,先让它们长度相同
        while(pPath.size() > qPath.size())
            pPath.pop();
        while(pPath.size() < qPath.size())
            qPath.pop();
        //再同时走,求交点
        while(pPath.top()!= qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }
        return pPath.top();
    }
};

2.2 从前序与中序遍历序列构造二叉树

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

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
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]);
        prei++;

        int rooti = inBegin;
        while(rooti<=inEnd)
        {
            if(inorder[rooti] == root->val)
                break;
            else
                rooti++;
        }

        root->left = _buildTree(preorder, inorder, prei, inBegin, rooti-1);
        root->right = _buildTree(preorder, inorder, prei, rooti+1, inEnd);

        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int prei = 0, inBegin = 0, inEnd = preorder.size()-1;
        TreeNode* root = _buildTree(preorder, inorder, prei, inBegin, inEnd);
        return root;
    }
};

2.3 二叉树的前序遍历(非递归)

非递归前序遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while(cur || !st.empty())
        {
        	//遍历左路节点,左路节点入栈——访问一棵树的开始
            while(cur)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur = cur->left;
            }
            
            //依次取左路节点的右子树访问
            TreeNode* top = st.top();
            st.pop();
            
			//访问左路节点的右子树--子问题
            cur = top->right;
        }
        return v;
    }
};

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

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

相关文章

计算机组成原理复习:数据的表示和运算

计算机组成原理复习&#xff1a;数据的表示和运算2. 数据的表示和运算2.1 数制与编码2.1.1 数制&#xff1a;进位计数制及其相互转换2.1.2 编码&#xff1a;数值数据的编码与表示2.1.2.1 逻辑型数据2.1.2.2 字符型数据 之 ASCII码2.1.2.3 数值型数据 之 BCD码2.1.3 校验码——奇…

Part类 -- 上传文件

Part类 -- 上传文件一、核心方法1.1 HttpServletRequest 类方法1.2 Part 类方法二、代码示例前端搭配 form 表单&#xff1a;form input type “file”&#xff0c;允许通过浏览器选中一个文件上传给服务器。 Servlet 就支持处理这种上传文件的请求&#xff0c;把这个请求到的文…

安科瑞智能操控无线测温装置在江苏某化工产业园项目的应用

安科瑞 李亚俊 1 概述 江苏富强新材料有限公司是中国企业500强——山东金岭集团在江苏淮安盐化新材料产业园区投资设立的盐化工企业。公司将利用淮安丰富的盐矿资源和优越的发展环境&#xff0c;投资200亿元&#xff0c;建设120万吨/年离子膜烧碱项目、70万吨/年甲烷氯化物项…

vue新春游戏-拼手速抢车票小游戏,学习玩乐两不误,春节小游戏,新年小游戏

ue新春游戏-拼手速抢车票&#xff0c;老规矩&#xff0c;体验地址&#xff1a;http://game.pkec.net/word-ticket/。 写这个主要是前几天群里运营老师说咋没人写抢车票的&#xff0c;再加上我上一篇文章上了掘金一周&#xff0c;听说多上几次有证书&#xff0c;我还没搞到过掘金…

Go语言 函数传递:值传递 和 虚假的 “引用传递”

前言 其实从变量本身来说&#xff0c;go只有值传递&#xff0c;函数内的修改不会影响函数外。但有一种特例是指针&#xff0c;go可以传指针给函数&#xff0c;指针指向申请出来的实际内存&#xff0c;也就是保存元素的内存&#xff0c; 这样在函数内的修改&#xff0c;可以影响…

就算是TOP程序员,也有这些坏习惯

绝大多数程序员在职业生涯中&#xff0c;多多少少都会养成一些坏习惯&#xff0c;今天就来说一说身边最常见的一些坏习惯&#xff0c;也给刚入行的新朋友们提个醒&#xff0c;少走一些弯路。 那么&#xff0c;就让我们开始吧&#xff01; 1.不注意适当休息 比如日常工作时、…

B端产品-登录功能设计

在项目启动初期&#xff0c;基本大家都是先从账号体系先开始着手设计的&#xff0c;那么B端的登录功能如何设计呢&#xff1f; 一、需求分析 B端的产品的用户基本上可以分为内部员工和客户&#xff0c;如果是客户使用&#xff0c;基本上都会有注册功能&#xff1b;如果是内部员…

C# Console.Read读取回车和换行

C#的Console.Read函数&#xff1a; 读取缓冲区中的数据&#xff0c;读取到一个字符时停止。 C#的Console.ReadLine函数 读取缓冲区中的数据&#xff0c;遇到回车时停止。 Enter键&#xff1a; 将行数据输入缓冲区&#xff0c;并且将回车符和换行符 加入缓冲区。 注意 Consol…

【H5UI库和二维码】一.H5UI库;二.加密技术;三.二维码

目录​​​​​​​ 一.H5UI库 1.使用方法&#xff1a; &#xff08;1&#xff09;页面中引入css文件 &#xff08;2&#xff09;页面中引入js文件 2.组件的用法 &#xff08;2&#xff09;按钮的使用&#xff1a;button。有三种类型&#xff08;primary、danger、defaul…

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《3》

前面了解到Faster R-CNN在实验中的效果很不错&#xff0c;以及对论文做了一个大概的了解&#xff0c;对此有兴趣的伙伴们也可以先浏览前面两篇文章&#xff1a;MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《1》MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《…

【Node】Node.js安装与配置(详细步骤)

Node.js安装与配置&#xff08;详细步骤&#xff09;一、安装Node.js1.1 下载1.2 安装1.3 环境变量二、验证是否安装成功三、修改模块下载位置3.1 查看npm默认存放位置3.2 在 nodejs 安装目录下&#xff0c;创建 “node_global” 和 “node_cache” 两个文件夹3.3 修改默认文件…

xd卡数据丢失原因和三种数据恢复方法介绍

xd卡适用于富士/奥林巴斯相机品牌&#xff0c;它能配合各式读卡器&#xff0c;方便的与个人电脑连接传输图像视频等。但是xd卡与其他类型的存储卡一样容易因各种原因出现数据丢失。如果您的xd卡出现了数据丢失的问题&#xff0c;不妨了解下这里给大家分析的xd卡数据丢失原因和具…

解决仓库产品管理痛点,选对条码工具至关重要

" 最近我们仓库管理遇到了很多问题&#xff0c;一方面 我们进出库的产品数量非常庞大&#xff0c;目前公司的条码扫描系统识别效率非常低&#xff0c;只能单个产品进行扫描&#xff0c;经常需要加班加点出入库&#xff0c;而且有些产品条码比较特殊&#xff0c;现在的扫描…

【LeetCode每日一题】——507.完美数

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 数学 二【题目难度】 简单 三【题目编号】 507.完美数 四【题目描述】 对于一个 正整数&…

windows10安装goland

一、安装包 下载以下两个安装包&#xff1a; go语言二进制包&#xff1a; https://golang.google.cn/dl/ goland安装包 https://www.jetbrains.com/go/nextversion/ 二、安装go语言包 双击下载的语言包&#xff0c;直接next&#xff0c;选择安装路径&#xff08;自己选择&am…

图的遍历(深度DFS与广度BFS)

文章目录图的遍历深度优先遍历思路邻接表邻接矩阵性能分析广度优先遍历思路邻接表邻接矩阵性能分析源代码图的遍历 **对有向图和无向图进行遍历是按照某种次序系统地访问图中的所有顶点, 并且使得每一个顶点只能访问一次. ** 对于图的遍历需要解决掉两个问题: 如果存在回路/环…

实力总结四类Bean注入Spring的方式

xml 方式 注解方式 Configuration Bean Import FactoryBean BDRegistryPostProcessor 源码 实战 一提到Spring&#xff0c;大家最先想到的是啥&#xff1f;是AOP和IOC的两大特性&#xff1f;是Spring中Bean的初始化流程&#xff1f;还是基于Spring的Spring Cloud全家桶呢…

Vue组件之间的通信

1、组件&#xff1a;是vue的重要的特征之一&#xff0c;可以扩展html的功能&#xff0c;也可以封装代码实现重复使用 2、组件的创建 &#xff08;1&#xff09;非脚手架方式创建&#xff1a; 1️⃣使用vue.extend创建组件 2️⃣使用vue.component注册组件 3️⃣在html页面…

一个平凡打工人在 CSDN 的 2022 与 2023

平凡又不平凡的一年 2022 年是不平凡的一年&#xff0c;这一年经历了疫情的起起伏伏&#xff0c;随着身边好多同学的毕业离开&#xff0c;手头的工作也愈发的繁重&#xff0c;2022 年也顺理成章的成为了工作3年来最忙碌的一年&#xff0c;但却也是博客产出与自己收获最多的一年…

大数据hadoop和spark怎么选择?

Hadoop框架的主要模块包括如下&#xff1a; Hadoop Common Hadoop分布式文件系统(HDFS) Hadoop YARN Hadoop MapReduce 虽然上述四个模块构成了Hadoop的核心&#xff0c;不过还有其他几个模块。这些模块包括&#xff1a;Ambari、Avro、Cassandra、Hive、 Pig、Oozie、Flume…