在各种数据结构中进行选择的方法如图
一、线性表
线性表有两种,一种是数组,一种是链表。
数组的优点在于查询,因为可以通过数组中元素的下标快速查找。但由于数组中的元素在物理存储单元中是连续的,所以对其进行元素添加删除的操作开销是非常大的。
数组因为存储的都是同一类值且存储空间连续,所以长度和地址是可以计算的。比如我们定义一个长度为10的整型数组,假设在内存中从1000地址开始,我们知道一个整型占用四个字节,那么就可以计算出该数组的在空间中占用1000-1039这块区域,而比如说假设要对数组下标为2的元素进行查询,就可以很快得到该元素的位置在1008.所以在数组中在知道下标的情况下对一个数据进行查询的时间复杂度为O(1)。
而链表的优点则是数组不擅长的添加和删除,因为链表是由一个个独立的节点构成的,节点与节点的地址并不像数组中那样是连续的。每个节点包含了该存储的值(Date)以及指向下一节点的引用地址(Next)。这样在对链表进行添加删除操作的时候只需要更改指向下一节点的引用地址,非常的便捷。但是链表的查询操作十分的慢,因为无法精准定位,只能顺着一个一个节点来查。
链表添加删除操作图
链表对内存利用率比较高,并且大小没有限制,扩展性强。
二、栈与队列(有限制条件的线性表)
栈与队列也可以说是特殊的线性表,之所以说特殊,是因为它们的增删改查都不像数组链表那么随心所欲,在对其进行操作时,栈遵循的规则是先进后出,并且栈只能操作栈顶的元素。而队列则恰好相反,先进先出,新添加的元素只能在队尾,只能对队头的元素进行操作。说得形象一点的话,我们可以想象一下有一群人要进一个房间然后从这个房间出去,而栈就是只有一个门的房间,而队列则是有规定方向一个入口一个出口。
栈的示意图(上图)
队列的示意图(上图)
而在Java中,还有一种特殊的队列,叫做优先队列。
优先队列中的每一个元素都会被赋予一个优先度的属性,在对数据进行操作的过程中,优先度最高的元素才能被操作。
三、树与二叉树(非线性)
在之前的线性表中,表中存储的数据都是只有一个前驱和一个后继,而树则不同。树由一个根结点和无数子结点构成,之所以称之为树,就是因为其结构类型一棵倒过来的大树。
树具有以下特点:
①每个节点有零个或多个子节点;
②没有父节点的节点称为根节点;
③每一个非根节点有且只有一个父节点;
④除了根节点外,每个子节点可以分为多个不相交的子树;
本人对树的学习还不够完善,等之后再进行补充。
四、哈希表
哈希表是一种非常常用的数据结构,它采用的是键值存储法,在进行查询时,就通过键来找到其对应的值,所以其进行查询的理论时间复杂度为O(1)。
在Java中:
HashMap是基于数组来实现哈希表的,所以其扩展性也不太行,虽然会自动进行扩容,但这就需要预留空间,而且即将装满的哈希表性能是非常低的。
HashMap的每个记录就是一个Entry<K, V>对象,数组中存储的就是这些对象;
HashMap的哈希函数 = 计算出hashCode + 计算出数组的index;
HashMap解决冲突:使用链地址法,每个Entry对象都有一个引用next来指向链表的下一个Entry;
在这里要注意的有一点,在哈希表的存储中,当你输入一个自己定义的键时,在虚拟机中会通过哈希函数来对这个键进行计算,然后将它放在对应的位置,这时就会出现一个问题,有些变量虽然它的name是不一样的,比如“地”和“哈”(例子使用的地和哈不一定相同,只是为了表述这种含义),但是有可能经过哈希函数计算后得到的值是一样的,这就是哈希冲突。
所以我们在使用自定义对象当作hashMap的键时,一定要重写Object类的hashCode和equals方法,因为在键的判定中,Object中的hashCode方法实际是返回地址,那么如果使用的是俩个自定义的变量(比如k k1 = new k和k k2 = new k;),那么即便他们的值相同,也无法通过k2调用k1对应的值。HashMap是用链地址法来处理冲突,也就是说,在100号位置上,有可能存在着多个用链表形式存储的对象。它们通过hashCode方法返回的hash值都是100。这里就需要用equals方法来判断了,如果我们没有重写equals方法,那么调用的就是Object中的equals方法,那样他们同样是不同的。具体可以运行以下代码来复现。
import java.util.HashMap;
class Key {
private Integer id;
public Integer getId()
{return id; }
public Key(Integer id)
{this.id = id; }
//故意先注释掉equals和hashCode方法
// public boolean equals(Object o) {
// if (o == null || !(o instanceof Key))
// { return false; }
// else
// { return this.getId().equals(((Key) o).getId());}
// }
// public int hashCode()
// { return id.hashCode(); }
}
public class WithoutHashCode {
public static void main(String[] args) {
Key k1 = new Key(1);
Key k2 = new Key(1);
HashMap<Key,String> hm = new HashMap<Key,String>();
hm.put(k1, "Key with id is 1");
System.out.println(hm.get(k2));
}
}
有什么错误的地方拜托指点一下,也期待大伙相关方面的指导。