数据结构--二叉搜索树的实现

news2025/1/11 11:02:30

目录

1.二叉搜索树的概念

2.二叉搜索树的操作

二叉搜索树的插入

中序遍历(常用于排序)

二叉搜索树的查找

二叉搜索树的删除

完整二叉树代码:

二叉搜索树的应用

key/value搜索模型整体代码


1.二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树 ,或者是具有以下性质的二叉树 :
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别 为二叉搜索树
注:搜索二叉树中没有重复值
二叉搜索树与其结点的代码实现
#include<iostream>
using namespace std;

template<class K>//搜索二叉树的结点
struct BSTreeNode
{
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;

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

template<class K>//搜索二叉树
class BSTree
{
public:
    
    BSTree()
        :_root(nullptr)
    {}

	//...各种操作二叉搜索树方法的实现
	//...
private:
	typedef BSTreeNode<K> Node;
	Node* _root;
};

2.二叉搜索树的操作

二叉搜索树的插入

这里根据二叉搜索树的概念分两种情况:

a. 树为空,则直接新增节点,赋值给 root 指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

非递归

bool Insert(const K & key)
	{
		if (_root == nullptr)//情况a
		{
			_root = new Node(key);
		}
		else//情况b
		{
			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  //数字重复插入失败
				{
					return false;
				}
			}
			cur = new Node(key);
			if (parent->_key > key)//判断插入结点是在parent的左子树还是右子树
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}
	}

为什么要定义parent变量记录cur的父节点?

这里我们要知道,cur=new Node(key)这行代码的真正意义是给cur赋值,并没有把结点插入到树中。

注:在向二叉搜索树插入时,一定要判断是在父节点的左子树还是右子树。

递归:

public: 
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;
	}
	else
	{
		if (root->_key > key)
		{
			return _insertR(root->_left, key);
		}
		else if (root->_key < key)
		{
			return _insertR(root->_right, key);
		}
		else
		{
			return false;
		}
	}
}

注:由于使用递归时,需要用到成员变量_root作为实参,但是在类外面无法直接调用,因此,将递归调用的函数封装到了InsertR()里面。

为什么这里不用记录父节点,就可以插入到树中。

这里我们要注意函数的第一个变量,我们使用了引用!

这里的root就是父节点的左孩子或有孩子。

那么在非递归里面可以使用引用来达到不设置parent变量来记录父节点吗?
不能,因为在C++里引用只能指向一个。之后就不能改变指向。在递归中,我们是在传参是使用的,每次引用都重新开辟了一个新的变量,而非递归中我们一直用的是一个变量。

中序遍历(常用于排序)

public:
void Inorder()
{
	_Inorder(_root);
}

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

这里根据二叉搜索树的概念我们清楚,其中序遍历相当于将树里面的数据按从小到大排序输出。

二叉搜索树的查找

查找方法:

a 、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b 、最多查找高度次,走到到空,还没找到,这个值不存在。

注意:

为完全二叉树时间复杂度最好,为O(log n)   

树的结点全部为左孩子或右孩子时,时间复杂度最坏,为O(n)

非递归

bool Find(const K& key)
{
	if (_root == nullptr)//树为空
		return false;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)
		{
			cur = cur->left;// _key>key.左走
		}
		else if (cur->_key < key)
		{
			cur = cur->_right;//_key<key.右走
		}
		else
		{
			return true;//相等,找到
		}
	}
	return false;//没有一个相等
}

递归:

public:
Node* FindR(const K& key)
{
	return _FindR(_root, key);
}

