【数据结构1-2】二叉树

树形结构不仅能表示数据间的指向关系,还能表示出数据的层次关系,而有很明显的递归性质。因此,我们可以利用树的性质解决更多种类的问题。

但是在平常的使用中,我们并不需要使用这么复杂的结构,只需要建立一个包含int right和int left的结构体即可,left和right用于指示该节点的左儿子和右儿子。


一、【P4913】二叉树深度(递归/层次遍历)

本题的重点在于二叉树的存储和二叉树的层次遍历。

1. 二叉树存储:考虑一个二叉树的每个节点都有两个子节点,所以我们可以考虑用一个结构体来存储。其中,leftright分别代表节点的左节点编号和右节点编号。

struct node {
    int left, right;
};
node tree[MAXN];

2. 二叉树层次遍历:我们可以从根节点出发,先递归遍历该节点的左节点,再递归遍历该节点的右节点。对于每个父亲节点,先比较左右子树的高度,然后选取较高的子树,将其深度加1,每次遍历更新子树的高度。当遍历到叶子节点后返回0(叶子节点的左右子树高度均为0)。

int maxDepth(int id)
{
	if (id == 0) return 0;
	return 1 + max(maxDepth(Tree[id].left), maxDepth(Tree[id].right));
}

AC代码:

#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>

using namespace std;

struct node
{
	int left;
	int right;
}Tree[1000005];

int maxDepth(int id)
{
	if (id == 0) return 0;
	return 1 + max(maxDepth(Tree[id].left), maxDepth(Tree[id].right));
}

int main()
{
	int n;	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int a, b;	cin >> a >> b;
		Tree[i].left = a;
		Tree[i].right = b;
	}
	cout << maxDepth(1);

}

二、【P1827】美国血统(前中后序遍历)

前序遍历、中序遍历和后序遍历是三种利用深度优先搜索遍历二叉树的方式。它们是在对节点访问的顺序有一点不同,其它完全相同。

需要注意的是下图中伪代码并未给出base case,即递归返回的条件。

AC代码:

先序遍历的特点是优先遍历根节点,所以整棵树的根应该会在inorder字符串的最开始出现;中序遍历的特点是优先遍历左子树,然后遍历根节点,所以我们可以根据中序遍历序列判断左右子树中包含哪些点。

以本题为例:

中序(inorder)遍历:ABEDFCHG;先序(preorder)遍历:CBADEFGH。

  1. 首先可以根据preorder判断C为根节点,然后依据inorder判断ABEDF为左子树中的节点,HG为右子树中的节点。
  2. 回到preorder序列,第二个节点是B,所以左子树的根节点为B,依据inorder序列可知A构成次级左子树,DEF构成次级右子树。
  3. 回到preorder序列,第三个节点是A,刚好是以B为根的子树的左子树的根;第四个节点是D,所以以B为根的子树的右子树的根为D。
  4. 以此类推,可以还原整棵树的情况。
#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <stack>
#include <set>

using namespace std;

