代码随想录Day 49|leetcode题目:42.接雨水、84.柱状图中最大矩形

news2024/9/21 0:33:41

提示:DDU,供自己复习使用。欢迎大家前来讨论~

文章目录

  • 题目
    • 题目一:42. 接雨水
      • 解题思路:
      • 暴力解法
      • 双指针优化
      • 思路:
      • 单调栈解法
        • 单调栈处理逻辑
    • 题目二: 84.柱状图中最大的矩形
      • 解题思路:
      • 暴力解法
      • 双指针解法
      • 单调栈
        • 为什么要在开头加入元素0?
        • 特殊情况下的栈行为:
    • 总结

单调栈Part02

题目

题目一:42. 接雨水

42. 接雨水

解题思路:

接雨水问题在面试中还是常见题目的,有必要好好讲一讲。

本文深度讲解如下三种方法:

  • 暴力解法
  • 双指针优化
  • 单调栈

暴力解法

本题暴力解法也是也是使用双指针。

首先要明确,要按照行来计算,还是按照列来计算。

按照行来计算如图: 42.接雨水2

按照列来计算如图: 42.接雨水1

一些同学在实现的时候,很容易一会按照行来计算一会按照列来计算,这样就会越写越乱。

我个人倾向于按照列来计算,比较容易理解,接下来看一下按照列如何计算。

首先,如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。

可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。

这句话可以有点绕,来举一个理解,例如求列4的雨水高度,如图:

42.接雨水3

列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。

列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。

列4 柱子的高度为1(以下用height表示)

那么列4的雨水高度为 列3和列7的高度最小值减列4高度,即: min(lHeight, rHeight) - height。

列4的雨水高度求出来了,宽度为1,相乘就是列4的雨水体积了。

此时求出了列4的雨水体积。

一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。

首先从头遍历所有的列,并且要注意第一个柱子和最后一个柱子不接雨水,代码如下:

for (int i = 0; i < height.size(); i++) {
    // 第一个柱子和最后一个柱子不接雨水
    if (i == 0 || i == height.size() - 1) continue;
}

在for循环中求左右两边最高柱子,代码如下:

int rHeight = height[i]; // 记录右边柱子的最高高度
int lHeight = height[i]; // 记录左边柱子的最高高度
for (int r = i + 1; r < height.size(); r++) {
    if (height[r] > rHeight) rHeight = height[r];
}
for (int l = i - 1; l >= 0; l--) {
    if (height[l] > lHeight) lHeight = height[l];
}

最后,计算该列的雨水高度,代码如下:

int h = min(lHeight, rHeight) - height[i];
if (h > 0) sum += h; // 注意只有h大于零的时候,在统计到总和中

整体代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        int sum = 0;
        for (int i = 0; i < height.size(); i++) {
            // 第一个柱子和最后一个柱子不接雨水
            if (i == 0 || i == height.size() - 1) continue;

            int rHeight = height[i]; // 记录右边柱子的最高高度
            int lHeight = height[i]; // 记录左边柱子的最高高度
            for (int r = i + 1; r < height.size(); r++) {
                if (height[r] > rHeight) rHeight = height[r];
            }
            for (int l = i - 1; l >= 0; l--) {
                if (height[l] > lHeight) lHeight = height[l];
            }
            int h = min(lHeight, rHeight) - height[i];
            if (h > 0) sum += h;
        }
        return sum;
    }
};

因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2),空间复杂度为O(1)。

双指针优化

思路:

  1. 初始化两个指针
    • left 指针置于数组的起始位置。
    • right 指针置于数组的结束位置。
  2. 初始化两个变量记录最大高度
    • leftMax 用于存储从左侧开始到当前位置的左侧最大高度。
    • rightMax 用于存储从右侧开始到当前位置的右侧最大高度。
  3. 填充 maxLeftmaxRight 数组
    • maxLeft 数组:从左到右遍历数组,对于每个位置,更新其值为该位置及左侧所有位置中的最大值。这个数组在遍历过程中构建。
    • maxRight 数组:从右到左遍历数组,对于每个位置,更新其值为该位置及右侧所有位置中的最大值。这个数组在遍历过程中构建。
  4. 计算雨水量
    • 遍历数组中的每个位置,使用 min(maxLeft[i], maxRight[i]) - height[i] 来计算该位置能接的雨水量。这里,min 函数确保了我们使用的是两侧较小的最大高度,因为雨水的存储高度受限于较矮的一侧。
    • 将每个位置计算出的雨水量累加,得到总雨水量。
  5. 优化点
    • 避免了对每个位置重复计算两侧最大高度的需要,因为这些值已经在构建 maxLeftmaxRight 数组时计算并存储了。
    • 只需要一次遍历即可构建这两个数组,然后再次遍历计算雨水量,总体时间复杂度为 O(n)。

