数据结构—— 再探二叉树

news2024/9/20 22:54:19

1. TOP-K问题

TOP-K问题:求数据结合中前K个最大或者最小的数据

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等

思路:

1. 用数据集合中前K个数据来建堆:
                                                            a.  前k个最大的数据,则建小堆
                                                            b.  前k个最小的数据,则建大堆

                                          一般推荐建小堆   

2. 用剩余的数据依次与堆顶的数据来比较,如果比堆顶的数据就替换堆顶数据(覆盖位置,向下调整)

将剩余的数据依次与堆顶元素比完之后,堆中剩余的数据就是所求的前K个最小或最大的元素

通俗来讲就是:用前K个数建立一个小堆,然后剩下的数依次遍历和堆顶最小的数据比较,如果比堆顶的数据大,就把大的数赋给堆顶,再向下调整,最后堆里剩下的K个数就是最大的K个 ,因为小堆的堆顶一定是最小的数,只要随便拿个数比他大就交换他俩,大的那个数进入堆后再建小堆,大的数就沉在最下面,所以最后堆里面一定是K个最大的数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
 
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;
		}
	}
}	
 
 
void CreateNData()
{
	int n = 100000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (size_t i = 0; i < n; i++) {
		int x = rand() % 10000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}
 
void PrintTopK(int k)
{
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc error");
		return;
	}
 
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}
	// 建小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}
 
	int val = 0;
	while (!feof(fout))
	{
		fscanf(fout, "%d", &val);
		if (val > kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k, 0);
		}
	}
 
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
	fclose(fout);
}
 
int main()
{
	CreateNData();
	PrintTopK(5);
	return 0;
}


2. 二叉树(BinaryTree)

2.1 介绍

前面我们的完全二叉树和满二叉树是使用数组来存储,如果不是完全二叉树和满二叉树就不适合使用数组存储,更适合链式结构来存储

二叉树是:

                1. 空树

                2. 非空:根结点,根结点的左子树、根结点的右子树组成的

                3.二叉树是递归结构

二叉树是整棵树拆分为根和子树,然后拆分过的子树再作为根继续拆分 ,一直这样持续至最后一个没有子树的根


在遍历之前先手搓一个二叉树的基础模板(Creat Binary Tree):

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<stdbool.h>


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

BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->val = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

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

	//node1的左和右指向node2和node4
	node1->left = node2;
	node1->right = node4;

	//node1的左指向node3
	node2->left = node3;

	//node4的左和右指向node5和node6
	node4->left = node5;
	node4->right = node6;

	return node1;
}



int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();

	return 0;
}

2.2 二叉树的遍历

前序遍历 (Prer Order)   

访问顺序:(根  左子树 右子树  )任何一棵树的访问,都要符合前序,被拆成根   左子树  右子树

访问顺序为:

 

//前序
void PrerOrder(BTNode* root)
{
	//如果根为空
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
        //打印根的数
		printf("%d\n", root->val);
        //打印完之后访问根的左子树和右子树
		PrerOrder(root->left);
		PrerOrder(root->right);
	}
}

                        代码执行完成之后就进行递归调用,一直到遇到空根结束

int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();
	PrerOrder(root);
	printf("\n");
	return 0;
}


中序遍历(In Order)

访问顺序:(左子树   根  右子树 )任何一棵树的访问,都要符合中序,被拆成左子树   根  右子树

访问顺序为:


//中序
void InOrder(BTNode* root)
{
	//如果根为空
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
		//先访问左子树
		InOrder(root->left);
		//打印根的数
		printf("%d\n", root->val);
		//打印完之后访问根的右子树
		InOrder(root->right);
	}
}
int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();
	
	//中序
	//InOrder(root);

	printf("\n");
	return 0;
}


后序遍历(Post Order)

访问顺序:(左子树  右子树   根)任何一棵树的访问,都要符合后序,被拆成左子树  右子树   根

访问顺序为: 

//后序
void PostOrder(BTNode* root)
{
	//如果根为空
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
		//先访问左子树和右子树
		PostOrder(root->left);			
		PostOrder(root->right);
		//再打印根的数
		printf("%d\n", root->val);
	}
}

int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();
	//前序
	//PrerOrder(root);
	//中序
	//InOrder(root);
	//后序
	PostOrder(root);
	printf("\n");
	return 0;
}


求二叉树节点的个数 (Tree Size):

节点个数:

                1.如果为空,则为0

                2.如果不为空,则是左子树 + 右子树 + 1

//求整棵树节点的个数
int TreeSize(BTNode* root)
{
	//如果为空则返回0,不为空则返回左子树加右子树加1
	return root == NULL ? 0 :TreeSize(root->left) + TreeSize(root->right) + 1;
}
int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();
	//前序
	//PrerOrder(root);
	//中序
	//InOrder(root);
	//后序
	//PostOrder(root);
	//printf("\n");
    
    //求整棵树节点的个数
	printf("TreeSize:%d\n", TreeSize(root));
	
	return 0;
}


获取叶子节点个数(Tree Leaf Size):

