leetcode 栈 I

20. 有效的括号

题目链接

bool isValid(string s) {
	int n = s.size();
	if (n % 2 == 1)
		return false;
	unordered_map<char, char> pairs = {
		{')', '('},
		{']', '['},
		{'}', '{'}
	};
	stack<char> stk;
	for (char ch : s) {
		if (pairs.count(ch)) {
			if (stk.empty() || stk.top() != pairs[ch]) 
				return false;
			stk.pop();
		}
		else
			stk.push(ch);
		}
	return stk.empty();
}			

32. 最长有效括号

题目链接

/*动态规划*/
// dp[i] :以下标 i 字符结尾的最长有效括号的长度。
int longestValidParentheses(string s) {
	int ans = 0, n = s.length();
	vector<int> dp(n, 0);
	for (int i = 1; i < n; i++) {				//i 从 1 开始计数是因为 i = 0 时无论 '(' 还是 ')' 都有 dp[0] = 0
		if (s[i] == ')') {
			if (s[i - 1] == '(')
				dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
			else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(')
				dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;			
			ans = max(ans, dp[i]);
		}
	return ans;
}	
//Time O(n) Space O(n)
/*栈*/
int longestValidParentheses(string s) {
	int ans = 0;
	stack<int> stk;						//入栈的是字符下标
	stk.push(-1);						//防止第一个字符为 ')' 而使得 pop() 操作空栈 
	for (int i = 0; i < s.length(); i++) {
		if (s[i] == '(') 
			stk.push(i);
		else {
			stk.pop();
			if (stk.empty())
				stk.push(i); 			//多余的 ')' 压入栈中,自此以前字符无法再出栈
			else
				ans = max(ans, i - stk.top());
		}
	}
	return ans;
}
//Time O(n) Space O(n)
/*不需额外空间,两个计数器*/
int longestValidParentheses(string s) {
	int left = 0, right = 0, ans = 0;
	for (int i = 0; i < s.length(); i++) {
		if (s[i] == '(') 
			left++;
		else 
			right++;
		if (left = right) 
			ans = max(ans, 2 * right);
		else if (left < right)
			left = right = 0; 
	}
	left = right = 0;
	for (int i = (int)s.length() - 1; i >= 0; i--) {
		if (s[i] == '(') 
			left++;
		else 
			right++;
		if (left == right) 
			ans = max(ans, 2 * left);
		else if (left > right)
			left = right = 0;
	}
	return ans;		
}

42. 接雨水

题目链接

  • 朴素做法:对于数组中每个元素,分别向左和向右扫描并记录左侧和右侧最大的高度,然后计算每个下标位置能接的雨水量。时间复杂度: O(n2)
  • 动态规划:可以在 O(n) 时间内预处理得到每个位置两边的最大高度。
  • 单调栈:维护一个单调栈,单调栈存储的下标,满足从栈底到栈顶的下标对应的数组中元素递减。
  • 双指针:由于 i 处能接的雨水量由 leftMax[i] 和 rightMax[i] 中的最小值决定,于是使用双指针和两个变量代替两个数组。
/*动态规划*/
//正向遍历得到下标 i 及其左侧位置最大高度,反向遍历得到下标 i 及其右侧最大高度。
//leftMax[0] = height[0]
//rightMax[n-1] = height[n-1]
// 1<= i <= n-1, leftMax[i] = max(leftMax[i-1], height[i])
// 0<= i <= n-2, rightMax[i] = max(rightMax[i+1], height[i])
int trap(vector<int> &height) {
	int n = height.size();
	if (n == 0) return 0;
	
	vector<int> leftMax(n);
	leftMax[0] = height[0];
	for (int i = 1; i < n; i++) 
		leftMax[i] = max(leftMax[i-1], height[i]);

	vector<int> rightMax(n);
	rightMax[n - 1] = height[n - 1];
	for (int i = n - 2; i >= 0; --i) 
		rightMax[i] = max(rightMax[i + 1], height[i]);

	int ans = 0;
	for (int i = 0; i < n; ++i)
		ans += min(leftMax[i], rightMax[i]) - height[i];
	return ans;
}
//Time O(n) Space O(n)
/*单调栈*/
//栈中元素永远保持递减,直到有更高的元素进入后开始弹出
int trap(vector<int> &height) {
	int ans = 0, n = height.size();
	stack<int> stk;
	for (int i = 0; i < n; i++) {
		while (!stk.empty() && height[i] > height[stk.top()]) {
			int top = stk.top();
			stk.pop();
			if (stk.empty()
				break;
			int left = stk.top();
			int currWidth = i - left - 1;
			int currHeight = min(height[left], height[i]) - height[top];
			ans += currWidth * currHeight;
		}
		stk.push(i);
	}
	return ans;
}
//Time O(n) Space O(n)
/*双指针*/
//双指针相向而行
int trap(vector<int> &height) {
	int ans = 0;
	int left = 0, right = height.size() - 1;
	int leftMax = 0, rightMax = 0;
	while (left <right) {
		leftMax = max(leftMax, height[left]);
		rightMax = max(rightMax, height[right]);
		if (height[left] < height[right]) {
			ans += leftMax - height[left];
			++left;
		}
		else {
			ans += rightMax - height[right];
			--right;
		}
	}
	return ans;
}

71. 简化路径

题目链接

string simplifyPath(string path) {
	vector<string> temp;
	for (int i = 0; i < path.size(); ){
		if (path[i] == '/')
			i++;
		else {
			int cur = i;
			while (i < path.size() && path[i] != '/') 
				i++;
			string s = path.substr(cur, i - cur);
			if (s == ".." && !temp.empty())
				temp.pop_back();
			else if (s != "." && s != "..") 
				temp.push_back(s);
		}
	}
	if (temp.empty()) return "/";
	string res;
	for (string &s : temp)
		res += "/" + s;
	return res;
}		

388. 文件的最长绝对路径

题目链接

/*栈*/
//若当前结点深度小于当前路径深度,表示当前结点不是栈顶结点的孩子结点,按照先序遍历的顺序,
//则此时需要进行回退到栈顶结点为当前结点的父亲结点,然后再求出当前结点的路径与长度
int lengthLongestPath(string input) {
	int n = input.size(), pos = 0, ans = 0;
	stack<int> stk;
	while (pos < n) {
		//检测当前文件的深度
		int depth = 1;
		while (pos < n && input[pos] == '\t') {		//string 中 \n \t算一个字符,即sizeof("\n") = 2, strlen("\n") = 1
			pos++;
			depth++;
		}
		//统计当前文件名长度
		int len = 0;
		bool isFile = false;
		while (pos < n && input[pos] != '\n') {
			if (input[pos] == '.')
				isFile = true;
			len++;
			pos++;
		}
		//跳过换行符 \n
		pos++;
		while (stk.size() >= depth) 
			stk.pop();
		if (!stk.empty()) 
			len += stk.top() + 1;
		if (isFile) 
			ans = max(ans, len);
		else
			stk.emplace(len);
	}
	return ans;
}
int lengthLongestPath(string input) {
	int n = input.size();
	int pos = 0, ans = 0;
	vector<int> level(n + 1);

	while (pos < n) {
		int depth = 1;
		while (pos < n && input[pos] == '\t') {
			pos++;
			depth++;
		}
		int len = 0;
		bool isFile = false;
		while (pos < n && input[pos] != '\n') {
			if (input[pos] == '.') 
				isFile = true;
			len++;
			pos++;
		}
		pos++;
		if (depth > 1)
			len += level[depth - 1] + 1;
		if (isFile) 
			ans = max(ans, len);
		else
			level[depth] = len;
	}
	return ans;
}	

84. 柱状图中最大的矩形*

题目链接

  • 单调栈时间复杂度:每一个位置只会入栈一次(在枚举到它时)且最多出栈一次。因此当我们从左向右或从右向左遍历数组时,对栈的操作次数为 O(N) 。所以单调栈总时间复杂度为 O(N)
/*暴力*/
//枚举宽
int largestRectangleArea(vector<int> &height) {
	int ans = 0;
	for (int left = 0; left < height.size(); left++) {
		int minHeight = INT_MAX;
		for (int right = left; right < height.size(); right++) {
			minHeight = min(minHeight, height[right]);
			ans = max(ans, (right - left + 1) * minHeight);
		}
	}
	return ans;
}
//枚举高,
int largestRectangleArea(vector<int> &height) {
	int ans = 0;
	for (int mid = 0; mid < n; mid++) {
		int height = height[mid];
		int left = mid, right = mid;
		while (left - 1 >= 0 && height[left - 1] >= height)
			--left;
		while (right + 1 < height.size() && height[right + 1] >= height) 
			++right;
		ans = max(ans, (right - left + 1) * height);
	}
	return ans;
}
/*单调栈*/
//如何求出一根柱子左右两侧且最近的小于其高度的柱子
//对于两根柱子 j0 和 j1(j0 在 j1 左侧且 j0 高于 j1)那么在后面的柱子 i 向左找小于其高度的柱子时,j1 会‘挡住’ j0, j0 就不会作为答案
int largestRectangleArea(vector<int> &height) {
	int n = height.size();
	vector<int> left(n), right(n);
	
	stack<int> mono_stack;
	for (int i = 0; i < n; i++) {
		while (!mono_stack.empty() && height[mono_stack.top()] >= height[i]) 			//进来一个更低的矩形,开始出栈
			mono_stack.pop();
		left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
		mono_stack.push(i);
	}

	mono_stack = stack<int> ();
	for (int i = n - 1; i >= 0; i--) {
		while (!mono_stack.empty() && height[mono_stack.top()] >= height[i]) 
			mono_stack.pop();
		right[i] = (mono_stack.empty() ? n : mono_stack.top());
		mono_stack.push(i);
	}
	
	int ans = 0;
	for (int i = 0; i < n; i++) 
		ans = max(ans, (right[i] - left[i] - 1) * height[i]);
	return ans;
}
//Time O(n) Space O(n)	
/*单调栈 + 常数优化*/
//对位置 i 进行入栈操作时确定了它的左边界。对位置 i 进行出栈操作时可以确定它的右边界
//遍历结束后栈中剩余的位置对应的右边界就是位置为 n 的哨兵。对此可以一次出栈后更新右边界或初始化时直接置所有元素为 n 。
int largestRectangleArea(vector<int> & height) {
	int n = height.size();
	vector<int> left(n), right(n, n);

	stack<int> mono_stack;
	for (int i = 0; i < n; i++) {
		while (!mono_stack.empty() && height[mono_stack.top()] >= height[i]) {
			right[mono_stack.top()] = i;
			mono_stack.pop();
		}
		left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
		mono_stack.push(i);
	}


	int ans = 0;
	for (int i = 0; i < n; i++) 
		ans = max(ans, (right[i] - left[i] - 1) * height[i]);
	return ans;
}		
//Time O(n) Space O(n)

85. 最大矩形

题目链接

/*暴力*/
//
int maximalRectangle(vector<vector<char>> &matrix) {
	int m = matrix.size(), n = matrix[0].size();
	if (m == 0) return 0;
	vector<vector<int>> left(m, vector<int>(n, 0));
	
	//二维数组记录每个元素左边连续 1 的数量
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
			if (matrix[i][j] == '1')
				left[i][j] = (j == 0 ? 0 : left[i][j-1]) + 1;
	
	//对于任意一个点枚举以该点为右下角的全 1 矩阵
	int ret = 0;
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++) {
			if (matrix[i][j] == '0') continue;
			int width = left[i][j];
			int area = width;
			
			//纵向观察该元素列方向上最大矩阵面积,类似Pro84
			for (int k = i - 1; k >= 0; k--) {
				width = min(width, left[k][j]);
				area = max(area, (i - k + 1) * width);
			}
			ret = max(ret, area);
		}
	}
	return ret;
}
//Time O(n * m^2) Space O(mn)
/*单调栈*/
int maximalRectangle(vector<vector<char>> &matrix) {
	int m = matrix.size();
	if (m == 0) return 0;
	int n = matrix[0].size();
	vector<vector<int>> left(m, vector<int>(n, 0));
	for (int i = 0; i < m; i++) 
		for (int j = 0; j < n; j++)
			if (matrix[i][j] == '1')
				left[i][j] = (j == 0 ? 0 : lef[i][j-1]) + 1;
	
	//对于每一列,纵向使用基于柱状图的方法
	int ret = 0;
	for (int j = 0; j < n; j++) {
		vector<int> up(m, 0), down(m, 0);
		stack<int> stk;
		
		for (int i = 0; i < m; i++) {
			while (!stk.empty() && left[stk.top()][j] >= left[i][j]) 
				stk.pop();
			up[i] = stk.empty() ? -1 : stk.top();
			stk.push(i);
		}
		
		stk = stack<int>();
		for (int i = m - 1; i >= 0; i--) {
			while (!stk.empty() && left[stk.top()][j] >= left[i][j]) 
				stk.pop();
			down[i] = stk.empty() ? m : stk.top();
			stk.push(i);
		}
		
		for (int i = 0; i < m; i++) {
			int height = down[i] - up[i] - 1;
			int area = height * left[i][j];
			ret = max(ret, area);
		}
	}
	return ret;
} 
//Time O(mn) Space O(mn)		

150. 逆波兰表达式求值

题目链接

  • 逆波兰表达式是一种后缀表达式,没有括号,运算符总是放在和它相关的操作符之后。
/*栈模拟*/
int evalRPN(vector<string>& tokens) {
    stack<int> stk;
    for (string &s : tokens) {
        if (s == "+" || s == "-" || s == "*" || s == "/") {
            int second = stk.top();
            stk.pop();
            int first = stk.top();
            stk.pop();
            if (s == "+")
                stk.push(first + second);
            else if (s == "-")
                stk.push(first - second);
            else if (s == "*")
                stk.push(first * second);
            else if (s == "/")
                stk.push(first / second);
        }
        else
            stk.push(atoi(s.c_str()));	
    }
    return stk.top();
}

155. 最小栈

题目链接

/*辅助栈*/
class MinStack {
	stack<int> x_stack;
	stack<int> min_stack;
public:
	MinStack() {
		min_stack.push(INT_MAX);
	}
	
	void push(int x) {
		x_stack.push(x);
		min_stack.push(min(min_stack.top(), x));
	}

	void pop() {
		x_stack.pop();
		min_stack.pop();
	}

	int top() {
		return x_stack.top();
	}

	int getMin() {
		return min_stack.top();
	}
};	

244. 基本计算器

题目链接

/*展开括号 + 栈*/
int calculate(string s) {
	stack<int> ops;				//维护一个栈,栈顶元素记录当前位置所处的每个括号所共同造成的符号
	ops.push(1);
	int sign = 1;				//使用一个取值为 {-1, +1} 的整数代表当前的符号
	
	int ret = 0;
	int n = s.length(), i = 0;
	while (i < n) {
		if (s[i] == ' ') {
			i++;
		} else if (s[i] == '+') {
			sign = ops.top();
			i++;
		} else if (s[i] == '-') {
			sign = -ops.top();
			i++;
		} else if (s[i] == '(') {
			ops.push(sign);
			i++;
		} else if (s[i] == ')') {
			ops.pop();
			i++;
		} else {
			long num = 0;
			while (i < n && s[i] >= '0' && s[i] <='9') {
				num = num * 10 + s[i] - '0';				//处理多位
				i++;			
			}
			ret += sign * num;
		}
	}
	return ret;
}		

227. 基本计算器II

题目链接

/*栈*/
int calculate(string s) {
	vector<int> stk;									//向量模拟栈 其内存操作数
	char preSign = '+';									//记录数字前的符号,初始化第一个数之前的符号视为 +
	int num = 0;
	int n = s.length();
	for (int i = 0; i < n; ++i) {
		if (isdigit(s[i]))
			num = num * 10 + int(s[i] - '0');			//高位数
		if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1) {
			switch (preSign) {							//根据 preSign 决定计算方式
				case '+':
					stk.push_back(num);
					break;
				case '-':
					stk.push_back(-num);
					break;
				case '*':
					stk.back() *= num;
					break;
				case '/':
					stk.back() /= num;
					break;
			}
			preSign = s[i];									//记录当下符号作为下一数字的前置符号
			num = 0;										//归零
		}
	}
	return accumulate(stk.begin(), stk.end(), 0);			//求和
}	