下面给出一个例子:

假设我们有以下柱子的高度数组:

height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]

我们的目标是计算在这些柱子下面能接多少雨水。

步骤 1: 计算 maxLeft 数组
maxLeft 数组存储了每个位置左侧的最大高度。

maxLeft[0] = 0:第一根柱子的高度。
maxLeft[1] = max(0, 1) = 1:第二根柱子的高度大于第一根,所以更新为1。
maxLeft[2] = max(1, 0) = 1:第三根柱子的高度不大于第二根,保持为1。
maxLeft[3] = max(1, 2) = 2:第四根柱子的高度大于前两根,更新为2。
maxLeft[4] = max(2, 1) = 2:第五根柱子的高度不大于第四根,保持为2。
以此类推,直到数组末尾。

最终的 maxLeft 数组为:
maxLeft = [0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3]
步骤 2: 计算 maxRight 数组
maxRight 数组存储了每个位置右侧的最大高度。

maxRight[height.length - 1] = 1:最后一根柱子的高度。
maxRight[10] = max(1, 2) = 2:倒数第二根柱子的高度大于倒数第三根,所以更新为2。
maxRight[9] = max(2, 1) = 2:倒数第三根柱子的高度不大于倒数第二根,保持为2。
maxRight[8] = max(2, 3) = 3:倒数第四根柱子的高度大于后面两根,更新为3。

以此类推,直到数组开始。
最终的 maxRight 数组为:

maxRight = [1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 2, 1]

步骤 3: 计算雨水量
现在我们有了 maxLeft 和 maxRight 数组,我们可以计算每个位置能接的雨水量。雨水量由该位置的柱子高度和左右两侧最大高度决定:

对于每个位置 i,雨水量 water[i] = min(maxLeft[i], maxRight[i]) - height[i]。
具体计算过程
water[0] = min(0, 1) - 0 = 0
water[1] = min(1, 1) - 1 = 0
water[2] = min(1, 1) - 0 = 1
water[3] = min(2, 2) - 2 = 0
water[4] = min(2, 2) - 1 = 1
water[5] = min(2, 2) - 0 = 2
water[6] = min(2, 3) - 1 = 1
water[7] = min(3, 3) - 3 = 0
water[8] = min(3, 3) - 2 = 1
water[9] = min(3, 2) - 1 = 1
water[10] = min(3, 2) - 2 = 0
water[11] = min(3, 1) - 1 = 1
累加所有的 water[i] 得到总的雨水量。

总结
通过双指针优化解法,我们只需要两次遍历(一次填充 maxLeft,一次填充 maxRight),再加上一次遍历来计算雨水量,总共需要 O(n) 的时间复杂度。这种方法避免了暴力解法中的重复计算,大大提高了效率。

代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);
        int size = maxRight.size();

        // 记录每个柱子左边柱子最大高度
        maxLeft[0] = height[0];
        for (int i = 1; i < size; i++) {
            maxLeft[i] = max(height[i], maxLeft[i - 1]);
        }
        // 记录每个柱子右边柱子最大高度
        maxRight[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            maxRight[i] = max(height[i], maxRight[i + 1]);
        }
        // 求和
        int sum = 0;
        for (int i = 0; i < size; i++) {
            int count = min(maxLeft[i], maxRight[i]) - height[i];
            if (count > 0) sum += count;
        }
        return sum;
    }
};