struct TreeNode //建树
{
	char val;
	TreeNode* left;
	TreeNode* right;
	TreeNode() :val(0), left(nullptr), right(nullptr) {}
	TreeNode(char x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(char x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};

//对preorder中的[s0,e0]部分操作,s1用于跟踪preorder序列中某个父亲节点
TreeNode* buildTree(string& preorder, map<char, int>& mp, int s0, int e0, int s1)
{
	if (s0 > e0) return nullptr;
	char mid = preorder[s1];
	int index = mp[mid]; //找到当前父亲节点在中序遍历inorder中的位置
	int left_length = index - s0; //左子树大小
	TreeNode* node = new TreeNode(mid);
	node->left = buildTree(preorder, mp, s0, index - 1, s1 + 1);
	node->right = buildTree(preorder, mp, index + 1, e0, s1 + 1 + left_length);
	return node;
}

void PostOrder(TreeNode* tree)
{
	if (!tree) return;
	PostOrder(tree->left);
	PostOrder(tree->right);
	cout << tree->val;
}

int main()
{
	string preorder, inorder;
	cin >> inorder >> preorder;
	map<char, int> mp;
	//使用map存储,便于搜索
	for (int i = 0; i < inorder.length(); i++)
		mp[inorder[i]] = i;
	TreeNode* res = buildTree(preorder, mp, 0, inorder.length() - 1, 0);
	PostOrder(res);
}

 由于需要通过preorder序列的节点,查询该点在inorder序列的位置,所以将inorder序列用map存储,实现O(1)量级的查询。

使用buildTree函数递归还原整棵树,然后使用postorder函数对树进行后序遍历,得到答案。


三、【P1229】遍历问题(前中后序遍历)

思路:给定inorder能确定树的具体结构,是因为可以确定子树是左子树还是右子树;而只给定preorder和postorder,则不能确定,这就是会出现不同树结构的原因。

只有前和后,那么主要问题就是没有办法处理只有一个子树的情况,因为这种情况下不知道子树究竟是这个节点的左子树还是右子树,也就是说其实这道题要判断遍历中存在着多少个只有一棵子树的情况。对于前,如果一个结点的下个结点等于后中对应结点的前一个结点的话,那么这个结点就是根节点且其只有一个子树。sum初始化为1,出现一个只有一棵子树的情况,就把sum*2(每次会出现两种不同的情况,分别是出现左子树和出现右子树)。

AC代码:

#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <cstring>
#include <queue>

using namespace std;
const int INF = 0x7fffffff;

int main()
{
	string preorder, postorder;
	cin >> preorder >> postorder;
	int sum = 1;
	for (int i = 0; i <= preorder.length() - 2; i++)
	{
		for (int j = 0; j <= postorder.length() - 1; j++)
		{
			if (preorder[i] == postorder[j] && preorder[i + 1] == postorder[j - 1])
			{
				sum *= 2;
			}
		}
	}
	cout << sum << endl;
}

四、【P1030】求先序序列(前中后序遍历)

首先,一点基本常识,给你一个后序遍历,那么最后一个就是根(如ABCD,则根为D),因为题目求先序,意味着要不断找根。

那么我们来看这道题方法:(示例)

中序ACGDBHZKX,后序CDGAHXKZB,首先可找到主根B;

那么我们找到中序遍历中的B,由这种遍历的性质,可将中序遍历分为ACGD和HZKX两棵子树,那么对应可找到后序遍历CDGA和HXKZ(从头找即可);从而问题就变成求:

  1. 中序遍历ACGD,后序遍历CDGA的树;
  2. 中序遍历HZKX,后序遍历HXKZ的树;

接着递归,按照原先方法,找到

1.子根A,再分为两棵子树;

2.子根Z,再分为两棵子树。

就按这样一直做下去(先输出根,再递归)。

模板概括为:

  • step1:找到根并输出
  • step2:将中序,后序各分为左右两棵子树;
  • step3:递归,重复step1,2;

AC代码:

#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <cstring>
#include <queue>

using namespace std;
const int INF = 0x7fffffff;

string inorder, postorder;

void preorder(string in,string after)
{
	if (in.size() > 0)
	{
		char ch = after[after.size() - 1];
		cout << ch;
		int k = in.find(ch);
		preorder(in.substr(0, k), after.substr(0, k)); //左子树
		preorder(in.substr(k + 1), after.substr(k, in.size() - k - 1));//从k开始,截取这么多个数
	}
}


int main()
{
	cin >> inorder >> postorder;
	preorder(inorder, postorder);
}

将2,3,4三题进行比较,2的解题思路具有普适性,但是建树的过程相对复杂;4的解题思路较为清晰,但是缺点在于只能输出序列,并不能得到完整的树。


五、【P5076】简化二叉树(二叉搜索树)

参考:https://www.cnblogs.com/do-while-true/p/13566274.html 

但值得强调的是,二叉搜索树是一种低级的平衡二叉树,因为在最差情况下,可以会呈现链状结构,导致时间复杂度为O(n),而不是所需的O(logN)。因此,只需要了解即可,重点需要掌握的是平衡二叉树。

splay搜索树的几种操作:

(1)插入:x 是当前节点的下标,v 是要插入的值。要在树上插入一个 v 的值,就要找到一个合适 v 的位置,如果本身树的节点内有代表 v 的值的节点,就把该节点的计数器加 11 ,否则一直向下寻找,直到找到叶子节点,这个时候就可以从这个叶子节点连出一个儿子,代表 v 的节点。具体向下寻找该走左儿子还是右儿子是根据二叉搜索树的性质来的。

void add(int x,int v)
{
	tree[x].siz++;
	//如果查到这个节点,说明这个节点的子树里面肯定是有v的,所以siz++
	if(tree[x].val==v){
		//如果恰好有重复的数,就把cnt++,退出即可,因为我们要满足第四条性质
		tree[x].cnt++;
		return ;
	}
	if(tree[x].val>v){//如果v<tree[x].val,说明v实在x的左子树里
		if(tree[x].ls!=0)
		  add(tree[x].ls,v);//如果x有左子树,就去x的左子树
		else{//如果不是,v就是x的左子树的权值
			cont++;//cont是目前BST一共有几个节点
			tree[cont].val=v;
			tree[cont].siz=tree[cont].cnt=1;
			tree[x].ls=cont;
		}
	}
	else{//右子树同理
		if(tree[x].rs!=0)
		  add(tree[x].rs,v);
		else{
			cont++;
			tree[cont].val=v;
			tree[cont].siz=tree[cont].cnt=1;
			tree[x].rs=cont;
		}
	}
}

 (2)找前驱:x 是当前的节点的下标,val 是要找前驱的值,ans 是目前找到的比 val 小的数的最大值。找前驱的方法也是不断的在树上向下爬找具体节点,具体爬的方法可以参考代码注释部分。

int queryfr(int x, int val, int ans) {
	if (tree[x].val>=val)
	{//如果当前值大于val,就说明查的数大了,所以要往左子树找
		if (tree[x].ls==0)//如果没有左子树就直接返回找到的ans
			return ans;
		else//如果不是的话,去查左子树
			return queryfr(tree[x].ls,val,ans);
	}
	else
	{//如果当前值小于val,就说明我们找比val小的了
		if (tree[x].rs==0)//如果没有右孩子,就返回tree[x].val,因为走到这一步时,我们后找到的一定比先找到的大(参考第二条性质)
			return (tree[x].val<val) ? tree[x].val : ans
		//如果有右孩子,,我们还要找这个节点的右子树,因为万一右子树有比当前节点还大并且小于要找的val的话,ans需要更新
		if (tree[x].cnt!=0)//如果当前节数的个数不为0,ans就可以更新为tree[x].val
			return queryfr(tree[x].rs,val,tree[x].val);
		else//反之ans不需要更新
			return queryfr(tree[x].rs,val,ans);
	}
}

(3)找后继:与找前驱同理,只不过需要反过来。

int queryne(int x, int val, int ans) {
	if (tree[x].val<=val)
	{
		if (tree[x].rs==0)
			return ans;
		else
			return queryne(tree[x].rs,val,ans);
	}
	else
	{
		if (tree[x].ls==0)
			return (tree[x].val>val)? tree[x].val : ans;
		if (tree[x].cnt!=0)
			return queryne(tree[x].ls,val,tree[x].val);
		else
			return queryne(tree[x].ls,val,ans);
	}
}

(4)按值找排名:这里我们就要用到 siz 了,排名就是比这个值要小的数的个数再 +1,所以我们按值找排名,就可以看做找比这个值小的数的个数,最后加上 1 即可。

int queryval(int x,int val)
{
	if(x==0) return 0;//没有排名 
	if(val==tree[x].val) return tree[tree[x].ls].siz;
	//如果当前节点值=val,则我们加上现在比val小的数的个数,也就是它左子树的大小 
	if(val<tree[x].val) return queryval(tree[x].ls,val);
	//如果当前节点值比val大了,我们就去它的左子树找val,因为左子树的节点值一定是小的 
	return queryval(tree[x].rs,val)+tree[tree[x].ls].siz+tree[x].cnt;
	//如果当前节点值比val小了,我们就去它的右子树找val,同时加上左子树的大小和这个节点的值出现次数 
	//因为这个节点的值小于val,这个节点的左子树的各个节点的值一定也小于val 
}
//注:这里最终返回的是排名-1,也就是比val小的数的个数,在输出的时候记得+1

(5)按排名找值:排名为 n 的数在BST上是第 n 靠左的数。或者说排名为 n 的数的节点在BST中,它的左子树的 siz 与它的各个祖先的左子树的 siz 相加恰好 =n (这里相加是要减去重复部分)。

int queryrk(int x,int rk)
{
	if(x==0) return INF; 
	if(tree[tree[x].ls].siz>=rk)//如果左子树大小>=rk了,就说明答案在左子树里 
		return queryrk(tree[x].ls,rk);//查左子树 
	if(tree[tree[x].ls].siz+tree[x].cnt>=rk)//如果左子树大小加上当前的数的多少恰好>=k,说明我们找到答案了 
		return tree[x].val;//直接返回权值 
	return queryrk(tree[x].rs,rk-tree[tree[x].ls].siz-tree[x].cnt);
	//否则就查右子树,同时减去当前节点的次数与左子树的大小 
}

AC代码:

#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <stack>
#include <set>

using namespace std;
const int INF = 0x7fffffff;

struct node
{
	int val; //节点值
	int left, right; //左右子树
	int cnt; //计数器
	int siz; //子树大小
}tree[100005];
int cont = 0;//记录节点数量

//初始化
void init()
{
	for (int i = 0; i < 100005; i++)
	{
		tree[i].val = 0;
		tree[i].left = 0;
		tree[i].right = 0;
		tree[i].cnt = 0;
		tree[i].siz = 0;
	}
}

//添加新点
void add(int x, int value) //当前节点为x,需要插入value
{
	tree[x].siz++; //肯定在x子树上,规模加一
	if (tree[x].val == value) //如果value刚好等于节点值,计数器加一即可
	{
		tree[x].cnt++;
		return;
	}
	if (tree[x].val > value) //如果x的值大于value,value只能放在x的左子树上
	{
		if (tree[x].left != 0)//如果左子树上有节点,递归查找
			add(tree[x].left, value);
		else//如果左子树上没有点,直接添加
		{
			cont++;
			tree[cont].val = value;
			tree[cont].siz = tree[cont].cnt = 1;
			tree[x].left = cont;
		}
	}
	else if (tree[x].val < value)//如果x的值小于value,value要放到x的右子树上
	{
		if (tree[x].right != 0)//如果右子树上有节点,递归查找
			add(tree[x].right, value);
		else//如果右子树上没有点,直接添加
		{
			cont++;
			tree[cont].val = value;
			tree[cont].siz = tree[cont].cnt = 1;
			tree[x].right = cont;
		}
	}
}

//查找前驱
int query_front(int x, int value, int ans) //x 是当前的节点的下标,val 是要找前驱的值,ans 是目前找到的比val小的数的最大值。
{
	if (cont == 0) return -INF;
	if (tree[x].val >= value)//当前节点val大于所查value,到左子树查找
	{
		if (tree[x].left == 0) //左子树不存在,那么前驱为ans
			return ans;
		else
			return query_front(tree[x].left, value, ans); //否则遍历左子树
	}
	else
	{
		if (tree[x].right == 0)//若无右孩子,说明当前点满足条件
			return (tree[x].val < value) ? tree[x].val : ans;
		else
		{
			if (tree[x].cnt != 0) //如果当前节点的个数不为0,ans就可以更新为tree[x].val
				return query_front(tree[x].right, value, tree[x].val);
			else
				return query_front(tree[x].right, value, ans);
		}
	}
}

//查找后继
int query_next(int x, int value, int ans)
{
	if (cont == 0) return INF;
	if (tree[x].val <= value)
	{
		if (tree[x].right == 0)
			return ans;
		else
			return query_next(tree[x].right, value, ans);
	}
	else
	{
		if (tree[x].left == 0)
			return (tree[x].val > value) ? tree[x].val : ans;
		else
		{
			if (tree[x].cnt != 0)
				return query_next(tree[x].left, value, tree[x].val);
			else
				return query_next(tree[x].left, value, ans);
		}
	}
}

//按值找排名
int val_to_rank(int x, int value)
{
	if (x == 0) 
		return 0;
	if (value == tree[x].val)
		return tree[tree[x].left].siz;//比它小的数
	if (value < tree[x].val)
		return val_to_rank(tree[x].left, value);
	if (value > tree[x].val)
		return val_to_rank(tree[x].right, value) + tree[tree[x].left].siz + tree[x].cnt;//左子树上的所有点加上根上的点都小于value
}

//按排名找值
int rank_to_val(int x, int rank)
{
	if (x == 0)
		return INF;
	if (tree[tree[x].left].siz >= rank) //如果小于x.val的数多于rank,那么该值一定在左子树上,递归查找
		return rank_to_val(tree[x].left, rank);
	if (tree[tree[x].left].siz + tree[x].cnt >= rank) //如果左子树上的点数+根上的节点数大于rank,且左子树上的点数小于rank,那么点x的值为所需值
		return tree[x].val;
	return rank_to_val(tree[x].right, rank - tree[tree[x].left].siz - tree[x].cnt); //否则在右子树上,要先将左子树+根上的点数减去
}

int main()
{
	int q;	cin >> q;
	init();
	for (int i = 1; i <= q; i++)
	{
		int op, v;
		cin >> op >> v;
		if (op == 1)
			cout << val_to_rank(1, v) + 1 << endl;
		else if (op == 2)
			cout << rank_to_val(1, v) << endl;
		else if (op == 3)
			cout << query_front(1, v, -INF) << endl;
		else if (op == 4)
			cout << query_next(1, v, INF) << endl;
		else
		{
			if (cont == 0)
			{
				cont++;
				tree[cont].cnt = tree[cont].siz = 1;
				tree[cont].val = v;
			}
			else
				add(1, v);
		}
	}
}

六、【P1364】医院设置(BFS广搜)

广度优先搜索(breadth-first search, BFS)不同与深度优先搜索,它是一层层进行遍历的,因此需要用先入先出的队列而非先入后出的栈进行遍历。由于是按层次进行遍历,广度优先搜索时按照“广”的方向进行遍历的,也常常用来处理最短路径等问题。

在本题中,由于每个点都可以是原点(树根),因此采用树结构存储是行不通的,因为指针只能单方向指向。这里采用邻接矩阵的方式来存储树。

AC代码:

本题中只考虑每个点到根节点的距离,因此采用DFS和BFS均可。

由于BFS操作起来更直观,因此本题用普通图的形式存储树,枚举每个节点为根节点的情况,进行n遍BFS,然后每遍都进行答案的比较更新。

#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <cstring>
#include <queue>

using namespace std;
const int INF = 0x7fffffff;

bool graph[105][105] = { 0 };
bool visit[105] = { 0 };
int value[105] = { 0 };

struct node
{
	int num;
	int step; //用于存储某点到根节点的距离
};
int n;

int BFS(int x) //以x为根对树进行BFS遍历
{
	memset(visit, 0, sizeof(visit)); 
	queue<node> q;
	visit[x] = 1;
	node gen; 
	gen.num = x, gen.step = 0;
	q.push(gen); //将树根节点入队
	int sum = 0;
	while (!q.empty())
	{
		node now = q.front();
		q.pop();
		for (int i = 1; i <= n; i++) //依次取出队列中的点,并找到与该点距离为1且未被访问过的点
		{
			if (graph[now.num][i] && !visit[i])
			{
				node next;
				next.num = i, next.step = now.step + 1; //更新选中点到根的距离
				visit[i] = 1;
				q.push(next);
				sum += value[i] * next.step; //该点的权重乘以与根的距离
			}
		}
	}
	return sum;
}

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int w, u, v;
		cin >> w >> u >> v;
		value[i] = w;
		if (u != 0)
			graph[i][u] = graph[u][i] = 1;
		if (v != 0)
			graph[i][v] = graph[v][i] = 1;
	}
	int MinDis = INF;
	for (int i = 1; i <= n; i++)
	{
		int tmp = BFS(i);
		if (tmp < MinDis)
			MinDis = tmp;
	}
	cout << MinDis << endl;
}