225. 用队列实现栈

题目链接

/*两个队列*/
class MyStack {
public:
	queue<int> q1, q2;		//q1 用于存储栈内元素, q2用于入栈操作的辅助队列
	
	MyStack() {}

	void push(int x) {
		q2.push(x);			//初始 q2 为空作为辅助栈
		while (!q1.empty()) {
			q2.push(q1.front());
			q1.pop();
		}
		swap(q1, q2);		//q2 被倒满后变更为 q1栈,原 q1 变空作为辅助栈
	}
	//Time O(n)		 
	
	int pop() {
		int r = q1.front();
		q1.pop();
		return r;
	}

	int top() {
		int r = q1.front();
		return r;
	}

	bool empty() {
		return q1.empty();
	}
};
class MyStack {
public: 
	queue<int> q;
	
	MyStack() {}

	void push(int x) {
		int n = q.size();
		q.push(x);
		for (int i = 0; i < n; i++) {
			q.push(q.front());
			q.pop();
		}
	}
	//Time O(n)
	
	int pop() {
		int r = q.front();
		q.pop();
		return r;
	}

	int top() {
		int r = q.front();
		return r;
	}

	bool empty() {
		return q.empty();
	}			
};	

232. 用栈实现队列

题目链接

/*双栈*/
class MyQueue {
private: 
	stack<int> inStack, outStack;

