算法与数据结构始终是计算机基础的重要一环,今天咱们来讨论下 Java 中二叉树的实现以及一些简单的小算法,如二分查找,归并排序等。java
二分查找git
二分查找是一种在有序数组中查找某一特定元素的搜索算法,它在开发中应用的也是很是普遍,须要注意的是二分法是创建在有序数组基础上的快速查找,因此通常须要先对数组进行排序。github
算法思想算法
搜素过程从数组的中间元素开始,若是中间元素正好是要查找的元素,则搜素过程结束;数组
若是某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,并且跟开始同样从中间元素开始比较;数据结构
若是在某一步骤数组为空,则表明找不到,若是找到则返回post
这种搜索算法的特色是每一次比较都使搜索范围缩小一半。测试
实现思路ui
找出位于数组中间的值,并存放在一个变量中(为了下面的说明,变量暂时命名为 base 基准值);this
须要找到的 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 不能大于 to if(from <= to ){ //获取数组中间下标 int centerIndex = (from + to) / 2; //将数组中间值做为基准值比较 int base = data[centerIndex]; //目标值比基准值大,则须要往中间下标后查找,起始点为 centerIndex + 1 if (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小的下标 end while(end > start && data[end] >= base){ end --; } // 找到比base小的下标,与base交换位置 if(end > start && data[end] < base){ swap(data,start,end); } //开始从前日后找,找到比基准值小的放过,让start下标加1,直到找到比base大的下标 end while(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; //根节点不为null if(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 ----文中代码地址
关注一下,我写的就更来劲儿啦