Java数据结构与算法高级篇之树、图

数据是基础,算法是灵魂

本文出自门心叼龙的博客,属于原创类容,转载请注明出处。https://blog.csdn.net/geduo_83/article/details/86557628

源码下载地址:https://download.csdn.net/download/geduo_83/10913510

初级篇:Java数据结构与算法初级篇之数组、集合和散列表
中级篇:Java数据结构与算法中级篇之栈、队列、链表
高级篇:Java数据结构与算法高级篇之树、图

理论篇:Java数组、集合、散列表常见算法浅析
理论篇:Java栈、队列、链表常见算法浅析
理论篇:Java树、图常见算法浅析

1.前言
2.树
    2.1 树的概念
    2.2 满二叉树
    2.3 完全二叉树
    2.4 红黑树
    2.5 相关算法
3.图
    3.1 概念
    3.2 存储结构
    3.3 图的实现
4.小结

1.前言

2010年的一部电影创造了奇迹,它是全球第一部票房到达27亿美元,总票房及时排名第一的影片,那就是詹姆斯.卡梅隆执导的电影《阿凡达》。

电影里提到了一颗高大274米的参天大树,是那个潘多拉星球的纳威人的家园,让人印象非常深刻。可惜那只是导演的梦想,地球上不存在这样的物种。

无论多高大的树,那也是从小到大、由根到叶、一点点成长起来的。俗话说十年树木、百年树人,一棵大树又何止是十年这样的容易。而今天我们讲另外一种新的数据结构--树。

2.树

2.1 树的概念

有N个节点组成,具有一定层次关系的集合。

2.2 满二叉树

note = 2^k - 1

2.3 完全二叉树

2.3.1 特点

  • 叶子节点都在k或者k-1层
  • k层可以是不满的,但是k层的所有节点都
  • 必须集中在最左边

2.3.2 堆

  • 大顶堆:父节点都大于子节点
  • 小顶堆:附近点都小于子节点

2.4 红黑树

  • 根节点都是黑色的
  • 红色节点的子节点都必须是黑色的
  • 叶子结点都必须是null
  • 任意节点到其所有路径所包含的黑色节点的个数是相同的

2.5 相关算法

  • 求二叉树的高节点数中序遍历
package F树.A001求二叉树的高节点数中序遍历;

/**
 * Description: <求二叉树的节点数高遍历><br>
 * Author: 门心叼龙<br>
 * Date: 2018/11/21<br>
 * Version: V1.0.0<br>
 * Update: <br>
 */
public class MainAlgorithm {
  public static void main(String[] args) {
    TreeNode treeNode = new TreeNode(0);
    TreeNode treeNode1 = new TreeNode(1);
    TreeNode treeNode2 = new TreeNode(2);
    TreeNode treeNode3 = new TreeNode(3);
    TreeNode treeNode4 = new TreeNode(4);
    TreeNode treeNode5 = new TreeNode(5);

    treeNode.setLeftNote(treeNode1);
    treeNode.setRightNote(treeNode2);

    treeNode1.setLeftNote(treeNode3);
    treeNode1.setRightNote(treeNode4);

    treeNode3.setLeftNote(treeNode5);

    printTreeNote(treeNode);
  }

  // 求这个二叉树的高
  public static int getTreeHeight(TreeNode treeNode) {
    if (treeNode == null) {
      return 0;
    } else {
      int leftHeight = 1 + getTreeHeight(treeNode.getLeftNote());
      int rightHeight = 1 + getTreeHeight(treeNode.getRightNote());
      if (leftHeight > rightHeight) {
        return leftHeight;
      } else {
        return rightHeight;
      }
    }
  }

  // 遍历二叉树的所有节点
  public static void printTreeNote(TreeNode treeNode) {
    if (treeNode == null) {
      return;
    } else {
      System.out.println(treeNode.getValue());
      printTreeNote(treeNode.getLeftNote());
      // System.out.println(treeNode.getValue());//中序遍历
      printTreeNote(treeNode.getRightNote());
    }
  }

