面试中关于key/value的问题及map的理解

面试问题1  用什么数据结构来存放电视剧,并且输出为有序。

面试问题2 统计一个字符串中单词的频率,并且频率按从大到小的顺序排列

对于第一个问题:把包含电视剧名字和集数的字符串作为key,对应的存储位置做为value 。用treemap比较合适。

对于第二个问题:单词作为key,出现的次数作为value。由于treemap的默认排序是对key进行排序,用treemap不合适,不如直接用hashmap,存取效率高。排序单独设计。

基础知识:先来理解一下map

map有两种理解方式,一种是当成key/value来理解,可以存放包含key/value属性的对象;另一种更理解可以理解为一种特殊的数组,只不过这个数组的下标【index】可以是

任意对象。



hashmap在存储键值对时借助了“数组+链表”的方式,就是哈希表。

存取元素:HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。

我们对一个键值对的查询,是分为四步的。
先根据key值计算出hash值以及h值(h值是java实现中处理得到的更优的index索引值)
查找table数组中的h位置,得到相应的键值对链表
根据key值,遍历键值对链表,找到相应的键值对,
从键值对中取出value值。

hashmap()查找,插入,删除的时间复杂度可以认为为O(1);


LinkedHashMap物理结构和hashmap差不多,只不过hashmap中,hashtable里存储的元素为链表的header,而在LinkedHashMap里面,hashTable里存储的元素为双向链表结点,结点的数据元素为header,结点的next或previous指向其他hashTable位置结点,从而可以实现按照插入或者访问顺序存储。


LinkedHashMap的双向链表为循环双向链表。为什么要用循环双向链表而不是普通双向链表,我个人认为这样可以少记一个头节点或者尾结点的位置。迭代器要想正向或逆向打印双链表,只需要知道header位置即可。

(循环双向链表的头部存放的是最久访问的节点或最先插入的节点,尾部为最近访问的或最近插入的节点,迭代器遍历方向是从链表的头部开始到链表尾部结束,在链表尾部有一个空的header节点,该节点不存放key-value内容,为LinkedHashMap类的成员属性,循环双向链表的入口。这句话其实就是描述了一个循环双向链表的结构,尾部都会空一个,作为header。)

迭代器总的过程应该是这样的:LinkedHashMap只有一个入口,就是数组hashtable的头地址。迭代器通过HashTable的头结点next或previou,找到循环双线链表头节点或尾节点,从而可以正向或逆向打印元素。

LinkedHashmap()查找,插入,删除的时间复杂度也为O(1),但却实现了可以按照key的插入顺序或访问顺序输出元素。


treemap的存储结构用的是红黑树。红黑树的操作时间跟二叉查找树的时间复杂度是一样的,执行查找、插入、删除等操作的时间复杂度为O(logn)。


面试问题1的实现

import java.util.*;

public class TestTreeMapDelete {
	public static void main(String[] args) {
		TreeMap<String, Integer> treemap = new TreeMap<String, Integer>();
		treemap.put("fengshen_001", 342314);
		treemap.put("fengshen_004", 322514);
		treemap.put("fengshen_005", 341319);
		treemap.put("fengshen_002", 344364);
		treemap.put("fengshen_004", 322514);
		
		System.out.println(treemap);
		System.out.println(treemap.subMap("fengshen_002", "fengshen_004"));
		
	}
}

面试问题2的实现:
<pre name="code" class="java">import java.util.*;

public class CountOccurenceOfWordsAdv {
	
	  public static void main(String[] args) {
	    // Text in a string
	    String text = "Have a good day. Have a good class. " +
	      "Have a good visit. Have fun!";

	    // Create a hash map to hold words and key and count as value
	    HashMap<String, Integer> hashMap = new HashMap<String, Integer>();

	    StringTokenizer st = new StringTokenizer(text, " .!?");
	    while (st.hasMoreTokens()) {
	      String key = st.nextToken();

	      if (hashMap.get(key) != null) {
	        int value = ((Integer)hashMap.get(key)).intValue();
	        value++;
	        hashMap.put(key, new Integer(value));
	      }
	      else {
	        hashMap.put(key, new Integer(1));
	      }
	    }


	    // Get an entry set for the tree map
	      Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet();

	    // Get an iterator for the entry set
	    Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator();

	    ArrayList<WordOccurrence> list = new ArrayList<WordOccurrence>();

	    while (iterator.hasNext()) {
	      StringTokenizer st1 =
	        new StringTokenizer(iterator.next().toString(), "=");
	      list.add(new WordOccurrence(st1.nextToken(),
	    		  Integer.parseInt(st1.nextToken())));
	    }

	    Collections.sort(list);
	    for (int i = 0; i < list.size(); i++) {
	      System.out.println(list.get(i));
	    }
	    
	  }
	}

	class WordOccurrence implements Comparable {
	  String word;
	  int count;

	  public WordOccurrence(String word, int count) {
	    this.word = word;
	    this.count = count;
	  }

	  public int compareTo(Object o) {
//	    return count - ((WordOccurrence)o).count;
	    return ((WordOccurrence)o).count - count;
	  }

	  public boolean equals(Object o) {
	    return word.equals(((WordOccurrence)o).word);
	  }

	  public String toString() {
	    return word + " " + count;
	  }
	}





展开阅读全文

没有更多推荐了,返回首页