private:
Node* _FindR(Node* root, const K& key)
{
	if (root == nullptr)
		return nullptr;
	if (root->_key > key)
	{
		_FindR(root->_left, key);
	}
	else if (root->_key < key)
	{
		_FindR(root->_right, key);
	}
	else
	{
		return root;
	}
}

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回 , 否则要删除的结点可能分下面四种情
况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有 4 中情况,实际情况 a 可以与情况 b 或者 c 合并起来,因此真正的删除过程
如下:
情况 b :删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点 -- 直接删除
情况 c :删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点 -- 直接删除
情况 d :在它的右子树中寻找中序下的第一个结点 ( 关键码最小 ) ,用它的值填补到被删除节点
中,再来处理该结点的删除问题 -- 替换法删除
bool Erase(const K& key)
{
	if (_root == nullptr)
		return false;
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = nullptr;
			cur = cur->_right;
		}
		else
		{
			if (cur->_left == nullptr)//情况a
			{
				if (cur == _root)//特殊条件,等于根节点
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->right == nullptr)//情况b
			{
				if (cur == _root) //特殊条件,等于根节点
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else            //情况c
			{
				Node* parent = cur;
				Node* subnode = cur->_right;
				while (subnode->_left)
				{
					parent = subnode;
					subnode = subnode->_left;
				}
				swap(cur->_key, subnode->_key);
				if (parent->_right == subnode)   //当cur->_right->left==nullptr
				{
					parent->_right = subnode->_right;
				}
				else
				{
					parent->_left = subnode->_right;
				}
				delete subnode;
			}
			return true;
		}
	}
	return false;
}

递归:

public:

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

private:

bool _EraseR(Node*& root, const K& key)
{
	if (root == nullptr)
	{
		return false;
	}
	if (root->_key > key)
	{
		return _EraseR(root->_left, key);
	}
	else if (root->_key < key)
	{
		return _EraseR(root->_right, key);
	}
	else
	{
		if (root->_left == nullptr)
		{
			Node* temp = root;
			root = root->_right;
			delete temp;
		}
		else if (root->right)
		{
			Node* temp = root;
			root = root->_left;
			delete temp;
		}
		else
		{
			Node* subnode = root->_right;
			while (subnode->left)
			{
				subnode = subnode->_left;
			}
			swap(root->_key, subnode->_key);
			return _EraseR(root - right, key);
		}
	}
}

完整二叉树代码:

#include<iostream>
using namespace std;

template<class K>//搜索二叉树的结点
struct BSTreeNode
{
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;

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

template<class K>//搜索二叉树
class BSTree
{
public:

	BSTree()
		:_root(nullptr)
	{}

	 bool Insert(const K & key)
	{
		if (_root == nullptr)//情况a
		{
			_root = new Node(key);
		}
		else//情况b
		{
			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  //数字重复插入失败
				{
					return false;
				}
			}
			cur = new Node(key);
			if (parent->_key > key)//判断插入结点是在parent的左子树还是右子树
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}
	}

	 bool Find(const K& key)
	 {
		 if (_root == nullptr)//树为空
			 return false;
		 Node* cur = _root;
		 while (cur)
		 {
			 if (cur->_key > key)
			 {
				 cur = cur->left;// _key>key.左走
			 }
			 else if (cur->_key < key)
			 {
				 cur = cur->_right;//_key<key.右走
			 }
			 else
			 {
				 return true;//相等,找到
			 }
		 }
		 return false;//没有一个相等
	 }

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


	 void Inorder()
	 {
		 _Inorder(_root);
	 }

	 bool InsertR(const K& key)
	 {
		 return _insertR(_root, key);
		 cout << endl;
	 }


