【初阶数据结构】二叉树全面知识总结

news2024/11/18 13:33:43

二叉树详解

  • 树的概念及其结构
    • 树的概念
    • 树的相关概念
    • 树的表示方法
      • 孩纸兄弟表示法
      • 双亲表示法(并查集)
    • 树的实际应用
  • 二叉树
    • 二叉树的概念
    • 二叉树的种类
    • 二叉树的性质
    • 二叉树的存储结构
  • 二叉树顺序结构的实现
    • 堆的概念及结构
    • 堆向上、向下调整法
    • 堆的插入
    • 堆的删除
    • 堆的创建
    • 堆实现总代码
    • 建堆时间复杂度的证明
    • 堆排序
    • TopK问题
  • 二叉树链式结构的实现
    • 创建二叉树
    • 前序遍历及其实现
    • 中序遍历及其实现
    • 后序遍历及其实现
    • 销毁二叉树
    • 求二叉树的高度
    • 求二叉树总节点个数
    • 求二叉树叶子节点个数
    • 求二叉树第k层节点个数
    • 二叉树查找值为x的节点
    • 二叉树总代码实现
    • 层序遍历
    • 判断是否为二叉树
    • 总代码

铁汁们,今天给大家分享一篇二叉树全面知识总结,来吧,开造⛳️

树的概念及其结构

树的概念

树的概念:是一种非线性的数据结构,它由n个有限的节点组成的一个具有层次关系的集合。树的结构类似于真实世界中的树,它看起来就像一颗倒挂着的树,即:它的根是朝上的,而叶子是朝下的。

在这里插入图片描述
在这里插入图片描述
说明:

1.有一个特殊的节点,根节点,该节点没有父节点(前驱节点);

2.根节点除外,其他节点被分成了M个互不相交的集合{a1, a2, a3…},每个集合又是一颗结构类似的子树(每个子树又可以被分成根节点、左子树、右子树)——》递归思想,把大事化小,树是递归定义的

3.其他节点都有一个父节点(前驱节点),并且可以有零个或多个子节点(后继节点)。一个节点可以有一个或多个子节点,但每个节点最多只能有一个父节点 ——》说明子树是不相交的

在这里插入图片描述

注意:树形结构中,子树之间不能有交集,否则就不是树形结构。

树的相关概念

在这里插入图片描述

树的表示方法

树形结构的线性表:树在进行存储时,既要保存值域,也要保存各个节点之间的关系。

树的表示方法:双亲表示法、孩纸兄弟表示法、孩纸表示法、孩纸双亲表示法等(多叉树)。

孩纸兄弟表示法

孩纸兄弟表示法:让根节点指向第一个节点,让第一个节点指向靠的最近的兄弟节点,依次往后,直到无兄弟节点和兄弟节点无第一个节点。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

typedef int DataType;

typedef struct Node  //孩纸兄弟表示法
{
	struct Node* firstchild; 
	struct Node* secondchild;
	DataType val;  //值域
}Node;

int main()  
{
	Node* parent;  //从根节点开始
	Node* child = parent->firstchild;  //让根节点指向第一个节点
	while (child)  
	{
		printf("%d ", child->val);
		child = child->secondchild;  //第一个节点指向靠的最近的兄弟节点
	}

	return 0;
}

在这里插入图片描述

双亲表示法(并查集)

在这里插入图片描述

//c++实现代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream> //合并查集(最基本版:用来1.合并两个集合, 2.查找两个数是否在一个集合内)

using namespace std;

const int N = 1e5 + 10;
//每个集合都用一个树来表示,树的编号就是整个集合的编号,每个数都有个树根。每个节点用来存储其父节点,p[x]表示节点x的父节点
//判断每个集合的树根,p[x] == x
//求x的集合编号,while(p[x] != x) x = p[x];
//将两个集合编号合并,因px是x的集合编号,py是y的集合编号,p[x] = y(让x所在的集合变为y所在集合的儿子)或者p[y] = x(让y所在的集合变为x所在集合的儿子)
int p[N];

int find(int x) //查找每个数的树根(集合编号)+路径压缩(因每个节点均向上找树根,当一个节点找到了树根,则该节点所在的路径上所有的点父节点直接等于树根)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= n; i++) p[i] = i; //因每个数都在不同的集合中,让集合的编号的本身(p[x] == x,树根)

    while (m--)
    {
        char op[2];
        int a, b;
        cin >> op >> a >> b;

        if (*op == 'I') p[find(a)] = find(b); //两个集合的合并
        else  //判断两数所在的集合是否相同,即:判断树根值是否向他
        {
            if (find(a) == find(b)) printf("YES\n"); //相同
            else  printf("NO\n"); //不相同
        }
    }

    return 0;
}

树的实际应用

树的应用:用于表示文件系统的目录,表示方法为孩子兄弟法。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二叉树

二叉树的概念

二叉树定义:是由有限节点组成的集合,该集合可能为空(空树),或不为空(由一个根节点加上左子树和右子树组成)。

注意
1.二叉树度小于等于2
2.二叉树不存在度大于2的节点
3.二叉树有左、右子树之分,次序不能颠倒,所以二叉树也为有序树

二叉树都是由以下几种情况的树复合而成的:
在这里插入图片描述

二叉树的种类

特殊的二叉树:满二叉树、完全二叉树。

1.满二叉树
一个二叉树,每一层节点达到了最大值(度等于2),则这个二叉树为满二叉树。
在这里插入图片描述

注意:满二叉树高度为h,满二叉树总结点树为2^n-1个

现实生活中的满二叉树:
在这里插入图片描述
2.完全二叉树
二叉树深度为n,1.我是文本 红色red前n-1层结点数都要达到最大值(度为2),最后一层结点数不一定达到最大值,但最后一层节点一定是从左到右排布的。完全二叉树是一种特殊的满二叉树
在这里插入图片描述

注意:完全二叉树的总结点个数范围为[ 2^(n-1), 2^n-1]

二叉树的性质

在这里插入图片描述
题目:
在这里插入图片描述

二叉树的存储结构

二叉树一般可分为两种结构存储,一种为顺序存储,一种为链式存储

1.顺序存储
顺序结构存储数据实质就是用数组来存储,一般数组只适用于存储满二叉树、完全二叉树,若存储普通的二叉树则会造成空间浪费,堆、栈、顺序表均为顺序存储结构,均用数组来存储数据。二叉树顺序存储在逻辑结构上是一颗二叉树(想象出来的结构),在 color = red>物理结构上是一个数组(实际存在的)。
在这里插入图片描述
2.链式存储