由于bfs每遍都是O(V+E)的,而这里特殊之处在于本身是个树,所以是n个节点和n-1条边

所以总复杂度近似为O(n^2).


七、【P3884】二叉树问题(Floyd算法)

树其实一种特殊的图。

即没有环路,一定联通,而且有且有只有一个节点没有入度的图。

  • 树上任意两点间的距离可以直接通过最短路算法获得。
  • 树的深度就是距离根节点最远的那个点的距离。
  • 树的宽度是节点最多的一层,同一层的节点距离根节点的距离都是相同的,所以也可以直接枚举获得。

AC代码:

Floyd:使用邻接矩阵存储树,先将邻接矩阵的对角线位置初始化为0(某点到自己的距离为0),其他位置初始化为INT_MAX/4(不相连的节点理论上距离为无限大,但是在计算是要考虑可能发生的溢出情况,所以除以4)

三层嵌套循环,列举每两个点 i 和 j 之间的距离,并且要考虑有中继点 k 存在的情况下,两点间的距离。(由于树是无环图,所以只需要一个中继点即可覆盖所有情况)

#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <cstring>
#include <queue>

using namespace std;
const int INF = 0x7fffffff / 4; //若直接为INT_MAX,则使用floyed时会发生溢出

int a[105][105] = { 0 };
int b[10005] = { 0 };