	 bool Erase(const K& key)
	 {
		 if (_root == nullptr)
			 return false;
		 Node* cur = _root;
		 Node* parent = nullptr;
		 while (cur)
		 {
			 if (cur->_key > key)
			 {
				 parent = cur;
				 cur = cur->_left;
			 }
			 else if(cur->_key<key)
			 {
				 parent = nullptr;
				 cur = cur->_right;
			 }
			 else
			 {
				 if (cur->_left == nullptr)
				 {
					 if (cur == _root)
					 {
						 _root = cur->_right;
					 }
					 else
					 {
						 if (parent->_left == cur)
						 {
							 parent->_left = cur->_right;
						 }
						 else
						 {
							 parent->_right = cur->_right;
						 }
					 }
					 delete cur;
				 }
				 else if(cur->right==nullptr)
				 {
					 if (cur == _root)
					 {
						 _root = cur->_left;
					 }
					 else
					 {
						 if (parent->_left == cur)
						 {
							 parent->_left = cur->_left;
						 }
						 else
						 {
							 parent->_right = cur->_left;
						 }
					 }
					 delete cur;
				 }
				 else
				 {
					 Node* parent = cur;
					 Node* subnode = cur->_right;
					 while (subnode->_left)
					 {
						 parent = subnode;
						 subnode = subnode->_left;
					 }
					 swap(cur->_key, subnode->_key);
					 if (parent->_right == subnode)
					 {
						 parent->_right = subnode->_right;
					 }
					 else
					 {
						 parent->_left = subnode->_right;
					 }
					 delete subnode;
				 }
				 return true;
			 }
		 }
		 return false;
	 }


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

private:
	typedef BSTreeNode<K> Node;
	Node* _root;

	bool _insertR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		else
		{
			if (root->_key > key)
			{
				return _insertR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _insertR(root->_right, key);
			}
			else
			{
				return false;
			}
		}
	}

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

	Node*_FindR(Node* root, const K& key)
	{
		if (root == nullptr)
			return nullptr;
		if (root->_key > key)
		{
			_FindR(root->_left, key);
		}
		else if (root->_key < key)
		{
			_FindR(root->_right, key);
		}
		else
		{
			return root;
		}
	}

	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}
		else if (root->_key < key)
		{
			return _EraseR(root->_right, key);
		}
		else
		{
			if (root->_left == nullptr)
			{
				Node* temp = root;
				root = root->_right;
				delete temp;
			}
			else if (root->right)
			{
				Node* temp = root;
				root = root->_left;
				delete temp;
			}
			else
			{
				Node* subnode = root->_right;
				while (subnode->left)
				{
					subnode = subnode->_left;
				}
				swap(root->_key, subnode->_key);
				return _EraseR(root - right, key);
			}
		}
	}
};
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二叉搜索树的应用

1. K 模型: K 模型即只有 key 作为关键码,结构中只需要存储 Key 即可,关键码即为需要搜索到
的值
比如: 给一个单词 word ,判断该单词是否拼写正确 ,具体方式如下:
以词库中所有单词集合中的每个单词作为 key ,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
2. KV 模型:每一个关键码 key ,都有与之对应的值 Value ,即 <Key, Value> 的键值对 。该方式在现实生活中非常常见:
比如 英汉词典就是英文与中文的对应关系 ,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文 <word, chinese> 就构成一种键值对;
再比如 统计单词次数 ,统计成功后,给定单词就可快速找到其出现的次数, 单词与其出
现次数就是 <word, count> 就构成一种键值对

key/value搜索模型整体代码

#pragma once
// 改造二叉搜索树为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;
public:

	bool Insert(const K& key,const V& value)
	{
		if (_root == nullptr)//情况a
		{
			_root = new Node(key,value);
		}
		else//情况b
		{
			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  //数字重复插入失败
				{
					return false;
				}
			}
			cur = new Node(key,value);
			if (parent->_key > key)//判断插入结点是在parent的左子树还是右子树
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}
	}

	bool Find(const K& key)
	{
		if (_root == nullptr)//树为空
			return false;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->left;// _key>key.左走
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;//_key<key.右走
			}
			else
			{
				return true;//相等,找到
			}
		}
		return false;//没有一个相等
	}

	bool Erase(const K& key)
	{
		if (_root == nullptr)
			return false;
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = nullptr;
				cur = cur->_right;
			}
			else
			{
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else
				{
					Node* parent = cur;
					Node* subnode = cur->_right;
					while (subnode->_left)
					{
						parent = subnode;
						subnode = subnode->_left;
					}
					swap(cur->_key, subnode->_key);
					if (parent->_right == subnode)
					{
						parent->_right = subnode->_right;
					}
					else
					{
						parent->_left = subnode->_right;
					}
					delete subnode;
				}
				return true;
			}
		}
		return false;
	}
	
	void Inorder()
	{
		_Inorder(_root);
	}