	void in2out() {
		while (!inStack.empty()) {
			outStack.push(inStack.top());
			inStack.pop();
		}
	}

public: 
	MyQueue() {}
	
	void push(int x) {
		inStack.push(x);
	}

	int pop() {
		if (outStack.empty())
			in2out();
		int x = outStack.top();
		outStack.pop();
		return x;
	}

	int peek() {
		if (outStack.empty()) 
			in2out();
		return outStack.top();
	}

	bool empty() {
		return inStack.empty() && outStack.empty();
	}
};

316. 去除重复字母

题目链接

  • 去重:用一个 vis 数组,如果字符入栈就置其值为1,当他再出现时就直接跳过。
  • 保证最小字典序:当 s[i] > s[i+1] 出现时(即栈中前一个字符字典序大于后一个字符,类似单调栈),s[i] 就应该被弹出,但如果该字符只出现一次就保持现状不能被弹出。用一个 num 数组存储 s 中剩余字符情况,每当我们抛出一个字符或跳过一个数时,其值都要减一。
/*贪心 + 单调栈*/
string removeDuplicateLetters(string s) {
	vector<int> vis(26), num(26);
	for (char ch : s) 					//遍历记录26个字母各出现次数
		num[ch - 'a']++; 				//[ch - 'a']通过这种计算获取 ch 是第几个英文字母 num[0]代表a···
	string stk;							//单调栈 string 本身可以模拟栈的使用,节省空间
	for (char ch : s) {
		if (!vis[ch - 'a']) {
			while (!stk.empty() && stk.back() > ch) {
				if (num[stk.back() - 'a'] > 0) {			//如果是栈顶是多次重复字符
					vis[stk.back() - 'a'] = 0;				//因为要弹栈顶所以重置栈顶字符为未访问
					stk.pop_back();
				} else										//如果栈顶是独苗
					break;
			}
			vis[ch - 'a'] = 1;			//将该字符标为 1 代表 ch 已经访问过
			stk.push_back(ch);			//ch 字符入栈
		}
		num[ch - 'a'] -= 1;				//遍历完该字符后将计数器减一
	}
	return stk;
}			
//Time O(n) Space O(k)  k ~ 26

321. 拼接最大数

题目链接

/* 单调栈 + 自定义比较法 */
//从两个数组中分别选出最大的子序列,然后将两个子序列合并

vector<int> maxNumber(vector<int> &nums1, vector<int> &nums2, int k) {
	int m = nums1.size(), n = nums2.size();
	vector<int> maxSubsequence(k, 0);
	int start = max(0, k - n), end = min(k, m);				//保证满足 0 <= x <= m 和 0 <= y <= n
	for (int i = start; i <= end; i++) {					//遍历每一组的 x 和 y 值
		vector<int> subsequence1(MaxSubsequence(nums1, i));
		vector<int> subsequence2(MaxSubsequence(nums2, k - i));					//两数组分别选出最大的子序列
		vector<int> curMaxSubsequence(merge(subsequence1, subsequence2));		//拼接
		if (compare(curMaxSubsequence, 0, maxSubsequence, 0) > 0) {
			maxSubsequence.swap(curMaxSubsequence);								//更新更大的
		}
	}
	return maxSubsequence;
}

