力扣单调栈专题 739. 每日温度 496.下一个更大元素I 503.下一个更大元素II 42. 接雨水 84.柱状图中最大的矩形 步骤及C++实现 总结

文章目录

  • 739. 每日温度
  • 496.下一个更大元素 I
  • 503.下一个更大元素II
    • 方法一 数组拼接
    • 方法二 模拟遍历两边nums
  • 42. 接雨水
    • 双指针 暴力法
    • 双指针优化
    • 单调栈
  • 84.柱状图中最大的矩形
    • 暴力解法
    • 双指针解法
    • 单调栈
  • 总结

739. 每日温度

在这里插入图片描述

  1. 暴力解法,双层for循环,时间复杂度是O(n^2)。

  2. 什么时候用单调栈?
    通常是一维数组,要找任一个元素的右边(左边)第一个比自己大(小)的元素的位置,此时优先考虑单调栈,时间复杂度为O(n)。

  3. 使用单调栈时,首先要明确如下几点:

 1)单调栈里存放的元素是什么
   单调栈只需要存放元素的下标i,T[i]直接获取对应的元素。

 2)单调栈里元素是递增还是递减
   本题使用递增顺序(从栈头到栈底的顺序)。当单调栈递增,元素i入栈时,才能保证 元素i栈顶元素 在数组中右边第一个比自己大的元素,也就是说找到栈顶元素 右边第一个比自己大的元素是 元素i。

 3)三种情况:
  当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

  1. C++实现
class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {//递增栈stack<int> st;//存放对应元素的下标vector<int> result(temperatures.size(), 0);st.push(0);//初始化for(int i=1; i<temperatures.size(); i++){if(temperatures[i] < temperatures[st.top()]) st.push(i);//情况1  当前元素比栈顶元素小 当前元素入栈 else if(temperatures[i] == temperatures[st.top()]) st.push(i);//情况2  当前元素等于栈顶元素 当前元素入栈 else//情况3 当前元素比栈顶元素大 保存结果 栈顶出栈 当前元素入栈 {while(!st.empty() && temperatures[i] > temperatures[st.top()]){result[st.top()] = i - st.top();st.pop();}st.push(i);}}return result;}
};

简化

class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {//递增栈 简化stack<int> st;vector<int> result(temperatures.size(), 0);st.push(0);for(int i=1; i<temperatures.size(); i++){while(!st.empty() && temperatures[i] > temperatures[st.top()]){result[st.top()] = i - st.top();st.pop();}st.push(i);}return result;}
};

496.下一个更大元素 I

在这里插入图片描述
需要注意点:

  1. result数组初始化应该为多少?
     题目提示,如果不存在下一个更大元素就输出 -1,所以result数组初始化为-1。

  2. 遍历nums2时需要判断nums2[i]在nums1中是否出现过,因为最后是根据nums1元素的下标来更新result数组。

  3. 题目提示nums1 和 nums2是不重复的数组,因此可以用map做映射,根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。使用集合来解决哈希问题时,优先使用unordered_set,它的查询和增删效率是最优的。

  4. 单调递增栈顺序,栈头到栈底元素顺序要从小到大。栈内元素递增才能找到右边第一个比自己大的元素。

  5. 三种情况:

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()],满足递增栈,直接入栈。
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况,直接入栈,求的是右边第一个比自己大的元素,而不是大于等于
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况,不满足递增栈,找到右边第一个比自己大的元素,对元素进行处理:栈里的元素是nums2的元素,判断栈顶元素是否在nums1里出现过。如果出现过就记录,此时栈顶元素在nums2数组中右面第一个大的元素是当前遍历元素nums2[i]。

C++实现

