单调栈用法

文章目录

  • 1. 单调栈
    • 1.1 理解单调栈(模板)
    • 1.2 每日温度
    • 1.3 子数组的最小值之和
    • 1.4 柱状图中最大的矩形
    • 1.5 最大矩形
    • 1.6 最大宽度坡
    • 1.7 去除重复字母

1. 单调栈

单调栈经典的用法:

对每个位置都求:

  • 当前位置的左侧比当前位置的数,且距离最近的位置
  • 当前位置的右侧比当前位置的数,且距离最近的位置

或者:

  • 当前位置的左侧比当前位置的数,且距离最近的位置
  • 当前位置的右侧比当前位置的数,且距离最近的位置

1.1 理解单调栈(模板)

image-20240309212339183

  1. 题目分析
单调栈顾名思义就是用栈来维护单调性。栈中存放的是数组元素的下标

在本题思路:
	- 遍历nums数组,将数组元素按从小到大的顺序入栈
	- 若当前数组元素比栈顶元素小,则弹出栈顶元素,直到栈空或者栈顶元素小于当前元素为止

如何记录距离某个元素左边最近且小与它的数的下标、右边最近且小于它的数的下标:
	- 在弹栈的时候,弹出元素就是目标元素
	- 小于目标元素、左边最近的元素就是弹出它后的栈顶元素,若栈为空,则为-1
	- 大于目标元素、右边最近的元素就是当前还未入栈的那个元素(迫使目标元素出栈的那个元素)
	- 当遍历到nums数组末位的时候,如果栈内还有元素,则依次弹出,右边最近且小于它的数没有,为-1,左边就是弹出它后的栈顶元素,若栈空,则为-1
    
若nums数组中有值相同的元素,那么会有一种情况:
	- 假如将要入栈的元素的值等于栈顶元素的值,将栈顶元素弹出
	- 弹出后的目标元素的右边最近且小于它的数就是将要入栈的数(当然这只是暂时性的)
	- 记cur为目标元素,left为cur左边最近且小于cur的数,right同理。
	- 遍历完nums数组后,再从后向前遍历,找nums[cur] == nums[right]
	- 将cur的right重新赋值为right的right
  1. 代码实现
from collections import deque

def foundMonotoneStack(nums):
    n = len(nums)

    # 需要返回的二维数组
    ans = [[] for _ in range(n)]

    # 初始化栈
    s = deque()
    # 存入nums第一个元素的下标
    s.append(0)
    
    # 开始遍历数组
    for i in range(1, n):
        # 如果将要入栈的元素大于栈顶元素就入栈
        if nums[s[-1]] < nums[i]:
            s.append(i)
            continue
        
        # 如果将要入栈的元素小于等于栈顶元素,就挨着弹栈,直到栈空或者将要入栈的元素大于栈顶元素
        # 并在弹栈的过程中填入cur的left和right
        while len(s) > 0 and nums[s[-1]] >= nums[i]:
            cur = s.pop()
            if len(s) > 0:
                ans[cur].append(s[-1])
            else:
                ans[cur].append(-1)
            ans[cur].append(i)
        
        # 最后记得将 将要入栈的元素 压栈
        s.append(i)
    
    # 遍历完数组后检查栈是否为空,不为空又继续弹,规则跟上面那个弹栈差不多
    while len(s) > 0:
        cur = s.pop()
        if len(s) > 0:
            ans[cur].append(s[-1])
        else:
            ans[cur].append(-1)
        ans[cur].append(-1)
    
    # 最后再检查nums[cur]是否等于nums[right],若等于,nums[right]就应该等于right的right
    for i in range(n - 1, -1, -1):
        if ans[i][1] != -1 and nums[i] == nums[ans[i][1]]:
            ans[i][1] = ans[ans[i][1]][1]
    return ans

image-20240309223756878

1.2 每日温度

image-20240309231131059

  1. 题目分析:
在当前位置,想要观察比当前温度更高的气温,需要等待的天数->
在当前位置,想要在右侧找到比当前位置的数更大的数,且距离最近,求这个距离

这样就转换成了类似于模板的题目,只不过栈内元素是从大到小入栈
  1. 模板代码实现