/*单调栈关键步骤,关键点在于 remain 控制了单调栈大小*/
vector<int> MaxSubsequence(vector<int> &nums, int k) {	
	int length = nums.size();
	vector<int> stack(k, 0);							//单调栈用数组实现,数组最左侧为栈底
	int top = -1;										//当前栈顶的数组下标 index
	int remain = length - k;							//需要排除的元素个数
	for (int i = 0; i < length; i++) {					//满足从栈底(vec[0])到栈顶(vec[k])单调递减的单调栈
		int num = nums[i];
		while (top >= 0 && stack[top] < num && remain > 0) {		
			top--;
			remain--;
		}
		if (top < k - 1)
			stack[++top] = num;
		else
			remain--;
	}
	return stack;										//从左向右遍历数组即直接得到最大子序列
}

vector<int> merge(vector<int> &subsequence1, vector<int> &subsequence2) {		//普通合并
	int x = subsequence1.size(), y = subsequence2.size();
	if (x == 0) 
		return subsequence2;
	if (y == 0)
		return subsequence1;
	int mergeLength = x + y;
	vector<int> merged(mergeLength);
	int index1 = 0, index2 = 0;
	for (int i = 0; i < mergeLength; i++) {
		if (compare(subsequence1, index1, subsequence2, index2) > 0) 
			merged[i] = subsequence1[index1++];
		else
			merged[i] = subsequence2[index2++];
	}
	return merged;
}

int compare(vector<int> &subsequence1, int index1, vector<int> &subsequence2, int index2) {
	int x = subsequence1.size(), y = subsequence2.size();
	while (index1 < x && index2 < y) {
		int difference = subsequence1[index1] - subsequence2[index2];
		if (difference != 0)
			return difference;
		index1++;
		index2++;
	}
	return (x - index1) - (y - index2);
}			
//Time O( k(m+n+k^2) ) Space O(k)	 
//两个子序列长度之和为 k ,共 k 种不同长度组合
//得到两个最大序列的时间复杂度为线性 O(m + n)
//合并两个最大子序列,需要 k 次合并,每次合并需要进行比较,最坏情况下比较的时间复杂度 O(k),故合并操作复杂度 O(k^2)
//因此对于每一种组合有时间复杂度 O(m + n + k^2)
/*update version 1.1*/
vector<int> maxNumber(vector<int> &nums1, vector<int> &nums2, int k) {
	vector<int> ans(k,0);
	int n = nums1.size(), m = nums2.size();
	for (int i = max(0, k - m); i <= min(k, n); i++) {
		vector<int> seq1 = get(nums1, i);
		vector<int> seq2 = get(nums2, k - i);
		vector<int> cur = merge(seq1, seq2);
		if (cmp(seq1, 0, ans, 0))
			ans = cur;
	}
	return ans;
}

vector<int> get(vector<int> &nums, int k) {
	int remain = nums.size() - k;				//numbers of elements needed to be removed
	vector<int> seq;
	for (int &x : nums) {
		while (seq.size() && remain > 0 && seq.back() < x) {
			seq.pop_back();
			remain--;
		}
		seq.emplace_back(x);
	}
	for (int i = 0; i < remain; i++)
		seq.pop_back();
	return seq;
}

vector<int> merge(vector<int> &seq1, vector<int> &seq2) {
	int n = seq1.size(), m = seq2.size();
	int i = 0, j = 0;
	vector<int> seq;
	for ( ; i < n && j < m; ) {
		if (cmp(seq1, i, seq2, j))
			seq.emplace_back(seq1[i++]);
		else
			seq.emplace_back(seq2[j++]);
	}
	while (i < n) seq.emplace_back(seq1[i++]);
	while (j < m) seq.emplace_back(seq2[j++]);
	return seq;
}

bool cmp(vector<int> &seq1, int index1, vector<int> &seq2, int index2) {
	int n = seq1.size(), m = seq2.size();
	while (index1 < n && index2 < m) {
		int diff = seq1[index1] - seq2[index2];
		if (diff != 0)
			return diff > 0;
		index1++, index2++;
	}
	return (n - index1) > (m - index2);
}		

341. 扁平化嵌套列表迭代器

题目链接

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * class NestedInteger {
 *   public:
 *     // Return true if this NestedInteger holds a single integer, rather than a nested list.
 *     bool isInteger() const;
 *
 *     // Return the single integer that this NestedInteger holds, if it holds a single integer
 *     // The result is undefined if this NestedInteger holds a nested list
 *     int getInteger() const;
 *
 *     // Return the nested list that this NestedInteger holds, if it holds a nested list
 *     // The result is undefined if this NestedInteger holds a single integer
 *     const vector<NestedInteger> &getList() const;
 * };
 */

/**
 * Your NestedIterator object will be instantiated and called as such:
 * NestedIterator i(nestedList);
 * while (i.hasNext()) cout << i.next();
 */
/* 深度优先搜索 */
//嵌套的整形列表是一个树形结构,树上的叶子结点对应一个整数,非叶结点对应一个列表
//在树上深度优先搜索的顺序就是迭代器遍历的顺序
class NestedIterator {
private:
	vector<int> vals;
	vector<int>::iterator cur;

	void dfs(const vector<NestedInteger> &nestedList) {
		for (auto &nest : nestedList) {
			if (nest.isInteger())
				vals.push_back(nest.getInteger());
			else
				dfs(nest.getList());
		}
	}

public:
	NestedIterator(vector<NestedInteger> &nestedList) {
		dfs(nestedList);
		cur = vals.begin();
	}
	//initialize Time O(n)
	
	int next() {
		return *cur++;
	}
	//O(1)
	
	bool hasNext() {
		return cur != vals.end();
	}
	//O(1)				
};	
/* 栈 */
//用栈来维护深度优先搜索时从根节点到当前结点路径上的所有节点
//由于非叶节点对应的是一个列表,我们在栈中存储的是指向列表当前遍历的元素的指针(下标)
class NestedIterator {
private:
	//pair 中存储的是列表当前遍历位置,以及一个尾后迭代器用于判断是否遍历到了列表末尾
	stack<pair<vector<NestedInteger>::iterator, vector<NestedInteger>::iterator>> stk;

public:
	NestedIterator(vector<NestedInteger> &nestedList) {
		stk.emplace(nestedList.begin(), nestedLisst.end());
	}
	//O(1)

	int next() {
		//调用next之前会调用hasNest,直接返回栈顶列表当前的元素然后迭代器指向下一元素
		return stk.top().first++->getInteger();
	}
	//O(1)
	
	bool hasNest() {
		while (!stk.empty()) {
			auto &p = stk.top();
			if (p.first == p.second) {		//遍历到当前列表末尾出栈
				stk.pop();
				continue;
			}
			if (p.first->isInteger()) 
				return true;
			//若当前元素为列表则入栈,且迭代器指向下一元素
			auto &lst = p.first++->getList();
			stk.emplace(lst.begin(), lst.end());
		}
		return false;
	}
	//均摊为O(1)
};

385. 迷你语法分析器

