一、散列函数构造方法
- 除留取余法
对于散列表长度为 m 的散列函数公式为:
f(key)= key mod p (p <= m)
mod 就是取余的方法。一般 p 为小于或等于表长(最好接近 m)的最小质数或者不包含小于 20 质因子的合数。
二、处理散列冲突的方法
- 1. 开放地址法
一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
线性探测法:
fi(key)= (f(key)+ di)MOD m (di = 1,2,3,......,m-1)
- 2. 链地址法
其实这个就是 HashMap 的实现原理,将所有关键字为同义词的记录存储在一个单链表中,散列表只需要存储所有同义词子表的头指针就行了。
三、查找的一些面试题
- 剑指 Offer 面试题 3(Java 版):二维数组中的查找
题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路: 首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束;如果该数字大于要查找的数字,剔除这个数字所在的列;如果该数字小于要查找的数字,剔除这个数字所在的行。
也就是说如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除)行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。
show my code
/**
* 二维数组的查找
* @author innovator
*
*/
public class BinaryArraySearch {
/**
* 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
* 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
*
* 规律:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束:
* 如果该数字大于要查找的数字,剔除这个数字所在的列:如果该数字小于要查找的数字,剔除这个数字所在的行。
* 也就是说如果要查找的数字不在数组的右上角,则每-次都在数组的查找范围中剔除)行或者一列,这样每一步都可以缩小
* 查找的范围,直到找到要查找的数字,或者查找范围为空。
*
* @param data 待查找的数组
* @param number 要查找的数
* @return 查找结果,true 找到,false 没有找到
*/
public static boolean find(int[][] data,int number){
boolean found = false;
//输入条件判断
if(data == null || data.length <1 || data[0].length <1){
return false;
}
//选取右上角的数字
int row = 0;
int colum = data[0].length-1;
//行的数量
int rows = data.length;
//列的数量
int colums = data[0].length;
//行数正向增加,列数减少,同时保证在数组内
while(row >=0 && row < rows && colum >=0 && colum < colums){
//右上角的数字和指定的数字相等
if(data[row][colum] == number){
found = true;
break;
}else if(data[row][colum] > number){
//右上角的数字大于指定的数字,可以去掉最后一列
colum --;
}else{
//右上角的数字小于指定的数字,可以去掉所在的这一行
row ++;
}
}
return found;
}
public static void main(String[] args){
int[][] matrix = {
{1, 2, 8, 9},
{2, 4, 9, 12},
{4, 7, 10, 13},
{6, 8, 11, 15}
};
System.out.println(find(matrix, 7)); // 要查找的数在数组中
System.out.println(find(matrix, 5)); // 要查找的数不在数组中
System.out.println(find(matrix, 1)); // 要查找的数是数组中最小的数字
System.out.println(find(matrix, 15)); // 要查找的数是数组中最大的数字
System.out.println(find(matrix, 0)); // 要查找的数比数组中最小的数字还小
System.out.println(find(matrix, 16)); // 要查找的数比数组中最大的数字还大
System.out.println(find(null, 16)); // 健壮性测试,输入空指针
}
}
- 剑指 Offer 面试题 35(Java 版):第一个只出现一次的字符
题目:在字符串中找出第一个只出现一次的字符。如输入 "abaccdef",则输出 'b'。
思路: 我们可以通过将扫描到的 char 当成哈希表的 key,它出现的次数作为 value。当我们扫描完一次后,所有字符出现的次数都已经检查完毕了,接下来只需要顺序输出哈希表的 Entry,如果出现的次数是 1,那么直接返回。整个过程的时间复杂度是 O(n) 和 O(1)。
show my code
/**
* 寻找第一个不重复出现的字符
* @author innovator
*
*/
public class FirstNotRepeatChar {
/**
* 寻找第一个不重复出现的字符
* @param s
* @return
*/
public static char findFirstNotRepeatChar(String s){
if(s == null || s.length() <1){
throw new IllegalArgumentException("String should not be null or empty");
}
//用 LinkedHashMap 存放每个字符出现的次数,同时保证扫描顺序
Map times = new LinkedHashMap();
//第一次扫描字符串
for(int i=0;i> entries = times.entrySet();
for(Map.Entry entry:entries){
//只出现过一次
if(entry.getValue() == 1){
result = entry.getKey();
return result;
}
}
return result;
}
public static void main(String[] args) {
System.out.println(findFirstNotRepeatChar("google")); // l
System.out.println(findFirstNotRepeatChar("aabccdbd")); // '\0'
System.out.println(findFirstNotRepeatChar("abcdefg")); // a
System.out.println(findFirstNotRepeatChar("gfedcba")); // g
System.out.println(findFirstNotRepeatChar("zzgfedcba")); // g
}
}
- 剑指 Offer 面试题 40(Java 版):数组中只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
思路: 这个题目在强调一个(或两个)数字只出现一次,其他的出现两次。这有什么意义呢?我们想到异或运算的一个性质:任何一个数字异或它自己都等于0。了解了异或去重的原理,而且知道如果只有一个只出现一次的数字的求法,我们便要想办法把它分为两个子数组,每个子数组中包含一个只出现一次的数字,其他的数字都出现了两次。
剑指 offer 上的思路很巧妙,依然从头到尾异或所有的数字,这样得到的结果实际上就是两个只出现了一次的数字异或的结果,我们在异或后的结果中找出其二进制中最右边为 1 的位,该位既然为 1,说明异或的两个数 字对应的该位肯定不同,必定一个为 1,一个为 0。因此我们可以考虑根据此位是否为 1 来划分这两个子数组,这样两个只出现一次的数字就分开了。
但我们还要保证出现两次的数字都分到同一个子数组中,肯定不能两个重复的数字分在两个不同的子数组中,这样得到的结果是不对的。很明显,相同的数字相同的位上的值是相同的,要么都为 1,要么都为 0,因此我们同样可以通过判断该位是否为 1 来将这些出现两次的数字划分到同一个子数组中,该位如果为 1,就分到一个子数组中,如果为 0,就分到另一个子数组中。这样就能保证每个子数组中只有一个出现一次的数字,其他的数字都出现两次,分别全部异或即可得到这两个只出现一次的数字。时间复杂度为 O(n)。
另外,所有元素异或后,在找出最右边为 1 的时,我用的比剑指 offer 上更简洁的代码,主要用到了下面的结论:
对于一个数字 X,X&(-X) 之后得到的数字,是把 X 中最右边的 1 保留下来,其他位全部为 0。注意,这里的 -X 是 X 的相反数,-X=~X+1,这里的 ~X 意思是对 X 所有位取反,不要将二者弄混了。
show my code
public class FindNumsAppearOnceTest {
/**
* 寻找只出现一次的数字
* @param arr
* @throws Exception
*/
public static void findNumsAppearOnce(int[] arr) throws Exception{
if(arr == null || arr.length<2){
throw new Exception("输入的数据不合法");
}
int i;
int AllXOR = 0;
//全部异或
for(i=0;i