from collections import deque
def dailyTemperatures(temperatures):
    """
    :type temperatures: List[int]
    :rtype: List[int]
    """

    n = len(temperatures)

    ans = [0] * n

    # 初始化栈
    s = deque()
    s.append(0)

    for i in range(1, n):
        # 如果将要入栈的元素的值小于栈顶元素的值,就入栈
        if temperatures[i] < temperatures[s[-1]]:
            s.append(i)
            continue

        # 如果将要入栈的元素的值大于等于栈顶元素的值,就不断弹出栈顶元素
        while len(s) > 0 and temperatures[i] >= temperatures[s[-1]]:
            cur = s.pop()
            # 这里只用考虑右边,所以简洁一些
            ans[cur] = i - cur
        s.append(i)

    # 当遍历完数组后,后面一定没有比栈顶元素大的数了
    while len(s) > 0:
        cur = s.pop()
        ans[cur] = 0
	
    # 最后这里有点难想,我也是因为测试用例不通过,进而调试改进的,测试用例是下面这个
    # [34,80,80,34,34,80,80,80,80,34]
    for i in range(n - 1, -1, -1):
        if ans[i] != 0 and temperatures[i] == temperatures[i + ans[i]]:
            if ans[i + ans[i]] == 0:
                ans[i] = ans[i + ans[i]]
            else:
                ans[i] += ans[i + ans[i]]
    return ans

image-20240310001909684

  1. 模板代码改进
# 改进的地方就是,针对这个题而言,当栈顶元素等于将要入栈的元素时,入栈元素照常入栈,没有影响
# 这样也少了最下面那一个稍微难想的循环
from collections import deque
def dailyTemperatures(temperatures):
    """
    :type temperatures: List[int]
    :rtype: List[int]
    """

    n = len(temperatures)

    ans = [0] * n

    # 初始化栈
    s = deque()
    s.append(0)

    for i in range(1, n):
        # 如果将要入栈的元素的值小于栈顶元素的值,就入栈
        if temperatures[i] <= temperatures[s[-1]]:
            s.append(i)
            continue

        # 如果将要入栈的元素的值大于等于栈顶元素的值,就不断弹出栈顶元素
        while len(s) > 0 and temperatures[i] > temperatures[s[-1]]:
            cur = s.pop()
            # 这里只用考虑右边,所以简洁一些
            ans[cur] = i - cur
        s.append(i)

    # 当遍历完数组后,后面一定没有比栈顶元素大的数了
    while len(s) > 0:
        cur = s.pop()
        ans[cur] = 0

    return ans

image-20240310002328716

1.3 子数组的最小值之和

image-20240310135008741

  1. 题目思路
一般思路:找出所有的连续子数组,然后再求出每个子数组中的最小值进行累加

如何利用单调栈的特性来做?
	- 一个最小值可能在多个子数组中出现
	- 在单调栈的模板中,我们有cur、left和right。
		* left表示在cur的左边、距离cur最近且小于cur的数的下标
		* right表示在cur的右边、距离cur最近且小于cur的数的下标
	- 那么在left到cur之间、cur到right之间的数一定是比cur大的数
	- 由这些数组成的连续子数组且包含cur,它们的最小值一定是cur位置的数,所以我们的目的就变成了计算这些数组的数目
	- 那么如何计算这些数组的数量呢?
		* 假如给定一系列下标:[left, a, b, c, cur, d, e, f, g, right]
		* 那么符合条件的子数组有:
			1. [a, b, c, cur]
			2. [a, b, c, cur, d]
			3. [a, b, c, cur, d, e]
			4. [a, b, c, cur, d, e, f]
			5. [a, b, c, cur, d, e, f, g]
			6. [b, c, cur]
			7. [b, c, cur, d]
			8. [b, c, cur, d, e]
			9. [b, c, cur, d, e, f]
			10.[b, c, cur, d, e, f, g]
			11.[c, cur]
			12.[c, cur, d]
			13.[c, cur, d, e]
			14.[c, cur, d, e, f]
			15.[c, cur, d, e, f, g]
			16.[cur]
			17.[cur, d]
			18.[cur, d, e]
			19.[cur, d, e, f]
			20.[cur, d, e, f, g]
			一共二十个
		* 可以看到由a开头的数组5个,b开头的5个,c开头的5个,cur开头的5个,一共是由left到cur中间的数包括cur来开头的,像这种由哪个开头的这类数组一共有cur-left种
		* 又可以看到由cur结尾的数组4个,d结尾的4个,e结尾的4个,f结尾的4个,g结尾的4个,一共是由cur到right中间的数包括cur来结尾的,像这种由哪个结尾的这类数组一共有right-cur种
		* 所以总共就有(cur-left)*(right-cur)种数组
		
	- 当将要入栈的元素的值等于栈顶元素的时候怎么办?
		* 找个特例自己看哪种合适
		* 本题中,照常弹栈即可
  1. 代码实现