class Solution {
public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {stack<int> st;vector<int> result(nums1.size(), -1);//处理的是nums1的元素if(nums1.size() == 0) return result;//没有重复元素 map做映射 根据数值快速找到下标,判断nums2[i]是否在nums1中出现过。unordered_map<int, int> umap;// key:下标元素,value:下标  为了快速查找元素for(int i=0; i<nums1.size(); i++) umap[nums1[i]] = i;st.push(0);for(int i=1; i<nums2.size(); i++){if(nums2[i] < nums2[st.top()]) st.push(i);else if(nums2[i] == nums2[st.top()]) st.push(i);else{while(!st.empty() && nums2[i] > nums2[st.top()]){if(umap.count(nums2[st.top()]) > 0)//判断nums2[i]是否在nums1中出现过{int index = umap[nums2[st.top()]];result[index] = nums2[i];//nums2[i]在nums1出现过,记录结果}st.pop();}st.push(i);}}return result;}
};

简化

class Solution {
public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {//三种情况合并写stack<int> st;vector<int> result(nums1.size(), -1);if(nums1.size() == 0) return result;st.push(0);//映射容器unordered_map<int, int> umap;for(int i=0; i<nums1.size(); i++) umap[nums1[i]] = i;//遍历nums2for(int i=1; i<nums2.size(); i++){while(!st.empty() && nums2[i] > nums2[st.top()]){if(umap.count(nums2[st.top()]) > 0){int index = umap[nums2[st.top()]];result[index] = nums2[i];}st.pop();}st.push(i);}return result;}
};

503.下一个更大元素II

在这里插入图片描述
在题496变形,一个循环数组找下一个更大的元素

方法一 数组拼接

把nums数组首尾拼接组成新的nums来操作,最后resize再返回结果。

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {//拼接数组vector<int> nums1(nums.begin(), nums.end());nums.insert(nums.end(), nums1.begin(), nums1.end());vector<int> result(nums.size(), -1);if(nums.size() == 0) return result;stack<int> st;st.push(0);for(int i=1; i<nums.size(); i++){if(nums[i] < nums[st.top()]) st.push(i);else if(nums[i] == nums[st.top()]) st.push(i);else{while(!st.empty() && nums[i] > nums[st.top()]){result[st.top()] = nums[i];st.pop();}st.push(i);}}result.resize(nums.size() / 2);return result;}
};

方法二 模拟遍历两边nums

在遍历的过程中模拟走了两边nums,用i % nums.size()来代替i 操作

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {//遍历中模拟走了两边nums 用i % nums.size()来操作vector<int> result(nums.size(), -1);if(nums.size() == 0) return result;stack<int> st;st.push(0);for(int i=1; i<nums.size() * 2; i++){if(nums[i % nums.size()] < nums[st.top()]) st.push(i % nums.size());else if(nums[i % nums.size()] == nums[st.top()]) st.push(i % nums.size());else {while(!st.empty() && nums[i % nums.size()] > nums[st.top()]){result[st.top()] = nums[i % nums.size()];st.pop();}st.push(i % nums.size());}}return result;}
};

简化

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {//简化 遍历中模拟走了两边nums 用i % nums.size()来操作vector<int> result(nums.size(), -1);if(nums.size() == 0) return result;stack<int> st;st.push(0);for(int i=1; i<nums.size() * 2; i++){while(!st.empty() && nums[i % nums.size()] > nums[st.top()]){result[st.top()] = nums[i % nums.size()];st.pop();}st.push(i % nums.size());}return result;}
};

42. 接雨水

在这里插入图片描述

双指针 暴力法

按照列来计算,宽度w=1,每一列雨水的高度取决于该列左侧最高的柱子右侧最高的柱子 ,两者中较矮的柱子高度

  • 列4的雨水高度 = min(列3高度, 列7高度) — 列4高度,height = min(lefth, righth) - h,列4雨水体积 = w * height。
    • 列4 左侧最高的柱子是列3,高度为2,lefth;
    • 列4 右侧最高的柱子是列7,高度为3,righth;
    • 列4 柱子的高度h为1;
  • 从头遍历所有列,求出每一列的雨水体积,再累加就是总雨水体积。注意第一个柱子和最后一个柱子不接雨水。
  • 双指针-超时 时间复杂度是 O ( n 2 ) O(n^2) O(n2)
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 leftheight = height[i];// 记录左边柱子的最高高度int rightheight = height[i];// 记录右边柱子的最高高度for(int l=i-1; l>=0; l--){if(height[l] > leftheight) leftheight = height[l];}for(int r=i+1; r<height.size(); r++){if(height[r] > rightheight) rightheight = height[r];}//统计高度差 累加int h = min(rightheight, leftheight) - height[i];if(h > 0) sum += h;}return sum;}
};

双指针优化

暴力解法中,通过列来计算每一列的雨水体积再累加求出雨水总体积,其中,当前列雨水面积 = 1 * (min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度)。双指针遍历时,每到一个柱子都向两边遍历一遍,有重复计算。

优化:把每一个位置的左边最高高度记录在一个数组上maxLeft,右边最高高度记录在一个数组上maxRight,避免了重复计算:

