155. 最小栈
设计一个支持
push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
push(x)
—— 将元素 x 推入栈中。pop()
—— 删除栈顶的元素。top()
—— 获取栈顶元素。getMin()
—— 检索栈中的最小元素。示例:
输入: ["MinStack","push","push","push","getMin","pop","top","getMin"] [[],[-2],[0],[-3],[],[],[],[]] 输出: [null,null,null,null,-3,null,0,-2] 解释: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2.
提示:
pop
、top
和getMin
操作总是在 非空栈 上调用。
class MinStack {
public:
/** initialize your data structure here. */
stack<int> myStack;
stack<int> minStack;
MinStack() {
}
void push(int x) {
myStack.push(x);
if(minStack.empty() || minStack.top() >= x) minStack.push(x);
}
void pop() {
if(myStack.top()==minStack.top()) minStack.pop();
myStack.pop();
}
int top() {
return myStack.top();
}
int min() {
return minStack.top();
}
};
特别注意:if(minStack.empty() || minStack.top() >= x)
在最小栈为空时,要先push进一个最小值
剑指 Offer 40. 最小的k个数
输入整数数组
arr
,找出其中最小的k
个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。示例 1:
输入:arr = [3,2,1], k = 2 输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1 输出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> res;
priority_queue<int, vector<int>, greater<int> > c;
for(auto i:arr)
c.push(i);
for(int i=0;i<k;i++){
res.push_back(c.top());
c.pop();
}
return res;
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> res;
priority_queue<int> c;
if(k==0)return res;
for(int i=0;i<k;i++)
c.push(arr[i]);
for(int i=k;i<arr.size();i++){
if(arr[i]<c.top()){
c.pop();
c.push(arr[i]);
}
}-
for(int i=0;i<k;i++){
res.push_back(c.top());
c.pop();
}
return res;
}
快排法???
232. 用栈实现队列
难度简单287
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(
push
、pop
、peek
、empty
):实现
MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你只能使用标准的栈操作 —— 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
进阶:
- 你能否实现每个操作均摊时间复杂度为
O(1)
的队列?换句话说,执行n
个操作的总时间复杂度为O(n)
,即使其中一个操作可能花费较长时间。示例:
输入: ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []] 输出: [null, null, null, 1, 1, false] 解释: MyQueue myQueue = new MyQueue(); myQueue.push(1); // queue is: [1] myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) myQueue.peek(); // return 1 myQueue.pop(); // return 1, queue is [2] myQueue.empty(); // return false
提示:
1 <= x <= 9
- 最多调用
100
次push
、pop
、peek
和empty
- 假设所有操作都是有效的 (例如,一个空的队列不会调用
pop
或者peek
操作)
class MyQueue {
public:
/** Initialize your data structure here. */
stack<int> pushStack;
stack<int> popStack;
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
pushStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
int res = peek();
popStack.pop();
return res;
}
/** Get the front element. */
int peek() {
if(popStack.empty()){
while(!pushStack.empty()){
popStack.push(pushStack.top());
pushStack.pop();
}
}
return popStack.top();
}
/** Returns whether the queue is empty. */
bool empty() {
if(pushStack.empty()&&popStack.empty())
return true;
return false;
}
};
71. 简化路径
难度中等250
给你一个字符串
path
,表示指向某一文件或目录的 Unix 风格 绝对路径 (以'/'
开头),请你将其转化为更加简洁的规范路径。在 Unix 风格的文件系统中,一个点(
.
)表示当前目录本身;此外,两个点 (..
) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//'
)都被视为单个斜杠'/'
。 对于此问题,任何其他格式的点(例如,'...'
)均被视为文件/目录名称。请注意,返回的 规范路径 必须遵循下述格式:
- 始终以斜杠
'/'
开头。- 两个目录名之间必须只有一个斜杠
'/'
。- 最后一个目录名(如果存在)不能 以
'/'
结尾。- 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含
'.'
或'..'
)。返回简化后得到的 规范路径 。
示例 1:
输入:path = "/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:path = "/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。
示例 3:
输入:path = "/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:path = "/a/./b/../../c/" 输出:"/c"
提示:
1 <= path.length <= 3000
path
由英文字母,数字,'.'
,'/'
或'_'
组成。path
是一个有效的 Unix 风格绝对路径。
class Solution {
public:
string simplifyPath(string path) {
string res="";
int pathSize = path.size();
vector<string> pathStack;
int start =0;
int end =0;
int i =0;
while(i<pathSize){
while(i<pathSize && path[i]=='/') i++;
start = i;
while(i<pathSize && path[i]!='/') i++;
end = i;
if(end==start) break;
string word(path.begin()+start,path.begin()+end);
if(word=="..")
{
if(!pathStack.empty())pathStack.pop_back();
}
else if(word!=".") pathStack.push_back(word);
}
for(auto j :pathStack){
res.append("/");
res.append(j);
}
return res=="" ? "/":res;
}
};
388. 文件的最长绝对路径
难度中等67
假设文件系统如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXISuphs-1627553827076)(栈与优先队列.assets/mdir.jpg)]
这里将
dir
作为根目录中的唯一目录。dir
包含两个子目录subdir1
和subdir2
。subdir1
包含文件file1.ext
和子目录subsubdir1
;subdir2
包含子目录subsubdir2
,该子目录下包含文件file2.ext
。在文本格式中,如下所示(⟶表示制表符):
dir ⟶ subdir1 ⟶ ⟶ file1.ext ⟶ ⟶ subsubdir1 ⟶ subdir2 ⟶ ⟶ subsubdir2 ⟶ ⟶ ⟶ file2.ext
如果是代码表示,上面的文件系统可以写为
"dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"
。'\n'
和'\t'
分别是换行符和制表符。文件系统中的每个文件和文件夹都有一个唯一的 绝对路径 ,即必须打开才能到达文件/目录所在位置的目录顺序,所有路径用
'/'
连接。上面例子中,指向file2.ext
的绝对路径是"dir/subdir2/subsubdir2/file2.ext"
。每个目录名由字母、数字和/或空格组成,每个文件名遵循name.extension
的格式,其中名称和扩展名由字母、数字和/或空格组成。给定一个以上述格式表示文件系统的字符串
input
,返回文件系统中 指向文件的最长绝对路径 的长度。 如果系统中没有文件,返回0
。示例 1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hQo61Nhx-1627553827079)(栈与优先队列.assets/dir1.jpg)]
输入:input = "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" 输出:20 解释:只有一个文件,绝对路径为 "dir/subdir2/file.ext" ,路径长度 20 路径 "dir/subdir1" 不含任何文件
示例 2:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJFANJNf-1627553827081)(栈与优先队列.assets/dir2.jpg)]
输入:input = "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" 输出:32 解释:存在两个文件: "dir/subdir1/file1.ext" ,路径长度 21 "dir/subdir2/subsubdir2/file2.ext" ,路径长度 32 返回 32 ,因为这是最长的路径
示例 3:
输入:input = "a" 输出:0 解释:不存在任何文件
示例 4:
输入:input = "file1.txt\nfile2.txt\nlongfile.txt" 输出:12 解释:根目录下有 3 个文件。 因为根目录中任何东西的绝对路径只是名称本身,所以答案是 "longfile.txt" ,路径长度为 12
提示:
1 <= input.length <= 104
input
可能包含小写或大写的英文字母,一个换行符'\n'
,一个指表符'\t'
,一个点'.'
,一个空格' '
,和数字。
class Solution {
public:
string res;
int lengthLongestPath(string input) {
int inputSize = input.size();
int maxLength = 0;
int start = 0;
int end =0;
int i = 0;
vector<int> pathLength(50,0);
while(i<inputSize){
int deepth =0;
while(input[i]=='\t')
{
i++;
deepth++;
}
start =i++;
while(i<inputSize && input[i]!='\n')i++;
end =i++;
if(start!= end)
{
string str(input.begin()+start, input.begin()+end);
if(deepth == 0){
if(str.find(".")!=string::npos) maxLength = str.size() > maxLength ? str.size() : maxLength;
pathLength[deepth] = str.size() +1;
}
else{
if(str.find(".")!=string::npos){
maxLength = pathLength[deepth-1]+str.size() > maxLength ? pathLength[deepth-1]+str.size() : maxLength;
}
else{
pathLength[deepth] = pathLength[deepth-1] + str.size() +1;
}
}
}
}
return maxLength;
}
};
358. K 距离间隔重排字符串
难度困难58
给你一个非空的字符串 s 和一个整数 k,你要将这个字符串中的字母进行重新排列,使得重排后的字符串中相同字母的位置间隔距离至少为 k。
所有输入的字符串都由小写字母组成,如果找不到距离至少为 k 的重排结果,请返回一个空字符串
""
。示例 1:
输入: s = "aabbcc", k = 3 输出: "abcabc" 解释: 相同的字母在新的字符串中间隔至少 3 个单位距离。
示例 2:
输入: s = "aaabc", k = 3 输出: "" 解释: 没有办法找到可能的重排结果。
示例 3:
输入: s = "aaadbbcc", k = 2 输出: "abacabcd" 解释: 相同的字母在新的字符串中间隔至少 2 个单位距离。
// 本题用大顶堆存储数据和剩余次数,用窗口存储当前K个字符。基本思路是:
// 每次我们要取的是剩余次数的最大的字符
// 每取一次,将此字符加入窗口(窗口大小不大于K),记录下此字符,该字符次数减一,从堆中删除此字符;
// 当窗口中长度大于k,再将删除的字符加入到堆中。
class Solution {
public:
string rearrangeString(string s, int k) {
vector<int> lettersNum(26,0);
string res;
for(int i: s){
lettersNum[i-'a']++;
}
priority_queue<pair<int,char>> lettersHeap;
for(int i=0;i<26;i++){
if(lettersNum[i]!=0)lettersHeap.push(make_pair(lettersNum[i],'a'+i));
}
queue<char> window;
while(!lettersHeap.empty()){
char maxChar = lettersHeap.top().second;
lettersHeap.pop();
lettersNum[maxChar-'a']--;
res+=maxChar;
window.push(maxChar);
if(window.size()>=k){
char temp = window.front();
window.pop();
if(lettersNum[temp-'a']>0)
lettersHeap.push(make_pair(lettersNum[temp-'a'],temp));
}
}
if(res.size()==s.size()) return res;
return "";
}
};
402. 移掉K位数字
难度中等533
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
- num 的长度小于 10002 且 ≥ k。
- num 不会包含任何前导零。
示例 1 :
输入: num = "1432219", k = 3 输出: "1219" 解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。
示例 2 :
输入: num = "10200", k = 1 输出: "200" 解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :
输入: num = "10", k = 2 输出: "0" 解释: 从原数字移除所有的数字,剩余为空就是0。
class Solution {
public:
string removeKdigits(string num, int k) {
if(k==0) return num;
vector<int> reservedNum;
reservedNum.push_back(num[0]);
int deleteNum=0;
for(int i=1;i<num.size();i++){
while(deleteNum<k && !reservedNum.empty() && num[i]<reservedNum.back()){
reservedNum.pop_back();
deleteNum++;
}
if(reservedNum.empty()&&num[i]=='0')continue;
else
reservedNum.push_back(num[i]);
}
string res;
int n = reservedNum.size()>num.size()-k ?num.size()-k:reservedNum.size();
for(int i=0;i<n;i++){
res+=reservedNum[i];
}
return res.size() == 0 ? "0" : res;
}
};
316. 去除重复字母
难度中等490
给你一个字符串
s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。**注意:**该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters 相同
示例 1:
输入:s = "bcabc" 输出:"abc"
示例 2:
输入:s = "cbacdcbc" 输出:"acdb"
提示:
1 <= s.length <= 104
s
由小写英文字母组成
class Solution {
public:
string removeDuplicateLetters(string s) {
vector<int> chTimes(26,0);
for(auto i : s){
chTimes[i-'a']++;
}
vector<char> reserveCh;
for(int i = 0;i<s.size();i++){
if(find(reserveCh.begin(),reserveCh.end(),s[i])==reserveCh.end()){
while(!reserveCh.empty() && s[i]<reserveCh.back() && chTimes[reserveCh.back()-'a']>0){
reserveCh.pop_back();
}
reserveCh.push_back(s[i]);
}
chTimes[s[i]-'a']--;
}
string res;
for(auto i : reserveCh) res+=i;
return res;
}
};
uplicateLetters(string s) {
vector chTimes(26,0);
for(auto i : s){
chTimes[i-‘a’]++;
}
vector reserveCh;
for(int i = 0;i<s.size();i++){
if(find(reserveCh.begin(),reserveCh.end(),s[i])==reserveCh.end()){
while(!reserveCh.empty() && s[i]<reserveCh.back() && chTimes[reserveCh.back()-‘a’]>0){
reserveCh.pop_back();
}
reserveCh.push_back(s[i]);
}
chTimes[s[i]-‘a’]–;
}
string res;
for(auto i : reserveCh) res+=i;
return res;
}
};