二叉树链式存储:
用链表来表示二叉树节点之间的逻辑关系,通常链表中每个节点由三个域组成,数据域和左、右指针域,左、右指针域分别存储该节点的左、右孩子的地址,根节点通过其左右子节点指针连接到左右子树,子节点可以依次连接到它们的子树。链式结构又可以分为二叉链、三叉链。

二叉链
每个节点包含数据元素和指向左右子节点的指针。通过这个指针,可以实现从子节点到父节点的访问。二叉链结构使得在树中的任意节点上,能够直接访问其父节点,方便进行反向操作。但要注意,根节点的父节点指针为空。

三叉链
每个节点除了包含数据元素和指向左右子节点的指针之外,还包含一个指向父节点的指针。三叉链结构使得在树中的任意节点上,能够同时访问其父节点和子节点,方便进行各种树的操作。

二叉树顺序结构的实现

堆的概念及结构

堆:堆中所有的元素按完全二叉树的顺序全部存储到一维数组中,当根节点的值<=左、右子树的根节点的值,任意父亲节点值<=左、右孩纸节点的值,则该堆为小堆;当根节点的值>=左、右子树的根节点的值,任意父亲节点值>=左、右孩纸节点的值,则该堆为大堆

注意:堆的性质:
1.任意父亲节点值<=(或者>=)左、右孩纸节点的值。
2.堆的逻辑结构为完全二叉树(堆是一颗完全二叉树),物理结构为数组。

在这里插入图片描述

堆向上、向下调整法

给出一个数组,就可以画出其对应得完全二叉树,通过向上或向下调整法可以将其调整为一个小堆。

向上、向下调整法使用前提:左、右子树必须都为堆。

向上调整法:从根节点的左结点开始,从左到右依次调整每一层的所有节点形成堆。

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

在这里插入图片描述

向下调整法:从最后一个非叶子节点开始,数据从下往上依次向下调整每个节点形成堆。

void AdjustDown(int* a, int n, int parent)  //(使用前提:根的左、右子树均为堆)向下调整,从最后一个非叶子节点开始,数据从下往上依次向下调整形成堆
{
	int child = parent * 2 + 1;  //孩纸节点,初始化左孩子值比右孩纸值小(找孩纸节点)
	while (child < n) //
	{
		if (child + 1 < n && a[child] > a[child + 1]) //左、右孩纸值进行比较,确保右孩纸存在,且左孩子值比右孩纸值大
			swap(&a[child], &a[child + 1]);  //交换,此时child节点中的值为孩纸节点的最小值
		if (a[child] < a[parent])  //左孩子值与父节点值进行比较
			swap(&a[child], &a[parent]);  //交换,形成小堆

		parent = child;  //递归
		child = parent * 2 + 1;
	}
}

在这里插入图片描述

堆的插入

思路:将数据直接插入到最后一个位置,新插入的元素在向上调整。

void HeapPush(Heap* hp, HPDataType x)  //向堆中插入一个元素
{
	assert(hp); //断言,不能对空指针进行加、减、解引用操作

	if (hp->size == hp->capacity) //空间满了,不能进行插入数据,如需插入数据,需要realloc进行扩容
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2; //新容量
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity); 
		if (tmp == NULL) //realloc开辟失败
		{
			perror("realloc failed");
			exit(-1);
		}
		hp->a = tmp;  //realloc开辟成功
		hp->capacity = newcapacity;  //容量进行更新为新容量
	}

	hp->a[hp->size] = x; //在末尾插入一个元素
	hp->size++;

	AdjustUp(hp->a, hp->size - 1); //在将新插入的元素进行向上调整形成堆
}

堆的删除

思路:将堆中最后一个元素覆盖堆顶元素,堆顶元素在向下调整。

void HeapPop(Heap* hp)  //删除堆顶元素
{
	assert(hp); //断言,不能对空指针进行加、减、解引用操作

	hp->a[0] = hp->a[hp->size-1]; //将最后一个元素覆盖堆顶元素
	hp->size--; 

	AdjustDown(hp->a, hp->size, 0); //在将堆顶元素进行向下调整形成堆
}

堆的创建

void HeapCreat(Heap* hp, int* b, int n) //建堆(堆的创建)
{
	assert(hp && b); //断言,不能对空指针进行加、减、解引用操作

	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);  //malloc动态开辟数组空间 
	if (hp->a == NULL)  //malloc动态开辟失败
	{
		perror("malloc failed");
		exit(-1);  //异常退出,终止程序,(非0值表示不正常退出,0表示正常退出)
	}

	memcpy(hp->a, b, sizeof(HPDataType) * n);  //此处需要将已知的数组建成堆,将数组中所有值拷贝给动态开辟的数组
	hp->size = hp->capacity = n;  

	for (int i = (n-2)/2; i >= 0; i--) //向下调整法建堆,从倒数第一个非叶子节点开始调整,层数依次向上,每层从右到左遍历每个节点,每个节点都向下调整
		AdjustDown(hp->a, n, i);
}

堆实现总代码

Heap.h
#pragma once  //用数组来模拟实现堆(顺序表实现)

#include<stdio.h>  
#include<stdlib.h>    //malloc
#include<assert.h>   //assert
#include<string.h>  //memcpy

typedef int HPDataType;  

typedef struct HeapNode  //动态版
{
	HPDataType* a; //malloc动态开辟数组空间,存储每个节点的值
	int size;  //表示堆中实际有效节点的总个数
	int capacity;  //容量
}Heap;

//传址调用,此处只需要改变结构体中的成员变量,只需要传结构体的地址,形参为一级指针(顺序表),若需要改变结构体的地址,则形参为二级指针(单链表)。

void HeapCreat(Heap* hp, int* b, int n);  //建堆(堆的创建)

void HeapPush(Heap* hp, HPDataType x);  //向堆中插入一个元素

void HeapDestory(Heap* hp); //销毁

void HeapPop(Heap* hp);  //删除堆顶元素

HPDataType HeapTop(Heap* hp);  //获取堆顶元素

int HeapSize(Heap* hp);  //获取堆中有效节点的总个数

int HeapEmpty(Heap* hp);  //判断堆是否为空

