二分法与二叉树的 Java 实现
算法与数据结构始终是计算机基础的重要一环,今天我们来讨论下 Java 中二叉树的实现以及一些简单的小算法,如二分查找,归并排序等。
二分查找
二分查找是一种在有序数组中查找某一特定元素的搜索算法,它在开发中应用的也是非常广泛,需要注意的是二分法是建立在有序数组基础上的快速查找,所以一般需要先对数组进行排序。
算法思想
搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;
如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较;
如果在某一步骤数组为空,则代表找不到,如果找到则返回
这种搜索算法的特点是每一次比较都使搜索范围缩小一半。
实现思路
找出位于数组中间的值,并存放在一个变量中(为了下面的说明,变量暂时命名为 base 基准值);
需要找到的 key 和 base 进行比较;
如果 key 值大于 base,则把数组中间位置作为下一次计算的起点;重复 1 2;
如果 key 值小于 base,则把数组中间位置作为下一次计算的终点;重复 1 2;
如果 key 值等于 base,则返回数组下标,完成查找。
代码如下:
/*** 二分法查找 : 在有序数组中查找特定元素的算法。(有序数组)*/public class TwoSplitSearch {public static void main(String[] args) {int[] data = {-10, -3, 1, 4, 6, 8, 10, 22, 33, 44, 100, 203};//存在System.out.println(towSplitSearch(data, 0, data.length - 1,4));System.out.println(towSplitSearch(data, 0, data.length - 1,9));}/*** 二分查找** @param data 有序数组* @param from 开始下标* @param to 终止下标* @param key 目标值* @return int 目标值的下标,如果没有返回 -1*/private static int towSplitSearch(int[] data, int from, int to, int key) {if (from < 0 || to < 0) {return -1;}// 判断 from 不能大于 toif(from <= to ){//获取数组中间下标int centerIndex = (from + to) / 2;//将数组中间值作为基准值比较int base = data[centerIndex];//目标值比基准值大,则需要往中间下标后查找,起始点为 centerIndex + 1if (key > base) {from = centerIndex + 1;//目标值比基准值小,则需要往中间下标1前查找,终止点为 centerIndex - 1} else if (key < base) {to = centerIndex - 1;} else {return centerIndex;}}else{return -1;}return towSplitSearch(data, from, to, key);}}
在了解二分法之后,还需要知道一些简单的排序算法,下面我们介绍下快速排序法和归并排序法。
快速排序
快速排序是通过在数组中选定基准值,通过其他元素与基准值的比较,使得基准值前是比基准值小的数组,基准值后是比基准值大的数组,再通过递归调用完成排序的方法。
算法思想
通过一趟排序将要排序的数据分割成独立的两部分;
其中一部分的所有数据都比另外一部分的所有数据都要小,基准值在中间;
再按此方法对这两部分数据分别进行快速排序;
整个排序过程可以递归进行,以此达到整个数据变成有序序列。
实现思路
选取基准值,一般选择数组第一个元素
从后往前与基准值比较,如果比基准值小,与其调换位置
再从前往后比较,如果比基准值大,与其调换位置
经过比较后,达到基准值前元素都比它小,基准值后元素都比它大
递归比较,将基准值前所有元素看做一个新数组比较,后所有元素看做一个新数组比较
代码如下:
public class QuickSort {public static void main(String[] args){int[] a = {1,5,45,-2,-44,3,20,8,11,-7};System.out.println(Arrays.toString(a));quickSort(a);System.out.println(Arrays.toString(a));}private static void quickSort(int[] data) {if(data.length > 0){quickSubSort(data,0,data.length-1);}}/**** @param data* @param low 最小下标* @param high 最高小标*/private static void quickSubSort(int[] data, int low, int high) {// 定义基准值int base = data[low];//定义开始下标int start = low;//定义结束下标int end = high;while(end > start){//从后往前找,找到比基准值大的 放过,让下标减1 ,直到找到比base小的下标 endwhile(end > start && data[end] >= base){end --;}// 找到比base小的下标,与base交换位置if(end > start && data[end] < base){swap(data,start,end);}//开始从前往后找,找到比基准值小的放过,让start下标加1,直到找到比base大的下标 endwhile(end > start && data[start] <= base){start ++;}// 找到比base大的下标,与base交换位置if(end > start && data[start] > base){swap(data,start,end);}}//第一次循环后 开始递归调用//基准值前的数if(start > low){quickSubSort(data,low,start-1);}//基准值后的数if(end < high){quickSubSort(data,end+1,high);}}/*** 交换位置* @param data* @param start* @param end*/private static void swap(int[] data, int start, int end) {int temp = data[start];data[start] = data[end];data[end] = temp;}}
归并排序
归并排序 是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。
算法思想
将一个序列拆分成两个序列;
先使每个子序列有序,再使子序列段间有序;
将已有序的子序列合并,得到完全有序的序列。
实现思路
使用二路归并法,将两个有序表合并成一个有序表;
新建临时数组,用于存储比较后的数值;
将左右数组同时比较,小的放入临时数组中;
一旦有某一数组比较完成,则将剩下的数组全放到临时数组中;
最后将临时数组复制回原数组。
代码如下:
/*** 归并排序,二分归并* 新建临时数组,将左右数组同时比较,小的放入临时数组中* 一旦有某一数组比较完成,则将剩下的数组全放到临时数组中* 两个循环只会走一个,因为只有一个数组没有遍历完* <p>* 最后将临时数组复制回原数组*/public class MergeSort {public static void main(String[] args) {int[] a = {1, 5, 45, -2, -44, 3, 20, 8, 11, -7};System.out.println(Arrays.toString(a));mergeSort(a);System.out.println(Arrays.toString(a));}private static void mergeSort(int[] a) {if (a.length > 0) {mergeSubSort(a, 0, a.length - 1);}}/*** 二分归并** @param data* @param left* @param right*/private static void mergeSubSort(int[] data, int left, int right) {if (left >= right) {return;}//获取中间值int center = (left + right) / 2;//划分成左右2个数组mergeSubSort(data, left, center);mergeSubSort(data, center + 1, right);//开始归并排序merge(data, left, center, center + 1, right);}/*** @param data* @param leftStart* @param leftEnd* @param rightStart* @param rightEnd*/private static void merge(int[] data, int leftStart, int leftEnd, int rightStart, int rightEnd) {//定义循环开始左下标int leftIndex = leftStart;//定义循环开始右下标int rightIndex = rightStart;//定义临时数组开始下标int tempIndex = 0;//定义临时数组int[] temp = new int[rightEnd - leftStart + 1];//开始循环 ,当左右有任意一方下标达到临界值 停止循环while (leftIndex <= leftEnd && rightIndex <= rightEnd) {//比较最小值,将最小值放到临时数组中if (data[leftIndex] > data[rightIndex]) {temp[tempIndex++] = data[rightIndex++];} else {temp[tempIndex++] = data[leftIndex++];}}//有一方数组循环完成// 一下循环只有其实只有一个执行while (leftIndex <= leftEnd) {temp[tempIndex++] = data[leftIndex++];}while (rightIndex <= rightEnd) {temp[tempIndex++] = data[rightIndex++];}//将临时数组复制回原数组tempIndex = leftStart;for (int element : temp) {data[tempIndex++] = element;}}}
二叉树
二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加。但是它也有自己的缺点:删除操作复杂。查找,插入,删除的复杂度都为 O(logN)。
二叉查找树:是每个结点最多有两个子树的有序树,在使用二叉树的时候,数据并不是随便插入到节点中的,一个节点的左子节点的关键值必须小于此节点,右子节点的关键值必须大于或者是等于此节点,所以又称二叉排序树、二叉搜索树。
下面我们就以 int 类型作为树的根节点,来看看 二叉树的 Java 实现。
定义节点
/*** 定义节点类型* 主要属性: value 值 left 左节点 right 右节点**/public class TreeNode {//关键值private int value;//左子树private TreeNode left;//右子树private TreeNode right;//删除状态private Boolean deleteStatus;public TreeNode() {}public TreeNode(int value) {this(value,null,null,false);}public TreeNode(int value, TreeNode left, TreeNode right, Boolean deleteStatus) {this.value = value;this.left = left;this.left = right;this.deleteStatus = deleteStatus;}…… get set 方法}
定义二叉树
利用节点来创建二叉树,由于二叉树删除操作比较复杂,这里使用删除标识 deleteStatus 来记录删除状态。代码中主要写的是树的插入,查找,遍历。
/*** 创建 树* 主要属性 root 只有获取根节点方法** 主要方法* 插入* 查找* 遍历**/public class BinaryTree {private TreeNode root;public TreeNode getRoot() {return root;}/*** 向树中插入数据* @param value*/public void insert(int value){TreeNode newNode = new TreeNode(value);//插入数据时判断是否是根节点插入if(root == null){root = newNode;root.setLeft(null);root.setRight(null);}else{// 不是根节点插入,获取根节点作为当前节点TreeNode currentNode = root;TreeNode parentNode;//循环插入,直到找到叶子节点,将新值插入while(true){//将根节点赋值给父节点parentNode = currentNode;if(newNode.getValue() > currentNode.getValue()) {currentNode = currentNode.getRight();if (currentNode == null) {parentNode.setRight(newNode);return;}}else{currentNode = currentNode.getLeft();if(currentNode == null){parentNode.setLeft(newNode);return;}}}}}/*** 查找** @param value* @return*/public TreeNode find(int value){//获取根节点作为当前节点TreeNode currentNode = root;//根节点不为nullif(root != null){while (currentNode.getValue() != value){if(value > currentNode.getValue()){currentNode = currentNode.getRight();}else{currentNode = currentNode.getLeft();}if(currentNode == null){return null;}}if(currentNode.getDeleteStatus()){return null;}else{return currentNode;}}else{return null;}}/*** 中序遍历* @return*/public void inOrder(TreeNode root){if(root != null){inOrder(root.getLeft());System.out.println(root.getValue());inOrder(root.getRight());}}/*** 前序遍历* @return*/public void preOrder(TreeNode root){if(root != null){System.out.println(root.getValue());preOrder(root.getLeft());preOrder(root.getRight());}}/*** 后序遍历* @return*/public void postOrder(TreeNode root){if(root != null){postOrder(root.getLeft());postOrder(root.getRight());System.out.println(root.getValue());}}}
测试代码
public static void main(String[] args){BinaryTree binaryTree = new BinaryTree();binaryTree.insert(10);binaryTree.insert(3);binaryTree.insert(5);binaryTree.insert(20);binaryTree.insert(30);binaryTree.insert(15);binaryTree.insert(45);binaryTree.insert(123);TreeNode root = binaryTree.getRoot();System.out.println("跟节点是"+root.getValue());TreeNode treeNode = binaryTree.find(5);if(treeNode != null){System.out.println("找到了");}else{System.out.println("没找到");}System.out.println("===前序====");binaryTree.preOrder(root);System.out.println("===中序====");binaryTree.inOrder(root);System.out.println("===后序====");binaryTree.postOrder(root);}
以上便是二叉树的 Java 实现,相关代码参照参考资料。
参考资料:
https://github.com/fanpengyi/jianzhi-offer ----文中代码地址
关注一下,我写的就更来劲儿啦
二分法与二叉树的 Java 实现的更多相关文章
- 【数据结构】之二叉树的java实现
转自:http://blog.csdn.net/wuwenxiang91322/article/details/12231657 二叉树的定义: 二叉树是树形结构的一个重要类型.许多实际问题抽象出来的 ...
- 数据结构二叉树的java实现,包括二叉树的创建、搜索、删除和遍历
根据自己的学习体会并参考了一些网上的资料,以java写出了二叉树的创建.搜索.删除和遍历等操作,尚未实现的功能有:根据先序和中序遍历,得到后序遍历以及根据后序和中序遍历,得到先序遍历,以及获取栈的深度 ...
- 二叉树的Java实现及特点总结
二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加.但是他也有自己的缺点:删除操作复杂. 我们先介绍一些关于二叉树的概念名词. 二叉树: ...
- 剑指offer面试题6 重建二叉树(java)
注:(1)java中树的构建 (2)构建子树时可以直接利用Arrays.copyOfRange(preorder, from, to),这个方法是左开右闭的 package com.xsf.SordF ...
- 【数据结构】之二叉树(Java语言描述)
有关树的一些基础知识点请参考[这篇文章]. 本文主要记录Java语言描述的二叉树相关的一些操作,如创建.遍历等. 首先,我们需要一个表示树中节点的数据结构TreeNode,代码如下: public c ...
- 剑指Offer-22.从上往下打印二叉树(C++/Java)
题目: 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 分析: 按层次打印二叉树的节点,重点就是我们在打印一层节点的时候,同时按顺序保存好当前节点的下一层节点,也就是左节点和右节点,当此层节点 ...
- 剑指Offer-4.重建二叉树(C++/Java)
题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2 ...
- 【LeetCode】101. Symmetric Tree 对称二叉树(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS BFS 日期 [LeetCode] 题目地址 ...
- 轻松搞定面试中的二叉树题目(java&python)
树是一种比较重要的数据结构,尤其是二叉树.二叉树是一种特殊的树,在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子),并且二叉树的子树有左右之分,其次序不能任意颠倒.二叉 ...
随机推荐
- node传统读取文件和promise,async await,
先上传统文件加载方式代码,传统方式在处理多层嵌套时代码比较混乱 const fs = require('fs') //引入文件系统 function readFile (cb) { fs.readFi ...
- ArcCore重构-Makefile模块化
基于官方arc-stable-9c57d86f66be,AUTOSAR版本3.1.5 基本问题 2. 编译系统中代码文件是否编译及目标文件集中定义在boards/board_common.mk,而 ...
- Python之命名空间、闭包、装饰器
一.命名空间 1. 命名空间 命名空间是一个字典,key是变量名(包括函数.模块.变量等),value是变量的值. 2. 命名空间的种类和查找顺序 - 局部命名空间:当前函数 - 全局命名空间:当前模 ...
- socket和webService的区别
网络七层协议为:物理层.数据链路层.网络层.传输层.会话层.表示层.应用层 socket 只是 java在网络层定义的类,用来实现网络层.上面的各层需要我们自己在程序里实现. 例如端口可以自己定义 . ...
- 二十四、Hadoop学记笔记————Spark的架构
master为主节点 一个集群中可能运行多个application,因此也可能会有多个driver DAG Scheduler就是讲RDD Graph拆分成一个个stage 一个Task对应一个Spa ...
- 探讨SELECT语句的元数据&动态取样&读一致性导致的一致性读和递归操作
前几天,论坛上的同行在讨论SELECT语句的元数据,动态取样和读一致性导致的一致性读和递归问题,今天有时间,就试着进行了测试,本人测试环境如下: win7_64+Oracle11.2.0.4_64 那 ...
- Manifest文件的最新理解
今天看了Manifest文件内容的相关视频,感觉对知识的理解深刻了一些: 首先,先来说说这个文件的作用,这个文件可以说是聚集了很多个标签,其实对于每个主标签,在将来编译的时候,都会被处理成一个类,而标 ...
- 第四章——训练模型(Training Models)
前几章在不知道原理的情况下,已经学会使用了多个机器学习模型机器算法.Scikit-Learn很方便,以至于隐藏了太多的实现细节. 知其然知其所以然是必要的,这有利于快速选择合适的模型.正确的训练算法. ...
- spring boot sso
https://hellokoding.com/hello-single-sign-on-sso-with-json-web-token-jwt-spring-boot/ https://github ...
- mongodb的设计特征
MongoDB 的设计目标是高性能.可扩展.易部署.易使用,存储数据非常方便.其主要功能特性如下. (1)面向集合存储,容易存储对象类型的数据.在MongoDB 中数据被分组存储在集合中,集合类似 ...