题目链接

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * class NestedInteger {
 *   public:
 *     // Constructor initializes an empty nested list.
 *     NestedInteger();
 *
 *     // Constructor initializes a single integer.
 *     NestedInteger(int value);
 *
 *     // Return true if this NestedInteger holds a single integer, rather than a nested list.
 *     bool isInteger() const;
 *
 *     // Return the single integer that this NestedInteger holds, if it holds a single integer
 *     // The result is undefined if this NestedInteger holds a nested list
 *     int getInteger() const;
 *
 *     // Set this NestedInteger to hold a single integer.
 *     void setInteger(int value);
 *
 *     // Set this NestedInteger to hold a nested list and adds a nested integer to it.
 *     void add(const NestedInteger &ni);
 *
 *     // Return the nested list that this NestedInteger holds, if it holds a nested list
 *     // The result is undefined if this NestedInteger holds a single integer
 *     const vector<NestedInteger> &getList() const;
 * };
 */
/*深度优先搜索*/
//NestedInteger 是通过递归定义的,因此也可以用递归解析
int index = 0;
NestedInteger deserialize(string s) {
	if (s[index] == '[') {						//代表待解析的是一个列表
		index++;
		NestedInteger ni;
		while (s[index] != ']') {
			ni.add(deserialize(s));				//进入下层递归
			if (s[index] == ',') 
				index++;
		}
		index++;
		return ni;								//返回 NestedInteger 实例
	}
	else {
		bool negative = false;
		if (s[index] == '-') {
			negative = true;
			index++;
		}
		int num = 0;
		while (index < s.size() && isdigit(s[index])) {
			num = num * 10 + s[index] - '0';
			index++;
		}
		if (negative) 
			num *= 1;
		return NestedInteger(num);
	}
}	
//Time O(n) Space O(n)
/*栈*/
NestedInteger deserialize(string s) {
	if (s[0] != '[') 
		return NestedInteger(stoi(s));
	stack<NestedInteger> stk;
	int num = 0;
	bool negative = false;
	for (int i = 0; i < s.size(); i++) {
		char c = s[i];
		if (c == '-')
			negative = true;
		else if (isdigit(c)) 
			num = num * 10 + c -'0';
		else if (c == '[')
			stk.emplace(NestedInteger());			//新的 NestedInteger 实例需要入栈
		else if (c == ',' || c == ']') {			// ',' ']' 表示一个数字或 NestedInteger 实例结束,需要将其加入栈顶的 NestedInteger 实例
			if (isdigit(s[i - 1])) {
				if (negative) 
					num *= -1;
				stk.top().add(NestedInteger(num));
			}
			num = 0;
			negative = false;						//还原标记
			if (c == ']' && stk.size() > 1) {
				NestedInteger ni = stk.top();		//栈顶保存的是最后一个子 NestedInteger 实例 eg:[456]
				stk.pop();							//需要弹出该子实例
				stk.top().add(ni);					//并将此子实例加入其父实例中 eg: [123,[456]]
			}
		}
	}
	return stk.top();
}	
//Time O(n) Space O(n)	

394. 字符串解码

题目链接

/*栈操作*/
string getDigits(string &s, size_t &ptr) {
	string ret = "";
	while (isdigit(s[ptr]))
		ret.push_back(s[ptr++]);
	return ret;
}

string getString(vector<string> &v) {
	string ret;
	for (const auto &s : v)
		ret += s;
	return ret;
}

string decodeString(string s) {
	vector<string> stk;
	size_t ptr = 0;
	while (ptr < s.size()) {
		char cur = s[ptr];
		if (isdigit(cur)) {
			string digits = getDigits(s, ptr);			//解析出多个数位
			stk.push_back(digits);						//并入栈
		} else if (isalpha(cur) || cur == '[') {		
			stk.push_back(string(1, s[ptr++]));			//当前字符为字母或左括号直接入栈
		} else {							
			++ptr;
			vector<string> sub;							//准备承接子串
			while (stk.back() != "[") {					//当前字符为右括号
				sub.push_back(stk.back());				//压入sub子串栈
				stk.pop_back();							//stk弹栈
			}
			reverse(sub.begin(), sub.end());			//翻转出栈顺序
			stk.pop_back();								//左括号出栈
			int repTime = stoi(stk.back());				//此时栈顶为个数
			stk.pop_back();
			string t, o = getStirng(sub);
			while (repTime--) t += o;					//拼接构造字符串
			stk.push_back(t);							//构造好的字符串重新入栈
		}
	}
	return getString(stk);
}
//Time O(S) Space O(S) S for decoded strlen
/*递归*/
class Solution {
public:
	string src;
	size_t ptr;

