问题驱动式
举例让抽象问题具体化:看找不出问题中隐藏的规律时,可以使用一两个具体的例子模拟操作过程,之后可能可以从中找到问题的规律
题目
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字
思路
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 | 压入3 | 3 | 3 | 3 |
2 | 压入4 | 3,4 | 3,3 | 3 |
3 | 压入2 | 3,4,2 | 3,3,2 | 2 |
4 | 压入1 | 3,4,2,1 | 3,3,2,1 | 1 |
5 | 弹出 | 3,4,2 | 3,3,2 | 2 |
6 | 弹出 | 3,4 | 3,3 | 3 |
7 | 压入0 | 3,4,0 | 3,3,0 | 0 |
#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 | 压入1 | 1 | |
2 | 压入2 | 1,2 | |
3 | 压入3 | 1,2,3 | |
4 | 压入4 | 1,2,3,4 | |
5 | 弹出 | 1,2,3 | 4 |
6 | 压入5 | 1,2,3,5 | |
7 | 弹出 | 1,2,3 | 5 |
8 | 弹出 | 1,2 | 3 |
9 | 弹出 | 1 | 2 |
10 | 弹出 | 1 |
2、一个压入序列{1,2,3,4,5}的栈以及一个弹出序列{4,3,5,1,2}
步骤 | 操作 | 栈 | 弹出数字 |
---|---|---|---|
1 | 压入1 | 1 | |
2 | 压入2 | 1,2 | |
3 | 压入3 | 1,2,3 | |
4 | 压入4 | 1,2,3,4 | |
5 | 弹出 | 1,2,3 | 4 |
6 | 弹出 | 1,2 | 3 |
7 | 压入5 | 1,2,5 | |
8 | 弹出 | 1,2 | 5 |
弹出序列中,下一个弹出的是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;
}