单调栈解法

  1. 栈的初始化:定义一个空栈 st,用来存储柱子的索引。将第一个柱子的索引(0)压入栈中。
  2. 遍历柱子:从第二个柱子开始遍历,直到最后一个柱子。
  3. 情况一:如果当前柱子的高度小于栈顶柱子的高度,说明当前柱子在栈顶柱子形成的“凹槽”内。这时,将当前柱子的索引压入栈中。
  4. 情况二:如果当前柱子的高度等于栈顶柱子的高度,这意味着栈顶柱子不是凹槽的底部。因此,我们弹出栈顶柱子,将当前柱子的索引压入栈中。
  5. 情况三:如果当前柱子的高度大于栈顶柱子的高度,这意味着我们找到了一个可能的凹槽的右边界。我们继续弹出所有比当前柱子矮的栈内柱子,直到栈顶柱子的高度小于当前柱子的高度或栈为空。对于每个弹出的柱子,我们计算它和它左侧最高柱子(栈顶柱子)之间的凹槽能接多少雨水,并将这个值累加到 sum 中。
  6. 雨水量的计算:雨水量由凹槽的宽度(当前柱子索引减去栈顶柱子索引再减去1)和高度(栈顶柱子的高度和当前柱子的高度中的较小值减去弹出柱子的高度)决定。
  7. 循环结束:继续遍历直到所有柱子都被处理。

那么本题使用单调栈有如下几个问题:

  1. 首先单调栈是按照行方向来计算雨水,如图:
42.接雨水2

知道这一点,后面的就可以理解了。

  1. 使用单调栈内元素的顺序

从大到小还是从小到大呢?

从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。

因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。

如图:

42.接雨水4
  1. 遇到相同高度的柱子怎么办。

遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。

例如 5 5 1 3 这种情况。如果添加第二个5的时候就应该将第一个5的下标弹出,把第二个5添加到栈中。

因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度

如图所示:

42.接雨水5
  1. 栈里要保存什么数值

使用单调栈,也是通过 长 * 宽 来计算雨水面积的。

长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,

那么栈里有没有必要存一个pair<int, int>类型的元素,保存柱子的高度和下标呢。

其实不用,栈里就存放下标就行,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。

所以栈的定义如下:

stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度

明确了如上几点,我们再来看处理逻辑。

单调栈处理逻辑

以下逻辑主要就是三种情况

  • 情况一:当前遍历的元素(柱子)高度小于栈顶元素的高度 height[i] < height[st.top()]
  • 情况二:当前遍历的元素(柱子)高度等于栈顶元素的高度 height[i] == height[st.top()]
  • 情况三:当前遍历的元素(柱子)高度大于栈顶元素的高度 height[i] > height[st.top()]

先将下标0的柱子加入到栈中,st.push(0);。 栈中存放我们遍历过的元素,所以先将下标0加进来。

然后开始从下标1开始遍历所有的柱子,for (int i = 1; i < height.size(); i++)

如果当前遍历的元素(柱子)高度小于栈顶元素的高度,就把这个元素加入栈中,因为栈里本来就要保持从小到大的顺序(从栈头到栈底)。

代码如下:

if (height[i] < height[st.top()])  st.push(i);

如果当前遍历的元素(柱子)高度等于栈顶元素的高度,要跟更新栈顶元素,因为遇到相相同高度的柱子,需要使用最右边的柱子来计算宽度。

代码如下:

if (height[i] == height[st.top()]) { // 例如 5 5 1 7 这种情况
  st.pop();
  st.push(i);
}

如果当前遍历的元素(柱子)高度大于栈顶元素的高度,此时就出现凹槽了,如图所示:

42.接雨水4

取栈顶元素,将栈顶元素弹出,这个就是凹槽的底部,也就是中间位置,下标记为mid,对应的高度为height[mid](就是图中的高度1)。

此时的栈顶元素st.top(),就是凹槽的左边位置,下标为st.top(),对应的高度为height[st.top()](就是图中的高度2)。

当前遍历的元素i,就是凹槽右边的位置,下标为i,对应的高度为height[i](就是图中的高度3)。

此时就是栈顶和栈顶的下一个元素以及要入栈的元素,三个元素来接水!

那么雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度,代码为:int h = min(height[st.top()], height[i]) - height[mid];

雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1(因为只求中间宽度),代码为:int w = i - st.top() - 1 ;

当前凹槽雨水的体积就是:h * w

求当前凹槽雨水的体积代码如下:

while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,持续跟新栈顶元素
    int mid = st.top();
    st.pop();
    if (!st.empty()) {
        int h = min(height[st.top()], height[i]) - height[mid];
        int w = i - st.top() - 1; // 注意减一,只求中间宽度
        sum += h * w;
    }
}