  // 求二叉树节点的个数
  public static int getTreeNodeCount(TreeNode treeNode) {
    if (treeNode == null) {
      return 0;
    } else {
      return 1 + getTreeNodeCount(treeNode.getLeftNote())
          + getTreeNodeCount(treeNode.getRightNote());
    }
  }
}
  • 判断两个二叉树是否完全相同:
package F树.A002判断两颗二叉树是否完全相同;

import F树.A001求二叉树的高节点数中序遍历.TreeNode;

/**
 * Description: <判断两颗二叉树是否完全相同><br>
 * Author: gxl<br>
 * Date: 2018/11/23<br>
 * Version: V1.0.0<br>
 * Update: <br>
 */
public class MainAlgorithm {
  public static void main(String[] args) {
    TreeNode treeNode = new TreeNode(0);
    TreeNode treeNode1 = new TreeNode(1);
    TreeNode treeNode2 = new TreeNode(2);
    TreeNode treeNode3 = new TreeNode(3);
    TreeNode treeNode4 = new TreeNode(4);
    TreeNode treeNode5 = new TreeNode(5);

    treeNode.setLeftNote(treeNode1);
    treeNode.setRightNote(treeNode2);

    treeNode1.setLeftNote(treeNode3);
    treeNode1.setRightNote(treeNode4);

    treeNode3.setLeftNote(treeNode5);
    // ===============================
    TreeNode treeNode0 = new TreeNode(0);
    TreeNode treeNode11 = new TreeNode(11);
    TreeNode treeNode22 = new TreeNode(2);
    TreeNode treeNode33 = new TreeNode(3);
    TreeNode treeNode44 = new TreeNode(4);
    TreeNode treeNode55 = new TreeNode(5);

    treeNode0.setLeftNote(treeNode11);
    treeNode0.setRightNote(treeNode22);

    treeNode11.setLeftNote(treeNode33);
    treeNode11.setRightNote(treeNode44);

    treeNode33.setLeftNote(treeNode55);

    boolean sameTree = isSameTree(treeNode, treeNode0);
    System.out.println(sameTree);
  }
  //每个节点对应的值一样就可以
  public static boolean isSameTree(TreeNode treeNode, TreeNode treeNode1) {
    if (treeNode == null && treeNode1 == null) {
      return true;
    }
    if (treeNode != null && treeNode1 == null) {
      return false;
    }
    if (treeNode == null && treeNode1 != null) {
      return false;
    }
    return (treeNode.getValue() == treeNode1.getValue())
        && isSameTree(treeNode.getLeftNote(), treeNode1.getLeftNote())
        && isSameTree(treeNode.getRightNote(), treeNode1.getRightNote());
  }
}
  • 判断一个二叉树是否是对称二叉树
package F树.A003判断一个二叉树是否是对称二叉树;

import F树.A001求二叉树的高节点数中序遍历.TreeNode;

/**
 * Description: <一个二叉树是否是对称二叉树><br>
 * Author: gxl<br>
 * Date: 2018/11/23<br>
 * Version: V1.0.0<br>
 * Update: <br>
 */
public class MainAlgorithm {
  public static void main(String[] args) {
    TreeNode treeNode = new TreeNode(0);
    TreeNode treeNode1 = new TreeNode(10);
    TreeNode treeNode2 = new TreeNode(10);

    TreeNode treeNode3 = new TreeNode(20);
    TreeNode treeNode4 = new TreeNode(30);

    treeNode.setLeftNote(treeNode1);
    treeNode.setRightNote(treeNode2);

    treeNode1.setLeftNote(treeNode3);
    treeNode1.setRightNote(treeNode4);

    treeNode2.setLeftNote(treeNode4);
    treeNode2.setRightNote(treeNode3);

    boolean duicheng = isDuicheng(treeNode);
    System.out.println(duicheng);

  }

  public static boolean isDuicheng(TreeNode rootNode) {
    if (rootNode == null) {
      return true;
    } else {
      return isDuicheng(rootNode.getLeftNote(), rootNode.getRightNote());
    }
  }