# 还是照常套模板

from collections import deque

def sumSubarrayMins(arr):
    """
    :type arr: List[int]
    :rtype: int
    """
    MAX = 10 ** 9 + 7

    n = len(arr)

    all = 0

    s = deque()
    s.append(0)

    # 大压小
    for i in range(1, n):
        if arr[i] > arr[s[-1]]:
            s.append(i)
            continue

        while len(s) > 0 and arr[i] <= arr[s[-1]]:
            cur = s.pop()

            if len(s) > 0:
                left = s[-1]
            else:
                # 如果栈内没元素,就表示cur的左边没有比它小的数了,此时cur-left=cur-(-1),是因为要包括0在内
                left = -1

            right = i

            # 同余原理
            all += (all + (cur - left) * (right - cur) * arr[cur]) % MAX
        s.append(i)

    while len(s) > 0:
        cur = s.pop()

        # 最后执行弹栈操作时,它们没有right,表示右边没有比它们小的数了,right-cur=n-cur,这也是因为需要将n-1包括进去
        right = n

        if len(s) > 0:
            left = s[-1]
        else:
            left = -1

        all += (all + (cur - left) * (right - cur) * arr[cur]) % MAX

    return all

image-20240310144314334

1.4 柱状图中最大的矩形

image-20240310145540738

  1. 题目思路
跟上个题的思路差不多

找到cur的left和right:
	- cur:当前弹出栈的元素的下标
	- left:cur左边、距离cur最近且比cur小的数的下标
	- right:cur右边、距离cur最近且比cur小的数的下标
	
每个cur的最大面积:(right-left-1) * heights[cur]
  1. 代码实现
from collections import deque

def largestRectangleArea(heights):
    """
    :type heights: List[int]
    :rtype: int
    """
    max_area = 0

    n = len(heights)

    s = deque()
    s.append(0)

    for i in range(1, n):
        if heights[i] > heights[s[-1]]:
            s.append(i)
            continue

        while len(s) > 0 and heights[i] <= heights[s[-1]]:
            cur = s.pop()
            left = s[-1] if len(s) > 0 else -1
            right = i
            max_area = max(max_area, (right - left - 1) * heights[cur])

        s.append(i)

    while len(s) > 0:
        cur = s.pop()
        left = s[-1] if len(s) > 0 else -1
        right = n
        max_area = max(max_area, (right - left - 1) * heights[cur])

    return max_area

image-20240310150901646

1.5 最大矩形

image-20240310153137614

  1. 题目分析
解决方法:转换为柱状图的解决思路

首先,以上图为例:
以第一行为底,柱状图的大小为[1,0,1,0,0]
以第二行为底,柱状图的大小为[2,0,2,1,1]
以第三行为底,柱状图的大小为[3,1,3,2,2]
以第四行为底,柱状图的大小为[4,0,0,1,0]
循环三次,在每次循环中找最大的矩形面积即可
  1. 代码实现
from collections import deque

def maximalRectangle(matrix):
    """
    :type matrix: List[str]
    :rtype: int
    """
    # 将matrix中的每个字符串变成一个整型列表
    matrix = [list(map(int, list(matrix[i]))) for i in range(len(matrix))]

    if len(matrix) == 0:
        return 0

    n = len(matrix)
    m = len(matrix[0])
    max_area = 0

    # 用来更新柱状图的信息
    temp_arr = [0] * m

    for i in range(n):
        
        # 下面的循环就跟前面柱状图那个题的代码一样
        # 需要注意的是不要把i和j搞混了
        for j in range(m):
            temp_arr[j] = 0 if matrix[i][j] == 0 else (temp_arr[j] + matrix[i][j])

        s = deque()
        s.append(0)

        for j in range(1, m):
            if temp_arr[j] > temp_arr[s[-1]]:
                s.append(j)
                continue

            while len(s) > 0 and temp_arr[j] <= temp_arr[s[-1]]:
                cur = s.pop()
                left = s[-1] if len(s) > 0 else -1
                right = j
                max_area = max(max_area, (right - left - 1) * temp_arr[cur])

            s.append(j)

        while len(s) > 0:
            cur = s.pop()
            left = s[-1] if len(s) > 0 else -1
            right = m
            max_area = max(max_area, (right - left - 1) * temp_arr[cur])

    return max_area