void init()
{
	for (int i = 0; i < 105; i++)
	{
		for (int j = 0; j < 105; j++)
		{
			if (i != j)
				a[i][j] = INF;
		}
	}
}


int main()
{
	init();
	int n;	cin >> n;
	for (int i = 1; i <= n - 1; i++)
	{
		int u, v;	cin >> u >> v;
		a[u][v] = 1;
		a[v][u] = 2;
	}
	for (int k = 1; k <= n; k++)
	{
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
			}
		}
	}
	int maxdepth = 0;
	for (int i = 1; i <= n; i++)
	{
		if (maxdepth < a[1][i])
			maxdepth = a[1][i];
		b[a[1][i]]++;
	}
	int maxd = 0;
	for (int i = 1; i <= maxdepth; i++)
	{
		maxd = max(maxd, b[i]);
	}
	int u, v;
	cin >> u >> v;
	cout << maxdepth + 1 << endl;
	cout << maxd << endl;
	cout << a[u][v] << endl;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony—不支持解构赋值

规则&#xff1a;arkts-no-destruct-assignment 级别&#xff1a;错误 ArkTS不支持解构赋值。可使用其他替代方法&#xff0c;例如&#xff0c;使用临时变量。 TypeScript let [one, two] [1, 2]; // 此处需要分号 [one, two] [two, one];let head, tail [head, ...tail]…