  public static boolean isDuicheng(TreeNode left, TreeNode right) {
    if (left == null && right == null) {
      return true;
    } else if (left != null && right == null || left == null && right != null) {
      return false;
    } else {
      return left.getValue() == right.getValue()
          && isDuicheng(left.getLeftNote(), right.getRightNote())
          && isDuicheng(left.getRightNote(), right.getLeftNote());
    }
  }
}

3.图

3.1 概念

在线性表中,数据元素之间是被串联起来的,仅有线性关系,每个数据元素只有一个直接前驱和一个直接后继。在树形结构中,数据元素之间有着明显的层次关系,并且每一层的数据元素可能和下一层中多个元素关联,但只能和上一层中一个元素相关。这和一堆父母可以有多个孩子,但每个孩子只能有一对父母是一个道理。可在现实生活中,人与人之间的关系就非常的复杂,如果我认识的朋友,他们相互之间也互相认识,这就不是简单的一对一,一对多,研究人际关系很自然会考虑多对多的情况。那就是我们今天要研究的另外一种高级数据结构--图。图是一种比线性表和数更加复杂的数据结构。在图形结构中,节点之间的关系可以是任意的,图中任意两个元素之间都有可能相关。

3.2 存储结构

图可以使用两种存储结构,分别是邻接矩阵和邻接表。
邻接矩阵以矩阵的形式存储图所有顶点间的关系。邻接矩阵具有以下特点:

  • 1.邻接矩阵是正矩阵,即横纵维数相等。 
  • 2.矩阵的每一行或一列代表一个顶点,行与列的交点对应这两个顶点的边。 
  • 3.矩阵的点代表边的属性,1代表有边,0代表无边,所以矩阵的对角线都是0,因为对角线上对应的横纵轴代表相同的顶点,边没有意义。 
  • 4.如果是无向图,那么矩阵是对称矩阵;如果是有向图则不一定。 
  • 5.如果是有权图,矩阵点数值可以是权值。 
  • 6.邻接矩阵表示图的关系非常清晰,但消耗空间较大。

邻接表是以一组链表来表示顶点间关系,有以下特点:

  • 1.邻接表示一个有但链表组成的数组 
  • 2.图中的每一个顶点都有一个链,数组的大小等于图中顶点的个数。 
  • 3.无向图的链的第一个元素是本顶点,后继分别连接着和这个顶点相连的顶点;有向图的链第一个顶点是本顶点,后继是以本顶点为起点的边的终点。 
  • 4.如果是有权图,可以在节点元素中设置权值属性 
  • 5.邻接链表关系表示不如邻接矩阵清晰,数据结构相对复杂,但节省空间。

3.3 图的实现

3.3.1 邻接矩阵无向图

public class MatrixNDG {

    int size;//图顶点个数
    char[] vertexs;//图顶点名称
    int[][] matrix;//图关系矩阵

    public MatrixNDG(char[] vertexs,char[][] edges){
        size=vertexs.length;
        matrix=new int[size][size];//设定图关系矩阵大小
        this.vertexs=vertexs;

        for(char[] c:edges){//设置矩阵值
            int p1 = getPosition(c[0]);//根据顶点名称确定对应矩阵下标
            int p2 = getPosition(c[1]);

            matrix[p1][p2] = 1;//无向图,在两个对称位置存储
            matrix[p2][p1] = 1;
        }

    }

    //图的遍历输出
    public void print(){
        for(int[] i:matrix){
            for(int j:i){
                System.out.print(j+" ");
            }
            System.out.println();
        }
    }

    //根据顶点名称获取对应的矩阵下标
    private int getPosition(char ch) {
        for(int i=0; i<vertexs.length; i++)
            if(vertexs[i]==ch)
                return i;
        return -1;
    }

    public static void main(String[] args) {
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};
        MatrixNDG pG;
        // 自定义"图"(输入矩阵队列)
        // 采用已有的"图"
        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new MatrixNDG(vexs, edges);
            //pG.print();   // 打印图 
        }

        long end=System.nanoTime();

        System.out.println(end-start);
    }
}

3.3.2 邻接矩阵有向图

public class MatrixDG {
    int size;
    char[] vertexs;
    int[][] matrix;