关键部分讲完了,整体代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0; // 可以不加
        stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度
        st.push(0);
        int sum = 0;
        for (int i = 1; i < height.size(); i++) {
            if (height[i] < height[st.top()]) {     // 情况一
                st.push(i);
            } if (height[i] == height[st.top()]) {  // 情况二
                st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。
                st.push(i);
            } else {                                // 情况三
                while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while
                    int mid = st.top();
                    st.pop();
                    if (!st.empty()) {
                        int h = min(height[st.top()], height[i]) - height[mid];
                        int w = i - st.top() - 1; // 注意减一,只求中间宽度
                        sum += h * w;
                    }
                }
                st.push(i);
            }
        }
        return sum;
    }
};

题目二: 84.柱状图中最大的矩形

84. 柱状图中最大的矩形

解题思路:

我们先来看一下暴力解法的解法:

暴力解法

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int sum = 0;
        for (int i = 0; i < heights.size(); i++) {
            int left = i;
            int right = i;
            for (; left >= 0; left--) {
                if (heights[left] < heights[i]) break;
            }
            for (; right < heights.size(); right++) {
                if (heights[right] < heights[i]) break;
            }
            int w = right - left - 1;
            int h = heights[i];
            sum = max(sum, w * h);
        }
        return sum;
    }
};

如上代码并不能通过leetcode,超时了,因为时间复杂度是 O ( n 2 ) O(n^2) O(n2)

双指针解法

难就难在本题要记录记录每个柱子 左边第一个小于该柱子的下标,而不是左边第一个小于该柱子的高度。

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        vector<int> minLeftIndex(heights.size());
        vector<int> minRightIndex(heights.size());
        int size = heights.size();

        // 记录每个柱子 左边第一个小于该柱子的下标
        minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
        for (int i = 1; i < size; i++) {
            int t = i - 1;
            // 这里不是用if,而是不断向左寻找的过程
            while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
            minLeftIndex[i] = t;
        }
        // 记录每个柱子 右边第一个小于该柱子的下标
        minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
        for (int i = size - 2; i >= 0; i--) {
            int t = i + 1;
            // 这里不是用if,而是不断向右寻找的过程
            while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
            minRightIndex[i] = t;
        }
        // 求和
        int result = 0;
        for (int i = 0; i < size; i++) {
            int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
            result = max(sum, result);
        }
        return result;
    }
};

单调栈

本地单调栈的解法和接雨水的题目是遥相呼应的。

这里就涉及到了单调栈很重要的性质,就是单调栈里的顺序,是从小到大还是从大到小

img

只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。

所以本题单调栈的顺序正好与接雨水反过来。

此时大家应该可以发现其实就是栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度

主要就是分析清楚如下三种情况:

  • 情况一:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况
  • 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
  • 情况三:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况

C++代码如下:

// 版本一
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int result = 0;
        stack<int> st;
        heights.insert(heights.begin(), 0); // 数组头部加入元素0
        heights.push_back(0); // 数组尾部加入元素0
        st.push(0);

        // 第一个元素已经入栈,从下标1开始
        for (int i = 1; i < heights.size(); i++) {
            if (heights[i] > heights[st.top()]) { // 情况一
                st.push(i);
            } else if (heights[i] == heights[st.top()]) { // 情况二
                st.pop(); // 这个可以加,可以不加,效果一样,思路不同
                st.push(i);
            } else { // 情况三
                while (!st.empty() && heights[i] < heights[st.top()]) { // 注意是while
                    int mid = st.top();
                    st.pop();
                    if (!st.empty()) {
                        int left = st.top();
                        int right = i;
                        int w = right - left - 1;
                        int h = heights[mid];
                        result = max(result, w * h);
                    }
                }
                st.push(i);
            }
        }
        return result;
    }
};
为什么要在开头加入元素0?
  1. 初始化栈:在“接雨水”问题中,需要找到每个柱子左右两侧最高的柱子。将索引0加入栈是为了初始化这个过程,使得我们可以从第一个柱子开始比较和计算。
  2. 保证遍历的连续性:加入索引0确保了从数组的开始就有一个参考点,这使得我们可以在遍历过程中始终保持栈内有元素,从而避免在计算左侧最高柱子时出现空栈的情况。
