数据结构 | 二叉树(基本概念、性质、遍历、C代码实现)

news2024/11/24 7:06:26

1.树的基本概念

树是一种 非线性 的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。
把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
有一个特殊的结点,称为根结点,根结点没有前驱结点 除根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm
其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
因此, 树是递归定义 的。
结点的度:一个结点含有的子树的个数称为该结点的度;
叶结点:度为0的结点称为叶结点;
分支结点:度不为0的结点; 
父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;
子结点:一个结点含有的子树的根结点称为该结点的子结点;
兄弟结点:具有相同父结点的结点互称为兄弟结点;
树的度:一棵树中,最大的结点的度称为树的度; 
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推;
树的高度或深度:树中结点的最大层次; 

2.二叉树的基本概念

一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 由一个根结点加上两棵别称为左子树和右子树的二叉树组成
注意:二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

2.1特殊的二叉树

满二叉树

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。

完全二叉树

特征:前n-1层都是满的,最后一层可以不满,但最后一层从左到右必须是连续的。
ps: 满二叉树是一种特殊的完全二叉树。

3.二叉树的性质

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. 对于完全二叉树:
双亲序号:(i-1)/2  (i为子节点序号)
左孩子序号:2i+1  (i为双亲结点序号)
右孩子序号:2i+2 (i为双亲结点序号)
练习:一个具有767个结点的完全二叉树,其叶子结点个数为()
A 383
B 384
C 385
D 386
答案:B
详解:设有度为2节点n2个, 度为1节点n1个,度为0节点n0个,
767=n2+n1+n0
n2=n0-1
由上面两式可得:
767=n1+2n0-1
768=n1+2n0
由于2n0必为偶数,768为偶数,可得:n1为偶数
且完全二叉树中n1只能是1或0
因此n1=0
768=2n0
n0=384

4 .二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
1. 顺序存储
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空 间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
2.链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是
链表中每个结点由三个域组成,数据域左右指针域,左右指针分别用来给出该结点左孩子和右孩子所
在的链结点的存储地址 。链式结构又分为二叉链和三叉链。本文主要针对二叉链。

5.二叉树的遍历

5.1 前序、中序以及后序遍历

1. 前序遍历——根节点 左子树 右子树
2. 中序遍历——左子树  根节点 右子树
3. 后序遍历——左子树 右子树  根节点

5.2 层序遍历

层序遍历:一层一层地往下遍历

6.二叉树代码实现

思路

前序/中序/后序遍历

递归思想:将当前的大问题拆解成小问题

以前序遍历为例:

当前问题——打印根,打印左子树,打印右子树

子问题——如图

递归返回条件——root==NULL

前序遍历代码