    public MatrixDG(char[] vertexs,char[][] edges){
        size=vertexs.length;
        matrix=new int[size][size];
        this.vertexs=vertexs;

        //和邻接矩阵无向图差别仅仅在这里
        for(char[] c:edges){
            int p1 = getPosition(c[0]);
            int p2 = getPosition(c[1]);

            matrix[p1][p2] = 1;
        }

    }

    public void print(){
        for(int[] i:matrix){
            for(int j:i){
                System.out.print(j+" ");
            }
            System.out.println();
        }
    }

    private int getPosition(char ch) {
        for(int i=0; i<vertexs.length; i++)
            if(vertexs[i]==ch)
                return i;
        return -1;
    }



    public static void main(String[] args) {
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};
        MatrixDG pG;
        // 自定义"图"(输入矩阵队列)
        //pG = new MatrixUDG();
        // 采用已有的"图"
        pG = new MatrixDG(vexs, edges);

        pG.print();
    }

}

 3.3.3 邻接表无向图

public class ListNDG {

    Vertex[] vertexLists;//邻接表数组
    int size;

    class Vertex{//邻接表节点类,单链表数据结构
        char ch;
        Vertex next;

        Vertex(char ch){//初始化方法
            this.ch=ch;
        }
        void add(char ch){//加到链表尾
            Vertex node=this;
            while(node.next!=null){
                node=node.next;
            }
            node.next=new Vertex(ch);
        }
    }

    public ListNDG(char[] vertexs,char[][] edges){

        size=vertexs.length;
        this.vertexLists=new Vertex[size];//确定邻接表大小
        //设置邻接表头节点
        for(int i=0;i<size;i++){
            this.vertexLists[i]=new Vertex(vertexs[i]);
        }
        //存储边信息
        for(char[] c:edges){
           int p1=getPosition(c[0]);
           vertexLists[p1].add(c[1]);
           int p2=getPosition(c[1]);
           vertexLists[p2].add(c[0]);
        }

    }

    //跟据顶点名称获取链表下标
    private int getPosition(char ch) {
        for(int i=0; i<size; i++)
            if(vertexLists[i].ch==ch)
                return i;
        return -1;
    }

    //遍历输出邻接表
    public void print(){
       for(int i=0;i<size;i++){
           Vertex temp=vertexLists[i];
           while(temp!=null){
               System.out.print(temp.ch+" ");
               temp=temp.next;
           }
           System.out.println();
       }
    }

    public static void main(String[] args){
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};

        ListNDG pG;
        long start=System.nanoTime();
        for(int i=0;i<10000;i++){
            pG = new ListNDG(vexs, edges);
            //pG.print();   // 打印图 
        }
        long end=System.nanoTime();
        System.out.println(end-start);
    }

}

 3.3.4 邻接表有向图

public class ListDG {
    Vertex[] vertexLists;
    int size;

    class Vertex{
        char ch;
        Vertex next;

        Vertex(char ch){
            this.ch=ch;
        }
        void add(char ch){
            Vertex node=this;
            while(node.next!=null){
                node=node.next;
            }
            node.next=new Vertex(ch);
        }


    }

    public ListDG(char[] vertexs,char[][] edges){

        size=vertexs.length;
        this.vertexLists=new Vertex[size];
        for(int i=0;i<size;i++){
            this.vertexLists[i]=new Vertex(vertexs[i]);
        }

        for(char[] c:edges){
           int p=getPosition(c[0]);
           vertexLists[p].add(c[1]);
        }

    }

    private int getPosition(char ch) {
        for(int i=0; i<size; i++)
            if(vertexLists[i].ch==ch)
                return i;
        return -1;
    }

    public void print(){
       for(int i=0;i<size;i++){
           Vertex temp=vertexLists[i];
           while(temp!=null){
               System.out.print(temp.ch+" ");
               temp=temp.next;
           }
           System.out.println();
       }
    }

    public static void main(String[] args){
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};

        ListDG pG;

        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new ListDG(vexs, edges);
            //pG.print();   // 打印图 
        }
    
        long end=System.nanoTime();
        System.out.println(end-start);

    }
}

4.小结

现在我们对树和图已经有了一个基本的了解,在后续文章我们将继续讨论他们常用的一些算法。

