算法与数据结构始终是计算机基础的重要一环,今天我们来讨论下 Java 中二叉树的实现以及一些简单的小算法,如二分查找,归并排序等。

  • 二分查找

二分查找是一种在有序数组中查找某一特定元素的搜索算法,它在开发中应用的也是非常广泛,需要注意的是二分法是建立在有序数组基础上的快速查找,所以一般需要先对数组进行排序。

  • 算法思想

  1. 搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;

  2. 如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较;

  3. 如果在某一步骤数组为空,则代表找不到,如果找到则返回

这种搜索算法的特点是每一次比较都使搜索范围缩小一半。

  • 实现思路

  1. 找出位于数组中间的值,并存放在一个变量中(为了下面的说明,变量暂时命名为 base 基准值);

  2. 需要找到的 key 和 base 进行比较;

  3. 如果 key 值大于 base,则把数组中间位置作为下一次计算的起点;重复 1 2;

  4. 如果 key 值小于 base,则把数组中间位置作为下一次计算的终点;重复 1 2;

  5. 如果 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);    }}

在了解二分法之后,还需要知道一些简单的排序算法,下面我们介绍下快速排序法和归并排序法。

 

  • 快速排序

快速排序是通过在数组中选定基准值,通过其他元素与基准值的比较,使得基准值前是比基准值小的数组,基准值后是比基准值大的数组,再通过递归调用完成排序的方法。

  • 算法思想

  1. 通过一趟排序将要排序的数据分割成独立的两部分;

  2. 其中一部分的所有数据都比另外一部分的所有数据都要小,基准值在中间;

  3. 再按此方法对这两部分数据分别进行快速排序;

  4. 整个排序过程可以递归进行,以此达到整个数据变成有序序列。

  • 实现思路

  1. 选取基准值,一般选择数组第一个元素

  2. 从后往前与基准值比较,如果比基准值小,与其调换位置

  3. 再从前往后比较,如果比基准值大,与其调换位置

  4. 经过比较后,达到基准值前元素都比它小,基准值后元素都比它大

  5. 递归比较,将基准值前所有元素看做一个新数组比较,后所有元素看做一个新数组比较

  • 代码如下:

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;    }}
  • 归并排序

归并排序 是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。

  • 算法思想

  1. 将一个序列拆分成两个序列;

  2. 先使每个子序列有序,再使子序列段间有序;

  3. 将已有序的子序列合并,得到完全有序的序列。

  • 实现思路

  1. 使用二路归并法,将两个有序表合并成一个有序表;

  2. 新建临时数组,用于存储比较后的数值;

  3. 将左右数组同时比较,小的放入临时数组中;

  4. 一旦有某一数组比较完成,则将剩下的数组全放到临时数组中;

  5. 最后将临时数组复制回原数组。

  • 代码如下:

/** * 归并排序,二分归并 * 新建临时数组,将左右数组同时比较,小的放入临时数组中 * 一旦有某一数组比较完成,则将剩下的数组全放到临时数组中 * 两个循环只会走一个,因为只有一个数组没有遍历完 * <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   ----文中代码地址

关注一下,我写的就更来劲儿啦 

二分法与二叉树的 Java 实现的更多相关文章

  1. 【数据结构】之二叉树的java实现

    转自:http://blog.csdn.net/wuwenxiang91322/article/details/12231657 二叉树的定义: 二叉树是树形结构的一个重要类型.许多实际问题抽象出来的 ...

  2. 数据结构二叉树的java实现,包括二叉树的创建、搜索、删除和遍历

    根据自己的学习体会并参考了一些网上的资料,以java写出了二叉树的创建.搜索.删除和遍历等操作,尚未实现的功能有:根据先序和中序遍历,得到后序遍历以及根据后序和中序遍历,得到先序遍历,以及获取栈的深度 ...

  3. 二叉树的Java实现及特点总结

    二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加.但是他也有自己的缺点:删除操作复杂. 我们先介绍一些关于二叉树的概念名词. 二叉树: ...

  4. 剑指offer面试题6 重建二叉树(java)

    注:(1)java中树的构建 (2)构建子树时可以直接利用Arrays.copyOfRange(preorder, from, to),这个方法是左开右闭的 package com.xsf.SordF ...

  5. 【数据结构】之二叉树(Java语言描述)

    有关树的一些基础知识点请参考[这篇文章]. 本文主要记录Java语言描述的二叉树相关的一些操作,如创建.遍历等. 首先,我们需要一个表示树中节点的数据结构TreeNode,代码如下: public c ...

  6. 剑指Offer-22.从上往下打印二叉树(C++/Java)

    题目: 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 分析: 按层次打印二叉树的节点,重点就是我们在打印一层节点的时候,同时按顺序保存好当前节点的下一层节点,也就是左节点和右节点,当此层节点 ...

  7. 剑指Offer-4.重建二叉树(C++/Java)

    题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2 ...

  8. 【LeetCode】101. Symmetric Tree 对称二叉树(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS BFS 日期 [LeetCode] 题目地址 ...

  9. 轻松搞定面试中的二叉树题目(java&python)

    树是一种比较重要的数据结构,尤其是二叉树.二叉树是一种特殊的树,在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子),并且二叉树的子树有左右之分,其次序不能任意颠倒.二叉 ...

随机推荐

  1. Hive中Join的原理和机制

    转自:http://lxw1234.com/archives/2015/06/313.htm 笼统的说,Hive中的Join可分为Common Join(Reduce阶段完成join)和Map Joi ...

  2. 洛谷 P2725 解题报告

    P2725 邮票 Stamps 题目背景 给一组 N 枚邮票的面值集合(如,{1 分,3 分})和一个上限 K -- 表示信封上能够贴 K 张邮票.计算从 1 到 M 的最大连续可贴出的邮资. 题目描 ...

  3. 杨老师课堂_Java核心技术下之控制台模拟记事本案例

    预览效果图: 背景介绍: 编写一个模拟记事本的程序通过在控制台输入指令,实现在本地新建文件打开文件和修改文件等功能. 要求在程序中: 用户输入指令1代表"新建文件",此时可以从控制 ...

  4. Python_字符串格式化

    #冒泡排序 array = [1,2,3,6,5,4] for i in range(len(array)): for j in range(i): if array[j] > array[j ...

  5. 读《图解HTTP》有感-(了解web及网络基础)

    写在前面 <图解HTTP>是由上野宣先生著,于均良先生译 闲暇之余!写写博文甚是高兴.如有不准确,望各位斧正.共同学习! 正文 HTTP协议是什么?能做什么? HTTP(超文本传输协议)实 ...

  6. ActiveMQ的消息持久化机制

    为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一般都会采用持久化机制. ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式 ...

  7. shell 中的for、while循环及if语句

    shell与其他语言一样也支持for.while循环 for循环的一般格式如下: #!/bin/sh for 变量 in 列表 do command command command ......... ...

  8. 我珍藏的神兵利器 - 效率工具for Win[转]

        工欲善其事必先利其器. 我一直都在不断挑选和优化自己的兵器,以追求着最高效率. 此篇分享下我的私家珍藏的各种神兵利器.如果有朋友能推荐更好的,那就不枉此篇. 分为Windows软件和开发工具两 ...

  9. Sign http

    http接口请求参数签名工具类的实现和测试代码 http://blog.csdn.net/5iasp/article/details/52539901 http://www.what21.com/pr ...

  10. springmvc配置详解 教程

    https://www.cnblogs.com/sunniest/p/4555801.html