rename dir

This commit is contained in:
ranwenjie
2021-03-18 21:24:06 +08:00
parent 09d8a6131e
commit dcce9f67eb
138 changed files with 112 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
这部分内容是算法问题合集,题目大多来自网络和书籍。我做了下简单的整理,很多题做了一些思路标记。
### 字符串
计算机处理的数据除了数值,就是字符。字符处理的常见问题包括:
* 单词反转
* 回文判断
* 字符串的压缩
* 字符串的排列和组合
* 字符串比较
* 子串
### 链表
链表处理的常见问题包括:
* 链表反转
* 链表中是否有环
* 删除链表中的p节点,在p节点前面插入节点q, 要求O(1)复杂度
* 2个链表相交
* 2个链表合并
### 栈和队列
这是最基本数据结构,相对容易理解。
### 数值问题
这部分都是一些数学几何计算方面的问题。 主要有:
* 位运算
* 随机数
* 大数问题
### 数组数列问题
这部分的问题都集中在数据集合上。主要有:
* 数组排序
* top-k
* 子数组
* 多个数组合并,交集
### 矩阵
这部分都是矩阵和二维数组相关的问题。
### 二叉树
* 遍历
* 翻转
* 子树
### 图
图相关的问题
### 海量数据处理
海量处理问题常用的分析解决问题的思路是:
* Hash映射/分而治之
* Bitmap
* Bloom filter(布隆过滤器)
* Trie树
* 数据库索引
* 倒排索引(Inverted Index)
* 双层桶划分
* 外排序
* simhash算法
* 分布处理之Mapreduce
### 《剑指offer》
《剑指offer》里面给出了50到高质量的算法问题,很有学习的必要。
### 《编程之美》
### 智力思维训练
+13
View File
@@ -0,0 +1,13 @@
题目来自[leetcode](https://leetcode.com/)
[LeetCode题解C++版](https://github.com/soulmachine/leetcode), 151道题完整版
#### Minimum Height Trees
#### Additive Number
+5
View File
@@ -0,0 +1,5 @@
题目来自 [九度OJ](http://ac.jobdu.com/index.php)
+216
View File
@@ -0,0 +1,216 @@
问题涉及有:
* 遍历
* 翻转
* 子树
#### 把二元查找树转变成排序的双向链表
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求`不能创建任何新的结点`,只调整指针的指向。
```
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
};
```
分析:题目要求不能创建任何节点,也就是只能调整各个节点的指向
思路一:
#### 在二元树中找出和为某一值的所有路径
题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相等的所有路径。
例如 输入整数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
};
```
1. 累加当前节点,累加和大于给定值
2. 不为叶节点,左子树入栈
#### 判断整数序列是不是二元查找树的后序遍历结果
题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。
如果是返回true,否则返回false。
例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:
```
8
/ \
6 10
/\ / \
5 7 9 11
```
因此返回true。如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。
#### 一棵排序二叉树,令 f=(最大值+最小值)/2,设计一个算法,找出距离f值最近、大于f值的结点。
复杂度如果是O(n2)则不得分。
#### 二叉树翻转
输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点。用递归和循环两种方法完成树的镜像转换。例
如输入:
```
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
};
```
#### 按层打印二叉树
输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。
例如输入
```
8
/\
6 10
/ \ /\
5 7 9 11
```
输出`8 6 10 5 7 9 11`
#### 二元树的深度
题目:输入一棵二元树的根结点,求该树的深度.从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
例如:输入二元树:
```
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;给出程序实现,并分析时间复杂度和空间复杂度。
#### 把一个有序整数数组放到二叉树中?
分析:本题考察二叉搜索树的建树方法,简单的递归结构。
关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。
而,学会把递归改称非递归也是一种必要的技术。
毕竟,递归会造成栈溢出,关于系统底层的程序中不到非不得以最好不要用。
但是对某些数学问题,就一定要学会用递归去解决。
#### 二叉树两个结点的最低共同父结点
设计一个算法,找出二叉树上任意两个结点的最近共同父结点。复杂度如果是O(n2)则不得分
二叉树的结点定义如下:
```
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
```
输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。
分析:求数中两个结点的最低共同结点是面试中经常出现的一个问题。这个问题至少有两个变种。
#### 俩种方法实现二叉树的前序遍历。
递归和非递归
#### 恢复树结构
有一棵树(树上结点为字符串或者整数),请写代码将树的结构和数据写到一个文件中,并能通过读取该文件恢复树结构。
@@ -0,0 +1,567 @@
《剑指offer》 这本书给出了50到面试题,涉及到字符串处理,堆栈,链表,二叉树等问题的处理。
* 代码鲁榜性:边界条件,特殊输入,异常处理:空指针
* 分析方法:画图,举例,分解
* 查找和排序是常考:重点掌握二分查找,快速排序,归并排序
* 本书完整源代码在:
### 赋值运算符函数
### 实现Singleton模式
### 二维数组中的查找
二维数组中每一行从左到右递增,每一列从上到下递增,判断数组中是否包含该整数。
```
bool find(int *matrix,int rows,int columns,int numbers)
```
### 替换空格
如把字符串中的每个空格替换成`%20`
`二遍扫描`
```
void replace_blank(char *str);
```
### 从尾到头打印链表
`栈`
```
void print_reversing(LinkList *head)
```
### 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,重建该二叉树
```
BinaryTree *construct(int *preorder,int inroder,int length);
```
### 用两个栈实现队列
队列就是在尾部插入节点,头部删除节点。
### 旋转数组的最小数字
旋转数组是指把一个数组最开始的若干个元素搬到数组的末尾。输入一个递增排序的数组的旋转,比如{3,4,5,1,2}是{1,2,3,4,5}的一个旋转。求该数组的最小值。
```
int min(int *num, int length)
```
### 菲波那切数列
```
long long fabonacci(unsigned n)
```
### 二进制中1的个数
输入一个整数,输出该数二进制中1出现的次数。比如9的二进制 10001,输出是2
`n=n&n-1`
```
int one_appear_count(int n)
```
### 数值的整数次方
要求不得使用库函数。这里注意考虑指数是0和负数的情况
```
double power(double base,int exponent)
```
### 打印1到最大的n位数
比如n=3,就打印1到999
```
void print_to_max_with_length(int n)
```
### 在O(1)时间删除链表节点
已经有一个头节点指针,还有一个指向改删除节点的指针
`用下一个节点的内容覆盖当前删除节点的内容,删除下一个节点`
```
void deleteNode(LinkList *head,LinkList *targetToDelete);
```
### 调整数组顺序使奇数位于偶数前面
调整后,所有奇数在前半部分,偶数在后半部分
`两边向中间扫描`
```
void reorder(int *data,int length)
```
### 输出链表中倒数第K个节点
`使用两个指针,一个先走k-1步`
```
void print_lastK(LinkList *head);
```
### 反转链表
`三个指针`
```
void reverse(LinkList *head);
```
反转二叉树呢?
### 合并2个排序的链表
要求合并以后链表任然排序
`递归`
```
LinkList *merge(LinkList *one,LinkList *two);
```
### 树的子结构
考察二叉树的基本操作。输入2课二叉树A和B,判断B是不是A的子结构。
```
struct BinaryTreeNode{
int m_value;
BinaryTreeNode *m_pleft;
BinaryTreeNode *m_pRight;
}
```
```
8
/ \ 10
/ \ / \
6 10 子结构 11 9
/ \ / \
5 7 9 11
```
```
bool subTree(BinaryTreeNode *root1,BinaryTreeNode *root2);
```
### 二叉树翻转
```
8 8
/ \ / \
/ \ / \
6 10 翻转后 10 6
/ \ / \ / \ / \
5 7 9 11 11 9 7 5
```
`交换每个节点的左右子树`
```
void reverse(BinaryTreeNode *root);
```
### 从外向里顺时针打印矩阵
```
void print_matrix_clockwise(int *matrix,int cols,int rows);
```
延伸:按大小顺序打印矩阵
### 实现一个能找到栈的最小元素的函数
`最小元素用辅助栈保存`
```
int min(Stack *stack)
```
### 栈的压入,弹出序列
输入2个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。比如:
1,2,3,4,5是压栈序列,4,5,3,2,1是弹栈序列,但是4,3,5,1,2就不是弹栈序列
```
bool is_pop_order(int *push,int *pop,int length)
```
### 从上往下打印二叉树
`辅助队列`
```
void print_binary_level(BinaryTreeNode *root)
```
### 二叉搜索树的后续遍历序列
输入一个整数数组,判断该数组是不是某二叉查找树的后续遍历序列的结果。比如【 5,7,6,9,11,10,8】 是下面二叉查找树的后续遍历结果:
```
8
/ \
/ \
6 10
/ \ / \
5 7 9 11
```
`寻找规律`
```
bool is_post_order(BST *root,int *data, int length);
```
### 二叉树中和为某一值的路径
```
10
/ \
/ \
5 12
/ \
5 7
```
和为22的路径有2条:10--5--7, 10--12
`递归,栈`
```
void print_path(BinaryTree *root,int n)
```
### 复杂链表的复制
在复杂链表结构中,每个节点都有一个指向下一个节点的m_pNext;还有一个指向任意节点的m_pSibling
```
typedef struct LinkListNode{
int m_value;
LinkListNode *m_pNext;
LinkListNode *m_pSlbling;
}LinkList;
```
```
LinkList * copy(LinkList *head);
```
### 二叉搜索树与双向链表
将二叉搜索树转换成一个排序的双向链表,只调整树中节点的指针指向
`递归` `分解问题`
```
BST *transform(BST *root);
```
### 字符串的排列
输入一个字符串,打印该字符串中字符的所有排列
`递归,分解`
```
void print_full_permutation(char *string)
```
### 数组中出现次数超过一半的数字
`遍历数组,下一个数字和之前保存的数字一样就+1,否则就-1`
```
int find_more_than_half_num(int *nums ,int length)
```
### n个整数中最小的K个数
`快速排序` `最大堆`
```
void find_least_k(int *data,int n,int *ouput,int k)
```
最大的K个数呢?
### 连续子数组的最大和
输入一个整数数组,有正有负,求所有子数组的和的最大值
`分析规律` `动态规划`
```
int max_of_subarray(int *data,int length)
```
### 从1到n整数中,1出现的次数
比如 12,从1到12这些整数中,包含1的数字有 1, 10,11,12 ,1出现了5次
```
int one_appear_count(int n)
```
### 把数组排成最小的数
输入一个正整数数组,把所有数字拼起来排出一个最小数
```
int minSort(int *nums, int length);
```
### 丑数
> 只包含因子 2,3,5的数叫做丑数
求按从小到大的顺序,第1500个丑数
```
int ugly(int n)
```
### 第一个只出现一次的字符
在字符串中查找第一个只出现一次的字符
`哈希表:值为出现的次数` `二次扫描`
```
char find_appear_once_char(char *string)
```
### 数组中的逆序对
>数组中的两个数字如果前面一个数字大于后面的数字,这两个数字组成一个逆序对。如:[7,5,6,4] 的逆序对:(7,5)(7,6)(7,4)(5,4)(6,4)
输入一个数组,求出这个数组逆序对总数。
`归并排序 O(nlogn),空间O(n)`
```
int reversePairs(int *data,int length)
```
### 两个链表的第一个公共节点
`长的链表先走k步`
```
LinkListNode *common_node(LinkList *head1,LinkList head2);
```
### 数字在排序数组中出现的次数
比如 {1,2,3,3,3,3,4,5}, 数字 3出现了4次
`使用二分查找找第一个3,和最后一个3出现的位置`
```
int appear_count(int *nums,int length,int n);
```
### 二叉树的深度
`递归`
```
int tree_depth(BTree *root);
```
### 数组中只出现一次的数字
数组中除了2个数字之外,其他的数组都出现了2次,找出这两个数
`异或` `二进制`
>如果是只有1个数字只出现一次,我们可以通过对数组依次做异或运算。
如果我们能把原数组分成2个子数组,每个子数组都包含一个只出现一次的数字,问题就能解决了。我们把数组中的所有数字依次做异或操作,如果有2个数字不一样,结果肯定不是0,且异或结果数字的二进制表示中至少有一位是1(不然结果不就是0了)
1. 在结果数字二进制表示中找到第一个为1的位的位置,标记n
2. 以二进制表示中第n位是不是1为标准,把原数组分成2个子数组
```
void find_two_numbers_appear_once(int *data,int length,int *ouput)
```
### 和为s的两个数字 VS 和为s的连续正数序列
有一个递增排序数组,和一个数字s,找出数组中的2个数,使得和等于s。输出任意一对即可
`两边向中间扫描`
```
void print_two_numbers(int *data,int length,int sum)
```
### 反转单词顺序 VS 左旋转字符串
a. 翻转句子中单词的顺序,但单词内字符不变。如 『I am a student』 -> 『student. a am I』
`先以单词为单位翻转,整个句子再次翻转`
```
char *reverse_by_word(char *string)
```
b. 左旋转字符串是把字符串其那面的若干位转义到字符串的尾部。比如"abcedfsz"和数字2,结果是"cedfszab"
```
char *left_rotate_string(char *s,int n)
```
### n个色子的点数
把n个色子丢地上,朝上一面的点数之和为s。输入n,打印可能的值出现的概率
```
void print_sum_probability(int n)
```
### 扑克牌中的顺子
从扑克牌从随机抽5张牌,判断是不是顺子。A是1,J~K是11~13,大小王可以看出任意数字。
```
bool is_straight(int *data,int length)
```
### 圆圈中最后剩下的数字(约瑟夫问题)
0,1,...,n-1 这n个数字排成一个圆圈,从数字0开始从这个圆圈里面删除第m个数字,求出这个圆圈里最后剩下的数字。
```
int last_remaining(unsigned int n,unsigned int m)
```
### 求 1+2+...+n
要求不用乘除法,for/while/if/else/switch等关键字及条件判断语句
```
long long sum(unsigned int n);
```
### 不用加减乘除做加法
求2个整数之和
`位运算`
```
int sum(int,int)
```
### 不能被继承的类
```
```
### 把字符串转换成整数
比如 "12343567754" -> 12343567754
`NULL,空串,正负号,溢出`
```
int strToInt(char str);
```
### 树中2个结点的最低公共祖先
如果这个树是二叉排序树
如果不是二叉排序树,但是有父节点指针
如果不是二叉树,也没有父节点指针
+28
View File
@@ -0,0 +1,28 @@
31.华为面试题:
一类似于蜂窝的结构的图,进行搜索最短路径(要求5分钟)
求一个有向连通图的割点,割点的定义是,如果除去此节点和与其相关的边,
有向图不再连通,描述算法。
11、平面上N个点,每两个点都确定一条直线,
求出斜率最大的那条直线所通过的两个点(斜率不存在的情况不考虑)。时间效率越高越好。
2.求一个有向连通图的割点,割点的定义是,
如果除去此节点和与其相关的边,有向图不再连通,描述算法。
求一个二叉树中任意两个节点间的最大距离,
两个节点的距离的定义是 这两个节点间边的个数,
比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。
+139
View File
@@ -0,0 +1,139 @@
##堆和栈
栈的数据结构
```
typede int Value;
typedef struct Entry{
Entry *next;
Value value;
}Entry;
typedef struct Stack{
Entry *head;/*添加和删除都在这个节点指针上*/
int length;
int capacity;
}Stack;
Stack create(unsigned int size);
void push(Stack,Value);
Value pop(Stack);
```
队列的数据结构
```
typede int Value;
typedef struct Entry{
Entry *next;
Value value;
}Entry;
typedef struct Queue{
Entry *front;/*删除都在这个节点指针上*/
Entry *rear;/*添加在这个节点指针上*/
int length;
}Stack;
void enqueue(Queue,Value);
void dequeue(Queue);
```
这里的题目是有关栈和队列的问题。
#### 循环队列中元素个数
如果用一个循环数组q[0..m-1]表示队列时,该队列只有一个队列头指针front,不设队列尾指针rear,求这个队列中从队列头到队列尾的元素个数(包含队列头、队列尾)
分析:
有rear指针时:
```
if (rear>front)
count = rear-front+1
else
count = rear-front+m+1
```
综合: `count = (rear-front+m+1)%m`
不用rear指针的话:
数据结构中加一个 `int count`来计数。
#### 设计包含min函数的栈
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。
要求函数min、push以及pop的时间复杂度都是O(1)。
主要是min函数实现。咋一看,用一个变量来存储最小值,或最小值的下标不就ok了?这在只push的时候有用,想想如果这个最小值pop出去了呢?我们怎么更新现在的最小值?
这里用一个辅助栈,空间复杂度是O(n).
push一个元素a时,该元素a与辅助栈顶f[top]元素比较:
a > f[top],在辅助栈再次中push一遍f[top] (可以把这个省去,节省空间,代价是pop的时候要跟最小栈元素比较,如果pop元素跟最小栈栈顶相等,可能最小值要变了)
a < f(top),把 a push 到 辅助栈中
这样辅助栈中的元素是 从上到小 递增的数列。且 辅助栈中的栈顶元素 总是 栈中元素集合的最小值。
以上方法需要的空间复杂度是O(n)
#### 栈的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序列。
这里只是要判断是不是pop序列,并没有要求所有的pop序列
需要一个辅助栈(需要一个写好的栈结构辅助,如果是C语言,还要包装一个栈结构)
1. 对序列A中的A[0]入栈,比较B[0]
2. 如果不相等,A[1]入栈
3. 直到A[i] = B[0]
4. 栈顶出栈,开始匹配B[1]
5. 如果A[i-1]!=B[1],那就继续入栈A[i+1]
6 .循环往复~
7. 如果最后,栈为空,就是一个pop序列;如果栈不为空,或者B数组都没有遍历到尾部,就肯定不是pop序列了
#### 颠倒栈
题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。
思路一:所有元素pop出来,放到一个数组里,然后在从第一个元素开始入栈,空间复杂度需要 O(N)
思路二: 递归方法。把栈看出2部分 :1 和 {2,3,4,5} , 把栈{2,3,4,5}颠倒过来,然后把1放到底部,那整个栈就颠倒过来了。
```
void reverseStack(){
stack.pop();
reverseStack();
addTopToStackBottom();
}
```
+599
View File
@@ -0,0 +1,599 @@
字符串常见的问题:
* 单词反转/移动/回文判断
* 字符(串)统计
* 字符串的压缩
* 字符串的排列和组合
* 字符串比较
* 子串
#### 翻转句子中单词的顺序
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
```
char *revert_by_word(char *source);
```
原地逆序,字符串2边的字符逐个交换 , 再按单词逆序; 也可以先按单词逆序,再对整个句子逆序;
针对不允许临时空间的情况,也就是字符交换不用临时空间,可以使用的方法有:
1. 异或操作
2. 也就是2个整数相互交换一个道理
```
char a = 'a', b = 'b';
a = a + b;
b = a - b;
a = a - b;
```
最终示例代码:
```
void _reverse(char *start,char *end){
if ((start == NULL) || (end == NULL)) return;
while(start < end){
char tmp = *start;
*start = *end;
*end = tmp;
start++,
end--;
}
}
char *revert_by_word(char *source){
char *end = source;
char *start = source;
if (source == NULL) return NULL;
while (*end != '\0') end++;
end--;
_reverse(start,end);
start=end=source;
while(*start != '\0'){
if (*start == ' '){
start++;
end++;
}else if(*end == ' ' || *end == '\0'){
_reverse(start,end-1);
start = end;
}else{
end++;
}
}
return source;
}
```
类似的题目还有:
不开辟用于交换数据的临时空间,如何完成字符串的逆序
用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
#### 左旋转字符串
>字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。
如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
```
char *left_rotate(char *str,int offset){
}
```
我们可以abcdef分成两部分,ab和cdef,内部逆序以后,整体再次逆序,就可以得到想要的结果了,也就是跟上面的问题是同样的问题。
#### 判断字符串是否是回文
分析: 2个指针,一头一尾,逐个比较,都相同就是回文
```
/*
* eg acdeedca
* @ret 0 success , -1 fail
*/
int is_huiwen(const char *source){
if (source == NULL || source == '\0') return -1;
char *head = source;
char *tail = source;
while(*tail != '\0'){
tail++;
}
tail--;
while(head < tail){
if (*head != *tail){
return -1;
}
head++;
tail--;
}
return 0;
}
```
#### 只出现一次的字符
在一个字符串中找到第一个只出现一次的字符。如输入ahbaccdeff,则输出h。
```
char char_first_appear_once(const char *source)
```
思路一: 蛮力统计, O(n^2)复杂度
思路二: 使用hash表,2次扫描,第一次建立hash表 key为字符,value为出现次数;第二次扫描找到第一个value为1的key,时间复杂度O(n)
hash表长度 256,字符直接作为key值。需要注意的是 char 的范围是 -128~127,unsigned char 才是0~255
示例代码:
```
char char_first_appear_once(const unsigned char *source){
int hash[256]={0};
char *tmp = source;
if (tmp == NULL) return '\0';
while(*tmp != '\0'){
hash[*tmp]++;
tmp++;
}
tmp = source;
while(*tmp != '\0'){
if (hash[*tmp] == 1) return *tmp;
tmp++;
}
return '\0';
}
```
题目扩展:这里的字符换成整数,整数数量几十TB,海量数据处理,显然hash方法不可能,没有那么大得内容
#### 统计文章里单词出现的次数
设计相应的数据结构和算法,尽量高效的统计一片英文文章(总单词数目)里出现的所有英文单词,按照在文章中首次出现的顺序打印输出该单词和它的出现次数。
```
void statistics(char *string)
void statistics(FILE *fd)
```
延伸:
如果是海量数据里面统计top-k次数的单词呢?
#### 替换空格
实现一个函数,把每个空格替换成 "%20",如输入“we are happy”,则输出“we%20are%20happy”
```
char *replce_blank(char *source)
```
主要问题是一个字符替换成3个字符,替换后的字符串比原串长。
如果想要在原串上直接修改,就不能顺序替换。且原串的空间应该足够大,能容纳替换变长以后的字符串。如果空间不够,就要新建一块空间来保存替换的结果了。这里假设空间足够
1. 第一遍扫描,统计空格个数 n, 替换后的字符串长度 = 原长度+2*n
2. 从后向前扫描字符串,挪动每个字符的位置。注意碰到空格的地方
```
char *replace_blank(char *source){
int count = 0;
char *tail = source;
if (source == NULL) return NULL;
while(*tail != '\0'){
if (*tail == ' ') count++;
tail++;
}
while(count){
if(*tail != ' '){
*(tail+2*count) = *tail;
}else{
*(tail+2*count) = '0';
*(tail+2*count-1) = '2';
*(tail+2*count-2) = '%';
count--;
}
tail--;
}
return source;
}
```
#### 小写字母排在大写字母的前面
有一个由大小写组成的字符串,现在需要对他进行修改,将其中的所有小写字母排在大写字母的前面(大写或小写字母之间不要求保持原来次序),如有可能尽量选择时间和空间效率高的算法 c语言函数原型:
```
void proc(char *str)
```
分析:
比如:HaJKPnobAACPc,要小写字母前面且不要求保存顺序,可以是:anobcHJKPAACP
1. 小写字母 a~z 的 ASCII码值是 97~122,A~Z的 ASCII码值是 65~90;0~9 的ASCII码是 48~57
2. 两边向中间扫描,左边大写右边小写就交换;如果都小写,头指针向前知道找到大写;如果都是大写,尾指针向后找小写;
示例代码
```
char *proc(char *str){
char *start = str;
char *end = str;
if (str == NULL) return NULL;
while(*end != '\0') end++;
end--;
while(start < end){
if (*start >= 'A' && *start <= 'Z'){//大写
if (*end >= 'a' && *end <= 'z'){
char tmp = *start;
*start = *end;
*end = tmp;
start++;
}
end--;
}else{//小写
if (*end >= 'A' && *end <= 'Z'){
end--;
}
start++;
}
}
return str;
}
```
#### 实现字符串转整型的函数
也就是实现函数atoi的功能,这道题目考查的就是对各种情况下异常处理。比如:
以"213"分析转换成证书的过程。3+1x10+2x100,思路是:每扫描到一个字符,把之前得到的数字*10,再加上当前的数字
0开头,"0213"
正/负数,"-432" ,"--422","++23"
浮点数,"43.2344"
非法,"2123des"
存在空格," -32"," +432"," 234","23 432","353 "," + 321"
NULL/空串,这时候返回值0
溢出,"32111111112222222222222222222222222222222" , 与 `INT_MAX `比较
如何区分正常的'0'和异常情况下返回的结果"0"? 可以通过一个全局变量 g_status 来标示,值为 kValid/kInvalid。
```
int atoi(const char *str){
}
```
详细过程也可以[参考这里](http://blog.csdn.net/v_july_v/article/details/9024123)
#### 删除串中指定的字符
删除指定的字符以后,后面的字符都要向前移动一位。这种复杂度是O(N^2);那么有没有O(N)的方法呢?
比如 "abcdeccba" 删除字符 "c"。使用2个指针,一前一后,比较前面的指针和删除字符:
1. 不相等,两个指针一起跑,且前面的指针值拷贝到后面指针指向的空间
2. 相等时,快指针向前一步
```
char *delete_occurence_character(char *src , char target){
char *front = src;
char *rear = src;
while(*front != '\0'){
if (*front != target){
*rear = *front;
rear++;
}
front++;
}
*rear = '\0';
return src;
}
```
#### 在字符串中删除特定的字符
题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。
这是上个题目的升级版本。
```
char *delete_occurence_characterset(char *source,const char *del);
```
1. 蛮力法。 遍历字符串,每个字符去删除字符串集合中查找,有就删除
2. 使用上面的方式,一次遍历
#### 删除字符串中的数字并压缩字符串
如字符串”abc123de4fg56”处理后变为”abcdefg”。注意空间和效率。(下面的算法只需要一次遍历,不需要开辟新空间,时间复杂度为O(N))
这道题跟上一道题也是一个意思。
示例代码:
```
char *trim_number(char *source){
char *start = source;
char *end = source;
if (source == NULL) return NULL;
while(*end != '\0'){
if (*end < '0' || *end > '9' ){
*start = *end;
start++;
}
end++;
}
*start = '\0';
return source;
}
```
#### 字符串原地压缩
题目描述:“abeeeeegaaaffa" 压缩为 "abe5ag3f2a",请编程实现。
这道题需要注意:
1. 单个字符不压缩
2. 注意考虑压缩后某个字符个数是多位数(超过10个)
3. 原地压缩最麻烦的地方就是数据移动
这是使用2个指针,一前一后,如果不相等,都往前移动一位;如果相等,后一位变为数字2,且移动后面的指针一位,任然相等则数字加1,不相等
```
char *compress(const char *src,char *dest){
}
```
上面的压缩算法可以看到,压缩算法的`效率`验证依赖`给定字符串的特性`,如果'aaaaaaaa....aaa' 这样特征的字符串,使用上面的压缩算法,压缩率接近100%,相反,可能会的0%的压缩率。
#### 字符串中找出连续最长的数字串
写一个函数,功能:
在字符串中找出连续最长的数字串,并把这个串的长度返回,并把这个最长数字串赋值给其中一个函数参数outputstr所指内存。
例如:"abcd12345ed125ss123456789"的首地址传给intputstr后,函数将返回9,outputstr所指的值为123456789
它的原形是:
```
int longest_continuious_number(const char *input,char *output)
```
应该有3个指针,第一个指针指向一个当前最长数字串的第一个数字,第二个指针指向第二个数字串的第一个数字,第三个指针是遍历指针,且统计第二个数字串的长度;当统计出来的长度大于第一个数字串的长度,第一个指针指向第二个指针指向的数字,相反,第二个指针和第三个指针继续向后查找。
1. 当end首次碰到数字时,且tmp=0,说明是首次出现数字,第二个指针移到该数字,继续遍历
2. 如果数字后面还是数字,tmp!=0 就是第二个数字串,因此 tmp += 1;
3. 当end从数字到普通字符时,如果tmp > max ,就要修改max和第一个指针start ,并把tmp归为0
```
int longest_continuious_number(const char *input,char *output){
int max = 0;
char *start= input;
char *mid = input;
char *end = input;
int tmp = 0;
if (input == NULL || output == NULL) return 0;
while (*end != '\0'){
if (*end < '0' || *end < '9'){//字母
if(tmp > max){
max = tmp;
start = mid;
}
tmp = 0;
}else{//数字
if (tmp == 0){//发现数字
mid = end;
}
tmp++;
}
end++;
}
//修改已数字结尾的bug
if(tmp > max){
max = tmp;
start = mid;
}
//copy
int i=0;
while(i<max){
*(output+i) = *(start+i);
i++;
}
*(output+i)='\0';
return max;
}
```
#### 求最大连续递增数字串
如“ads3sl456789DF3456ld345AA”中的“456789”就是所求。这道题在上一道题目的基础上增加了数字要递增的条件。思路跟上面差不多,碰到不递增的数字就相当于第二个数字串了。
#### 最长公共子串问题
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子串,则输出它们的长度4,并打印任意一个子串。
```
int longest_common_subsequence(const char *s1,const char *s2, char *common)
```
分析:求最长公共子串(Longest Common Subsequence,LCS)是一道非常经典的动态规划题,因此一些重视算法的公司像MicroStrategy都把它当作面试题。如"abccade","dgcadde"的最大子串为"cad"
实例代码:
```
int longest_common_subsequence(const char *s1,const char *s2, char *common){
}
```
#### 对称子字符串的最大长度
题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的加强版。
```
int max_symmetrical_char_length(const char *scr);
```
思路一:蛮力法,3重循环(类似 求子数组的最大和 fmax(i,j)问题), fmax(i,j)区间i,j是最长的对称字符
思路二:遍历所有子串,然后判读是否对称 O(n^2)
思路三:有个O(n)复杂度的算法 http://www.cnblogs.com/McQueen1987/p/3559497.html 分析过程如下:
1.
2.
3.
```
int max_symmetrical_char_length(const char *scr){
}
```
#### 子串匹配的个数
已知一个字符串,比如asderwsde,寻找其中的一个子字符串比如sde的个数,如果没有返回0,有的话返回子字符串的个数。
```
char *substr_count(const char *src, const char *substr, int *count)
```
#### 请编写能直接实现strstr()函数功能的代码。
> strstr(str1,str2) 判断str2是否是str1的子串。
```
/*
@ret 有就返回第一次出现子串的地址,否则返回NULL
*/
char *strstr(const char *source, const char *target){
}
```
#### 匹配兄弟字符串
如果两个字符串的字符一样,但是顺序不一样,被认为是兄弟字符串,问如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。
思路:判断各自素数乘积是否相等。更多方法请参见:http://blog.csdn.net/v_JULY_v/article/details/6347454。
```
int isBrother(const char *first,const char *secd)
```
思路一: 循环匹配 指数级复杂度
思路二: 利用质数,平方和比较,但这样必须是2个串的长度要一样,需要的空间比较大,最多256个字节。
示例代码:
```
int isBrother(const char *first,const char *secd){
}
```
#### 字符串的排列
题目:输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。输入字符串 abcca,则输出由 a,b,c排列出来的所有字符串,字符出现个数不变
分析:这是一道很好的考查对递归理解的编程题
简单的回溯就可以实现了。当然排列的产生也有很多种算法,去看看组合数学,还有逆序生成排列和一些不需要递归生成排列的方法。
>印象中Knuth的<TAOCP>第一卷里面深入讲了排列的生成。这些算法的理解需要一定的数学功底,也需要一定的灵感,有兴趣最好看看。
#### n个字符串联接
有n个长为m+1的字符串,如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以联接,问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。
#### 字符串的集合合并
给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。
#### 编写strcpy 函数
已知strcpy 函数的原型是:
```
char *strcpy(char *strDest, const char *strSrc);
```
其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数
+297
View File
@@ -0,0 +1,297 @@
这部分都是一些数学几何计算方面的问题。主要由:
* 位运算
* 随机数
* 大数问题
#### 求1+2+…+n
要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
分析:
1. 不能使用循环,那就用递归
2. 递归需要终止递归的条件判断语句,这里也不能用if,想其他办法,可以使用 &&逻辑与 运算符(在n>0条件满足是,才会指向后面的递归语句
```
int factorial(n){
int sum=0;
(n>0) && sum=n+factorial(n-1)
return sum;
}
```
#### 整数的二进制表示中1的个数
题目:输入一个整数,求该整数的二进制表达中有多少个1。例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
分析:
这是一道很基本的考查位运算的面试题。
解法1:一轮循环移位计数 (移位运算比除法运算效率要高,注意要考虑是负数的情况)
解法2:位运算
解法3:num &= num-1 巧妙之处在于,对高位没有影响。不断做 `num &= num-1` 直到num=0。
1010 & 1001 = 1000
1000 * 0111 = 0000
```
int one_appear_count_by_binary(int num){
int count = 0;
while(num !=0 ){
num &= num-1;
count++;
}
return count;
}
```
#### 请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句
分析:
```
#define min(a,b) ((a)>(b)?(a):(b))
#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
```
这里不能使用比较符号:
```
#define min(a,b) ((a)-(b) & (0x1<<31))?(a):(b)
```
#### 把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列
分析:
```
char *integer_to_hex(long i); //eg: 20 => 14
char *integer_to_bin(long i); //eg: 20 => 10100
```
注意,转16进制中,要判断tmp[i]是否是有符号的数
```
tmp[i] = tmp[i]>=0 ? tmp[i] : tmp[i]+16;
```
#### 判断一个自然数是否是某个数的平方
说明:当然不能使用开方运算。
```
square(25) YES
square(35) NO
```
方法1: 从1开始遍历,显然这种方法很差
方法2: 除数跟余数比较,除数从2开始,每一轮都跟结果比较;相等就是存在这个数,不相等就把除数++;时间复杂度为(根号n)。在一个数比较大时,效率不够好。如1194877489 =(34567)^2,需要从2开始,一直比较到34566,做3万多次除法和比较运算。跟方法1一样。。
方法3:二分查找 O(logn)。比如25:
a. 先取(0+25)/2=12.5,12.5*12.5>25,因此这个数应该小于12.5
b.(0+12)/2 = 6, 6*6>25
c. (0+6)/2 = 3
d. (3+6)/2 = 4.5
e. (5+6)/2 = 5.5
```
int is_powered(int num)
```
#### 1024! 末尾有多少个0?
分析:
末尾0的个数取决于2和5的个数
能被2整除的数比能被5整除的数要多得多,因此只统计被5整除的数的个数
#### 编程实现两个正整数的除法(和取模)
编程实现两个正整数的除法,当然不能用除法操作符。
```
int div(const int x, const int y)
```
1. 循环减被除数,减到不能再减,当除数很大,被除数小时,效率很低
2. 位运算
#### 求两个或N个数的最大公约数和最小公倍数。
#### 大整数乘法(或 大整数阶乘)
请使用代码计算1234567891011121314151617181920*2019181716151413121110987654321 。
1. 注意结果可能超出长整形的最大范围 2^64-1
2. 采用分治算法,将大整数相乘转换为小整数计算
规律分析:任意位数的整数相乘,最终都可以转化为2位数相乘
#### 两个数相乘,小数点后位数没有限制,请写一个高精度算法
#### 数值的整数次方
题目:实现函数`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;
}
```
上面的代码没有考虑: exponent<=0
判断一个浮点数是不是等于0时,不是直接写 base == 0 ,而应该判断它们的差的绝对值是不是小于一个很小的范围
如果指数大于0,我们还可以使用递归实现:a^n = a^(n/2)*a^(n/2) (n为偶数), 通过这个思路也可以实现
```
2^16 = 2^8 * 2^8
2^8 = 2^4 * 2^4
2^4 = 2^2 * 2^2
2^2 = 2^1 * 2^1
2^1 = 2
```
用例:
```
2 3 = 8
0 3 = 0
2 0 = 1
2 -3 = 1/(2^3) = 0.125
0 -3
```
使用递归实现的代码示例:
```
double Power(double base, unsigned int exponent)
{
if (exponent == 0) return 1;
if (exponent == 1) return base;
double result = Power(base, exponent >> 1);
result *= result;
if (exponent & 1) result = result*base;
return reslut;
}
```
#### 求根号2的值
并且按照我的需要列出指定小数位,比如根号2是1.141 我要列出1位小数就是1.1 2位就是1.14, 1000位就是1.141...... 等。。
分析:
泰勒级数
牛顿迭代法
#### 整数的素数和分解问题
歌德巴赫猜想说任何一个不小于6的偶数都可以分解为两个奇素数之和。
对此问题扩展,如果一个整数能够表示成两个或多个素数之和,则得到一个素数和分解式。
对于一个给定的整数,输出所有这种素数和分解式。
注意,对于同构的分解只输出一次(比如5只有一个分解2 + 3,而3 + 2是2 + 3的同构分解式
例如,对于整数8,可以作为如下三种分解:
```
(1) 8 = 2 + 2 + 2 + 2
(2) 8 = 2 + 3 + 3
(3) 8 = 3 + 5
```
#### 大于K的最小正整数
给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含),指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数。
比如,A=[1,0] K=21 那么输出结构应该为100。
#### 给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数。
#### 设计一个随机算法
给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。
#### 构造一个随机发生器
已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器,
使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;...,
构造一个发生器,使得它构造1、2、3、...n的概率均为1/n,要求复杂度最低。
#### 两个圆相交
两个圆相交,交点是A1,A2。现在过A1点做一直线与两个圆分别相交另外一点B1,B2。B1B2可以绕着A1点旋转。问在什么情况下,B1B2最长
#### 输入四个点的坐标,求证四个点是不是一个矩形
关键点:
1.相邻两边斜率之积等于-1,
2.矩形边与坐标系平行的情况下,斜率无穷大不能用积判断。
3.输入四点可能不按顺序,需要对四点排序。
#### 排列组合问题
##### 1,2,3,4,5 五个不同的数字,打印不同的排列。这就是一个无向图的遍历,把每个数字看成一个节点。
##### 用1、2、2、3、4、5这六个数字,写一个main函数,打印出所有不同的排列,
如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连.
这是对上一题增加难度。但是需要
1. 去掉 3,5之间的联通
2. 2重复,过滤重复结果 treeset
3. 4不能在3位
@@ -0,0 +1,668 @@
这部分的问题都集中在数据集合上。主要有:
* 数组排序
* top-k
* 子数组
* 多个数组合并,交集
解决这一类问题时,可以从以下几个方面考虑:
* 万能的蛮力琼剧
* 散列表空间换事件
* 分治法,然后归并 (归并排序)
* 选择合适的数据结构可以显著提高算法效率 (堆排序求top-k)
* 对无序的数组先排序,使用二分
* 贪心算法或动态规划
#### 数组中超过出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
`+-1 计数法`
#### 找数组里面重复的一个数
找数组里面重复的一个数,一个含n个元素的整数数组至少存在一个重复数,请编程实现,在O(n)时间内找出其中任意一个重复数。
1. hash算法,空间要求多 (注意数组元素要是int类型),数的大小范围和数组长度N都可以是无穷大,这里使用hash算法,空间复杂度是O(n),空间可能无法满足条件。
2. 先排序,复杂度n(logn);然后遍历一遍就可以知道哪些数重复了。
2. 高级解法: 将问题转化为 `判断单链表中存在环`
类似问题:找出数组中唯一的重复元素
#### 数组中找出某数字出现的次数
在排序数组中,找出给定数字的出现次数。比如 [1, 2, 2, 2, 3] 中2的出现次数是3次。
分析:
1. 因为是排序数组,可以使用二分查找
2. 将二分查找坚持到底,这样在最坏的情况下([2,2,2,2,2,2,2])都有0(lgn)复杂度
```
int binary_search_first(int *a,int length,int key);
int binary_search_lash(int *a,int length,int key);
```
#### 查找最小的k个元素(top-k)
题目:输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
```
topMinK(int *a,int length,int k);
topMaxK(int *a,int length, int k);
```
1. 全部排序 复杂度 NlogN , 数据了较大时,内存可能承受不住
2. 部分排序 维护一个大小为K的数组,由大到小排序,然后遍历所有数据,每个数据跟数组中最小元素比较,如果比最小元素大,就要插入数组了,这里还有寻找插入位置,移动数组元素的cpu消耗。复杂度是N*K
3. 堆排序 。在这的K较大时(比如这道题目:2亿个整数中求最大的100万之和),上面的算法还是有很多可以改进的地方,如采用二分查找定位插入位置,移动数组元素的计算是躲不过去了。那有没有什么数据结构即能`快速查找,还能快速移动元素`呢?最好是O(1)复杂度。
答案就是`二叉堆`。我们可以遍历总量中的每个元素来跟二叉堆中的堆顶元素比较(堆顶元素在`小根堆`中最小值,在`大根堆`中是最大值),这样在0(1)复杂度就可以完成查找操作,揭下来需要的操作就是重新调整推结构,复杂度是O(logk),因此整个操作复杂度是 O(n*logk)
`top-k 小的时候用 *大根堆* ,top-k 大得时候用 *小根堆*`
#### 最长公共子序列 (动态规划的经典题目)
【最大/小差问题】
求相邻元素的最大差值,有无序的实数列V[N],要求求里面大小相邻的实数的差的最大值,关键是要求线性空间和线性时间
如 【9,-1,-11,2】 最大差值 = 2-(-11) = 13
最小差 hash合并
最大差 hash分解
桶排序
`桶排序` 比快排还快,最耗空间
#### 找出和为m的2个数
输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组【1、2、4、7、11、15=和数字15。由于4+11=15,因此输出4和11。
```
//返回0找到,返回-1没找到
int findaddends(int *data,int length,int sum,int *a,int *b);
```
分析:
数组升序排列,查找可用二分查找,时间复杂度O(logn),这样问题就变成了找其中一个加数的问题,复杂度为N*logN
巧妙解法:数组2端向中间扫描,复杂度O(N)
#### 和为m的组合
编程求解:输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来.
如: n=10,m=25;可能的组合有【】【】【】【】
```
print_sum_detials(int n , int sum)
```
`动态规划`(类似背包问题)
1. 划分问题
2. 选择状态
3. 状态转移方程
4. 规划方程
#### 若干个数的和与M最为接近
给定一个实数数组,按序排列(从小到大),从数组从找出若干个数,使得这若干个数的和与M最为接近,描述一个算法,并给出算法的复杂度。
#### 递减数列左移后的数组中找数
一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}
是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。
1. 右移,二分查找。 找到最小的数,右移到第一个位置的时候,右移完成 O(N)
2. 直接二分查找
#### 求子数组的最大和(最大字段和)
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为`1, -2, 3, 10, -4, 7, 2, -5` ,和最大的子数组为`3, 10, -4, 7, 2` ,因此输出为该子数组的和18。
1. 蛮力法 fmax(i,j) 找出最大的值,3重循环 ,复杂度 0(n^3)
2. maxendindhere保存当前累加的和,如果<0,就把maxendinghere清零 , max保存最终的最大和
```
int maxSumOfVector(int *data,int length){
int max=0;
int maxendinghere = 0;
if(data==NULL || length<=0){
return 0;
}
for(int i=0;i < length, i++){
maxendinghere+=data[i];
if(maxendinghere<0){
maxendinghere=0;
continue;
}
if(maxendinghere>max){
max=maxendinghere;
}
}
return max;
}
```
#### 给出一个洗牌算法
给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里
分析:扑克牌54张【2~10,J,Q,K,A,小王,大王】
1)产生随机数, 随机数 rand()%54 ,rand()每次运行都一样,要改为srand(time(NULL))
2) 遍历数组, 随机数k属于区间[i,n],然后a[i] 和 随机数 a[k] 对换
#### 重合区间最长的两个区间段
在一维坐标轴上有n个区间段,求重合区间最长的两个区间段
如【-7,21】,【4,23】,【14,100】,【54,76】
思路一:两两比较,复杂度 N^2
思路二:先排序+分而治之
#### 奇偶分离
给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。
要求:空间复杂度O(1),时间复杂度为O(n)
如 [4,5,2,7,5] => [5,7,5,4,2],
空间复杂度O(1),得使用 交换排序
插入排序思想
快速排序思想
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.
每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,
能否设计一个算法实现?
分析:难点在于 `不用辅助空间`。
思路一: sum(数组元素的总和)-sum(1~1000) 得到的差即为重复元素,N较大时注意总和溢出
思路二: 异或操作(位运算)
#### 找出数组中两个只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。
请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
分析:这是一道很新颖的关于位运算的面试题。
1) 任何一个数字异或自己都等于0,如果是只有1个只出现一次的的数字,问题就简单了。
2)
#### 用递归的方法判断整数组a[N]是不是升序排列
递归 isAscend(n-1) && a[N-1]< a[N]
在一个int数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。
直观想法是用两个数组a、b。a[i]、b[i]分别保存从前到i的最大的数和从后到i的最小的数,
一个解答:这需要两次遍历,然后再遍历一次原数组,
将所有data[i]>=a[i-1]&&data[i]<=b[i]的data[i]找出即可。
给出这个解答后,面试官有要求只能用一个辅助数组,且要求少遍历一次。
一排N(最大1M)个正整数+1递增,乱序排列,第一个不是最小的,把它换成-1,
最小数为a且未知。求第一个被-1替换掉的数原来的值,并分析算法复杂度。
[4,3,5,2,7,6]
题目啥意思?
正整数序列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)、设计测试数据来验证函数程序在各种输入下的正确性。
分析:
这个输出序列是要 递增排列
思路一: 类似对2各数组merge,取min( A[i],B[j]) .复杂度O(N)
不过这里要注意,去掉 a, b的公倍数。如 3,5 都有15可以整除
#### 扑克牌的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。
2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。
对这5个数(大小王看做0)
1)排序,如快排
2)统计0的个数
3)统计相邻元素空缺总数
```
//5个是不是顺子
isShunZi(int *a,int length)
```
#### n个骰子的点数
把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。
输入n,打印出S的所有可能的值出现的概率。
如n=1,值【1,2,3,4,5,6】每个概率是 1/6
如n=2,值【1~12】 概率。。
#### 把数组排成最小的数
题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。
例如输入数组{32, 321},则输出这两个能排成的最小数字32132。
请给出解决问题的算法,并证明该算法。
#### 旋转数组中的最小元素。
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,
时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。
#### 分割数组
一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。
能否只用一个额外数组和少量其它空间实现。
`快速排序`
#### 约瑟夫环问题
n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,
每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。
当一个数字删除后,从被删除数字的下一个继续删除第m个数字。
求出在这个圆圈中剩下的最后一个数字。
如 0,1,2,3,4,5 删除第2个数字 (n=6,m=2)
第一次删除:1
第二次删除: 3
第三次删除:5
第四次删除:2
因此,左后的一个数字就是 4
从数学上分析下规律:
#### 圆形和正方形是否相交
用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交
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, *.*)
分析:
2个形状不相交:
#### 两个序列和只差最小
有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
例如:
```
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];
```
#### 重新排列使负数排在正数前面
一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序
比如: 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。
#### 求最大重叠区间大小
题目描述:请编写程序,找出下面“输入数据及格式”中所描述的输入数据文件中最大重叠区间的大小。
对一个正整数 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 。
#### 四对括号可以有多少种匹配排列方式
比如两对括号可以有两种:()()和(())
#### 输出1到最大的N位数
题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。
分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。
输入4,输出: 1,2,3,。。9999
输入5,输出: 1,2,3,4,...99999
玄机一: 整数溢出
#### 和为n连续正数序列
题目:输入一个正数n,输出所有和为n连续正数序列。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。
print_continuous_sequence_sum(int n)
思路一:枚举法。从1开始一直加到等于n,再从2开始。。一直到 n/2+1,在每一轮,都要逐步比较。复杂度O(N^2)
思路二: a[small,big] sum[small,big]>N small往前移动,否则,big往前移动。 O(N)复杂度搞定
### 找出和为N+1的2个数
一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。
设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。
复杂度最好是O(n),如果是O(n2)则不得分。
分析:列出所有的数对,如输入15,输出【1,14】【2,13】【3,12】。。。
```
int print_sequence_sum(int n)
```
#### 寻找丑数
题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。
例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。
求按从小到大的顺序的第1500个丑数。
分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。
这里的因子应该不包含本身,因此这个序列应该是这样:
1,2,3,4,5,6,8,9,10,12,15,16,18,20,28....
1)所有的偶数都在序列中
2)3的倍数也在序列中
3)5的倍数也在系列中
0. 2,3,5最小公倍数是30
1. [1,30]符合条件有22个
2. [30,60]符合条件也22个
第1500个: 1500/22=68余4,一个周期内的前4个数是2,3,4,5; 最终答案是`68*30+5`
#### 调整数组顺序使奇数位于偶数前面
题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,
所有偶数位于数组的后半部分。要求时间复杂度为O(n)。
思路:两边向中间扫描,如果第一个指针是偶数,第二个指针是奇数,就交换;如果第一个是偶数,第二个也偶数,第二个指针向前移;反之,第一个指针向后移
```
void reorder(int *data, int length);
```
#### 在从1到n的正数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
分析:这是一道广为流传的google面试题。
```
int one_appear_count(int n);
```
思路1. 遍历1~n,统计出现1的个数;n足够大时,效率很低
思路2. 分析规律
#### 求一个数组的最长递减子序列
比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}
`动态规划`
#### 如何求2个集合的交集
1.每个集合里面是否有重复元素?
思路一:hash,复杂度O(M+N)
#### 两两之差绝对值最小的值
有一个整数数组,请求出两两之差绝对值最小的值,记住,只要得出最小值即可,不需要求出是哪两个数。
如 [-1, 3, 5, 9] 绝对值最小的是2(5-3)
最短区间问题
如果是有重复元素,那最小值就是0了
解题思路: 可以将这个问题转化为 ”求最大字段和“ 问题。。。
#### 数值是否连续相邻
一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。
注意:
- 5个数值允许是乱序的。比如: 8 7 5 0 6
- 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4
- 0可以多次出现。
- 复杂度如果是O(n2)则不得分。
这个问题跟”扑克牌顺子“判断问题一样,通过比较0的个数和相邻数字之间间隔总和来判断所有数是否连续。
#### 最长递增子序列
题目描述:设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列
Lin=< aK1,ak2,…,akm >,其中k1< k2<…< km且aK1< ak2<…< akm。
求最大的m值。
如【5,6,7,3,2,8】 最长子序列 【5,6,7,8】
`动态规划`
#### Fibonacci数列
题目:定义Fibonacci数列如下:
/ 0 n=0
f(n)= 1 n=1
/ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项。
虽然fibonacci数列是`递归`的经典应用,但递归效率很差,会有很多重复的计算,复杂度是成指数递增的,我测试了下计算50的时候已经要300s了。
思路二:从下往上计算,复杂度O(N),一个循环就搞定
思路三: logN
#### 一个整数数组,长度为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
////////////// 数列 ///////////////
输入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。
给你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次....
以此类推..
0xX0+1xX1+2xX2....+9xX9 = 10;
X0+X1+...+ = 10;
转换成多元一次方程求通解的问题。(Matlib)
给出两个集合A和B,其中集合A={name},
集合B={age、sex、scholarship、address、...},
要求:
问题1、根据集合A中的name查询出集合B中对应的属性信息;
问题2、根据集合B中的属性信息(单个属性,如age<20等),查询出集合A中对应的name。
@@ -0,0 +1,250 @@
这部分内容的题目侧重思维发散。
1.有两个房间,一间房里有三盏灯,另一间房有控制着三盏灯的三个开关,这两个房间是 分割开的,从一间里不能看到另一间的情况。
现在要求受训者分别进这两房间一次,然后判断出这三盏灯分别是由哪个开关控制的。
有什么办法呢?
2.你让一些人为你工作了七天,你要用一根金条作为报酬。金条被分成七小块,每天给出一块。如果你只能将金条切割两次,你怎样分给这些工人?
有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌,
A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,
A说不知道,B说不知道,C说不知道,然后A说知道了。
请教如何推理,A是怎么知道的。
如果用程序,又怎么实现呢?
有A、B、C、D四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在17分钟内这四个人都过桥?
有12个小球,外形相同,其中一个小球的质量与其他11个不同,给一个天平,问如何用3次把这个小球找出来,并且求出这个小球是比其他的轻还是重
“十袋白色粉末,其中有一袋溶于水两分钟以后会变蓝,现在只有四个杯子,无限多的水,要求是在最短的时间内找出这袋特殊的粉末”
一二三四号粉末放第1个杯子,二三四号粉末分别放在第234个杯子里,五六七号粉末放到2号杯子里,六七号粉末分别放到34号杯子里,八九号粉末放到第3个杯子里,九十号粉末放到第4个杯子里,OK了”
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.求固晶机的晶元查找程序
晶元盘由数目不详的大小一样的晶元组成,晶元并不一定全布满晶元盘,
照相机每次这能匹配一个晶元,如匹配过,则拾取该晶元,
若匹配不过,照相机则按测好的晶元间距移到下一个位置。
求遍历晶元盘的算法 求思路。
@@ -0,0 +1,37 @@
#include "stdio.h"
#include "string.h"
int string_to_integer(char *s,int length){
if (length>1)
{
return s[0]=='-'?string_to_integer(s,length-1)*10-(s[length-1]-'0'):string_to_integer(s,length-1)*10+s[length-1]-'0';
}else{
return s[0]=='-'?-1/10:s[0]-'0';
}
}
/* 问题:
1. s是空字符串
2. s传参为非数字字符
3. 超出整数所能表示范围
*/
int main(){
char *s = "4323";
printf("integer == %d\n",string_to_integer(s,strlen(s)) );
printf("integer == %d\n",string_to_integer("12342",5));
printf("integer == %d\n",string_to_integer("-11",3) );
printf("integer == %d\n",string_to_integer("-5",2) );
printf("integer == %d\n", string_to_integer("3",1));
printf("integer == %d\n", string_to_integer("abcd",4));
printf("integer == %d\n", string_to_integer("a",1));
return 0;
}
@@ -0,0 +1,47 @@
#include "stdio.h"
void generate(int a,int b,int N ,int * Q){
int p=1,q=1;
int tmpA,tmpB;
for (int i = 0; i < N; ++i)
{
tmpA = a*p;
tmpB = b*q;
if (tmpA>tmpB)
{
Q[i]=tmpB;
q++;
}
else if(tmpA<tmpB)
{
Q[i]=tmpA;
p++;
}
else{// ==
Q[i]=tmpA;
p++;
q++;
}
}
}
int main(int argc, char const *argv[])
{
int a[10];
generate(3,5,10,a);
for (int i = 0; i < 10; ++i)
{
printf(" %d \n", a[i]);
}
return 0;
}
@@ -0,0 +1,47 @@
#include "stdio.h"
void print_continuous_sequence_sum(int n){// n=1,2
int small=1;
int big=2;
int sum = small+big;
while(small<big && big<=n/2+1){
if (sum==n)
{
printf("%d ,%d\n",small,big);
}
while(sum>n){
sum-=small;
small++;
if (sum==n)
{
printf("%d, %d\n",small,big);
}
}
big++;
sum+=big;
}
}
int main(int argc, char const *argv[])
{
print_continuous_sequence_sum(115);
return 0;
}
@@ -0,0 +1,55 @@
#include "stdio.h"
void reorder(int *data, int length){
int i=0;
int j=length-1;
while(i<j){
if (!isEvenNum(data[i]) )
{
i++;
continue;
}
if (isEvenNum(data[j]) )
{
j--;
continue;
}
//交换
int tmp ;
tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
}
int isEvenNum(int n){
return (n&1) == 0;
}
int main(int argc, char const *argv[])
{
int a[]= {5,2,7,8,9,34,21,6};
reorder(a,8);
for (int i = 0; i < 8; ++i)
{
printf(" %d ", a[i]);
}
return 0;
}
@@ -0,0 +1,23 @@
#include "stdio.h"
int factorial(int n){
int ret=0;
(n==0) || (ret=n+factorial(n-1));
return ret;
}
int main(){
int n=10;
printf("factorial(10)= %d\n",factorial(10));
printf("factorial(5)= %d\n",factorial(5));
printf("factorial(1)= %d\n",factorial(1));
printf("factorial(0)= %d\n",factorial(0));
return 0;
}
@@ -0,0 +1,13 @@
#include <stdlib.h>
#include <stdio.h>
#define PrintInt(expr) printf("%s : %d\n",#expr,(expr))
int main()
{
int y = 100;
int *p;
p = malloc(sizeof(int));
*p = 10;
y = y / *p;
PrintInt(y);
return 0;
}
@@ -0,0 +1,9 @@
#include <stdio.h>
int main()
{
int day,month,year;
printf("Enter the date (dd-mm-yyyy) format including -'s:");
scanf("%d-%d-%d",&day,&month,&year);
printf("The date you have entered is %d-%d-%d\n",day,month,year);
return 0;
}
@@ -0,0 +1,10 @@
#include <stdio.h>
int main()
{
int n;
printf("Enter a number:\n");
scanf("%d",&n); // &
printf("You entered %d \n",n);
return 0;
}
@@ -0,0 +1,16 @@
#include <stdio.h>
int main()
{
int cnt = 5, a;
do {
a /= cnt;
} while (cnt --);
printf ("%d\n", a);
return 0;
}
/// cnt==1 时,会发生除0错误
@@ -0,0 +1,11 @@
#include <stdio.h>
int main()
{
int i = 6;
if( ((++i < 7) && ( i++/6)) || (++i <= 9))
;
printf("%d\n",i);
return 0;
}
// && 优先级大于 ||
@@ -0,0 +1,18 @@
#include <stdlib.h>
#include <stdio.h>
#define SIZE 15
int main()
{
int *a, i;
a = malloc(SIZE*sizeof(int));
for (i=0; i<SIZE; i++)
*(a + i) = i * i;
for (i=0; i<SIZE; i++)
printf("%d\n", *a++);
free(a);
return 0;
}
// 指针已经移动,free错误
@@ -0,0 +1,17 @@
// FROM:http://www.gowrikumar.com/c/
#include <stdio.h>
int main()
{
char dummy[80];
printf("Enter a string:\n");
scanf("%[^a]",dummy);
printf("%s\n",dummy);
return 0;
}
/**
输入一个串,以字符'a'结尾
*/
@@ -0,0 +1,21 @@
#include <stdio.h>
int main()
{
int a=3, b = 5;
printf(&a["Ya!Hello! how is this? %s\n"], &b["junk/super"]);
//printf(3["helloworld \n"]);
printf("%c\n", 0["this"] );
printf(&a["WHAT%c%c%c %c%c %c !\n"], 1["this"],
2["beauty"],0["tool"],0["is"],3["sensitive"],4["CCCCCC"]);
return 0;
}
/*
*/
@@ -0,0 +1,10 @@
#include <stdlib.h>
#include <stdio.h>
int main()
{
int* ptr1,*ptr2;
ptr1 = malloc(sizeof(int));
ptr2 = ptr1;
*ptr2 = 10;
return 0;
}
@@ -0,0 +1,71 @@
#include "stdio.h"
#include "stdlib.h"
char *integer_to_bin(long integer){
int bit_num = sizeof(long)*8;
char *buffer= (char *)malloc(bit_num+1);
if (buffer==NULL)
{
return "malloc error";
}
buffer[bit_num]='\0';
for (int i = 0; i < bit_num; ++i)
{
buffer[i]= integer<<i>>(bit_num-1);
buffer[i]= buffer[i]==0?'0':'1';
}
return buffer;
}
char *integer_to_hex(long integer){
int bit_num=sizeof(long)*8/4;
char *buffer=(char *)malloc(bit_num+3);
buffer[0]='0';
buffer[1]='x';
buffer[bit_num+2]='\0';
char *tmp=&buffer[2];
for (int i = 0; i < bit_num; i++)
{
tmp[i]= integer<<(i*4)>>(bit_num*4-4);
tmp[i]= tmp[i]>=0?tmp[i]:tmp[i]+16;
tmp[i]= tmp[i]<10?(tmp[i]+48):(tmp[i]-10+'A');
}
return buffer;
}
/*
A 65
0 48
*/
int main(){
printf("b(11) = %s\n", integer_to_bin(11));
printf("b(1023) = %s\n",integer_to_bin(1023) );
printf("b(23564) = %s\n",integer_to_bin(23564) );
printf("h(11) = %s\n", integer_to_hex(11));
printf("h(1023) = %s\n",integer_to_hex(1023) );
printf("h(23564) = %s\n",integer_to_hex(23564) );
printf("b(11) = %x\n", 11);
printf("b(1023) = %x\n",1023 );
printf("b(23564) = %x\n",23564 );
return 0;
}
@@ -0,0 +1,7 @@
#include "stdio.h"
int main(){
printf("%ld",236432123443*33453098);
return 0;
}
@@ -0,0 +1,25 @@
#include "stdio.h"
/*
1 是第一个元素
序列中元素被2或3或5整除
*/
unsigned long value_in_sequence(unsigned index){
}
int main(){
return 0;
}
@@ -0,0 +1,70 @@
#include "stdio.h"
int binary_search_first(int *a,int length,int key){
int low=0;
int high = length-1;
int mid = 0;
while(low<high){
mid = (low+high)/2;
if (a[mid]>=key)//往左找
{
high = mid;
}else{
low = mid+1;
}
}
return high;
}
int binary_search_last(int *a,int length,int key){
int low = 0;
int high = length -1;
int mid = 0;
while(low<high){
mid = (low+high+1)/2;
if (a[mid]<=key)// 往右找
{
low=mid;
}else{
high = mid-1;
}
}
return low;
}
int element_appear_times(int *a,int length,int key){
printf("(%d) first appear at index %d\n",key,binary_search_first(a,length,key));
printf("(%d) last appear at index %d\n",key,binary_search_last(a,length,key));
return binary_search_last(a,length,key)-binary_search_first(a,length,key)+1;
}
int main(){
int a[] = {2,3,4,4,4,4,4,5,5,7,7,11,11,11,11,11,32,32,54};
printf("(2)appear times = %d\n", element_appear_times(a,sizeof(a)/sizeof(int),2) );
printf("(4)appear times = %d\n", element_appear_times(a,sizeof(a)/sizeof(int),4) );
printf("(11)appear times = %d\n", element_appear_times(a,sizeof(a)/sizeof(int),11) );
printf("(36)appear times = %d\n", element_appear_times(a,sizeof(a)/sizeof(int),36) );
printf("(54)appear times = %d\n", element_appear_times(a,sizeof(a)/sizeof(int),54) );
return 0;
}
@@ -0,0 +1,14 @@
#include "stdio.h"
int main(){
int j,k,m;
int i= (j=4,k=5,m=8);
printf("%d\n", i);
i= (j==4)?:0;
printf("i=%d\n", i);
return 0;
}
@@ -0,0 +1,11 @@
#include "stdio.h"
int main(){
int a = 257;
unsigned char *result = (unsigned char *)&a;
printf("%d.%d.%d.%d\n", result[0],result[1],result[2],result[3] );
}
@@ -0,0 +1,53 @@
#include "stdio.h"
int isSquare(unsigned integer){
if (integer==1 || integer==0)
{
return integer;
}
int divider=2,result=integer/divider;
while(result>divider){
divider++;
result = integer/divider;
}
if (result!=divider)
{
return -1;
}
return result;
}
int main(int argc, char const *argv[])
{
// 243,1849(43),289(17), 64(8)
int a[] = {1849,144,243,289,64,1194877489};
for (int i = 0; i < sizeof(a)/sizeof(int); ++i)
{
int ret = isSquare(a[i]);
if (ret==-1)
{
printf("%d 不是某个数的平方\n",a[i]);
}else{
printf("%d 是 %d 的平方\n", a[i], ret);
}
}
return 0;
}
@@ -0,0 +1,23 @@
#include "stdio.h"
double Power(double base, int exponent)
{
if (exponent == 0) return 1;
if (exponent == 1) return base;
double result = Power(base, exponent >> 1);
result *= result;
if (exponent & 1) result = result*base;
return result;
}
int main(int argc, char const *argv[])
{
printf("%f\n", Power(2,4));
printf("%f\n", Power(2.1,4));
printf("%f\n", Power(0,4));
printf("%f\n", Power(2,0));
printf("%f\n", Power(2,-3)); //负数就挂了
return 0;
}
@@ -0,0 +1,27 @@
/*
二叉查找树变成双向链表
*/
strcut BSTreeNode{
int m_nValue;
BSTreeNode *m_pLeft,m_pRight;
}BSTree;
void convertDoubleLinks(BSTree *root){
}
int main(int argc, char const *argv[])
{
return 0;
}
@@ -0,0 +1,23 @@
// FROM:http://www.gowrikumar.com/c/
#include<stdio.h>
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23,34,12,17,204,99,16};
int main()
{
int d;
int n = TOTAL_ELEMENTS-2;
for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
printf("%d\n",array[d+1]);
return 0;
}
/*
d 是有符号数,sizeof()计算为无符号数,比较时d会转为无符号数,变成一个大数
*/
@@ -0,0 +1,10 @@
#include <stdio.h>
int main()
{
int i=43;
printf("%d\n",printf("%d",printf("%d",i)));
return 0;
}
// printf 返回值是打印数据的长度 4321
@@ -0,0 +1,20 @@
#include <stdio.h>
void foobar1(void)
{
printf("In foobar1\n");
}
void foobar2()
{
printf("In foobar2\n");
}
int main()
{
char ch = 'a';
foobar1(33, ch);
foobar2();
return 0;
}
// compile error
@@ -0,0 +1,18 @@
#include <stdio.h>
void foobar1(void)
{
printf("In foobar1\n");
}
void foobar2()
{
printf("In foobar2\n");
}
int main()
{
char ch = 'a';
foobar1();
foobar2(33, ch);
return 0;
}
@@ -0,0 +1,16 @@
// FROM:http://www.gowrikumar.com/c/
#include <stdio.h>
int main()
{
float a = 12.5;
printf("%d\n", a);
//printf("%d\n", *(int *)&a);
return 0;
}
// 浮点数 转 整形 ,用强制转换(int)a
/*
浮点数的存储
*/
@@ -0,0 +1 @@
int arr[80];
@@ -0,0 +1,19 @@
#include "c13-1.c"
extern int *arr;
int main()
{
arr[1] = 100;
return 0;
}
/**
类型并不匹配,所以导致这里的arr并没有指向c13-1.c中声明的arr[80],应该修改为 extern int arr[]
int *arr 和 int arr[80] 区别?
*/
@@ -0,0 +1,16 @@
#include<stdio.h>
int main()
{
int a=1;
switch(a)
{ int b=20;
case 1: printf("b is %d\n",b);
break;
default:printf("b is %d\n",b);
break;
}
return 0;
}
// In my computer ,output:b is 32767 switch中只会从case开始执行,b会随机输出
@@ -0,0 +1,17 @@
#include <stdio.h>
#define SIZE 10
void size(int arr[SIZE])
{
printf("size of array is:%d\n",sizeof(arr));
}
int main()
{
int arr[SIZE];
size(arr);
return 0;
}
// 32位机器 4, 64位机器 8
@@ -0,0 +1,38 @@
// FROM:http://www.gowrikumar.com/c/
#include <stdlib.h>
#include <stdio.h>
void Error(char* s)
{
printf(s);
return;
}
int main()
{
int *p;
p = malloc(sizeof(int));
if(p == NULL)
{
Error("Could not allocate the memory\n");
Error("Quitting....\n");
exit(1);
}
else
{
/*some stuff to use p*/
Error("Could not allocate the memory\n");
Error("Quitting....\n");
Error(5);
}
return 0;
}
/*
潜在的问题是:
Error函数中,如果输入的参数不是字符串,比如传一个整数5,5被转成一个内存地址,printf这时候访问就会出问题了。
$1 = 0x5 <error: Cannot access memory at address 0x5>
*/
@@ -0,0 +1,27 @@
// FROM:http://www.gowrikumar.com/c/
#include <stdio.h>
int main()
{
char c;
scanf("%c",&c);
printf("%c\n",c);
scanf(" %c",&c);
printf("%c\n",c);
return 0;
}
/*
scanf("%c",&c) 和 scanf(" %c",&c) 区别
使用第二个scanf("%c",&c) 时,系统会将前一个scanf()输入的回车符号读入改变量。
这里为什么加一个“空格”就可以?
scanf带“空格”后,会从输入缓冲区中skip 空白符(空格、tab,换行符),读取一个字符
*/
@@ -0,0 +1,12 @@
#include <stdio.h>
int main()
{
int i;
i = 10;
printf("i : %d\n",i);
printf("sizeof(i++) is: %d\n",sizeof(i++));
printf("i : %d\n",i);
return 0;
}
// sizeof()不会对传入的表达式计算 ,所以后面的输入还是i:10
@@ -0,0 +1,23 @@
#include <stdio.h>
#include <stdlib.h>
#define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))
#define PrintInt(expr) printf("%s:%d\n",#expr,(expr))
int main()
{
/* The powers of 10 */
int pot[] = {
0001,
0010,
0100,
1000
};
int i;
for(i=0;i<SIZEOF(pot);i++)
PrintInt(pot[i]);
return 0;
}
// 以0大头的都是8进制数据
@@ -0,0 +1,40 @@
#include<stdio.h>
void OS_Solaris_print()
{
printf("Solaris - Sun Microsystems\n");
}
void OS_Windows_print()
{
printf("Windows - Microsoft\n");
}
void OS_HP_UX_print()
{
printf("HP-UX - Hewlett Packard\n");
}
int main()
{
int num;
printf("Enter the number (1-3):\n");
scanf("%d",&num);
switch(num)
{
case 1:
OS_Solaris_print();
break;
case 2:
OS_Windows_print();
break;
case 3:
OS_HP_UX_print();
break;
default:
printf("Hmm! only 1-3 :-)\n");
break;
}
return 0;
}
@@ -0,0 +1,9 @@
int main()
{
char ch1;
char ch2;
ch1 = getchar();
ch2 = getchar();
printf("%d %d", ch1, ch2);
return 0;
}
@@ -0,0 +1,16 @@
#include <stdio.h>
enum {false,true};
int main()
{
int i=1;
do
{
printf("%d\n",i);
i++;
if(i < 15)
continue;
}while(false);
return 0;
}
@@ -0,0 +1,24 @@
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
fprintf(stdout,"hello-out");
fprintf(stderr,"hello-err");
sleep(1);
}
return 0;
}
/*
stdout 会缓冲输出
\n
fflush()
setbuf(stdout,NULL)
*/
@@ -0,0 +1,15 @@
// FROM:http://www.gowrikumar.com/c/
#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
int main()
{
printf("%s\n",h(f(1,2))); //
printf("%s\n",g(f(1,2))); // f(1,2)
return 0;
}
@@ -0,0 +1,20 @@
#include<stdio.h>
int main()
{
int a=10;
switch(a)
{
case '1':
printf("ONE\n");
break;
case '2':
printf("TWO\n");
break;
a:
printf("NONE\n");
}
return 0;
}
//什么也不输出, “default”这里随便什么都没有编译错误
@@ -0,0 +1,8 @@
#include <stdlib.h>
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
@@ -0,0 +1,20 @@
#include <stdio.h>
int main()
{
float f=0.0f;
int i;
for(i=0;i<10;i++)
f = f + 0.1f;
if(f == 1.0f)
printf("f is 1.0 \n");
else
printf("f is NOT 1.0\n");
return 0;
}
// 浮点数判相等
@@ -0,0 +1,13 @@
#include <stdio.h>
int main()
{
int a = (1,2);
printf("a : %d\n",a);
return 0;
}
// ,运算符优先级 最低 ,括号不能少
//
@@ -0,0 +1,28 @@
#include "stdio.h"
char char_first_appear_once(const unsigned char *source){
int hash[256]={0};
char *tmp = source;
if (tmp == NULL) return '\0';
while(*tmp != '\0'){
hash[*tmp]++;
tmp++;
}
tmp = source;
while(*tmp != '\0'){
if (hash[*tmp] == 1) return *tmp;
tmp++;
}
return '\0';
}
int main(int argc, char const *argv[])
{
char *test = "77ah-ba-ccdeff";
char c = char_first_appear_once(test);
printf("c=%c\n", c);/* c = 'h'*/
return 0;
}
@@ -0,0 +1,22 @@
#include "stdio.h"
char *delete_occurence_character(char *src , char target){
char *front = src;
char *rear = src;
while(*front != '\0'){
if (*front != target){
*rear = *front;
rear++;
}
front++;
}
*rear = '\0';
return src;
}
int main(int argc, char const *argv[])
{
char test[] = "abcdeccba";
printf("%s\n", delete_occurence_character(test,'c'));
return 0;
}
@@ -0,0 +1,57 @@
#include "stdio.h"
#include "time.h"
int fibonacci(int n){
int result[2] = {0,1};
if (n<2)
{
return result[n];
}
return fibonacci(n-1)+fibonacci(n-2);
}
// 循环
int fibonacci2(int n){
int result[2] = {0,1};
if ( n<2 )
{
return result[n];
}
int fibOne=0;
int fibTwo=1;
int fibN;
for (int i = 2; i < n; ++i)
{
fibN = fibOne + fibTwo;
fibOne = fibTwo;
fibTwo = fibN;
}
return fibN;
}
int main(){
int start,end;
long result;
start = clock();
result=fibonacci2(40);
end = clock();
printf("%ld %.6fs",result,(double)(end-start)/CLOCKS_PER_SEC);
return 0;
}
@@ -0,0 +1,56 @@
#include "stdio.h"
int longest_continuious_number(const char *input,char *output){
int max = 0;
char *start= input;
char *mid = input;
char *end = input;
int tmp = 0;
if (input == NULL || output == NULL) return 0;
while (*end != '\0'){
if (*end < '0' || *end > '9'){//字母
if(tmp > max){
max = tmp;
start = mid;
}
tmp = 0;
}else{//数字
if (tmp == 0){//发现数字
mid = end;
}
tmp++;
}
end++;
}
//修改已数字结尾的bug
if(tmp > max){
max = tmp;
start = mid;
}
//copy
int i=0;
while(i<max){
*(output+i) = *(start+i);
i++;
}
*(output+i)='\0';
return max;
}
int main(int argc, char const *argv[])
{
const char test[] = "abcd12345ed125ss123456789";
char output[100] = {};
int max = longest_continuious_number(test,output);
printf("max=%d,number=%s\n", max,output);
return 0;
}
@@ -0,0 +1,135 @@
#include "stdio.h" // fwrite
#include "unistd.h" // write
#include "stdlib.h" // itoa
#include "string.h"
#define test_file_path2 "./ip2.txt"
#define test_file_path "./ip.txt"
#define ip_count 100000000 //随机1亿个IP
#define tmp_file_count 32
#define mem_count 128*1024*1028 //128MB个IP空间
int hash(unsigned i){
return i>>27;
}
int main(){
// 模拟创建一个文件,产生测试用的IP
// FILE *fd;
// fd = fopen(tmp_file_path,"a+");
// for (int i = 0; i < ip_count;++i)
// {
// //char c[] = "this is sentence!";
// unsigned random_ip = (unsigned)rand();
// char tmp[50];
// sprintf(tmp,"%d",random_ip);
// //printf("random ip = %s\n",tmp);
// fwrite(tmp,strlen(tmp),1,fd);
// fputc('\n',fd);//加入换行符号
// }
//创建临时文件
FILE *fd[tmp_file_count];
for (int i = 0; i < tmp_file_count; ++i)
{
char file_path[128];
//s[0]=(char)(i+48);
//char *file_path = strcat("./ipslice/ipslice",s) ; // 这样写出现 bus error!!!
sprintf(file_path,"./ipslice/ipslice%d",i);
fd[i] = fopen(file_path ,"a+" );
if ( !fd[i])
{
printf("file %d open fail!! \n" ,i);
}
}
//开始读测试数据IP,按IP,映射到32个文件中
FILE *testfd = fopen(test_file_path2,"r");
if (!testfd)
{
printf("file open fail !!\n");
exit(-1);
}
unsigned tmp_data;
while( getline() ){ //能读到数据 // fread(&tmp_data,sizeof(unsigned),1,testfd) , C语言中没有getline() 这样的函数
printf("%d\n",(unsigned)tmp_data );
int key = hash((unsigned)tmp_data);// key值即为对应文件fd
int size = fwrite(&tmp_data,sizeof(unsigned),1,fd[key]);
printf("写入数据size=%d ,key=%d\n",size, key );
//跳过换行符
fseek(testfd,1,SEEK_CUR);
}
// 依次读入每个文件并统计, hash_map 统计每个区间段的最大IP
int hash_map[mem_count];
int max_ip;
int max_times;
for (int i = 0; i < tmp_file_count; ++i)
{
// hash统计
while(){}
//hash 表里找出最大的,变量一遍
for (int i = 0; i < mem_count; ++i)
{
/* code */
}
}
// 释放
for (int i = 0; i < tmp_file_count; ++i)
{
fclose(fd[i]);
}
return 0;
}
/**
关于warning
implicit declaration of function 'itoa' is invalid in C99
itoa() 函数并不是一个 standard functions ANSI C standard
inet_ntop() // 整数转换成 .分 ip地址 字符串
inet_nton() //
inet_aton() //
struct in_addr
*/
@@ -0,0 +1,18 @@
#include "stdio.h"
int one_appear_count_by_binary(int num){
int count = 0;
while(num !=0 ){
num &= num-1;
count++;
}
return count;
}
int main(int argc, char const *argv[])
{
int test = 10;
printf("%d\n", one_appear_count_by_binary(test));
printf("%d\n", one_appear_count_by_binary(32+1));
return 0;
}
@@ -0,0 +1,43 @@
#include "stdio.h"
// a[i*rows+j]
void print_matrix_incircle(int *matrix,int rows,int cols,int start){
int endX = cols-start-1;
int endY = rows-start-1;
//然后依次打印 上,右,下,左
for(int i=start;i<=endX;i++){
printf("%d ",matrix[start*rows+i]);
}
for(int i=start+1;i<=endY;i++){
printf("%d ",matrix[i*rows+endX]);
}
for(int i=endX-1;i>=start;i--){
printf("%d ",matrix[endY*rows+i]);
}
for(int i=endY-1;i>start;i--){
printf("%d ",matrix[i*rows+start]);
}
}
void print_matrix(int *matrix,int rows,int cols){
if(matrix==NULL) return ;
if(rows<=0 || cols<=0) return;
int start = 0;
while(start*2<cols && start*2<rows){
print_matrix_incircle(matrix,rows,cols,start);
start++;
}
}
int main(int argc, char const *argv[])
{
int matrix[] = {1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16};
print_matrix(matrix,4,4);
return 0;
}
@@ -0,0 +1,35 @@
#include "stdio.h"
char *proc(char *str){
char *start = str;
char *end = str;
if (str == NULL) return NULL;
while(*end != '\0') end++;
end--;
while(start < end){
if (*start >= 'A' && *start <= 'Z'){//大写
if (*end >= 'a' && *end <= 'z'){
char tmp = *start;
*start = *end;
*end = tmp;
start++;
}
end--;
}else{//小写
if (*end >= 'A' && *end <= 'Z'){
end--;
}
start++;
}
}
return str;
}
int main(int argc, char const *argv[])
{
char test[] = "HaJKPnobAACPc";//"char *test" always make "28767 bus error"
printf("%s\n", proc(test));
return 0;
}
@@ -0,0 +1,32 @@
#include "stdio.h"
char *replace_blank(char *source){
int count = 0;
char *tail = source;
if (source == NULL) return NULL;
while(*tail != '\0'){
if (*tail == ' ') count++;
tail++;
}
while(count){
if(*tail != ' '){
*(tail+2*count) = *tail;
}else{
*(tail+2*count) = '0';
*(tail+2*count-1) = '2';
*(tail+2*count-2) = '%';
count--;
}
tail--;
}
return source;
}
int main(int argc, char const *argv[])
{
char str[100]="we are happy";
printf("ret=%s\n",replace_blank(str));
return 0;
}
@@ -0,0 +1,57 @@
#include "stdio.h"
void _reverse(char *start,char *end){
if ((start == NULL) || (end == NULL)) return;
while(start < end){
char tmp = *start;
*start = *end;
*end = tmp;
start++,
end--;
}
}
char *_revert_by_word(char *source){
char *end = source;
char *start = source;
while(*start != '\0'){
if (*start == ' '){
start++;
end++;
}else if(*end == ' ' || *end == '\0'){
_reverse(start,end-1);
start = end;
}else{
end++;
}
}
return source;
}
char *revert_by_word(char *source){
char *end = source;
char *start = source;
if (source == NULL) return NULL;
while (*end != '\0') end++;
end--;
/*整个句子逆序*/
_reverse(start,end);
/*按单词逆序.tneduts a ma I */
_revert_by_word(source);
return source;
}
//为啥运行时 bus error?
int main(int argc, char const *argv[])
{
char *test = "how are you ?";
printf("%s\n",_revert_by_word(test));
//char *test = "I am a student.";
//printf("%s\n",revert_by_word(test));
return 0;
}
@@ -0,0 +1,429 @@
#include "stdio.h"
typedef int BOOL;
#define YES 1
#define NO 0
// 反序
void revert(const char *source,char *dest){
const char *s = source;
char *end = dest;
while(*s){
*end++ = *s++; //先拷贝字符串
}
*end='\0';
end--;// 指向最后一个字符
while(end>dest){
char tmp = *end;
*end = *dest;
*dest = tmp;
end--;
dest++;
}
}
// 按单词逆向 如 this is a sentence 输出 sentence a is this
// 先按单词逆序,再对整个句子逆序
void revertByWord(const char *src, char *dest){
// 先拷贝字符串到dest
char *tmp = dest;
char *dest_end;
while(*src){
*tmp++ = *src++;
}
*tmp='\0';
dest_end = tmp-1; //这里留作下面用;
tmp = dest;
// 扫描一遍 dest,按单词逆序
char *start=dest-1,*end=dest;
while(*tmp){
if (*tmp == ' ')//逆序
{
start++;
//对单词逆向
while(end>start){
*start = *start + *end;
*end = *start - *end;
*start = *start - *end;
start++;
end--;
}
start=tmp;// 如果句子中又多个连续空格,也会对空格做逆序,浪费cpu
}
tmp++;
end=tmp-1;
}
if (!*tmp)//最后一个字符,对最后一个单词逆序
{
start ++;
while(end>start){
*start = *start + *end;
*end = *start - *end;
*start = *start - *end;
start++;
end--;
}
}
tmp = dest;
// 逆向整个句子
while(dest_end>tmp){
*tmp = *tmp + *dest_end;
*dest_end = *tmp - *dest_end;
*tmp = *tmp - *dest_end;
tmp++;
dest_end--;
}
}
// 回文,或 对称
BOOL symmetrical(const char *scr,int length){
const char *end = scr+length-1;
BOOL is=YES;
while(end>scr){
if (*scr++ != *end--)
{
is=NO;
}
}
return is;
}
//找出第一个只出现一次的字符,找不到就返回‘0’
char first_appear_only_once(const char *source){
// 建立hash表
const int length=256;
int hash_table[length];
for (int i = 0; i < length; ++i)
{
hash_table[i]=0;
}
//第一次扫描,建立hash表
const char *tmp = source;
while(*tmp){
hash_table[*tmp++]++;
}
// 第二次扫描
while(*source){
if(hash_table[*source]==1){
return *source;
}
source++;
}
// 没找到
if (!*source)
{
return '0';
}
return '0';
}
// 最大对称子串的长度
int max_symmetrical_char_length(const char *scr){
int max_symmetrical_length=1;
return 0;
}
// 找最大数字子串
int continumax(char *outputstr,char *intputstr){
return 0;
}
// 最大连续递增数字串
int continuousnum(const char *input, char *output){
return 0;
}
// 2个字符串的最长公共子串
int longest_common_subsequence(const char *s1,const char *s2, char *common){
return 0;
}
// 找子串
char *strstr(const char *haystack, const char * needle){
return NULL;
}
// 子串个数
char *sub_str_count(const char *src, const char *substr, int *count){
return NULL;
}
// 删除子串
char *delete(char *source,const char *del,BOOL greedy){
return NULL;
}
// 字符串拷贝
char *strcpy(const char *src,char *dest){
char *tmp = dest;
while(*src){
*dest++ = *src++;
}
*dest = '\0';
return tmp;
}
// aabcd & bcad , abc&bca
BOOL isBrother(const char *first,const char *secd){
return YES;
}
// 字符串就地压缩 如 “aaabcc” “a3bc2”
/*
注意连续字符大于9的情况
*/
void compress(char *src){
if (*src == NULL)
{
return;
}
int count = 1;
char *back=src,forward=src+1;
while(*forward){
count = 1;
if (*forward == *back)
{
count++;
forward++;
}else{ //
if (count!=1)
{
back++;
*back=count;
}
}
}
*++back = '\0';
}
// 删除其中的数字并压缩,2个指针,一前一后
void filternum(char *src){
if (!*src)
{
return;
}
char *back=src ,*forward=src;
while(*forward){
if (*forward >= '0' && *forward <= '9')
{
forward++;
}else{
*back++ = *forward++;
}
}
*back='\0';
// 局部变量释放,不能作为返回值
}
// 删除字符串中的数字,不改变原串,同上
char *filternumber(const char*src,char *dest){
return NULL;
}
// 删除指定字符,不开辟空间
void delete_character(char *src , char target){
char *back=src,*forward = src;
while(*forward){
if (*forward == target)
{
forward++;
}else{
*back++ = *forward++;
}
}
*back = '\0';
}
// 统计文件中单词的出现的个数
void statistics(FILE *fd){
}
// 字符串中字符的所有排列
int main(int argc, char const *argv[])
{
// 反转
char *reverse = "nonstriater";
char reversed[20];
revert(reverse,reversed);
printf("%s after reverse : %s\n", reverse,reversed);
// 按单词逆序
char *sentence = "this is a sentence!"; // "this is a sentence"
char dest[50];
revertByWord(sentence,dest);
printf("%s revert by word: %s\n",sentence,dest );
// 回文
char symmetri[] = "dacbcacbcad";
printf("%s : %d\n", symmetri, symmetrical(symmetri,sizeof(symmetri)-1) );
char unsymmetrical[] = "aaacbdaa";
printf("%s : %d\n", unsymmetrical, symmetrical(unsymmetrical,sizeof(unsymmetrical)-1) );
// 第一次出现一次的字符
printf("%s第一次出现一次的字符是:%c\n",symmetri,first_appear_only_once(symmetri) );// '0'
printf("%s第一次出现一次的字符是:%c\n",unsymmetrical,first_appear_only_once(unsymmetrical) ); // 'c'
// 过滤数字
char *charnumber = "this is 58tongcheng12haha!";
//filternum(charnumber);
printf(" after filter: %s\n", charnumber );
// 过滤指定字符
//delete_character(charnumber,'i'); // 居然会出 bus error!!!!!????
printf("after filter:%s\n", charnumber);
return 0;
}
@@ -0,0 +1,477 @@
## 海量数据处理
这部分内容是海量数据处理的问题。所谓海量:
时间上:无法在较短时间内迅速解决。可以设计巧妙的算法搭配合适的数据结构解决
空间上:数据量太大,无法一次全部装入内存;针对方法是大而化小,分而治之。将规模大划分为规模小的问题,各个击破
海量处理问题常用的分析解决问题的思路是:
1. 分而治之/Hash映射 + hash统计/trie树/红黑树/二叉搜索树 + 堆排序/快速排序/归并排序
2. 双层桶划分
3. Bloom filter 、Bitmap
4. Trie树/数据库/倒排索引
5. 外排序
6. 分布处理之 Hadoop/Mapreduce
### top-1问题 —— 海量日志提取访问最多的IP
海量日志处理,提取某日访问百度次数最多的那个IP
#### 估算
我们先来估算下,一个IP可以用32bit表示,总共可以表示 2^32 = 42亿九千万个IP 。假设一个IP一天内访问百度的次数不超过40亿次,可以用unsigned 的数表示。用数组统计出每个IP地址出现的最大次数,就可以得到访问次数最大的IP地址
unsigned count[N],这个N=2^32=4G个IP地址 sizeof(count) = 4*N = 16G内存,远超过32位机器所支持的4G内存大小。因此不能直接创建这个数组。
#### 分治法/文件映射
假设运行使用的内存是512MB, 512M/4 = 128M个IP地址(一个IP地址32位,占4个字节),也就是内存中可以同时放 128M个内存地址;另一个角度理解就是,`512M内存可以统计128M个不同的IP地址的访问次数`,哪怕切割的小文件中的IP总数大于128M
4G/128M = 32,也就是把IP地址划分成 32 个不同的区间段,addr[1-xx],addr[xx-xx]..分别计算每个区间中访问次数最大的IP,然后再比较32个区间段中的最大次数,就可以得到所有IP的做大值了。
一个区间段的IP地址个数任然可能撑破128M个,从而撑爆内存
具体实施:
把大文件映射到小文件,有2种方式:
1. 取模映射 。 IP%32 映射到32个小文件,把model相同的IP保存到同一个文件
2. 位运算映射。把IP的前5为作为作为区间编号,即 IP>>27 结果就是[0,31],把相同区间的IP保存到同一个文件
上面的映射中,`都不会出现同一个IP映射到不同小文件的情况`
#### 统计每个小文件中的最大值
现在文件不同的IP数量达到内存承受的范围了,可以统计小文件中的IP次数了,使用常规的 hash_map(IP,count) 来 统计了。可以分块读取来减少磁盘IO
#### 排序 堆排序/快速排序/归并排序
#### 代码示例
```
```
#### 类似问题
1. 怎么在海量数据中找出重复次数最多的一个?
2. 服务器内存1G,有一个2G的文件,里面每行存着一个QQ号(5-10位数),怎么最快找出出现过最多次的QQ号。(10位数可以表示(不到)10亿个QQ号)
#### top-K 找出最大的k个数
类似的题目类型有:
100w个数中找出最大的100个数。
寻找热门查询,300万个查询字符串中统计最热门的10个查询。
有一个文件中保存了2亿个整数,每个整数都以' '分隔。求最大的100万个整数之和。
有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。请用5分钟时间,找出重复出现最多的前10条。
一个文件中包含了1亿个随机整数,如何快速的找到最大(小)的100万个数字?(时间复杂度:O(n lg k))
有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
分析: hashmap统计+二叉堆
方案1:在前面的题中,我们已经提到了,用一个含100个元素的最小堆完成。复杂度为O(100w*lg100)。
方案2:采用快速排序的思想,每次分割之后只考虑比轴大的一部分,知道比轴大的一部分在比100多的时候,采用传统排序算法排序,取前100个。复杂度为O(100w*100)。
方 案3:采用局部淘汰法。选取前100个元素,并排序,记为序列L。然后一次扫描剩余的元素x,与排好序的100个元素中最小的元素比,如果比这个最小的要 大,那么把这个最小的元素删除,并把x利用插入排序的思想,插入到序列L中。依次循环,知道扫描了所有的元素。复杂度为O(100w*100)。
这里给个最简单hash函数,`字符串到int类型的哈希`
```
int hash_function(const char *p)
{
int value = 0;
while (*p != '\0')
{
value = value * 31 + *p++;
if (value > HASHLEN)
value = value % HASHLEN;
}
return value;
}
```
这个hash函数要确保 不同的字符串 hash出不同的一个 整数。
类似的题目还有:
##### 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
方案1:顺序读文件中,对于每个词x,取clip_image014,然后按照该值存到5000个小文件(记为clip_image016) 中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,知道分解得到的小文件的大小都不超过1M。对 每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点 的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。
##### 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
方 案1:这题是考虑时间效率。用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个 词,可以用堆来实现,前面的题中已经讲到了,时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一 个。
##### 一个文本文件,找出前10个经常出现的词,但这次文件比较长,说是上亿行或十亿行,总之无法一次读入内存,问最优解。
方案1:首先根据用hash并求模,将文件分解为多个小文件,对于单个文件利用上题的方法求出每个文件件中10个最常出现的词。然后再进行归并处理,找出最终的10个最常出现的词。
##### 寻找热门查询:
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较 高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询 串,要求使用的内存不能超过1G。
(1) 请描述你解决这个问题的思路;
(2) 请给出主要的处理流程,算法,以及算法的复杂度。
方案1:采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。
#### 分布式中的top-K
海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。
方案1:
s 在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我们首先取前 10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元 素就是TOP10大。
s 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。
#### 统计指定时间段内ip访问量
假设某个网站每天有超过10亿次的页面访问量,出于安全考虑,网站会记录访问客户端访问的ip地址和对应的时间,如果现在已经记录了1000亿条数据,想统计一个指定时间段内的区域ip地址访问量,那么这些数据应该按照何种方式来组织,才能尽快满足上面的统计需求呢,
设计完方案后,并指出该方案的优缺点,比如在什么情况下,可能会非常慢?
参考答案:用B+树来组织,非叶子节点存储(某个时间点,页面访问量),叶子节点是访问的IP地址。这个方案的优点是查询某个时间段内的IP访问量很快,但是要统计某个IP的访问次数或是上次访问时间就不得不遍历整个树的叶子节点。或者可以建立二级索引,分别是时间和地点来建立索引。
#### 文件中的内容排序并去重复
1) 如果文件太大,就分片
2) 这里的问题是去除重复,分片后,小文件合并又是个麻烦事。
3) 直接hash统计,假设不重复的url最多300万,采用hash存储需要的空间 300万*4
linux中的几个命令:
```
sort 将文件内容排序
uniq 检查和删除文件内容中重复出现的行列
comm 比较2个已经排序的文件
```
对于较大文件 G级别的文件
split -b 把大文件分割成小文件
然后用 sort 分别排序
sort -m 合并结果
然后在 uniq 命令去重
类似的题目有:
大量的URL字符串,如何从中去除重复的,优化时间空间复杂度
对2亿条手机号码删除重复记录
#### 在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数
【解析】取前100 万个整数,构造成了一棵数组方式存储的具有小顶堆,然后接着依次取下一个整数,如果它大于最小元素亦即堆顶元素,则将其赋予堆顶元素,然后用Heapify调整整个堆,如此下去,则最后留在堆中的100万个整数即为所求 100万个数字。该方法可大大节约内存。
#### 搜索中的智能提示(如 "中国" ,"zhongguo",考虑拼音)
把2万个汉字排序,弄成一个超长的字符串
用Int16索引汉字的所有拼音
Int64 建立汉字和拼音的关联--汉字有多音字,需要把多个拼音pack到一个Int64,位操作搞定
二分+位移unpack,直接做到汉字到拼音的检索
#### 找出文件中相反的串对
一个文件,内含一千万行字符串,每个字符串在1K以内,要求找出所有相反的串对,如abc和cba。
分析:
文件大小上限是:1000万*1K ~ 10GB。不可能内存操作
1) 设计hash函数 使得相反串得到相同的hash值 ?
#### 求出这个文件里的整数里不包含的一个整数
一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数
40亿*4 = 15GB,
4个字节表示的整数,总共有2^32个可能
使用`位图法`来统计:
1)分配 2^29 X 2^3 = 512MB 的内存空间,每一个bit代表一个整数,全部初始化为0;
buffer[512x1024x1024]
2) 读入一个数,把对应的bit位置为1.
如读入的是 312 , 将对应的bit置为1:312/8=39 312%8=0 ,写入位置是 第40个字节,0号bit
3) 处理完40亿数据后,遍历500M内存,找到一个bit为0的位
for(unsigned int i=0;i<0xffffffff,i++){
if(!buffer){//为0则不包括这个整数
}
}
`位图法` 适用于大规模数据,通常用来判断某个数据存不存在
写入指定位:
bytepos = i/8 = i>>3
bitpos = i%8 = i & 0x1F
置为1:arr[bytepos] |= (1 << bitpos)
置为0:arr[bytepos] &= !(1<< bitpos)
void setbit(int *bitmap,int i){
bitmap[i>>3] |= (1 << (i&0x1F) );
}
读指定位:
getbit(int *bitmap,int i){
return bitmap[i>>3] & ( 1 << (i&0x1F) );
}
#### 对文件里的整数排序
假设一个文件中有9亿条不重复的9位整数,现在要求对这个文件进行排序。
9位整数做多可表示10亿条不重复整数(无符号数)
9亿*4=3.4GB内存,一次读入内存是不可能了。
思路一: 位图法排序
1) 计算需要内存: 9亿/8/1024/1024 < 120MB ,全部初始化为0
2) 读取文件中的数据,将数据对应的bit位置1
`分段读取`
3) 遍历整个bit,将bit为1的依次存入文件
思路二:?
#### {url,size} 查询子串并按size排序
给出一个文件,里面包含两个字段{url、size},即url为网址,size为对应网址访问的次数;
要求:
问题1、利用Linux Shell命令或自己设计算法,
查询出url字符串中包含“baidu”子字符串对应的size字段值;
问题2、根据问题1的查询结果,对其按照size由大到小的排列。
(说明:url数据量很大,100亿级以上)
#### 找出文件中的中位数
在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可。
#### 找出重复登陆的QQ号
腾讯服务器每秒有2w个QQ号同时上线,找出5min内重新登入的qq号并打印出来。
#### 海量节点树中寻找共同祖先
有一个一亿节点的树,现在已知两个点,找这两个点的共同的祖先。
#### 设计流量统计系统
某服务器流量统计器,每天有1000亿的访问记录数据,包括时间、url、ip。设计系统实现记录数据的保存、管理、查询。要求能实现一下功能:
(1)计算在某一时间段(精确到分)时间内的,某url的所有访问量。
(2)计算在某一时间段(精确到分)时间内的,某ip的所有访问量。
#### a,b文件中共同的记录
1. 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
方案1:可以估计每个文件安的大小为50G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
s 遍历文件a,对每个url求取clip_image002,然后根据所取得的值将url分别存储到1000个小文件(记为clip_image004)中。这样每个小文件的大约为300M。
s 遍历文件b,采取和a相同的方式将url分别存储到1000各小文件(记为clip_image006)。这样处理后,所有可能相同的url都在对应的小文件(clip_image008)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
s 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
方 案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
2. 有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
方案1:
s 顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为clip_image010)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
s 找一台内存在2G左右的机器,依次对clip_image010[1]用hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为clip_image012)。
s 对clip_image012[1]这10个文件进行归并排序(内排序与外排序相结合)。
方案2:
一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。
方案3:
与方案1类似,但在做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。
#### 找出不重复的整数(QQ号)
在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。
方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存clip_image020内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
方案2:也可采用上题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。
#### 字符串去重
1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
方案1:这题用trie树比较合适,hash_map也应该能行。
#### 如何随机选取1000个关键字
给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字)。如何才能从这个无穷尽的流中随机的选取1000个关键字?
类似的题目还有:
有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中随机取得m个记录
#### 最大间隙问题
给定n个实数clip_image042,求着n个实数在实轴上向量2个数之间的最大差值,要求线性的时间算法。
方案1:最先想到的方法就是先对这n个数据进行排序,然后一遍扫描即可确定相邻的最大间隙。但该方法不能满足线性时间的要求。故采取如下方法:
s 找到n个数据中最大和最小数据max和min。
s 用n-2个点等分区间[min, max],即将[min, max]等分为n-1个区间(前闭后开区间),将这些区间看作桶,编号为clip_image044,且桶clip_image046的上界和桶i+1的下届相同,即每个桶的大小相同。每个桶的大小为:clip_image048。实际上,这些桶的边界构成了一个等差数列(首项为min,公差为clip_image050),且认为将min放入第一个桶,将max放入第n-1个桶。
s 将n个数放入n-1个桶中:将每个元素clip_image052分配到某个桶(编号为index),其中clip_image054,并求出分到每个桶的最大最小数据。
s 最大间隙:除最大最小数据max和min以外的n-2个数据放入n-1个桶中,由抽屉原理可知至少有一个桶是空的,又因为每个桶的大小相同,所以最大间隙 不会在同一桶中出现,一定是某个桶的上界和气候某个桶的下界之间隙,且该量筒之间的桶(即便好在该连个便好之间的桶)一定是空桶。也就是说,最大间隙在桶 i的上界和桶j的下界之间产生clip_image056。一遍扫描即可完成。
#### 将多个集合合并成没有交集的集合:给定一个字符串的集合,格式如:clip_image058。要求将其中交集不为空的集合合并,要求合并完成的集合之间无交集,例如上例应输出clip_image060。
(1) 请描述你解决这个问题的思路;
(2) 给出主要的处理流程,算法,以及算法的复杂度;
(3) 请描述可能的改进。
方案1:采用并查集。首先所有的字符串都在单独的并查集中。然后依扫描每个集合,顺序合并将两个相邻元素合并。例如,对于clip_image062, 首先查看aaa和bbb是否在同一个并查集中,如果不在,那么把它们所在的并查集合并,然后再看bbb和ccc是否在同一个并查集中,如果不在,那么也把 它们所在的并查集合并。接下来再扫描其他的集合,当所有的集合都扫描完了,并查集代表的集合便是所求。复杂度应该是O(NlgN)的。改进的话,首先可以 记录每个节点的根结点,改进查询。合并的时候,可以把大的和小的进行合,这样也减少复杂度。
#### 最大子序列与最大子矩阵问题
数组的最大子序列问题:给定一个数组,其中元素有正,也有负,找出其中一个连续子序列,使和最大。
方案1:这个问题可以动态规划的思想解决。设clip_image064表示以第i个元素clip_image066结尾的最大子序列,那么显然clip_image068。基于这一点可以很快用代码实现。
最大子矩阵问题:给定一个矩阵(二维数组),其中数据有大有小,请找一个子矩阵,使得子矩阵的和最大,并输出这个和。
方案1:可以采用与最大子序列类似的思想来解决。如果我们确定了选择第i列和第j列之间的元素,那么在这个范围内,其实就是一个最大子序列问题。如何确定第i列和第j列可以词用暴搜的方法进行。代码详见我的博客。
#### 一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到clip_image022个数中的中数?
方案1:先大体估计一下这些数的范围,比如这里假设这些数都是32位无符号整数(共有clip_image018[1]个)。我们把0到clip_image024的整数划分为N个范围段,每个段包含clip_image026个整数。比如,第一个段位0到clip_image028,第二段为clip_image026[1]到clip_image030,…,第N个段为clip_image032到clip_image024[1]。 然后,扫描每个机器上的N个数,把属于第一个区段的数放到第一个机器上,属于第二个区段的数放到第二个机器上,…,属于第N个区段的数放到第N个机器上。 注意这个过程每个机器上存储的数应该是O(N)的。下面我们依次统计每个机器上数的个数,一次累加,直到找到第k个机器,在该机器上累加的数大于或等于clip_image034,而在第k-1个机器上的累加数小于clip_image034[1],并把这个数记为x。那么我们要找的中位数在第k个机器中,排在第clip_image036位。然后我们对第k个机器的数排序,并找出第clip_image036[1]个数,即为所求的中位数。复杂度是clip_image038的。
方案2:先对每台机器上的数进行排序。排好序后,我们采用归并排序的思想,将这N个机器上的数归并起来得到最终的排序。找到第clip_image034[2]个便是所求。复杂度是clip_image040的。
+166
View File
@@ -0,0 +1,166 @@
矩阵在计算机中表示就是二维数组。这部分内容都是有关二维数组和矩阵相关的题目。
#### 二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
```
int exist_inmatrix(int *matrix,int rows,int cols,int num);
```
#### 矩阵中最大的二维矩阵
求一个矩阵中最大的二维矩阵(元素和最大).如:
```
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
```
中最大的是:
```
4 5
5 3
```
要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码
```
int max_sum_submatrix(int *matrix,int rows,int cols,int *tmatrix,int *trows,int tcols)
```
#### 顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
例如:如果输入如下矩阵:
```
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在内的多家公司在面试或者笔试里采用过这道题。
一圈一圈打印,第一圈origin是(0,0),第二圈是(1,1)
```
void print_matrix(int **matrix,int rows,int cols);
//或
void print_matrix(int *matrix,int rows,int cols);
```
示例代码:
```
void print_matrix(int *matrix,int rows,int cols){
if(matrix==NULL) return ;
if(rows<=0 || cols<=0) return;
int start = 0;
while(start*2<cols && start*2<rows){
print_matrix_incircle(matrix,rows,cols,start);
start++;
}
}
// a[i*rows+j]
void print_matrix_incircle(int *matrix,int rows,int cols,int start){
int endX = cols-start-1;
int endY = rows-start-1;
//然后依次打印 上,右,下,左
for(int i=start;i<=endX,i++){
printf("\d ",matrix[start*rows+i]);
}
for(int i=start+1;i<=endY,i++){
printf("\d ",matrix[i*rows+endX]);
}
for(int i=endX-1;i>=start;i--){
printf("\d ",matrix[start*rows+i]);
}
for(int i=endY-1;i>start;i--){
printf("\d ",matrix[i*rows+start]);
}
}
```
测试代码见 print_matrix.c
#### 从小到大输出矩阵的值
思路1:采用归并进行排序然后进行顺序打印
思路2:用n个指针指向每一行的第一个数,比较n个指针的数,打印最小的,然后指针后移,若无下一位,则赋值为null,直至所有数都对印完毕
```
void printArry(int *a,int rows,int columns)
{
if(a==null)
return;
int *arr=new int[rows*(columns+1)];//添加看门狗值,INT_MAX
for(int i=0;i<rows;i++)
for(int j=0;j<columns+1;j++)
{
if(j==columns)
arr[i*(columns+1)+j]=INT_MAX;
else
arr[i*(columns+1)+j]=a[i*columns+j];
}
int* pArr1=new int[rows];//共存有这么多行指针
for(int i=0;i<rows;i++)
pArr1[i]=i*(columns+1);
while(1)
{
int min=arr[pArr1[0]];
int mini=0;
for(int i=0;i<rows;i++)
{//第i个数组最小
int index=pArr1[i];
if(min>arr[index])
{
min=arr[index];
mini=i;
}
}
if(min==INT_MAX)
break;//表示都达到了看门处,则跳出循环。
cout<<min<<" ";
pArr1[mini]++;//指针前移动
}
}
```
算法复杂度时O(mn);m是行数,n是列数;对原矩阵,增加一列,用于看门;利用类似归并排序中的整合过程,此处是整合m个有序数列,最后得到有序输出。
#### n支队伍比赛的名次
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。
@@ -0,0 +1,75 @@
#### 要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。
#### 设计一个系统处理词语搭配问题
设计一个系统处理词语搭配问题,比如说 中国 和人民可以搭配,
则中国人民 人民中国都有效。要求:
*系统每秒的查询数量可能上千次;
*词语的数量级为10W;
*每个词至多可以与1W个词搭配
当用户输入中国人民的时候,要求返回与这个搭配词组相关的信息。
#### 任务调度
系统有很多任务,任务之间有依赖,比如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);杀死进程
```
#### 设计一种内存管理算法
相关的问题:
##### 请编写实现malloc()内存分配函数功能一样的代码
##### 用C语言实现函数memmove()函数
```
void * memmove(void *dest, const void *src, size_t n)
```
memmove()函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。
分析:由于可以把任何类型的指针赋给void类型的指针,这个函数主要是实现各种数据类型的拷贝。
#### A向B发邮件,B收到后读取并发送收到,但是中间可能丢失了该邮件,怎么设计一种最节省的方法,来处理丢失问题。
#### 设计一种算法求出算法复杂度
@@ -0,0 +1,123 @@
书中的内容分为4个部分:
1. 游戏之乐:游戏中的一些问题
2. 数字之魅: 数字和字符的处理能力
3. 结构之法: 对字符串,链表,队列,树的操作
4. 数学之趣: 一些数学问题
《剑指offer》中已经出现的题目先不写了
### 游戏之乐
####
### 数字之魅
#### 二进制数中1的个数
#### 阶乘
#### 寻找发帖"水王"
#### 1的数目
#### 寻找最大的k个数
#### 精确表达浮点数
#### 最大公约数
#### 找符合条件的整数
#### Fibonacci数列
#### 寻找数组中的最大值和最小指
#### 寻找最近点对
#### 快速寻找满足条件的两个数
#### 子数组的最大乘积
#### 求数组的子数组之和的最大值
#### 子数组之和的最大值
#### 数组中最长递归子序列
#### 数组循环移位
#### 数组分割
#### 区间重合判断
### 结构之法
#### 字符串移位包含的问题
#### 电话号码应对英语单词
#### 计算字符串的相似度
#### 从无头单链表中删除节点
#### 最短摘要生成
#### 判断2个链表是否相交
#### 队列中取最大值
#### 求二叉树中节点的最大距离
#### 重建二叉树
#### 程序改错
### 数学之趣
####
+193
View File
@@ -0,0 +1,193 @@
链表常常碰到的问题有:
* 链表反转
* 链表中是否有环
* 删除链表中的p节点,在p节点前面插入节点q, 要求O(1)复杂度
* 2个链表相交
* 2个链表合并
#### 判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。为了简化问题,我们假设俩个链表均不带环。
问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?
#### 输出两个非降序链表的并集
请修改append函数,利用这个函数实现:
两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5 。另外只能输出结果,不能修改两个链表的数据。
#### 输出链表中倒数第k个结点
题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。
链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
#### 链表排序
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),从而检测到链表中有环。
类似的题有:
1 给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。
如果head1==head2,那么显然相交,直接返回head1。
否则,分别从head1,head2开始遍历两个链表获得其长度len1与len2,假设len1>=len2,
那么指针p1由head1开始向后移动len1-len2步,指针p2=head2,
下面p1、p2每次向后前进一步并比较p1p2是否相等,如果相等即返回该结点,
否则说明两个链表没有交点。
2 给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。
运用题一,我们可以检查链表中是否有环。如果有环,那么p1p2重合点p必然在环中。从p点断开环,方法为:p1=p, p2=p->next,
p->next=NULL。此时,原单链表可以看作两条单链表,一条从head开始,另一条从p2开始,
于是运用题二的方法,我们找到它们的第一个交点即为所求。
#### 删除链表中的p节点
只给定单链表中某个结点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)`,以复制一个复杂链表。
#### 找出两个链表的第一个公共结点
题目:两个单向链表,找出它们的第一个公共结点。链表的结点定义为:
```
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
```
分析:
第一个公共节点,也就是2个链表中的节点的m_pNext 指向的同一个节点。
1. 先遍历2个链表,得到各自的长度,和差sub
2. 长链表先遍历sub个节点,然后2个节点一起遍历
3. 第一次同时指向的同一个节点就是这个commonNode
#### 从尾到头输出链表
输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:
```
struct ListNode{
int m_nKey;
ListNode* m_pNext;
};
```
思路一: 辅助栈,需要一个栈空间
思路二: 反转链表,然后遍历
思路三: 递归实现,将printf语句放在递归调用后面。果然妙极。。
#### 在O(1)时间内删除链表结点
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
```
struct ListNode{
int m_nKey;
ListNode* m_pNext;
};
//函数的声明如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);
```
思路:
保存下一个节点的值tmp,删除下一个节点,当前节点=tmp
#### 将两链表中data值相同的结点删除
有双向循环链表结点定义为:
```
struct node{
int data;
struct node *front,*next;
};
````
有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中data值相同的结点删除。
#### 链表和数组的区别在哪里?
分析:主要在基本概念上的理解。
但是最好能考虑的全面一点,现在公司招人的竞争可能就在细节上产生,
谁比较仔细,谁获胜的机会就大。