附:基础 https://blog.csdn.net/shiyideliutang/article/details/103528550
单调栈之山峰形成数对问题
题目分析
再次总结算法思路【某一元素结算的方式】
程序代码完全按照该框架写出来的,理解比较容易
程序源代码C++实现
#include<iostream>
using namespace std;
#include <vector>//矢量vector头文件
#include <stack>//栈的头文件
/**< 打印容器vector中的值 */
void print(vector<int> vec)
{
//第一种打印方式:迭代器
for(auto it = vec.begin(); it != vec.end(); it++)
{
cout << *it << " ";
}
cout << "\n---------------------------\n";
//第二种打印方式:元素访问
for(int i = 0; i < vec.size(); i++)
{
cout << vec.at(i) << " ";//at函数访问元素
cout << vec[i] << " ";//[]方式访问元素
}
cout << "\n---------------------------\n";
}
/**< 当前容器索引位置的下一个位置 */
//因为是环形结构,所以需要做特殊的处理
int Get_NextIndex(vector<int> vec, int index)
{
return (index+1)==vec.size()? 0:index+1;
}
/**< 栈中存储的数据结构 */
class ValueTime
{
public:
int value;
int times;
public:
//times初始化采用的是 初始化列表初始化数据成员
ValueTime(int value):times(1)
{
this->value = value;
}
};
/**< 计算C(2,timses)的数值 */
int GetResult(int times)
{
return (times==1? 0:(times*(times-1)/2));
}
/** \brief 求解容器中元素组成有效数对的个数
* \param vec:给定的容器,已经存储已经数据元素
* \return 最终有效数对的个数
*/
int Communication(vector<int> vec)
{
//元素数量较少,无法组成数对
if(vec.size() <= 1)
return 0;
int res = 0;//存储最终需要返回数对的个数
//1、寻找容器中第一个Max出现的位置[即容器遍历的起始位置]
int index_max = 0;
for(int i = 0; i < vec.size(); i++)
{
index_max = vec.at(index_max)<vec.at(i)? i:index_max;
}
//创建一个存储ValueTime类的栈,并将index_max作为底压入栈中
stack<ValueTime> s;
ValueTime* temp = new ValueTime(vec.at(index_max));
s.push(*temp);
delete[] temp;
//2、对整个容器进行遍历
//采用while循环为了方便是从index_max下一个位置开始到再次到达index_max位置结束
int index_cur = Get_NextIndex(vec, index_max);
while(index_cur != index_max)
{
// 1)不符合入栈条件[当前元素小于等于栈顶元素],需要将栈顶元素弹出并进行结算
while(!s.empty() && vec.at(index_cur)>s.top().value)
{
int times_cur = s.top().times;
s.pop();
res += GetResult(times_cur) + times_cur*2;//求解 C(2,times)+times*2
}
// 2)符合入栈要求
//当前元素等于栈顶元素:直接更新栈顶元素的times
if(!s.empty() && vec.at(index_cur)==s.top().value)
s.top().times++;
//当前元素小于栈顶元素:创建新的ValueTime结构,并压入栈中
else
{
ValueTime* temp2 = new ValueTime(vec.at(index_cur));
s.push(*temp2);
delete[] temp2;
}
//更新容器迭代器,为下次遍历做准备
index_cur = Get_NextIndex(vec, index_cur);
}
//3、整个容器已经遍历完毕,但是栈中仍存在数据
while(!s.empty())
{
if(s.size() > 2)
{
int times_cur = s.top().times;
s.pop();
res += GetResult(times_cur) + times_cur*2;//求解 C(2,times)+times*2
}
else if(s.size() == 2)
{
int times_cur = s.top().times;
s.pop();
res += GetResult(times_cur) + times_cur;//求解 C(2,times)+times
if(s.top().times > 1)//倒数第二个数据结算受最后一个元素的times的影响
res += times_cur;
}
else//结算最后一个元素
{
res += GetResult(s.top().times);
s.pop();
}
}
return res;
}
int main()
{
vector<int> vec;
cout << "Please input some numbers: ";
while(cin.peek() != '\n')
{
int temp;
cin >> temp;
vec.push_back(temp);
}
//print(vec);//打印vec容器中存储的元素
cout << Communication(vec);
return 0;
}
代码错误分析
针对以下代码
/**< 当前容器索引位置的下一个位置 */
//因为是环形结构,所以需要做特殊的处理
int Get_NextIndex(vector<int> vec, int index)
{
return (index+1)==vec.size()? 0:index+1;
}
最开始是想利用迭代器来调整环形结构【代码如下所示】,经过调试发现迭代器中的end()并不是自己理解的那样,猜测仅仅作为容器的边界, 不可以简简单单通过((it + 1) == vec.end() ?判断[可以通过调试的方法来进行测试验证]
/**< 当前容器索引位置的下一个位置 */
//因为是环形结构,所以需要做特殊的处理
vector<int>::iterator Get_NextIndex(vector<int> vec, vector<int>::iterator it)
{
return ((it + 1) == vec.end() ? vec.begin() : it + 1);
}