	int getDigits() {
		int ret = 0;
		while (ptr < src.size() && isdigit(src[ptr])) {
			ret = ret * 10 + src[ptr++] -'0';
		return ret;
	}

	string getString() {
		if (ptr == src.size() || src[ptr] == ']') {
			return "";								//空串或右括号则返回上层
		char cur = src[ptr];
		int repTime = 1;
		if (isdigit(cur)) {
			repTime = getDigits();					//解析 digit
			++ptr;									//过滤左括号
			string str = getString();				//解析 string
			++ptr;									//过滤右括号
			while (repTime--) ret += str;			//构造字符串
		} else if (isalpha(cur)) {
			ret = string(1, src[ptr++]);			//解析 char
		}
		return ret + getString();
	}

	string decodeString(string s) P
		src = s;
		ptr = 0;
		return getString();
	}
};
//Time O(S) Space O(s) S for decoded strlen, s for original strlen

402. 移掉K位数字*

题目链接

/*贪心 + 单调栈*/
//对于两个长度相等的数字序列,高位(左侧)数字决定了数字的大小,若要是的剩下的数字最小,则需要保证靠前的数字尽可能的小
//删除 {一个数字} 的贪心策略:从左往右找到第一个位置满足 D[i] < D[i-1],若不存在则说明整个数字序列单调不降删最后一位即可
//维护一个当前答案序列的栈,其中元素代表截止到当前位置删除不超过k次个数字后所能得到的最小的整数
string removeKdigits(string num, int k) {
	vector<char> stk;
	for (auto &digit : num) {
		while (stk.size() > 0 && stk.back() > digit && k) {		//对于每个数字,若小于栈顶元素则
			stk.pop_back();										//不断弹栈顶元素
			k -= 1;												//更新删除的元素个数
		}														//直到·1栈空·2新栈顶元素不大于当前数字·3已经删除k位
		stk.push_back(digit);
	}

	for (; k > 0; k--)
		stk.pop_back();								//如果已经删除了 m 个数字且不足 k 个,直接从序列尾部删除
	string ans = "";
	bool isLeadingZero = true;
	for (auto &digit : stk) {
		if (isLeadingZero && digit == '0')			//去除前导零
			continue;
		isLeadingZero = false;
		ans += digit;
	} 
	return ans == "" ? "0" : ans;
}
//Time O(n) Space O(n)
//尽管存在嵌套循环,但内部循环最多运行 k 次,朱迅换时间复杂度被限制在 2n 内

456. 132模式

题目链接

/* 枚举 3 号位 */
bool find132pattern(vector<int> &nums) {
	int n = nums.size();
	if (n < 3) return false;
	int left_min = nums[0];								//左侧最小值
	multiset<int> right_all;							//右侧所有元素
	for (int k = 2; k < n; k++)
		right_all.insert(nums[k]);
	for (int j = 1; j < n - 1; j++) {
		if (left_min < nums[j]) {
			auto it = right_all.upper_boumd(left_min);
			if (it != right_all.end() && *it < nums[j]) 
				return true;;
		}
		left_min = min(left_min, nums[j]);
		right_all.erase(right_all.find(nums[j + 1]));
	}
	return false;
}	
//Time O(nlogn) Space O(n)
/* 枚举 1 号位*/
//若从左向右枚举 1 的下标 i,j k 的下标范围是减少的,不利于对其进行维护。考虑从右向左枚举 i 。
//使用一种数据结构维护所有遍历过的元素,它们作为 2 的候选元素
//遍历到一个新的元素的同时我们可以考虑是否作为 3 。若可则数据结构中所有严格小于它的元素都可以作为 2,将这些元素全部从数就结构中移除,并且使用一个变量维护所有被移除的元素的最大值。
//这些被移除的元素都是可以真正作为 2 的,并且yu
bool find132pattern(vector<int> &nums) {
	int n = nums.size();
	stack<int> candidate_k;							//使用单调递减栈作为维护 2 的数据结构
	candidate_k.push(nums[n - 1]);					//初始时入栈最后一个元素				
	int max_k = INT_MIN;							//记录所有可以真正作为 2 的元素的最大值

	for (int i = n - 2; i >= 0; i--) {				//从倒数第二个元素开始从右向左枚举 1 号位	
		//判断 i 是否可以作为 1 号位,可则找到一组
		if (nums[i] < max_k)						
			return true;
		//随后判断 a[i] 是否可以作为 3 号位,以此找出那些可以真正作为 2 号位的元素
		while (!candidate_k.empty() && nums[i] > candidate_k.top()) {	//若 a[i] 较大那么栈顶元素可以真正作为 2 号位
			max_k = candidate_k.top();				//更新栈顶元素
			candidate_k.pop();						//弹出栈顶
		}
		//最后我们将 a[i] 作为候选元素放入单调栈中
		if (nums[i] > max_k)						
			candidate_k.push(nums[i]);
	}
	return false;
}
//Time O(n) Space O(n)

496. 下一个更大元素I

题目链接

/*暴力*/
vector<int> nextGreaterElement(vector<int> &nums1, vector<int> &nums2) {
	int m = nums1.size(), n = nums2.size();
	vector<int> res(m);
	for (int i = 0; i < m; i++) {
		int j = 0;
		while (j < n && nums2[j] != nums1[i]) 
			j++;
		int k = j + 1;
		while (k < n && nums2[k] < nums2[j]) 
			k++;
		res[i] = k < n ? nums2[k] : -1;
	}
	return res;
}
//Time O(mn) Space O(1)	
/* 单调栈 + 哈希表 */
vector<int> nextGreaterElement(vector<int> &nums1, vector<int> &nums2) {
	unordered_map<int, int> hashmap;
	stack<int> stk;											//栈底到栈顶元素递减
	for (int i = nums2.size() - 1; i >= 0; --i) {			//倒序遍历
		int num = nums2[i];
		while (!stk.empty() && num >= stk.top())			//大数新进则弹栈
			stk.pop();
		hashmap[num] = stk.empty() ? -1 : stk.top();
		stk.push(num);
	}
	vector<int> res(nums1.size());
	for (int i = 0; i < nums1.size(); i++)
		res[i] = hashmap[nums1[i]];
	return res;
}

503. 下一更大元素II*

题目链接

/* 单调栈 + 循环数组 */
vector<int> nextGreaterElements(vector<int> &nums) {
	int n = nums.size();
	vector<int> ret(n, -1);
	stack<int> stk;									//单调栈保存下标,元素对应数值单调不升
	for (int i = 0; i < n * 2 - 1; i++) {			//不需要显性地将循环数组拉直,只需要在处理时对下标取模即可
		while (!stk.empty() && nums[stk.top()] < nums[i % n]) {
			ret[stk.top()] = nums[i % n];
			stk.pop();
		}
		stk.push(i % n);
	}
	return ret;
} 

581. 最短无序连续子数组

题目链接

/* 排序 */
int findUnsortedSubarray(vector<int> &nums) {
	if (is_sorted(nums.begin(), nums.end()))
		return 0;
	vector<int> numsSorted(nums);				//复制
	sort(numsSorted.begin(), numsSorted.end());
	int left = 0, right = nums.size() - 1;
	while (nums[left] == numsSorted[left]) {
		left++;
	while (nums[right] == numsSorted[right])
		right--;
	return right - left + 1;
}
//Time O(nlogn) Space O(n)
/* 一次遍历 */
//左段中段右段,找到中段的左右边界
int findUnsortedSubarray(vector<int> &nums) {
	int n = nums.size();
	int minn = INT_MAX, maxn = INT_MIN;
	int begin = -1, end = -1;				
	for (int i = 0; i < n; i++) {
		if (nums[i] >= maxn)
			maxn = nums[i];
		else
			end = i;								//中段右侧边界
		if (nums[n - i - 1] <= minn)		
			minn = nums[n - i - 1];
		else
			begin = n - i - 1;						//中段左边界
	}
	return end == -1 ? 0 : end - begin + 1;			//end = -1 原本有序情况			
}	

591. 标签验证器

题目链接

bool isValid(string code) {
	int n = code.size();
	stack<string> tags;
	
	int i = 0;
	while (i < n) {
		if (code[i] == '<') {
			if (i == n - 1) return false;
			
			if (code[i + 1] == '/') {
				int j = code.find('>', i);
				if (j == string::npos) return false;
				string tagname = code.substr(i + 2, j - (i + 2));
				if (tags.empty() || tags.top() != tagname) return false;
				tags.pop();
				i = j + 1;
				if (tags.empty() && i != n) return false;
			}
			
			else if (code[i + 1] == '!') {
				if (tags.empty()) return false;
				string cdata = code.substr(i + 2, 7);
				if (cdata != "[CDATA[") return false;
				int j = code.find("]]>", i);
				if (j == string::npos) return false;
				i = j + 3;
			}

			else {
				int j = code.find('>', i);
				if (j == string::npos) return false;
				string tagname = code.substr(i + 1, j - (i + 1));
				if (tagname.size() < 1 || tagname.size() > 9) return false;
				if (!all_of(tagname.begin(), tagname.end(), [](unsigned char c) {return isupper(c);})) return false;
				tags.push(move(tagname));
				i = j + 1;
			}
		}
		
		else {
			if (tags.empty()) return false;
			i++;
		}
	}
	return tags.empty();
}					

636. 函数的独占时间

题目链接

/*栈*/
vector<int> exclusiveTime(int n, vector<string> &logs) {
	stack<pair<int, int>> stk;			// <index, startTime>
	vector<int> res(n, 0);
	for (auto &log : logs) {
		char type[10];
		int idx, timestamp;
		sscanf(log.c_str(), "%d:%[^:]:%d", &idx, type, &timestamp);
		
		if (type[0] == 's') {
			if (!stk.empty()) {
				res[stk.top().first] += timestamp - stk.top().second;			
				stk.top().second = timestamp;			//记录本次开始运行的时间,为下次做准备(更新)
			}
			stk.emplace(idx, timestamp);
		} else {
			auto t = stk.top();
			stk.pop();
			res[t.first] += timestamp - t.second + 1;
			if (!stk.empty())
				stk.top().second = timestamp + 1;
		}
	}
	return res;
}
//Time O(n) Space O(n)		 

678. 有效的括号字符串*

题目链接

/* 动态规划 */
bool checkValidString(string s) {
	int n = s.size();
	vector<vector<bool>> dp = vector<vector<bool>> (n, vector<bool> (n, false));

	for (int i = 0; i < n; i++) {
		if (s[i] == '*')
			dp[i][i] = true;
	}
	
	for (int i = 1; i < n; i++) {
		char c1 = s[i - 1];
		char c2 = s[i];
		dp[i - 1][i] = (c1 == '(' || c1 == '*') && (c2 == ')' || c2 == '*');
	}

	for (int i = n - 3; i >= 0; i--) {
		char c1 = s[i];
		for (int j = i + 2; j < n; j++) {
			char c2 = s[j];
			if ((c1 == '(' || c1 == '*') && (c2 == ')' || c2 == '*')) }
				dp[i][j] = dp[i + 1][j - 1];
			}
			for (int k = i; k < j && !dp[i][j]; k++) 
				dp[i][j] = dp[i][k] && dp[k + 1][j];
		}
	}
	return dp[0][n-1];
}
//Time O(n^3) Space O(n^2)
/* 双栈 */
bool checkValidString(string s) {
	stack<int> leftStack;
	stack<int> asteriskStack;
	int n = s.size();

	for (int i = 0; i < n; i++) {
		char c = s[i];
		if (c == '(') 
			leftStack.push(i);
		else if (c == '*')
			asteriskStack.push(i);
		else {
			if (!leftStack.empty())
				leftStack.pop();
			else if (!asteriskStack.empty())
				asteriskStack.pop();
			else
				return false;
		}
	}

	while (!leftStack.empty() && !asteriskStack.empty()) {			//弹完后剩余
		int leftIndex = leftStack.top();
		leftStack.pop();
		int asteriskIndex = asteriskStack.top();
		asteriskStack.pop();
		if (leftIndex > asteriskIndex)								//左括号的下标必须小于星号的下标
			return false;
	}
	
	return leftStack.empty();
} 
//Time O(n) Space O(n)
/* 贪心 */
//遍历过程中维护未匹配的左括号数量可能的最小值和最大值
bool checkValidString(string s) {
	int minCount = 0, maxCount = 0;
	int n = s.size();
	for (int i = 0; i < n; i++) {
		char c = s[i];
		if (c == '(') {
			minCount++;
			maxConnt++;
		} else if (c == ')') {
			minCount = max(minCount - 1, 0);
			maxCount--;
			if (maxCount < 0)					//任何情况下未匹配的左括号数量必须非负
				return false;					//此时情况说明没有左括号和右括号匹配
		} else {
			minCount = max(minCount - 1, 0);	//保证最小值非负
			maxCount++;
		}
	}
	return minCount == 0;
}
//Time O(n) Space O(1)		