  • 从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);
  • 从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);
  • 时间复杂度是 O ( n ) O(n) 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循环从1开始遍历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]);//累计高度 左右柱子中较矮的柱子高度-当前柱子高度=雨水长度//求的是面积 宽度=1 相当于统计长度int sum = 0;int h = 0;for(int i=0; i<size; i++){h = min(maxright[i], maxleft[i]) - height[i];if(h>=0) sum += h;}return sum;}
};

单调栈

在这里插入图片描述

  • 需要找一个元素,及其右边最大元素和左边最大元素来计算雨水面积
  • 按照行方向来计算雨水,当前柱子左右两边柱子的高度差就是雨水面积的长度,下标差就是宽度
  • 单调栈内元素的顺序是从小到大,如果当前柱子高度大于栈头元素高度,出现凹槽,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,添加元素就是凹槽右边的柱子。
  • 栈里要保存什么数值:使用单调栈,也是通过 长 * 宽 来计算雨水面积的。长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算
  • 单调栈处理逻辑,主要有三种情况
    • 情况一:当前遍历的元素(柱子)高度小于栈顶元素的高度 height[i] < height[st.top()]
    • 情况二:当前遍历的元素(柱子)高度等于栈顶元素的高度 height[i] == height[st.top()]
    • 情况三:当前遍历的元素(柱子)高度大于栈顶元素的高度 height[i] > height[st.top()]
  • 注意点:
    • 遇到相同高度的柱子,即相同的元素时,更新栈内下标,弹出栈内元素(旧下标),栈内加入新元素(新下标),如5、5、1、3,如果添加第二个5时,先将第一个5的下标弹出,再把第二个5入栈
    • 栈内存放元素下标,通过height[stack.top()]直接获取对应下标的高度
    • 栈中存放遍历过的元素,因此先将下标0入栈,st.push(0);
class Solution {
public:int trap(vector<int>& height) {//单调栈if(height.size() <= 2) return 0;stack<int> st;//柱子下标-雨水长度st.push(0);int sum = 0;int mid = 0;//凹槽最低int h = 0, w = 0;for(int i=1; i<height.size(); i++){if(height[i] < height[st.top()]) st.push(i);//情况一else if(height[i] == height[st.top()])//情况二 保存较近的柱子高度{st.pop();st.push(i);}else//情况三 出现凹槽{while(!st.empty() && height[i] > height[st.top()]){mid = st.top();//注意这是柱子下标st.pop();//弹出凹槽底部if(!st.empty())//如果还有元素 该栈顶元素才是凹槽左边高度{h = min(height[st.top()], height[i]) - height[mid];w = i - st.top() - 1; // 注意减一,只求中间宽度sum += h * w;}}st.push(i);}}return sum;}
};

简化

class Solution {
public:int trap(vector<int>& height) {//单调栈 简化if(height.size() <= 2) return 0;stack<int> st;st.push(0);int sum = 0;int mid = 0, h = 0, w = 0;for(int i=1; i<height.size(); i++){while(!st.empty() && height[i] > height[st.top()]){mid = st.top();//凹槽底部st.pop();if(!st.empty()){h = min(height[i], height[st.top()]) - height[mid];w = i - st.top() - 1;//减1才是凹槽底部的宽度sum += h * w;}}st.push(i);}return sum;}
};

84.柱状图中最大的矩形

在这里插入图片描述

暴力解法

时间复杂度是 O ( n 2 ) O(n^2) O(n2)

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, h*w);}return sum;}
};

