top-10-algorithms-for-coding-interview 笔记

一个牛人写的LeetCode上的解题思路: http://blog.csdn.net/u011095253?viewmode=contents
国内算法论坛一亩地:http://www.1point3acres.com/bbs/forum-84-4.html
Leetcode网站,IT公司 http://leetcode.com/
Crack code inteview: http://hawstein.com/posts/ctci-solutions-contents.html : 重要程度四星,该博客以C++为主,思路很重要。
十大算法:http://geek.csdn.net/news/detail/3722


Java 方面的算法总结必看:top-10-algorithms-for-coding-interview ,个人部分笔记如下:

1. Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, *, /. Each operand may be an integer or another expression.  <= Answer ,  this problem is simple. 

 Some examples:
  ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9
  ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6

Java 1.7 之前switch 中的表达式只能是int或char,1.7之后可以是String

2. Finding the longest palindromic substring(回文子串) is a classic problem of coding interview.   

 Some examples:  ccabcbaceffe the longest palindromic substring is abcba.

Answer.  本题有三种方法,分别是最笨的方法,即逐一判断任何可能出现的子串是否为回文子串,然后找出最长者,这种方法中存在重复判断的问题,故可以考察采用动态规划算法,设P[i][j]为是否是回文子串,虽然时间复杂度降低,但空间复杂度上升为N*N,第三种方法是抓住回文子串的特性,或奇,或偶,以i为中心,判断可能出现的最长子串。

3.Word Break: Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.  Answer

    For example, given  s = “leetcode”,  dict = ["leet", "code"]. Return true because “leetcode” can be segmented as “leet code”.

  即使告诉你要使用动态规划,你知道怎么用吗?动态规划的初始状态是怎样的呢?该问题的动态规划方程是什么?,动态规划中记录重复子问题的数组,使得问题得以跳跃着前进。

4. Word Ladder   Answer

6Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:

Only one letter can be changed at a time
Each intermediate word must exist in the dictionary
For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.

Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
5. Median of Two Sorted Arrays Java   Answer

There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

6. Regular Expression Matching Answer

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") return false
isMatch("aa","aa") return true
isMatch("aaa","aa") return false
isMatch("aa", "a*") return true
isMatch("aa", ".*") return true
isMatch("ab", ".*") return true
isMatch("aab", "c*a*b") return true
7 Two Sum,  The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.  Answer

For example:

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

How Developers Sort in Java?

While analyzing source code of a large number of open source Java projects, I found Java developers frequently sort in two ways. One is using the sort() method of Collections or Arrays, and the other is using sorted data structures, such as TreeMap and TreeSet.

本质上是使用Java中已经提供好的各种集合类中方法或已经据有排序功能的有序类中,无论是前者还是后者都要提供自定义的comparator,而后者是先创建一个这样可以排序类,然后直接addALL(让未排序的集合类)。

Bit operators:

OR (|)AND (&)XOR (^)Left Shift (<<)Right Shift (>>)Not (~)
1|0=11&0=01^0=10010<<2=10001100>>2=0011~1=0

Get bit i for a give number n. (i count from 0 and starts from right)

public static boolean getBit(int num, int i){
	int result = num & (1<<i);
 
	if(result == 0){
		return false;
	}else{
		return true;
	}
}

For example, get second bit of number 10.

i=1, n=10
1<<1= 10
1010&10=10
10 is not 0, so return true;

判断字符串中的字符是否为数字: str.charAt(i)>='0' && str.charAt(i) <='9', 则Integer.valueOf(Str)的源码核心为:

      public int StringToInteger(String str) {

		int i = 0;
		double result = 0.0;
		while (i < str.length() && str.charAt(i) >= '0' && str.charAt(i) < '9') {

			result += result * 10 + (str.charAt(i) - '0');
			i++;
		}

		// handle max and min
		if (result > Integer.MAX_VALUE)
			return Integer.MAX_VALUE;

		if (result < Integer.MIN_VALUE)
			return Integer.MIN_VALUE;

		return (int) result;
	}

如上还有比较好的是,String中的整型字符得到整型值的方法是 0 + char -‘0’

问:char aa = '9';  int bb = aa;  System.out.println(bb); 输出是什么呢? 是9的ACSII值:57

而char转为int的方法:int i = 97,char c = (char)i  即强制类型转换。


toCharArray() //get char array of a String
Arrays.sort()  //sort an array
Arrays.toString(char[] a) //convert to string
charAt(int x) //get a char at the specific index
length() //string length
length //array size 
substring(int beginIndex) 
substring(int beginIndex, int endIndex)
Integer.valueOf()//string to integer
String.valueOf()/integer to string


树的深度优先和广度优先遍历:总体上来说,DFS 深度优先遍历就是尽量的朝着先访问叶子的方向,用到的数据结构是栈,BFS 而广度优先遍历相当于层级访问树,使用的数据结构是队列。在JAVA实现中,栈可以使用Stack类,队列可以使用Queue接口的实现类, 而LinkedList, ArrayDeque,都是Double-ended Queue --- Deque,即可以做Stack,又可以作为Queue。在算法上基本是相同的,深度是先将right结点放入stack中,而广度是先将left结点放入queue中,而在使用LinkedList和ArrayDeque时,使用pop和push相当使用stack,而poll和offer相当于使用使用Queue。

BTW,树的深度优先遍历,就相当于树的先根遍历,Pre-Order Traversal, 对比一下In-Order Trasversal 和 Post-Order Trasversal.

import java.util.ArrayDeque;

public class BinaryTree {
	static class TreeNode{
		int value;
		TreeNode left;
		TreeNode right;
		
		public TreeNode(int value){
			this.value=value;
		}
	}
	
	TreeNode root;
	
	public BinaryTree(int[] array){
		root=makeBinaryTreeByArray(array,1);
	}

	/**
	 * 采用递归的方式创建一颗二叉树
	 * 传入的是二叉树的数组表示法
	 * 构造后是二叉树的二叉链表表示法
	 */
	public static TreeNode makeBinaryTreeByArray(int[] array,int index){
		if(index<array.length){
			int value=array[index];
			if(value!=0){
				TreeNode t=new TreeNode(value);
				array[index]=0;
				t.left=makeBinaryTreeByArray(array,index*2);
				t.right=makeBinaryTreeByArray(array,index*2+1);
				return t;
			}
		}
		return null;
	}
	
	/**
	 * 深度优先遍历
	 * 采用非递归实现
	 */
	public void depthOrderTraversal(){
		if(root==null){
			System.out.println("empty tree");
			return;
		}		
		ArrayDeque<TreeNode> stack=new ArrayDeque<TreeNode>();
		stack.push(root);		
		while(stack.isEmpty()==false){
			TreeNode node=stack.pop();
			System.out.print(node.value+"    ");
			if(node.right!=null){
				stack.push(node.right);
			}
			if(node.left!=null){
				stack.push(node.left);
			}			
		}
		System.out.print("\n");
	}

	/**
	 * 广度优先遍历
	 * 采用非递归实现
	 */
	public void levelOrderTraversal(){
		if(root==null){
			System.out.println("empty tree");
			return;
		}
		ArrayDeque<TreeNode> queue=new ArrayDeque<TreeNode>();
		queue.add(root);
		while(queue.isEmpty()==false){
			TreeNode node=queue.remove();
			System.out.print(node.value+"    ");
			if(node.left!=null){
				queue.add(node.left);
			}
			if(node.right!=null){
				queue.add(node.right);
			}
		}
		System.out.print("\n");
	}
	
	/** 
	 *                  13
	 *                 /  \
	 *               65    5
	 *              /  \    \
	 *             97  25   37
	 *            /    /\   /
	 *           22   4 28 32
	 */
	public static void main(String[] args) {
		int[] arr={0,13,65,5,97,25,0,37,22,0,4,28,0,0,32,0};
		BinaryTree tree=new BinaryTree(arr);
		tree.depthOrderTraversal();
		tree.levelOrderTraversal();
                //DFS: 13    65    97    22    25    4    28    5    37    32    
                //BFS: 13    65    5    97    25    37    22    4    28    32    
	}    
}


后序遍历非递归算法

public class PostOrderTrasversal {

	public static ArrayList<Integer> postorderTraversal(TreeNode root) {

		ArrayList<Integer> lst = new ArrayList<Integer>();

		if (root == null)
			return lst;

		Stack<TreeNode> stack = new Stack<TreeNode>();
		stack.push(root);

		TreeNode prev = null;
		while (!stack.empty()) {
			TreeNode curr = stack.peek();

			// go down the tree.
			// check if current node is leaf, if so, process it and pop stack,
			// otherwise, keep going down
			if (prev == null || prev.left == curr || prev.right == curr) {
				// prev == null is the situation for the root node
				if (curr.left != null) {
					stack.push(curr.left);
				} else if (curr.right != null) {
					stack.push(curr.right);
				} else {
					stack.pop();
					lst.add(curr.val);
				}
				// go up the tree from left node, need to check if there is a
				// right child
				// if yes, push it to stack
				// otherwise, process parent and pop stack
			} else if (curr.left == prev) {
				if (curr.right != null) {
					stack.push(curr.right);
				} else {
					stack.pop();
					lst.add(curr.val);
				}
				// go up the tree from right node
				// after coming back from right node, process parent node and
				// pop stack.
			} else if (curr.right == prev) {
				stack.pop();
				lst.add(curr.val);
			}
			prev = curr;
		}

		return lst;
	}

	public static void iteratorPostNodeTree(TreeNode node) {
		// 定义标记node 记录上一个节点
		TreeNode tempNode = node;
		Stack<TreeNode> stack = new Stack<TreeNode>();
		while (node != null) {
			// 左节点入栈
			for (; node.left != null; node = node.left) {
				stack.push(node);
			}
			// 当前节点无右子 或者右子已经输出
			while (node != null
					&& (node.right == null || node.right == tempNode)) {
				// showValue(node.val);
				System.out.print(node.val);
				tempNode = node;
				if (stack.empty()) {
					return;
				}
				node = stack.pop();
			}

			// 处理右子
			stack.push(node);
			node = node.right;
		}
	}

	private static TreeNode init() {
		TreeNode node1 = new TreeNode(1);
		TreeNode node2 = new TreeNode(2);
		TreeNode node3 = new TreeNode(3);
		TreeNode node4 = new TreeNode(4);
		TreeNode node5 = new TreeNode(5);
		TreeNode node6 = new TreeNode(6);
		TreeNode node7 = new TreeNode(7);
		node1.left = node2;
		node1.right = node3;
		node2.left = node4;
		node2.right = node5;
		node3.left = node6;
		node3.right = node7;
		// 返回根节点
		return node1;
	}

	public static void main(String[] arg) {

		TreeNode root = init();

		ArrayList<Integer> list = postorderTraversal(root);

		for (Integer it : list) {
			System.out.print(it);
		}
		System.out.println("");
		iteratorPostNodeTree(root);

	}



久闻《编程珠玑》一书中提出的bitmap算法之大名,只是没有深入的去研究,今天下午有兴致研究一番,才知道其中的玄机奥秘,不亚于KMP算法之巧妙,下面就由浅入深的谈谈bitmap算法。 

一、bitmap算法思想 

    32位机器上,一个整形,比如int a; 在内存中占32bit位,可以用对应的32bit位对应十进制的0-31个数,bitmap算法利用这种思想处理大量数据的排序与查询

    优点:1.运算效率高,不许进行比较和移位;2.占用内存少,比如N=10000000(一千万);只需占用内存为N/8=1250000Byte=1.25M。 
   缺点:所有的数据不能重复。即不可对重复的数据进行排序和查找。 

   比如: 
          第一个4就是 
          00000000000000000000000000010000 
          而输入2的时候 
          00000000000000000000000000010100 
          输入3时候 
          00000000000000000000000000011100 
          输入1的时候 
          00000000000000000000000000011110 

    思想比较简单,关键是十进制和二进制bit位需要一个map图,把十进制的数映射到bit。下面详细说明这个map映射表。 

二、map映射表 

假设需要排序或者查找的总数N=10000000,那么我们需要申请内存空间的大小为int a[1 + N/32],其中:a[0]在内存中占32为可以对应十进制数0-31,依次类推: 
bitmap表为: 

a[0]--------->0-31 
a[1]--------->32-63 
a[2]--------->64-95 
a[3]--------->96-127 
.......... 

由此可见如果一个int值可能重复的次数最多为4次,则一个int32位只能为8个数值判断是否存在,则要申请的a[1+N/32 * 2] = a[1+N/16]
那么十进制数如何转换为对应的bit位,下面介绍用位移将十进制数转换为对应的bit。 

n/32的值对应数组下标,而n%32,即余数,对应数的Bit
三、位移转换 
例如十进制0,对应在a[0]所占的bit为中的第一位: 
00000000000000000000000000000001 
0-31:对应在a[0]中 
i =0                        00000000000000000000000000000000 
temp=0                  00000000000000000000000000000000 
answer=1                00000000000000000000000000000001 
i =1                         00000000000000000000000000000001 
temp=1                   00000000000000000000000000000001 
answer=2                 00000000000000000000000000000010 
i =2                          00000000000000000000000000000010 
temp=2                    00000000000000000000000000000010 
answer=4                  00000000000000000000000000000100 
i =30                         00000000000000000000000000011110 
temp=30                   00000000000000000000000000011110 
answer=1073741824  01000000000000000000000000000000 
i =31                         00000000000000000000000000011111 
temp=31                   00000000000000000000000000011111 
answer=-2147483648 10000000000000000000000000000000 

32-63:对应在a[1]中 

i =32                    00000000000000000000000000100000 
temp=0                00000000000000000000000000000000 
answer=1              00000000000000000000000000000001 
i =33                     00000000000000000000000000100001 
temp=1                 00000000000000000000000000000001 
answer=2               00000000000000000000000000000010 
i =34                      00000000000000000000000000100010 
temp=2                  00000000000000000000000000000010 
answer=4                00000000000000000000000000000100 
i =61                       00000000000000000000000000111101 
temp=29                  00000000000000000000000000011101 
answer=536870912   00100000000000000000000000000000 
i =62                        00000000000000000000000000111110 
temp=30                   00000000000000000000000000011110 
answer=1073741824  01000000000000000000000000000000 
i =63                         00000000000000000000000000111111 
temp=31                   00000000000000000000000000011111 
answer=-2147483648 10000000000000000000000000000000 


浅析上面的对应表: 
1.求十进制0-N对应在数组a中的下标,简而言之通过取余的整数得到在数组中的下标: 
十进制0-31,对应在a[0]中,先由十进制数n转换为与32的余可转化为对应在数组a中的下标。比如n=24,那么 n/32=0,则24对应在数组a中的下标为0。又比如n=60,那么n/32=1,则60对应在数组a中的下标为1,同理可以计算0-N在数组a中的下标。 如果是每个数字占两位那么应该是取32/2=16的余得到的整数。

2.0-N对应0-31中的数, 通过取余后的余数得到在位数: 
十进制0-31就对应0-31,而32-63则对应也是0-31,即给定一个数n可以通过模32求得对应0-31中的数。 

3.利用移位0-31使得对应32bit位为1
四、编程实现 

[cpp] view plaincopy

 

#include <stdio.h>  

  

#define BITSPERWORD 32  

#define SHIFT 5  

#define MASK 0x1F  

#define N 10000000  

  

int a[1 + N/BITSPERWORD];//申请内存的大小  

  

10 //set 设置所在的bit位为1  

11 //clr 初始化所有的bit位为0  

12 //test 测试所在的bit为是否为1  

13   

14 void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }  

15 void clr(int i) {        a[i>>SHIFT] &= ~(1<<(i & MASK)); }  

16 int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }  

17   

18 int main()  

19 {   int i;  

20     for (i = 0; i < N; i++)  

21         clr(i);    

22     while (scanf("%d", &i) != EOF)  

23         set(i);  

24     for (i = 0; i < N; i++)  

25         if (test(i))  

26             printf("%d\n", i);  

27   

28     return 0;  

29 }  

 

上面的位操作可以直接使用 i >>5 <=> i/32, i & 0x1F = i %32


解析本例中的void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); } 

1.i>>SHIFT: 
其中SHIFT=5,即i右移5为,2^5=32,相当于i/32,即求出十进制i对应在数组a中的下标。比如i=20,通过i>>SHIFT=20>>5=0 可求得i=20的下标为0; 

2.i & MASK: 
其中MASK=0X1F,十六进制转化为十进制为31,二进制为0001 1111i&0001 1111)相当于保留i的后5位。 

比如i=23,二进制为:0001 0111,那么 
                         0001 0111 
                   &    0001 1111 = 0001 0111 十进制为:23 
比如i=83,二进制为:0000 0000 0101 0011,那么 
                          0000 0000 0101 0011 
                     &   0000 0000 0001 1111 = 0000 0000 0001 0011 十进制为:19 

i & MASK相当于i%32 

3.1<<(i & MASK) 
相当于把1左移 (i & MASK)。 
比如(i & MASK)=20,那么i<<20就相当于: 
         0000 0000 0000 0000 0000 0000 0000 0001 >>20 
      =0000 0000 0000 1000 0000 0000 0000 0000 

4.void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }等价于: 
void set(int i) 

   a[i/32] |= (1<<(i%32)); 
}

 

 

 

【扩展】

Bloom filter可以看做是对bit-map的扩展

【问题实例】

1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。

8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的电话)

2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上,在遍历这些数的时候,如果对应位置的值是0,则将其置为1;如果是1,将其置为2;如果是2,则保持不变。或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个 2bit-map,都是一样的道理

 

Java String 的HashCode值:

Returns a hash code for this string. The hash code for a String object is computed as 
       s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]       
using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.) 


我们常用的数量级冠词有M和G,1M等于10的6次方,1G等于10的9次方。而kB属于计算机领域,B是字节(byte)的英文字头,字节是由8个位所组成即1 byte = 8 bit (字位),可代表一个字符(A~Z)、数字(0~9)、或符号(,.?!%&+-*/),是内存储存数据的基本单位。计算机用的是二进制,2的10次方是1024与十进制中的1000是同一个数量级称为1kB。 
1 KB = 1024 bytes 
1 MB = 1024 KB 
1 GB = 1024 MB = 1024 * 1024 *1024 * 8 bit = 2^10 * 2^10 * 2*10 *2^3 = 2^33 bit = 8 589 934 592 bit 约86亿bit

1G内存相当于2个512M内存
相当于4个256M内存
相当于8个128M内存
java中的int 是32位的没错,范围应该是- 2147483648 —— 2 147 483 647 约21亿, 故用1G的内存中的一个bit表示一个整数,1G内存可以完全表示出来。

java交换两个数或字符串可以用temp来交换,如果不使用temp,有下面两种交换方法:

1.对于数来说,可以用

a = a + b; 
b = a - b; 
a = a - b;

来进行交换

2.更为通用的方法是用异或来交换

a=a^b; 
b=b^a; 
a=b^a;

异位运算交换两个整数的算法原理。异或运算有两个特性:

1、一个数异或本身恒等于0,如5^5恒等于0;

2、一个数异或0恒等于本身,如5^0恒等于5。

Given an array of integers, every element appears twice except for one. Find that single one.

Thoughts

The key to solve this problem is bitmanipulation. XOR will return 1 only on two different bits. So if two numbers are the same, XOR will

return 0. Finally only one number left.  Array中的Integer不需要进行排序,不论什么样的顺序使用XOR之后结果都会得到single数。

Java Solution

public class Solution{

    public int singleNumber(int[] A){

        int x=0;

        for(int a: A){

            x = x ^ a;

        }

        return x;

    }

}


Least Recently Used (LRU) Cache Implementation (Java)

在Leetcode上也有相关的介绍: LRU 但是实现不够优雅,在该实现中使用hashMap来进行访问,使用双向linkedList来保证LRU的访问顺序,在JAVA中,LinkedHashMap可以简单的实现LRU,具体可以参考下面。

A cache is a mechanism by which future requests for that data are served faster and/or at a lower cost. This article describes a data structure to hold the cache data and an implementation in Java to service the  cache requests.

Requirements

  1. Fixed size: The cache needs to have some bounds to limit memory usage.
  2. Fast access: The cache insert and lookup operations need to be fast preferably O(1) time.
  3. Entry replacement algorithm: When the cache is full, the less useful cache entries are purged from cache. The algorithm to replace these entries is Least Recently Used (LRU) - or the cache entries which have not been accessed recently will be replaced.

Design discussion

Since the lookup and insert operationed need to fast a HashMap would be a good candidate. The HashMap accepts an initial capacity parameter but it re-sizes itself if more entries are inserted. So we need to override the put() operation and remove (or purge) an entry before inserting.

How do we select the entry to be purged? One approach is to maintain  a timestamp at which the entry was inserted and select the entry with the oldest timestamp. But this search would be linear taking O(N) time.

So we need the entries to be maintained in a sorted list based on the order in which the entries were accessed. An alternate way to achieve this would be to maintain the entries in a doubly linked list using which everytime an entry is accessed ( a cache lookup operation), the entry is also moved to the end of the list. When we need to purge the entries it is done from the top of the list. In an ArrayList when an element is removed the rest of the entries need to be moved by one to fill the gap. A doubly linked list does not have this issue.

We have come up with a design that meets our requirements and guarantees O(1) insert and O(1) lookup operations and also has a configurable limit on the number of entries. Let's begin the implementation.

Lucky for us, JDK already provides a class that is very suitable for our purpose - LinkedHashMap. This class maintains the entries in a HashMap for fast lookup at the same time maintains a doubly linked list of the entries either inAccessOrder or InsertionOrder. This is configurable so use use AccessOrder as true. It also has a methodremoveEldestEntry() which we can override to return true when the cache size exceeds the specified capacity(upper limit). So here is the implementation. Enjoy.

Implementation

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.LinkedHashMap;
import java.util.Map.Entry;
 
public class LRUCache < K, V > extends LinkedHashMap < K, V > {
 
     private int capacity; // Maximum number of items in the cache.
     
     public LRUCache( int capacity) {
         super (capacity+ 1 , 1 .0f, true ); // Pass 'true' for accessOrder.
         this .capacity = capacity;
     }
    //Returns true if this map should remove its eldest entry.
     protected boolean removeEldestEntry(Entry entry) {
         return (size() > this .capacity); //直接删除
     }
}

LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
          Constructs an empty LinkedHashMap instance with the specified initial capacity, load factor and ordering mode.

The removeEldestEntry(Map.Entry) method may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map. 

The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased.When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table isrehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.

As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of theHashMap class, includingget and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur. 


同时注意,LinkedHashMap非线程安全的,Collections.synchronizedMap(new LinkedHashMap(...));  而synchronizedMap的实现原理是使用了一个wrapper, 这个是装饰者模式吧? someone总结了一个JDK中所有模式列表:http://www.uml.org.cn/itnews/2014031107.asp

具体到synchronziedMap的实现方式是在里面创建了一个SynchronziedMap类,该类内部维护了一个mutex锁,用来给所有的真正的map类的方法方法加上锁保护。

Returns a synchronized (thread-safe) map backed by the specified map.  In order to guarantee serial access, it is critical that all access to the backing map is accomplished through the returned map.

It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views: 即虽然使用了synchronizedMap但是要遍历新线性安全的对象的Collection view对象时, 仍需要锁保护。据我的理解是,因为返回的是一个新对象(这个对象又使用了同一把锁产生了新的同步对象),为了保证原对象与新对象之间的同步关系,必须使用锁将两者紧密的结合起来。

Map m = Collections.synchronizedMap(new HashMap());
   ...
Set s = m.keySet();  // Needn't be in synchronized block
    ...
synchronized(m) {  // Synchronizing on m, not s!
    Iterator i = s.iterator(); // Must be in synchronized block
    while (i.hasNext())
      foo(i.next());
}

Failure to follow this advice may result in non-deterministic behavior.

Java Collection进行迭代访问时是使用Iterator, 不论是使用那种方式,在多线程中使用都要对迭代的过程进行加锁,不然会有ConcurrentmodificationException抛出。

时间复杂度的计算:常见的时间复杂度,按数量级递增排列依次为:常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n^2)、立方阶O(n^3)、k次方阶O(n^k)、指数阶O(2^n)。  nlogn 之前的都是比较快速的算法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值