数据结构(C语言实现)——二叉树的概念及二叉树顺序结构和链式结构的实现(堆排序+TOP-K问题+链式二叉树相关操作)

news2024/11/24 20:36:56

文章目录

  • 1. 前言
  • 2. 树的概念及结构
    • 2.1 树的概念
    • 2.2 树的相关概念
    • 2.3 树的表示
  • 3. 二叉树的概念
    • 3.1 特殊二叉树
    • 3.2 二叉树的性质
  • 4. 二叉树的顺序存储
    • 4.1 堆的概念
    • 4.2 堆的实现
      • 4.2.1 堆的结点定义
      • 4.2.2 堆的打印和销毁
      • 4.2.3 堆的插入
      • 4.2.4 堆的删除
      • 4.2.5 取堆顶数据
      • 4.2.6 堆的判空
      • 4.2.7 堆的数据个数
    • 4.3 堆的应用
      • 4.3.1 堆排序
      • 4.3.2 TOP-K问题
  • 5. 二叉树的链式存储
    • 5.1 链式二叉树的结点定义
    • 5.2 结点创建
    • 5.3 模拟创建二叉树
    • 5.4 二叉树的遍历
      • 5.4.1 前序遍历
      • 5.4.2 中序遍历
      • 5.4.3 后序遍历
      • 5.4.4 层序遍历
    • 5.5 二叉树结点个数及高度等操作
      • 5.5.1 二叉树的结点个数
      • 5.5.2 二叉树叶子结点的个数
      • 5.5.3 二叉树第k层的结点个数
      • 5.5.4 查找二叉树中值为x的结点
      • 5.5.5 计算二叉树的深度
      • 5.5.6 判断是否为完全二叉树
  • 6. 结尾

1. 前言

前面学习了数据结构中线性结构的几种结构,顺序表,链表,栈和队列等,今天我们来学习一种非线性的数据结构——树。由于二叉树是数据结构中的一个重点和难点,所以本文着重介绍二叉树的相关概念和性质,以及二叉树的应用。

2. 树的概念及结构

2.1 树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
注意事项:
1.树是递归定义的
2.树形结构中,子树之间不能有交集,否则就不是树形结构

在这里插入图片描述

2.2 树的相关概念

如果有一棵树如下图所示:
在这里插入图片描述
那么它有以下概念:

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;

2.3 树的表示

树在存储时,既要保存数据,也要保存结点与结点之间的关系,而且实际中树有许多种表示方法,如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。下面我们就介绍最常用的孩子兄弟表示法。

typedef int TreeDataType;
struct TreeNode
{
	TreeDataType data;//结点的数据域
	struct TreeNode* FirstChild;//指向其第一个孩子结点
	struct TreeNode* NextBrother;//指向其下一个兄弟结点
};

在这里插入图片描述

3. 二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:
1.或者为空
2.由一个根结点加上两棵分别称为左子树和右子树的二叉树组成

如下图所示就是一颗二叉树:
在这里插入图片描述
从上图我们可以看出:

1.二叉树不存在度大于2的结点
2.二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

3.1 特殊二叉树

二叉树中还有俩种特殊的二叉树:

1.满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k - 1,则它就是满二叉树。

在这里插入图片描述

2.完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。

在这里插入图片描述

3.2 二叉树的性质

1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点
2.若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1
3.对任何一棵二叉树, 如果度为0其叶结点个数为n0, 度为2的分支结点个数为n2,则有n0 = n2 + 1
4.若规定根节点的层数为1,具有n个结点的满二叉树的深度,h = log2(n+1)
5.对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,
则对于序号为i的结点有:
(1)若i > 0,i位置节点的双亲序号:(i - 1) / 2;若i = 0,则i为根节点编号,无双亲节点
(2)若2i + 1 < n,左孩子序号:2i + 1,若2i + 1 >= n则无左孩子
(3)若2i + 2 < n,右孩子序号:2i + 2,若2i + 2 >= n则无右孩子

4. 二叉树的顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。
二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
而现实中只有堆才会使用数组来存储。

4.1 堆的概念

所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

4.2 堆的实现

4.2.1 堆的结点定义

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

4.2.2 堆的打印和销毁

打印:

void HeapPrint(HP* php)
{
	assert(php);
	int i = 0;
	for (i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

销毁:

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

4.2.3 堆的插入

先插入一个数据到数组的尾上,再进行向上调整算法,直到满足大根堆或者小根堆。
向上调整算法:以小根堆来举例,从该结点开始向上找父结点,如果该结点小于父结点,就把该结点和父结点交换,继续向上调整,直到满足小根堆。

插入:

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("malloc");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);
}

向上调整算法:

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

交换:

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