void HeapPrint(Heap* hp);  //打印堆中节点的值
Heap.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"Heap.h"

void swap(int* p1, int* p2)  //交换两元素的值,传址调用,传值调用(形参的改变不会影响实参的改变)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDown(int* a, int n, int parent)  //(使用前提:根的左、右子树均为堆)向下调整,从最后一个非叶子节点开始,数据从下往上依次向下调整形成堆
{
	int child = parent * 2 + 1;  //孩纸节点,初始化左孩子值比右孩纸值小
	while (child < n) //
	{
		if (child + 1 < n && a[child] > a[child + 1]) //左、右孩纸值进行比较,确保右孩纸存在,且左孩子值比右孩纸值大
			swap(&a[child], &a[child + 1]);  //交换,此时child节点中的值为孩纸节点的最小值
		if (a[child] < a[parent])  //左孩子值与父节点值进行比较
			swap(&a[child], &a[parent]);  //交换,形成小堆

		parent = child;  //递归
		child = parent * 2 + 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;
	}
}

void HeapCreat(Heap* hp, int* b, int n) //建堆(堆的创建)
{
	assert(hp && b); //断言,不能对空指针进行加、减、解引用操作

	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);  //malloc动态开辟数组空间 
	if (hp->a == NULL)  //malloc动态开辟失败
	{
		perror("malloc failed");
		exit(-1);  //异常退出,终止程序,(非0值表示不正常退出,0表示正常退出)
	}

	memcpy(hp->a, b, sizeof(HPDataType) * n);  //此处需要将已知的数组建成堆,将数组中所有值拷贝给动态开辟的数组
	hp->size = hp->capacity = n;  

	for (int i = (n-2)/2; i >= 0; i--) //向下调整法建堆,从倒数第一个非叶子节点开始调整,层数依次向上,每层从右到左遍历每个节点,每个节点都向下调整
		AdjustDown(hp->a, n, i);
}

void HeapPush(Heap* hp, HPDataType x)  //向堆中插入一个元素
{
	assert(hp); //断言,不能对空指针进行加、减、解引用操作

	if (hp->size == hp->capacity) //空间满了,不能进行插入数据,如需插入数据,需要realloc进行扩容
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2; //新容量
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity); 
		if (tmp == NULL) //realloc开辟失败
		{
			perror("realloc failed");
			exit(-1);
		}
		hp->a = tmp;  //realloc开辟成功
		hp->capacity = newcapacity;  //容量进行更新为新容量
	}

	hp->a[hp->size] = x; //在末尾插入一个元素
	hp->size++;

	AdjustUp(hp->a, hp->size - 1); //在将新插入的元素进行向上调整形成堆
}

void HeapDestory(Heap* hp)  //销毁
{
	assert(hp);//断言,不能对空指针进行加、减、解引用操作

	free(hp->a); //释放malloc动态开辟的空间
	hp->a = NULL; //防止该空间被其他变量存储了该地址,通过该地址访问此空间,不能访问已经释放的空间,会造成野指针
	hp->size = hp->capacity = 0;
}

void HeapPop(Heap* hp)  //删除堆顶元素
{
	assert(hp); //断言,不能对空指针进行加、减、解引用操作

	hp->a[0] = hp->a[hp->size-1]; //将最后一个元素覆盖堆顶元素
	hp->size--; 

	AdjustDown(hp->a, hp->size, 0); //在将堆顶元素进行向下调整形成堆
}

HPDataType HeapTop(Heap* hp) //获取堆顶元素
{
	return hp->a[0];  
}

int HeapSize(Heap* hp)  //获取堆中有效节点的总个数
{
	return hp->size;
}

int HeapEmpty(Heap* hp)  //判断堆是否为空,为空,则为true,不为空,则为false
{
	if (hp->size > 0)  return 0;
	else return 1;
}
 
void HeapPrint(Heap* hp) //打印堆中节点的值
{
	assert(hp);

	for (int i = 0; i < hp->size; i++)  //遍历堆中元素(通过数组的下标来遍历完全二叉树中的每个节点)
		printf("%d ", hp->a[i]);
	printf("\n");
}

Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include"Heap.h"

int main()
{
	Heap hp;
	int b[4] = { 3, 2, 1 ,4};
	int n = sizeof(b) / sizeof(int);
	HeapCreat(&hp, b, n);
    HeapPrint(&hp);

	for (int i = 0; i < 5; i++)
	{
		int x;
		scanf("%d", &x);
		HeapPush(&hp, x);
	}
	HeapPrint(&hp);

	HeapPop(&hp);
	HeapPrint(&hp);
	
	printf("%d\n", HeapTop(&hp));
    printf("%d\n", HeapSize(&hp));
	if (HeapEmpty(&hp))
		printf("YES\n");
	else
		printf("NO\n");

	HeapDestory(&hp);
	return 0;
}

在这里插入图片描述

建堆时间复杂度的证明

在这里插入图片描述

堆排序

注意:在进行堆排序建堆时:升序,建大堆、 降序,建小堆

原因:堆排序是为了对数组进行排序,不是进行打印数组,便于进行其他一系列操作。

升序,如果建小堆,只可以第一次获得最小的数,若要将剩余的元素进行排升序,只能将剩余的元素看成个堆,但各个元素对应的节点之间的关系已全部打乱,需要将剩余的元素重新建成堆,代价太大,时间复杂度为O(n*longn)

排升序,建大堆,将第一个最大的数与最后一个元素进行交换,个数减1,在从剩余的n-1个找出次大的数,在与最后一个元素交换,个数减1,如此反复,时间复杂度为O(nlong)

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void swap(int* p1, int* p2) //传址调用,形参的改变会影响实参的改变
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void down(int* a, int n, int parent) //向下调整法建堆,建大堆
{
	int child = parent * 2 + 1; //左孩纸节点
	while (child < n) //边界
	{
		if (child + 1 < n && a[child] < a[child + 1])//找到左孩纸与右孩纸节点中的最大值
			swap(&a[child], &a[child + 1]);
         
		if(a[parent] < a[child]) //建大堆
			swap(&a[child], &a[parent]);

		parent = child; //循环处理
		child = parent * 2 + 1;
	}
}

