最近为了找实习,在学数据结构,都没怎么刷面试题了,记录一下这段时间以来学过的代码吧,有需要的可以参考一下。有什么不对的地方,望指正,也可以在评论区一起互动起来!!!
代码是参考看了一些厉害的博主才完善的,要是有侵权的,请联系我及时删除!!
二叉树(Binary Tree)是一种常见的树状数据结构,它由节点组成,每个节点最多有两个子节点:一个左子节点和一个右子节点。这些子节点也可以是空的。
以下是二叉树的一些关键特点:
- 根节点:二叉树的顶部节点称为根节点。
- 父节点、子节点:每个节点都可以有零个、一个或两个子节点。与节点相连的较高节点称为父节点,而与其相连的较低节点称为子节点。
- 叶子节点:没有子节点的节点称为叶子节点。
- 深度:从根节点到某个节点的唯一路径上的边数。
- 高度:从该节点到叶子节点的最长路径上的边数。
- 层次:树中的每一层都有一组节点,根节点在第0层,其子节点在第1层,依此类推。
- 遍历:二叉树的遍历是按照某种顺序访问树中所有节点的过程。常见的遍历方式包括前序遍历、中序遍历和后序遍历。
二叉树有许多类型,例如平衡二叉树、二叉搜索树、完全二叉树等,每种类型都有不同的特点和应用场景。
这里我主要以二叉搜索树为主。
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
/*节点*/
typedef struct inode{
int nValue;
struct inode *left;
struct inode *right;
}stTreeNode;
/************************************************
* 前序遍历
* 定义:先遍历根节点,然后左,再右
************************************************/
void tree_preScanLoop(stTreeNode *pRoot)
{
if(pRoot == NULL)
return;
printf("nValue = %d\n",pRoot->nValue);
tree_preScanLoop(pRoot->left);
tree_preScanLoop(pRoot->right);
return;
}
/************************************************
* 中序遍历
* 定义:先遍历左节点,然后根,再右
************************************************/
void tree_midScanLoop(stTreeNode *pRoot)
{
if(pRoot == NULL)
return;
tree_midScanLoop(pRoot->left);
printf("nValue=%d\n",pRoot->nValue);
tree_midScanLoop(pRoot->right);
return;
}
/************************************************
* 后序遍历
* 定义:先遍历左节点,然后右,再根
************************************************/
void tree_PostScanLoop(stTreeNode *pRoot)
{
if(pRoot == NULL)
return;
tree_PostScanLoop(pRoot->left);
tree_PostScanLoop(pRoot->right);
printf("nValue=%d\n",pRoot->nValue);
return;
}
/************************************************
* 创建节点
* 定义:先遍历左节点,然后右,再根
************************************************/
stTreeNode *tree_CreateInode(void)
{
stTreeNode *pinode = NULL;
pinode = (stTreeNode*)malloc(sizeof(stTreeNode));
if(pinode!=NULL)
{
memset(pinode,0,sizeof(stTreeNode));
return pinode;
}
return NULL;
}
/************************************************
* 销毁单个节点
* 定义:
************************************************/
void tree_destoryInode(stTreeNode *pinode)
{
if(pinode!=NULL)
{
free(pinode);
pinode = NULL;
}
}
/************************************************
* 获取树的深度
* 定义:深度即为数的层数
* 一棵树若只有一个节点那么其深度为0
************************************************/
int tree_getDeeps(stTreeNode *pRoot)
{
int ndeep = 0;
int nleftDeep = 0;
int nrightDeep = 0;
if(pRoot!=NULL)
{
nleftDeep = tree_getDeeps(pRoot->left);
nrightDeep = tree_getDeeps(pRoot->right);
ndeep = (nleftDeep > nrightDeep)?(nleftDeep+1):(nrightDeep+1);
}
return ndeep;
}
/************************************************
* 获取叶子节点数
* 定义:叶子无左右子节点的节点
************************************************/
int tree_getLeafCnts(stTreeNode *pRoot)
{
static int nCounts = 0;
if(pRoot!=NULL)
{
if(pRoot->left==NULL && pRoot->right == NULL){
nCounts++;
}
tree_getLeafCnts(pRoot->left);
tree_getLeafCnts(pRoot->right);
}
return nCounts;
}
/************************************************
* 查找节点
* 定义:查找规则,如果比根节点小则在左字数查找
************************************************/
stTreeNode *tree_searchInode(stTreeNode *pRoot,int nValue)
{
if(pRoot==NULL)
return NULL;
if(nValue == pRoot->nValue)
return pRoot;
else if(pRoot->nValue > nValue)
return tree_searchInode(pRoot->left,nValue);
else if(pRoot->nValue < nValue)
return tree_searchInode(pRoot->right,nValue);
else
return NULL;
}
/************************************************
* 查找大节点
* 定义:查找规则,如果比根节点小则在左字数查找
************************************************/
stTreeNode *tree_findMax(stTreeNode *pRoot)
{
// 方法一
// if(pRoot){
// /*往右找到最底,最底的最大*/
// for(;pRoot->right!=NULL;pRoot = pRoot->right);
// return pRoot;
// }
// return NULL;
// 方法二
if(pRoot==NULL){
return NULL;
}else if(pRoot->right==NULL){
return pRoot;
}else{
return tree_findMax(pRoot->right);
}
}
/************************************************
* 查找小节点
* 定义:查找规则,如果比根节点小则在左字数查找
************************************************/
stTreeNode* tree_findMin(stTreeNode *pRoot)
{
// 方法一
// if(pRoot){
// /*往左找到最底,最底的最小*/
// for(;pRoot->left!=NULL;pRoot=pRoot->left);
// return pRoot;
// }
// return NULL;
// 方法二
if (pRoot == NULL)
return NULL;
if (pRoot->left == NULL) // 结点没有左子树,即为最左叶子结点
return pRoot;
else // 有左子树,则继续在左子树中查找
return tree_findMin(pRoot->left);
}
// 删除最小结点
stTreeNode* DeleteMin(stTreeNode *BiTree)
{
if (BiTree->left == NULL){
stTreeNode *temp = BiTree->right;
free(BiTree);
return temp;
}
else {
//这里是多次重复赋值,只有最后一步才是将右子树赋值给父子树的左节点
BiTree->left = DeleteMin(BiTree->left);
return BiTree;
}
}
// 删除最大结点
stTreeNode* DeleteMax(stTreeNode *BiTree)
{
if (BiTree->right == NULL){
stTreeNode *temp = BiTree->left;
free(BiTree);
return temp;
}
else {
BiTree->right = DeleteMax(BiTree->right); //这里是多次重复赋值,只有最后一步才是将左子树赋值给父子树的右节点
return BiTree;
}
}
// 删除指定的节点的递归实现
stTreeNode *delNode(stTreeNode *pRoot,int nDeleteValue){
// 先找到要删除的结点在二叉树中的位置
if (pRoot == NULL) { // 没有找到元素
perror("can`t find element\n");
return NULL;
}
if (pRoot->nValue < nDeleteValue) { // 元素值大于结点元素值,则去结点右子树寻找
pRoot->right = delNode(pRoot->right, nDeleteValue);
return pRoot;
}
else if (pRoot->nValue > nDeleteValue) { // 元素值小于结点元素值,则去结点左子树寻找
pRoot->left = delNode(pRoot->left, nDeleteValue);
return pRoot;
}
// 找到结点
else if(pRoot->nValue == nDeleteValue){
//找到要删除的节点
if(pRoot->left == NULL && pRoot->right == NULL)//叶节点
{
tree_destoryInode(pRoot);
return NULL;
}else if(pRoot->left == NULL){//只有右孩子
stTreeNode * temp = pRoot->right;
tree_destoryInode(pRoot);
return temp;
}else if(pRoot->right == NULL){//只有左孩子
stTreeNode* temp = pRoot->left;
tree_destoryInode(pRoot);
return temp;
}else{ //左右都有
//找左子树的最大节点
stTreeNode * tempNode = pRoot->right;
stTreeNode *minNode = tree_findMin(tempNode);
minNode->left = pRoot->left;
tree_destoryInode(pRoot);
return tempNode;
}
}
}
/************************************************
* 删除指定的节点(循环实现)
************************************************/
void tree_deleteInode(stTreeNode *pRoot,int nDeleteValue)
{
stTreeNode *pRetNode = NULL;
stTreeNode *pNode = pRoot;
stTreeNode *parentNode = pRoot;
if(pNode == NULL) // 没有找到元素
return;
while(pNode)
{
if(pNode->nValue == nDeleteValue)
{
if(pNode->left == NULL && pNode->right == NULL)/*删除叶子节点*/
{
if(pNode == pRoot) //要是是根节点
{
tree_destoryInode(pNode);
}
else if(parentNode->left == pNode) //是左边的节点
{
parentNode->left = NULL;
tree_destoryInode(pNode);
}
else if(parentNode->right == pNode) //是右边的节点
{
parentNode->right = NULL;
tree_destoryInode(pNode);
}
}
else if(pNode->left == NULL)/*左节点空,表示右节点不为空*/
{
if(parentNode->left == pNode) //是左边的节点
{
parentNode->left = pNode->right;
tree_destoryInode(pNode);
}
else if(parentNode->right == pNode) //是右边的节点
{
parentNode->right = pNode->right;
tree_destoryInode(pNode);
}
}
else if(pNode->right == NULL)/*右节点空,表示左节点不为空*/
{
if(parentNode->left == pNode) //是左边的节点
{
parentNode->left = pNode->left;
tree_destoryInode(pNode);
}
else if(parentNode->right == pNode) //是右边的节点
{
parentNode->right = pNode->left;
tree_destoryInode(pNode);
}
}
else //删除有双节点的节点
{
if(parentNode->left == pNode) //是左边的节点
{
/*找到最小的节点*/
pRetNode = tree_findMin(pNode->right);
/*替换内容*/
pRetNode->left=pNode->left;
parentNode->left = pNode->right;
}
else if(parentNode->right == pNode) //是右边的节点
{
/*找到最小的节点*/
pRetNode = tree_findMin(pNode->right);
/*替换内容*/
pRetNode->left=pNode->left;
parentNode->right=pNode->right;
}
tree_destoryInode(pNode);
}
}
else if(nDeleteValue < pNode->nValue)/*往左*/
{
parentNode = pNode;
pNode = pNode->left;
}
else if(nDeleteValue > pNode->nValue)/*往右*/
{
parentNode = pNode;
pNode = pNode->right;
}
}
return;
}
/************************************************
* 插入节点
* 定义:插入规则为:左<根<右
************************************************/
int tree_insertNode(stTreeNode **ppRoot,int nVal)
{
stTreeNode * pNewInode = tree_CreateInode();
stTreeNode *pinodeTmp = NULL;
if(pNewInode==NULL){
printf("%s tree_CreateInode failed at (%d) line\n",__FUNCTION__,__LINE__);
return -1;
}
pNewInode->nValue = nVal;
if(pNewInode ==NULL){
printf("%s input paras error\n",__FUNCTION__);
return -1;
}
/*如果是空树*/
if(*ppRoot==NULL)
{
*ppRoot = pNewInode;
}
else
{
/*树根开始*/
pinodeTmp = *ppRoot;
while (pinodeTmp !=NULL)
{
/*插入节点小于当前节点*/
if(pNewInode->nValue < pinodeTmp->nValue)
{
if(pinodeTmp->left == NULL) //左孩子节点为空,插入
{
pinodeTmp->left = pNewInode;
break;
}
else
{
pinodeTmp = pinodeTmp->left;
}
}
/*插入节点大于当前节点*/
else if(pNewInode->nValue > pinodeTmp->nValue)
{
/*右孩子节点为空,插入*/
if(pinodeTmp->right == NULL)
{
pinodeTmp->right = pNewInode;
break;
}
else
{
/*右孩子节点非空,继续向右孩子节点找*/
pinodeTmp = pinodeTmp->right;
}
}else{
break; //遇到相等节点时要break,不然就会卡死 获取上面的 > < 换成 <= >=
}
}
}
return 0;
}
/************************************************
* 删除全部节点(递归实现)
************************************************/
void tree_destory(stTreeNode *pRoot)
{
stTreeNode *pleft = NULL;
stTreeNode *pright = NULL;
if(pRoot==NULL){
return;
}
else
{
pleft = pRoot->left;
pright = pRoot->right;
tree_destoryInode(pRoot); //要放上面,不要放下面(因为是只需要释放一次pRoot节点即可)
tree_destory(pleft);
tree_destory(pright);
}
return;
}
// 插入一个结点(< 或 > 不会插入重复节点,<= 或 >= 则会插入重复节点)
stTreeNode * InsertNode(stTreeNode **pRoot, int n)
{
if (*pRoot == NULL) { //树为空,或已到达叶子结点
// 为新结点分配内存
stTreeNode* newNode = (stTreeNode*)malloc(sizeof(stTreeNode));
if (newNode == NULL) {
perror("malloc failed!\n");
// exit(-1);
return NULL;
}
newNode->nValue = n;
newNode->left = NULL;
newNode->right = NULL;
*pRoot = newNode;
}
else if ((*pRoot)->nValue <= n) { // 插入值大于当前结点的值,则将元素插入到结点的右子树
(*pRoot)->right = InsertNode(&(*pRoot)->right, n);
}
else if ((*pRoot)->nValue >= n) { // 插入值小于当前结点的值,则将元素插入到结点的左子树
(*pRoot)->left = InsertNode(&(*pRoot)->left, n);
}
return *pRoot;
}
int main()
{
int ni = 0;
int nRet = -1;
int nSqe[]={35,2,6,8,10,3,44,1,32,65,7,21};
int delNum=0;
stTreeNode *pTreeRoot = NULL;
stTreeNode *pNewTreeNode = NULL;
stTreeNode *tempNode = NULL;
for(ni= 0;ni<(sizeof(nSqe)/sizeof(int));ni++)
{
nRet = tree_insertNode(&pTreeRoot,nSqe[ni]);
if(nRet<0){
printf("%s tree_insertNode failed at (%d) line\n",__FUNCTION__,__LINE__);
break;
}
}
printf("delNum is : ");
scanf("%d",&delNum);
tree_preScanLoop(pTreeRoot);
printf("------------------------------------------------------\n");
delNode(pTreeRoot,delNum);
tree_preScanLoop(pTreeRoot);
printf("------------------------------------------------------\n");
tree_destory(pTreeRoot);
return 0;
}
删除节点的那块得多花时间,二叉树删除节点有四种情况,其实就是要注意删除节点后,其子节点的位置调整。
我自己写的流程图就放这了,有不懂的可以来看看。(需要结合上面的代码去分析,不然就会一脸懵逼)
这里有很多地方我都使用了 递归 和 循环遍历 两种方式实现,把它们搞懂,是学会二叉树必不可少的。