image-20240310160037675

1.6 最大宽度坡

image-20240310162245209

  1. 题目思路
要求的是什么?
	求的是一段距离(也就是下标之差)
	cur的右边、距离cur最远、比cur大的数的下标
	cur的左边、距离cur最远、比cur小的数的下标
	
单调栈压栈方式:小压大,比栈顶元素小的进栈,如果比栈顶元素大,就不用进栈,继续比较下一个。
如果比栈顶元素大的进栈了,那么后续遇到比这个栈顶元素大的,所形成的最大宽度坡一定比它下面那个元素所形成的最大宽度坡小。
因为它下面的那个本身值也比栈顶的小,并且位置也比栈顶的靠前

一轮比较完后,栈内的元素从底到顶就是按照从大到小的顺序。

然后再从尾到头遍历数组A,如果A[i]大于等于栈顶元素,就弹出栈顶元素,记录当前的宽度坡(始终记录最大的宽度坡);如果A[i]小于栈顶元素,就往前遍历A数组。
  1. 代码实现
from collections import deque

def maxWidthRamp(nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    n = len(nums)

    max_slope = 0

    s = deque()
    s.append(0)

    for i in range(1, n):
        if nums[i] < nums[s[-1]]:
            s.append(i)
            continue
    
    for i in range(n - 1, -1, -1):
        while len(s) > 0 and nums[s[-1]] <= nums[i]:
            max_slope = max(max_slope, i - s.pop())

    return max_slope

image-20240310164450840

1.7 去除重复字母

image-20240310170359248

  1. 题目分析
首先,字符的顺序不能更改

对于前面的字符,我们只需要统计一下它们的总出现次数,只要大于0,就能删除

如何用单调栈来做呢?
	- 压栈的时候大压小,如果当前字符大于栈顶字符、且栈内没有该元素,压栈
	- 如果当前字符小于等于栈顶字符,判断栈顶字符出现的次数是否大于0,若大于就可以弹出,若小于0就不能弹出
	- 每一次压栈后,将这个字符的出现次数-1,并且将其标记为True,表示栈里面有这个元素
	- 每一次弹栈后,将这个字符标记为False,表示栈里没有这个元素了
  1. 下面的代码有点绕,也是自己测试了好久写出来的
from collections import deque

def removeDuplicateLetters(s):
    """
    :type s: str
    :rtype: str
    """
    n = len(s)

    cnts = [0] * 26
    for i in range(n):
        cnts[ord(s[i]) - ord('a')] += 1

    marked = [False] * 26

    # 初始化栈
    stack = deque()
    stack.append(s[0])
    cnts[ord(s[0]) - ord('a')] -= 1
    marked[ord(s[0]) - ord('a')] = True

    for i in range(1, n):
        # 比栈顶字符大、且没在栈内出现过,入栈
        if marked[ord(s[i]) - ord('a')] == False and s[i] > stack[-1]:
            stack.append(s[i])
            marked[ord(s[i]) - ord('a')] = True
            cnts[ord(s[i]) - ord('a')] -= 1
            continue

        # 栈不为空、当前字符小于等于栈顶字符、栈顶元素剩余出现次数大于0、且当前字符未在栈内出现,弹栈
        while len(stack) > 0 and s[i] <= stack[-1] and cnts[ord(stack[-1]) - ord('a')] > 0 and marked[ord(s[i]) - ord('a')] == False:
            marked[ord(stack[-1]) - ord('a')] = False
            stack.pop()

        # 如果当前字符连上面两个条件都不满足,但是它没在栈内出现过,入栈
        if marked[ord(s[i]) - ord('a')] == False:
            stack.append(s[i])
            marked[ord(s[i]) - ord('a')] = True
        
        # 如果在栈内出现过,就跳过这个字符,并且出现次数减一
        cnts[ord(s[i]) - ord('a')] -= 1

    # 将栈内字符拼接成一个字符串
    ans = [''] * len(stack)
    for i in range(len(ans) - 1, -1, -1):
        ans[i] = stack.pop()

    return ''.join(ans)

image-20240310200856515

  1. 精简版
from collections import deque

def removeDuplicateLetters(s):
    """
    :type s: str
    :rtype: str
    """
    n = len(s)

    cnts = [0] * 26
    for i in range(n):
        cnts[ord(s[i]) - ord('a')] += 1

    marked = [False] * 26

    # 初始化栈
    stack = deque()
    stack.append(s[0])
    cnts[ord(s[0]) - ord('a')] -= 1
    marked[ord(s[0]) - ord('a')] = True

    for i in range(1, n):
        # 如果当前字符没在栈内出现
        if marked[ord(s[i]) - ord('a')] == False:
            # 如果栈内有字符、当前字符小于等于栈顶字符、且栈顶字符剩余出现次数大于0
            while len(stack) > 0 and s[i] <= stack[-1] and cnts[ord(stack[-1]) - ord('a')] > 0:
                marked[ord(stack[-1]) - ord('a')] = False
                stack.pop()

            # 如果栈空、当前字符大于栈顶字符、栈顶字符剩余出现次数等于0,就直接入栈
            stack.append(s[i])
            marked[ord(s[i]) - ord('a')] = True
        
        # 不管它入不入栈,它的剩余出现次数都要减1
        cnts[ord(s[i]) - ord('a')] -= 1

    # 将栈内字符拼接成一个字符串
    ans = [''] * len(stack)
    for i in range(len(ans) - 1, -1, -1):
        ans[i] = stack.pop()

    return ''.join(ans)

image-20240310202113348

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UDP网络程序

上一章中&#xff0c;我们介绍了socket&#xff0c;以及TCP/UDP协议。这一章带大家实现几个UDP协议的网络服务。我们需要一个 服务端和一个客户端。 1.服务端实现 1.1socket函数 #include <sys/types.h> #include <sys/socket.h>int socket(int domain, in…

【JAVA基础篇教学】第六篇:Java异常处理

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第五篇&#xff1a; Java异常处理。 异常处理是Java编程中重要的一部分&#xff0c;它允许开发人员在程序运行时检测和处理各种错误情况&#xff0c;以保证程序的稳定性和可靠性。在Java中&#xff0c;异常被表示为对象&am…

【 书生·浦语大模型实战营】作业(三):“茴香豆” 搭建你的RAG 智能助理

【 书生浦语大模型实战营】学习笔记&#xff08;三&#xff09;&#xff1a;“茴香豆” 搭建你的RAG 智能助理作业 &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI…

JVM参数列表

-client :设置JVM使用client模式,特点启动较快(神机不明显(I5/8G/SSD)) -server :设置JVM使用server模式。64位JDK默认启动该模式 -agentlib:libname[options] :用于加载本地的lib -agentlib:hprof :用于获取JVM的运行情况 -agentpath:pathnamep[options] :加载制定路径的本…

PHP01——php快速入门 之 使用phpstudy快速搭建PHP环境

PHP01——php快速入门 之 使用phpstudy快速搭建PHP环境 0. 前言1. 下载小皮面板1.1 下载phpstudy&#xff08;小皮面板&#xff09;1.2 启动、简单访问1.2.1 启动Apache1.2.2 访问1.2.3 访问自定义文件或页面 2. 创建网站2.1 创建网站2.2 可能遇到的问题2.2.1 hosts权限问题&am…

极海APM32电机驱动板记录(二)

文章目录 1、解除写保护2、极海驱动板资源概述3、新建工程4、点灯5、嘀嗒定时器6、中断7、串口打印8、adc读取9、i2c尝试10、定时器测试11、电机驱动pwm测试 上一篇文章算是简单了解了一下极海的板子开发环境吧&#xff0c;结果前几天板子来了&#xff0c;然后发现一个大bug&am…

力扣题目 19:删除链表的倒数第N个节点 【python】

&#x1f464;作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a; 码上找工作http://t.csdnimg.cn/Q59WX 作者专栏每日更新&#xff1a; LeetCod…

Qt-绘制多边形、椭圆、多条直线

1、说明 所有的绘图操作是在绘图事件中进行。mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWi…

C++ 类和对象(一)

目录 0.前言 1.面向过程&面向对象 1.1面向过程编程&#xff08;PP&#xff09; 1.2面向对象编程&#xff08;OOP&#xff09; 1.3从C到C 2.类的引入 2.1C语言中的结构体 2.2C中类的引入 2.3结构体与类的区别 2.4为什么引入类 3.类的定义 3.1声明与定义不分离 …

【Java探索之旅】从输入输出到猜数字游戏

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、输入输出1.1 输出到控制台1.2 从键盘输入 二、猜数字游戏2.1 所需知识&#xff1a…

【动态规划】【01背包】Leetcode 1049. 最后一块石头的重量 II

【动态规划】【01背包】Leetcode 1049. 最后一块石头的重量 II 解法 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法 &#x1f612;: 我的代码实现> 动规五部曲 ✒️确定dp数组以及下标的含义 dp[j]表示容量为…

Learn SRP 01

学习链接&#xff1a;Custom Render Pipeline (catlikecoding.com) 使用Unity版本&#xff1a;Unity 2022.3.5f1 1.A new Render Pipeline 1.1Project Setup 创建一个默认的3D项目&#xff0c;项目打开后可以到默认的包管理器删掉所有不需要的包&#xff0c;我们只使用Unit…

陆面、生态、水文模拟与多源遥感数据同化

原文链接&#xff1a;陆面、生态、水文模拟与多源遥感数据同化https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601198&idx6&sn51b9b26b75c9df1f11dcb9a187878261&chksmfa820dc9cdf584df9ac3b997c767d63fef263d79d30238a6523db94f68aec621e1f91df85f6…

算法——字符串

T04BF &#x1f44b;热门专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享字符串相关算法 如果有不足的或者错误的请您指出! 目录 1.最长公共前缀1.1解析1.2题解 2.最长回文子串2.1解析2.2题解 3.二级制求和3.1解析3.2题解 4.字符串相乘4.1解析4.2…

【环境变量】常见的环境变量 | 相关指令 | 环境变量系统程序的结合理解 | 环境变量表 | 本地变量环境变量 | 外部命令内建命令

目录 常见的环境变量 HOME PWD SHELL HISTSIZE 环境变量相关的指令 echo&env export unset 本地变量 环境变量整体理解 程序现象_代码查看环境变量 ​整体理解 环境变量表 环境变量表的传递 环境变量表的查看 内建命令 少说废话&#x1f197; 每个用…

大型网站系统架构演化

大型网站质量属性优先级&#xff1a;高性能 高可用 可维护 应变 安全 一、单体架构 应用程序&#xff0c;数据库&#xff0c;文件等所有资源都在一台服务器上。 二、垂直架构 应用和数据分离&#xff0c;使用三台服务器&#xff1a;应用服务器、文件服务器、数据服务器 应用服…

JavaEE 初阶篇-深入了解 CAS 机制与12种锁的特征(如乐观锁和悲观锁、轻量级锁与重量级锁、自旋锁与挂起等待锁、可重入锁与不可重入锁等等)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 乐观锁与悲观锁概述 1.1 悲观锁&#xff08;Pessimistic Locking&#xff09; 1.2 乐观锁&#xff08;Optimistic Locking&#xff09; 1.3 区别与适用场景 2.0 轻…

我企业的业务需要制作企业网站吗?11个支持的理由以及5个反对的理由!

如果你的企业经营得还不错&#xff0c;你可能会找出很多理由&#xff0c;说明为什么一个高效的网站对你来说并不那么重要。确实&#xff0c;你明白企业需要在互联网上有一定的存在感&#xff0c;但你可能并不认为一个高效的网站会对你的特定业务产生太大的影响——尤其是当你已…

实战纪实 | 编辑器漏洞之Ueditor-任意文件上传漏洞 (老洞新谈)

UEditor 任意文件上传漏洞 前言 前段时间在做某政府单位的项目的时候发现存在该漏洞&#xff0c;虽然是一个老洞&#xff0c;但这也是容易被忽视&#xff0c;且能快速拿到shell的漏洞&#xff0c;在利用方式上有一些不一样的心得&#xff0c;希望能帮助到一些还不太了解的小伙…

PCIe总线-存储器域和PCIe总线域访问流程(二)

1.概述 PCIe总线的最大特点是像CPU访问DDR一样&#xff0c;可以直接使用地址访问PCIe设备&#xff08;桥&#xff09;&#xff0c;但不同的是DDR和CPU同属于存储器域&#xff0c;而CPU和PCIe设备属于两个不同的域&#xff0c;PCIe设备&#xff08;桥&#xff09;的地址空间属于…