void Heapsort(int* a, int n) //堆排序,对数组进行排序,升序,建大堆
{
	for (int i = (n - 2) / 2; i >= 0; i--) //向下调整法建堆,从下标为(i-1-1)/2的节点开始
		down(a, n, i);

	int end = n - 1; //最后一个元素所对应的下标值
	while (end > 0)
	{
		swap(&a[0], &a[end]); //堆中最大的树与最后一个树进行交换
		down(a, end, 0); //将剩余的n-1个元素建大堆
		end--;
	}

	for (int i = 0; i < 6; i++) 
		printf("%d ", a[i]);

}

int main()
{
	int a[6];

	for (int i = 0; i < 6; i++)
		scanf("%d", &a[i]);

	Heapsort(a, sizeof(a) / sizeof(int)); //堆排序

	return 0;
}

在这里插入图片描述

TopK问题

TopK问题:

求数据中的前k个大的数或者前k个小的数,该数据个数的范围非常的大,一般情况是建个堆,但在内存中不能一次性将所有数据全部加载到内存中,此时考虑将数据存入文件中,即:在文件中找TopK问题,实际应用场景:世界前500名富豪,游戏中前100名活跃的玩家等。

在文件中找前K个大的数
1.将所有数据先存入文件中去,在从文件中读取,建成前K个数小堆
2.在依次将文件中剩余的数据读取,每读取一个数,分别与堆中第一个树进行比较,比它大,两数据值进行交换,该数往下沉建成小堆,如此反复,最终堆中的K个数为文件中最大的前K个数。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>  //malloc, srand
#include<time.h>  //time(时间戳)

void swap(int* p1, int* p2)  //传址调用
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDown(int* a, int n, int parent)  //(使用前提:根的左、右子树均为堆)向下调整,从最后一个非叶子节点开始,数据从下往上依次向下调整形成堆
{
	int child = parent * 2 + 1;  //孩纸节点,初始化左孩子值比右孩纸值小
	while (child < n) //
	{
		if (child + 1 < n && a[child] > a[child + 1]) //左、右孩纸值进行比较,确保右孩纸存在,且左孩子值比右孩纸值大
			swap(&a[child], &a[child + 1]);  //交换,此时child节点中的值为孩纸节点的最小值
		if (a[child] < a[parent])  //左孩子值与父节点值进行比较
			swap(&a[child], &a[parent]);  ///交换,形成小堆

		parent = child;  //递归
		child = parent * 2 + 1;
	}
}

void CreatNData()  //在文件中创造数据
{
	int n = 100000;
	const char* file = "data.txt";  
	FILE* fin = fopen(file, "w");  //以写的方式将文件打开
	if (fin == NULL)  //文件打开失败
	{
		perror("fopen failed");
		exit(-1); //异常退出,终止程序(负数)
	}
	for (int i = 0; i < n; i++) //创造n个随机数
	{
		int x = rand() % 10000; //数据值得范围为0~9999
		fprintf(fin, "%d\n", x); //将n个树写到文件中("%d\n",每个数据占一行)
	}

	fclose(fin);  //关闭文件
}

/*思路:一、从文件中读出k个数据放在数组中(fscanf,有格式),将k个数建成小堆(不可以是大堆,最大的数据会堵在堆顶不下去)
*       二、依次将文件中剩余的n-k个数据读取过来,与堆顶进行比较,大于,则往下沉
*       此时堆中剩余的k个数为最大的前k个数(且为升序)
*/
void HeapTopK(int k)  //topk
{
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r"); //以读的方式将文件打开
	if (file == NULL) //文件打开失败
	{
		perror("fopen failed");
		exit(-1);  //异常退出,终止程序(负数)
	}
	//一、从文件中读出k个数据放在数组中(fscanf,有格式),将k个数建成小堆(不可以是大堆,最大的数据会堵在堆顶不下去)
	int* mink = (int*)malloc(sizeof(int) * k); //malloc创造一个动态数组用来存储文件中的前k个数
	if (mink == NULL)  //malloc开辟失败
	{
		perror("malloc failed");
		exit(-1);//异常退出,终止程序(负数)
	}
	
	for (int i = 0; i < k; i++)  //从文件中读出k个数据
		fscanf(fout, "%d", &mink[i]); 
	for (int i = (k-2)/2; i >= 0; i--)//将k个数建成小堆
		AdjustDown(mink, k, i);

	// 二、依次将文件中剩余的n - k个数据读取过来,与堆顶进行比较,大于,则往下沉
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)//将文件中剩余的n - k个数据读取
	{ //fscanf返回值为读取的个数,读取失败或者读取为NULL,则返回EOF(~EOF(-1)== 0)
		if (x > mink[0]) //与堆顶进行比较,大于,则往下沉
			mink[0] = x;
		AdjustDown(mink, k, 0);
	}

	for (int i = 0; i < k; i++)
		printf("%d ", mink[i]);

	free(mink); //释放malloc动态开辟的空间
	mink = NULL;
	fclose(fout); //关闭文件
}

int main()
{
	srand((unsigned int)time(NULL)); //生成随机数srand~time~rand
	//CreatNData(); //创造数据

	int k;
	scanf("%d", &k);  //topk
	HeapTopK(k);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二叉树链式结构的实现

创建二叉树

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* i) //由前序遍历将数组中的值创建二叉树
{ 
	if (a[*i] == '#') // '#'代表空节点
	{
		(*i)++; //数组向后走一位,构建下一个节点
		return NULL; 
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode)); //malloc动态开辟内存,创建新节点
	if (root == NULL) //malloc动态开辟失败
	{
		perror("malloc failed"); 
		exit(-1); //终止程序,异常退出,0表示正常退出,非0表示异常退出
	}
	root->val = a[*i]; //数组中值不为空,将该值赋给新节点
	(*i)++; //数组向后走一位,构建下一个节点

	//该节点的左、右节点均创建完成,该节点在其左、右节点进行链接
	root->left = BinaryTreeCreate(a, n, i); //递归处理创建左节点,左节点遇到空,递归结束
	root->right = BinaryTreeCreate(a, n, i); //递归处理创建右节点,右节点遇到空,递归结束

	return root; //返回树的根节点
}

在这里插入图片描述

前序遍历及其实现

void BinaryTreePrevOrder(BTNode* root)  //前序遍历,根、左、右
{
	if (root == NULL) //空树
		return ;

	printf("%c ", root->val); //根
	BinaryTreePrevOrder(root->left);  //递归处理左
	BinaryTreePrevOrder(root->right); //递归处理右
}

在这里插入图片描述