特殊情况下的栈行为:
  • 数组是降序的:如果数组是降序的,比如 [8, 6, 4, 2],每次遇到的新柱子都比栈顶柱子矮。这意味着每次比较都会触发“情况三”的逻辑,即当前柱子比栈顶柱子高,可以形成凹槽。
  • 凹槽的计算:在这种情况下,每次遇到新的矮柱子时,栈顶的柱子(之前的高柱子)就会与它形成凹槽。通过计算凹槽的宽度(当前柱子索引减去栈顶柱子索引减1)和高度(栈顶柱子高度和当前柱子高度的较小值减去凹槽底部柱子的高度),我们可以累加每个凹槽能接的雨水量。
  • 避免空栈:在处理降序数组时,如果最开始不加入索引0,那么在第一次遇到比栈顶柱子矮的柱子时,栈可能是空的,这会导致无法计算左侧最高柱子,从而无法计算凹槽。加入索引0确保了即使在极端情况下,我们也有一个初始的参考点来进行计算。

如图所示:

img

所以我们需要在 height数组前后各加一个元素0。

总结

接雨水问题:

**单调栈的应用:**通过单调栈,我们可以高效地找到每个柱子左侧和右侧最高的柱子,从而计算出在两个最高柱子之间较低的柱子上能接多少雨水。单调栈保证了栈内元素的高度是单调递增或递减的,这使得我们可以在遍历过程中快速更新和查询最大高度。

避免重复计算:在双指针优化解法中,我们通过预先计算并存储每个位置的 maxLeftmaxRight 值来避免在计算雨水量时的重复计算。而在单调栈解法中,我们通过维护一个单调递增的栈来确保每个柱子的左右最大高度只被计算一次,进一步提高了算法的效率。

直观理解:

  1. 接雨水问题:就像一排高低不同的篱笆,你要在它们之间放水桶接雨水。水桶能接多少水,取决于它两边篱笆的高度。如果一边高一边低,水桶就能接满;如果两边一样高,水桶就接不到水。我们要算的是所有水桶加起来能接多少水。

  2. 柱状图中最大的矩形问题:就像一堆不同宽度的砖头堆在一起,你要找出一个最大的长方形区域。这个区域可以是竖着的,也可以是横着的。我们要做的是找出所有可能的长方形,然后挑出面积最大的那个。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2145486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

闯关leetcode——35. Search Insert Position

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/search-insert-position/description/ 内容 Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it wou…

【数据结构】排序算法---冒泡排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 结语 1. 定义 冒泡排序&#xff08;英语&#xff1a;Bubble sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的…

svn回退到以前历史版本修改并上传

svn回退到以前版本&#xff0c;并在以前版本上修改代码后&#xff0c;上传到svn库当中&#xff0c;如下步骤&#xff1a; 3、 以回退到版本号4为例&#xff1a;选中版本号4&#xff0c;右键->Revert to this version,在出现的对话框中 点击yes&#xff01; 4、 5、

【ARM】Trustzone和安全架构

Trustzone的基本概念&背景和历史 什么是Trustzone&#xff1f; 什么是TEE&#xff1f; Trustzone是一个技术&#xff0c;是一个技术的设计&#xff0c;一个安全架构&#xff0c;既不是软件也不是硬件。 TEE (Trusted Execution Environment) 可信执行环境。就是依托Trust…

Java项目——苍穹外卖(二)

Redis 简介 Redis是一个基于内存的key-value结构数据库 基于内存存储&#xff0c;读写性能高适合存储热点数据&#xff08;热点商品、资讯、新闻&#xff09;企业应用广泛 基础操作 启动 在redis安装目录中打开cmd&#xff0c;输入如上图指令即可启动&#xff0c;按下crtl…

linux入门到实操-6 Linux服务管理、系统运行级别、配置服务开机启动和关闭防火墙、关机重启

教程来源&#xff1a;B站视频BV1WY4y1H7d3 3天搞定Linux&#xff0c;1天搞定Shell&#xff0c;清华学神带你通关_哔哩哔哩_bilibili 整理汇总的课程内容笔记和课程资料&#xff08;包含课程同版本linux系统文件等内容&#xff09;&#xff0c;供大家学习交流下载&#xff1a;…

html详细知识

1-标题标签、水平线、字体标签 <!--1.标题标签1&#xff09;格式&#xff1a;<hn></hn> n的范围是1-6&#xff0c;依次递减2&#xff09;标题标签特点&#xff1a;a:单独占一行b:自动加粗2.水平线1&#xff09;格式&#xff1a;<hr/>2)属性&#xff1a;…

soc及其相关概念