web前端项目-五子棋【附源码】

五子棋&#xff08;人机对弈&#xff09; 本项目【五子棋】是一款人机对弈的策略型棋类游戏。可以选择落子方&#xff1b;游戏难度和是否显示落子次序。游戏双方分别使用黑白两色的棋子&#xff0c;在棋盘直线与横线的交叉点上进行对弈。五子棋可以促进大脑发育、提高思维能力…

时序分析中的去趋势化方法

时序分析中的去趋势化方法 时序分析是研究随时间变化的数据模式的一门学科。在时序数据中&#xff0c;趋势是一种随着时间推移而呈现的长期变化趋势&#xff0c;去趋势化是为了消除或减弱这种趋势&#xff0c;使数据更具平稳性。本文将简单介绍时序分析中常用的去趋势化方法&a…

我爱这夜色茫茫

夜来香 - 李香兰 那南风吹来清凉 那夜莺啼声细唱月下的花儿都入梦 只有那夜来香 吐露着芬芳 我爱这夜色茫茫 也爱这夜莺歌唱 更爱那花一般的梦 拥抱着夜来香 吻着夜来香夜来香我为你歌唱 夜来香我为你思量 啊~啊我为你歌唱 我为你思量 我爱这夜色茫茫 也爱这夜莺歌唱更爱那花…