源码下载地址:https://download.csdn.net/download/geduo_83/10913510

初级篇:Java数据结构与算法初级篇之数组、集合和散列表
中级篇:Java数据结构与算法中级篇之栈、队列、链表
高级篇:Java数据结构与算法高级篇之树、图

理论篇:Java数组、集合、散列表常见算法浅析
理论篇:Java栈、队列、链表常见算法浅析
理论篇:Java树、图常见算法浅析

 

<span style="color:#404040;">1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构算法支撑。</span><br /><br /><span style="color:#404040;">2.网上数据结构算法的课程不少,但存在两个问题:</span><br /><br /><span style="color:#404040;">1)授课方式单一,大多是照着代码念一遍,数据结构算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了</span><br /><span style="color:#404040;">2)说是讲数据结构算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级 </span><br /><span style="color:#404040;">3)授课方式采用解+算法游戏的方式,让课程生动有趣好理解 </span><br /><span style="color:#404040;">4)系统全面的讲解了数据结构算法, 除常用数据结构算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成、最小连通、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴</span><br /><br /><span style="color:#404040;">3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构算法。</span><br /><br /><span style="color:#404040;">教程内容:</span><br /><span style="color:#404040;">本教程是使用Java来讲解数据结构算法,考虑到数据结构算法较难,授课采用解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉、二叉与数组转换、二叉排序(BST)、AVL、线索二叉、赫夫曼、赫夫曼编码、多路查找(BB+和B*)、的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。</span><br /><br /><span style="color:#404040;">学习目标:</span><br /><span style="color:#404040;">通过学习,学员能掌握主流数据结构算法的实现机制,开阔编程思路,提高优化程序的能力。</span>
相关推荐
程序员的必经之路! 【限时优惠】 现在下单,还享四重好礼: 1、教学课件免费下载 2、课程案例代码免费下载 3、专属VIP学员群免费答疑 4、下单还送800元编程大礼包 【超实用课程内容】  根据《2019-2020年中国开发者调查报告》显示,超83%的开发者都在使用MySQL数据库。使用量大同时,掌握MySQL早已是运维、DBA的必备技能,甚至部分IT开发岗位也要求对数据库使用和原理有深入的了解和掌握。 学习编程,你可能会犹豫选择 C++ 还是 Java;入门数据科学,你可能会纠结于选择 Python 还是 R;但无论如何, MySQL 都是 IT 从业人员不可或缺的技能!   套餐中一共包含2门MySQL数据库必学的核心课程(共98课时)   课程1:《MySQL数据库从入门到实战应用》   课程2:《高性能MySQL实战课》   【哪些人适合学习这门课程?】  1)平时只接触了语言基础,并未学习任何数据库知识的人;  2)对MySQL掌握程度薄弱的人,课程可以让你更好发挥MySQL最佳性能; 3)想修炼更好的MySQL内功,工作中遇到高并发场景可以游刃有余; 4)被面试官打破沙锅问到底的问题问到怀疑人生的应聘者。 【课程主要讲哪些内容?】 课程一:《MySQL数据库从入门到实战应用》 主要从基础篇,SQL语言篇、MySQL进阶篇三个角度展开讲解,帮助大家更加高效的管理MySQL数据库。 课程二:《高性能MySQL实战课》主要从高可用篇、MySQL8.0新特性篇,性能优化篇,面试篇四个角度展开讲解,帮助大家发挥MySQL的最佳性能的优化方法,掌握如何处理海量业务数据和高并发请求 【你能收获到什么?】  1.基础再提高,针对MySQL核心知识点学透,用对; 2.能力再提高,日常工作中的代码换新貌,不怕问题; 3.面试再加分,巴不得面试官打破沙锅问到底,竞争力MAX。 【课程如何观看?】  1、登录CSDN学院 APP 在我的课程中进行学习; 2、移动端:CSDN 学院APP(注意不是CSDN APP哦)  本课程为录播课,课程永久有效观看时长 【资料开放】 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化。  下载方式:电脑登录课程观看页面,点击右侧课件,可进行课程资料的打包下载。
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页
实付 29.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值