4.2.4 堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
向下调整算法:以小根堆来举例,从该结点开始向下找子结点,如果子结点小于该结点,就把子结点和该结点交换,再继续向下调整,直到满足小根堆

删除:

void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

向下调整算法:

void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && (a[child + 1] < a[child]))
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

4.2.5 取堆顶数据

堆顶元素就是数组下标为0的元素

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

4.2.6 堆的判空

size为0即为空

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

4.2.7 堆的数据个数

size的大小就是数据个数

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

4.3 堆的应用

4.3.1 堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1.建堆
升序:建大堆
降序:建小堆
2.利用堆删除思想来进行排序

void HeapSort(int* arr, int size)
{
	//排升序建大堆,排降序建小堆
	//向上调整建堆O(N*logN)
	//int i = 0;
	//for (i = 1; i < size; i++)
	//{
	//	AdjustUp(arr, i);
	//}

	//向下调整建堆O(N)
	int i = 0;
	for (i = (size - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, size, i);
	}

	int end = size - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, end, 0);
		end--;
	}
}

int main()
{
	int arr[10] = { 23,45,48,123,12,49,80,15,5,35 };
	HeapSort(arr, 10);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

4.3.2 TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
思路:
1.用数据集合中前K个元素来建堆
求前k个最大的元素,则建小堆
求前k个最小的元素,则建大堆
2.用剩余的N - K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N - K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

void PrintTopK(int* a, int n, int k)
{
	//1.建堆--用a中前k个元素建堆
	int* kMaxHeap = (int*)malloc(sizeof(int)*k);
	assert(kMaxHeap);
	int i = 0;
	for (i = 0; i < k; i++)
	{
		kMaxHeap[i] = a[i];
	}
	for (i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kMaxHeap, k, i);
	}

	//2.将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int j = 0;
	for (j = k; j < n; j++)
	{
		if (a[j] > kMaxHeap[0])
		{
			kMaxHeap[0] = a[j];
			AdjustDown(kMaxHeap, k, 0);
		}
	}
	for (i = 0; i < k; i++)
	{
		printf("%d ", kMaxHeap[i]);
	}
}

void TestTopk()
{
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	srand(time(0));
	for (int i = 0; i < n; i++)
	{
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;
	PrintTopK(a, n, 10);
}

int main()
{
	TestTopk();
	return 0;
}

5. 二叉树的链式存储

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。
通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。
链式结构又分为二叉链和三叉链。

5.1 链式二叉树的结点定义

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	BTDataType data;
}BTNode;

5.2 结点创建

BTNode* CreatBTNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	assert(node);
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

由于二叉树的创建比较复杂,所以下面我们来手动模拟一个简单的二叉树,便于后边操作的实现

5.3 模拟创建二叉树

BTNode* CreatBinaryTree()
{
	BTNode* node1 = CreatBTNode(1);
	BTNode* node2 = CreatBTNode(2);
	BTNode* node3 = CreatBTNode(3);
	BTNode* node4 = CreatBTNode(4);
	BTNode* node5 = CreatBTNode(5);
	BTNode* node6 = CreatBTNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	return node1;
}

5.4 二叉树的遍历

二叉树的遍历有:前序/中序/后序/层序的递归结构遍历:
1.前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2.中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3.后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
4.层序遍历(Level Traversal)——从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

5.4.1 前序遍历

void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

5.4.2 中序遍历

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

5.4.3 后序遍历

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

5.4.4 层序遍历

层序遍历稍微复杂一点,需要队列来实现,我们这里就之前使用之前创建好的队列,不再过多展示,核心思路是:先判断该结点是不是空,如果不是就进队,再判断队是不是空,如果不是先打印出此时队头元素,然后删除队头元素,接下来再分别判断打印的队头元素的左孩子和右孩子是不是为空,不是空就继续入队。这样循环迭代即层序遍历。

void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);
}

5.5 二叉树结点个数及高度等操作

5.5.1 二叉树的结点个数

因为二叉树的左子树和右子树都可以分别看成单独的二叉树,所以核心思路是递归计算左子树和右子树的结点个数,最后返回再加1就是该二叉树的结点个数。

int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

5.5.2 二叉树叶子结点的个数

左右子树都为空的结点就是叶子结点,这里也采用递归的思路,把二叉树分为左子树和右子树,左子树也可以分为左子树和右子树,递归计算并返回即可求的整棵树的叶子结点个数。

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

5.5.3 二叉树第k层的结点个数

这里的思路是,求根结点的第k层的结点个数,可以转换成求该结点的左孩子的第k-1层的结点个数+该结点的右孩子的第k-1层的结点个数,递归下去,结束后返回的即为根结点第k层的结点个数。

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k >= 1);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}

	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);

}

5.5.4 查找二叉树中值为x的结点