//前序遍历 根节点 左节点 右节点
void BinaryTreePrevOrder(BTNode* root) {
	if (root == NULL) {
		printf("N ");
		return;
	}

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

中序遍历代码

void BinaryTreeInOrder(BTNode* root) {
	if (root == NULL) {
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}

后序遍历代码

void BinaryTreePostOrder(BTNode* root) {
	if (root == NULL) {
		printf("N ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}

节点个数/叶子节点个数/树高/第k层叶子数

1.节点个数

递归思想:

情况1:空,0个

情况2:不为空,左子树+右子树+1

2.叶子节点个数

情况1:空,返回0

情况2:只有一个结点,返回1

情况3:左子树+右子树

3.树的高度

情况1:空,返回0

情况2:左子树和右子树高度中大的值+1

4.第k层叶子数

情况1:空,返回0

情况2:非空,k==1,返回1

情况3:非空,k>1,左子树第k-1层+右子树第k-1层

int BinaryTreeSize(BTNode* root) {
	if (root == NULL) {
		return 0;
	}
	if (root->left == NULL && root->right == NULL) {
		return 1;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right)+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 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 BinaryTreeLevelKSize(BTNode* root, int k) {
	if (root == NULL) {
		return 0;
	}
	if (k==1) {
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

查找值为x的节点

递归思想

情况1:空,返回NULL

情况2:不为空,根值为x,返回根节点

情况3:不为空,根值不为x,查找左子树,有则返回

             左子树中无,查找右子树,有则返回

             右子树中也无,返回空

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

层序遍历/完全二叉树

层序遍历

1.根进队列

2.节点出队列时,该节点的子节点(非空)进队列

3.当队列为空时,循环结束

完全二叉树

1.进行层序遍历,空也进队列

2.遇到第一个空节点,开始判断,后面全空就是完全二叉树,后面有非空就不是完全二叉树

void BinaryTreeLevelOrder(BTNode* root) {
	if (!root) {
		return;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	
	while (QueueSize(&q) > 0) {
		BTNode* head = QueueFront(&q);
		if (head->left) {
			QueuePush(&q, head->left);
		}
		if (head->right) {
			QueuePush(&q, head->right);
		}
		printf("%d", head->data);
		QueuePop(&q);
	}
	QueueDestroy(&q);
}


bool BinaryTreeComplete(BTNode* root) {
	if (!root) {
		return;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);

	while (QueueSize(&q) > 0) {
		BTNode* head = QueueFront(&q);
		if (head == NULL) {
			break;
		}
			QueuePush(&q, head->left);
			QueuePush(&q, head->right);
		QueuePop(&q);
	}
	while(!QueueEmpty(&q)){
		BTNode* head = QueueFront(&q);
		if (head) {
			QueueDestroy(&q);
			return false;
		}
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return true;
}

代码汇总

binarytree.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int BTDataType;

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

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate();
// 二叉树销毁
void BinaryTreeDestory(BTNode* root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);

binarytree.c

#define _CRT_SECURE_NO_WARNINGS
#include "binarytree.h"
#include "queue.h"


BTNode* BuyNode(BTDataType x) {
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL) {
		perror("malloc fail!");
	}
	newnode->left = NULL;
	newnode->right = NULL;
	newnode->data = x;
	return newnode;
}

BTNode* BinaryTreeCreate() {
	BTNode* Node1 = BuyNode(1);
	BTNode* Node2 = BuyNode(2);
	BTNode* Node3 = BuyNode(3);
	BTNode* Node4 = BuyNode(4);
	BTNode* Node5 = BuyNode(5);
	BTNode* Node6 = BuyNode(6);
	BTNode* Node7 = BuyNode(7);
	
	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;
	//Node6->left = Node7;

	return Node1;//返回根节点
}
//前序遍历 根节点 左节点 右节点
void BinaryTreePrevOrder(BTNode* root) {
	if (root == NULL) {
		printf("N ");
		return;
	}

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

void BinaryTreeInOrder(BTNode* root) {
	if (root == NULL) {
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}

void BinaryTreePostOrder(BTNode* root) {
	if (root == NULL) {
		printf("N ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}





int BinaryTreeSize(BTNode* root) {
	if (root == NULL) {
		return 0;
	}
	if (root->left == NULL && root->right == NULL) {
		return 1;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right)+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) {
	if (root == NULL) {
		return 0;
	}
	if (k==1) {
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

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

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


void BinaryTreeLevelOrder(BTNode* root) {
	if (!root) {
		return;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	
	while (QueueSize(&q) > 0) {
		BTNode* head = QueueFront(&q);
		if (head->left) {
			QueuePush(&q, head->left);
		}
		if (head->right) {
			QueuePush(&q, head->right);
		}
		printf("%d", head->data);
		QueuePop(&q);
	}
	QueueDestroy(&q);
}


bool BinaryTreeComplete(BTNode* root) {
	if (!root) {
		return;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);

	while (QueueSize(&q) > 0) {
		BTNode* head = QueueFront(&q);
		if (head == NULL) {
			break;
		}
			QueuePush(&q, head->left);
			QueuePush(&q, head->right);
		QueuePop(&q);
	}
	while(!QueueEmpty(&q)){
		BTNode* head = QueueFront(&q);
		if (head) {
			QueueDestroy(&q);
			return false;
		}
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return true;
}

void BinaryTreeDestory(BTNode* root) {
	if (root==NULL) {
		return;
	}

	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

在实现层序遍历时,会使用到队列。但由于C语言中没有现成的数据结构队列可以直接使用,需要自己实现。

queue.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef struct BinaryTreeNode* QDataType;

typedef struct QListNode{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

queue.c

#define _CRT_SECURE_NO_WARNINGS
#include "queue.h"
// 初始化队列 
void QueueInit(Queue* q) {
	assert(q);
	q->phead = q->ptail = NULL;
	q->size = 0;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data) {
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL) {
		perror("malloc fail!");
		exit(1);
	}
	else {
		newnode->data = data;
		newnode->next = NULL;
		if (q->ptail == NULL) {
			q->phead = q->ptail = newnode;
			q->size++;
		}
		else {
			q->ptail->next =newnode;
			q->ptail = newnode;
			q->size++;
		}
	}
}
// 队头出队列 
void QueuePop(Queue* q) {
	assert(q);
	assert(q->size != 0);
	if (q->phead->next == NULL) {
		free(q->ptail);
		q->ptail = q->phead = NULL;
		q->size--;
	}
	else {
		QNode* next = q->phead->next;
		free(q->phead);
		q->phead = next;
		q->size--;
	}
}
// 获取队列头部元素 
QDataType QueueFront(Queue* q) {
	assert(q);
	assert(q->size > 0);
	return q->phead->data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q) {
	assert(q);
	assert(q->size > 0);
	return q->ptail->data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* q) {
	assert(q);
	return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q) {
	assert(q);
	return !QueueSize(q);
}
// 销毁队列 
void QueueDestroy(Queue* q) {
	assert(q);
	while (q->size) {
		QueuePop(q);
	}
	q->phead = NULL;
	q->ptail = NULL;
}

7.堆及堆排序及TopK问题

详见我的另一篇文章~(TopK问题待更)

数据结构 | 详解二叉树——堆与堆排序

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

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

相关文章

ChatGPT的逆袭历程:核心技术深度解析

在ChatGPT问世之前&#xff0c;已有许多大模型存在&#xff0c;但为何只有它成为了AI时代的“iPhone时刻”&#xff1f;这不仅得益于其技术优势&#xff0c;还在于其发展过程中所采用的一系列创新策略。本文将深度复盘ChatGPT的逆袭历程&#xff0c;分析其核心技术&#xff0c;…

GIS毕业薪资从2K到20K究竟要多久?

同大多数行业一样&#xff0c;GIS毕业生薪资跟行业有密切关系。 测绘地理信息行业紧跟时代步伐&#xff0c;随着测绘地理信息技术的变革和进步&#xff0c;测绘产品也逐渐升级发展。 经过三个阶段的发展&#xff0c;测绘地理信息产品初步实现从抽象到真实、从平面到立体、从静…

npm run dev 同时运行vue前端项目和node后端项目

将两个项目放到一个目录下 项目拖进vscode中&#xff0c;安装包依赖&#xff0c;修改配置 npm i concurrently "dev": "concurrently \"vite --mode development\" \"nodemon app.js\"" 命令行 npm run dev 运行 没有运行成功排查 …

window11 设置 ubuntu2204 至最佳体验(安装/右键菜单/root用户/docker)

前言 在 window 中如果不使用 ubuntu 命令行会非常不方便&#xff0c;还好微软提供了 ubuntu 的终端&#xff0c;下载安装后简单设置下就可以愉快的使用了。 本文会涉及的方面 安装右键菜单设置root 用户设置docker 设置 安装 ubuntu 到微软的软件商店中下载安装即可&…

探索python数据可视化的奥秘:打造专业绘图环境

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、搭建专业绘图环境 二、掌握绘图基本原理 三、解锁绘图高级技巧 四、总结与展望 在数据…

taskENTER_CRITICAL()分析

1. 临界段代码 //任务级的临界段代码保护 taskENTER_CRITICAL() taskEXIT_CRITICAL()//中断级的临界段代码保护 taskENTER_CRITICAL_FROM_ISR() taskEXIT_CRITICAL_FROM_ISR()2. 以STM32为例 &#xff08;1&#xff09;STM32有0~15&#xff0c;共16级中断&#xff0c;可嵌套…

【linux】开机调用python脚本

linux中&#xff0c;可以使用crontab 设置开机自动调用 crontab的安装在前面文章里写过了&#xff0c;不再重复 首先&#xff0c;还是进入crontab配置文件 crontab -e 进入之后&#xff0c;跟其他定时任务不同&#xff0c;只需要在时间配置那里用rebooot 这类之后的两个文件的…

计算机图形学入门05:投影变换

1.投影变换 上一章已经介绍了投影变换&#xff0c;就是将三维图像投影到二维平面上&#xff0c;而投影变换又分为正交投影(Orthographic Projection)和透视投影(Perspective Projection)。如下图&#xff1a; 正交投影 没有近大远小的现象&#xff0c;无论图形与视点距离是远是…

Three.js 研究:4、创建设备底部旋转的科技感圆环

1、实现效果 2、PNG转SVG 2.1、原始物料 使用网站工具https://convertio.co/zh/png-svg/进行PNG转SVG 3、导入SVG至Blender 4、制作旋转动画 4.1、给圆环着色 4.2、修改圆环中心位置 4.3、让圆环旋转起来 参考一下文章 Three.js 研究&#xff1a;1、如何让物体动起来 Thre…

万字长文深度解析Agent反思工作流框架Reflexion上篇:安装与运行

今天&#xff0c;我们将迈出从理论到实践的关键一步——通过安装和测试Reflexion框架&#xff0c;我们将揭开智能体工作流的神秘面纱&#xff0c;实现知识的深度融合与应用。由于框架东西较多&#xff0c;我们暂定分为上中下三篇来讲解。 1. 安装 1.1 克隆和查看项目 git clo…

大语言模型实战——最小化模型评测

1. 引言 现在国内外的主流模型&#xff0c;在新模型发布时都会给出很多评测数据&#xff0c;用以说明当前模型在不同数据集上的测评表现&#xff08;如下面llama3发布的评测数据&#xff09;。 这些评测数据是如何给出来的呢&#xff1f;这篇文章会用一个最小化的流程来还原下…

u盘文件保密的方法有哪些?关于U盘的使用你要知道这些!

U盘作为便携式的存储设备&#xff0c;被广泛应用于日常工作和生活中。 然而&#xff0c;U盘的丢失或被盗可能导致敏感数据泄露&#xff0c;因此&#xff0c;掌握U盘文件保密的方法至关重要。 本文将介绍几种有效的U盘文件保密方法&#xff0c;并分享关于U盘使用的关键知识&…

Android HIDL接口添加

一.HIDL介绍 HIDL的全称是HAL interface definition language&#xff08;硬件抽象层接口定义语言&#xff09;&#xff0c;是Android Framework 与Android HAL之间的接口。HIDL 旨在用于进程间通信 (IPC)&#xff0c;进程之间的通信 采用 Binder 机制。 二.HIDL 与AIDL 的对…

CSRF跨站请求伪造漏洞

CSRF跨站请求伪造漏洞 1.CSRF漏洞概述2.防御CSRF攻击3.CSRF防御绕过CSRF令牌未绑定到用户会话自定义标头令牌绕过绕过Referer检查关键词绕过 4.利用示例使用HTML标签进行GET表单 GET 请求表单POST请求通过 iframe 发送表单 POST 请求Ajax POST 请求 5.CSRF BP 验证方法6.CSRF测…

【qt】自定义对话框

自定义对话框 一.自定义对话框的使用1.应用场景2.项目效果3.界面拖放4.模型和视图的设置5.action功能实现 二.自定义对话框的创建1.设置对话框界面2.创建对话框 三.对话框的功能与样式实现1.对话框数据的交换2.对话框的显示3.设置对话框的特性4.完成按钮的功能 四.编辑表头的对…

Spring Security3.0版本

前言&#xff1a; 核心&#xff1a; A >> &#xff1f; >> B &#xff1f;代表判断层&#xff0c;由Security实现 这是之前的版本浓缩&#xff0c;现在3.0版本添加了更匹配的内容描写&#xff0c;匹配了mvc模式 非mvc模式 核心&#xff1a;client&#x…

医院该如何应对网络安全?

在线医生咨询受到很多人的关注&#xff0c;互联网医疗行业的未来发展空间巨大&#xff0c;但随着医院信息化建设高速发展 医院积累了大量的患者基本信息、化验结果、电子处方、生产数据和运营信息等数据 这些数据涉及公民隐私、医院运作和发展等多因素&#xff0c;医疗行业办…

客户关系管理系统

CRM系统是协调企业与顾客间在销售、营销和服务关系&#xff0c;提升企业管理水平&#xff0c;向客户提供创新、个性化服务的过程。 最终目标是吸引新客户、保留老客户以及将已有客户转为忠实客户&#xff0c;增加市场份额。 CRM系统开发团队应该深谙“客户关系”的内涵&#xf…

17-java网络编程

目录 第17章 网络编程 17.1 软件结构 17.2 网络通信三要素 17.2.1 IP地址和域名 1、IP地址 2、域名 17.2.2 端口号 17.2.3 网络通信协议 17.3 TCP与UDP协议 17.3.1 UDP协议 17.3.2 TCP协议 1、三次握手 2、四次挥手 17.4 网络编程API 17.4.1 InetAddress类 17.4…

dubbo复习:(19)dubbo 和spring整合(老古董)

一、服务端依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM…