一.查找算法;
1.顺序查找时间复杂度为O(n)
/**
* 在s[0]-s[n-1]中顺序查找关键字为Key的记录 ,查找成功时返回该记录的下标序号;失败时返回-1
*/
int SequelSearch(elemtype s[], keytype Key, int n){
int i;
i = 0;
while (i < n && s[i].Key != Key)
i++;
if (s[i].Key == Key)
return i;
else
return -1;
}
2.折半查找时间复杂度为O(logN)
/**
* 在下届为low,上界为high的数组a中折半查找数据元素x
*/
int binarySearch(elemtype a[], elemtype x, int low, int high) {
int mid;
if (low > high)
return -1;
mid = (low + high) / 2;
if (x == a[mid])
return mid;
if (x < a[mid])
return (binarySearch(a, x, low, mid - 1));
else
return (binarySearch(a, x, mid + 1, high));
}
nt binarySearch(elemtype a[], keytype key, int n) {
int low, high, mid;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low + high) / 2;
if (a[mid].key == key)
return mid;
else if (a[mid].key < key)
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
3.二叉排序树时间复杂度为O(logN)
- public TreeNode search(int key) {
- TreeNode pNode = root;
- while (pNode != null && pNode.key != key) {
- if (key < pNode.key) {
- pNode = pNode.leftChild;
- } else {
- pNode = pNode.rightChild;
- }
- }
- return pNode;
- }
- /**
- * 获取该二叉查找树的字符串表示
- */
- public String toString() {
- StringBuilder sbBuilder = new StringBuilder(" [ ");
- for (TreeNode p : inOrderTraverseList()) {
- sbBuilder.append(p);
- sbBuilder.append(" ");
- }
- sbBuilder.append("]");
- return sbBuilder.toString();
- }
4.散列(hash)表查找找效率不依赖于数据长度n,查找效率非常快,很多能达到O(1),查找的效率是a(装填因子)的函数,而不是n的函数
拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中
1) Step1 对给定k值,计算哈希地址 Di=H(k);若HST为空,则查找失败;若HST=k,则查找成功;否则,执行step2(处理冲突)。
2) Step2 重复计算处理冲突的下一个存储地址 Dk=R(Dk-1),直到HST[Dk]为空,或HST[Dk]=k为止。若HST[Dk]=K,则查找成功,否则查找失败。
- public static int searchHash(int[] hash, int hashLength, int key) {
- // 哈希函数
- int hashAddress = key % hashLength;
- // 指定hashAdrress对应值存在但不是关键值,则用开放寻址法解决
- while (hash[hashAddress] != 0 && hash[hashAddress] != key) {
- hashAddress = (++hashAddress) % hashLength;
- }
- // 查找到了开放单元,表示查找失败
- if (hash[hashAddress] == 0)
- return -1;
- return hashAddress;
- }
-
二.如何判断一个单链表是否有环?
设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。
仔细研究两个链表,如果他们相交的话,那么他们最后的一个节点一定是相同的,否则是不相交的。因此判断两个链表是否相交就很简单了,分别遍历到两个链表的尾部,然后判断他们是否相同,如果相同,则相交;否则不相交。示意图如下:
判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个问len2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可
三.找出所有的重复行
select
userid,username,userlevel
from
tb
where
userid
not
in
(
select
userid
from
tb
group
by
username,
userlevel
having
count
(1)=1)
在某个项目中,我们需要对2亿条手机号码删除重复记录(过滤号码黑名单同样有效)
建立一个足够大的bit数组当作hash表
以bit数组的下标来表示一个整数
以bit位中的0或1来表示这个整数是否在这个数组中存在
适用于无重复原始数据的搜索
原来每个整数需要4byte空间变为1bit,空间压缩率为32倍
3000w数据的表,取某项字段前50项数据,内存2G
3000w的数据划分为1000段,也就是1-3w为一段,30001-6w项为第二段,依次类推,从每3w的数据中提取出前50条数
假设一个文件中有9亿条不重复的9位整数,现在要求对这个文件进行排序。
http://blog.csdn.net/lixam/article/details/8845310
1.Bloom Filter
大致思想是这样,把一个数据通过N个哈希函数映射到一个长度为M的数组的一位上,将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明该数据的存在。但不能保证完全正确性,但是此方法无比高效。
【实例】给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?
2.哈希法
这个简单,无非是通过一些哈希函数把元素搞到一个指定的位置,简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。这个很一般啊感觉。无非就是分类查找么,完全不如1猛。
3.最大或最小堆
就是一个完全的最大或最小二叉树,用途,比如:1)100w个数中找最大的前100个数。 用一个100个元素大小的最小堆即可。感觉还是不错的。
4.Bit-map
所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。
从100万数据中找出最大10条的最优算法
最大就设最 小根堆
构建一个只有10个元素的min-heap,那么根结点就是这10个数中最小的数,然后开始遍历数组,如果遇到的数比min-heap的根结点还小,直接跳过,遇到比min-heap根结点大的数,就替代根结点,然后对这个min-heap进行维护(也就是排序,保证heap的特征)。那么遍历完数组后,这个min-heap的10个元素就是最大的10个数。
怎样对10亿个数字快速去重?——浅析位图数据结构及其应用
借助hash算法思想,把一个大文件哈希分割到多个小文件中,而哈希冲突的数字
一定会在同一个小文件中,从而保证了子问题的独立性,然后就可以单独对小文件通过快速排序来去重
——这样就通过分而治之解决了几G数据进行内排序的问题。
虽然哈希分割文件是O(n)的时间复杂度,但总的效率仍是依从快速排序的时间复杂度O(logn)。
另外,分而治之有个好处就是借助子问题的独立性可以利用多核来做并行处理,甚至做分布式处理。
位图的原理类似我们常用的标记数组map[]/vis[],比如map[i] = 1表示把第i个元素标记为1,按照这种思想来去重是很简单的。
现在假定QQ号数字范围是[0, 10亿),则要申请10亿个char元素用来做标记,那么进程就需要1G的运行内存。
那如果数字范围增大到100亿,一般的计算机可能就吃不消了。
位图数据结构只需要1/8的空间, 节省7/8的内存是非常可观的 。
因为标记只有1和0两个值,所以可以 只用一个比特位来做标记 。
设有char类型数x,1字节包括8个位,我们可以申请char bit_map[10亿/8+1]的空间,就足以给范围在[0,10亿)的数字去重了。
选择char类型 而不是int等其它类型是考虑到,C标准规定任何实现都要保证char类型占1个字节。
+1 ,是考虑到C整型除法向下取整的特点,比如100/8结果为12,这样访问编号>=96的比特位(设从0开始编号),就会发生数组越界。
我们知道位图的数据结构就是一个数组,而 位图的操作(算法) 基本依赖于下面3个元操作
set_bit(char x, int n); //将x的第n位置1,可以通过x |= (x << n)来实现
clr_bit(char x, int n); //将x的第n位清0,可以通过x &= ~(1 << n)来实现
get_bit(char x, int n); //取出x的第n位的值,可以通过(x >> n) & 1来实现
有了上面3个元操作,位图的具体操作就简单了——
比如,要对数字int x = 1848105做标记,就可以调用set_bit(bit_map[x/8], x%8);
除法看做求“组编号”,x/8即是 以8个位为一个小组,分组到编号为 idx = x/8 的bit_map元素中,然后在组内偏移 lft = x%8 个比特位。
考虑到这些操作是非常频繁的,所以把上述三个方法改写成宏减少函数调用的开销,并且把x/8改为x<<3,x%8改为x&7。
http://wenku.baidu.com/link?url=lDRUWDDSWm5dRxyEVdH0B2sM6XMUxFQ86y8MBgv3hlvmRqLAIkPB5ZdzyIT9TOil0gURZwAJEcJXBvBEB9-tNUyYI39Rg9ixK8kZ19t0sTa
写一段程序需要高效的实现读取一个超过1G 的文本文件,文件中包含很多字节的数据。如果需要统计文件中出现“面试”重复的次数,用 Linux命令如何处理。
cat a.txt | grep '面试' -o | wc -w
百度提示词:
索引与前缀查询
典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。Trie是一颗存储多个字符串的树。相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。
TopK算法用于解决统计热词的问题。解决TopK问题主要有两种策略:hashMap统计+排序、堆排序
hashmap统计: 先对这批海量数据预处理。具体方法是:维护一个Key为Query字串,Value为该Query出现次数的HashTable,即hash_map(Query,Value),每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可,最终在O(N)的时间复杂度内用Hash表完成了统计。
堆:借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。即借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。所以,我们最终的时间复杂度是:O(N) + N’ * O(logK),(N为1000万,N’为300万)。排序
StringBuffer转成String的时候可以用toString()方法
把String添加到StringBuffer中用append()方法
String s=integer.toString(i);int i=integer.valueOf(my_str).intValue();
Integer转换成int的方法
Integer i = new Integer(10);
int k = i.intValue();
即Integer.intValue();
int转换成Integer
int i = 10;
Integer it = new Integer(i);
String str = "10";
Integer it = new Interger(str);
int转换成String
int i = 10;
(1)String s = String.valueOf(i);
(2)String s = Ingeger.toString(i);
int i = it.intValue();
即:int i = Integer.intValue(string);
String转换成Integer
String str = "10";
Integer it = Integer.valueOf(str);
Integer转换成String
Integer it = new Integer(10);
String str = it.toString();