目录
一、验证回文串
二、字符串相加
三、杨辉三角
四、最小栈
五、栈的压入、弹出序列
一、验证回文串
思路:
1、注:for(auto ch : s) -》指将s中每一个字符赋值给ch,ch的改变并不会影响s
但写为for(auto& ch : s) -》指将ch是s中每一个字符的引用,ch的改变影响s
2、将字符串中大写都转为小写或小写都转为大写,那判断是否为字母时,只需判断是否为小写字母即可,但这点最好的点在于比较字母时可以直接比较,因为都是统一的字母。
3、在走的过程中begin<end是因为如果一直没遇到字母和数字字符,不能让他一直走,走到尾要结束
代码如下:
class Solution {
public:
//判断是否为字母或数字
bool isCharOrNumber(char ch)
{
if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
return true;
else
return false;
}
//验证回文串
bool isPalindrome(string s) {
//1、将字符串中所有的大写字母转成小写
for (auto& ch : s)
{//取别名才能改变ch
if (ch >= 'A' && ch <= 'Z')
{
ch += 32;
}
}
//2、用快排的思路进行比较
int begin = 0, end = s.size() - 1;
while (begin < end)
{//比较的大前提是begin < end
while (begin < end && !isCharOrNumber(s[begin]))
++begin;//若不是字母或数字则begin++往后找
while (begin < end && !isCharOrNumber(s[end]))
--end;//若不是字字母或数字则end--往前找
//找到字母或数字了则开始比较
if (s[begin] != s[end])
{
return false;
}
else
{
++begin;
--end;
}
}
return true;
}
};
二、字符串相加
题目的意思是字符串变为整数相加后又变为字符串的形式 ,那这个字符串是多少?不允许字符串直接全转换为整形后相加,因为如果这个字符串很长,转换是存不下的
先给代码后说思路:
class Solution {
public:
string addString(string num1, string num2) {
//end1和end2从两个字符串的最后一位开始往前比
int end1 = num1.size() - 1, end2 = num2.size() - 1;
int next = 0;//用来保存是否进一位
string retstr;//此字符串用来保存结果并返回
while (end1 >= 0 || end2 >= 0)
{//两个字符串都结束了才算结束
int val1 = 0, val2 = 0;
if (end1 >= 0)//>=0说明没走完,因为存在一个字符串走完另个字符串没走完的情况
val1 = num1[end1] - '0';//将字符转换为数字
if (end2 >= 0)
val2 = num2[end2] - '0';
int ret = val1 + val2 + next;//转换成的数字相加
if (ret > 9)
{
ret -= 10;//>9则需要进位
next = 1;//进位出来的1
}
else
{//加出来的那一位ret<=9则无需进位,next=0
next = 0;
}
//retstr.insert(retstr.begin(), '0' + ret);
ret += (ret + '0');//再把这一位转换为字符尾插到ret末尾
--end1;//--来进行前一位的相加
--end2;
}
if (next == 1)
{//在最后一次若next==1,尾插一个'1'
//retstr.insert(retstr.begin(), '1');
retstr += '1';
}
//因为之前是尾插的,那正确的字符串应该逆置
reverse(retstr.begin(), retstr.end());
return retstr;
}
};
思路:
对于两个字符串从后往前一位一位进行相加,当然,字符不能直接相加,我们先把每一位对应的字符先转换为数字相加,如果加出来的这一位>9,则next=1(next用来表示是否进位) ,若<9,则next=0,当前一位的对应字符相加时就要加上next,进不进位就由next来区分了
代码中的剖析:
①、while结束条件是end1 >= 0 || end2 >= 0 因为给的字符串可能一个长一个短,只有长的也结束了相加才结束,所以用或,end1和end2原来给的是字符串最后一个字符的下标,若end1<0说明end1对应的字符串走完了,end2同理
②、在走的过程中要判断end1和end2是否>=0,因为存在一个字符串长一个字符串短的情况,长的没走完还要进入循环中,短的字符串走完了就不用计算了,所以如果>=0说明还没走完,要继续计算
③、字符的插入方式:
1、用insert来头插,但头插需要每次都向后移动,现有几个元素就需移几次,每次都头插一个元素,有一个元素需移动一次,两个元素需要移动两次,n个元素需要移动n次,1+2+...+n,故时间复杂度:O(N*N),效率不好
2、每插入一个元素都尾插,最后全尾插完再用algorithm中的reverse实现(需#include<algorithm>),时间复杂度:O(N),故推荐此方式
下面给出一个例子:
三、杨辉三角
题述:
给定一个非负整数 numRows,生成杨辉三角的前 numRows行。
示例:
题中已给:
class Solution {
public:
vector<vector<int>> generate(int numRows) {
}
};
知识点:理解vector<vector<int>>vv
思路:
对于杨辉三角的实际状态,除了1外,每个数=上一行同下标的数+上一行同下标-1的数,而每一行的首元素和尾元素都会是1,利用这个规律就好求解了
代码如下:
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows);//开辟numRows个(行)
for (size_t i = 0; i < numRows; ++i)
{
vv[i].resize(i + 1);//每一行对应开i+1个
vv[i][0] = 1;//每一行的第一个元素为1
vv[i][vv[i].size() - 1] = 1;//每一行的最后一个元素为1
}
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
if (vv[i][j] != 1)
{//不是首尾元素的,则等于上一行同列下标+同列下标-1的值
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
}
return vv;
}
};
四、最小栈
题述:
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x)—— 将元素 x 推入栈中。
- pop()—— 删除栈顶的元素。
- top()—— 获取栈顶元素。
- getMin()—— 检索栈中最小元素
输入:["MinStack","push","push","push","getMin","pop","top","get"]
[[ ],[-2],[0],[-3],[ ],[ ],[ ],[ ]]
输出:
[null,null,null,null,-3,null,0,-2]
题中已给:
class MinStack { public: /* initialize your data with structure here */ MinStack() { } void push(int x) { } void pop() { } int top() { } int getMin() { } };
思路:
①、错误思路:
用两个栈,假设为栈A和栈B,A用来把所有元素一个个入栈,B用来存最小的元素
假如A中要存数据5 2 1 6,那么初始先入5到B,再考虑2,2比5小,删除B的栈顶,再把2入B,再考虑1,1比2小,删除B的栈顶,再把1入B,再考虑6,6没2小,不入B,那么B中最小的就是栈顶了
但是若删除A中数据呢,B也受影响?栈一定是从栈顶开始删的,删除完后再找最小的元素就找不到了,因为之前的都被删除了。但是可以再遍历一遍A,再找最小元素,但这不符合题目规定的常数时间了,故这种方法不可以
②、正确思路:
用两个栈,假设为栈A和栈B,跟错误思路的区别就是,B中不是只存最小的元素,而是在每次比较中,只要比B当前的栈顶元素小或等于的都入B,不删除之前的元素,那么再删除栈顶的时候,只有当B中和A中的元素相同时,才会删除B的栈顶,删除完后的栈顶还是当前最小的元素
代码如下:
//最小元素
class MinStack {
public:
/* initialize your data with structure here */
MinStack() {}//因为栈是自定义类型,他有自己的构造函数,故构造函数可不写
void push(int x) {
_st.push(x);//_st的所有数据都会入栈
if (_minst.empty() || _minst.top() >= x)
{//只有当没数据或栈顶的元素>=要入的数据时,才会入_minst
//重复的最小元素也要入_minst,因为假设有0 1 0三个数先后入_st,那如果
// 不重复入minst,_minst只有0,当删除0时,_minst为空,再访问最小元素就
// 没有了,但理应还返回0,所以重复的元素也要入栈
_minst.push(x);
}
}
void pop() {
if (_st.top() == _minst.top())
_minst.pop();
_st.pop();
}
int top() {
return _st.top();
}
int getMin() {
return _minst.top();
}
stack<int> _st;//所有数据都入栈
stack<int> _minst;//每次的更小值入栈
};
五、栈的压入、弹出序列
题述:
题意是入栈的顺序是固定的,但出栈有好几种顺序,因为可以边进边出,pushV储存的入栈的元素,popV指的是出栈的元素
思路:
在已知出栈顺序的前提下,定义一个栈st,开始先入一个数据到st中,入完一个数据立即比较,比较跟出栈顺序的第一个元素是否相等,相等则出栈,然后比较下一元素,不相等则再入数据到栈中
注意①:出栈是个while循环操作,条件是栈不为空且st中的栈顶=此时popV指向的当前元素才行,栈不为空才出栈因为可能在连续出栈的过程中栈为空了,这时应该再入数据
注意②:入栈直到栈数据全入完了才终止,那么入完了就可以判断顺序是否合法,此时只要栈中还有数据就是不合法,没数据才合法,因为只有出栈入栈的顺序合法才会为空
代码实现:
class Solution {
public:
bool IsPopOrder(vector<int>pushV, vector<int>popV) {
//栈来模拟进出 (用vector类似于用了c语言中的数组)
stack<int> st;
int pushi = 0, popi = 0;//入栈和出栈的下标
while (pushi < pushV.size())//当走到尾就可判定了,但在这之前都要入栈
{
st.push(pushV[pushi]);//入数据
++pushi;
//入完一个数据就要立即跟已给的出栈顺序比较
//因为可能要连续出,所以用while循环
while (!st.empty() && st.top() == popV[popi])
{
//1、出到空就不能再出了,要退出while
//2、若栈顶和已给的出栈顺序的对应位置相同,则进入while
st.pop();
++popi;
}
}
//只有入栈的数据全部进入,且最后栈为空就是合法的顺序
return st.empty();//若为空,则为合法的顺序
}
};