这是个过渡篇章,简单介绍哈希表、有序表和链表的使用,这是刷算法题必备的基础知识。哈希表、有序表和链表的深入实现原理,会在后续篇章中介绍,目前暂不需要。
一、哈希表
哈希表通过把关键码值映射到表中一个位置来访问记录,不需比较便可直接取得所查记录,加快了查找的速度。
哈希表在使用层面上可以理解为一种集合结构:
1、如果只有key,没有伴随数据values,可以使用HashSet结构
2、如果既有key,又有伴随数据values,可以使用HashMap结构
3、有无伴随数据,是HashMap和HashSet唯一的区别,底层的实际结构是一样的
4、使用哈希表增(put)、删(remove)、改(put)和查(get)的操作,可以认为时间复杂度为O(1),但是常数时间比较大
5、放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
6、放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地址的大小
二、有序表
有序表是指所有元素以递增或递减方式的有序排列。
有序表在使用层面上可以理解为一种集合结构:
1、如果只有key,没有伴随数据value,可以使用TreeSet结构。
2、如果既有key,又有伴随数据value,可以使用TreeMap结构。
3、有无伴随数据,是TreeSet和TreeMap唯一的区别,底层的实际结构是一样的,时间复杂度O(logN)。
4、有序表与哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织。
5、红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同。
6、放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小。
7、放入有序表的东西,如果不是基础类型,必须提供比较器,内部按引用传递,内存占用是这个东西内存地址的大小。
8、有序表的固定操作:
java代码实例:
public static void main(String[] args) {
TreeMap<Integer, String> treeMap = new TreeMap<>();
// 1)void put(K key, V value): 将一个(key,value)记录加入到表中,或者根据key将记录更新成value。
treeMap.put(2, "我是2");
treeMap.put(5, "我是5");
treeMap.put(1, "我是1");
treeMap.put(4, "我是4");
// 2)V get(K key):根据给定的key,查询value并返回。
System.out.println(treeMap.get(1)); // 我是1
// 3)void remove(K key):移除key的记录。
treeMap.remove(1);
System.out.println(treeMap.get(1)); // null
// 4)boolean containsKey(K key):判断是否有关于key的记录。
System.out.println(treeMap.containsKey(2)); // true
// 5)K firstKey():返回所有键值的排序结果中,最左(最小)的那个。
System.out.println("最小:" + treeMap.firstKey()); // 2
// 6)K lastKey():返回所有键值的排序结果中,最右(最大)的那个。
System.out.println("最大:" + treeMap.lastKey()); // 5
// 7)K floorKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中,key的前一个。
System.out.println(treeMap.floorKey(3)); // 在表中所有<=3的数中,离3最近的数:2
System.out.println(treeMap.floorKey(4)); // 在表中所有<=4的数中,离4最近的数:4
// 8)K ceilingKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中,key的后一个。
System.out.println("" + treeMap.ceilingKey(3)); // 在表中所有>=3的数中,离3最近的数:4
System.out.println("" + treeMap.ceilingKey(4)); // 在表中所有>=4的数中,离4最近的数:4
}
以上所有操作时间复杂度都是O(logN),N为有序表含有的记录数。
三、链表
链表(LinkedList) 虽然是一种线性表,但是并不会按线性的顺序存储数据,使用的不是连续的内存空间来存储数据。
链表的插入和删除操作的复杂度为 O(1) ,只需要知道目标位置元素的上一个元素即可。但是,在查找一个节点或者访问特定位置的节点的时候复杂度为 O(n) 。
使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但链表不会节省空间,相比于数组会占用更多的空间,因为链表中每个节点存放的还有指向其他节点的指针。除此之外,链表不具有数组随机读取的优点。
1、单链表的节点结构
Class Node<V> {
V value;
Node next;
}
单链表示意图:
2、双链表的节点结构
Class Node<V> {
V value;
Node next;
Node last;
}
3、循环链表
循环链表其实就是链表的尾结点不是指向null,而是指向链表的头节点