中序遍历及其实现

void BinaryTreeInOrder(BTNode* root) //中序遍历,左、根、右
{
	if (root == NULL)  //空树
		return;
	
	BinaryTreeInOrder(root->left);  //递归处理左
	printf("%c ", root->val);  //根
	BinaryTreeInOrder(root->right);  //递归处理右
}

在这里插入图片描述

后序遍历及其实现

void BinaryTreePostOrder(BTNode* root) //后序遍历,左、右、根
{
	if (root == NULL)   //空树
		return;

	BinaryTreePostOrder(root->left);  //递归处理左
	BinaryTreePostOrder(root->right);   //递归处理右
	printf("%c ", root->val);   //根
}

销毁二叉树

/*采用后序遍历,不可采用前序遍历,原因:销毁根节点之前需要存储左子树的根,便于可以找到左子树,也需要存储右子树的根,便于可以找到右子树*/
void BinaryTreeDestory(BTNode* root)  //销毁,后序遍历
{
	if (root == NULL) //空树,未动态开辟任何节点
		return;

	BinaryTreeDestory(root->left); //递归处理左
	BinaryTreeDestory(root->right); //递归处理右
	free(root);  //销毁根
}

求二叉树的高度

int BinaryTreeHeight(BTNode* root) //树的高度
{
	if (root == NULL)  //空树
		return 0;

	int leftheight = BinaryTreeHeight(root->left); //左子树的高度
	int rightheight = BinaryTreeHeight(root->right);   //右子树的高度

	return leftheight > rightheight ? leftheight + 1 : rightheight + 1; //找出左、右子树高度大的树+根(+1)
}

求二叉树总节点个数

int BinaryTreeSize(BTNode* root) //树的总节点个数,分治法、递归法(将其分为根、左子树、右子树,对应的子树又可以分成根、左、右子树)
{
	if (root == NULL) //空树
		return 0;
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1; //左子树的节点+右子树的节点+根节点(+1)
}

求二叉树叶子节点个数

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); //左子树叶子节点个数+右子树叶子节点个数
}

求二叉树第k层节点个数

int BinaryTreeLevelKSize(BTNode* root, int k) //树中第k层节点的总个数,将第k层转换为1层,将k-1层转换为第2层..直到k==1,则为第k层
{
	if (root == NULL)  //空树
		return 0;
	if (k == 1) //第k层
		return 1; 
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1); 
}

二叉树查找值为x的节点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) //在树中查找是否存在值为x的节点
{
	if (root == NULL)  //空树
		return 0;

	if (root->val == x) //找到了
		return root;

	BTNode* ret = NULL; 
	ret = BinaryTreeFind(root->left, x); //递归左子树
	if (ret)   //若左子树找到了直接返回
		return ret;
	ret = BinaryTreeFind(root->right, x); //左子树找不到,在找右子树
	if (ret)  //右子树找到了直接返回
		return ret;

	return NULL;  //找不到
}

二叉树总代码实现

BinaryTree.h
#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h> //malloc
#include<string.h> //strlen

typedef char BTDataType;  //二叉树节点值的类型,适用于任意数据类型

typedef struct BinaryTreeNode //树中的节点
{
	struct BinaryTreeNode* left; //指针域:左孩纸
	struct BinaryTreeNode* right;        //右孩纸
	BTDataType val;  //数据域
}BTNode;

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* i); //由前序遍历将数组中的值创建二叉树

void BinaryTreePrevOrder(BTNode* root);//前序遍历,根、左、右

void BinaryTreeInOrder(BTNode* root); //中序遍历,左、根、右

void BinaryTreePostOrder(BTNode* root); //后序遍历,左、右、根

void BinaryTreeDestory(BTNode *root); //销毁

int BinaryTreeHeight(BTNode* root); //树的高度

int BinaryTreeSize(BTNode* root); //树的总节点个数

int BinaryTreeLeafSize(BTNode* root); //树中叶子节点的个数

int BinaryTreeLevelKSize(BTNode* root, int k); //树中第k层节点的总个数

BTNode* BinaryTreeFind(BTNode* root, BTDataType x); //在树中查找是否存在值为x的节点

BinaryTree.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"BinaryTree.h"

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* i) //由前序遍历将数组中的值创建二叉树
{ 
	if (a[*i] == '#') // '#'代表空节点
	{
		(*i)++; //数组向后走一位,构建下一个节点
		return NULL; 
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode)); //malloc动态开辟内存,创建新节点
	if (root == NULL) //malloc动态开辟失败
	{
		perror("malloc failed"); 
		exit(-1); //终止程序,异常退出,0表示正常退出,非0表示异常退出
	}
	root->val = a[*i]; //数组中值不为空,将该值赋给新节点
	(*i)++; //数组向后走一位,构建下一个节点

	//该节点的左、右节点均创建完成,该节点在其左、右节点进行链接
	root->left = BinaryTreeCreate(a, n, i); //递归处理创建左节点,左节点遇到空,递归结束
	root->right = BinaryTreeCreate(a, n, i); //递归处理创建右节点,右节点遇到空,递归结束

	return root; //返回树的根节点
}

void BinaryTreePrevOrder(BTNode* root)  //前序遍历,根、左、右
{
	if (root == NULL) //空树
		return ;

	printf("%c ", root->val); //根
	BinaryTreePrevOrder(root->left);  //递归处理左
	BinaryTreePrevOrder(root->right); //递归处理右
}

void BinaryTreeInOrder(BTNode* root) //中序遍历,左、根、右
{
	if (root == NULL)  //空树
		return;
	
	BinaryTreeInOrder(root->left);  //递归处理左
	printf("%c ", root->val);  //根
	BinaryTreeInOrder(root->right);  //递归处理右
}

void BinaryTreePostOrder(BTNode* root) //后序遍历,左、右、根
{
	if (root == NULL)   //空树
		return;

	BinaryTreePostOrder(root->left);  //递归处理左
	BinaryTreePostOrder(root->right);   //递归处理右
	printf("%c ", root->val);   //根
}
/*采用后序遍历,不可采用前序遍历,原因:销毁根节点之前需要存储左子树的根,便于可以找到左子树,也需要存储右子树的根,便于可以找到右子树*/
void BinaryTreeDestory(BTNode* root)  //销毁,后序遍历
{
	if (root == NULL) //空树,未动态开辟任何节点
		return;

	BinaryTreeDestory(root->left); //递归处理左
	BinaryTreeDestory(root->right); //递归处理右
	free(root);  //销毁根
}