private:
	Node* _root;

	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_key << ":"<<root->_value<<endl;
		_Inorder(root->_right);
	}
};
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

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

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

相关文章

【第十三课】Trie字符串统计(acwing-835 / 二维数组的含义 / c++代码)

思想 Trie树在我们之前学习树的时候简单提过一嘴。 Trie树也称为前缀树或字典树&#xff0c;是一种用于高效存储和查找字符串的数据结构。Trie树的主要思想是利用字符串之间的公共前缀来节省存储空间&#xff0c;提高查询效率。 节点表示&#xff1a;Trie树中的每个节点代表一…

[Angular] 笔记 16:模板驱动表单 - 选择框与选项

油管视频&#xff1a; Select & Option (Template Driven Forms) Select & Option 在 pokemon.ts 中新增 interface: export interface Pokemon {id: number;name: string;type: string;isCool: boolean;isStylish: boolean;acceptTerms: boolean; }// new interface…

JavaScript(简写js)常用事件举例演示

目录 1.窗口事件onblur :失去焦点onfocus:获得焦点onload:窗口加载事件onresize:窗口大小缩放事件 二、表单事件oninput &#xff1a;当文本框内容改变时 &#xff0c;立即将改变内容 输出在控制台onchange&#xff1a; 内容改变事件onclick&#xff1a;鼠标单击时触发此事件 三…

OR-NeRF论文笔记

OR-NeRF论文笔记 文章目录 OR-NeRF论文笔记论文概述Abstract1 Introduction2 Related Work3 Background4 Method4.1 Multiview Segmentation4.2 Scene Object Removal 5 ExperimentsDatasetsMetricsMultiview SegmentationScene Object Removal 6 Conclusion 论文概述 目的&am…

resnet18

ResNet18的基本含义是&#xff0c;网络的基本架构是ResNet&#xff0c;网络的深度是18层。但是这里的网络深度指的是网络的权重层&#xff0c;也就是包括池化&#xff0c;激活&#xff0c;线性层。而不包括批量化归一层&#xff0c;池化层。 transforms.RandomCrop(32, pa…

悔不该用中文作为Windows的用户名啊~

前言 汉字在中华文明已经有了几千年的历史&#xff0c;小伙伴们所使用名字更是伴随了自己一生。所以小白们在拿到自己的新电脑&#xff0c;总会想着把自己的中文名字设置为电脑的用户名&#xff0c;这样更能显示出那是自己的专属电脑&#xff01; 一开始小白也是这么想的&…

Unity中Shader裁剪空间推导(在Shader中使用)

文章目录 前言一、在Shader中使用转化矩阵1、在顶点着色器中定义转化矩阵2、用 UNITY_NEAR_CLIP_VALUE 区分平台矩阵3、定义一个枚举用于区分当前是处于什么相机 二、我们在DirectX平台下&#xff0c;看看效果1、正交相机下2、透视相机下3、最终代码 前言 在上一篇文章中&…

迁移Ubuntu报错问题

问题描述&#xff1a; 使用LxRunOffline-v3.5.0-mingw迁移Ubuntu至非系统盘时&#xff0c;出现如下报错 ‘Couldn’t set the case sensitive attribute of the directory “\?\C:\Users\xxx\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\Loc…

基于策略模式和简单工厂模式实现zip、tar、rar、7z四种压缩文件格式的解压

