first commit

This commit is contained in:
nonstriater
2014-05-05 19:44:01 +08:00
commit b02ae8cf53
33 changed files with 3464 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
>算法虐我千百遍,我待算法如初恋。
##学习方法
1) 看源码 算法
2) 把所有经典算法写一遍
3) 加入算法学习社区,相互鼓励学习
4) 看经典书籍
##基本数据结构和算法
这些算法全部自己敲一遍。
二叉树
二叉查找树
Trie树
后缀树
平衡二叉树AVL
红黑树
B树(B-),B+,B*
R树
最优二叉树(赫夫曼树)
伸展树
二叉堆
二项树
二项堆
斐波那契堆(Fibonacci Heap)
哈希表/散列表 (Hash Table)
字符串算法:
BF算法
KMP算法
BM算法
图的算法:
最小生成树
拓扑排序
关键路径
最短路径: Floyd,Dijkstra,bellman-ford,spfa
排序(八大排序):
快排
插入排序 : 希尔排序
选择排序 : 堆排序
归并排序
基数排序
查找算法
静态查找: 顺序表查找,有序表查找,
动态查找: 二叉排序树,AVL树,B- ,B+
哈希表:
15个经典基础算法
A*.
Dijkstra.
DP
.BFS/DFS.
红黑树.
KMP
.遗传算法.
启发式搜索.
图像特征提取SIFT sift算法
.傅立叶变换
.Hash.
快速排序
.SPFA.
快递选择SELECT
@ July
《数学建模十大经典算法》
《数据挖掘领域十大经典算法》
《十道海量数据处理面试题》
《数字图像处理领域的二十四个经典算法》
《精选微软等公司经典的算法面试100题》
## 算法设计思想
为了更简洁的形式设计和藐视算法,在算法设计时又常常采用递归技术,用递归描述算法。
迭代法
穷举搜索法
递推法
动态规划
贪心算法
回溯
分治算法
http://www.chinaunix.net/old_jh/23/437639.html
## 后续阅读
《算法导论》
《编程珠玑》
《编程之美》
《剑指offer》
《深入理解计算机系统》
《TCP/IP详解三卷》
《UNIX网络编程二卷》
《UNIX环境高级编程:第2版》
## 参考链接和学习网站
微软面试100题 http://blog.csdn.net/column/details/ms100.html
程序员编程艺术 http://blog.csdn.net/v_JULY_v/article/details/6460494
https://github.com/julycoding/The-Art-Of-Programming-By-July 算法题集合
https://github.com/activesys/libcstl 通用数据结构和算法库
https://github.com/nonstriater/Learn-Algorithm 算法小组
贝叶斯:
1. 阮一峰总结的这两篇《贝叶斯推断及其互联网应用》
http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.html
http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_two.html
2. 算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)
http://www.cnblogs.com/leoo2sk/archive/2010/09/17/naive-bayesian-classifier.html
3. 朴素贝叶斯(NB,Naive Bayes)简介
http://home.cnblogs.com/group/topic/40112.html
4. 数学之美番外篇:平凡而又神奇的贝叶斯方法
http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/
这4部分看完后,就能自己推贝叶斯、朴素贝叶斯,还能了解各种常见概率、集合论等,实际上对应用贝叶斯已经可以打好基础了。
MYSQL 背后的数据结构和算法 http://blogread.cn/it/article/4088?f=wb2
算法演示:
http://sjjg.js.zwu.edu.cn/SFXX/sf1/sfys.html
http://www.cs.usfca.edu/~galles/visualization/Algorithms.html
编程网站:
http://leetcode.com/
http://oj.leetcode.com/
+31
View File
@@ -0,0 +1,31 @@
/*
### B树基础
B树即B-tree(B树就是B-tree),B是balanced,也就是平衡的意思。
B树又叫平衡多路查找树,节点的直接点个数可以多于2个,一颗m阶的B树:
1) 树中每个节点做多含有m个子节点
2) 根节点不是叶子节点,至少有2个孩子
3) 所有叶子节点在同一层
4) 除根节点和叶子节点外,每个分支节点至少有[m/2]个子树
5) 有j个孩子的非叶子节点有 j-1 个关键码,关键码递增一次排列
### B树应用
数据库
文件系统
### B树存储结构
### B树问题和延伸阅读
*/
+49
View File
@@ -0,0 +1,49 @@
/*
### 红黑树基础
一种自平衡的二叉查找树。在每个节点中增加一个存储位表示节点的颜色,可以是red或black
红黑树比AVL树优秀在哪?
5个性质
性质1. 节点是红色或黑色。
性质2. 根是黑色。
性质3. 所有叶子都是黑色(叶子是NIL节点)。
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有简单路径 都包含相同数目的黑色节点。
### 红黑树应用
### 存储结构
### 问题与延伸阅读
*/
typedef int ElemType
typedef struct node{
int color;
ElemType key;
struct node *lChild,*rChild,*pChild;
}*RBTree;
int rbtree_insert(RBTree *tree,ElemType key);
int rbtree_remove(RBTree *tree,ElemType key);
int rbtree_search(RBTree *tree,ElemType key);
+34
View File
@@ -0,0 +1,34 @@
/*
### 后缀树(suffix tree)基础
又叫后缀trie,与trie最大不同在于:字符串集合由指定的后缀子串组成。
很适合用来操作字符串的子串。 用于字符串的匹配和查询
### 后缀树应用
从目标串T中判断是否包含模式串P(时间复杂度接近KMP算法);
从目标串T中查找最长的重复子串;
从目标串T1和T2中查找最长公共子串;
Ziv-Lampel无损压缩算法;
从目标串T中查找最长的回文子串;
### 存储结构
### suffix tree 问题与延伸阅读
后缀数组
*/
@@ -0,0 +1,7 @@
###环境
###编译
Binary file not shown.
@@ -0,0 +1,337 @@
//
// bisearchtree.c
// BiSearchTree
//
// Created by nonstriater on 14-2-22.
//
//
#include "bisearchtree.h"
#include <stdio.h>
#include <stdlib.h>
/**
* 创建二叉查找树
*
* @return 指向一颗空树的指针
*/
BiSearchTree *bisearch_tree_new(){
return NULL;
}
/**
* 插入节点
*
* @param tree tree
* @param node 节点值
*/
BiSearchTree *bisearch_tree_insert(BiSearchTree *tree,ElemType node){
BiSearchTree *t = tree;
BiSearchTree *parent = NULL;
BiSearchTree *newNode = (BiSearchTree *)malloc(sizeof(BiSearchTree));
if (NULL==newNode) {
printf("malloc failure\n");
return tree;
}
newNode->key = node;
newNode->lChild = NULL;
newNode->rChild = NULL;
if (NULL==t) {
tree = newNode;
printf("insert first node %d \n",node);
return tree;
}
// 找合适的位置
while (t!=NULL) {
if( node < t->key){
parent = t;
t=t->lChild;
}else if(node == t->key){
printf("insert ignore:the bi search tree has the node with %d\n",node);
break;
}else{
parent = t;
t=t->rChild;
}
}
if (parent!=NULL) {
if (node>parent->key) {
printf("insert node %d to right of %d\n",node,parent->key);
parent->rChild = newNode;
}else{
printf("insert node %d to left of %d\n",node,parent->key);
parent->lChild = newNode;
}
}
return tree;
}
/**
* 查找节点
*
* @param tree tree
* @param node 节点值
* (也可以使用递归方式实现)
*/
int bisearch_tree_search(BiSearchTree *tree,ElemType node){
BiSearchTree *t;
t= tree;
while (NULL!=t) {
if (node<t->key) {
t=t->lChild;
}
else if (node==t->key) {
break;
}else{
t=t->rChild;
}
}
if (NULL==t) {
return -1;
}
return 0;
}
//private
int _delete_node(BiSearchTree *node,BiSearchTree*parent){
//该节点为叶子节点,直接删除
if (!node->rChild && !node->lChild)
{
free(node);//父节点???不然野指针,造成崩溃
}
else if(!node->rChild){ //右子树空则只需重接它的左子树
BiSearchTree *target=node->lChild;
node->key = node->lChild->key;
node->lChild=node->lChild->lChild;
node->rChild=node->lChild->rChild;
free(target);
}
else if(!node->lChild){ //左子树空只需重接它的右子树
BiSearchTree *target=node->rChild;
node->key = node->rChild->key;
node->lChild=node->rChild->lChild;
node->rChild=node->rChild->rChild;
free(target);
}
else{ //左右子树均不空
BiSearchTree *parent=node,*target=node->lChild;
while (target->rChild) {
parent = target;
target=target->rChild;
}// 找到左子树最大的,是删除节点的直接“前驱”
node->key = target->key;
if (target!=node) {
parent->rChild = target->lChild;
}else{
node->lChild = target->lChild;
}
free(target);
}
return 0;
}
//删除节点,需要重建排序树
/*
1) 删除节点是叶子节点(分支为0),结构不破坏
2)删除节点只有一个分支(分支为1),结构也不破坏
3)删除节点有2个分支,此时删除节点
思路一: 选左子树的最大节点,或右子树最小节点替换
*/
int bisearch_tree_delete(BiSearchTree **tree,ElemType node){
if (NULL==tree) {
return -1;
}
// 查找删除目标节点
BiSearchTree *target=*tree,*parent=NULL;
while (NULL!=target) {
if (node<target->key) {
parent=target;
target=target->lChild;
}else if(node==target->key){
break;
}else{
parent=target;
target=target->rChild;
}
}
if (NULL==target) {
printf("树为空,或想要删除的节点不存在\n");
return -1;
}
//该节点为叶子节点,直接删除
if (!target->rChild && !target->lChild)
{
if (NULL==parent) {////只有一个节点的二叉查找树
*tree=NULL;
}else{
if (target->key>parent->key) {
parent->rChild=NULL;
}else{
parent->lChild=NULL;
}
}
free(target);//父节点处理,不然野指针,造成崩溃
}
else if(!target->rChild){ //右子树空则只需重接它的左子树
BiSearchTree *del=target->lChild;
target->key = target->lChild->key;
target->lChild=target->lChild->lChild;
target->rChild=target->lChild->rChild;
free(del);
}
else if(!target->lChild){ //左子树空只需重接它的右子树
BiSearchTree *del=target->rChild;
target->key = target->rChild->key;
target->lChild=target->rChild->lChild;
target->rChild=target->rChild->rChild;
free(del);
}
else{ //左右子树均不空
BiSearchTree *p=target,*t=target->lChild;
while (t->rChild) {
p = t;
t=t->rChild;
}// 找到左子树最大的,是删除节点的直接“前驱”
target->key = t->key;
if (p!=target) {
p->rChild = t->lChild;
}else{
target->lChild = t->lChild;
}
free(t);
}
return 0;
// if (node==tree->key) {
// return _delete_node(tree);
// }else if(node>tree->key){
// return bisearch_tree_delete(tree->rChild,node);
// }else{
// return bisearch_tree_delete(tree->lChild,node);
// }
/*
// 查找删除目标节点
BiSearchTree *t=tree,*parent=NULL;
while (NULL!=t) {
if (node<t->key) {
parent=t;
t=t->lChild;
}else if(node==t->key){
break;
}else{
parent=t;
t=t->rChild;
}
}
if (NULL==t) {
printf("树为空,或想要删除的节点不存在\n");
return -1;
}
// parent 可能为null
//删除节点0个分支
if (!t->lChild && !t->rChild) {
if (t->key>parent->key) {
parent->rChild=NULL;
}else{
parent->lChild=NULL;
}
free(t);
return 0;
}
//删除节点有1个分支
else if (t->lChild && !t->rChild) {
if (t->key>parent->key) {
parent->rChild=t->lChild;
}else{
parent->lChild=t->lChild;
}
free(t);
return 0;
}
else if (!t->lChild && t->rChild) {
if (t->key>parent->key) {
parent->rChild=t->rChild;
}else{
parent->lChild=t->rChild;
}
free(t);
return 0;
}
//删除节点有2个分支
else if (t->lChild && t->rChild) {
if (t->key->parent->key) {
}
}
return -1;
*/
}
// 中序遍历节点,也就是从小到大输出
void bisearch_tree_inorder_traversal(BiSearchTree *tree){
if (tree) {
bisearch_tree_inorder_traversal(tree->lChild);
printf("%d \n",tree->key);
bisearch_tree_inorder_traversal(tree->rChild);
}
}
@@ -0,0 +1,65 @@
//
// bisearchtree.h
// BiSearchTree
//
// Created by nonstriater on 14-2-22.
//
//
/**
*
二叉查找树(Binary search tree),也叫有序二叉树(Ordered binary tree),排序二叉树(Sorted binary tree)。是指一个空树或者具有下列性质的二叉树:
1. 若任意节点的左子树不为空,则左子树上所有的节点值小于它的根节点值
2. 若任意节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值
3. 任意节点左右子树也为二叉查找树
4. 没有键值相等的节点
*/
#ifndef BiSearchTree_bisearchtree_h
#define BiSearchTree_bisearchtree_h
typedef int ElemType;
typedef struct BiSearchTree{
ElemType key;
struct BiSearchTree *lChild;
struct BiSearchTree *rChild;
}BiSearchTree;
/**
* 创建二叉查找树
*
* @return 指向一颗空树的指针
*/
BiSearchTree *bisearch_tree_new();
/**
* 插入节点
*
* @param tree tree
* @param node 节点值
*/
BiSearchTree *bisearch_tree_insert(BiSearchTree *tree,ElemType node);
/**
* 查找节点
*
* @param tree tree
* @param node 节点值
* @return -1失败 0 成功
*/
int bisearch_tree_search(BiSearchTree *tree,ElemType node);
//删除节点
int bisearch_tree_delete(BiSearchTree **tree,ElemType node);
// 遍历节点
void bisearch_tree_inorder_traversal(BiSearchTree *tree);
#endif
@@ -0,0 +1,149 @@
//
// main.c
// BiSearchTree
//
// Created by nonstriater on 14-2-22.
//
//
#include <stdio.h>
#include "bisearchtree.h"
int main(){
printf("begining...\n");
BiSearchTree *searchTree;
searchTree = bisearch_tree_new();
searchTree = bisearch_tree_insert(searchTree,19);// 第一次insert
bisearch_tree_insert(searchTree,3);
bisearch_tree_inorder_traversal(searchTree);
bisearch_tree_insert(searchTree,122);
bisearch_tree_insert(searchTree,55);
bisearch_tree_insert(searchTree,65);
bisearch_tree_insert(searchTree,180);
printf("inorder traversal\n");
bisearch_tree_inorder_traversal(searchTree);
//=======search ============================================
printf("find node...\n");
int find = 55;
if(bisearch_tree_search(searchTree,find)<0){
printf("node %d not exits\n",find);
}else{
printf("node %d exits\n",find);
}
find = 33;
if(bisearch_tree_search(searchTree,find)<0){
printf("node %d not exits\n",find);
}else{
printf("node %d exits\n",find);
}
//====== delete,会影响到serachTree值,所以应该传递serachTree的指针 ======================================
printf("delete node..\n");
find = 4;
if(bisearch_tree_delete(&searchTree,find)<0){
printf("delete node %d failure\n",find);
}else{
printf("delete node %d success\n",find);
bisearch_tree_inorder_traversal(searchTree);
bisearch_tree_insert(searchTree,find);
}
find = 65;//叶子
if(bisearch_tree_delete(&searchTree,find)<0){
printf("delete node failure\n");
}else{
printf("delete node %d success\n",find);
bisearch_tree_inorder_traversal(searchTree);
bisearch_tree_insert(searchTree,find);
}
find = 55;//单分支
if(bisearch_tree_delete(&searchTree,find)<0){
printf("delete node failure\n");
}else{
printf("delete node %d success\n ",find);
bisearch_tree_inorder_traversal(searchTree);
bisearch_tree_insert(searchTree,find);
}
find = 122;//双分支
if(bisearch_tree_delete(&searchTree,find)<0){
printf("delete node failure\n");
}else{
printf("delete node %d success\n",find);
bisearch_tree_inorder_traversal(searchTree);
bisearch_tree_insert(searchTree,find);
}
return 0;
}
/*
测试结果:
begining...
insert first node 19
insert node 3 to left of 19
3
19
insert node 122 to right of 19
insert node 55 to left of 122
insert node 65 to right of 55
insert node 180 to right of 122
inorder traversal
3
19
55
65
122
180
find node...
node 55 exits
node 33 not exits
delete node..
树为空,或想要删除的节点不存在
delete node 4 failure
delete node 65 success
3
19
55
122
180
insert node 65 to right of 55
delete node 55 success
3
19
65
122
180
insert node 55 to left of 65
delete node 122 success
3
19
55
65
180
insert node 122 to left of 180
*/
+334
View File
@@ -0,0 +1,334 @@
/*
自平衡二叉查找树(AVL tree): 首先也是二次查找树,其实 任何2个子树的高度差不大于1
在删除,插入的过程中不断调整子树的高度,保证平均和最坏情况下都是O(logn)
Adelson-Velskii 和 Landis 1962年 创造。
1) 平衡因子 -1 0 1 节点是正常的。平衡因子 = 左子树高度-右字数高度
2) 除此之外的节点是不平衡的,需要重新平衡这个树。也就是AVL旋转
插入节点:
a: 左旋转(RR型:节点x的右孩子的右孩子上插入新元素)平衡因子由-1 -》-2 时,需要绕节点x左旋转
b:右旋转(LL型:节点X的左孩子的左孩子上插入新元素) 平衡因子有1-》2,右旋转
c: 先左后右旋转:(LR型:树中节点X的左孩子的右孩子上插入新元素) 平衡因子从1变成2后,就需要 先绕X的左子节点Y左旋转,接着再绕X右旋转
d: 先右后左旋转:(RL型:节点X的右孩子的左孩子上插入新元素)
6 6 6 6
/ \ / \
5 7 3 9
/ \ \ /
3 8 5 7
(LL型) (RR) (LR) (RL)
删除节点:
可以看到,为了保证高度平衡,插入和删除操作代价增加
记于2014-2-28 by @nonstriater
*/
#include <stdio.h>
#include <stdlib.h>
#define LH 1
#define EH 0
#define RH -1
typedef int KEY_TYPE;
typedef struct node{
KEY_TYPE key;
int height; // 平衡因子
struct node *lChild;
struct node *rChild;
}AVLTree;
//
void avltree_rr_rotate(AVLTree **tree){
AVLTree *right= *tree->rChild;
right->lChild = *tree;
*tree->rChild = right->lChild;
}
//
void avltree_ll_rotate(AVLTree **tree){
AVLTree *left = *tree->lChild;
left->rChild = *tree;
*tree->lChild = left->rChild
}
//
void avltree_lr_rotate(AVLTree **tree){
}
void avltree_rl_rotate(AVLTree **tree){
}
void avltree_left_balance(AVLTree **root)
{
AVLTree *left,*lr;
left=(*root)->lChild;
switch(left->height)
{
//检查T的左子树平衡度,并作相应的平衡处理
case LH://新节点插入在T的左孩子的左子树上,做单右旋处理
(*root)->height=left->height=EH;
avltree_ll_rotate(root);
break;
case RH://新插入节点在T的左孩子的右子树上,做双旋处理
lr=left->rChild;
switch(lr->height)
{
case LH:
(*root)->height=RH;
left->height=EH;
break;
case EH:
(*root)->height=left->height=EH;
break;
case RH:
(*root)->height=EH;
left->height=LH;
break;
}
lr->height=EH;
L_Rotate(&(*T)->lChild);
R_Rotate(T);
}
}
void avltree_right_balance(AVLTree **root)
{
AVLTree right,rl;
right=(*root)->rChild;
switch(right->height)
{
case RH://新节点插在T的右孩子的右子树上,要做单左旋处理
(*root)->height=right->height=EH;
avltree_rr_rotate(root);
break;
case LH://新节点插在T的右孩子的左子树上,要做双旋处理
rl=right->lChild;
switch(rl->height)
{
case LH:
(*root)->height=EH;
right->height=RH;
break;
case EH:
(*root)->height=right->height=EH;
break;
case RH:
(*root)->height=LH;
right->height=EH;
break;
}
rl->height=EH;
R_Rotate(&(*root)->rChild);
L_Rotate(T);
}
}
// 插入一个节点key
/*
算法描述:
1)如果root为null,则插入一个数据元素为kx 的新结点作为T 的根结点
2)如果key和root->key相等,不插入
3)如果key<root->key, 插在root左子树上:
*/
AVLTree* avltree_insert(AVLTree* root, KEY_TYPE key){
if (NULL==root)
{
root = (AVLTree *)malloc(sizeof(struct AVLTree));
if (!root)
{
printf("内存分配失败,插入节点失败\n");
return root;
}
root.key = key;
root.lChild = NULL;
root.rChild = NULL;
root.height = 0;
}
else if (key=root->key)
{
printf("节点 %d 已存在 \n", key);
}
else if (key<root->key)//插入左
{
root->lChild = avltree_insert(root->lChild,key);
if (root->lChild->height-root->rChild->height == 2)//不平衡
{
if (key<root->lChild->key)// LL型
{
root = avltree_ll_rotate(tree);
}
if (key>root->lChild->key)//LR
{
root = avltree_lr_rotate(tree);
}
}
}
else if (key>root->key){
root->rChild = avltree_insert(root->rChild,key);
if (key<root->rChild->key)// RL
{
root = avltree_rl_rotate(tree);
}
if (key>root->rChild->key)// RR
{
root = avltree_rr_rotate(tree);
}
}
return root;
}
// 删除一个节点
AVLTree* avltree_delete(AVLTree* root, KEY_TYPE key){
}
// 判断是否为AVL树
int avltree_isbalance(AVLTree *root){
}
// 查找
AVLTree* avltree_search(AVLTree *root,KEY_TYPE key){
if (root==NULL)
{
return NULL;
}
if (root->key == key)
{
return root;
}
else if (root->key>key)
{
return avltree_search(root->lChild,key);
}
else{
return avltree_search(root->rChild,key);
}
}
// 中序遍历
void avltree_inorder_traversal(AVLTree* root){
if (root)
{
avltree_inorder_traversal(root->lChild);
printf(节点值=%d,左右子树的高度差=%d\n,root->key,root->height);
avltree_inorder_traversal(root->rChild);
}
}
// test
int main(){
AVLTree *avlTree=NULL;
printf("插入节点,创建一个AVL树...\n");
int values[] = {11,7,222,456,23,8,65,124,88,2,54};
for (int i = 0; i < sizeof(values)/sizeof(int); ++i)
{
printf("插入节点 %d\n", values[i]);
avlTree = avltree_insert(avlTree,values[i]);
avltree_inorder_traversal(avltree);
printf("\n\n");
}
printf("中序遍历结果:\n");
avltree_inorder_traversal(avlTree);
printf("删除一个存在的节点 %d\n", values[1]);
avlTree=avltree_delete(avlTree,values[1]);
printf("中序遍历结果:\n");
avltree_inorder_traversal(avlTree);
printf("删除一个不存在的节点 %d\n",111 );
avltree_delete(avlTree,111);
printf("中序遍历结果:\n");
avltree_inorder_traversal(avlTree);
printf("查找一个存在的节点 %d\n", values[3]);
avltree_search(avltree,values[3]);
printf("查找一个不存在的节点 %d\n",51);
avltree_search(avltree,51);
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
### AVL 实现过程中的问题
### AVL 实际使用案例
* LLVM 的 ImmutableSet,其底层的实现选择为 AVL 树
* 《一种基于二叉平衡树的P2P覆盖网络的研究》论文
View File
+155
View File
@@ -0,0 +1,155 @@
/*
### trie基本
字典树,英文名Trie树,Trie一词来自retrieve,发音为/tri:/ “tree”,也有人读为/traɪ/ “try”,
又称单词查找树,Trie树,是一种树形结构。
trie,又称为前缀树或字典树,是一种有序树,用于保存关联数组。
1) 一个节点的所有子孙都有相同的前缀,即为这个节点对应的字符串
2) 根节点对应空字符串,即不包含字符
3) 不是所有节点都有对应值,只有叶子节点和部分内部节点所对应的键才有相关的值
4) 键不是保存在节点中,而是由节点在树中的位置决定。
### trie应用
典型应用是:
用于统计,排序和保存大量的字符串(但不仅限于字符串),
所以经常被搜索引擎系统用于文本词频统计。
排序大量字符串
用于索引结构
敏感词过滤
它的优点是:
节省空间:利用字符串的公共前缀来节约存储空间
最大限度地减少无谓的字符串比较
查询效率比hash table 更优??
### trie 存储结构设计
最简单实现 ---- 26个字母表 a-z 。
子树用数组存储,浪费空间;
如果用链表存储,查询时需要遍历链表,降低查询效率
DATrie libdatrie
double-array trie
参考作者的这篇论文 http://linux.thai.net/~thep/datrie/datrie.html
trie树的增加和删除都比较麻烦,但索引本身就是写少读多,是否考虑添加删除的复杂度上升,依靠具体场景决定
### trie 问题
中文支持
*/
#include <stdio.h>
#include <stdlib.h>
#define ALPHABET_SIZE 26
typedef struct node
{
int count; //count 如果为0,则代表非黄色点,count>0代表是黄色点,同时表示出现次数;
char value; //字符
struct node * subtries[ALPHABET_SIZE]; //子树
} *Trie;
//插入字符串,建立字典树. 返回值 < 0 表示插入失败
int trie_insert(Trie *trie,char *c){
if (NULL==trie)
{
*trie = (Trie)malloc(sizeof(struct node));
if (NULL==trie)
{
printf("malloc failure...\n");
}
}
char *p = c;
while(*p!='\0'){
if (*p)
{
/* code */
}
p++;
}
}
// 字符串查找,返回值<0表示没有查找到
int trie_query(Trie *trie,char *c){
}
//
void trie_remove(){}
int main(){
Trie trie = NULL;
char *dict[10]={"int","integer","float","char","nonstriater","weibo"};
for (int i = 0; i < sizeof(dict)/sizeof(int); ++i)
{
if (trie_insert(&trie,dict[i])<0)
{
printf("%s 插入失败\n", dict[i]);
}
}
// 查询cha
printf("查询cha \n");
// 查询char
printf("查询char \n");
// 查询hello
printf("查询hello \n");
return 0;
}
View File
+111
View File
@@ -0,0 +1,111 @@
#include "stdio.h"
typedef char * Key;
typedef int Value;
typedef unsigned int Hash;
typedef struct Entry
{
struct Entry *next;
Hash hash; // key 对应的hash值
Key key;
Value value;
}Entry;
// 拉链法实现,也就是链表的数组.也是 数组和链表优势的结合
typedef struct HashTable{
Entry **head;
unsigned int size;
unsigned int usage;
}HashTable;
HashTable *create(unsigned int);
HashTable *put(HashTable *,Key,Value);
HashTable *putInHeads(HashTable *,Key,Value); // 数组
HashTable *putInLists(HashTable *,Key,Value); // 链表
Value find(HashTable *,Key );
HashTable*delete(HashTable *,Key );
void print(HashTable *);
Hash hashCode(Key); // 哈希函数
HashTable *create(unsigned int size){
HashTable *hashTable = malloc(sizeof(HashTable));
if(hashTable==NULL){
printf("malloc error\n");
return NULL;
}
hashTable->size=size;
hashTable->usage = 0;
hashTable->head = calloc(size,sizeof(Entry *));
return hashTable;
}
HashTable *put(HashTable *hashTable,Key key,Value value){
if (key==NULL)
{
return hashTable;
}
}
Value find(HashTable *hashTable,Key key){
}
void print(HashTable *hashTable){
Entry *entry;
for (int i = 0; i < hashTable->size; ++i)
{
entry = hashTable->head[i];
while(entry!=NULL){
printf("%s=%d\n",entry->key,entry->value);
entry=entry->next;
}
}
}
// ?????
Hash hashCode(char *key){
int offset = 5;
Hash hashCode = 0;
while(*key){
hashCode = (hashCode << offset) + (*key++);
}
return hashCode;
}
int main(){
return 0;
}
View File
+186
View File
@@ -0,0 +1,186 @@
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"
#define DEFAULT_MAP_SIZE 1<<5
#define MAX_MAP_SIZE 1<<30
#define CAPACITY_FACTOR 0.6f
#define MAX_KEY_LENGTH 1<<3
typedef unsigned int Hash;
typedef char* Key;
typedef int Value;
typedef struct Entry{
struct Entry *next;
Hash hash;
Value value;
Key key;
}Entry;
typedef struct HashMap{
Entry** heads;
Value nul;
unsigned int size;
unsigned int usage;
unsigned int realCapacity; //size * CAPACITY_FACTOR
unsigned int sizeForIndex; //size - 1
}HashMap;
HashMap* create(unsigned int);
HashMap* transfer(HashMap*, HashMap*);
Hash hash(char[]);
HashMap* put(HashMap*, Key, Value);
HashMap* putInHeads(HashMap*, int, Hash, Key, Value);
HashMap* putInList(HashMap*, int, Hash, Key, Value);
HashMap* resize(HashMap*);
int find(char[]);
void print(HashMap*);
HashMap* create(unsigned int size){
HashMap* hashMap = malloc(sizeof(HashMap));
int i;
hashMap->size = size;
hashMap->sizeForIndex = size - 1;
hashMap->realCapacity = (unsigned int)(size * CAPACITY_FACTOR);
hashMap->usage = 0;
hashMap->heads = calloc(size, sizeof(Entry*));
hashMap->nul = 0;
return hashMap;
}
HashMap* transfer(HashMap* newHashMap, HashMap* oldHashMap){
unsigned int oldIndex, newIndex;
Entry *oldEntry, *oldNextEntry;
for (oldIndex = 0; oldIndex < oldHashMap->size; oldIndex++){
if (oldHashMap->heads[oldIndex] != NULL){
oldEntry = oldHashMap->heads[oldIndex];
do{
newIndex = (oldEntry->hash) & (newHashMap->sizeForIndex);
oldNextEntry = oldEntry->next;
oldEntry->next = newHashMap->heads[newIndex];
newHashMap->heads[newIndex] = oldEntry;
oldEntry = oldNextEntry;
} while (oldEntry != NULL);
}
}
newHashMap->usage = oldHashMap->usage;
newHashMap->nul = oldHashMap->nul;
free(oldHashMap->heads);
oldHashMap->heads = NULL;
free(oldHashMap);
oldHashMap = NULL;
return newHashMap;
}
Hash hashCode(char* key){
int offset = 5;
Hash hashCode = 0;
while (*key) hashCode = (hashCode << offset) + (*key++);
return hashCode;
}
HashMap* put(HashMap* hashMap, Key key, Value value){
if (key == NULL){
hashMap->nul = value;
return hashMap;
}
Hash hash = hashCode(key);
int index = hash & (hashMap->sizeForIndex);
if (hashMap->heads[index] == NULL){
return putInHeads(hashMap, index, hash, key, value);
} else {
return putInList(hashMap, index, hash, key, value);
}
}
HashMap* putInHeads(HashMap* hashMap, int index, Hash hash, Key key, Value value){
Entry* newHead = malloc(sizeof(Entry));
newHead->hash = hash;
newHead->key = _strdup(key);
newHead->value = value;
newHead->next = NULL;
hashMap->heads[index] = newHead;
(hashMap->usage)++;
if (hashMap->usage > hashMap->realCapacity) return resize(hashMap);
return hashMap;
}
HashMap* putInList(HashMap* hashMap, int index, Hash hash, Key key, Value value){
Entry* lastEntry = hashMap->heads[index];
while (lastEntry != NULL){
if (lastEntry->hash == hash){
lastEntry->value += value;
return hashMap;
}
else lastEntry = lastEntry->next;
}
lastEntry = malloc(sizeof(Entry));
lastEntry->hash = hash;
lastEntry->key = _strdup(key);
lastEntry->value = value;
lastEntry->next = hashMap->heads[index];
hashMap->heads[index] = lastEntry;
(hashMap->usage)++;
if (hashMap->usage > hashMap->realCapacity) return resize(hashMap);
return hashMap;
}
HashMap* resize(HashMap* hashMap){
HashMap* newHashMap;
if ((hashMap->size << 1) <= MAX_MAP_SIZE) {
newHashMap = create((hashMap->size << 1));
newHashMap = transfer(newHashMap, hashMap);
return newHashMap;
}
return hashMap;
}
Value get(HashMap* hashMap, Key key){
Hash hash = hashCode(key);
int index = hash & (hashMap->sizeForIndex);
Entry* entry = hashMap->heads[index];
while (entry != NULL){
if (entry->hash == hash) return entry->value;
entry = entry->next;
}
return NULL;
}
void print(HashMap* hashMap){
unsigned int i;
Entry* entry;
for (i = 0; i < hashMap->size; i++){
entry = hashMap->heads[i];
while (entry != NULL){
printf("%s: %d\n", entry->key, entry->value);
entry = entry->next;
}
}
}
int main(){
clock_t start, end;
HashMap* hashMap = create(DEFAULT_MAP_SIZE);
FILE* fp;
char filePath[0xFF];
char line[5];
char key[MAX_KEY_LENGTH];
printf("input file path: ");
scanf_s("%s", filePath, 0xFF);
if (fopen_s(&fp, filePath, "r") == 0){
start = clock();
while (fgets(line, 5, fp) != NULL){
line[3] = '\0';
hashMap = put(hashMap, line, 1);
}
end = clock();
printf("\n%fs", (float)(end - start)/1000);
fclose(fp);
print(hashMap);
} else {
printf("can't open file");
}
return 0;
}
View File
View File
View File
View File
View File
View File
+188
View File
@@ -0,0 +1,188 @@
1.把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ /
6 14
/ / / /
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
首先我们定义的二元查找树 节点的数据结构如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
4.在二元树中找出和为某一值的所有路径
题目:输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如 输入整数22和如下二元树
10
/ /
5 12
/ /
4 7
则打印出两条路径:10, 12和10, 5, 7。
二元树节点的数据结构定义为:
struct BinaryTreeNode // a node in the binary tree
{
int m_nValue; // value of node
BinaryTreeNode *m_pLeft; // left child of node
BinaryTreeNode *m_pRight; // right child of node
};
判断整数序列是不是二元查找树的后序遍历结果
题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。
如果是返回true,否则返回false。
例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:
8
/ /
6 10
/ / / /
5 7 9 11
因此返回true。
如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。
13、设计一个算法,找出二叉树上任意两个结点的最近共同父结点。
复杂度如果是O(n2)则不得分。
14、一棵排序二叉树,令 f=(最大值+最小值)/2,
设计一个算法,找出距离f值最近、大于f值的结点。
复杂度如果是O(n2)则不得分。
第15题:
题目:输入一颗二元查找树,将该树转换为它的镜像,
即在转换后的二元查找树中,左子树的结点都大于右子树的结点。
用递归和循环两种方法完成树的镜像转换。
例如输入:
8
/ /
6 10
// //
5 7 9 11
输出:
8
/ /
10 6
// //
11 9 7 5
定义二元查找树的结点为:
struct BSTreeNode // a node in the binary search tree (BST)
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
第16题:
题目(微软):
输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。
例如输入
8
/ /
6 10
/ / / /
5 7 9 11
输出8 6 10 5 7 9 11。
52.二元树的深度。
题目:输入一棵二元树的根结点,求该树的深度。
从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
例如:输入二元树:
10
/ /
6 14
/ / /
4 12 16
输出该树的深度3。
二元树的结点定义如下:
struct SBinaryTreeNode // a node of the binary tree
{
int m_nValue; // value of node
SBinaryTreeNode *m_pLeft; // left child of node
SBinaryTreeNode *m_pRight; // right child of node
};
分析:这道题本质上还是考查二元树的遍历。
对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点;如果当前节点已经是该层的最后一个节点,则将pNext指针指向NULL;给出程序实现,并分析时间复杂度和空间复杂度。
怎样编写一个程序,把一个有序整数数组放到二叉树中?
分析:本题考察二叉搜索树的建树方法,简单的递归结构。
关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。
而,学会把递归改称非递归也是一种必要的技术。
毕竟,递归会造成栈溢出,关于系统底层的程序中不到非不得以最好不要用。
但是对某些数学问题,就一定要学会用递归去解决。
43.递归和非递归俩种方法实现二叉树的前序遍历。
5、怎样编写一个程序,把一个有序整数数组放到二叉树中?
6、怎样从顶部开始逐层打印二叉树结点数据?请编程。
2、有一棵树(树上结点为字符串或者整数),请写代码将树的结构和数据写到一个文件中,并能通过读取该文件恢复树结构 。
75.二叉树两个结点的最低共同父结点
题目:二叉树的结点定义如下:
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。
分析:求数中两个结点的最低共同结点是面试中经常出现的一个问题。这个问题至少有两个变种。
+28
View File
@@ -0,0 +1,28 @@
31.华为面试题:
一类似于蜂窝的结构的图,进行搜索最短路径(要求5分钟)
求一个有向连通图的割点,割点的定义是,如果除去此节点和与其相关的边,
有向图不再连通,描述算法。
11、平面上N个点,每两个点都确定一条直线,
求出斜率最大的那条直线所通过的两个点(斜率不存在的情况不考虑)。时间效率越高越好。
2.求一个有向连通图的割点,割点的定义是,
如果除去此节点和与其相关的边,有向图不再连通,描述算法。
求一个二叉树中任意两个节点间的最大距离,
两个节点的距离的定义是 这两个节点间边的个数,
比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。
+43
View File
@@ -0,0 +1,43 @@
18、如果用一个循环数组q[0..m-1]表示队列时,该队列只有一个队列头指针front,不设队列尾指针rear,求这个队列中从队列头到队列尾的元素个数(包含队列头、队列尾)(华赛面试题、腾讯笔试题)。
2.设计包含min函数的栈。
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。
要求函数min、push以及pop的时间复杂度都是O(1)。
29.栈的push、pop序列
题目:输入两个整数序列。其中一个序列表示栈的push顺序,
判断另一个序列有没有可能是对应的pop顺序。
为了简单起见,我们假设push序列的任意两个整数都是不相等的。
比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。
因为可以有如下的push和pop序列:
push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,
这样得到的pop序列就是4、5、3、2、1。
但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。
实现一个队列。
队列的应用场景为:
一个生产者线程将int类型的数入列,一个消费者线程将int类型的数出列
66.颠倒栈。
题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。
颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。
+273
View File
@@ -0,0 +1,273 @@
翻转句子中单词的顺序。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。
为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
1.不开辟用于交换数据的临时空间,如何完成字符串的逆序
(在技术一轮面试中,有些面试官会这样问)
题目:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。
分析:这道题是2006年google的一道笔试题。
题目:匹配兄弟字符串
如果两个字符串的字符一样,但是顺序不一样,被认为是兄弟字符串,问如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。思路:判断各自素数乘积是否相等。更多方法请参见:http://blog.csdn.net/v_JULY_v/article/details/6347454。
给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。
给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
1. 设计相应的数据结构和算法,尽量高效的统计一片英文文章(总单词数目)里出现的所有英文单词,按照在文章中首次出现的顺序打印输出该单词和它的出现次数。
用一种算法使通用字符串相匹配。
找到一个子字符串。优化速度。优化空间。
比较两个字符串,用O(n)时间和恒量空间。
54、字符串原地压缩
题目描述:“eeeeeaaaff" 压缩为 "e5a3f2",请编程实现。
3.给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
1.给出一个函数来输出一个字符串的所有排列。
55、字符串匹配实现
请以俩种方法,回溯与不回溯算法实现。
56、一个含n个元素的整数数组至少存在一个重复数,
请编程实现,在O(n)时间内找出其中任意一个重复数。
39、对称子字符串的最大长度
题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。
比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的加
强版。
35、编程实现:找出两个字符串中最大公共子字符串,如"abccade","dgcadde"的最大子串为"cad"
3、给出一个函数来输出一个字符串的所有排列。
2、写一个函数,检查字符是否是整数,如果是,返回其整数值。
(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?)
17、有一个由大小写组成的字符串,现在需要对他进行修改,将其中的所有小写字母排在答谢字母的前面(大写或小写字母之间不要求保持原来次序),如有可能尽量选择时间和空间效率高的算法 c语言函数原型void proc(char *str) 也可以采用你自己熟悉的语言
第20题:
题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。
例如输入字符串"345",则输出整数345。
第25题:
写一个函数,它的原形是int continumax(char *outputstr,char *intputstr)
功能:
在字符串中找出连续最长的数字串,并把这个串的长度返回,
并把这个最长数字串付给其中一个函数参数outputstr所指内存。
例如:"abcd12345ed125ss123456789"的首地址传给intputstr后,函数将返回9,
outputstr所指的值为123456789
26.左旋转字符串
题目:
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。
如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。
要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
33.
实现一个挺高级的字符匹配算法:
给一串很长字符串,要求找到符合要求的字符串,例如目的串:123
1******3***2 ,12*****3这些都要找出来
其实就是类似一些和谐系统。。。。。
37.
有n个长为m+1的字符串,
如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以联接,
问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。
3)设计一个系统处理词语搭配问题,比如说 中国 和人民可以搭配,
则中国人民 人民中国都有效。要求:
*系统每秒的查询数量可能上千次;
*词语的数量级为10W;
*每个词至多可以与1W个词搭配
当用户输入中国人民的时候,要求返回与这个搭配词组相关的信息。
1 判断一字符串是不是对称的,如:abccba
1.编写strcpy 函数
已知strcpy 函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请
编写函数 strcpy
24、用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
4.删除字符串中的数字并压缩字符串。
如字符串”abc123de4fg56”处理后变为”abcdefg”。注意空间和效率。
(下面的算法只需要一次遍历,不需要开辟新空间,时间复杂度为O(N))
5.求两个串中的第一个最长子串(神州数码以前试题)。
如"abractyeyt","dgdsaeactyey"的最大子串为"actyet"。
2.删除串中指定的字符
(做此题时,千万不要开辟新空间,否则面试官可能认为你不适合做嵌入式开发)
2.编码实现字符串转整型的函数(实现函数atoi的功能),据说是神州数码笔试题。如将字符
串 ”+123”123, ”-0123”-123, “123CS45”123, “123.45CS”123, “CS123.45”0
85.又见字符串的问题
1.给出一个函数来复制两个字符串A和B。
字符串A的后几个字节和字符串B的前几个字节重叠。
分析:记住,这种题目往往就是考你对边界的考虑情况。
2.已知一个字符串,比如asderwsde,寻找其中的一个子字符串比如sde的个数,
如果没有返回0,有的话返回子字符串的个数。
函数将字符串中的字符'*'移到串的前部分,
前面的非'*'字符后移,但不能改变非'*'字符的先后顺序,函数返回串中字符'*'的数量。
如原始串为:ab**cd**e*12,
处理后为*****abcde12,函数并返回值为5。(要求使用尽量少的时间和辅助空间)
2.求最大连续递增数字串(如“ads3sl456789DF3456ld345AA”中的“456789”)
3.实现strstr功能,即在父串中寻找子串首次出现的位置。
(笔试中常让面试者实现标准库中的一些函数)
73.对策字符串的最大长度。
题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。
比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的加强版。
70.给出一个函数来输出一个字符串的所有排列。
分析:简单的回溯就可以实现了。当然排列的产生也有很多种算法,去看看组合数学,
还有逆序生成排列和一些不需要递归生成排列的方法。
印象中Knuth的<TAOCP>第一卷里面深入讲了排列的生成。
这些算法的理解需要一定的数学功底,
也需要一定的灵感,有兴趣最好看看。
56.最长公共字串。
题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,
则字符串一称之为字符串二的子串。
注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子串,
则输出它们的长度4,并打印任意一个子串。
分析:求最长公共子串(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,
因此一些重视算法的公司像MicroStrategy都把它当作面试题。
53.字符串的排列。
题目:输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
分析:这是一道很好的考查对递归理解的编程题,
因此在过去一年中频繁出现在各大公司的面试、笔试题中。
63.在字符串中删除特定的字符。
题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。
例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。
分析:这是一道微软面试题。在微软的常见面试题中,与字符串相关的题目占了很大的一部分,
因为写程序操作字符串能很好的反映我们的编程基本功。
+109
View File
@@ -0,0 +1,109 @@
写一个函数,检查字符是否是整数,如果是,返回其整数值。
(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?)
第12题
题目:求1+2+…+n,
要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
28.整数的二进制表示中1的个数
题目:输入一个整数,求该整数的二进制表达中有多少个1。
例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
分析:
这是一道很基本的考查位运算的面试题。
包括微软在内的很多公司都曾采用过这道题。
58、整数的素数和分解问题
歌德巴赫猜想说任何一个不小于6的偶数都可以分解为两个奇素数之和。
对此问题扩展,如果一个整数能够表示成两个或多个素数之和,则得到一个素数和分解式。
对于一个给定的整数,输出所有这种素数和分解式。
注意,对于同构的分解只输出一次(比如5只有一个分解2 + 3,而3 + 2是2 + 3的同构分解式
)。
例如,对于整数8,可以作为如下三种分解:
(1) 8 = 2 + 2 + 2 + 2
(2) 8 = 2 + 3 + 3
(3) 8 = 3 + 5
给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含),
指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数。
比如,A=[1,0] K=21 那么输出结构应该为100。
请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句
两个数相乘,小数点后位数没有限制,请写一个高精度算法
编程实现:把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列
如何求根号2的值,并且按照我的需要列出指定小数位,比如根号2是1.141 我要列出1位小数就是1.1 2位就是1.14, 1000位就是1.141...... 等。。
求两个或N个数的最大公约数和最小公倍数。
40、用1、2、2、3、4、5这六个数字,写一个main函数,打印出所有不同的排列,
如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连.
21、1024! 末尾有多少个0?
30.在从1到n的正数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
分析:这是一道广为流传的google面试题。
19、判断一个自然数是否是某个数的平方
说明:当然不能使用开方运算。
20、给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数。
9、编程实现两个正整数的除法
编程实现两个正整数的除法,当然不能用除法操作符。
// return x/y.
int div(const int x, const int y)
{}
请使用代码计算1234567891011121314151617181920*2019181716151413121110987654321 。
+517
View File
@@ -0,0 +1,517 @@
3.求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
第21题
编程求解:
输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来.
5.查找最小的k个元素
题目:输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
第14题:
题目:输入一个已经按升序排序过的数组和一个数字,
在数组中查找两个数,使得它们的和正好是输入的那个数字。
要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
1.给出两个集合A和B,其中集合A={name},
集合B={age、sex、scholarship、address、...},
要求:
问题1、根据集合A中的name查询出集合B中对应的属性信息;
问题2、根据集合B中的属性信息(单个属性,如age<20等),查询出集合A中对应的name。
题目:
输入a1,a2,...,an,b1,b2,...,bn,
在O(n)的时间,O(1)的空间将这个序列顺序改为a1,b1,a2,b2,a3,b3,...,an,bn,
且不需要移动,通过交换完成,只需一个交换空间。
例如,N=9时,第2步执行后,实际上中间位置的两边对称的4个元素基本配对,
只需交换中间的两个元素即可,如下表所示。颜色表示每次要交换的元素,左边向右交换,右边向左交换。
交换过程如下表所示
交换x1,x3;交换x2,x4;再交换中间的x1,x4;交换y1,y2。
第6题
给你10分钟时间,根据上排给出十个数,在其下排填出对应的十个数
要求下排每个数都是先前上排那十个数在下排出现的次数。
上排的十个数如下:
【0,1,2,3,4,5,6,7,8,9】
初看此题,貌似很难,10分钟过去了,可能有的人,题目都还没看懂。
举一个例子,
数值: 0,1,2,3,4,5,6,7,8,9
分配: 6,2,1,0,0,0,1,0,0,0
0在下排出现了6次,1在下排出现了2次,
2在下排出现了1次,3在下排出现了0次....
以此类推..
昨天,花了一个下午,用c++实现了此题。(*^__^*)
第18题:
题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,
每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。
当一个数字删除后,从被删除数字的下一个继续删除第m个数字。
求出在这个圆圈中剩下的最后一个数字。
如 0,1,2,3,4,5 删除第2个数字 (n=6,m=2)
第一次删除:1
第二次删除: 3
第三次删除:5
第四次删除:2
因此,左后的一个数字就是 4
第23题:
用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交。"
3D坐标系 原点(0.0,0.0,0.0)
圆形:
半径r = 3.0
圆心o = (*.*, 0.0, *.*)
正方形:
4个角坐标;
1:(*.*, 0.0, *.*)
2:(*.*, 0.0, *.*)
3:(*.*, 0.0, *.*)
4:(*.*, 0.0, *.*)
32.
有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
例如:
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];
2.一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值
比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1;
{3,6}{2,4,3} m=2
{3,3}{2,4}{6} m=3 所以m的最大值为3
36.
n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系,
存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j的队伍中更强的一支。
所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中,
比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是 4对3, 5对8。.......
胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排,
下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4对5,直至出现第一名
编程实现,给出二维数组w,一维数组order 和 用于输出比赛名次的数组result[n],求出result。
1.一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],
其左边的数都小于等于它,右边的数都大于等于它。
能否只用一个额外数组和少量其它空间实现。
一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序 比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时间复杂度O(N),空间O(1) 。(此题一直没看到令我满意的答案,一般达不到题目所要求的:时间复杂度O(N),空间O(1),且保证原来正负数之间的相对位置不变)。
updated:设置一个起始点j, 一个翻转点k,一个终止点L
从右侧起
起始点在第一个出现的负数, 翻转点在起始点后第一个出现的正数,终止点在翻转点后出现的第一个负数(或结束)
如果无翻转点, 则不操作
如果有翻转点, 则待终止点出现后, 做翻转, 即ab => ba 这样的操作
翻转后, 负数串一定在左侧, 然后从负数串的右侧开始记录起始点, 继续往下找下一个翻转点
例子中的就是
1, 7, -5, 9, -12, 15
第一次翻转: 1, 7, -5, -12,9, 15 => 1, -12, -5, 7, 9, 15
第二次翻转: -5, -12, 1, 7, 9, 15
N维翻转空间占用为O(1)复杂度是2N;在有一个负数的情况下, 复杂度最大是2N, ;在有i个负数的情况下, 复杂度最大是2N+2i, 但是不会超过2N+N实际的复杂度在O(3N)以内
但从最终时间复杂度分析,此方法是否真能达到O(N)的时间复杂度,还待后续考证。感谢John_Lv,MikovChain。2012.02.25。
1, 7, -5, -6, 9, -12, 15(后续:此种情况未能处理)
1 7 -5 -6 -12 9 15
1 -12 -5 -6 7 9 15
-6 -12 -5 1 7 9 15
更多请参考此文,程序员编程艺术第二十七章:重新排列数组(不改变相对顺序&时间O(N)&空间O(1),半年未被KO)http://blog.csdn.net/v_july_v/article/details/7329314。
给定一个实数数组,按序排列(从小到大),从数组从找出若干个数,使得这若干个数的和与M最为接近,描述一个算法,并给出算法的复杂度。
1. 有无序的实数列V[N],要求求里面大小相邻的实数的差的最大值,关键是要求线性空间和线性时间
3. 有一个函数int getNum(),每运行一次可以从一个数组V[N]里面取出一个数,N未知,当数取完的时候,函数返回NULL。现在要求写一个函数int get(),这个函数运行一次可以从V[N]里随机取出一个数,而这个数必须是符合1/N平均分布的,也就是说V[N]里面任意一个数都有1/N的机会被取出,要求空间复杂度为O(1)
21、搜狗笔试题:一个长度为n的数组a[0],a[1],...,a[n-1]。现在更新数组的名个元素,即a[0]变为a[1]到a[n-1]的积,a[1]变为a[0]和a[2]到a[n-1]的积,...,a[n-1]为a[0]到a[n-2]的积(就是除掉当前元素,其他所有元素的积)。程序要求:具有线性复杂度,且不能使用除法运算符。
思路:left[i]标示着a[i]之前的乘积,right[i]标示着a[i]之后的乘积,但不申请空间,那么a[i]=left[i]*right[i] 。不过,left的计算从左往右扫的时候得出,right是从右往左扫得出。因为就是当中某个元素a[i]的左边所有元素与右边所有元素的乘积,就这么简单。所以计算a[i]=left[i]*right[i]时,直接出结果。
22、后2012年4月6日的腾讯暑期实习生招聘笔试中,出了一道与上述21题类似的题,原题大致如下:
两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i];
要求:
1.不准用除法运算
2.除了循环计数值,a[N],b[N]外,不准再用其他任何变量(包括局部变量,全局变量等)
3.满足时间复杂度O(n),空间复杂度O(1)。
说白了,你要我求b=a[0]*a*...a[i-1]*a*a[i+1]..*a[N-1]/a ,就是求:a[0]*a[1]*...a[i-1]*a[i+1]..*a[N-1]。只是我把a[i]左边部分标示为left[i],b[i]右边部分标示为right[i],而实际上完全不申请left[i],与right[i]变量,之所以那样标示,无非就是为了说明:除掉当前元素a[i],其他所有元素(a[i]左边部分,和a[i]右边部分)的积。读者你明白了么?
下面是此TX笔试题的两段参考代码,如下:
//ncicc
b[0] = 1;
for (int i = 1; i < N; i++)
{
b[0] *= a[i-1];
b[i] = b[0];
}
b[0] = 1;
for (i = N-2; i > 0; i--)
{
b[0] *= a[i+1];
b[i] *= b[0];
}
b[0] *= a[1];
from wasd6081058上面第二段代码最后一行的意义是:我们看第二个循环,从N-2到 1;再看for循环中b[0]的赋值,从N-1到2,而根据题目要求b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i],b[0]应等于a[1]*a[2]* ....a[N-1],所以这里手动添加a[1]。
此外,也有朋友在微博上:http://weibo.com/1761944724/ydwuMt9bH 给出了另外一种解法,如下图所示:
94.微软笔试题
求随机数构成的数组中找到长度大于=3的最长的等差数列9 d- x' W) w9 ?" o3 b0 R
输出等差数列由小到大:
如果没有符合条件的就输出
格式:
输入[1,3,0,5,-1,6]
输出[-1,1,3,5]
要求时间复杂度,空间复杂度尽量小
93.在一个int数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。
直观想法是用两个数组a、b。a[i]、b[i]分别保存从前到i的最大的数和从后到i的最小的数,
一个解答:这需要两次遍历,然后再遍历一次原数组,
将所有data[i]>=a[i-1]&&data[i]<=b[i]的data[i]找出即可。
给出这个解答后,面试官有要求只能用一个辅助数组,且要求少遍历一次。
第19题:
题目:定义Fibonacci数列如下:
/ 0 n=0
f(n)= 1 n=1
/ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项。
分析:在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。
因此很多程序员对这道题的递归解法非常熟悉,但....呵呵,你知道的。。
寻址重复的元素
1.a~z包括大小写与0~9组成的N个数
用最快的方式把其中重复的元素挑出来。
74.数组中超过出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司
都曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,除了较好的编程能力之外,
还需要较快的反应和较强的逻辑思维能力。
+-1 ,计数法
题目大意如下:
一排N(最大1M)个正整数+1递增,乱序排列,第一个不是最小的,把它换成-1,
最小数为a且未知求第一个被-1替换掉的数原来的值,并分析算法复杂度。
4.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。
在一维坐标轴上有n个区间段,求重合区间最长的两个区间段。
16、正整数序列Q中的每个元素都至少能被正整数a和b中的一个整除,现给定a和b,需要计算出Q中的前几项,例如,当a=3,b=5,N=6时,序列为3,5,6,9,10,12
(1)、设计一个函数void generate(int a,int b,int N ,int * Q)计算Q的前几项
(2)、设计测试数据来验证函数程序在各种输入下的正确性。
57、求最大重叠区间大小
题目描述:请编写程序,找出下面“输入数据及格式”中所描述的输入数据文件中最大重叠区间的大小。
对一个正整数 n ,如果n在数据文件中某行的两个正整数(假设为A和B)之间,即A<=n<=B或A>=n>=B ,则 n 属于该行;
如果 n 同时属于行i和j ,则i和j有重叠区间;重叠区间的大小是同时属于行i和j的整数个数。
例如,行(10 20)和(12 25)的重叠区间为 [12 20] ,其大小为9,行(20 10)和( 20 30 )的重叠区间大小为 1 。
给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。
要求:空间复杂度O(1),时间复杂度为O(n)。
48、找出数组中唯一的重复元素
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.
每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,
能否设计一个算法实现?
2.用递归的方法判断整数组a[N]是不是升序排列
给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。
要求:空间复杂度O(1),时间复杂度为O(n)。
35.
求一个矩阵中最大的二维矩阵(元素和最大).如:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
中最大的是:
4 5
5 3
要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码
1.对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一,
现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。
38、顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
例如:如果输入如下矩阵:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
则依次打印出数字1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10。
分析:包括Autodesk、EMC在内的多家公司在面试或者笔试里采用过这道题。
48.微软:
一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}
是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。
67.俩个闲玩娱乐。
1.扑克牌的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。
2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。
2.n个骰子的点数。
把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。
输入n,打印出S的所有可能的值出现的概率。
68.把数组排成最小的数。
题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。
例如输入数组{32, 321},则输出这两个能排成的最小数字32132。
请给出解决问题的算法,并证明该算法。
分析:这是09年6月份百度的一道面试题,
从这道题我们可以看出百度对应聘者在算法方面有很高的要求。
69.旋转数组中的最小元素。
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,
时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。
71.数值的整数次方。
题目:实现函数double Power(double base, int exponent),求base的exponent次方。
不需要考虑溢出。
分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码:
double Power(double base, int exponent)
{
double result = 1.0;
for(int i = 1; i <= exponent; ++i)
result *= base;
return result;
}
61.找出数组中两个只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。
请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
分析:这是一道很新颖的关于位运算的面试题。
64. 寻找丑数。
题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。
例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。
求按从小到大的顺序的第1500个丑数。
分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。
65.输出1到最大的N位数
题目:输入数字n,按顺序输出从1最大的n位10进制数。
比如输入3,则输出1、2、3一直到最大的3位数即999。
分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。
一个有序数列,序列中的每一个值都能够被2或者3或者5所整除,1是这个序列的第一个元素。求第1500个值是多少?
51.和为n连续正数序列。
题目:输入一个正数n,输出所有和为n连续正数序列。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。
分析:这是网易的一道面试题。
54.调整数组顺序使奇数位于偶数前面。
题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,
所有偶数位于数组的后半部分。要求时间复杂度为O(n)。
47.创新工场:
求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}
1、有一个整数数组,请求出两两之差绝对值最小的值,
记住,只要得出最小值即可,不需要求出是哪两个数。
12、一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。
请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。
注意:
- 5个数值允许是乱序的。比如: 8 7 5 0 6
- 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4
- 0可以多次出现。
- 复杂度如果是O(n2)则不得分。
15、一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。
设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。
复杂度最好是O(n),如果是O(n2)则不得分。
10、在排序数组中,找出给定数字的出现次数
比如 [1, 2, 2, 2, 3] 中2的出现次数是3次。
53、最长递增子序列
题目描述:设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列
Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。
求最大的m值。
+236
View File
@@ -0,0 +1,236 @@
此贴选一些 比较怪的题,,由于其中题目本身与算法关系不大,仅考考思维。特此并作一题。
1.有两个房间,一间房里有三盏灯,另一间房有控制着三盏灯的三个开关,这两个房间是 分割开的,从一间里不能看到另一间的情况。
现在要求受训者分别进这两房间一次,然后判断出这三盏灯分别是由哪个开关控制的。
有什么办法呢?
2.你让一些人为你工作了七天,你要用一根金条作为报酬。金条被分成七小块,每天给出一块。如果你只能将金条切割两次,你怎样分给这些工人?
第22题:
有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌,
A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,
A说不知道,B说不知道,C说不知道,然后A说知道了。
请教如何推理,A是怎么知道的。
如果用程序,又怎么实现呢?
27.跳台阶问题
题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。
求总共有多少总跳法,并分析算法的时间复杂度。
这道题最近经常出现,包括MicroStrategy等比较重视算法的公司都
曾先后选用过个这道题作为面试题或者笔试题。
1.设计一个魔方(六面)的程序。
1.用天平(只能比较,不能称重)从一堆小球中找出其中唯一一个较轻的,使用x次天平,最多可以从y个小球中找出较轻的那个,求y与x的关系式
26、有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。
木杆很细,不能同时通过一只蚂蚁。开始时,蚂蚁的头朝左还是朝右是任意的,它们只会朝前走或调头,但不会后退。
当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟可以走一厘米的距离。
编写程序,求所有蚂蚁都离开木杆的最小时间和最大时间。
20、13个球一个天平,现知道只有一个和其它的重量不同,问怎样称才能用三次就找到那个球?(http://zhidao.baidu.com/question/66024735.html
)。
1.烧一根不均匀的绳,从头烧到尾总共需要1个小时。
现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?
2.你有一桶果冻,其中有黄色、绿色、红色三种,闭上眼睛抓取同种颜色的两个。
抓取多少个就可以确定你肯定有两个同一颜色的果冻?(5秒-1分钟)
3.如果你有无穷多的水,一个3公升的提捅,一个5公升的提捅,两只提捅形状上下都不均匀,
问你如何才能准确称出4公升的水?(40秒-3分钟)
一个岔路口分别通向诚实国和说谎国。
来了两个人,已知一个是诚实国的,另一个是说谎国的。
诚实国永远说实话,说谎国永远说谎话。现在你要去说谎国,
但不知道应该走哪条路,需要问这两个人。请问应该怎么问?(20秒-2分钟)
100.第4组微软面试题,挑战思维极限
1.12个球一个天平,现知道只有一个和其它的重量不同,问怎样称才能用三次就找到那个球。
13个呢?(注意此题并未说明那个球的重量是轻是重,所以需要仔细考虑)(5分钟-1小时)
2.在9个点上画10条直线,要求每条直线上至少有三个点?(3分钟-20分钟)
3.在一天的24小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?
都分别是什么时间?你怎样算出来的?(5分钟-15分钟)
91.
1.一道著名的毒酒问题
有1000桶酒,其中1桶有毒。而一旦吃了,毒性会在1周后发作。
现在我们用小老鼠做实验,要在1周内找出那桶毒酒,问最少需要多少老鼠。
2.有趣的石头问题
有一堆1万个石头和1万个木头,对于每个石头都有1个木头和它重量一样,
把配对的石头和木头找出来。
92.
1.多人排成一个队列,我们认为从低到高是正确的序列,但是总有部分人不遵守秩序。
如果说,前面的人比后面的人高(两人身高一样认为是合适的),
那么我们就认为这两个人是一对“捣乱分子”,比如说,现在存在一个序列:
176, 178, 180, 170, 171
这些捣乱分子对为
<176, 170>, <176, 171>, <178, 170>, <178, 171>, <180, 170>, <180, 171>,
那么,现在给出一个整型序列,请找出这些捣乱分子对的个数(仅给出捣乱分子对的数目即可,不用具体的对)
要求:
输入:
为一个文件(in),文件的每一行为一个序列。序列全为数字,数字间用”,”分隔。
输出:
为一个文件(out),每行为一个数字,表示捣乱分子的对数。
详细说明自己的解题思路,说明自己实现的一些关键点。
并给出实现的代码 ,并分析时间复杂度。
限制:
输入每行的最大数字个数为100000个,数字最长为6位。程序无内存使用限制。
29、有A、B、C、D四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在17分钟内这四个人都过桥?
30、有12个小球,外形相同,其中一个小球的质量与其他11个不同,
给一个天平,问如何用3次把这个小球找出来,并且求出这个小球是比其他的轻还是重
创新工场面试题:abcde五人打渔,打完睡觉,a先醒来,扔掉1条鱼,把剩下的分成5分,拿一份走了;b再醒来,也扔掉1条,把剩下的分成5份,拿一份走了;然后cde都按上面的方法取鱼。问他们一共打了多少条鱼,写程序和算法实现。提示:共打了多少条鱼的结果有很多。但求最少打的鱼的结果是3121条鱼(应该找这5个人问问,用什么工具打了这么多条鱼)。(http://blog.csdn.net/nokiaguy/article/details/6800209)。
我们有很多瓶无色的液体,其中有一瓶是毒药,其它都是蒸馏水,实验的小白鼠喝了以后会在5分钟后死亡,而喝到蒸馏水的小白鼠则一切正常。现在有5只小白鼠,请问一下,我们用这五只小白鼠,5分钟的时间,能够检测多少瓶液体的成分?
淘宝2012笔试(研发类):http://topic.csdn.net/u/20110922/10/e4f3641a-1f31-4d35-80da-7268605d2d51.html(一参考答案)。
华为面试2:1分2分5分的硬币,组成1角,共有多少种组合。
43、Smith夫妇召开宴会,并邀请其他4对夫妇参加宴会。在宴会上,他们彼此握手,
并且满足没有一个人同自己握手,没有两个人握手一次以上,并且夫妻之间不握手。
然后Mr. Smith问其它客人握手的次数,每个人的答案是不一样的。
求Mrs Smith握手的次数
44、有6种不同颜色的球,分别记为1,2,3,4,5,6,每种球有无数个。现在取5个球,求在一下
的条件下:
1、5种不同颜色,
2、4种不同颜色的球,
3、3种不同颜色的球,
4、2种不同颜色的球,
它们的概率。
45、有一次数学比赛,共有A,B和C三道题目。所有人都至少解答出一道题目,总共有25人。
在没有答出A的人中,答出B的人数是答出C的人数的两倍;单单答出A的人,比其他答出A的人
总数多1;在所有只有答出一道题目的人当中,答出B和C的人数刚好是一半。
求只答出B的人数。
12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?
这个笔试题,很YD,因为把某个递归关系隐藏得很深.
47、金币概率问题(威盛笔试题)
题目:10个房间里放着随机数量的金币。每个房间只能进入一次,并只能在一个房间中拿金币。
一个人采取如下策略:前四个房间只看不拿。随后的房间只要看到比前四个房间都多的金币数,
就拿。否则就拿最后一个房间的金币。?
编程计算这种策略拿到最多金币的概率。
22、有5个海盗,按照等级从5到1排列,最大的海盗有权提议他们如何分享100枚金币。
但其他人要对此表决,如果多数反对,那他就会被杀死。
他应该提出怎样的方案,既让自己拿到尽可能多的金币又不会被杀死?
(提示:有一个海盗能拿到98%的金币)
五只猴子分桃。半夜,第一只猴子先起来,它把桃分成了相等的五堆,多出一只。于是,它吃掉了一个,拿走了一堆; 第二只猴子起来一看,只有四堆桃。于是把四堆合在一起,分成相等的五堆,又多出一个。于是,它也吃掉了一个,拿走了一堆;.....其他几只猴子也都是 这样分的。问:这堆桃至少有多少个?(朋友说,这是小学奥数题)。
参考答案:先给这堆桃子加上4个,设此时共有X个桃子,最后剩下a个桃子.这样:
第一只猴子分完后还剩:(1-1/5)X=(4/5)X;
第二只猴子分完后还剩:(1-1/5)2X;
第三只猴子分完后还剩:(1-1/5)3X;
第四只猴子分完后还剩:(1-1/5)4X;
第五只猴子分完后还剩:(1-1/5)5X=(1024/3125)X;
得:a=(1024/3125)X;
要使a为整数,X最小取3125.
减去加上的4个,所以,这堆桃子最少有3121个。
已知有个rand7()的函数,返回1到7随机自然数,让利用这个rand7()构造rand10() 随机1~10。
(参考答案:这题主要考的是对概率的理解。程序关键是要算出rand10,1到10,十个数字出现的考虑都为10%.根据排列组合,连续算两次rand7出现的组合数是7*7=49,这49种组合每一种出现考虑是相同的。怎么从49平均概率的转换为1到10呢?方法是:
1.rand7执行两次,出来的数为a1=rand7()-1,a2=rand7()-1.
2.如果a1*7+a2<40,b=(a1*7+a2)/4+1;如果a1*7+a2>=40,重复第一步。参考代码如下所示:
int rand7()
{
return rand()%7+1;
}
int rand10()
{
int a71,a72,a10;
do
{
a71= rand7()-1;
a72 = rand7()-1;
a10 = a71 *7 + a72;
} while (a10>= 40);
return (a71*7+a72)/4+1;
}
2)一串首尾相连的珠子(m个),有N种颜色(N<=10),
设计一个算法,取出其中一段,要求包含所有N中颜色,并使长度最短。
并分析时间复杂度与空间复杂度。
41.求固晶机的晶元查找程序
晶元盘由数目不详的大小一样的晶元组成,晶元并不一定全布满晶元盘,
照相机每次这能匹配一个晶元,如匹配过,则拾取该晶元,
若匹配不过,照相机则按测好的晶元间距移到下一个位置。
求遍历晶元盘的算法 求思路。
+92
View File
@@ -0,0 +1,92 @@
/////////////////// 海量数据处理(千万以上量级) ///////////
2.有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
3.大量的URL字符串,如何从中去除重复的,优化时间空间复杂度
2.一个文件,内含一千万行字符串,每个字符串在1K以内,
要求找出所有相反的串对,如abc和cba。
2.给出一个文件,里面包含两个字段{url、size},
即url为网址,size为对应网址访问的次数,
要求:
问题1、利用Linux Shell命令或自己设计算法,
查询出url字符串中包含“baidu”子字符串对应的size字段值;
问题2、根据问题1的查询结果,对其按照size由大到小的排列。
(说明:url数据量很大,100亿级以上)
3.有10个文件,每个文件1G,
每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。
要求按照query的频度排序.
2.有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中随机取得m个记录
31、在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可。
32、一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数
33、腾讯服务器每秒有2w个QQ号同时上线,找出5min内重新登入的qq号并打印出来。
淘宝面试题:有一个一亿节点的树,现在已知两个点,找这两个点的共同的祖先。
海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。(此题请参考本博客内其它文章)。
某服务器流量统计器,每天有1000亿的访问记录数据,包括时间、url、ip。设计系统实现记录数据的
保存、管理、查询。要求能实现一下功能:
(1)计算在某一时间段(精确到分)时间内的,某url的所有访问量。
(2)计算在某一时间段(精确到分)时间内的,某ip的所有访问量。
假设某个网站每天有超过10亿次的页面访问量,出于安全考虑,网站会记录访问客户端访问的ip地址和对应的时间,如果现在已经记录了1000亿条数据,想统计一个指定时间段内的区域ip地址访问量,那么这些数据应该按照何种方式来组织,才能尽快满足上面的统计需求呢,
设计完方案后,并指出该方案的优缺点,比如在什么情况下,可能会非常慢?(参考答案:用B+树来组织,非叶子节点存储(某个时间点,页面访问量),叶子节点是访问的IP地址。这个方案的优点是查询某个时间段内的IP访问量很快,但是要统计某个IP的访问次数或是上次访问时间就不得不遍历整个树的叶子节点。或者可以建立二级索引,分别是时间和地点来建立索引。)
腾讯1.服务器内存1G,有一个2G的文件,里面每行存着一个QQ号(5-10位数),怎么最快找出出现过最多次的QQ号。(此题与稍后下文的第14题重复,思路参考请见下文第14题)。
18、如何随机选取1000个关键字
给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字)。如何才能从这个无穷尽的流中随机的选取1000个关键字?
14、腾讯最新面试题:服务器内存1G,有一个2G的文件,里面每行存着一个QQ号(5-10位数),怎么最快找出出现过最多次的QQ号。
以下是个人所建第Algorithms_12群内朋友的聊天记录:
首先你要注意到,数据存在服务器,存储不了(内存存不了),要想办法统计每一个qq出现的次数。
比如,因为内存是1g,首先 你用hash 的方法,把qq分配到10个(这个数字可以变动,比较)文件(在硬盘中)。
相同的qq肯定在同一个文件中,然后对每一个文件,只要保证每一个文件少于1g的内存,统计每个qq的次数,可以使用hash_map(qq, qq_count)实现。然后,记录每个文件的最大访问次数的qq,最后,从10个文件中找出一个最大,即为所有的最大。更多读者可以参见此文:海量数据处理面试题集锦与Bit-map详解 。
那若面试官问有没有更高效率的解法之类的?这时,你可以优化一下,但是这个速度很快,hash函数,速度很快,他肯定会问,你如何设计,用bitmap也行。
+144
View File
@@ -0,0 +1,144 @@
///////////////////////// 排序 ///////////////////////////
49.一道看上去很吓人的算法面试题:
如何对n个数进行排序,要求时间复杂度O(n),空间复杂度O(1)
3.快速排序(东软喜欢考类似的算法填空题,又如堆排序的算法等)
1.编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?
2.编写实现数组排序的一种算法。说明为什么你会选择用这样的方法?
3.收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)
四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())
要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。
给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。
2.已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器,
使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;...,
构造一个发生器,使得它构造1、2、3、...n的概率均为1/n,要求复杂度最低。
3.请编写能直接实现strstr()函数功能的代码。
请编写实现malloc()内存分配函数功能一样的代码。
25、用C语言实现函数void * memmove(void *dest, const void *src, size_t n)。memmove
函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。
分析:由于可以把任何类型的指针赋给void类型的指针,这个函数主要是实现各种数据类型的拷贝。
8、请编写能直接实现int atoi(const char * pstr)函数功能的代码。
int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?
42、两个圆相交,交点是A1,A2。现在过A1点做一直线与两个圆分别相交另外一点B1,B2。
B1B2可以绕着A1点旋转。问在什么情况下,B1B2最长
题目:输入四个点的坐标,求证四个点是不是一个矩形
关键点:
1.相邻两边斜率之积等于-1,
2.矩形边与坐标系平行的情况下,斜率无穷大不能用积判断。
3.输入四点可能不按顺序,需要对四点排序。
51、矩阵式螺旋输出
/////////////// 系统设计 ///////////////////////////////
一、系统有很多任务,任务之间有依赖,比如B依赖于A,则A执行完后B才能执行
(1)不考虑系统并行性,设计一个函数(Task *Ptask,int Task_num)不考虑并行度,最快的方法完成所有任务。
(2)考虑并行度,怎么设计
typedef struct{
int ID;
int * child;
int child_num;
}Task;
提供的函数:
bool doTask(int taskID);无阻塞的运行一个任务;
int waitTask(int timeout);返回运行完成的任务id,如果没有则返回-1;
bool killTask(int taskID);杀死进程
2.设计一种内存管理算法。
1.有不同的手机终端,如iphone,安卓,Symbian,不同的终端处理不一样,设计一种服务器和算法实现对不同的终端的处理。
3.A向B发邮件,B收到后读取并发送收到,但是中间可能丢失了该邮件,怎么设计一种最节省的方法,来处理丢失问题。
4.设计一种算法求出算法复杂度 。
+199
View File
@@ -0,0 +1,199 @@
链表反转
链表中是否有环
删除链表中的p节点,在p节点前面插入节点q, 要求O(1)复杂度
2个链表相交
2个链表合并
判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。
问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?
请修改append函数,利用这个函数实现:
两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5
另外只能输出结果,不能修改两个链表的数据。
题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。
链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
链表操作,
(1)单链表就地逆置,
(2)合并链表
30、微软面试题:Given a head pointer pointing to a linked list ,please write a function that sort the list
in increasing order. You are not allowed to use temporary array or memory copy
struct
{
int data;
struct S_Node *next;
}Node;
Node * sort_link_list_increasing_order (Node *pheader):
题一、 给定单链表,检测是否有环。
使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。
如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。
题二、 给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。
如果head1==head2,那么显然相交,直接返回head1。
否则,分别从head1,head2开始遍历两个链表获得其长度len1与len2,假设len1>=len2,
那么指针p1由head1开始向后移动len1-len2步,指针p2=head2,
下面p1、p2每次向后前进一步并比较p1p2是否相等,如果相等即返回该结点,
否则说明两个链表没有交点。
题三、 给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。
运用题一,我们可以检查链表中是否有环。
如果有环,那么p1p2重合点p必然在环中。从p点断开环,方法为:p1=p, p2=p->next,
p->next=NULL。此时,原单链表可以看作两条单链表,一条从head开始,另一条从p2开始,
于是运用题二的方法,我们找到它们的第一个交点即为所求。
题四、只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。
办法很简单,首先是放p中数据,然后将p->next的数据copy入p中,接下来删除p->next即可。
题五、只给定单链表中某个结点p(非空结点),在p前面插入一个结点。
办法与前者类似,首先分配一个结点q,将q插入在p后,
接下来将p中的数据copy入q中,然后再将要插入的数据记录在p中。都可以做到0(1)复杂度
题六、 链表反转
三个指针,遍历一遍(0(n)复杂度)
复杂链表的复制
题目:有一个复杂链表,其结点除了有一个m_pNext指针指向下一个结点外,
还有一个m_pSibling指向链表中的任一结点或者NULL。其结点的C++定义如下:
struct ComplexNode
{
int m_nValue;
ComplexNode* m_pNext;
ComplexNode* m_pSibling;
};
下图是一个含有5个结点的该类型复杂链表。
图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。
为简单起见,指向NULL的指针没有画出。
请完成函数ComplexNode* Clone(ComplexNode* pHead),以复制一个复杂链表。
//图,接下来,自会补上。July、11.19.
分析:在常见的数据结构上稍加变化,这是一种很新颖的面试题。
要在不到一个小时的时间里解决这种类型的题目,
我们需要较快的反应能力,对数据结构透彻的理解以及扎实的编程功底。
62.找出链表的第一个公共结点。
题目:两个单向链表,找出它们的第一个公共结点。
链表的结点定义为:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
题目:从尾到头输出链表。输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道很有意思的面试题。
该题以及它的变体经常出现在各大公司的面试、笔试题中。
60.在O(1)时间内删除链表结点。
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
函数的声明如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);
分析:这是一道广为流传的Google面试题,能有效考察我们的编程基本功,还能考察我们的反应速度,更重要的是,还能考察我们对时间复杂度的理解。
36、有双向循环链表结点定义为:
struct node
{
int data;
struct node *front,*next;
};
有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中data值相同的结点删除。
78.链表和数组的区别在哪里?
分析:主要在基本概念上的理解。
但是最好能考虑的全面一点,现在公司招人的竞争可能就在细节上产生,
谁比较仔细,谁获胜的机会就大。
  ★说说各种股票分类算法的优点和缺点。
  ★用一种算法来颠倒一个链接表的顺序。现在在不用递归式的情况下做一遍。
  ★用一种算法在一个循环的链接表里插入一个节点,但不得穿越链接表。
  ★用一种算法整理一个数组。你为什么选择这种方法?
  
  ★假设你有一个用1001个整数组成的数组,这些整数是任意排列的,但是你知道所有的整数都在1到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数字。如果你在运算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?
  ★不用乘法或加法增加8倍。现在用同样的方法增加7倍。