【数据结构与算法(十六)】

问题驱动式
举例让抽象问题具体化:看找不出问题中隐藏的规律时,可以使用一两个具体的例子模拟操作过程,之后可能可以从中找到问题的规律

题目

顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字

思路

1、不要一上来就使用蠢办法
2、把顺时针打印的每一次都看成是打印圈圈,而打印几个圈,即循环退出的条件是什么?
3、拿几个小的矩阵来分析,每一圈的第一个数字的纵坐标和横坐标总是相等的,记为(start,start),所以start的最大值是什么就是我们要知道的循环退出的条件了
4、start的最大值又是多少?必须满足条件columns>startX*2 && rows>startY*2
5、现在要解决的是打印一圈的问题。打印一圈一般需要四步,从左到右、从上到下、从右到左、从下到上。但是又有一个问题就是最后一圈的时候可能不再需要四步,有可能是需要3步、2步、1步,如下:
这里写图片描述
至少需要一步,所以第一步总是需要的。如果只有一行,那就只有第一步;需要第二步的前提条件是终止行号大于起始行号。需要第三步的前提条件是至少有两行两列,即终止行号大于起始行号,并且终止列号大于起始列好。需要第四步的前提条件是至少有三行两列,所以终止行号比起始行号至少大2,并且终止列号大于起始列号

//顺时针打印矩阵,该函数主要是控制打印圈退出的条件
void PrintMatrixClockwisely(int** numbers, int columns, int rows)
{
    if (numbers == nullptr || columns <= 0 || rows <= 0)
        return;

    int start = 0;  //start是每个圈开始的坐标(横纵一样)
    while (columns > start * 2 && rows > start * 2) {
        PrintMatrixInCircle(numbers, columns, rows, start);
        ++start;
    }
}
//按圈打印出数字.每次调用只打印一圈
void PrintMatrixInCircle(int** numbers, int columns, int rows, int start)
{
    int endX = columns - 1 - start;//这个画图看会比较清晰
    int endY = rows - 1 - start;
    //从左到右打印一行
    for (int i = start; i <= endX; i++) {
        int number = numbers[start][i];
        cout << number << "\t";
    }
    //从上到下打印一列
    if (endY > start)
        for (int i = start + 1; i <= endY; i++) {
            int number = numbers[i][endY];
            cout << number << "\t";
        }
    //从右到左打印一行
    //终止行号大于起始行号,终止列号大于起始列号
    if(endX>start&&endY>start)
        for (int i = endX - 1; i >= start; i--) {
            int number = numbers[endY][i];
            cout << number << "\t";
        }
    //从下到上打印一列,所以是列号不变,行号减小
    if(endX>start&&endY-1>start)
        for (int i = endY; i >= start + 1; i--) {
            int number = numbers[i][start];
            cout << number << "\t";
        }
}

包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度都是O(1)

思路

1、把每次的最小元素(之前的最小元素和新压入栈的元素两者的较小值)都保存起来放到另一个辅助栈中
2、直接举个例子说明:就是说数据栈和辅助栈要同步

步骤操作数据栈辅助栈最小值
1压入3333
2压入43,43,33
3压入23,4,23,3,22
4压入13,4,2,13,3,2,11
5弹出3,4,23,3,22
6弹出3,43,33
7压入03,4,03,3,00
#include<>
//StackWithMin中的函数的实现
template<typename T>void StackWithMin<T>::push(const T&value)
{
    m_data.push(value);
    if (min.size() == 0 || value < min.top())
        m_min.push(value);
    else
        m_min.push(m_min.top());
}
//数据栈和辅助栈要同步
template<typename T>void StackWithMin<T>::pop()
{
    assert(m_data.size() > 0 && m_min.size() > 0);
    m_data.pop();
    m_min.pop();
}
//弹出当前的最小值
template<typename T>const T& StackWithMin<T>::min() const
{
    assert(m_data.size() > 0 && m_min.size() > 0);
    return m_min.top();
}

栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列{1,2,3,4,5}是某栈的压栈序列,序列{4,5,3,2,1}是该压栈序列对应的一个弹出序列,但{4,3,5,1,2}就不可能是该压栈序列的弹出序列

思路

1、分析压栈序列为{1,2,3,4,5},弹出序列为{4,5,3,2,1}对应的压栈和弹出过程

步骤操作弹出数字
1压入11
2压入21,2
3压入31,2,3
4压入41,2,3,4
5弹出1,2,34
6压入51,2,3,5
7弹出1,2,35
8弹出1,23
9弹出12
10弹出1

2、一个压入序列{1,2,3,4,5}的栈以及一个弹出序列{4,3,5,1,2}

步骤操作弹出数字
1压入11
2压入21,2
3压入31,2,3
4压入41,2,3,4
5弹出1,2,34
6弹出1,23
7压入51,2,5
8弹出1,25

弹出序列中,下一个弹出的是1,但1不再栈顶,压栈序列的数字都已入栈,操作无法进行
3、总结上述入栈、出栈过程,可以找到判断一个序列是否为栈的弹出序列的规律:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出;如果下一个弹出的数字不在栈顶,则把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止;如果所有数字都压入栈了还没有找到下一个弹出的数字,那么该序列就不可能是一个弹出序列了。

#include<stack>
using std::stack;
//pPush是压入序列,pPop是弹出序列
bool isPopOrder(const int* pPush, const int* pPop, int nLength)
{
    bool isPossible = false;
    //如果压入序列或者弹出序列不存在,那就是输入错误了,就不需要进行操作了
    if (pPush != nullptr&&pPop != nullptr&&nLength > 0)
    {
        const int* pNextPush = pPush;
        const int* pNextPop = pPop;
        stack<int> stackData;

        while (pNextPop - pPop < nLength)//还有没出栈的数字
        {
            //栈空或者栈顶不是下一个弹出的数,直到新压入的数字是下一个弹出的数,停止压栈
            while (stackData.empty() || stackData.top() != *pNextPop)
            {
                //继续压栈
                if (pNextPush - pPush == nLength)//所有数字都已经压入栈了
                    break;
                stackData.push(*pNextPush);//压入压栈序列中的下一个数字
                pNextPush++;
            }
            if (stackData.top() != *pNextPop)//结束while
                break;
            stackData.pop();
            pNextPop++; //准备弹出的下一个数字
        }
        if (stackData.empty() && pNextPop - pPop == nLength)//弹出的数字完了,栈也为空了,说明有这个弹出序列存在的
            //如果栈中还有数字?为什么不行?可以压进去但是暂时不弹出来?
            isPossible = true;
    }
    return isPossible;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值