本学期出现问题拾遗
本文仅用于记录自身在本学期的学习过程中所存在的问题,借此劝诫自身好好学习!
周报3.17
关于kmp
算法:
void calculateNext(char *pattern, int *next, int len) {
int i = 0, j = -1;
next[0] = -1;
while (i < len - 1) {
if (j == -1 || pattern[i] == pattern[j]) {
i++;
j++;
next[i] = j;
} else {
j = next[j];//出现问题
}
}
}
问题
在求取kmp
算法中的next
数组时,对于j
指针的回溯不够了解,在自己进行实现的时候,当字符串不匹配的时候,直接将j
赋值为0,少考虑了,当当前后缀与前缀不匹配时,后缀还有可能与之前的前缀相匹配。
解决方法
巩固
28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)
代码实现
void GetNext(int* next, char* s, int len){
int i = 0, j = -1;
next[0] = -1;
while (i < len - 1) {
if(j == -1 || s[i] == s[j]){
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
}
int strStr(char* haystack, char* needle) {
int len1 = strlen(haystack);
int len2 = strlen(needle);
int next[len2];
GetNext(next, needle, len2);
int j = 0, i = 0;
while (i < len1 && j < len2) {
if (j == -1 || haystack[i] == needle[j]) {
i++;
j++;
} else {
j = next[j]; // 失配时,模式串右移
}
if(j == len2){
return i - j;
}
}
return -1;
}
周报3.24
问题
关于栈与队列
能通过c语言实现队列和栈,但是对于栈和队列用c语言结构互相实现的理解还不够到位
关于回溯算法
在回溯算法中,关于for
循环嵌套递归的理解不够到位,导致无法很好的理解题目,并进行代码实现。
解决方法
巩固
用栈模拟队列,本质上就是涉及两个栈的协同工作。我们可以通过将元素推入一个栈(称为入栈操作),然后从该栈中弹出元素并推入另一个栈(称为出栈操作),以实现队列的先进先出(FIFO)顺序。
- 入队列操作:
- 当需要向队列中添加一个元素时,将元素推入第一个栈(称为入栈操作)。
- **出队列操作:
- 如果第二个栈为空,则将第一个栈中的所有元素弹出并推入第二个栈,然后从第二个栈中弹出元素(称为出栈操作)。
- 如果第二个栈不为空,则直接从第二个栈中弹出元素。
代码:
typedef struct {
int* stk;
int stkSize;
int stkCapacity;
} Stack;
Stack* stackCreate(int cpacity) {
Stack* ret = malloc(sizeof(Stack));
ret->stk = malloc(sizeof(int) * cpacity);
ret->stkSize = 0;
ret->stkCapacity = cpacity;
return ret;
}
void stackPush(Stack* obj, int x) {
obj->stk[obj->stkSize++] = x;
}
void stackPop(Stack* obj) {
obj->stkSize--;
}
int stackTop(Stack* obj) {
return obj->stk[obj->stkSize - 1];
}
bool stackEmpty(Stack* obj) {
return obj->stkSize == 0;
}
void stackFree(Stack* obj) {
free(obj->stk);
}
typedef struct {
Stack* in;
Stack* out;
} MyQueue;
MyQueue* myQueueCreate(){
MyQueue* q =malloc(sizeof(MyQueue));
q->in = stackCreate(100);
q->out = stackCreate(100);
return q;
}
void InToOut(MyQueue* q) {
while(!stackEmpty(q->in)) {
stackPush(q->out, stackTop(q->in));
stackPop(q->in);
}
}
void myQueuePush(MyQueue* obj, int x) {
stackPush(obj->in, x);
}
int myQueuePop(MyQueue* obj) {
if(stackEmpty(obj->out)){
InToOut(obj);
}
int x = stackTop(obj->out);
stackPop(obj->out);
return x;
}
int myQueuePeek(MyQueue* obj) {
if (stackEmpty(obj->out)) {
InToOut(obj);
}
return stackTop(obj->out);
}
bool myQueueEmpty(MyQueue* obj) {
return stackEmpty(obj->in) && stackEmpty(obj->out);
}
void myQueueFree(MyQueue* obj) {
stackFree(obj->in);
stackFree(obj->out);
}
我们使用了循环队列实现栈的后进先出(LIFO)的顺序。
-
**入栈操作 **:
- 入栈操作将元素添加到队列的末尾(
rear
指针指向的位置)。
- 入栈操作将元素添加到队列的末尾(
-
**出栈操作 **:
- 出栈操作首先计算队列中实际的元素数量。
- 然后将队列中除了最后一个元素外的其他元素重新排列,使得原来的最后一个元素被移到队列的前端。
- 最后,弹出队列的第一个元素(现在是原来的最后一个元素)作为栈顶元素。
-
**获取栈顶元素操作 **:
- 直接返回队列中最后元素的值。
-
这段程序使用队列来实现栈的基本操作,即入栈(push)、出栈(pop)、获取栈顶元素(top)以及判断栈是否为空(empty)。虽然使用队列来实现栈不是常见的方法,但这段代码展示了一种可能的实现方式。
以下是该程序如何使用队列实现栈的方法:
- 数据结构:
- 定义了一个结构体
MyStack
,其中包含一个大小为 100 的数组queue
作为队列的底层数据结构。 - 使用两个指针
front
和rear
分别指示队列的前端和后端。
- 定义了一个结构体
- 入栈操作 (
myStackPush
):- 入栈操作将元素添加到队列的末尾(
rear
指针指向的位置)。
- 入栈操作将元素添加到队列的末尾(
- 出栈操作 (
myStackPop
):- 出栈操作首先计算队列中实际的元素数量。
- 然后将队列中除了最后一个元素外的其他元素重新排列,使得原来的最后一个元素被移到队列的前端。
- 最后,弹出队列的第一个元素(现在是原来的最后一个元素)作为栈顶元素。
- 获取栈顶元素操作 (
myStackTop
):- 直接返回队列中倒数第二个元素的值。
- 判断栈是否为空操作 (
myStackEmpty
):- 如果队列的前端指针和后端指针相等,则队列为空,栈也为空。
- 释放栈内存 (
myStackFree
):- 释放
MyStack
结构体占用的内存。
- 释放
- 数据结构:
typedef struct {
int queue[100];
int front;
int rear;
} MyStack;
MyStack* myStackCreate() {
MyStack* stack = malloc(sizeof(MyStack));
stack->front = 0;
stack->rear = 0;
return stack;
}
void myStackPush(MyStack* obj, int x) {
obj->queue[(obj->rear)++] = x;
}
int myStackPop(MyStack* obj) {
int rear = obj->rear;
int front = obj->front;
int size = rear - front;
while (size-- > 1) {
obj->queue[rear++] = obj->queue[front++];
}
int top = obj->queue[front++];
obj->front = front;
obj->rear = rear;
return top;
}
int myStackTop(MyStack* obj) {
return obj->queue[(obj->rear) - 1];
}
bool myStackEmpty(MyStack* obj) {
return obj->rear == obj->front;
}
void myStackFree(MyStack* obj) {
obj->front = 0;
obj->rear = 0;
}
[回溯算法](77. 组合 - 力扣(LeetCode))
class Solution {
public:
vector<int> temp;
vector<vector<int>> ans;
void dfs(int curr, int n, int k) {
if((temp.size() - curr + n + 1) < k) {
return;
}
if(temp.size() == k) {
ans.push_back(temp);
return;
}
temp.push_back(curr);
dfs(curr + 1, n, k);
temp.pop_back();
dfs(curr + 1, n, k);
}
vector<vector<int>> combine(int n, int k) {
dfs(1, n, k);
return ans;
}
};
周报3.31
问题
学习了单调栈的相关算法,但对在遇到较为复杂问题,即遇到所求的内容不能直接反映出与栈的单调存储关系的题目,很难判断选用单调递增栈还是单调递减栈。
解决方法
多刷题,对单调栈进行更深的理解,下面会进行博客的撰写以巩固自己的学习程度。
巩固
题目较为直白,就是找出当前元素在数组中下一个比其更大的元素,要是没有就返回0,那么就是使用单调递减栈,栈中存储温度的下标,遇到比栈顶元素还大的元素就将栈顶元素出栈,并返回求取出栈元素下标于当前元素下标的差值。
class Solution {
public:
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 temp = s.top();
ans[temp] = i - temp;
s.pop();
}
s.push(i);
}
return ans;
}
};
本题也是使用单调递减栈的题目,由于我们想知道当前高度能否接到雨水,那就是要找到左边第一个高于它的,和右边第一个高于它的,若都能找得到那么就说明该位置能接住雨水,左右两边高度最小值再减去当前位置的高度,不断进行累加,就能求出该答案。在这道题里我们不需要去判断,栈中的剩余内容,因为当栈中还存有元素时,说明它的右边没有比它更高的物体,存不住水。
class Solution {
public:
int trap(vector<int>& height) {
int sum = 0;
int n = height.size();
stack<int> s;
int width = 0, temp = 0, h = 0;
for (int i = 0;i < n; i++) {
while (!s.empty() && height[i] > height[s.top()]) {
int temp = s.top();
s.pop();
if (s.empty()) break;
int width = i - s.top() - 1;
int h = min(height[s.top()], height[i]) - height[temp];
sum += h * width;
}
s.push(i);
}
return sum;
}
};
这道题就是使用单调递增栈完成的题目,我们要求出能圈出的最大的矩形面积,其实要找到左边第一个大于栈顶元素的矩形,和右边第一个大于栈顶元素,因为只有两边都大于当前栈顶元素,矩形的高度才会是当前栈顶的高度,不断遍历,找到最大的区域。与接雨水不同,我们还需要判断栈是否为空,当栈不为空时我们就将其循环弹出,将弹出元素作为左边界,将heights
的最后一个元素作为右边界,计算矩形大小。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> s;
int n = heights.size();
int max_area = 0;
for (int i = 0; i < heights.size(); i++) {
while (!s.empty() && heights[i] < heights[s.top()]) {
int temp = s.top();
s.pop();
int width = (s.empty())? i : i - s.top() - 1;
max_area = max(max_area, (width * heights[temp]));
}
s.push(i);
}
while (!s.empty()) {
int temp = s.top();
s.pop();
int width = (s.empty())? n : n - s.top() - 1;
max_area = max(max_area, (width * heights[temp]));
}
return max_area;
}
};
想说的话
在每周的学习中,我们应该及时总结自身在学习过程中发生的问题,并对其进行深入分析和思考。如果我们忽视了这些问题,可能会导致学习效率的降低,以及对知识的理解和掌握程度的减弱。因此,对于每个出现的问题,我们应该及时记录下来,并思考它们的根源、解决方案以及可能的改进方法。
借此机会,劝诫自己戒骄戒躁,端正学习态度,及时修正自身行为,不应因为自身的怠惰而忽视对问题的发现和总结,希望自己能够永怀谦卑之心,在学习路上走的更远。
是故无冥冥之志者,无昭昭之明;无惛惛之事者,无赫赫之功。行衢道者不至,事两君者不容。目不能两视而明,耳不能两听而聪。螣蛇无足而飞,鼫鼠五技而穷。《诗》曰:“尸鸠在桑,其子七兮。淑人君子,其仪一兮。其仪一兮,心如结兮!”故君子结于一也。