数据结构课程设计 - 二叉树系统实现java

205 阅读5分钟

数据结构课程设计 - 二叉树系统实现java

问题描述

从键盘读入一组数据,建立二叉排序树并对其进行查找、遍历、格式化输出操作。设数据元素关键字序列为{11,33,44,55,58,79,88}。

需求分析

1.按照用户的需求,构建二叉排序树。

2.对二叉排序树进行遍历输出操作。

3.对二叉排序树进行查找,包括成功和不成功两种情况,并给出查找长度。

4.对二叉排序树进行插入操作。

5.对二叉排序树进行删除操作。

1、主界面设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AV71Z49H-1619058739933)(file:////Users/zhenggengqiong/Library/Group%20Containers/UBF8T346G9.Office/TemporaryItems/msohtmlclip/clip_image002.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uraZLqPC-1619058739937)(file:////Users/zhenggengqiong/Library/Group%20Containers/UBF8T346G9.Office/TemporaryItems/msohtmlclip/clip_image003.jpg)]

为了方便操作,界面结合了多个操作,并且可以通过相应的序号调取,实现对二叉排序树的各个操作。

2、存储结构设计

二叉排序树是通过树的基本结构类型构成。有存储数据的data和左右两结点lchild以及rchild。

3、系统功能设计

本程序共设置了8个子功能菜单。具体如下。

1、创建二叉树,creatBST,其参数为整形数组,通过循环调用add函数,不断插入数组值实现。可根据系统提示,输入值,并以逗号隔开。长度可以根据输入长度动态变化。

2、查找功能,通过searchData函数实现。查找到则返回1,并可以输出查找长度;如果查找失败则返回0。主要是通过判断查询值key与结点的大小,递归向左子树或右子树查找,知道找到相等的值,否则查找失败。

3、删除功能,主要通过deleteNode实现。还有search和searchParent找到要删除的结点和父节点删除有三种情况:

1)是要删除的是树中的叶子节点,则判断是左子节点还是右子节点,另其为空。

2)如果要删除的结点有两颗子树,则通过deleteRightNodeMin函数,删除右子树最小的结点,并替换位置(也可以找左子树最大的结点替换)。

3)如果要删除的结点有一颗子树,通过判断是左子树还是右子树,进行删除操作。

4、插入功能,通过add函数添加结点,判断当前树的情况,再通过比较插入值key的大小,如果小于当前结点则继续递归左子树,如果大于等于,则递归右子树(最好避免有相等的数据)。

5、前序遍历,preOrderTraverse,DLR输出二排序树的结点,如果树为空,则出现提示。

6、中序遍历,infixOrderTraverse,LDR输出二叉排序树的结点。

7、后续遍历,postOrderTraverse,LRD输出二叉排序树的结点。

8、退出系统。

4、模块设计