双指针解法

重点在于要记录每个柱子的左边第一个小于该柱子的下标,而不是高度。所以需要循环查找,使用while

class Solution {
public:int largestRectangleArea(vector<int>& heights) {//双指针 优化//记录的是左右两边最矮柱子中离得最近的柱子的下标int size = heights.size();vector<int> minleftindex(size);vector<int> minrightindex(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;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 sum = 0;int result = 0;for(int i=0; i<size; i++){sum = heights[i] * (minrightindex[i] - minleftindex[i] - 1);result = max(sum, result);}return result;}
};

单调栈

  • 题42是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子高度的柱子,因此单调栈的元素顺序是从大到小,这样才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
  • 要求的最大面积是由当前元素(要入栈元素)、栈顶元素、栈顶下一个元素组成凸性面积:
    • 当前元素是height[i] = 40,凸形的右边(相对于柱子60下标)
    • 栈顶元素是height[st.top()] = 60,凸形的顶部,中间位置
    • 栈顶下一个元素是50,凸形的左边(相对于柱子60下标)
  • 单调栈处理逻辑,主要有三种情况
    • 情况一:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况
    • 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
    • 情况三:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况
class Solution {
public:int largestRectangleArea(vector<int>& heights) {//单调栈int result = 0;stack<int> st;//原数组头尾加入数字0分别为了避免数组是降序 升序时无法去三个值组成面积heights.insert(heights.begin(), 0);heights.push_back(0);st.push(0);//从大到小入栈 才能找到凸形面积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.push(i);else{while(!st.empty() && heights[i] < heights[st.top()]){int mid = st.top();st.pop();if(!st.empty()){int left = st.top();int right = i;int w = right - left - 1;//只要最高柱子的宽度result = max(result, w * heights[mid]);}}st.push(i);}}return result;}
};

化简

class Solution {
public:int largestRectangleArea(vector<int>& heights) {//单调栈 简化int result = 0;stack<int> st;heights.insert(heights.begin(), 0);heights.push_back(0);st.push(0);for(int i=1; i<heights.size(); i++){while(!st.empty() && heights[i] < heights[st.top()]){int mid = st.top(); st.pop();if(!st.empty()){//int left = st.top();//int right = i;//right - left - 1int w = i - st.top() - 1;result = max(result, heights[mid] * w);}}st.push(i);}return result;}
};

总结

  1. 什么时候用单调栈?
  • 通常是一维数组,要找任一个元素的右边(左边)第一个比自己大(小)的元素的位置,此时优先考虑单调栈,时间复杂度为O(n)
  • 找右边第一个比自己大的元素位置,单调递增栈——题739、题496、题503
  • 找左边和右边第一个比自己大的元素位置,单调递增栈——题42
  • 找左边和右边第一个比自己小的元素位置,单调递递减——题84
  1. 单调栈的原理是什么?为什么时间复杂度是O(n)
  • 单调栈的本质是空间换时间,因为在遍历过程中需要用一个栈来记录右边第一个比当前元素大的元素。
  • 优点是整个数组只需要遍历一次,因此时间复杂度是O(n)。
  • 单调栈是用来记录遍历过的元素。遍历数组时,如果不知道哪些元素是遍历过的,会导致无法得知是否有一个比当前元素更小的元素被遍历了。所以要用一个容器——单调栈,来记录遍历过的元素。
  1. 使用单调栈时,首先要明确如下几点:

 1)单调栈里存放的元素是什么
   单调栈只需要存放元素的下标i,T[i]直接获取对应的元素。

 2)单调栈里元素是递增还是递减
   如果求一个元素右边第一个更大元素,单调栈是递增的,如果求一个元素右边第一个更小元素,单调栈是递减的。
   当单调栈递增,元素i入栈时,才能保证 元素i栈顶元素 在数组中右边第一个比自己大的元素,也就是说找到栈顶元素 右边第一个比自己大的元素是 元素i。

 3)三种情况:
  当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在 MATLAB 中进行图像分割(matlab仿真与图像处理系列第7期)