用户无法直接操作内存&#xff0c;只能让内存映射到用户空间然后操作 1. 内存映射&#xff08;Memory-Mapped Files&#xff09;内存映射文件是一种方法&#xff0c;它允许一个或多个进程将一个文件或者一个匿名区域映射到它们各自的虚拟地址空间中。当文件被映射到内存后&…

rsync 远程同步及实时同步部署

一、rsync 远程同步 1.1 rsync简介 Rsync&#xff08;Remote Sync&#xff0c;远程同步&#xff09;是一个开源的快速备份工具&#xff0c;适用于异地备份、镜像服务器等应用。它的主要功能特性包括&#xff1a; 数据镜像同步&#xff1a;在不同主机之间同步整个目录树。增量…

基于STM32的无人小车自主避障系统设计

文章目录 前言资料获取设计介绍功能介绍设计程序具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设…

设计模式 享元模式(Flyweight Pattern)

享元模式 简绍 享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;它的目的是通过共享技术来有效地支持大量细粒度的对象。享元模式可以极大地减少内存的使用&#xff0c;从而提高程序的性能。它特别适用于需要创建大量相似对象的场景&#…

鸿蒙 ArkUI组件三

ArkUI组件&#xff08;续&#xff09; QRCode组件 用于显示单个二维码的组件。 说明 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。二维码组件的像素点数量与内容有关&#xff0c;当组件尺寸过小时&#xff0c;可能…

二叉树的层序遍历(含十道leetcode相关题目)

文章目录 二叉树层序遍历模板102. 二叉树的层序遍历 二叉树层序遍历模板 我们之前讲过了关于二叉树的深度优先遍历的文章&#xff1a;前中后序遍历的递归法和迭代法。 接下来我们再来介绍二叉树的另一种遍历方式&#xff1a;层序遍历。 层序遍历一个二叉树。就是从左到右一层…

vue2使用npm引入依赖(例如axios),报错Module parse failed: Unexpected token解决方案

报错情况 Module parse failed: Unexpected token (5:2) You may need an appropriate loader to handle this file type. 原因 因为我们npm install时默认都是下载最新版本&#xff0c;然后个别依赖的版本太新&#xff0c;vue2他受不起这个福分。 解决方法 先去package.js…

SD-WAN如何保障企业数据安全?

SD-WAN&#xff08;软件定义广域网&#xff09;作为一种现代化网络解决方案&#xff0c;不仅能够优化和管理广域网的连接&#xff0c;还集成了一系列安全功能&#xff0c;帮助企业保护其数据安全。以下将详细介绍SD-WAN如何有效保障企业数据安全的机制。 在采用SD-WAN技术之前&…

[论文笔记]MRRNET

这是一篇河大的论文 感觉跟SANET很像 摘要 摘要&#xff1a;随着物联网&#xff08;IoT&#xff09;的大规模部署&#xff0c;道路场景中实时感知和环境理解的需求变得越来越迫切。 同时&#xff0c;语义分割作为像素级场景解析得到了广泛的研究。 然而&#xff0c;资源有限…

合宙Air201模组LuatOS:PWRKEY控制,一键解决解决关机难问题

不知不觉间&#xff0c;我们已经发布拉期课程&#xff1a;hello world初体验&#xff0c;点灯、远程控制、定位和扩展功能&#xff0c;你学的怎么样&#xff1f;很多伙伴表示已经有点上瘾啦&#xff01;合宙Air201&#xff0c;如同我们一路升级打怪的得力法器&#xff0c;让开发…

打通最后一公里:使用CDN加速GitHub Page的访问

无论是互联网从业者还是科研人员&#xff0c;使用Github Page能够很友好的建立个人网站。 目前比较主流的方案是使用GitHub Page托管文字网页&#xff0c;利用GitHub仓库托管图床&#xff0c;稳定可靠&#xff08;Gitee的page突然撤退&#xff0c;让人不敢再将图床放到上面&am…

大数据-138 - ClickHouse 集群 表引擎详解3 - MergeTree 存储结构 数据标记 分区 索引 标记 压缩协同

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Linux上Qt安装相关的内容及在QtCreator使用QChart模块需要的配置

引言 下面是Ubuntu上Qt安装相关的内容及在QtCreator使用QChart模块需要的配置。 关于Qt安装及环境 Qt的模块 查看已经安装的模块 sudo apt search qt5-安装新的模块 sudo apt install qt5-svg # 安装Qt SVG模块3.查看qt已经安装了哪些模块 dpkg -l | grep libqt安装qt,…