int BinaryTreeSize(BTNode* root) //树的总节点个数,分治法、递归法(将其分为根、左子树、右子树,对应的子树又可以分成根、左、右子树)
{
	if (root == NULL) //空树
		return 0;
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1; //左子树的节点+右子树的节点+根节点(+1)
}

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); //左子树叶子节点个数+右子树叶子节点个数
}

int BinaryTreeLevelKSize(BTNode* root, int k) //树中第k层节点的总个数,将第k层转换为1层,将k-1层转换为第2层..直到k==1,则为第k层
{
	if (root == NULL)  //空树
		return 0;
	if (k == 1) //第k层
		return 1; 
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1); 
}

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) //在树中查找是否存在值为x的节点
{
	if (root == NULL)  //空树
		return 0;

	if (root->val == x) //找到了
		return root;

	BTNode* ret = NULL; 
	ret = BinaryTreeFind(root->left, x); //递归左子树
	if (ret)   //若左子树找到了直接返回
		return ret;
	ret = BinaryTreeFind(root->right, x); //左子树找不到,在找右子树
	if (ret)  //右子树找到了直接返回
		return ret;

	return NULL;  //找不到
}

int BinaryTreeHeight(BTNode* root) //树的高度
{
	if (root == NULL)  //空树
		return 0;

	int leftheight = BinaryTreeHeight(root->left); //左子树的高度
	int rightheight = BinaryTreeHeight(root->right);   //右子树的高度

	return leftheight > rightheight ? leftheight + 1 : rightheight + 1; //找出左、右子树高度大的树+根(+1)
}
Test.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"BinaryTree.h"

int main()
{
	BTDataType arr[] = "ABD##E#H##CF##G##"; 
	int n = strlen(arr);
	int j = 0;
	BTNode* root = BinaryTreeCreate(arr, n, &j);  //由前序遍历将数组中的值创建二叉树

	BinaryTreePrevOrder(root); //前序遍历
	printf("\n");
	BinaryTreeInOrder(root); //中序遍历
	printf("\n");
	BinaryTreePostOrder(root); //后序遍历
	printf("\n");
	printf("%d\n", BinaryTreeHeight(root));  //树的高度
	printf("%d\n",BinaryTreeSize(root)); //树的总节点个数
	printf("%d\n", BinaryTreeLeafSize(root)); //树中叶子节点的个数
	printf("%d\n", BinaryTreeLevelKSize(root, 3)); //树中第k层节点的总个数
	BTNode* ret = BinaryTreeFind(root, 'F');   //在树中查找是否存在值为x的节点
	if (ret != NULL)  
		printf("找到了\n"); 
	else
		printf("找不到\n");
	BinaryTreeDestory(root);  //销毁
	return 0;
} 

在这里插入图片描述

层序遍历

在这里插入图片描述

void BinaryTreeLevelOrder(BTNode* root)  //层次遍历,用队列实现,上一层带下一层,当上一层节点全部出队,则下一层所有节点均入队了
{
	Queue plist;
	QueueInit(&plist); //初始化队列,队列中用于存储树中的节点

	if (root == NULL) //空树
		return;
	QueuePush(&plist, root);  //将根插入队列中
	while (!QueueEmpty(&plist))  //
	{
	    BTNode* front = QueueFront(&plist); //获取队头元素
		printf("%d ", front->val); //打印树中节点的值
		if (front->left)  //左孩纸不为空,空节点不能入队
			QueuePush(&plist, front->left);   //将该节点的左孩子入队
		if (front->right)  //右孩纸不为空,空节点不能入队
			QueuePush(&plist, front->right);  //将该节点的右孩子入队

		QueuePop(&plist);  //删除队头元素
	}

	printf("\n");
	QueueDestroy(&plist); //二叉树的销毁
}

判断是否为二叉树

在这里插入图片描述

int BinaryTreeComplete(BTNode* root)  //判断其是否为二叉树,最后一层非空节点从左到右连续分布
{
	Queue plist;
	QueueInit(&plist); //初始化队列,队列中用于存储树中的节点

	if (root == NULL) //空树
		return;
	QueuePush(&plist, root);  //将根插入队列中
	while (!QueueEmpty(&plist)) //
	{
		BTNode* front = QueueFront(&plist);  //获取队头元素
		if (front == NULL) //队头为空节点
			break;
		QueuePush(&plist, front->left);  //左孩纸入队,空节点也需入队
		QueuePush(&plist, front->right);  //右孩纸入队,空节点也需入队

		QueuePop(&plist); //删除非空节点
	}
	while (!QueueEmpty(&plist))  //
	{
		BTNode* front = QueueFront(&plist);  //获取队头元素
		QueuePop(&plist); //删除队头节点
		if (front != NULL)  //队头为非空节点
		{
			QueueDestroy(&plist); 
			return false; //不是完全二叉树
		}
		QueuePop(&plist); //删除队头节点
	}
	QueueDestroy(&plist); //销毁
	return true; //是完全二叉树
	printf("\n");
}

总代码

Tset.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"Queue.h"

/*void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePush(&plist, 4);
	QueuePush(&plist, 5);
	QueuePush(&plist, 6);
	while (!QueueEmpty(&plist))
	{
		printf("%d ", QueueFront(&plist));
		QueuePop(&plist);
	}
	printf("\n");
	QueueDestroy(&plist);
}

int main()
{
	test();  //测试队列,先进先出
	return 0;
}
*/
BTNode* BuyNode(int x)
{
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->left = NULL;
	root->right = NULL;
	root->val = x;
	return root;
}

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue plist;
	QueueInit(&plist);

	if (root == NULL)
		return;
	QueuePush(&plist, root);
	while (!QueueEmpty(&plist))
	{
	    BTNode* front = QueueFront(&plist);
		printf("%d ", front->val);
		if (front->left)
			QueuePush(&plist, front->left);
		if (front->right)
			QueuePush(&plist, front->right);

		QueuePop(&plist);
	}

	printf("\n");
	QueueDestroy(&plist);
}

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

	if (root == NULL)
		return;
	QueuePush(&plist, root);
	while (!QueueEmpty(&plist))
	{
		BTNode* front = QueueFront(&plist);
		if (front == NULL)
			break;
		QueuePush(&plist, front->left);
		QueuePush(&plist, front->right);

		QueuePop(&plist);
	}
	while (!QueueEmpty(&plist))
	{
		BTNode* front = QueueFront(&plist);
		QueuePop(&plist); //
		if (front != NULL)
		{
			QueueDestroy(&plist);
			return false;
		}
		QueuePop(&plist);
	}
	QueueDestroy(&plist);
	return true;
	printf("\n");
}