在 MATLAB 中进行图像分割有多种方法,下面介绍一些常用的方法: 基于阈值的二值化分割这是一种最简单的分割方法,将图像分为两个部分:背景和前景。其主要思想是,选择一个阈值,将图像中的像素值与阈值进行比较,将像素值大于阈值的像素标记为前景(白色),将像素值小于阈值…

17. 订单金额趋势分析

文章目录 题目需求思路一实现一实现二&#xff1a;使用 over(range)学习链接题目来源 题目需求 查询截止每天的最近3天内的订单金额总和以及订单金额日平均值&#xff0c;保留两位小数&#xff0c;四舍五入。 最近三天 的业务逻辑通常是基于当天往前推2天 期望结果如下&#x…

归并排序的具体实现过程

作者主页&#xff1a;paper jie的博客_CSDN博客-C语言,算法详解领域博主 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《算法详解》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心…

【JavaEE初阶】CSS

摄影分享~ 文章目录 一.CSS基本规范1. CSS基本语法规范2.CSS选择器 二.CSS常用属性1. 字体属性2.文本属性3.背景属性4.圆角矩形5.元素的显示模式块级元素行内元素 6.盒子模型边框内边距外边距 7.弹性布局 一.CSS基本规范 层叠样式表。(Cascading Style Sheets) CSS 能够对网页…

【深度学习】AIGC ,ControlNet 论文,原理,训练,部署,实战,教程

论文&#xff1a;https://arxiv.53yu.com/pdf/2302.05543 代码&#xff1a;https://github.com/lllyasviel/ControlNet 得分几个博客完成这个事情的记录了&#xff0c;此篇是第一篇&#xff0c;摘录了一些论文内容。ControlNet 的原理极为朴实无华&#xff08;对每个block添加…

BPMNJS 在HTML中的引入与使用

BPMNJS 在HTML中的引入与使用 在网上看到的大多是基于vue使用BPMN的示例或者教程&#xff0c;竟然没有在HTML使用的示例&#xff0c;有也是很简单的介绍核心库的引入和使用&#xff0c;并没有涉及到扩展库。于是简单看了下&#xff0c;真的是一波三折&#xff0c;坎坎坷坷。不过…

【UE5 Cesium】08-Cesium for Unreal 子关卡应用实例(上)

UE版本&#xff1a;5.1 效果 &#xff08;运行游戏可以看到进入关卡体积内楼房模型才会显现&#xff0c;以此来减少电脑性能消耗&#xff09; 步骤 一、新建两个子关卡&#xff08;以北京和上海为例&#xff09; 点击窗口-》关卡-》新建 命名第一个子关卡为“SubLevel_Bei…

【数据库】使用DBever连接人大金仓数据库

下载安装DBever 首先需要下载并安装DBever&#xff0c;可以在DBever官网上下载最新版的安装程序&#xff0c;根据提示进行安装即可。 下载驱动程序 首先需要从人大金仓官方网站下载适用于DBever的驱动程序。下载完成后&#xff0c;将驱动程序保存到本地计算机上。 添加驱动…

7.4.2 【Linux】特殊设备 loop 挂载 (镜像文件不烧录就挂载使用)

挂载光盘/DVD镜像文件 如此一来我们不需要将这个文件烧录成为光盘或者是 DVD 就能够读取内部的数据了。 创建大文件以制作 loop 设备文件&#xff01; 创建大型文件 假设我要创建一个空的文件在 /srv/loopdev &#xff0c;那可以这样做&#xff1a; 将 512 块&#xff0c;每…

【算法|动态规划系列No.5】leetcode62. 不同路径

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

linux内核TCP源码浅析

目录 数据接收流程驱动层网络层ip_local_deliverip_local_deliver_finish 传输层tcp_v4_rcvtcp_v4_do_rcvtcp_rcv_establishedtcp_recvmsg linux内核源码下载&#xff1a;https://cdn.kernel.org/pub/linux/kernel/ 我下载的是&#xff1a;linux-5.11.1.tar.gz 数据接收流程 …

window.open()实现PDF预览

效果图如下&#xff1a; 页面使用: window.open(strUrl) 参数说明如下图: