菜鸟系列之C/C++经典试题(三)

设计包含min函数的栈

题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及pop的时间复杂度都是O(1)。

分析:这是2006年google的一道面试题。

我看到这道题目时,第一反应就是每次push一个新元素时,将栈里所有逆序元素排序。这样栈顶元素将是最小元素。但由于不能保证最后push进栈的元素最先出栈,这种思路设计的数据结构已经不是一个栈了。

在栈里添加一个成员变量存放最小元素(或最小元素的位置)。每次push一个新元素进栈的时候,如果该元素比当前的最小元素还要小,则更新最小元素。

乍一看这样思路挺好的。但仔细一想,该思路存在一个重要的问题:如果当前最小元素被pop出去,如何才能得到下一个最小元素?

因此仅仅只添加一个成员变量存放最小元素(或最小元素的位置)是不够的。我们需要一个辅助栈。每次push一个新元素的时候,同时将最小元素(或最小元素的位置。考虑到栈元素的类型可能是复杂的数据结构,用最小元素的位置将能减少空间消耗)push到辅助栈中;每次pop一个元素出栈的时候,同时pop辅助栈。

参考代码:

#include <deque>
#include <assert.h>

template <typename T> class CStackWithMin
{
public:
    CStackWithMin(void) {}
    virtual ~CStackWithMin(void) {}

    T& top(void);
    const T& top(void) const;

    void push(const T& value);
    void pop(void);

    const T& min(void) const;

private:
    T > m_data;               // the elements of stack
    size_t > m_minIndex;      // the indices of minimum elements
};

// get the last element of mutable stack
template <typename T> T& CStackWithMin<T>::top()
{
    return m_data.back();
}

// get the last element of non-mutable stack
template <typename T> const T& CStackWithMin<T>::top() const
{
    return m_data.back();
}

// insert an elment at the end of stack
template <typename T> void CStackWithMin<T>::push(const T& value)
{
    // append the data into the end of m_data
    m_data.push_back(value);

    // set the index of minimum elment in m_data at the end of m_minIndex
    if (m_minIndex.size() == 0)
        m_minIndex.push_back(0);
    else
    {
        if (value < m_data[m_minIndex.back()])
            m_minIndex.push_back(m_data.size() - 1);
        else
            m_minIndex.push_back(m_minIndex.back());
    }
}

// erease the element at the end of stack
template <typename T> void CStackWithMin<T>::pop()
{
    // pop m_data
    m_data.pop_back();

    // pop m_minIndex
    m_minIndex.pop_back();
}

// get the minimum element of stack
template <typename T> const T& CStackWithMin<T>::min() const
{
    assert(m_data.size() > 0);
    assert(m_minIndex.size() > 0);

    return m_data[m_minIndex.back()];
}

举个例子演示上述代码的运行过程:

步骤             数据栈            辅助栈               最小值

1.push 3           3                                 0                       3

2.push 4           3,4                             0,0           3

3.push 2           3,4,2                         0,0,2                2

4.push 1           3,4,2,1                               0,0,2,3            1

5.pop                3,4,2                         0,0,2                2

6.pop                3,4                             0,0           3

7.push 0           3,4,0                         0,0,2        0

讨论:如果思路正确,编写上述代码不是一件很难的事情。但如果能注意一些细节无疑能在面试中加分。比如我在上面的代码中做了如下的工作:

·         用模板类实现。如果别人的元素类型只是int类型,模板将能给面试官带来好印象;

·         两个版本的top函数。在很多类中,都需要提供const和非const版本的成员访问函数;

·         min函数中assert。把代码写的尽量安全是每个软件公司对程序员的要求;

·         添加一些注释。注释既能提高代码的可读性,又能增加代码量,何乐而不为?

总之,在面试时如果时间允许,尽量把代码写的漂亮一些。说不定代码中的几个小亮点就能让自己轻松拿到心仪的Offer。

原文链接:http://zhedahht.blog.163.com/blog/static/25411174200712895228171/

谢谢分享, 转载表明出处。

菜鸟系列之C/C++经典试题(三),布布扣,bubuko.com

时间: 2024-08-01 17:46:05

菜鸟系列之C/C++经典试题(三)的相关文章

菜鸟系列之C/C++经典试题(二)