这里的思路是,如果该结点就是要求的结点,直接返回该结点,如果不是就递归查找该结点的左子树和右子树,找到就返回。

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
	{
		return ret1;
	}
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL;
}

5.5.5 计算二叉树的深度

二叉树的深度就是该二叉树的左子树和右子树的最大深度再+1,这里的思路是,递归计算左子树的深度和右子树的深度,每次返回都是以某个结点为根的二叉树的深度,最后返回的就是该二叉树的深度。

int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	int LeftDepth = BinaryTreeDepth(root->left);
	int RightDepth = BinaryTreeDepth(root->right);

	return LeftDepth > RightDepth ? LeftDepth + 1 : RightDepth + 1;

}

5.5.6 判断是否为完全二叉树

这里也要用到队列,核心思路是利用完全二叉树的性质,完全二叉树的非空结点一定是连在一起的,一旦出现空结点,则空结点后面的所有结点都应该为空,如果还有非空结点,就不是完全二叉树。

int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			break;
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

6. 结尾

到这里,二叉树的概念及二叉树顺序结构和链式结构的实现和应用就介绍完了,二叉树作为数据结构中的重点和难点需要反复学习,理解透彻,非常考验大家的理解能力和基本功,本人也是一个初学者,文中难免有很多地方出现错误和纰漏,此文仅供大家学习和参考。如果本文对大家学习二叉树有帮助的话,博主感到非常荣幸。
最后,感谢各位大佬的耐心阅读和支持,觉得本篇文章写的不错的朋友可以三连关注支持一波,如果有什么问题或者本文有错误的地方大家可以私信我,也可以在评论区留言讨论,再次感谢各位。

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

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

相关文章

【Python学习笔记】cs231nPython Numpy教程

【Python学习笔记】cs231nPython Numpy教程 回顾经典教程cs231n&#xff0c;完成assignments觉得很困难&#xff0c;感觉自己python基础语法掌握的不是很熟&#xff0c;就顺藤摸瓜找了cs231n他们的官方Python个Numpy教程 如果对英文原版上手有困难&#xff0c;可以看这个&…

智慧园区水电监测系统

随着人们对环保意识的提高&#xff0c;智慧园区的建设也越来越受到关注。其中&#xff0c;水电监测系统是智慧园区的一个重要组成部分。本文将从以下几个方面介绍智慧园区水电监测系统的特点和优势。 一、智慧园区水电监测系统的特点 1.实时监测&#xff1a;智慧园区水电监测系…

某医院网络故障分析案例

1、背景 某市第一医院用户反馈&#xff0c;近期内部业务系统出现访问慢的情况&#xff0c;这种情况严重影响到用户的体验和工作效率。 针对此问题&#xff0c;我们通过NetInside流量分析系统&#xff0c;提供实时和历史原始流量。重点针对网络异常流量跟踪分析&#xff0c;找…

go数据结构(二叉树的遍历)

用数组来存储二叉树如何遍历的呢&#xff1f; 如果父节点的数组下表是i&#xff0c;那么它的左孩子就是i * 2 1&#xff0c;右孩子就是 i * 2 2。 二叉树的遍历方式&#xff1a; 二叉树有三种基本遍历方式&#xff0c;分别是前序遍历、中序遍历和后序遍历。遍历的原理是从根…

Jenkins 流水线

采用Jenkins的自由风格构建的项目&#xff0c;适合用于测试和学习&#xff0c;主要问题有&#xff1a; 构建过程中整体流程是不可见的&#xff0c;无法确认每个流程花费的时间出现问题不方便快速的定位无法进行版本化管理多个任务中有很多步骤需要重复搭建 Jenkins的Pipeline…

MYSQL Row 752 was cut by GROUP_CONCAT()

因为group_concat有个最大长度的限制&#xff0c;GROUP_CONCAT函数返回的结果大小被MySQL默认限制为1024(字节)的长度。超过最大长度就会被截断掉 解决方法&#xff1a;更改配置文件&#xff0c;修改长度。 https://blog.csdn.net/zzddada/article/details/115082236 concat…

DHCP与RIP配置实验

目录 一、实验要求与拓扑结构 1、实验要求 2、提前规划好网段的拓扑结构如下图 二、实验步骤 1、给每台路由器的各个接口配置ip 2、给路由器AR1和AR3配置dhcp服务 3、在路由器AR1和AR2上运行ripv2&#xff0c;在AR3上运行ripv1 4、更改接口的rip协议版本 一、实验要求与…

chatglm-6b模型在windows的详细安装教程