叶子:就是度为0,不包括任何子节点,通俗来讲就是没有孩子的节点

//获取叶子节点个数
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
		return 1;

	return TreeLeafSize(root->left)
		+ TreeLeafSize(root->right);
}
int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();
	//前序
	//PrerOrder(root);
	//中序
	//InOrder(root);
	//后序
	//PostOrder(root);
	//printf("\n");
    //求整棵树节点的个数
	//printf("TreeSize:%d\n", TreeSize(root));

    //获取叶子节点个数
	printf("TreeLeafSize:%d\n", TreeLeafSize(root));
	
	return 0;
}


二叉树的高度 (Tree Height):

                        二叉树的高度是有几层高度就是几

//二叉树的高度
int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	//使用两个变量记录下来,避免重复计算
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ?
		leftHeight + 1 : rightHeight + 1;
}
int main()
{
	//root:根   CreatBinaryTree:创建二叉树
	BTNode* root = CreatBinaryTree();
	//前序
	//PrerOrder(root);
	//中序
	//InOrder(root);
	//后序
	//PostOrder(root);
	//printf("\n");
	 //求整棵树节点的个数
	//printf("TreeSize:%d\n", TreeSize(root));

    //获取叶子节点个数
	//printf("TreeLeafSize:%d\n", TreeLeafSize(root));

    //二叉树的高度
	printf("TreeHeight:%d\n", TreeHeight(root));

	return 0;
}

 二叉树第k层结点个数:

// 二叉树第k层节点个数
int TreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

	// 子问题
	return TreeLevelKSize(root->left, k - 1)
		+ TreeLevelKSize(root->right, k - 1);
}


二叉树查找值为x的结点:

// 二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->val == x)
		return root;

	BTNode* ret1 = TreeFind(root->left, x);
    //如果找到了直接返回数值,没有找到直接返回空
	if (ret1)
		return ret1;

	BTNode* ret2 = TreeFind(root->right, x);
    //如果找到了直接返回数值,没有找到直接返回空
	if (ret2)
		return ret2;

	return NULL;
}


                                                            休息一下,马上回来~                                                            

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

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

相关文章

easypoi模板导出word并且合并行

导出流程 引入依赖制作模板合并导出 引入依赖 <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.1.2</version> </dependency>制作模板 合并行是备注那一列&#xff0c;这一列…

UCIE-state machion

1.retrain/linkerror/active状态 状态转变都是从下往上。 &#xff08;1&#xff09;Retrain&#xff1a;multistack和raw model相互违背&#xff1b; &#xff08;2&#xff09;Linkerror&#xff1a;如果一个stack需要进入linkerror&#xff0c;表示链路已经存在问题&…

论文解读:从Dijkstra的On-the-Fly到Go的三色标记算法,并行垃圾回收的起源

我们经常听到关于垃圾回收的说法是&#xff0c;某种垃圾回收算法是一种特定语言特有的&#xff0c;容易理解成&#xff0c;垃圾回收的算法跟特定编程语言是绑定的&#xff0c;但是仔细想想&#xff0c;垃圾回收器是一种分配和管理内存的机制或者程序&#xff0c;内存管理跟语言…

微分方程(Blanchard Differential Equations 4th)中文版Section2.3

阻尼谐振子 在本节中,我们将描述一种解析技术,它适用于本书中最重要的模型之一——阻尼谐振子。这一二阶微分方程用于建模各种现象,如质量-弹簧系统、电路理论中的RLC电路,以及人体的血糖调节系统。 例如,考虑汽车的悬挂系统。它可以平滑崎岖道路上的颠簸,并帮助保持轮…

告别U盘:看医院如何挑选高效安全的文件摆渡系统

基于法规要求和自身安全管理需要&#xff0c;医院普遍使用网闸&#xff0c;将网络隔离为院内网、院外网。网络隔离后&#xff0c;医院各科室部门仍存在频繁的网间数据交换需求&#xff0c;需要文件摆渡系统进行内外网数据的安全交换。具体交换场景如下&#xff1a; 1.影像科&am…

iOS Native与JS通信:JSBridge

文章目录 一、简介二、JS 调用 Native1.使用 URL Schemea.UIWebViewb.WKWebView 2.使用 JavaScriptCore (iOS 7)3.使用 WKWebView 和 WKScriptMessageHandler (iOS 8) 三、Native 调用 JS1.使用 UIWebView2.使用 WKWebView3.使用 JavaScriptCore (iOS 7) 一、简介 对于移动应用…

江西学术会议:第五届计算机、大数据与人工智能国际会议

第五届计算机、大数据与人工智能国际会议(ICCBDAI 2024)将于2024年11月1日-3日在江西景德镇召开。本届会议由景德镇陶瓷大学主办&#xff0c;西安交通大学、暨南大学、南京邮电大学、景德镇学院、ELSP&#xff08;爱迩思出版社&#xff09;、ESBK国际学术交流中心、AC学术平台协…

ctfshow之web29~web51

