二叉排序树算法
- 格式:doc
- 大小:175.53 KB
- 文档页数:26
二叉排序树1.二叉排序树定义二叉排序树(Binary Sort Tree)或者是一棵空树;或者是具有下列性质的二叉树:(1)若左子树不空,则左子树上所有结点的值均小于根结点的值;若右子树不空,则右子树上所有结点的值均大于根结点的值。
(2)左右子树也都是二叉排序树,如图6-2所示。
2.二叉排序树的查找过程由其定义可见,二叉排序树的查找过程为:(1)若查找树为空,查找失败。
(2)查找树非空,将给定值key与查找树的根结点关键码比较。
(3)若相等,查找成功,结束查找过程,否则:①当给值key小于根结点关键码,查找将在以左孩子为根的子树上继续进行,转(1)。
②当给值key大于根结点关键码,查找将在以右孩子为根的子树上继续进行,转(1)。
3.二叉排序树插入操作和构造一棵二叉排序树向二叉排序树中插入一个结点的过程:设待插入结点的关键码为key,为将其插入,先要在二叉排序树中进行查找,若查找成功,按二叉排序树定义,该插入结点已存在,不用插入;查找不成功时,则插入之。
因此,新插入结点一定是作为叶子结点添加上去的。
构造一棵二叉排序树则是逐个插入结点的过程。
对于关键码序列为:{63,90,70,55,67,42,98,83,10,45,58},则构造一棵二叉排序树的过程如图6-3所示。
4.二叉排序树删除操作从二叉排序树中删除一个结点之后,要求其仍能保持二叉排序树的特性。
设待删结点为*p(p为指向待删结点的指针),其双亲结点为*f,删除可以分三种情况,如图6-4所示。
(1)*p结点为叶结点,由于删去叶结点后不影响整棵树的特性,所以,只需将被删结点的双亲结点相应指针域改为空指针,如图6-4(a)所示。
(2)*p结点只有右子树或只有左子树,此时,只需将或替换*f结点的*p子树即可,如图6-4(b)、(c)所示。
(3)*p结点既有左子树又有右子树,可按中序遍历保持有序地进行调整,如图6-4(d)、(e)所示。
设删除*p结点前,中序遍历序列为:① P为F的左子女时有:…,Pi子树,P,Pj,S子树,Pk,Sk子树,…,P2,S2子树,P1,S1子树,F,…。
数据结构之⼆叉树(BinaryTree)⽬录导读 ⼆叉树是⼀种很常见的数据结构,但要注意的是,⼆叉树并不是树的特殊情况,⼆叉树与树是两种不⼀样的数据结构。
⽬录 ⼀、⼆叉树的定义 ⼆、⼆叉树为何不是特殊的树 三、⼆叉树的五种基本形态 四、⼆叉树相关术语 五、⼆叉树的主要性质(6个) 六、⼆叉树的存储结构(2种) 七、⼆叉树的遍历算法(4种) ⼋、⼆叉树的基本应⽤:⼆叉排序树、平衡⼆叉树、赫夫曼树及赫夫曼编码⼀、⼆叉树的定义 如果你知道树的定义(有限个结点组成的具有层次关系的集合),那么就很好理解⼆叉树了。
定义:⼆叉树是n(n≥0)个结点的有限集,⼆叉树是每个结点最多有两个⼦树的树结构,它由⼀个根结点及左⼦树和右⼦树组成。
(这⾥的左⼦树和右⼦树也是⼆叉树)。
值得注意的是,⼆叉树和“度⾄多为2的有序树”⼏乎⼀样,但,⼆叉树不是树的特殊情形。
具体分析如下⼆、⼆叉树为何不是特殊的树 1、⼆叉树与⽆序树不同 ⼆叉树的⼦树有左右之分,不能颠倒。
⽆序树的⼦树⽆左右之分。
2、⼆叉树与有序树也不同(关键) 当有序树有两个⼦树时,确实可以看做⼀颗⼆叉树,但当只有⼀个⼦树时,就没有了左右之分,如图所⽰:三、⼆叉树的五种基本状态四、⼆叉树相关术语是满⼆叉树;⽽国际定义为,不存在度为1的结点,即结点的度要么为2要么为0,这样的⼆叉树就称为满⼆叉树。
这两种概念完全不同,既然在国内,我们就默认第⼀种定义就好)。
完全⼆叉树:如果将⼀颗深度为K的⼆叉树按从上到下、从左到右的顺序进⾏编号,如果各结点的编号与深度为K的满⼆叉树相同位置的编号完全对应,那么这就是⼀颗完全⼆叉树。
如图所⽰:五、⼆叉树的主要性质 ⼆叉树的性质是基于它的结构⽽得来的,这些性质不必死记,使⽤到再查询或者⾃⼰根据⼆叉树结构进⾏推理即可。
性质1:⾮空⼆叉树的叶⼦结点数等于双分⽀结点数加1。
证明:设⼆叉树的叶⼦结点数为X,单分⽀结点数为Y,双分⽀结点数为Z。
实现二叉树的各种基本运算的算法1.二叉树的定义及概述二叉树是一种重要的数据结构,它是由节点组成的序列,每个节点最多有两个子节点。
二叉树的根节点是唯一的,且每个节点都有一个“父节点”,除了根节点外,每个子节点称作“左孩子”和“右孩子”。
二叉树的组成部分是节点,每个节点包括一个数据元素和左右孩子指针。
通过这些指针构成的树形结构,可以便捷地进行数据存储和操作。
本文将介绍二叉树的各种基本运算及实现方法。
2.二叉树的遍历二叉树的遍历分为三种:前序遍历、中序遍历和后序遍历。
前序遍历:按照“根节点-左孩子-右孩子”的顺序遍历二叉树。
中序遍历:按照“左孩子-根节点-右孩子”的顺序遍历二叉树。
后序遍历:按照“左孩子-右孩子-根节点”的顺序遍历二叉树。
3.二叉树的建立二叉树的建立有三种方法:链式存储法、顺序存储法和扩展二叉树。
链式存储法:链式存储法是用链表来表示二叉树的方法,每个节点包括数据域和左右孩子指针域。
链式存储法建立二叉树比较容易,操作起来也比较方便。
顺序存储法:顺序存储法是用数组来表示二叉树的方法,便于存取、操作和查找。
但是顺序存储法的空间利用率不高,只有满二叉树才能利用完全。
扩展二叉树:是指二叉树中所有的空节点都必须存储起来,以构成一颗可以存储不满的二叉树。
由于扩展二叉树浪费了大量的空间,因此很少使用。
4.二叉树的查找二叉树的查找分为两种:层序遍历和二叉排序树的查找。
层序遍历:是一种广度优先搜索的方式来遍历二叉树。
层序遍历可以找到二叉树中从根节点到任意节点的路径,具有较高的效率。
层序遍历可以使用队列来实现。
二叉排序树的查找:是指在一颗二叉排序树中查找某个元素的算法。
二叉排序树(BST)是一颗二叉树,其中每个节点的值都比它的左子节点大,比它的右子节点小。
通过对BST的查找操作,可以将查找的效率高效地进行。
5.二叉树的删除在二叉树中删除节点有两种情况:删除叶子节点和删除非叶子节点。
下面给出二叉树的删除基本操作。
二叉排序树查找的递归算法介绍二叉排序树(Binary Search Tree),也称二叉查找树、有序二叉树或排序二叉树,是一种常用的数据结构。
它具有以下特点:•每个节点都包含一个键值和对应的数据。
•左子树中的所有节点的键值都小于根节点的键值。
•右子树中的所有节点的键值都大于根节点的键值。
•左右子树也分别是二叉排序树。
二叉排序树支持高效的查找、插入和删除操作,其中查找操作是利用递归实现的。
本文将详细介绍二叉排序树查找的递归算法。
二叉排序树的定义二叉排序树的定义如下:class TreeNode:def __init__(self, key, data):self.key = keyself.data = dataself.left = Noneself.right = Noneclass BinarySearchTree:def __init__(self):self.root = None在二叉排序树中,每个节点都是一个TreeNode对象,包含键值key和对应的数据data。
left和right分别指向左子树和右子树的根节点。
树的根节点由BinarySearchTree对象的root属性表示。
二叉排序树查找的递归算法二叉排序树的查找操作是利用递归实现的,其具体算法如下:1.如果待查找的键值等于当前节点的键值,返回当前节点的数据。
2.如果待查找的键值小于当前节点的键值,递归在左子树中查找。
3.如果待查找的键值大于当前节点的键值,递归在右子树中查找。
4.如果在左子树或右子树中找不到对应的键值,则返回空。
下面是二叉排序树查找的递归算法的代码实现:def search_recursive(node, key):if node is None or node.key == key:return node.dataelif key < node.key:return search_recursive(node.left, key)else:return search_recursive(node.right, key)在上述代码中,node表示当前节点,key表示待查找的键值。
平衡树——特点:所有结点左右子树深度差≤1排序树——特点:所有结点―左小右大字典树——由字符串构成的二叉排序树判定树——特点:分支查找树(例如12个球如何只称3次便分出轻重)带权树——特点:路径带权值(例如长度)最优树——是带权路径长度最短的树,又称Huffman树,用途之一是通信中的压缩编码。
1.1 二叉排序树:或是一棵空树;或者是具有如下性质的非空二叉树:(1)若左子树不为空,左子树的所有结点的值均小于根的值;(2)若右子树不为空,右子树的所有结点均大于根的值;(3)它的左右子树也分别为二叉排序树。
例:二叉排序树如图9.7:二叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树的存储结构。
中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。
每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。
搜索,插入,删除的复杂度等于树高,期望O(logn),最坏O(n)(数列有序,树退化成线性表).虽然二叉排序树的最坏效率是O(n),但它支持动态查询,且有很多改进版的二叉排序树可以使树高为O(logn),如SBT,AVL,红黑树等.故不失为一种好的动态排序方法.2.2 二叉排序树b中查找在二叉排序树b中查找x的过程为:1. 若b是空树,则搜索失败,否则:2. 若x等于b的根节点的数据域之值,则查找成功;否则:3. 若x小于b的根节点的数据域之值,则搜索左子树;否则:4. 查找右子树。
[cpp]view plaincopyprint?1.Status SearchBST(BiTree T, KeyType key, BiTree f, BiTree &p){2. //在根指针T所指二叉排序樹中递归地查找其关键字等于key的数据元素,若查找成功,3. //则指针p指向该数据元素节点,并返回TRUE,否则指针P指向查找路径上访问的4. //最好一个节点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL5. if(!T){ p=f; return FALSE;} //查找不成功6. else if EQ(key, T->data.key) {P=T; return TRUE;} //查找成功7. else if LT(key,T->data.key)8. return SearchBST(T->lchild, key, T, p); //在左子树继续查找9. else return SearchBST(T->rchild, key, T, p); //在右子树继续查找10.}2.3 在二叉排序树插入结点的算法向一个二叉排序树b中插入一个结点s的算法,过程为:1. 若b是空树,则将s所指结点作为根结点插入,否则:2. 若s->data等于b的根结点的数据域之值,则返回,否则:3. 若s->data小于b的根结点的数据域之值,则把s所指结点插入到左子树中,否则:4. 把s所指结点插入到右子树中。
算法(平衡⼆叉树)科普⼆叉树⼆叉树⼆叉数是每个节点最多有两个⼦树,或者是空树(n=0),或者是由⼀个根节点及两个互不相交的,分别称为左⼦树和右⼦树的⼆叉树组成满⼆叉树有两个⾮空⼦树(⼆叉树中的每个结点恰好有两个孩⼦结点切所有叶⼦结点都在同⼀层)也就是⼀个结点要么是叶结点,要么是有两个⼦结点的中间结点。
深度为k且含有2^k-1个结点的⼆叉树完全⼆叉树从左到右依次填充从根结点开始,依次从左到右填充树结点。
除最后⼀层外,每⼀层上的所有节点都有两个⼦节点,最后⼀层都是叶⼦节点。
平衡⼆叉树AVL树[3,1,2,5,9,7]⾸先科普下⼆叉排序树⼜称⼆叉查找树,议程⼆叉搜索树⼆叉排序树的规则⽐本⾝⼤放右边,⽐本⾝⼩放左边平衡⼆叉数⾸先是⼀个⼆叉排序树左右两个⼦树的⾼度差不⼤于1下⾯图中是平衡的情况下⾯是不平衡的情况引⼊公式(LL)右旋function toateRight(AvlNode){let node=AvlNode.left;//保存左节点 AvlNode.left=node.right;node.right=AvlNode;}(RR)左旋function roateLeft(AvlNode){let node=AvlNode.right;//保存右⼦节点AvlNode.right=node.left;node.left=AvlNode;return node;}左右旋⼤图判断⼆叉树是不是平衡树⼆叉树任意结点的左右⼦树的深度不超过1深度计算定义⼀个初始化的⼆叉树var nodes = {node: 6,left: {node: 5,left: {node: 4},right: {node: 3}},right: {node: 2,right: {node: 1}}}//计算⾼度const treeDepth = (root) => {if (root == null) {return 0;}let left = treeDepth(root.left)let right = treeDepth(root.right)return 1+(left>right?left:right)}//判断深度const isTree=(root)=>{if (root == null) {return true;}let left=treeDepth(root.left)let right=treeDepth(root.right)let diff=left-right;if (diff > 1 || diff < -1) {return false}return isTree(root.left)&&isTree(root.right) }console.log(isTree(nodes))判断⼆叉数是不是搜索⼆叉树//第⼀种 //中序遍历let last=-Infinity;const isValidBST=(root)=>{if (root == null) {return true;}//先从左节点开始if (isValidBST(root.left)) {if (last < root.node) {last=root.node;return isValidBST(root.right)}}return false}console.log(isValidBST(nodes))//第⼆种const isValidBST = root => {if (root == null) {return true}return dfs(root, -Infinity, Infinity)}const dfs = (root, min, max) => {if (root == null) {return true}if (root.node <= min || root.node >= max) {return false}return dfs(root.left, min, root.node) && dfs(root.right, root.node, max)}console.log(isValidBST(nodes))实现⼀个⼆叉树实现了⼆叉树的添加,删除,查找,排序//⼆叉树结点class TreeNode {constructor(n, left, right){this.n = n;this.left = left;this.right = right;}}//⼆叉树class BinaryTree {constructor(){this.length = 0;this.root = null;this.arr = [];}//添加对外⼊⼝,⾸个参数是数组,要求数组⾥都是数字,如果有不是数字则试图转成数字,如果有任何⼀个⽆法强制转成数字,则本操作⽆效 addNode(){let arr = arguments[0];if(arr.length == 0) return false;return this.judgeData('_addNode', arr)}//删除结点deleteNode(){let arr = arguments[0];if(arr.length == 0) return false;return this.judgeData('_deleteNode', arr)}//传值判断,如果全部正确,则全部加⼊叉树judgeData(func, arr){let flag = false;//任何⼀个⽆法转成数字,都会失败if(arr.every(n => !Number.isNaN(n))){let _this = this;arr.map(n => _this[func](n));flag = true;}return flag;}//添加的真实实现_addNode(n){n = Number(n);let current = this.root;let treeNode = new TreeNode(n, null, null);if(this.root === null){this.root = treeNode;}else {current = this.root;while(current){let parent = current;if(n < current.n){current = current.left;if(current === null){parent.left = treeNode;}}else {current = current.right;if(current === null){parent.right = treeNode;}}}}this.length++;return treeNode;}//删除节点的真实实现_deleteNode(n){n = Number(n);if(this.root === null){return;}//查找该节点,删除节点操作⽐较复杂,为排除找不到被删除的节点的情况,简化代码,先保证该节点是存在的,虽然这样做其实重复了⼀次查询了,但⼆叉树的查找效率很⾼,这是可接受的let deleteNode = this.findNode(n);if(!deleteNode){return;}//如果删除的是根节点if(deleteNode === this.root){if(this.root.left === null && this.root.right === null){this.root = null;}else if(this.root.left === null){this.root = this.root.right;}else if(this.root.right === null){this.root = this.root.left;}else {let [replaceNode, replacePNode, rp] = this.findLeftTreeMax(deleteNode);replacePNode[rp] = null;replaceNode.left = this.root.left;replaceNode.right = this.root.right;this.root = replaceNode;}}else {//被删除的⽗节点,⼦节点在⽗节点的位置p,有left,right两种可能let [deleteParent, p] = this.findParentNode(deleteNode);if(deleteNode.left === null && deleteNode.right === null){deleteParent[p] = null;}else if(deleteNode.left === null){deleteParent[p] = deleteNode.right;}else if(deleteNode.right === null){deleteParent[p] = deleteNode.left;}else {//⽤来替换被删除的节点,⽗节点,节点在⽗节点的位置let [replaceNode, replacePNode, rp] = this.findLeftTreeMax(deleteNode);if(replacePNode === deleteNode){deleteParent[p] = replaceNode;}else {deleteParent[p] = replaceNode;replacePNode.right = null;}replacePNode[rp] = null;replaceNode.left = deleteNode.left;replaceNode.right = deleteNode.right;}}this.length--;}//查找findNode(n){let result = null;let current = this.root;while(current){if(n === current.n){result = current;break;}else if(n < current.n){current = current.left;}else {current = current.right;}}return result;}//查找⽗节点findParentNode(node){let [parent, child, p] = [null, null, null];if(this.root !== node){parent = this.root;if(node.n < parent.n){child = parent.left;p = 'left';}else {child = parent.right;p = 'right';}while(child){if(node.n === child.n){break;}else if(node.n < child.n){parent = child;child = parent.left;p = 'left';}else {parent = child;child = parent.right;p = 'right';}}}return [parent, p];}//查找当前有左⼦树的节点的最⼤值的节点M,如有A个节点被删除,M是最接近A点之⼀(还有⼀个是右⼦树节点的最⼩值) findLeftTreeMax(topNode){let [node, parent, p] = [null, null, null];if(this.root === null || topNode.left === null){return [node, parent, p];}parent = topNode;node = topNode.left;p = 'left';while(node.right){parent = node;node = node.right;p = 'right';}return [node, parent, p];}//查找最⼤值maxValue(){if(this.root !== null){return this._findLimit('right');}}//查找最⼩值minValue(){if(this.root !== null){return this._findLimit('left');}}//实现查找特殊值_findLimit(pro){let n = this.root.n;let current = this.root;while(current[pro]){current = current[pro];n = current.n;}return n;}//中序排序,并⽤数组的形式显⽰sortMiddleToArr(){this._sortMiddleToArr(this.root);return this.arr;}//中序⽅法_sortMiddleToArr(node){if(node !== null){this._sortMiddleToArr(node.left);this.arr.push(node.n);this._sortMiddleToArr(node.right);}}//打印⼆叉树对象printNode(){console.log(JSON.parse(JSON.stringify(this.root)));}}//测试var binaryTree = new BinaryTree();binaryTree.addNode([50, 24, 18, 65, 4, 80, 75, 20, 37, 40, 60]);binaryTree.printNode();//{n: 50, left: {…}, right: {…}}console.log(binaryTree.maxValue());//80console.log(binaryTree.minValue());//4console.log(binaryTree.sortMiddleToArr());// [4, 18, 20, 24, 37, 40, 50, 60, 65, 75, 80] binaryTree.deleteNode([50]);binaryTree.printNode();//{n: 40, left: {…}, right: {…}}排序复习function ArrayList() {this.array = [];}ArrayList.prototype = {constructor: ArrayList,insert: function(item) {this.array.push(item);},toString: function() {return this.array.join();},swap: function(index1, index2) {var aux = this.array[index2];this.array[index2] = this.array[index1];this.array[index1] = aux;},//冒泡排序bubbleSort: function() {var length = this.array.length;for (var i = 0; i < length; i++) {for (var j = 0; j < length - 1 - i; j++) {if (this.array[j] > this.array[j + 1]) {this.swap(j, j + 1);}}}},//选择排序selectionSort: function() {var length = this.array.length;var indexMin;for (var i = 0; i < length - 1; i++) {indexMin = i;for (var j = i; j < length; j++) {if (this.array[indexMin] > this.array[j]) {indexMin = j;}}if (indexMin !== i) {this.swap(indexMin, i);}}},//插⼊排序insertionSort: function() {var length = this.array.length;var j;var temp;for (var i = 1; i < length; i++) {temp = this.array[i];j = i;while (j > 0 && this.array[j - 1] > temp) {this.array[j] = this.array[j - 1];j--;}this.array[j] = temp;}},//归并排序mergeSort: function() {function mergeSortRec(array) {var length = array.length;if (length === 1) {return array;}var mid = Math.floor(length / 2);var left = array.slice(0, mid);var right = array.slice(mid, length);return merge(mergeSortRec(left), mergeSortRec(right)); }function merge(left, right) {var result = [];var il = 0;var ir = 0;while (il < left.length && ir < right.length) {if (left[il] < right[ir]) {result.push(left[il++]);} else {result.push(right[ir++]);}}while (il < left.length) {result.push(left[il++]);}while (ir < right.length) {result.push(right[ir++]);}return result;}this.array = mergeSortRec(this.array);},//快速排序quickSort:function(){function sort(array){if (array.length <= 1) {return array;}var pivotIndex = Math.floor(array.length/2);var pivot = array.splice(pivotIndex,1)[0];var left = [];var right = [];for(var i = 0; i < array.length; i++){if (array[i] < pivot) {left.push(array[i]);}else{right.push(array[i]);}}return sort(left).concat([pivot],sort(right));}this.array = sort(this.array);}};...................................................................................................................############################################################################ ###################################################################################。
二叉排序树非递归插入算法
二叉排序树非递归插入算法指的是在二叉排序树中插入一个新
节点时,采用非递归的方式进行操作的算法。
其基本思想是从根节点开始找到插入位置,并将新节点插入到相应位置上。
具体实现过程为:先将新节点插入到二叉排序树的最底层,然后从底层向上逐层调整,使得整个树仍然满足二叉排序树的性质。
在插入过程中,需要从根节点开始一直向下查找,直到找到插入位置为止。
如果插入节点的值大于当前节点的值,则向右子树继续查找;如果插入节点的值小于当前节点的值,则向左子树继续查找。
非递归插入算法相对于递归插入算法具有简单、高效、节省空间等优点,因此在实际应用中被广泛采用。
同时,该算法的实现也相对较为简单,适合初学者学习二叉排序树的基本操作。
- 1 -。
树、⼆叉树、查找算法总结树的定义形式化定义树:T={D,R }。
D是包含n个结点的有限集合(n≥0)。
当n=0时为空树,否则关系R满⾜以下条件:l 有且仅有⼀个结点d0∈D,它对于关系R来说没有前驱结点,结点d0称作树的根结点。
l 除根结点外,每个结点有且仅有⼀个前驱结点。
l D中每个结点可以有零个或多个后继结点。
递归定义树是由n(n≥0)个结点组成的有限集合(记为T)。
其中:l 如果n=0,它是⼀棵空树,这是树的特例;l 如果n>0,这n个结点中存在⼀个唯⼀结点作为树的根结点(root),其余结点可分为m (m≥0)个互不相交的有限⼦集T1、T2、…、Tm,⽽每个⼦集本⾝⼜是⼀棵树,称为根结点root的⼦树。
ð 树中所有结点构成⼀种层次关系!树的基本术语度结点的度:⼀个结点的⼦树的个数树的度:各节点的度的最⼤值。
通常将度为m的树成为m次树或m叉树结点分⽀结点:度不为0的结点(也称⾮终端结点)度为1的结点成为单分⽀结点,度为2的结点称为双分⽀结点叶结点:度为0的结点路径与路径长度路径:两个结点di和dj的结点序列(di,di1,di2,…,dj)。
其中<dx,dy>是分⽀。
路径长度:等于路径所通过的结点数⽬减1(即路径上的分⽀数⽬)结点的层次和树⾼度层次:根结点层次为1,它的孩⼦结点层次为2。
以此类推。
树的⾼度(深度):结点中的最⼤层次;有序树和⽆序树有序树:若树中各结点的⼦树是按照⼀定的次序从左向右安排的,且相对次序是不能随意变换的⽆序树:和上⾯相反森林只要把树的根结点删去就成了森林。
反之,只要给n棵独⽴的树加上⼀个结点,并把这n棵树作为该结点的⼦树,则森林就变成了⼀颗树。
树的性质性质1:树中的结点数等于所有结点的度数之和加1。
证明:树的每个分⽀记为⼀个度,度数和=分⽀和,⽽再给根节点加个分⽀性质2:度为m的树中第i层上⾄多有mi-1个结点(i≥1)。
性质3 ⾼度为h的m次树⾄多有个结点。
沈阳航空航天大学课程设计报告课程设计名称:数据结构课程设计课程设计题目:二叉排序树算法院(系):计算机学院专业:计算机科学与技术班级:04010103学号:2010040101097姓名:郭胜龙指导教师:丁一军此页为任务书目录1 课程设计介绍 (1)1.1课程设计内容 (1)1.2课程设计要求 (1)2 课程设计原理 (2)2.1课设题目粗略分析 (2)2.2原理图介绍 (2)2.2.1 功能模块图 (2)2.2.2 main(主函数) (2)2.2.3 SearchBST(查找) (3)2.2.4 InsertBST(插入) (4)2.2.5 CreatBST(建立) (4)2.2.6 DeleteBST(删除) (4)2.2.7 PreOrder(先序遍历) (5)2.2.8 InOrder(中序遍历) (5)3 数据结构分析 (7)3.1存储结构 (7)3.2算法描述 (7)4 调试与分析 (12)4.1调试过程 (12)4.2程序执行过程 (12)参考文献 (15)附录(关键部分程序清单) (16)沈阳航空航天大学课程设计报告1 课程设计介绍1.1 课程设计内容题目内容:1.构造二叉排序树;2.输出该二叉排序树的先序、中序遍历序列;3.删除该二叉排序树的任意一个结点;4.输出删除后的二叉排序树的先序、中序遍历序列。
1.2课程设计要求题目要求:1.按要求建立不同的二叉排序树;2.数据自定3.独立完成课程设计任务2 课程设计原理2.1课设题目粗略分析根据课设题目要求,拟将整体程序分为七大模块。
以下是七个模块的大体分析:main ():主函数模块SearchBST ():查找相应的结点 InsertBST1():插入一个新的结点 CreatBST ():创建一棵二叉排序树 DeleteNode ():删除结点PreOrder ()对二叉排序树进行先序遍历 InOrder ()对二叉排序树进行中序遍历2.2 原理图介绍2.2.1 功能模块图图2.2.1 功能模块结构图2.2.2 main (主函数)功能:连接各个函数,确定他们的执行顺序和条件。
二叉排序树算法主模块查找模块 插入模块 建立模块 删除模块 先序遍历模块 中序遍历模块图2.2.2主函数流程图2.2.3 SearchBST (查找)功能:查找相应的结点。
图2.2.3 查找操作的流程图开始选择功能Choose=1 建立二叉排序树Choose=2 删除x 结点Choose=3 先序遍历二叉排Choose=0 退出Choose=4 中序遍历二叉排A结束AYY开始T->data==key s=TT->data>keys=SearchBST(T->rchild)s=SearchBST(T->lchild)NN返回s结束2.2.4 InsertBST (插入)功能:插入一个新的结点。
图2.2.4 插入操作的流程图2.2.5 CreatBST (建立)功能:建立一个已知数据的二叉排序树。
图2.2.5 建立操作的流程图2.2.6 DeleteBST (删除)功能:删除值为x 的结点。
YY开始T==NULL T=ss->data>T->datas=InserBST(T->rchild)s=InsertBST(T->lchild)NN结束开始输入结点数n调用插入函数结束开始调用查找函数删除p结点右子树连在左子树上左子树连在p结点的父母节点上结束图2.2.6 删除操作的流程图2.2.7 PreOrder(先序遍历)功能:对二叉排序树进行先序遍历,并输出序列。
开始访问TPreOrder(T->rchild)PreOrder(T->lchild)结束图2.2.7 先序遍历的流程图2.2.8 InOrder(中序遍历)功能:对二叉排序树进行先序遍历,并输出序列。
开始InOrder(T->rchild)访问TInOrder(T->lchild)结束图2.2.8 中序遍历的流程图沈阳航空航天大学课程设计报告错误!未指定书签。
3 数据结构分析3.1 存储结构定义一个链表式的二叉排序树,用链表的方式构造结点,存储二叉排序树中的结点、结点类型和指针类型如下:typedef struct BiTNode{int data;struct BiTNode *lchild,*rchild;}BiTNode,*BiTree;3.2 算法描述1、二叉排序树的查找算法(1)若二叉排序树为空,则查找失败。
(2)否则,将根结点的关键字与待查关键字进行比较,若相等,则查找成功;若根节点关键字大于待查值,则进入左子树重复次步骤,否则,进入右子树进行此步骤;若在查找过程中遇到二叉排序树的叶子节点时,还没有找到待查节点,则查找不成功。
算法如下:BiTree SearchBST1(BiTree T, int key){//在根指针T所指二叉排序树中递归查找关键字等于 key 的数据元素//若成功,返回指向该数据元素结点的指针,否则返回空指针;s为返回 //指针BiTree s;if(T==NULL) return NULL;else if(T->data==key) s=T;else if(T->data>key) //key大于当前结点的关键字,则查找左子树s=SearchBST1(T->lchild,key);else//key小于当前结点的关键字则查找右子树s=SearchBST1(T->rchild,key);return s;}3、二叉排序树的节点插入算法在二叉排序树中插入一个新节点,首先要查找该节点在二叉排序树中是否已经存在。
若二叉排序树中不存在关键字等于x的节点,则插入。
将一个关键字值为x的节点s插入到二叉排序树中,可以用下面的方法:(1)若二叉排序树为空,则关键字为x的节点s成为二叉排序树的根(2)若二叉排序树非空,则将x与二叉排序树根进行比较,如果x的值等于根节点关键值,则停止插入;如果x的根节点值小于根节点关键值,则将x插入左子树;如果x的值大于根节点关键字的值,则将x插入右子树。
在左右两个子树的插入方法与整个二叉排序树相同。
算法如下:void InsertBST1(BiTree &T,BiTNode *s){//在二叉树排序树T中,插入一个结点s的递归算法BiTree t;t=SearchBST1(T,s->data);//若s的关键字不在T中出现,则插入if(!t){if(T==NULL)T=s; //空树时作为根结点else if(s->data<T->data)InsertBST1(T->lchild,s); //将s插入T的左子树 elseInsertBST1(T->rchild,s); //将s插入T的右子树}}3、二叉排序树的节点删除算法在二叉排序树中删除节点,首先要进行查找操作,以确定被删除的节点是否在二叉排序树中若不在,则不做任何操作;否则,假设要删除的节点为*p,节点*p的父节点为*f,并假设*p是*f的左孩子。
根据被删除节点*p有无孩子,删除部分可做以下3中情况讨论:(1)若*p为叶子节点,则可令其父节点*f的左孩子指针域为空,直接将其删除。
(2)若*p节点只有右子树或左子树,则可以将p的左子树或右子树直接改为其双亲节点f的左子树。
(3)若*p既有左子树又有右子树;将节点*s为*p的中序前驱。
首先找到*p 的中序前驱节点*s,然后用节点*s的值代替节点*p的值,再将节点*s删除,节点*s的原左子树改为*s的双亲节点*q的右子树;算法如下:DeleteNode(BiTree &T, int x){//从二叉树排序树T中删除任意结点,其关键字为xBiTree p,q,pParent,pChileNode;p=T; //从根结点开始查找pParent = NULL;// T的双亲为NULLwhile (p)// 开始查找关键字为x的结点p,及双亲pParent{if (x == p->data)break;pParent = p;p = x > p->data ? p->rchild : p->lchild;}if (p==NULL){printf("要删除的结点不存在\n");return false;} // 至此已找到目标结点p// pChileNode = p存在的孩子或NULL,左右都存在时,取左pChileNode = p->lchild!= NULL ? p->lchild : p->rchild;if(p->lchild==NULL||p->lchild==NULL){if (pParent == NULL)T= pChileNode;else{if(p==pParent->lchild)pParent->lchild= pChileNode;elsepParent->rchild = pChileNode;}free(p);//释放空间} // 当2个孩子都存在时else{//pChileNode已指向p->lchildq=p;while (pChileNode->rchild){//在p的左字树中查找中序p的前驱pChileNode,q为其双亲 q=pChileNode;pChileNode = pChileNode->rchild;}p->data=pChileNode->data;//p的前驱pChileNodede 的关键值赋给pif(q!=p) //将删除p转化为删除pChileNodede(最多只有左字树)结点{q->rchild=pChileNode->lchild;//p的左字树有右孩子 }else{q->lchild=pChileNode->lchild;//p的左字树有右孩子 }free(pChileNode);}return true;}4 调试与分析4.1 调试过程在调试程序是主要遇到以下几类问题:1.二叉树的存储结构的选择2.界面函数的调整3.删除的时候二叉树的调整4.2 程序执行过程执行过程如下图:图4.2.1 界面图图4.2.2 建立二叉排序树图4.2.3 先序遍历序列图4.2.4 中序遍历序列图4.2.5 删除结点图4.2.6 删除后的先序遍历序列图4.2.7 删除后的中序遍历序列沈阳航空航天大学课程设计报告错误!未指定书签。
参考文献[1] 殷人昆,等. 数据结构(用面向对象方法与C++描述)[M]. 北京:清华大学出版社,2002.[2] 宁正元,等. 算法与数据结构习题精解和实验指导[M]. 北京:清华大学出版社,2002.[3] 严蔚敏,吴伟民.数据结构[M].北京:清华大学出版社,2007.[4] 张长海,陈娟.C程序设计[M].北京:高等教育出版社,2004.[5] 谭浩强.C程序设计[M].北京:清华大学出版社,2005.附录(关键部分程序清单)#include <stdio.h>#include <malloc.h>#include <stdlib.h>typedef struct BiTNode{int data;struct BiTNode *lchild,*rchild;}BiTNode,*BiTree;/************************查找**************************/BiTree SearchBST1(BiTree T, int key){//在根指针T所指二叉排序树中递归查找关键字等于 key 的数据元素//若成功,返回指向该数据元素结点的指针,否则返回空指针;s为返回 //指针BiTree s;if(T==NULL) return NULL;else if(T->data==key) s=T;else if(T->data>key) //key大于当前结点的关键字,则查找左子树s=SearchBST1(T->lchild,key);else//key小于当前结点的关键字则查找右子树s=SearchBST1(T->rchild,key);return s;}/************************插入**************************/void InsertBST1(BiTree &T,BiTNode *s){//在二叉树排序树T中,插入一个结点s的递归算法BiTree t;t=SearchBST1(T,s->data);//若s的关键字不在T中出现,则插入if(!t){if(T==NULL)T=s; //空树时作为根结点else if(s->data<T->data)InsertBST1(T->lchild,s); //将s插入T的左子树elseInsertBST1(T->rchild,s); //将s插入T的右子树}}/***********************建立****************************/BiTree CreatBST(int n){//建立n个关键字的二叉排序树,//从键盘输入调建立n个关键字依次用InsertBST1(插入函数),//返回根结点TBiTree T,s;int i,key;T=NULL;printf("建树过程:\n");for(i=1;i<=n;i++){printf("插入结点关键值:\n");scanf("%d",&key);s=(BiTree)malloc(sizeof(BiTNode));//开辟存储单元,并对结点赋值s->data=key;s->lchild=NULL; s->rchild=NULL;InsertBST1(T,s); //调用插入算法}return T;}/***********************删除*****************************/ DeleteNode(BiTree &T, int x){//从二叉树排序树T中删除任意结点,其关键字为xBiTree p,q,pParent,pChileNode;p=T; //从根结点开始查找pParent = NULL;// T的双亲为NULLwhile (p)// 开始查找关键字为x的结点p,及双亲pParent{if (x == p->data)break;pParent = p;p = x > p->data ? p->rchild : p->lchild;}if (p==NULL){printf("要删除的结点不存在\n");return false;} // 至此已找到目标结点p// pChileNode = p存在的孩子或NULL,左右都存在时,取左pChileNode = p->lchild!= NULL ? p->lchild : p->rchild;if(p->lchild==NULL||p->lchild==NULL){if (pParent == NULL)T= pChileNode;else{if(p==pParent->lchild)pParent->lchild= pChileNode;elsepParent->rchild = pChileNode;}free(p);//释放空间} // 当2个孩子都存在时else{//pChileNode已指向p->lchildq=p;while (pChileNode->rchild){//在p的左字树中查找中序p的前驱pChileNode,q为其双亲 q=pChileNode;pChileNode = pChileNode->rchild;}p->data=pChileNode->data;//p的前驱pChileNodede 的关键值赋给p if(q!=p) //将删除p转化为删除pChileNodede(最多只有左字树)结点 {q->rchild=pChileNode->lchild;//p的左字树有右孩子 }else{q->lchild=pChileNode->lchild;//p的左字树有右孩子 }free(pChileNode);}return true;}/**********************先序遍历************************/void PreOrder(BiTree T){if(T!=NULL){printf("%3d",T->data);PreOrder(T->lchild);PreOrder(T->rchild);}}/*********************中序遍历**************************/void InOrder(BiTree T){if(T!=NULL){InOrder(T->lchild);printf("%3d",T->data);InOrder(T->rchild);}}/*********************主函数******************************/void main(){int n,x,choose;BiTree T;while(1){printf("\n\n*******************************************\n"); printf("************************************\n");printf("二叉排序树算法\n");printf("************************************\n"); printf("请选择操作:\n");printf("0.退出\n");printf("1.建立一棵二叉排序树\n");printf("2.删除一个任意结点\n");printf("3.先序遍历序列\n");printf("4.中序遍历序列\n");printf("Please choose:");scanf("%d",&choose);switch(choose){case 1:system("cls");printf("输入结点个数n=:\n");scanf("%d",&n);T=CreatBST(n);system("cls");break;case 2:system("cls");printf("输入想要删除的结点x=:\n"); scanf("%d",&x);DeleteNode(T, x);system("cls");break;case 3:system("cls");printf("先序遍历序列:\n");PreOrder(T);printf("\n");break;case 4:system("cls");printf("中序遍历序列:\n");InOrder(T);printf("\n");break;default:exit(0);}}}沈阳航空航天大学课程设计报告课程设计总结:指导教师评语:指导教师(签字):年月日课程设计成绩。