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的主要内容,如果未能解决你的问题,请参考以下文章

C++STL之map和set的使用

C++STL之map和set的使用

C++之STLmap和set的模拟实现

第七节2:Java集合框架之map和set

20160227.CCPP体系详解(0037天)

C++之map和set总结