682. 棒球比赛

题目链接

int calPoints(vector<string> &ops) {
	int ret = 0;
	vector<int> points;						//向量模拟栈操作节省空间
	for (auto &op : ops) {
		int n = points.size();
		switch (op[0]) {
			case '+':
				ret += points[n - 1] + points[n - 2];
				points.push_back(points[n - 1] + points[n - 2]);
				break;
			case 'D':
				ret += 2 * points[n - 1];
				points.push_back(2 * points[n - 1]);
				break;
			case 'C':
				ret -= points[n - 1];
				points.pop_back();
				break;
			default: 
				ret += stoi(op);
				points.push_back(stoi(op));
				break;
		}
	}
	return ret;
}		

726. 原子的数量

题目链接

string countOfAtoms(string formula) {
	int i = 0, n = formula.length();
	
	auto parseAtom = [&]() -> string {
		string atom;
		atom += formula[i++];		//首字母 formula[i],然后做 i++
		while (i < n && islower(formula[i]))
			atom += formula[i++];
		return atom;
	};

	auto parseNum = [&]() -> int {
		if (i == n || !isdigit(formula[i])) 
			return 1;									//无数字默认1
		int num = 0;
		while (i < n && isdigit(formula[i])) 
			num = num * 10 + int(formula[i++] - '0');	//多位数字
		return num;
	};

	stack<unordered_map<string, int>> st;
	st.push({});
	while (i < n) {
		char ch = formula[i];
		if (ch == '(') {
			i++;
			st.push({});						//将一个空的哈希表压入栈中准备统计括号内的原子数量
		} else if (ch == ')') {
			i++;
			int num = parseNum();				//计数
			auto atomNum = st.top();
			st.pop();							//弹出括号内原子数量
			for (auto &[atom, v] : atomNum) 
				st.top()[atom] += v * num;		//括号内原子数量乘上 num, 加到上一层的原子数量中
		} else {
			string atom = parseAtom();
			int num = parseNum();
			st.top()[atom] += num;
		}
	}

	auto &atomNum = st.top();
	vector<pair<stirng, int>> pairs;
	for (auto &[atom, v] : atomNum) 
		pairs.emplace_back(atom, v);
	sort(pairs.begin(), pairs.end());			//Time O(nlogn)
	
	string ans;
	for (auto &p : pairs) {
		ans += p.first;
		if (p.second > 1)
			ans += to_string(p.second);
	}
	return ans;
}
//Time O(n^2) Space O(n)

735. 行星碰撞

题目链接

vector<int> asteroidCollision(vector<int> &asteroids) {
	vector<int> st;
	for (auto aster : asteroids) {
		bool alive = true;
		while (alive && aster < 0 && !st.empty() && st.back() > 0) {							//栈中只存正反向的星
			alive = st.back() < -aster;
			if (st.back() <= -aster)
				st.pop_back();
		}
		if (alive) 
			st.push_back(aster);
	}
	return st;
}		

736. Lisp语法解析

题目链接

/* 递归解析 */
class Solution {
private: 
	unordered_map<string, vector<int>> scope;					//scope 记录作用域

public:
	int evaluate(string expression) {
		int start = 0;
		return innerEvaluate(expression, start);
	}