目录 web29 题解&#xff1a; web30 web31 web32&#xff08;32~36&#xff09; web33 web34 web35 web36 web37 web38 web39 web40 web41 web42 &#xff08;42~51&#xff09; web43 web44 web45 web50 web51 web29 前瞻知识&#xff1a; isset() …

【sgCreateReadonlyForm】自定义小工具:敏捷开发→自动化生成只读表单代码片段脚本(无需列表展示数据,多用于查看某一条数据记录)

sgCreateReadonlyForm源码 <template><!-- 前往https://blog.csdn.net/qq_37860634/article/details/141389231 查看使用说明 --><div :class"$options.name"><div class"sg-head">只读表单生成工具<el-dropdown:show-timeo…

8.20 roles的基本用法+使用剧本安装nginx

安装nginx并更改其端口 创建目录 mkdir /etc/ansible/playbook 编辑配置文件 vim /etc/ansible/palybook/nginx.yml --- - hosts: s remote_user: root tasks: - name: 卸载httpd yu…

人工智能 | 结对编程助手GithubCopilot

简介 GitHub Copilot 是一款 AI 结对程序员&#xff0c;可帮助您更快、更少地编写代码。它从注释和代码中提取上下文&#xff0c;以立即建议单独的行和整个函数。GitHub Copilot 由 GitHub、OpenAI 和 Microsoft 开发的生成式 AI 模型提供支持。它可作为 Visual Studio Code、…

智慧水务平台:数智化驱动,‌实现管理全面升级!‌

智慧生产体系聚焦水务行业的生产环节,涵盖水源管理、水厂管理、生产调度、二次供水管理等各个环节。对各生产环节的实时生产数据和设备运行参数进行监测,并提供报警、日常运维、能耗分析、流程优化,为水务生产管理的成本压降、效率提升、安全保障、服务优化提供支撑。 智慧管网…

Echarts添加水印

如果直接说水印,很难在官方找到一些痕迹,但是换个词【纹理】就能找到了。水印就是一种特殊的纹理背景。 Echarts-backgroundColor backgroundColor 支持使用rgb(255,255,255),rgba(255,255,255,1),#fff等方式设置为纯色,也支持设置为渐变色和纹理填充,具体见option.colo…

哪个牌子的开放式耳机性价比高?五款地表最强机型推荐!

在我们的日常生活中&#xff0c;街道、地铁车厢或公交车等地方常常充满了噪音&#xff0c;这些杂音不仅可能扰乱心情&#xff0c;还可能对我们的听力造成潜在的伤害。在这样的环境下&#xff0c;如果想要享受音乐或追剧&#xff0c;同时又能保持对周围环境的警觉&#xff0c;开…

充电宝哪个品牌好?360度全方面测评热门款充电宝

在这个智能手机、平板电脑等移动设备普及的时代&#xff0c;充电宝已成为我们日常生活中不可或缺的伴侣。无论是在通勤途中、旅行出行&#xff0c;还是在户外运动时&#xff0c;充电宝都能为我们的设备提供源源不断的电力支持。然而&#xff0c;市场上充电宝品牌众多&#xff0…

c++开发,下载安装Boost库并检测是否安装成功

c开发&#xff0c;下载安装Boost库并检测是否安装成功 系统说明下载Boost库安装测试验证 系统说明 win10系统 下载Boost库 从官方网站下载&#xff0c;点击版本号 进去后选择windows系统的下载 安装 第1步 将下载后的压缩包解压到你想存储的文件夹中&#xff0c;比如我这里…

自主身份:Web3如何重新定义个人数据所有权

随着数字时代的快速发展&#xff0c;个人数据成为了一种新型的资产&#xff0c;深刻影响着我们的生活。然而&#xff0c;在Web2时代&#xff0c;个人数据往往被科技巨头所掌控&#xff0c;用户在享受互联网服务时&#xff0c;无意中失去了对自己数据的控制权。Web3的到来&#…

Java 调整字符串,验证码生成

package text7;public class ZiFanz {public static void main(String[] args) {//1.定义两个字符串String strA "abcde";String strB "deabc";//2.abcde->bcdea->cdeab->deabc旋转字符串//旋转并比较boolean result cheak(strA, strB);System…

时间序列分析中的特征提取

一、说明 在多变量时间序列分析期间&#xff0c;数据包含随时间推移测量的多个数据。为了管理模型性能&#xff0c;建议进行特征提取&#xff0c;以使模型的数据点更加紧凑。 二、时间序列的挑战 2.1 特征提取 仅选择“信息性”特征&#xff0c;这些特征在多变量时间序列分析…

【Java】了解线程 Thread 类的使用,如何创建、终止、等待一个线程,一文读懂不迷路

线程是什么 线程是操作系统中调度的基本单位&#xff0c;是比进程更小的执行单元。线程在进程内部运行&#xff0c;共享该进程的资源&#xff0c;如内存和文件句柄&#xff0c;但每个线程都有自己的执行栈和程序计数器。 线程的主要特点包括&#xff1a; 轻量级&#xff1a;…