int main()
{

	BTNode* n1 = BuyNode(1);
	BTNode* n2 = BuyNode(6);
	BTNode* n3 = BuyNode(7);
	BTNode* n4 = BuyNode(2);
	BTNode* n5 = BuyNode(3);
	BTNode* n6 = BuyNode(4);
	BTNode* n7 = BuyNode(5);

	n1->left = n2;
	n1->right = n3;
	n2->left = n4;
	n2->right = n5;
	n3->left = n6;
	n3->right = n7;

	BinaryTreeLevelOrder(n1);
	if (BinaryTreeComplete(n1))
		printf("是完全二叉树\n");
	else
		printf("不是完全二叉树\n");

	return 0;
}

Queue.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"Queue.h"

void QueueInit(Queue* p)   //初始化
{
	assert(p);  //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	p->front = NULL;
	p->rear = NULL;
	p->size = 0;  
}

void QueuePush(Queue* p, QDataType x)   //入队
{
	assert(p);   //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	QNode* newnode=(QNode*)malloc(sizeof(QNode));  //malloc动态开辟新的节点
	if (newnode == NULL)  //开辟空间失败
	{
		perror("malloc");  //报错原因
		exit(-1);   //终止程序,异常结束
	}
	newnode->data = x; 
	newnode->next = NULL;
	if (p->rear == NULL)  //注意:头插(特殊处理),链表为空
	{
		p->front = p->rear = newnode;
	}
	else  //尾插 ,需要找尾节点
	{
		p->rear->next = newnode;  
		p->rear=p->rear->next;
	}
	p->size++;  //链表中有效元素个数加+
}

bool QueueEmpty(Queue* p)   //判断队列是否为空 
{
	assert(p);   //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	return p->front == NULL;  //为空,则为真,返回非0值,若不为空,为假,则返回0
}

void QueuePop(Queue* p)   //出队
{
	assert(p);   //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	assert(!QueueEmpty(p));  //断言,链表为空,则不能进行删除 

	if (p->front->next == NULL)  //注意:当链表中只剩一个元素,因为尾指针、头指针同时指向该节点,释放该节点,需要将尾指针、头指针都置成NULL,否则会造成野指针(指向已经被释放的空间)
	{
		free(p->front);
		p->front = p->rear = NULL;
	}

	else  //链表中剩余至少有1个元素
	{
		QNode* next = p->front->next;
		free(p->front);
		p->front = next;
	}
	p->size--;  //链表中有效元素个数加-
}

QDataType QueueFront(Queue* p)   //获取队头元素
{
	assert(p);  //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	assert(!QueueEmpty(p));   //断言,链表为空,则不能获取到队头的元素
	return p->front->data;  
}

QDataType QueueBack(Queue* p)   //获取队尾元素
{
	assert(p);   //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	assert(!QueueEmpty(p));    //断言,链表为空,则不能获取到队尾的元素
	return p->rear->data;
}

int QueueSize(Queue* p)   //获取队列中有效元素的个数
{
	assert(p);  //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	assert(!QueueEmpty(p));   //断言,链表为空,则不能获取到有效元素的总个数
	return p->size;
}

void QueueDestroy(Queue* p)   //销毁
{
	assert(p);   //断言,检查指针的有效性,防止对空指针进行解引用,加减操作
	while (p->front)  //遍历链表
	{
		QNode* next = p->front->next;
		free(p->front);
		p->front = next;
	}
	p->front = p->rear = NULL;
	p->size = 0;
}
Queue.h
#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>   //malloc
#include<assert.h>   //assert
#include<stdbool.h>  //bool类型

typedef struct BinaryTreeNode* QDataType;

typedef int BTDataType;

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

typedef struct QueueNode  //链式结构:表示队列
{
	QDataType data;   
	struct QueueNode* next;  
}QNode;

typedef struct Queue  //队列结构
{
	QNode* front;  //队头
	QNode* rear;   //队尾
	int size;  //有效元素个数
}Queue;

void QueueInit(Queue* p);  //初始化
void QueuePush(Queue* p, QDataType x);  //入队
void QueuePop(Queue* p);   //出队
QDataType QueueFront(Queue* p);  //获取队头元素
QDataType QueueBack(Queue* p);  //获取队尾元素
int QueueSize(Queue* p);   //获取队列中有效元素的个数
bool QueueEmpty(Queue* p);  //判断队列是否为空
void QueueDestroy(Queue* p);  //销毁

在这里插入图片描述
铁铁们,二叉数全面知识总结就到此结束啦,若博主有不好的地方,请指正,欢迎铁铁们留言,请动动你们的手给作者点个👍鼓励吧,你们的鼓励就是我的动力✨

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

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

相关文章

基于ENC28J60+uIP1.0+STM32的UDP Server实现,服务器主动发送数据的实现,几个关键的问题可算整明白了!

ENC28J60&#xff0c;是一款SPI接口的以太网PHYMAC芯片&#xff0c;实现以太网物理层和MAC层硬件通信。uIP是一个TCP/IP软件协议栈&#xff0c;实现TCP、UDP、ARP、ICMP等网络协议。STM32F103RCT6通过SPI接口与ENC28J60通讯&#xff0c;并移植uIP协议&#xff0c;实现一个小型的…

20230224_HDR-ISP_环境配置

https://github.com/JokerEyeAdas/HDR-ISP/tree/main 一、环境配置 *.VS2015编译不过&#xff1b;VS2017没问题 *.将红框部分复制到工程下面 *.添加头文件路径 *.添加原文件

【C#】Redis在net core下使用教程

系列文章 文章目录 系列文章前言一、Redis 简介1.1 Redis 优势1.2 Redis与其他key-value存储有什么不同&#xff1f; 二、Redis安装步骤2.1 下载链接2.2 安装测试 三、Redis修改帐户密码四、Redis写成Windows服务五、.net core - 使用CSRedisCore操作redis 前言 官方教程&…

【C语言】模拟实现内存函数