推荐语 这篇技术文章深入探讨了基于策略模式和简单工厂模式实现四种常见压缩文件格式的解压方法。通过阅读该文章&#xff0c;你将了解到如何利用这两种设计模式来实现灵活、可扩展的解压功能&#xff0c;同时适应不同的压缩文件格式。如果你对设计模式和文件处理感兴趣或刚好…

【ES】es介绍,使用spring-boot-starter-data-elasticsearch整合的ES来进行操作Es

文章目录 倒排索引&#xff08;Inverted Index&#xff09;和正排索引&#xff08;Forward Index&#xff09;es和MySQL对比IK分词器的总结mapping映射使用springboot整合的ES来进行操作Es1. 实体类中添加注解2. 编写Repository层3. 通过Repository进行增删改查 倒排索引&#…

学习Vue的key作用和原理

今天主要学习了列表渲染和key的作用和原理&#xff0c;先来说说列表渲染&#xff0c;顾名思义想要渲染列表最快的方式就是使用for循环&#xff0c;我们要学习的就是Vue中对标签实现for循环的语法&#xff0c;它和我们传统的js语法有些不同&#xff0c;它是先要有Vue实例中data的…

力扣热题100道-双指针篇

文章目录 双指针283.移动零11.盛最多水的容器15.三数之和42.接雨水 双指针 283.移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 …

C语言 volatile关键字

volatile关键字介绍 volatile 是一个关键字&#xff0c;用于修饰变量&#xff0c;表示该变量是易变的&#xff0c;即可能在任何时候被意外地改变。在多线程编程中&#xff0c;当多个线程同时访问同一个变量时&#xff0c;由于线程之间的交互和优化&#xff0c;可能会导致变量的…

亚信安慧AntDB数据库——通信运营商核心系统的全面演进

AntDB数据库源自通信运营商核心系统&#xff0c;经过15年的平稳运行和不断演进&#xff0c;成功跟随通信技术的升级步伐&#xff0c;逐步迈向5G时代&#xff0c;并且在这期间完成了8次大版本的迭代&#xff0c;为行业树立了技术领先的典范。其独特之处在于具备超融合架构&#…

vue本地缓存搜索记录(最多4条)

核心代码 //保存到搜索历史&#xff0c;最多存四个 item.name和item.code格式为&#xff1a;塞力斯000001var history uni.getStorageSync(history) || [];console.log("history", history)var index history.findIndex((items) > {return item.name items.nam…

通过nginx配置防御web漏洞

一、常见web漏洞 二、nginx防御策略&#xff1a; 要使用Nginx配置防御Web漏洞&#xff0c;可以采取以下措施&#xff1a; 禁用不必要的HTTP方法&#xff1a;Nginx默认启用了许多HTTP方法&#xff0c;如PUT、DELETE等。可以通过在Nginx配置中禁用不必要的HTTP方法来减少潜在的安…

Vue - 使用Element UI Upload / importExcelJs进行文件导入

1 情景一 需求背景&#xff1a;后端配合&#xff0c;点击"导入"按钮&#xff0c;弹出“导入”弹窗&#xff0c;将电脑本地Excel表格数据导入到页面中表格位置&#xff08;需要调用后端接口&#xff09;&#xff0c;而页面中表格通过后端接口获取最新数据。 实现思路…

学python一定要下载python吗,学python都需要什么软件

大家好&#xff0c;本文将围绕想学python都要下载什么软件展开说明&#xff0c;初学python需要安装什么软件是一个很多人都想弄明白的事情&#xff0c;想搞清楚学python一定要下载python吗需要先了解以下几个事情。 工欲善其事必先利其器。初学者在学Python的时候&#xff0c;往…

雷军称小米汽车不可能卖 9 万 9;杭州破获重大勒索病毒案丨 RTE 开发者日报 Vol.116

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

thinkphp+vue_mysql汽车租赁管理系统1ma2x

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp5 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 课题主要分为三大模块&#xff1a;即管理员模块、用户模块…