形式化定义:算法的集合树(Tree)是由一个或多个结点组成的有限集合T,其中有一个特定的称为根的结点;其他结点可分为(m≥0)个互不相交的有限集T1,T2,T3,…,Tm,每个集合自己又是一棵树,且称为根的子树。java
逻辑结构:算法
树的表示:数组
树的术语:数据结构
树的存储结构能够采用具备多个指针域的多重链表,结点中指针域的个数应由树的度来决定。ide
定义:二叉树是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵不相交的左子树和右子树组成。this
特色:spa
基本形态:二叉树有五种不一样的形态。指针
满二叉树:深度为k具备个结点的二叉树,称为满二叉树。code
彻底二叉树:若是一棵具备n个结点的深度为k的二叉树,它的每个结点都与深度为k的满二叉树中编号为1~n的结点一一对应,则称这棵二叉树为彻底二叉树。彻底二叉树又称为顺序二叉树。blog
性质:
若二叉树的层数从1开始,则二叉树的第k层结点数,最多为个(k≥1)。
遍历二叉树:令L,R,T分别表明二叉树的左子树、右子树、根结点,若规定二叉树中必须先左后右(左右顺序不能颠倒),则只有TLR、LTR、LRT三种遍历规则。
二叉树的存储结构:
/** * 二叉树 * @author wangfei * */ public class TestBinaryTree { public static void main(String[] args) { //建立一棵树 BinaryTree binaryTree = new BinaryTree(); //建立一个根节点 Node root = new Node(1); //把根节点赋值给树 binaryTree.setRoot(root); //建立一个左节点 Node rootL = new Node(2); //把新建立的节点设置为根节点的子节点 root.setLeftNode(rootL); //建立一个右节点 Node rootR = new Node(3); //把新建立的节点设置为根节点的子节点 root.setRightNode(rootR); //为第二层建立两个子节点 root.setLeftNode(new Node(4)); root.setRightNode(new Node(5)); //前序遍历树 binaryTree.frontShow(); System.out.println("==========================="); //中序遍历树 binaryTree.midShow(); System.out.println("==========================="); //后序遍历树 binaryTree.afterShow(); System.out.println("==========================="); //前序查找 Node result = binaryTree.frontSearch(5); System.out.println(result); System.out.println("==========================="); //删除一个子树 binaryTree.delete(4); binaryTree.frontShow(); } }
public class BinaryTree { Node root; //设置根节点 public void setRoot(Node root) { this.root = root; } //获取根节点 public Node getRoot(Node root) { return root; } //前序遍历 public void frontShow() { if(root!=null) { root.frontShow(); } } //中序遍历 public void midShow() { if(root!=null) { root.midShow(); } } //后序遍历 public void afterShow() { if(root!=null) { root.afterShow(); } } //前序查找 public Node frontSearch(int i) { return root.frontSearch(i); } //删除一个子树 public void delete(int i) { if(root.value==i) { root = null; }else { root.delete(i); } } }
public class Node { //节点的权 int value; //左儿子 Node leftNode; //右儿子 Node rightNode; public Node(int value) { this.value = value; } //设置左儿子 public void setLeftNode(Node leftNode) { this.leftNode = leftNode; } //设置右儿子 public void setRightNode(Node rightNode) { this.rightNode = rightNode; } //前序遍历 public void frontShow() { //先遍历当前节点内容 System.out.println(value); //左节点 if(leftNode!=null) { leftNode.frontShow(); } //右节点 if(rightNode!=null) { rightNode.frontShow(); } } //中序遍历 public void midShow() { //左子节点 if(leftNode!=null) { leftNode.midShow(); } //当前节点 System.out.println(value); //右子节点 if(rightNode!=null) { rightNode.midShow(); } } //后序遍历 public void afterShow() { //左子节点 if(leftNode!=null) { leftNode.afterShow(); } //右子节点 if(rightNode!=null) { rightNode.afterShow(); } //当前节点 System.out.println(value); } //前序查找 public Node frontSearch(int i) { Node target = null; //对比当前节点的值 if(this.value==i) { return this; //当前节点的值不是要查找的节点 }else { //查找左儿子 if(leftNode!=null) { //查不到的话,target仍是null target = leftNode.frontSearch(i); } //若是不为空,说明在左儿子中已经找到 if(target!=null) { return target; } //查找右儿子 if(rightNode!=null) { target = rightNode.frontSearch(i); } } return target; } //删除节点 public void delete(int i) { Node parent = this; //判断左儿子 if(parent.leftNode!=null && parent.leftNode.value==i) { parent.leftNode = null; return; } //判断右儿子 if(parent.rightNode!=null && parent.rightNode.value==i) { parent.rightNode = null; return; } //递归检查并删除左儿子 parent = leftNode; if(parent!=null) { parent.delete(i); } //递归检查并删除右儿子 parent = rightNode; if(parent!=null) { parent.delete(i); } } }
定义:图是由顶点集V和顶点间的关系集合E(边的集合)组成的一种数据结构,能够用二元组定义为:G=(V,E)。
有向图和无向图:为新的对话框类添加方在图中,若用箭头标明了边是有方向性的,则称这样的图为有向图,不然称为无向图。
上图可描述为:
G1=(V1,E),V1={a,b,c,d},E1={(a,b),(a,c),(a,d),(b,d),(c,d)}
G2=(V2,E2), V2={1,2,3},E2={<1,2>,<1,3>,<2,3>,<3,1>}
度、入度、出度:具备n个顶点,在图中,一个顶点依附的边或弧的数目,称为该顶点的度。在有向图中,一个顶点依附的弧头数目,称为该顶点的入度。一个顶点依附的弧尾数目,称为该顶点的出度,某个顶点的入度和出度之和称为该顶点的度。
另外,若图中有n个顶点,e条边或弧,第i个顶点的度为di,则有
权:若在图的边或弧中给出相关的数,称为权。权能够表明一个顶点到另外一个顶点的距离,耗费等,带权图通常称为网。
连通图和非连通图:在无向图中,若任意两个顶点都是连通的,则称此无向图为连通图,不然称为非连通图。
强连通图和非强连通图:在有向图中,若图中任意两个顶点都是连通的,则称此有向图为强连通图,不然称为非强连通图
定义:
在邻接矩阵表示中,除了存放顶点自己信息外,还用一个矩阵表示各个顶点之间的关系。若(i,j)∈E(G)或〈i,j〉∈E(G),则矩阵中第i行第j列元素值为1,不然为0。
从无向图的邻接矩阵能够得出以下结论:
从有向图的邻接矩阵能够得出以下结论:
定义:将每一个结点的边用一个单链表连接起来,若干个结点能够获得若干个单链表,每一个单链表都有一个头结点,全部头结点组成一个一维数组,称这样的链表为邻接表。
从无向图的邻接表能够获得以下结论:
从有向图的邻接表能够获得以下结论:
深度优先搜索思想:深度优先搜索遍历相似于树的先序遍历。假定给定图G的初态是全部顶点均未被访问过,在G中任选一个顶点i做为遍历的初始点,则深度优先搜索遍历可定义以下:
广度优先搜索的思想:广度优先搜索遍历相似于树的按层次遍历。设图G的初态是全部顶点均未访问,在G中任选一顶点i做为初始点,则广度优先搜索的基本思想是:
/** * 图 * @author wangfei * */ public class Graph { private Vertex[] vertexs; private int currentSize; public int[][] adjMat; private MyStack stack = new MyStack(); //当前遍历的下标 private int currentIndex; public Graph(int size) { vertexs = new Vertex[size]; adjMat = new int[size][size]; } /** * 向图中加入一个顶点 * @param v */ public void addVertex(Vertex v) { vertexs[currentSize++] = v; } /** * 向相邻顶点添加链接线 * @param v1 * @param v2 */ public void addEdge(String v1, String v2) { //找出两个顶点的下标 int index1 = 0; for(int i=0; i<vertexs.length; i++) { if(vertexs[i].getValue().equals(v1)) { index1 = i; break; } } int index2 = 0; for(int i=0; i<vertexs.length; i++) { if(vertexs[i].getValue().equals(v2)) { index2 = i; break; } } adjMat[index1][index2] = 1; adjMat[index2][index1] = 1; } /** * 深度优先搜索算法遍历图 */ public void dfs() { //把第0个顶点标记为已访问状态 vertexs[0].visited = true; //把第0个元素压入栈中 stack.push(0); //打印顶点的值 System.out.println(vertexs[0].getValue()); //遍历 out:while(!stack.isEmpty()) { for(int i=currentIndex; i<vertexs.length; i++) { //若是和下一个遍历的元素是通的 if(adjMat[currentIndex][i]==1 && vertexs[i].visited==false) { //把下一个元素压入栈中 stack.push(i); vertexs[i].visited = true; System.out.println(vertexs[i].getValue()); continue out; } } //弹出栈顶元素 stack.pop(); //修改当前位置为栈顶元素 if(!stack.isEmpty()) { currentIndex = stack.peek(); } } } }
/** * 顶点类 * @author wangfei * */ public class Vertex { private String value; public boolean visited; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Vertex(String value) { super(); this.value = value; } @Override public String toString() { return value; } }
public class MyStack { //栈的底层咱们使用数组来存储数据 int[] elements; public MyStack() { elements = new int[0]; } //压入元素 public void push(int element) { // 建立一个新的数组 int[] newArr = new int[elements.length + 1]; // 把原数组中的元素复制到新数组中 for (int i = 0; i < elements.length; i++) { newArr[i] = elements[i]; } // 把添加的元素放入新数组中 newArr[elements.length] = element; // 使用新数组替换旧数组 elements = newArr; } //取出栈顶元素 public int pop() { //栈中没有元素 if(elements.length==0) { throw new RuntimeException("stack is empty"); } //取出数组的最后一个元素 int element = elements[elements.length-1]; //建立一个新的数组 int[] newArr = new int[elements.length-1]; //原数组中除了最后一个元素的其它元素都放入新的数组中 for(int i=0;i<elements.length-1;i++) { newArr[i]=elements[i]; } //替换数组 elements=newArr; //返回栈顶元素 return element; } //查看栈顶元素 public int peek() { //栈中没有元素 if(elements.length==0) { throw new RuntimeException("stack is empty"); } return elements[elements.length-1]; } //判断栈是否为空 public boolean isEmpty() { return elements.length==0; } }