1.先是看了github的文章&#xff08;如果打不开这篇文章&#xff0c;可能需要科学上网&#xff0c;即访问外网的VPN&#xff09;&#xff1a; https://github.com/THUDM/ChatGLM-6B 2.准备&#xff1a;台式机&#xff0c;GPU是8G。&#xff08;关于是否可以在笔记本运行&#x…

【网路】-- HTTP协议

目录 HTTP协议 认识URL urlencode和urldecode Http http的宏观结构 http请求报文格式 http响应报文格式 HTTP的方法 表单 重定向 HTTP常见Header 会话管理 Cookie 实验证明 Connection选项 工具推荐 Fiddler 原理 应用层&#xff1a;就是程序员基于socket接口…

如何在大厂做好架构演进?

1 架构演进的定义 1.1 定义 通过设计新的系统架构(4R)&#xff0c;来应对业务和技术的发展变化。 1.2 关键点 新架构新的复杂度 1.3 目的 应对业务和技术的发展变化后带来新的复杂度。 案例 淘宝去IOE&#xff0c;是因为业务发展大了后&#xff0c;IOE的成本和可控性难…

51单片机入门

文章目录 一、安装keil5及proteus二、MCS-51单片机结构与原理(一).8051单片机基本组成(二).8051单片机引脚1.电源引脚2.时钟电路引脚3.控制信号引脚4.输入/输出端口 (三) 并行输入/输出端口结构 三、单片机cx51编程基础(一).变量定义(二).数据类型(三).存储类型(四).Cx51语言程…

【Python】逆向解析js代码

目录 1. 打开百度翻译网页&#xff0c;查找翻译结果的网络资源包 2. 获取翻译结果网络资源包的url、请求头、请求体&#xff0c;解析json文件数据 3. 观察请求体字段&#xff0c;发现 query 字段便是我们输入的需要翻译的值 4. ctrl F 快捷键搜索sign值的网络资源包&#x…

自然语言处理:词嵌入简介

动动发财的小手&#xff0c;点个赞吧&#xff01; Word Embeddings 机器学习模型“查看”数据的方式与我们&#xff08;人类&#xff09;的方式不同。例如&#xff0c;我们可以轻松理解“我看到一只猫”这一文本&#xff0c;但我们的模型却不能——它们需要特征向量。此类向量或…

MongoDB 聚合管道的集合关联($lookup)及合并($unionWith)

目前为止&#xff0c;我们已经介绍了一部分聚合管道中的管道参数&#xff1a; $match&#xff1a;文档过滤 $group&#xff1a;文档分组&#xff0c;并介绍了分组中的常用操作&#xff1a;$addToSet&#xff0c;$avg&#xff0c;$sum&#xff0c;$min&#xff0c;$max等。 $add…

python OCR识别验证码

1. 抓取网页验证码图像并保存 import lxml.html, urllib3# 使用urllib3抓取网页数据 http urllib3.PoolManager() html http.request(GET,site).data# 使用lxml解析网页数据 tree lxml.html.fromstring(html) # 解析HTML&#xff0c;补全不完整的格式 fixedhtml lxml.ht…

LeetCode:102. 二叉树的层序遍历

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 可以参考&#x1f449;LeetCode&#xff1a;二叉树的前、中、后序遍历——如何创建一棵【二…

机器学习 特征工程

文章目录 一、数据预处理1. 缺失值处理1.1 删除1.2 统计值填充1.3 前后向值填充1.4 直接忽略1.5 模型预测 2. 异常值处理 二、特征提取1. 数值型特征提取1.1 标准化与缩放1.1.1标准化&#xff08;Standardization&#xff09;1.1.2 归一化&#xff08;Normalization&#xff09…

STM32F4_定时器精讲(TIM)

目录 1. 什么是定时器&#xff1f; 2. STM32定时器简介 2.1 高级控制定时器 TIM1和TIM8 2.1.1 TIM1和TIM8简介 2.1.2 时基单元 2.1.3 计数器模式 2.1.4 重复计数器 2.1.5 时钟选择 2.1.6 捕获/比较通道 2.1.7 输入捕获模式 2.1.8 其他功能 2.2 通用定时器 TIM2到TI…

算法----删点成林

题目 给出二叉树的根节点 root&#xff0c;树上每个节点都有一个不同的值。 如果节点值在 to_delete 中出现&#xff0c;我们就把该节点从树上删去&#xff0c;最后得到一个森林&#xff08;一些不相交的树构成的集合&#xff09;。 返回森林中的每棵树。你可以按任意顺序组…

2023年天梯赛模拟赛

//能力有限&#xff0c;只展示一百分代码。前八个题一般是原题&#xff0c;所以不展示题目。 L1-1 嫑废话上代码 #include<bits/stdc.h> using namespace std; int main(){cout<<"Talk is cheap. Show me the code.";return 0; } L1-2 九牛一毛 这是…