0037数据结构之Set和Map
Posted xiao1572662
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了0037数据结构之Set和Map相关的知识,希望对你有一定的参考价值。
基于链表和二分搜索树实现Set,基于二分搜索树实现Map。
---------------------集合Set-----------------------------
Set<E>
void add<E>
void remove<E>
boolean contains<E>
int getSize()
boolean isEmpty()
1、 使用二分搜索树实现Set集合:
package set;
import tree.BST;
/*
* 欲使集合中存放的元素具有可比性,需使E extends Comparable<E>
* */
public class BSTSet<E extends Comparable<E>> implements
Set<E> {
BST<E> bst = new BST<>();
@Override
public int getSize() {
return bst.getSize();
}
@Override
public boolean isEmpty() {
return bst.isEmpty();
}
@Override
public boolean contains(E e) {
return bst.contains(e);
}
@Override
public void add(E e) {
//由于自己实现的BST类中的add方法不包含重复元素,所以可以直接调用
bst.add(e);
}
@Override
public void remove(E e) {
bst.removeElement(e);
}
@Override
public String toString() {
return bst.toString();
}
}
2、使用链表实现集合Set:
package set;
import linked.LinkedList;
/*
* 链表实现的Set包含的元素不用实现Comparable接口
* */
public class LinkedListSet<E> implements Set<E>{
private LinkedList linkedList;
public LinkedListSet(){
linkedList = new LinkedList();
}
@Override
public int getSize() {
return linkedList.getSize();
}
@Override
public boolean isEmpty() {
return linkedList.isEmpty();
}
@Override
public boolean contains(E e) {
return linkedList.contains(e);
}
@Override
public void add(E e) {
//由于链表中没有限制元素不能重复,所以此处需要加上判断
if(!linkedList.contains(e)){
//在链表头加新的元素,时间复杂度最低
linkedList.addFirst(e);
}
}
@Override
public void remove(E e) {
linkedList.removeElement(e);
}
@Override
public String toString() {
return linkedList.toString();
}
}
总结:基于链表实现的Set效率慢于基于二分搜索树实现的Set,原因是基于二分搜索树实现的链表的add、remove、contains都是O(h)级别的,h是二分搜索树的高度,对于满二分搜索树,其中h=log2n代表log以2为底n的对数,所以时间复杂度也可以看成是O(log2n),不管是2是底数或者10是底数、100是底数,都可以写成O(logn),而最坏形式的二分搜索树就是只有一个分支,即链表形式的二分搜索树,那么时间复杂度也就成了O(n)了;而基于链表实现的Set,add(调用了链表的contains进行判断)、remove、contains的时间复杂度都是O(n)的
比如:n=100万的时候,logn是20,两者相差5万倍,即如果logn用时1秒的话,n就要用时14个小时,这样看来两者的差距是非常大的。且随着n的增大,差距会越来越大。
nlogn和n2之间的差距相当于logn和n之间的差距。
logn的效率是非常高的。
有序集合最好基于搜索树实现,无序集合最好基于hash表实现(比如无序集合基于链表实现效率是极低的)
--------------------------映射Map----------------------
链表实现:
class Node{
K key;
V value;
Node next;
}
二分搜索树实现:
class Node{
K key;
V value;
Node left;
Node right;
}
Map接口定义:
int getSize()
boolean isEmpty()
Map<K,V>
void add(K,V)
V remove(K)
boolean contains(K)
V get(K)
void set(K,V)
1、 基于二分搜索树实现Map
package map;
public class BSTMap<K extends Comparable<K>,V> implements Map<K,V> {
private class Node{
public K key;
public V value;
public Node left;
public Node right;
public Node(K key,V value,Node left,Node right){
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
public Node(K key,V value){
this.key = key;
this.value = value;
this.left = null;
this.right = null;
}
public Node(){
this.key = null;
this.value = null;
this.left = null;
this.right = null;
}
}
private int size;
private Node root;
public BSTMap(){
root = null;
size = 0;
}
@Override
public int getSize() {
//此处不初始化root,是为了下边其他方法判断node == null方便
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void add(K k, V v) {
root = add(root,k,v);
}
//内容递归调用,返回原node树的基础上增加新node后的node树
private Node add(Node node,K k,V v){
if(k == null){
return null;
}
//1终止条件
if(node == null){
//创建新的节点
size++;
return new Node(k,v);
}
//2递归调用
if(k.compareTo(node.key) == 0){
//返回当前节点
return node;
}else if(k.compareTo(node.key) < 0){
node.left = add(node.left,k,v);
return node;
}else{
node.right = add(node.right,k,v);
return node;
}
}
@Override
public V remove(K k) {
V v = get(k);
root = remove(root,k);
return v;
}
//内部递归调用
public Node remove(Node node,K k){
//1终止条件
if(node == null){
return null;
}
//2递归调用
if(k.compareTo(node.key) == 0){
//如果右子树为null,则返回左子树
if(node.right == null){
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}else if(node.left == null){
//如果左子树为null,则返回右子树
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}else{
//否则左右子树都不为空
//需要调用getMin方法,removeMin方法,然后拼接出新的node树返回
Node rightNode = node.right;
Node min = minumum(rightNode);
Node newRightNode = removeMin(rightNode);
min.left = node.left;
min.right = newRightNode;
node.left = node.right = null;
return min;
}
}else if(k.compareTo(node.key) < 0){
node.left = remove(node.left,k);
}else{
node.right = remove(node.right,k);
}
return node;
}
//私有的内部递归调用的的方法
private Node minumum(Node node){
//终止条件
if(node.left == null){
return node;
}
return minumum(node.left);
}
//返回删除最小节点后的node
public Node removeMin(Node node){
//终止条件
if(node.left == null){
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
//相当于如果node.left是最小的节点,
//使node.left = node.left.right
node.left = removeMin(node.left);
return node;
}
@Override
public V get(K k) {
return get(root,k);
}
//内部递归调用
private V get(Node node,K k){
//1终止条件
if(node == null || k == null){
return null;
}
//2递归调用
if(k.compareTo(node.key) == 0){
//返回当前节点
return node.value;
}else if(k.compareTo(node.key) < 0){
return get(node.left,k);
}else{
return get(node.right,k);
}
}
@Override
public void set(K k, V v) {
root = set(root,k,v);
}
//内部递归调用,返回原node树的基础上更改值后的node树
public Node set(Node node,K k,V v){
if(k == null){
return null;
}
//1终止条件
if(node == null){
//创建新的节点
size++;
return new Node(k,v);
}
//2递归调用
if(k.compareTo(node.key) == 0){
//返回当前节点,与add方法的区别是add不会改值
node.value = v;
return node;
}else if(k.compareTo(node.key) < 0){
node.left = add(node.left,k,v);
return node;
}else{
node.right = add(node.right,k,v);
return node;
}
}
@Override
public boolean contains(K k){
return contains(root,k);
}
public boolean contains(Node node,K k) {
/* if(k == null){
return false;
}
//1终止条件
if(node == null){
return false;
}
//2递归调用
if(k.compareTo(node.key) == 0){
return true;
}else if(k.compareTo(node.key) < 0){
return contains(node.left,k);
}else{
return contains(node.right,k);
}*/
//调用get(root,k)方法
return get(root,k) != null;
}
}
2、BSTMap的测试代码如下:
package map;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
Map<Integer,Integer> map = new BSTMap();
System.out.println("isEmpty:" + map.isEmpty());
int[] arr = {13,6,9,2,7,3,1,8,2,6,4,5,8};
for(int i=0;i<arr.length;i++){
map.add(arr[i],arr[i]);
}
System.out.println("size:" + map.getSize());
System.out.println("isEmpty:" + map.isEmpty());
System.out.println("contains5:" + map.contains(5));
System.out.println("contains1:" + map.contains(5));
System.out.println("contains12:" + map.contains(12));
System.out.println("get6:" + map.get(6));
map.set(12,12);
map.set(13,14);
System.out.println("size:" + map.getSize());
System.out.println("get12:" + map.get(12));
System.out.println("get13:" + map.get(13));
}
}
如有理解不到之处,望指正!
以上是关于0037数据结构之Set和Map的主要内容,如果未能解决你的问题,请参考以下文章