day 1 数组专题(二分查找+双指针等)
二分查找
确定循环不变量:
如果是取 区间左闭右开,则每次循环都要保证得到的区间是左闭右开的。
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size();
//以左闭右开方式二分,则r取最后一个元素的下一位置即算法运行区间为:[l,r);
int mid;
while(l < r){ //取l < r而不取l <= r的原因:循环条件l<r本质上是指循环内部能取l但不能取r,从而与左闭右开的初始状态保持一致。
mid = l + r >> 1; //只能下取整,当区间为[l,l+1)时,此时r=l+1,mid取值下取整刚好是区间中唯一能取到的元素,同样与左闭右开的初始态保持一致。
if(nums[mid] < target) l = mid + 1; //l为左区间断点,能取到,一旦判断出mid位置不符,赋值就跳过该值,避免出现死循环:当区间只有两个元素时,取l = mid会使mid和l一直在同一位置无限循环这条语句。
else if(nums[mid] > target) r = mid; //r为右区间端点,其指向的值不能取到, mid - 1位置的值还未与target比较,不能将r = mid - 1,不然每次循环都会漏掉这个位置
else return mid;
}
return -1;
}
};
左开右闭或者左闭右闭同理。关键是抓住“每次循环划分出的区间要保持:对元素的处理不漏不重”
双指针
解法1
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0, fast = 0;
for(;fast < nums.size(); fast++) //fast遍历寻找不是val的元素,slow接收fast找到的元素
{
if(nums[fast] != val)
{
nums[slow] = nums[fast];
slow++;
}
}
return slow;//最后slow一定小于等于fast,slow为有效元素的下一个位置,数值上等于有效元素的个数
}
};
解法2
day 2 双指针、滑动窗口、二分、前缀和、排序
977.有序数组的平方
思想:
双指针算法,设一个头指针front和一个尾指针tail。由于数组元素非递减排列,所以要求的平方数数组高位的元素一定从原数组的两头产生,循环比较:头尾指针指向的值的平方的大小,并取大者放入新数组高位,退出循环时条件:front等于tail
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int front = 0, tail = nums.size() - 1;
vector<int> result;
while(front <= tail)//头尾没相遇则循环取平方大的数
{
if(nums[front] * nums[front] >= nums[tail] * nums[tail] )
{
result.push_back(nums[front] * nums[front]);
front++;
}
else
{
result.push_back(nums[tail] *nums[tail]);
// if(nums[tail] > nums[front]) swap(nums[tail],nums[front]);
tail--;
}
}
//因为push_back()只能将元素放到vector尾部,故倒转一下再返回。
int cnt = (nums.size() - 1);
for(auto a : result)
nums[cnt--] = a;
return nums;
}
};
209.长度最小的子数组
有三种方法:
1.暴力即两重for循环(leetcode加强了数据已经过不了了)
第一重遍历作为子数组起始位置,第二重遍历当前其实位置开始有多少符合大小的子数组,取最小者。
两重循环完,能找出最小长度的子数组。
复杂度为O()
可以用二分优化第二重循环的搜索,但要构造合适的二分使用条件。
2.二分+前缀和
在暴力的基础上,暴力解法中每轮循环都要重复计算“部分”前缀和,比如当target = 10,前三个数组元素为[1]、[2]、[6],那么每次都要计算[1] +[2],[1]+[2]+[3];
于是想到用前缀和优化,
前缀和可以通过O(1)的时间复杂度得到子数组的元素和;如
prefix_sum[i] - prefix_sum[j];
,就表示从数组i + 1到j的元素和。由于要找满足条件:
prefix_sum[i] - prefix_sum[j] >= target;
的j,又希望可以用二分优化,可以将上式化为prefix_sum[i] >= prefix_sum[j] + target;即在前缀和数组中查找满足条件:大于等于prefix_sum[j] + target的i;
代码如下:(力扣官方题解)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int ans = INT_MAX;
vector<int> sums(n + 1, 0);
// 为了方便计算,令 size = n + 1
// sums[0] = 0 意味着前 0 个元素的前缀和为 0
// sums[1] = A[0] 前 1 个元素的前缀和为 A[0]
// 以此类推
for (int i = 1; i <= n; i++) {
sums[i] = sums[i - 1] + nums[i - 1];
}
for (int i = 1; i <= n; i++) {
int target = s + sums[i - 1];
auto bound = lower_bound(sums.begin(), sums.end(), target);
if (bound != sums.end()) {
ans = min(ans, static_cast<int>((bound - sums.begin()) - (i - 1)));
}
}
return ans == INT_MAX ? 0 : ans;
}
};
3 滑动窗口
代码简单,注意窗口前后端的移动!!
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum = 0;
int len = 0;
int result = INT32_MAX;
for(int i = 0,j = 0; i < nums.size(); i ++)
{
sum += nums[i];
while(sum >= target)
{
len = i - j + 1;
result = result > len ? len : result;
sum -= nums[j++];//窗口缩小,并从sum中减去移出窗口的值
}
}
return result == INT32_MAX ? 0:result;
}
};
边界条件很多,涉及四个循环,环环相扣,注意每个都要保持步调一致即不能破坏循环不变量;循环不变量可以仍选:左闭右开,左开右闭,等等。
代码随想录题解:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
day 3 链表专题(1)
203.移除链表元素
需要说明的是力扣上的头结点和高校教材上的头结点定义不同:
力扣中的头结点是存储了数据的真实结点,可以认为力扣的定义和算法导论一致,但是算法导论用的是“链表头”而力扣用的是“头结点”;为统一链表各结点的操作而设置的空结点称为哨兵(哑对象,不带有效数据)。
国内的教材统一定义为:头结点不含有效数据,是为统一链表各结点的操作而设置的;由此链表可分为带头结点和不带头结点的两种。(计算机408统考中也是这种定义)。
搞清楚题设后就是本题就是简单的链表删除操作:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *real_head = new ListNode(0,head);
ListNode *p =real_head;
if(real_head->next != nullptr)
{
while(head != nullptr)
{
if(head->val != val)
{
head = head->next;
p = p->next;
}
else
{
auto q = head;
head = head->next;
p->next = head;
delete q;
}
}
return real_head->next;
}
else return nullptr;
}
};
707.设计链表
手写一个链表类,并实现类的成员函数:头插法、尾插法、指定位置插入、指定位置删除、返回指定位置的值。可以用单链表也可以用双链表实现。
需要注意的点:
每个结点都有相同的属性,但是不同位置的结点可能对属性的使用不一样;例如:如果链表类含有长度len成员变量,则只需要在头结点中维护即可,其余结点中len一直保持初始化时的值,也即是头结点描述了整个链表的性质,从而用到了更多的属性,其余结点只描述本结点的信息,只需要用少部分的属性,如data,next;
另外要搞清楚链表长度和结点编号的关系,选择带头结点的实现是最方便的,因为其中头结点可以存储整个链表的性质。
以下为单链表实现:
class MyLinkedList {
private:
int val;
MyLinkedList *next;
int len;
public:
MyLinkedList() {//构造函数
this->val = 0;
this->next = nullptr;
//this.tail = nullptr;
this->len = 0;
}
int get(int index) {//返回第index+1个结点的val(节点从0开始编号,故index+1)
if(this->len > 0 && index >=0 && index < len)
{
auto q = this;
while(index >= 0)
{
q = q->next;
index--;
}
return q->val;
}
else return -1;
}
void addAtHead(int val) {//头插法插入新结点
MyLinkedList *p = new MyLinkedList();
p->val = val;
p->next = this->next;
this->next = p;
this->len++;
}
void addAtTail(int val) {//尾插法插入新结点
MyLinkedList *p = new MyLinkedList();
p->val = val;
//p.next = nullptr;
auto q = this;
while(q->next != nullptr)
q = q->next;
q->next = p;
this->len++;
}
void addAtIndex(int index, int val) {//根据给出的位置插入结点:插入到下标为index的结点之前,index为最后一个元素的下一个位置时(即index为链表长度)直接插入尾部;index大于链表长度,参数违法什么也不做。
if(index == this->len)
{
addAtTail(val);
return;
}
if(index == 0)
{
addAtHead(val);
return;
}
if(index > this->len) return;
MyLinkedList *p = new MyLinkedList();
p->val = val;
auto q = this;
while(index > 0)//找到插入位置
{
q = q->next;
index--;
}
p->next = q->next;
q->next = p;
this->len++;
}
void deleteAtIndex(int index) {//在合法范围内,删除下标为index的结点,否则什么也不做
if(index < 0) return;
if(index >= this->len) return;
auto q = this;
while(index > 0)
{
q = q->next;
index--;
}
auto p = q->next;
q->next = p->next;
this->len--;
delete p;
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
206.反转链表
直接了当的做法就是把整个链表作为输入,从头到尾遍历每个结点,并用头插法再生成一个链表,生成的就是原链表的反转了。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
auto old_head = head;
ListNode *new_head = new ListNode();
while(old_head != nullptr)
{
auto p = old_head->next;
old_head->next = new_head->next;
new_head->next = old_head;
old_head = p;
}
return new_head->next;
}
};
day 4 链表专题(2)
leetcode的链表题代码规范要求太严格了,自己写第一遍老是报错:heap-use-after-free ,也怪自己没养成良好习惯,debug都搞半天;
链表规范:
1. 任何结点只要逻辑上无后继结点必须置nullptr,不然后面用的时候leetcode就不让你再往后面加结点了,即使你只是改最后一个结点的next指针;
2. 使用指针前都要判空,以免后面代码出现访问指向类对象的空指针的成员变量的错误,即使你可能不会访问。
24. 两两交换链表中的节点
注意代码规范本体不难,关键在于画图理清步骤和结点间断链之后要确保不丢失结点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *dummy_head = new ListNode();
dummy_head->next = head;
auto cur = dummy_head;
if(cur->next == nullptr || cur->next->next == nullptr) return head;
while(cur->next != nullptr && cur->next->next != nullptr)
{
auto tmp1 = cur->next->next;
auto tmp2 = cur->next->next->next;
cur->next->next = tmp2;
tmp1->next = cur->next;
cur->next = tmp1;
cur = tmp1->next;
}
return dummy_head->next;
}
};
19.删除链表的倒数第N个节点
双指针:要找倒数第n个,可转化为找离终点距离为n的结点,设置两个指针,只要保持两个指针距离为n,则可使后一个指针到达尾端时,前一个指针与尾端的距离为n。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummy_head = new ListNode();//设置一个哨兵头结点
dummy_head->next = head;//哨兵指向头结点
ListNode *fast = dummy_head, *slow = dummy_head;
int cnt = 0;
while(slow->next != nullptr)
{
slow = slow->next;
cnt++;
if(cnt >= n + 1)
{
fast = fast->next;
}
}
//if(fast == dummy_head) return nullptr;
fast->next = fast->next->next;
return dummy_head->next;
}
};
面试题 02.07. 链表相交
仍然是双指针算法,循环遍历如下内容:相交的链表有一个性质是从头开始遍历完自己后遍历别人,当把相交链表中的所有元素都遍历完后刚好相遇。把是否相遇作为循环的条件:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// int len_a = 0;
// int len_b = 0;
if(!headA || !headB) return NULL;
ListNode *headA_0 = new ListNode(0);
headA_0->next = headA;
ListNode *headB_0 = new ListNode(0);
headB_0->next = headB;
ListNode *A = headA_0;
ListNode *B = headB_0;
while(A->next != B->next)
{
if(A->next)
A = A->next;
else A = headB_0;
if(B->next)
B = B->next;
else
B = headA_0;
}
return A->next;
}
};
142.环形链表II
这题第一种解法感觉像在写数学题,下次再刷可能还是一点思路没有(所以了解即可);还是哈希表解法容易掌握也更具有普适性。
(力扣官解)哈希:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};
day5 哈希专题
242.有效的字母异位词
class Solution {
public:
bool isAnagram(string s, string t) {
if(s.size() != t.size()) return false;
int record[26] = {};
for(auto &a : s)
{
record[a - 'a']++;
}
for(auto &a : t)
{
record[a - 'a']--;
}
for(int i = 0; i < 26; i++)
{
if(record[i] != 0) return false;
}
return true;
}
};
349. 两个数组的交集
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
set<int> result1,tmp;
vector<int> result2;
for(auto &a : nums1)
result1.insert(a);
for(auto &a : nums2)
tmp.insert(a);
for(auto &a: tmp)
if(result1.find(a) != result1.end()) result2.push_back(a);
return result2;
}
};
202. 快乐数(要搞清楚死循环是因为sum开始重复出现了)
class Solution {
public:
// 取数值各个位上的单数之和
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
while(1) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
// 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
if (set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
};
1. 两数之和
暴力:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i1 = 0, j1 = 0;
for(int i = 0; i < nums.size(); i++)
{
for(int j = i+1; j < nums.size(); j++)
if(nums[i] + nums[j] == target) i1 = i,j1 = j;
}
vector<int>a{i1,j1};
return a;
}
};
哈希(代码随想录):
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for(int i = 0; i < nums.size(); i++) {
// 遍历当前元素,并在map中寻找是否有匹配的key
auto iter = map.find(target - nums[i]);
if(iter != map.end()) {
return {iter->second, i};
}
// 如果没找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
day6 哈希(2)
454.四数相加II
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int count = 0;
unordered_map<int,int> map1_2;
for(auto &a : nums1)
for(auto &b : nums2)
map1_2[a+b]++;//nums1和nums2中所有可能的两数和记录在map1_2中并记录各和出现的次数
for(auto &c : nums3)
for(auto &d: nums4)
if(map1_2.find(0-(c+d))!= map1_2.end())//a+b+c+d=0,则c+d=0-(a+b)
count+=map1_2[0-(c+d)];//注意别直接count++,因为map1_2中和重复的次数也要加进去
return count;
}
};
383. 赎金信
题目明确说了都是小写字母,那么可以开一个含26个整数的数组来哈希映射每个字母出现情况。
构造时:通过map[x-'a']++记录字母出现次数。
检查时:通过map[x-'a']--构造消耗字母的数组,然后遍历该数组如果出现小于零的元素则表明有不存在于magezine中的字母被使用。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int map[26] = {0};
for(auto &a : magazine)
map[a-'a']++;
for(auto &b : ransomNote)
map[b-'a']--;
for(int i = 0; i < 26; i++)
if(map[i] < 0) return false;
return true;
}
};
15. 三数之和
用哈希做人做麻了,绷不住直接用的卡哥的代码,哈希做法去重写不出来,最后把双指针写法看懂就行了,留着二刷再看。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
if (nums[i] > 0) {
return result;
}
// 错误去重a方法,将会漏掉-1,-1,2 这种情况
/*
if (nums[i] == nums[i + 1]) {
continue;
}
*/
// 正确去重a方法
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
/*
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
*/
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
return result;
}
};
18. 四数之和
同样用的卡哥代码,看懂就过,时间不多,优先跟上进度。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 剪枝处理
if (nums[k] > target && nums[k] >= 0) {
break; // 这里使用break,统一通过最后的return返回
}
// 对nums[k]去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
day7 字符串(1)
344.反转字符串
双指针;元素变化:头变尾,尾变头,可通过一个swap()函数交换两个指针位置的值来实现,两指针存在两种情况:1.字符串为奇数个字符时会相遇 2 .偶数个时会互相越过一个位置。所以条件判断设为头<尾就行。
class Solution {
public:
void reverseString(vector<char>& s) {
int i = 0,j = s.size() -1 ;
while(i < j)
{
swap(s[i++],s[j--]);
}
}
};
541. 反转字符串II
class Solution {
public:
void reverse(string& s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
swap(s[i], s[j]);
}
}
string reverseStr(string s, int k) {
for (int i = 0; i < s.size(); i += (2 * k)) {
// 1. 每隔 2k 个字符的前 k 个字符进行反转
// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k <= s.size()) {
reverse(s, i, i + k - 1);
continue;
}
// 3. 剩余字符少于 k 个,则将剩余字符全部反转。
reverse(s, i, s.size() - 1);
}
return s;
}
};
卡码网:54.替换数字
开一个string变量放字符,遇到数字单独处理,因为数字编码比字母小,凡是字符减去a小于0的都是数字,遇到数字就往string变量后边push_back 一个number串。
#include<iostream>
#include<vector> ;
#include<string>
using namespace std;
const string tmp = "number";
int main()
{
string s;
cin >> s;
string c;
for(auto &a : s)
{
if(a - 'a' >= 0) c.push_back(a);
else
{
for(auto &tmp1 : tmp)
c.push_back(tmp1);
}
}
cout << c;
return 0;
}
151.翻转字符串里的单词
卡码网:55.右旋转字符串
day 10 栈和队列(1)
232.用栈实现队列
在输出栈和输入栈转移元素时,使用语句:stOut.push(this->stIn.pop()); 会导致编译错误,原因是空时,pop()可能返回一个不匹配的类型。 也不能使用return pop()。
class MyQueue {
private:
std::stack<int> stOut,stIn;
public:
MyQueue() {
}
void push(int x) {
this->stIn.push(x);
}
int pop() {
if(this->stOut.empty())
{
while(!this->stIn.empty())
{
this->stOut.push(this->stIn.top());
this->stIn.pop();
}
}
int result = this->stOut.top();
stOut.pop();
return result;
// 只有当stOut为空的时候,再从stIn里导入数据(导入stIn全部数据)
// if (stOut.empty()) {
// // 从stIn导入数据直到stIn为空
// while(!stIn.empty()) {
// stOut.push(stIn.top());
// stIn.pop();
// }
// }
// int result = stOut.top();
// stOut.pop();
// return result;
}
int peek() {
int result = this->pop();
this->stOut.push(result);
return result;
}
bool empty() {
return (stIn.empty() && stOut.empty());
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
225. 用队列实现栈
一个队列实现栈;出栈操作等于出队列尾元素,前面的所有元素依次再入队回到初始状态。
class MyStack {
private:
std::queue<int> que;
public:
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
int size = que.size();
size--;//减少一次搬运,使尾部元素停留在头部;
while(size--)
{
que.push(que.front());
que.pop();
}
int result = 0;
result = que.front();
que.pop();
return result;
}
int top() {
int result = pop();
que.push(result);
return result;
}
bool empty() {
return que.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
day 11 栈和队列(2)
20. 有效的括号
这题能想到用栈但是具体细节怎么处理不好想。比如一边取出string中的括号字符一边要往栈中存匹配的字符而不是原字符,而且只存左括号不存右括号,右括号先出现又与前一个不匹配就是无效括号。匹配则出栈,全匹配最后栈会空。
class Solution {
private:
std::stack<char> st;
public:
bool isValid(string s) {
if(s.size() % 2 != 0) return false;
for(int i = 0; i < s.size(); i++)
{
if(s[i] == '(') st.push(')');
else if(s[i] == '{') st.push('}');
else if(s[i] == '[') st.push(']');
else if(!st.empty() && s[i] == st.top()) st.pop();
else return false;
}
if(st.empty()) return true;
else return false;
}
};