	int innerEvaluate(const string &expression, int &start) {
		if (expression[start] != '(') {							//非表达式	
			if (islower(expression[start])) {					//变量	
				string var = parseVar(expression, start);	
				return scope[var].back();
			} else {											//整数
				return paresInt(expression, start);
			}
		}
		int ret;												//expression[start] == (
		start++;												//跳过左括号
		if (expression[start] == 'l') {
			start += 4;											//let 表达式 
			vector<string> vars;
			while (true) {
				if (!islower(expression[start])) {
					ret = innerEvaluate(expression, start);		//let 表达式的最后一个 expr 表达式的值,需要进入下层递归
					break;										//eg: (let x 3 y 2 x (add x y) (add x y)) -> ==5	
				}
				string var = parseVar(expression, start);
				if (expression[start] == ')') {
					ret = scope[var].back();					//let 表达式的最后一个 expr 表达式的值
					break;
				}
				vars.push_back(var);
				start++;										//空格
				int e = innerEvaluate(expression, start);	
				scope[var].push_back(e);
				start++;										//空格	
			}
			for (auto var : vars)
				scope[var].pop_back();							//清除当前作用域的变量
		} 
		else if (expression[start] == 'a') {					//add 表达式
			start += 4;
			int e1 = innerEvaluate(expression, start);
			start++;
			int e2 = innerEvaluate(expression, start);
			ret = e1 + e2;
		} 
		else {													//mult 表达式
			start += 5;
			int e1 = innerEvaluate(expression, start);
			start++;
			int e2 = innerEvaluate(expression, start);
			ret = e1 * e2;
		}
		start++;												//右括号
		return ret;
	}

	int parseInt(const string &expression, int &start) {		//解析整数
		int n = expression.size();
		int ret = 0, sign = 1;
		if (expression[start] == '-') {							//负数
			sign = -1;
			start++;
		}
		while (start < n && isdigit(expression[start])) {
			ret = ret * 10 + (expression[start] - '0');
			start++;
		}
		return sign * ret;
	}

	string parseVar(const string &expression, int &start) {		//解析变量字符
		int n = expression.size();
		string ret;
		while (start < n && expression[start] != ' ' && expression[start] != ')') {
			ret.push_back(expression[start]);
			start++;
		}
		return ret;
	}
};	

739. 每日温度

题目链接

/* 不太一样的暴力 */
vector<int> dailyTemperatures(vector<int>& temperatures) {
	int n = temperatures.size();
	vector<int> ans(n), next(101, INT_MAX);				//由于温度范围有限,因此可以维护一个数组 next 记录每个温度第一次出现的下标
	for (int i = n - 1; i >= 0; --i) {					//倒序遍历
		int warmerIndex = INT_MAX;
		for (int t = temperatures[i] + 1; t <= 100; t++) 
			warmerIndex = min(warmerIndex, next[t]);
		if (warmerIndex != INT_MAX) 
			ans[i] = warmerIndex - i;
		next[temperatures[i]] = i;
	}
	return ans;
}
//Time O(mn) Space O(m)
/* 单调栈 */
vector<int> dailyTemperatures(vector<int> &temperatures) {
	int n = temperatures.size();
	vector<int> ans(n);
	stack<int> s;
	for (int i = 0; i < n; i++) {
		while (!s.empty() && temperatures[i] > temperatures[s.top()]) {
			int previousIndex = s.top();
			ans[previousIndex] = i - previousIndex;
			s.pop();
		}
		s.push(i);
	}
	return ans;
}
//Time O(n) Space O(n)

768. 最多能完成排序的块II

题目链接

/* 排序 + 哈希表 */
int maxChunksToSorted(vector<int> &arr) {
	unordered_map<int, int> cnt;
	int res = 0;
	vector<int> sortedArr = arr;
	sort(sortedArr.begin(), sortedArr.end());
	for (int i = 0; i < sortedArr.size(); i++) {
		int x = arr[i], y = sortedArr[i];
		cnt[x]++;
		if (cnt[x] == 0) cnt.erase(x);
		cnt[y]--;
		if (cnt[y] == 0) cnt.erase(y);
		if (cnt.size() == 0) res++;
	}
	return res;
}
//Time O(nlogn) Space O(n)
/* 单调栈 */
//利用结论:右边块的所有数字均大于或等于左边块的所有数字。寻找每块右边界最大值
int maxChunksToSorted(vector<int> &arr) {
	stack<int> st;
	for (auto &num : arr) {
		if (st.empty() || num >= st.top())
			st.emplace(num);
		else {
			int mx = st.top();
			st.pop();
			while (!st.empty() && st.top() > num) 
				st.pop();
			st.emplace(mx);
		}
	}
	return st.size();
}	

769. 最多能完成的排序的块

题目链接

/* 暴力 */
int maxChunksToSorted(vector<int> & arr) {
	int ans = 0, max = 0;
	for (int i = 0; i < arr.size(); i++) {
		max = fmax(max, arr[i]);
		if (max == i) ans++;
	}
	return ans;
}

844. 比较含退格的字符串

题目链接

/* 栈 */
bool backspaceCompare(string S, string T) {
	return build(S) == build(T);
}
string build(string str) {
	string ret;
	for (char ch : str) {
		if (ch != '#') 
			ret.push_back(ch);
		else if (!ret.empty())
			ret.pop_back();
	}
	return ret;
}
//Time O(m + n ) Space O(m + n)			
/* 双指针 */
bool backspaceCompare(string s, string t) {
	int i = s.length() - 1, j = t.length() - 1;
	int skipS = 0, skipT = 0;			//表示当前待删除的字符数量
	while (i >= 0 || j >= 0) {			//逆序遍历
		while (i >= 0) {				//处理 # 出现的情况
			if (s[i] == '#')
				skipS++, i--;
			else if (skipS > 0)
				skipS--, i--;
			else 
				break;
		}
		while (j >= 0) {
			if (t[j] == '#')
				skipT++, j--;
			else if (skipT > 0)
				skipT--, j--;
			else 
				break;
		}
		if (i >= 0 && j >= 0) {
			if (s[i] != t[j]) 
				return false;
		} else {
			if (i >= 0 || j >= 0)			//某个字符串还有剩余
				return false;
		}
		i--, j--;
	}
	return true;
}	
//Time O(n + m) Space O(1)

853. 车队*

题目链接

/* 排序 + 单调栈*/
//相邻的两辆车,如果后车追不前车则从后者开始若干车会组成车队,如果能够追上则和前者形成车队
int carFleet(int target, vector<int> &position, vector<int> &speed) {	
	map<int, int> ps;									//map自动排序
	for (int i = 0; i < position.size(); i++)
		ps[position[i]] = speed[i];
	stack<float> st;
	for (auto &[pos, spd] : ps) {
		float time = float(target - pos) / spd;
		while (!st.empty() && time >= st.top()) 		//保证单调栈递减
			st.pop();
		st.push(time);
	}
	return st.size();
}	
//Time O(n) Space O(n)		 

856. 括号的分数

题目链接

/* 栈 */
int scoreOfParentheses(string s) {
	stack<int> st;
	st.push(0);
	for (char c : s) {
		if (c == '(')
			st.push(0);							//当前深度增加
		else {
			int v = st.top();					//当前深度得分
			st.pop();
			int w = st.top();
			st.pop();
			st.push(w + fmax(2 * v, 1));
		}
	}
	return st.top();
}
//Time O(n) Space O(n)	
/* 统计核心数目 */
int scoreOfParentheses(string s) {
	int ans = 0, bal = 0;
	for (int i = 0; i < s.size(); i++) {
		if (s[i] == '(')
			bal++;
		else {
			bal--;
			if (s[i - 1] == '(')			//只有()会对字符串 s 贡献实质的分数
				ans += 1 << bal;
		}
	}
	return ans;
}
//Time O(n) Space O(1)	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值