求子数组的最大和 题目描述: 输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4,7, 2,因此输出为该子数组的和18. 这个问题在各大公司面试中出现频率之频繁,被人引用次数之多,非一般面试题可与之匹敌.ok,下面,咱们来一步一步分析这个题. 分析与解法 解法一 求一个数组的最大子数组和

菜鸟系列之C/C++经典试题(一)

这个我写菜鸟系列之C/C++ 的第一篇文章 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 比如将二元查找树 10 /    \ 6       14 /  \     / \ 4     8  12   16 转换成双向链表 4=6=8=10=12=14=16. 分析:本题是微软的面试题.很多与树相关的题目都是用递归的思路来解决,本题也不例外.下面我们用两种不同的递归思路来分析. 思路一:当我们到达某一结点准备调整以该结点为根结点

菜鸟系列之C/C++经典试题(四)

题目一:查找最小的k个元素 输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 分析:这道题最简单的思路莫过于把输入的n个整数排序,这样排在最前面的k个数就是最小的k个数.只是这种思路的时间复杂度为O(nlogn).我们试着寻找更快的解决思路. 我们可以先创建一个大小为k的数据容器来存储最小的k个数字.接下来我们每次从输入的n个整数中读入一个数.如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中:如果容器中已有k

菜鸟系列之C/C++经典试题(十)

打印1到最大的n位数 题目:输入数字n,按顺序打印出从1到最大的n位十进制数.比如输入3,则打印1, 2, 3,-,999. 方法一: 这道题一看感觉很简单,首先求的n位数的最大值,然一个从1到这个最大值的循环就搞定了, 如果真的就把这样的答案面试官的话, 后果很是严重.首先, 没有考虑到获得的这个最大的数会不会溢出,如果溢出了, 答案肯定不对.这个代码比较简单, 我就不列出来了. 方法二: 很明显, 这道题我们可以用递归做, 就是每当多添加一位时, 我就把所以的都打印一遍连带刚添加的这个位,

菜鸟系列之C/C++经典试题(六)

含有指针成员的类的拷贝 题目:下面是一个数组类的声明与实现.请分析这个类有什么问题,并针对存在的问题提出几种解决方案. template<typename T> class Array { public: Array(unsigned arraySize) :data(0), size(arraySize) { if (size > 0) data = new T[size]; } ~Array() { if (data) delete[] data; } void setValue(u

菜鸟系列之C/C++经典试题(七)

找含单链表的环入口点 问题1:如何判断单链表中是否存在环(即下图中从结点E到结点R组成的环)? 分析:设一快一慢两个指针(Node *fast, *low)同时从链表起点开始遍历,其中快指针每次移动长度为2,慢指针则为1.则若无环,开始遍历之后fast不可能与low重合,且fast或fast->next最终必然到达NULL:若有环,则fast必然不迟于low先进入环,且由于fast移动步长为2,low移动步长为1,则在low进入环后继续绕环遍历一周之前fast必然能与low重合(且必然是第一次重

菜鸟系列之C/C++经典试题(五)

求圆圈中剩下的最后一个数字 题目:n个数字(0,1,-,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字).当一个数字删除后,从被删除数字的下一个继续删除第m个数字.求出在这个圆圈中剩下的最后一个数字. 本题就是著名的约瑟夫环问题. 本题的解法我们比较容易想到用链表,当然我们可以自己写一个链表,也可以直接用stl库中的list,实现代码如下: //使用标准库 int JosephusProblem_Solution2(int

菜鸟系列之C/C++经典试题(八)

计算二进制中1的个数 题目:位运算方面的编程很少遇到,但也是很重的一个只是点,一个比较常见的题目就是计算一个数的二进制表示中的1的个数. 分析:一个1都没有就是0, 我们的思路就是一位一位的运算, 我们很快就想到下面的做法: int countBit1(int val) { register int count = 0; while (val) { if (1 & val) { ++count; } val >>= 1; } return count; } 砟一看这个实现不错, 仔细看

菜鸟系列之C/C++经典试题(九)

寻找数组中出现的唯一重复的一个数 题目:1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它找出来:不用辅助存储空间,能否设计一个算法实现? 方法一:(当N为比较大时警惕溢出)将1001个元素相加减去1,2,3,--1000数列的和,得到的差即为重复的元素. int findRepeat(int *a) { int i; for (i = 0; i < 1001; i++) { a[1000] += a[i]; }