前面介绍了线性表结构中的顺序存储结构寻址容易但是插入删除性能不好,而链式结构插入删除性能较好寻址却欠佳,那么有没有“鱼和熊掌兼可得”的结构呢?
哈希表(Hash table也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。即它通过把关键码值映射到表中一个位置来访问记录,直接通过key来加快查找的速度,这个映射函数叫做散列函数,散列函数就是用于计算哈希值的,存放记录的数组叫做散列表。
以一个实例简单分析下哈希表的设计思想,首先我们有一组数据{14,19,5,7,21,1,13,0,18}需要存储,暂且设计散列表长度为预存储数组的长度,最后再设计一个映射公式即散列函数表达式f(x)= x mod 13,经过映射之后无论多大的数据都能确保经过散列函数计算之后在散列表下标范围内(当然我们用的hashCode要比这复杂得多不过核心思想是一样的)
当然以上是一种简单的哈希表基本设计思想,适用于特定的场景,比如说通讯录、QQ好友列表、微信好友列表、字典等有上限的且重复不多的数据存储。
JDK中采用的是所谓的拉链法,JDK1.7之前采用的是数组+单链表的结构,而在之后改成了数组+单链表+红黑树的结构,基本思想是一致的,区别在于解决哈希冲突的方案,JDK1.7之前散列表中存储的元素上一个单链表,当发生哈希冲突时,直接把值添加到链表尾部,这样就解决了哈希冲突,但是为了避免单链表长度过长,在JDK1.8之后设置来一个阈值,当链表长度超过这个阈值时则自动转为红黑树进行存储。
树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,不过它是根朝上,而叶朝下的,当n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点;.m>0时,子树的个数没有限制,但它们一定是互不相交的。单个结点是一棵树,树根就是该结点本身。
树的存储结构有有四种:双亲表示法、孩子兄弟表示法、孩子表示法、双亲孩子表示法
把所有节点都村存在一组连续空间中,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。
节点结构为
data(数据域) | parent(指针域) |
---|---|
存储结点的数据信息 | 存储该结点的双亲所在数组中的下标 |
根节点的指针域为-1,根据结点的parent指针很容易找到它的双亲结点。所用时间复杂度为O(1),直到parent为-1时,表示找到了树结点的根。
任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟,结点结构:
data(数据域) | firstchild(指针域) | rightsib(指针域) |
---|---|---|
data是数据域 | 存储该结点的第一个孩子结点的存储地址 | 存储该结点的右兄弟结点的存储地址 |
把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。为此设计两种结点结构:一种是孩子链表的孩子结点:
child(数据域) | next(指针域) |
---|---|
存储某个结点在表头数组中的下标 | 存储指向某结点的下一个孩子结点的指针 |
另一种是表头数组的表头结点: | |
data(数据域) | firstchild(头指针域) |
— | — |
存储某个结点的数据信息 | 存储该结点的孩子链表的头指针 |
二叉树是**每个结点最多有两个子树的树结构,**通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树不是树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别,树中结点的最大度数没有限制,而二叉树结点的最大度数为2, 树的结点无左、右之分,而二叉树的结点有左、右之分。
遍历是对树的一种最基本的运算,所谓遍历二叉树就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。假设L、D、R分别表示遍历左子树、访问根结点和遍历右子树, 则对一棵二叉树的遍历有三种情况:DLR(称为先根次序遍历),LDR(称为中根次序遍历),LRD (称为后根次序遍历)。
首先访问根,再先序遍历左子树,最后先序遍历右子树。
public void postOrderTraversal(TreeNode root) {
if (root != null) {
return;
}
postOrderTraversa1(root.left);
postOrderTraversa1(root.right);
System.out.print(root.val+" ");
}
非递归版本
public void preOrderTraversval2(TreeNode root) {
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode pNode = root;
while (pNode != null || !stack.isEmpty()) {
if (pNode != null) {
System.out.print(pNode.val+" ");
stack.push(pNode);
pNode = pNode.left;
} else { //pNode == null && !stack.isEmpty()
TreeNode node = stack.pop();
pNode = node.right;
}
}
}
首先中序遍历左子树,再访问根,最后中序遍历右子树
public void postOrderTraversal(TreeNode root) {
if (root != null) {
return;
}
postOrderTraversa1(root.left);
postOrderTraversa1(root.right);
System.out.print(root.val+" ");
}
首先后序遍历左子树,再后序遍历右子树,最后访问根,即
public void postOrderTraversal(TreeNode root) {
if (root != null) {
return;
}
postOrderTraversa1(root.left);
postOrderTraversa1(root.right);
System.out.print(root.val+" ");
}
package com.crazymo.ndk.tree;
public class BinaryTree<E> {
public TreeNode<E> root;
public BinaryTree(E data){
root=new TreeNode<>(data,null,null);
}
public void createTree(){
TreeNode<String> nodeB=new TreeNode<String>("B",null,null);
TreeNode<String> nodeC=new TreeNode<String>("C",null,null);
TreeNode<String> nodeD=new TreeNode<String>("D",null,null);
TreeNode<String> nodeE=new TreeNode<String>("E",null,null);
TreeNode<String> nodeF=new TreeNode<String>("F",null,null);
TreeNode<String> nodeG=new TreeNode<String>("G",null,null);
TreeNode<String> nodeH=new TreeNode<String>("H",null,null);
TreeNode<String> nodeJ=new TreeNode<String>("J",null,null);
TreeNode<String> nodeI=new TreeNode<String>("I",null,null);
root.leftChild= (TreeNode<E>) nodeB;
root.rightChild= (TreeNode<E>) nodeC;
nodeB.leftChild=nodeD;
nodeC.leftChild=nodeE;
nodeC.rightChild=nodeF;
nodeD.leftChild=nodeG;
nodeD.rightChild=nodeH;
nodeE.rightChild=nodeJ;
nodeH.leftChild=nodeI;
}
/**
* 中序访问树的所有节点
*/
public void midOrderTraverse(TreeNode<E> root){//逻辑
if(root==null){
return;
}
midOrderTraverse(root.leftChild);//逻辑
System.out.print("mid:"+root.data+"\t");//输出
midOrderTraverse(root.rightChild);//逻辑
}
/**
* 前序访问树的所有节点 Arrays.sort();
*/
public void preOrderTraverse(TreeNode<E> root){
if(root==null){
return;
}
System.out.print("pre:"+root.data+"\t");
preOrderTraverse(root.leftChild);
preOrderTraverse(root.rightChild);
}
/**
* 后序访问树的所有节点
*/
public void postOrderTraverse(TreeNode<E> root){
if(root==null){
return;
}
postOrderTraverse(root.leftChild);
postOrderTraverse(root.rightChild);
System.out.print("post:"+root.data+"\t");
}
/**
*节点的数据结构
* @param
*/
public class TreeNode<E> {
E data;
TreeNode<E> leftChild;
TreeNode<E> rightChild;
public TreeNode(E data, TreeNode<E> leftChild, TreeNode<E> rightChild) {
this.data = data;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
}
树的遍历
BinaryTree binarayTree=new BinaryTree("A");//构造简单二叉树
binarayTree.createTree();
binarayTree.midOrderTraverse(binarayTree.root);
System.out.println();
binarayTree.preOrderTraverse(binarayTree.root);
System.out.println();
binarayTree.postOrderTraverse(binarayTree.root);