缘由
以笔试、面试为目标学习算法。
这位v_JULY_v同学非常牛逼,总结了很多很多算法的知识,这次我打算先将微软面试100题的简单题就总结一遍,我发现对我来说难度还是比较大的,所以我这次打算先总结看的懂的题。
这位v_JULY_v同学非常牛逼,总结了很多很多算法的知识,这次我打算先将微软面试100题的简单题就总结一遍,我发现对我来说难度还是比较大的,所以我这次打算先总结看的懂的题。
- 以下内容的基本上都参考于:微软面试100题2010年版全部答案集锦(含下载地址)
第二题 设计一个可以瞬间找到栈中最小值的栈
题:
设计包含min 函数的栈。定义栈的数据结构,要求添加一个min 函数,能够得到栈的最小元素。要求函数min、push 以及pop 的时间复杂度都是O(1)。
解:
注意,我们是可以定义栈的数据结构的,可以不是通常使用的那种栈的结构。而是一个能够满足我们需求的新的栈的结构。
解体的关键就是:我们额外为栈每一个元素添加另外一个数据:这个数据会存放当当前元素的数据刚加进来式,这个栈中的最小值。
代码如下:
注意,我们是可以定义栈的数据结构的,可以不是通常使用的那种栈的结构。而是一个能够满足我们需求的新的栈的结构。
解体的关键就是:我们额外为栈每一个元素添加另外一个数据:这个数据会存放当当前元素的数据刚加进来式,这个栈中的最小值。
代码如下:
struct MinStackElement {
int data;
int min;//存放当data加入时,这个栈中最小的值
};
struct MinStack {
MinStackElement * data;//如果不是为了满足本题要求,此处应为int data
int size;
int top;//栈顶部的索引/下标
}
MinStack MinStackInit(int maxSize) {
MinStack stack;
stack.size = maxSize;
stack.data = (MinStackElement*) malloc(sizeof(MinStackElement)*maxSize);
stack.top = 0;
return stack;
}
void MinStackFree(MinStack stack) {
free(stack.data);
}
void MinStackPush(MinStack stack, int d) {
if (stack.top == stack.size) error(“out of stack space.”);
MinStackElement* p = stack.data[stack.top];//一个指针指向栈顶
p->data = d;//插入数据
p->min = (stack.top==0?d : stack.data[top-1]);//插入在这个元素插入之前,栈中最小的数据
if (p->min > d) p->min = d;//如果这次插入的数据更小,故做一个替换
top ++;
}
int MinStackPop(MinStack stack) {
if (stack.top == 0) error(“stack is empty!”);
return stack.data[--stack.top].data;
}
//一下子就获得了
int MinStackMin(MinStack stack) {
if (stack.top == 0) error(“stack is empty!”);
return stack.data[stack.top-1].min;
}
注意:该种方式未考虑到空间复杂度,因为每个元素都需要存放一个额外的最小的数据,占用的空间比正常的多了一倍。
上述解答应该是有错的!再次读博客的时候发现的。
这个解答应该是靠谱的:设计包含min函数的栈
第三题 求出子数组最大的和
题:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
解:
最容易想到的情况如下所示:
下面的红线表示,一个子数组,比如1和-2,又比如-2,3,10.那么我们只需要用两个数分别指着数组中的每一个数,然后遍历完所有的子数组,再求和,即可得到答案。
代码如下:
//Algorithm 2:时间效率为O(n*n)
int MaxSubsequenceSum2(const int A[],int N)
{
int ThisSum=0,MaxSum=0,i,j,k;
for(i=0;i<N;i++)
{
ThisSum=0;
for(j=i;j<N;j++)
{
ThisSum+=A[j];
if(ThisSum>MaxSum)
MaxSum=ThisSum;
}
}
return MaxSum;
}
然而时间复杂度不满足要求。再看优秀的解答:
- 准备两个变量:ThisSum,MaxSum
- 从左往右遍历,ThisSum依次加上数组的数
- 如果ThisSum小于0,则将ThisSum直接设置为0
- 再与之前保存的MaxSum比较,如果ThisSum大于MaxSum,则赋值给MaxSum
代码如下:
为什么会出现这样的情况呢?如下图:
如此以来,避免了出现在和最大的子数组中。
对本题更为详细的讨论请参见:
int MaxSubsequenceSum(const int A[],int N)
{
int ThisSum,MaxSum,j;
ThisSum=MaxSum=0;
for(j=0;j<N;j++)
{
ThisSum+=A[j];
if(ThisSum>MaxSum)
MaxSum=ThisSum;
else if(ThisSum<0)
ThisSum=0;
}
return MaxSum;
}
时间复杂度为O(n)。
为什么会出现这样的情况呢?如下图:
如此以来,避免了出现在和最大的子数组中。
对本题更为详细的讨论请参见:
第四题 从二元树中找出和为某一值的所有路径
题目:
输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如输入整数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
};
输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如输入整数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
};