GitHub Action 实现超简单的持续集成(CI)

GitHub Action 官方文档 GitHub Action 中使用 Docker 的官方文档 所用项目代码获取&#xff1a;公众号发送cloud 前言 在上一篇几分钟完成前后端分离项目部署文章中&#xff0c;我们完成了前后端分离项目的部署&#xff0c;但随着开发的进行&#xff0c;我们每次更新都手动打包…

IT网课满天飞,花两万学这个课真的有用吗?

我一直都觉得&#xff0c;IT类付费课程&#xff0c;无用且没有必要&#xff0c;我写代码10多年&#xff0c;没有花过一分钱报班学什么java, 学什么就业课&#xff0c;完全不妨碍我现在年薪百万。报班没有意义的地方在于&#xff0c;它会给你的大脑灌输一些你消化不了的知识&…

数据湖技术之应用场景篇

数据湖技术有较多的应用场景&#xff0c;本篇文章是针对一些典型的痛点场景做了一些介绍和说明。比如说在线数据抽取场景原有模式对线上库表产生较大压力&#xff0c;flink多流join维护的大状态导致的稳定性问题等等&#xff0c;具体场景如下图所示&#xff1a; 场景1:在线数据…

贪吃蛇/链表实现(C/C++)

本篇使用C语言实现贪吃蛇小游戏&#xff0c;我们将其分为了三个大部分&#xff0c;第一个部分游戏开始GameStart&#xff0c;游戏运行GameRun&#xff0c;以及游戏结束GameRun。对于整体游戏主要思想是基于链表实现&#xff0c;但若仅仅只有C语言的知识还不够&#xff0c;我们还…