程序主要分为主程序模块和二叉排序树各个操作的模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WW5RnNOk-1619058739940)(file:////Users/zhenggengqiong/Library/Group%20Containers/UBF8T346G9.Office/TemporaryItems/msohtmlclip/clip_image002.jpg)]

5、系统子程序及功能设计

(1) public void createBST(int[] array),创建二叉排序树,通过循环调用add函数将用户输入的数据插入树中,小于当前节点的值递归左子树,大于等于当前节点值的递归右子树,从而创建出一颗二叉排序树。

(2) public int searchData(Node node, int key),查找数据,通过比较要查找的数据与当前结点数据的大小,如果小于则向左子树递归,如果大于或等于则向右子树递归,直到找到与查找结点相等的。查找成功返回1,查找失败返回0。还可以计算查找长度。

(3) public void add(Node node),插入数据,先判断树的状态,在比较要插入结点与当前节点的大小,如果小于当前结点,则递归向左子树,如果大于或等于则向右子树递归。

(4) public void deleteNode(int data),删除数据。通过是否有左右子树判断,如果没有左右子树,则要删除的为叶子节点;如果有两颗子树,则通过public int deleteRightTreeMin(Node node)函数,替换右子树中最小的节点值;如果有一颗子树,则判断是左子树还是右子树,进行删除操作。

(5) public void preOrderTraverse(),前序遍历,如果树为空,则提示不能遍历。先输出根节点,再递归输出左子树,再递归输出右子树。

(6) public void infixOrderTraverse(),中序遍历,如果树为空,则提示不能遍历。先递归输出左子树,在输出根节点,再递归输出右子树。

(7) public void postOrderTraverse(),后续遍历,如果树为空,则提示不能遍历。先递归输出左子树,再递归输出右子树,再输出根节点。

6、函数主要调用关系图

函数主要调用关系图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7orHRQ5F-1619058739946)(file:////Users/zhenggengqiong/Library/Group%20Containers/UBF8T346G9.Office/TemporaryItems/msohtmlclip/clip_image002.jpg)]

对二叉排序树的存储结构类型定义如下:

public class Node {
	int data;
	Node lchild;
	Node rchild;
}

具体功能代码如下

(1)创建功能

//创建树
	public void createBST(int[] array) {
		for(int i = 0;i<array.length;i++) {
			add(new Node(array[i]));
		}
	}

(2)查找功能

Node lastNode = null;// 最近查找的结点
	int n = 1;//计算查找长度
	// 查找数据,查找成功返回1,失败返回0
	public int searchData(Node node, int key) {
		if (node == null) {
			return 0;
		}
		if (key == node.data) {
			lastNode = node;
			return 1;
		} else if (key < node.data) {// 查找数据小于当前结点
			n++;
			return searchData(node.lchild, key);// 递归向左子树查找
		} else {// 查找数据大于或等于当前结点
			n++;
			return searchData(node.rchild, key);// 递归向右子树查找
		}
	}

(3)删除功能

public Node search(int data) {
		if (root == null) {
			return null;
		} else {
			return root.search(data);
		}
	}

	// 查找父节点
	public Node searchParent(int data) {
		if (root == null) {
			return null;
		} else {
			return root.searchParent(data);
		}
	}

	// 删除并返回右子树最小结点
	public int deleteRightTreeMin(Node node) {
		Node target = node;
		// 循环查找左子节点,直到最小值
		while (target.lchild != null) {
			target = target.lchild;
		}
		// 这时target指向最小值
		// 删除最小结点
		deleteNode(target.data);
		return target.data;
	}

	// 删除结点
	public void deleteNode(int data) {
		if (root == null) {
			return;
		} else {
			// 先找到要删除的结点 targetNode
			Node targetNode = search(data);
			// 如果没找到
			if (targetNode == null) {
				System.out.println("不存在此数");
			}
			// 如果没有子节点
			if (root.lchild == null && root.rchild == null) {
				root = null;
				return;
			}

			// 找到targetNode的父节点
			Node parent = searchParent(data);

			// 共有三种情况:

			// ①如果要删除的是叶子节点
			if (targetNode.lchild == null && targetNode.rchild == null) {
				// 判断要删除的是其父节点的左结点还是右结点
				if (parent.lchild != null && parent.lchild.data == data) {// 是左子节点
					parent.lchild = null;
				} else if (parent.rchild != null && parent.rchild.data == data) {// 是右子节点
					parent.rchild = null;
				}
			}

			// ②如果要删除的是有两颗子树的结点
			else if (targetNode.lchild != null && targetNode.rchild != null) {
				// 可以找右子树的最小节点,或者找左子树的最大结点 替换要删除的结点
				int minData = deleteRightTreeMin(targetNode.rchild);
				targetNode.data = minData;
			}

			// ③如果要删除的是有一颗子树的结点
			else {
				// 如果是有左子结点
				if (targetNode.lchild != null) {
					// 如果是target是parent的左子节点
					if (parent.lchild.data == data) {
						parent.lchild = targetNode.lchild;
					} else {// 如果是target是parent的右子节点
						parent.rchild = targetNode.lchild;
					}
				} else {// 如果是有右子结点
					if (parent.lchild.data == data) {
						parent.lchild = targetNode.rchild;
					} else {
						parent.rchild = targetNode.rchild;
					}
				}
			}
		}
	}
public Node search(int data) {
		if (this.data == data) {// 如果当前结点即为所查找的值,直接返回该节点
			return this;
		} else if (data < this.data) {// 如果查找的值小于当前结点,向左子树递归查找
			// 如果左子结点空
			if (this.lchild == null) {
				return null;
			}
			return this.lchild.search(data);
		} else {// 如果查找的值大于等于当前结点,向右子树递归查找
			if (this.rchild == null) {
				return null;
			}
			return this.rchild.search(data);
		}
	}

	// 查找要删除节点的父节点
	public Node searchParent(int data) {
		// 如果当前结点就是要删除的结点的父节点,则返回
		if ((this.lchild != null && this.lchild.data == data) || (this.rchild != null && this.rchild.data == data)) {
			return this;
		} else {
			// 如果要查找的值小于当前结点的值,并且当前结点的左子节点不为空
			if (data < this.data && this.lchild != null) {
				return this.lchild.searchParent(data);// 向左子树递归查找
			} else if (data >= this.data && this.rchild != null) {
				return this.rchild.searchParent(data);// 向右子树递归查找
			} else {
				return null; // 无父节点,返回空
			}
		}
	}

(4)插入功能

public void add(Node node) {
		if (root == null) {
			root = node;// root为空,直接指向node
		} else {
			root.add(node);
		}
	}
	// 添加结点的方法--递归实现
	public void add(Node node) {
		if (node == null) {
			return;
		}
		// 判断添加结点与当前子树根节点的大小关系
		if (node.data < this.data) {
			if (this.lchild == null) {
				this.lchild = node;
			} else {
				// 递归向左子树添加
				this.lchild.add(node);
			}
		} else {
			if (this.rchild == null) {
				this.rchild = node;
			} else {
				// 递归向右子树添加 如果有相等的结点,则设添加到右子树
				this.rchild.add(node);
			}
		}
	}

(5)前序遍历

public void preOrderTraverse() {
		if (root == null) {
			System.out.println("当前树为空,不能遍历");
		} else {
			root.preOrderTraverse();
		}
	}	
public void preOrderTraverse() {
		System.out.print(this.data + "->");

		if (this.lchild != null) {
			this.lchild.infixOrderTraverse();
		}

		if (this.rchild != null) {
			this.rchild.infixOrderTraverse();
		}
	}

(6)中序遍历

public void infixOrderTraverse() {
		if (root == null) {
			System.out.println("当前树为空,不能遍历");
		} else {
			root.infixOrderTraverse();
		}
	}
public void infixOrderTraverse() {
		if (this.lchild != null) {
			this.lchild.infixOrderTraverse();
		}
		System.out.print(this.data + "->");
		if (this.rchild != null) {
			this.rchild.infixOrderTraverse();
		}
	}

(7)后续遍历

public void postOrderTraverse() {
		if (root == null) {
			System.out.println("当前树为空,不能遍历");
		} else {
			root.postOrderTraverse();
		}
	}
public void postOrderTraverse() {
		if (this.lchild != null) {
			this.lchild.infixOrderTraverse();
		}

		if (this.rchild != null) {
			this.rchild.infixOrderTraverse();
		}

		System.out.print(this.data + "->");
	}

实验结果

image-20210422102814748

image-20210422102820519

image-20210422102824940

image-20210422102829373

image-20210422102835379

image-20210422102839433

image-20210422102843389

image-20210422102847870