二叉排序树(Binary Search Tree, BST)是一种重要的数据结构,具有良好的查找、插入和删除性能。以下是针对你提出的问题的详细解答:
1. 二叉排序树的查找算法(SearchBST)
// 定义二叉树节点结构typedefstructBSTNode{intdata;structBSTNode*left,*right;}BSTNode,*BSTree;/** * 在二叉排序树中查找关键字 key * @param T: 当前树根节点 * @param key: 要查找的关键字 * @param father: 记录查找路径中的父节点(用于插入时定位) * @return: 找到返回该节点,否则返回 NULL */BSTNode*SearchBST(BSTree T,intkey,BSTNode*father){if(T==NULL)returnNULL;// 空树则未找到if(key==T->data)returnT;// 找到目标节点elseif(key<T->data)returnSearchBST(T->left,key,T);// 向左子树查找,当前节点成为潜在父亲elsereturnSearchBST(T->right,key,T);// 向右子树查找,当前节点成为潜在父亲}说明:
father参数通过递归传递,记录最后访问的非空节点(即插入位置的父节点),便于后续插入操作。
2. 二叉排序树的构造过程
构造基于逐个插入元素的过程,遵循以下规则:
- 若树为空,则第一个节点为根;
- 对每个新元素,从根开始比较:
- 小于当前节点 → 进入左子树;
- 大于当前节点 → 进入右子树;
- 直到遇到空指针位置进行插入。
示例:{46, 25, 54, 13, 29, 91}
| 插入顺序 | 操作 |
|---|---|
| 46 | 创建根节点 |
| 25 | 25 < 46 → 插入 46 的左子树 |
| 54 | 54 > 46 → 插入 46 的右子树 |
| 13 | 13 < 46 → 左;13 < 25 → 插入 25 的左子树 |
| 29 | 29 < 46 → 左;29 > 25 → 插入 25 的右子树 |
| 91 | 91 > 46 → 右;91 > 54 → 插入 54 的右子树 |
最终形成的树结构如下(缩进表示层次):
46 / \ 25 54 / \ \ 13 29 913. 二叉排序树的插入算法(InsertBST)
/** * 插入新关键字 newkey 到二叉排序树 * @param root: 指向根节点的指针(可能需要修改根) * @param newkey: 新的关键字 * @return: 成功返回 0,已存在返回 -1 */intInsertBST(BSTree*root,intnewkey){BSTNode*parent=NULL;BSTNode*found=SearchBST(*root,newkey,NULL);if(found!=NULL)return-1;// 关键字已存在,不允许重复// 创建新节点BSTNode*newNode=(BSTNode*)malloc(sizeof(BSTNode));if(!newNode)return-1;// 内存分配失败newNode->data=newkey;newNode->left=newNode->right=NULL;// 查找插入位置及其父节点BSTNode*current=*root;parent=NULL;while(current!=NULL){parent=current;if(newkey<current->data)current=current->left;elsecurrent=current->right;}// 插入新节点if(parent==NULL){*root=newNode;// 树为空,新节点作为根}elseif(newkey<parent->data){parent->left=newNode;}else{parent->right=newNode;}return0;// 插入成功}注意:使用双重指针
BSTree* root是为了在根为空时能正确修改根节点。
二叉排序树的特点总结
| 特性 | 说明 |
|---|---|
| 有序性 | 中序遍历可得有序序列,支持高效查找 |
| 平均时间复杂度 | 查找/插入/删除:O(log n)(平衡情况下) |
| 最坏情况 | 当输入有序时,退化为链表,时间复杂度变为 O(n) |
| 结构依赖插入顺序 | 不同插入顺序生成不同形状的树,影响效率 |
| 优化方向 | 使用平衡二叉树(如 AVL、红黑树)避免退化 |
要验证一棵二叉树是否为二叉排序树(BST),最有效的方法是利用其核心性质:中序遍历的结果必须是一个严格递增的序列。
✅ 验证方法:基于中序遍历
我们可以通过中序遍历二叉树,并在访问节点时检查当前节点的值是否大于前一个访问的节点值。如果是,则继续;否则不是 BST。
使用非递归或递归方式均可,但关键是要维护“前驱节点”的值进行比较。
🟩 方法一:递归 + 引用前驱值(推荐)
#include<stdio.h>#include<stdlib.h>#include<limits.h>// 二叉树节点定义typedefstructTreeNode{intval;structTreeNode*left;structTreeNode*right;}TreeNode;// 辅助函数:判断是否为BSTboolisValidBSTHelper(TreeNode*root,long*prev){if(root==NULL)returntrue;// 左子树必须是BSTif(!isValidBSTHelper(root->left,prev))returnfalse;// 当前节点必须大于前一个节点值if(root->val<=*prev)returnfalse;*prev=root->val;// 更新前驱值// 右子树必须是BSTreturnisValidBSTHelper(root->right,prev);}/** * 判断是否为二叉排序树 * @param root: 根节点 * @return: 是返回 true,否则返回 false */boolisValidBST(TreeNode*root){longprev=LONG_MIN;// 初始化为最小长整型,避免越界returnisValidBSTHelper(root,&prev);}说明:
- 使用
long类型防止int最小值被误判。 prev记录中序遍历过程中上一个访问节点的值。- 所有节点必须满足
当前值 > prev,即严格递增。
🟨 方法二:中序遍历存数组后判断有序性(简单直观)
voidinorder(TreeNode*root,int*arr,int*idx){if(root==NULL)return;inorder(root->left,arr,idx);arr[(*idx)++]=root->val;inorder(root->right,arr,idx);}boolisSorted(int*arr,intn){for(inti=1;i<n;i++)if(arr[i]<=arr[i-1])returnfalse;returntrue;}boolisValidBST_Array(TreeNode*root){intarr[10000],idx=0;inorder(root,arr,&idx);returnisSorted(arr,idx);}缺点:需要额外 O(n) 空间,效率较低,适合教学理解。
⚠️ 注意事项
- 必须是“严格递增”:不允许相等(除非题目允许重复元素且规定右子树 ≥ 根)。
- 不能只比较父子关系:例如:
虽然每个父节点与子节点满足大小关系,但左子树中的5 / \ 3 8 / \ 2 6 / 44 < 5却出现在右分支中 —— 实际上这棵树仍是合法的?不!此例需具体分析路径上下界!
👉 更高级写法可用带上下界的递归来处理跨层约束。
🔍 带上下界的递归验证法(更严谨)
boolvalidate(TreeNode*node,longminVal,longmaxVal){if(node==NULL)returntrue;if(node->val<=minVal||node->val>=maxVal)returnfalse;returnvalidate(node->left,minVal,node->val)&&validate(node->right,node->val,maxVal);}boolisValidBST_Bound(TreeNode*root){returnvalidate(root,LONG_MIN,LONG_MAX);}此方法可防止某些“局部满足但全局违反”的情况。