本篇文章目录 相关文章1. 模拟 memcpy 内存拷贝2. 模拟 memmove 内存移动 相关文章 【C语言】数据在内存中是以什么顺序存储的&#xff1f;【C语言】整数在内存中如何存储&#xff1f;又是如何进行计算使用的&#xff1f;【C语言】利用void*进行泛型编程【C语言】4.指针类型部…

相机有俯仰角时如何将像素坐标正确转换到其他坐标系

一般像素坐标系转相机坐标系都是默认相机是水平的&#xff0c;没有考虑相机有俯仰角的情况&#xff0c;大致的过程是&#xff1a;像素坐标系统-->图像坐标系-->相机坐标系 ->世界坐标系或雷达坐标系: 像素坐标系 像素坐标系&#xff08;u&#xff0c;v&#xff09;是…

【Java 基础篇】Java函数式接口详解

Java是一门强类型、面向对象的编程语言&#xff0c;但在Java 8引入了函数式编程的概念&#xff0c;这为我们提供了更多灵活的编程方式。函数式接口是函数式编程的核心概念之一&#xff0c;本文将详细介绍Java函数式接口的概念、用法以及一些实际应用。 什么是函数式接口&#…

JUC第八讲:Condition源码分析

JUC第八讲&#xff1a;Condition源码分析 本文是JUC第八讲&#xff0c;Condition详解。任意一个Java对象&#xff0c;都拥有一组监视器方法&#xff08;定义在java.lang.Object上&#xff09;&#xff0c;主要包括 wait()、wait(long timeout)、notify()以及notifyAll()方法&am…

【LeetCode-中等题】513. 找树左下角的值

文章目录 题目方法一&#xff1a;前序递归方法二&#xff1a;层序遍历 题目 方法一&#xff1a;前序递归 在递归遍历到叶子结点时&#xff0c;对比此时的节点深度&#xff0c;若当前节点深度大于当前最大深度&#xff0c;就更新value值&#xff0c;最后记录下的value即为最下最…

elementui 菜单选中优化

/** 父级菜单悬浮样式**/ .el-submenu__title:hover {color:#1890ff!important; } /** 父级菜单箭头悬浮样式**/ .el-submenu__title:hover>.el-submenu__icon-arrow{font-size: 13px!important;} /** 子菜单悬浮样式**/ .el-menu-item:hover{color:#1890ff!important; } /*…

Linux操作系统基础详解,计算机专业必看!

目录 Linux操作系统 Linux 简介 Linux 接口 Linux 组成部分 Shell Linux 应用程序 Linux 内核结构 Linux 进程和线程 基本概念 Linux 进程间通信 Linux 中进程管理系统调用 Linux 进程和线程的实现 Linux 调度 Linux 启动 Linux 内存管理 基本概念 Linux 内存…

【owt】 Intel® Media SDK for Windows: MSDK2021R1

https://www.intel.com/content/www/us/en/developer/articles/tool/media-sdk.html官方网不提供下载了: 2021地址 直接下载: MSDK2021R1.exe老版本 Intel Media SDK(Windows版本) 大神的介绍:owt-client-native 需要 https://github.com/open-webrtc-toolkit/owt-client…

spring security auth2.0实现

OAuth 2.0 的认证/授权流程 jwt只是认证中的一步 4中角色 资源拥有者&#xff08;resource owner&#xff09;、客户端&#xff08;client 第三方&#xff09;、授权服务器&#xff08;authorization server&#xff09;和资源服务器&#xff08;resource server&#xff09;。…

vue 使用cornerstone解析 .dcm 文件

// 首先下载依赖 npm install --save cornerstone-core cornerstone-math cornerstone-tools hammerjs cornerstone-web-image-loader 下载之后再package.json中可以看到最后图片的依赖// 下面是完成的组件代码 <template><div id"dicom_canvas" refdicom_c…

FL Studio21.1无限试用版体验新功能变化介绍

许多刚刚接触音乐创作的新朋友&#xff0c;通过各种渠道了解到FL Studio&#xff0c;但并不知道我们的历史以及在音乐创作方面所产生的影响&#xff0c;今天分享一篇来自coco玛奇朵博主Rio的深度科普文章&#xff0c;相信对新人会有很大启发。 FL Studio 21.1 通过钢琴卷中的音…

C++:类中的静态成员函数以及静态成员变量

一、静态成员变量 静态成员&#xff1a;在类定义中&#xff0c;它的成员&#xff08;包括成员变量和成员函数&#xff09;&#xff0c;这些成员可以用关键字static声明为静态的&#xff0c;称为静态成员。 静态成员变量需要在类外分配空间&#xff0c;static 成员变量是在初始…

速码!!BGP最全学习笔记:IBGP和EBGP基本配置

实验1&#xff1a;配置IBGP和EBGP 实验目的 熟悉IBGP和EBGP的应用场景掌握IBGP和EBGP的配置方法 实验拓扑 想要华为数通配套实验拓扑和配置笔记的朋友们点赞关注&#xff0c;评论区留下邮箱发给你! 实验步骤 1.IP地址的配置 R1的配置 <Huawei>system-view …

基于单片机火灾报警器仿真设计

一、系统方案 1、本设计采用51单片机作为主控器。 2、DS18B20采集温度值送到液晶1602显示。 3、MQ2采集烟雾值&#xff0c;送到液晶1602显示。 4、按键设置温度报警值&#xff0c;大于报警值&#xff0c;声光报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计…

微信小程序快速入门01(含案例)

文章目录 前言一、组件1.常用视图容器类组件viewscroll-viewswiper、swiper-item 2.text、rich-text3.其他常用组件buttonimagenavigator 二、小程序API三、数据绑定1.定义页面数据2.绑定数据 四、事件绑定1.什么是事件2.小程序中常用的事件3.事件对象 的属性列表target和curre…

Seata--分布式事务

1 分布式事务基础 1.1 事务 事务指的就是一个操作单元&#xff0c;在这个操作单元中的所有操作最终要保持一致的行为&#xff0c;要么所有操作都成功&#xff0c;要么所有的操作都被撤销。简单地说&#xff0c;事务提供一种“要么什么都不做&#xff0c;要么做全套”机制。 1…

代码随想录算法训练营day60|84.柱状图中最大的矩形 |完结撒花~

84.柱状图中最大的矩形 力扣题目链接 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 1 < heights.length <10^5 0 < heights[i] < 10^…