2024/1/27 备战蓝桥杯 1-1

目录 求和 0求和 - 蓝桥云课 (lanqiao.cn) 成绩分析 0成绩分析 - 蓝桥云课 (lanqiao.cn) 合法日期 0合法日期 - 蓝桥云课 (lanqiao.cn) 时间加法 0时间加法 - 蓝桥云课 (lanqiao.cn) 扫雷 0扫雷 - 蓝桥云课 (lanqiao.cn) 大写 0大写 - 蓝桥云课 (lanqiao.cn) 标题…

WordPress如何使用SQL实现一键关闭/开启评论功能(已有评论)

WordPress本人就自带评论功能&#xff0c;不过由于种种原因&#xff0c;有些站长不想开启评论功能&#xff0c;那么应该怎么实现一键关闭评论功能或开启评论功能呢&#xff1f;或者针对已有评论功能的文章进行一键关闭或开启评论功能应该怎么操作&#xff1f; 如果你使用的Wor…

第四篇:怎么写express的路由(接口+请求)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 引言&#xff1a; &#x1f4…

解密人工智能:探索机器学习奥秘

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 机器学习的定义二. 机器学习的发展历程三. 机器学习的原理四. 机器学习的分类…

5.Hive表修改Location,一次讲明白

Hive表修改Loction 一、Hive中修改Location语句二、方案1 删表重建1. 创建表&#xff0c;写错误的Location2. 查看Location3. 删表4. 创建表&#xff0c;写正确的Location5. 查看Location 三、方案2 直接修改Location并恢复数据1.建表&#xff0c;指定错误的Location&#xff0…

有趣的 CSS - 动态圆点水波纹效果

先看效果 整体效果 这个效果使用 css 中 animation 属性&#xff0c;以及搭配伪元素 ::after、::before 来实现两个圆交替变化。 核心代码 html部分代码 <div><label class"dot"></label> </div>label 标签画圆点主体。 css部分代码 .ap…

蓝桥杯---九数组分数

1,2,3 ... 9 这九个数字组成一个分数,其值恰好为1/3,如何组法? 下面的程序实现了该功能,请填写划线部分缺失的代码。 注意,只能填写缺少的部分,不要重复抄写已有代码。不要填写任何多余的文字。

烧录软件(Renesas Flash Programmer)瑞萨RL78G12系列单片机下载工具(E2)的软件配置与硬件链接说明

一、单片机与仿真器连接 E1引脚接线图 RL78系列单片机的GND接仿真器的pin2、pin12、pin14 RL78系列单片机的VDD接仿真器的pin8 RL78系列单片机的Tool0接仿真器的pin5 RL78系列单片机的Reset接仿真器的pin10、pin13 二、确认接线完成后&#xff0c;开始烧录 1、打开RFPV软件…

实时视觉效果制作 -- Resolume Arena 7 中文

Resolume Arena 7是一款专业的实时视觉效果软件&#xff0c;能够为观众带来令人难以置信的视听盛宴。它具备强大的功能和直观的界面设计&#xff0c;使得用户能够轻松地创作、编辑和演示各种视觉效果。无论是在音乐会、派对还是舞台演出中&#xff0c;Resolume Arena 7都能满足…

golang封装业务err(结合iris)

golang封装业务err 我们有时在web开发时&#xff0c;仅凭httpStatus以及msg是不方便维护和体现我们的业务逻辑的。所以就需要封装我们自己的业务错误。 自定义biz_err维护err map&#xff1a;errorResponseMap、errorHttpStatusMap 注意&#xff1a;本文主要以演示为主&#xf…

【ArcGIS微课1000例】0095:横向图例制作案例教程

文章目录 一、加载数据二、高程分级显示三、横向图例四、注意事项一、加载数据 为了便于直观演示,本实验加载一个栅格数据(配套实验数据包中的0095.rar)并进行分级显示,效果如下: 二、高程分级显示 双击dem数据图层,打开栅格数据的【图层属性】对话框,切换到【符号系统…

POJ No.1852 Ants

思路分析 “转向”问题 假设蚂蚁A与蚂蚁B相遇后转向&#xff0c;可以视作A&#xff0c;B交换位置&#xff0c;从而消除转向。 距离问题 最长距离&#xff1a;比较每只蚂蚁距两端的最大距离&#xff0c;取两端中最大值&#xff0c;取一组中最长距离的最大值。 最短距离&…