单调栈是指栈里的元素保持升序或者降序。

判别是否需要使用单调栈:通常是一维数组里面,需要寻找一个元素左边或者右边第一个比自己大或者小的元素的位置,则可以考虑使用单调栈;这样的时间复杂度一般为O(n)。

题目索引

  • 力扣 739. 每日温度(2022.9.16华为)
  • 力扣 496. 下一个更大元素 I
  • 力扣 503. 下一个更大元素 II
  • 力扣 42. 接雨水
  • 力扣 84. 柱状图中最大的矩形
  • 力扣 901. 股票价格跨度
  • 力扣 316. 去除重复字母

力扣 739. 每日温度(2022.9.16华为)

原题链接

要找出第一次比第i天温度高的那一天,是在第i天的res[i]天后,也就是对应每一个temperatures[i],找到几天后出现比它高的温度,如果没有,那一位就是0;

这就是很明显的找出元素右边第一个比自己大的元素,是单调栈题目。

对于单调栈题目,必须要要先明确三点:

  • 单调栈内存的是什么元素?
  • 单调栈内元素的是递增还是递减(从栈顶到栈底)?
  • 当前遍历到的元素和栈顶元素不同大小关系时进行什么操作?

这三点都知道了,代码逻辑也就明确了。

  1. 单调栈内存的是什么元素?
    注意题目要求的是升温的天数(也就是下标的差值),而不是升温后的温度,因此栈中应该存储下标,而非温度。

  2. 单调栈内元素的是递增还是递减(从栈顶到栈底)?

    • 如果是递减,那么想要保持递减关系,碰到比栈顶元素代表的温度更高的元素时,执行入栈操作,那就无法找出第一个比栈顶元素温度更高的那一天了;
    • 如果是递增,那么想要保持递增关系,碰到比栈顶元素温度更高的元素时,栈顶元素要出栈,并且后面的栈顶元素若是还是小于这个元素,还要继续出栈,这就符合“找出第一个比前面遍历过的元素更大的元素”,因为当前遍历到的元素就是第一个比栈内元素都大的元素。
  1. 当前遍历到的元素和栈顶元素不同大小关系时进行什么操作?
    因为找出符合题目要求的元素最重要的就是温度的比较,所以是用下标所代表的温度进行比较的:
    • ①temperatures[i]<temperatures[stack.peek()] (当前元素小于栈顶元素)
      说明不是第一个比栈顶元素指代的温度高的那一天的温度,入栈,栈内元素保持递增关系(自栈顶到栈底);即stack.push(i)
    • ②temperatures[i]=temperatures[stack.peek()] (当前元素等于栈顶元素)
      同理,入栈;即stack.push(i)
    • ③temperatures[i]>temperatures[stack.peek()] (当前元素大于栈顶元素)
      遇到了第一个比栈顶元素温度更高的温度,栈顶元素出栈,res数组记录“当前遍历到的这一天减去栈顶元素那一天(过了多少天)”,即res[stack.pop()]=i-stack.pop();也就是:若当日温度大于栈顶温度,说明栈顶元素的升温日已经找到了,则将栈顶元素出栈,计算其与当日相差的天数即可。(但是要注意,新的栈顶元素指代温度如果还是小于当前元素指代温度,要继续出栈)

如果想看具体的每种情况的图解举例,可以移步力扣官解:链接在此

代码如下:

//单调栈
class Solution {public int[] dailyTemperatures(int[] temperatures) {int len=temperatures.length;Deque<Integer> stack=new LinkedList<>();//Java建议使用Deque替换Stackint[] res=new int[len];stack.push(0);for(int i=1;i<len;i++){if(temperatures[i]<=temperatures[stack.peek()])stack.push(i);else{while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){res[stack.peek()]=i-stack.peek();stack.pop();}stack.push(i);}}return res;}
}

关于为什么使用Deque而不使用Stack,理由在此:CSDN文章解释

补充:新发现了一篇文章对这个问题的讲解更为全面:Java 程序员,别用 Stack?!

代码其实可以更简洁一些,因为每种情况最后都需要push(i),只有当栈非空,且当日温度大于栈顶温度才会出栈记录天数,所以情况可以合在一起:

//单调栈--简洁
class Solution {public int[] dailyTemperatures(int[] temperatures) {int len=temperatures.length;Deque<Integer> stack=new LinkedList<>();//Java建议使用Deque替换Stackint[] res=new int[len];for(int i=0;i<len;i++){while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){res[stack.peek()]=i-stack.peek();stack.pop();}stack.push(i);}return res;}
}

力扣 496. 下一个更大元素 I

原题链接

这比起739. 每日温度,多了nums1数组,可以直接把nums2数组当成739里的temperatures数组:

在nums2数组里找到右边第一个更大的数,当发生nusm2[i]>nums2[stack.peek()]时(即当前元素比栈顶元素大时),本来正常操作是要让栈顶元素出栈,并且继续比较下一个栈顶元素和当前元素关系,直到当前元素符合入栈条件为止。

但是在这题里,我们需要找到的是nums1出现在nums2数组中的元素第一个比自己大的元素的值,所以在这个操作里面,需要判断当前要出栈的栈顶元素,是不是在nums1里的元素。是的时候,就把res数组赋值;不是的时候,直接出栈就行了;

还需要解决一个问题,就是怎么快速判断栈顶元素在不在nums1里出现,可以用HashMap来存储nums1的元素值和下标,key-元素值,value-下标(因为hashMap.containsKey()里面的是value,stack.peek()中的元素是下标,需要判断这个下标–栈顶元素,对应的nums2元素是否在以nums1元素和下标为键值对建立的HashMap中:hashMap.containsKey(nums2[stack.peek()]))。

在nums1里,才执行res的赋值操作,然后栈顶元素出栈,继续判断栈顶元素是否符合,重复此操作,直至不符合while循环条件,当前元素入栈;

代码如下:

//单调栈--简洁写法
class Solution {public int[] nextGreaterElement(int[] nums1, int[] nums2) {HashMap<Integer,Integer> hashMap=new HashMap<>();//用于存//单调栈,用于找出nums2中右边第一个比自己大的元素的位置Deque<Integer> stack=new LinkedList<>();int[] res=new int[nums1.length];Arrays.fill(res,-1);//初始化res元素全为-1//kew-value映射for(int i=0;i<nums1.length;i++)hashMap.put(nums1[i],i);for(int i=0;i<nums2.length;i++){while(!stack.isEmpty()&&nums2[i]>nums2[stack.peek()]){if(hashMap.containsKey(nums2[stack.peek()]))res[hashMap.get(nums2[stack.peek()])]=nums2[i];stack.pop();}stack.push(i);}return res;}
}

力扣 503. 下一个更大元素 II

原题链接
这题是在739. 每日温度基础上,把数组改成了循环数组,即最后一个元素后面跟着的是数组第一个元素,这样循环往复,常规想法会把一个新的nums数组拼接在原来的nums数组后,但是我们可以从逻辑上模拟拼接后的数组:

代码如下:

//单调栈(时间O(n),空间O(n))
class Solution {public int[] nextGreaterElements(int[] nums) {int len=nums.length;Deque<Integer> stack=new LinkedList<>();int[] res=new int[len];Arrays.fill(res,-1);//模拟拼接数组(i<len*2)for(int i=0;i<len*2;i++){while(!stack.isEmpty()&&nums[i%len]>nums[stack.peek()]){res[stack.peek()]=nums[i%len];stack.pop();}stack.push(i%len);}return res;}
}

力扣 42. 接雨水

原题链接

双指针法

求解方法来自代码随想录:

可以按列来累加雨水,假设当前位置为4,那么第4列的雨水,按列来看可以这样求:

分别找到列4左右两边的最高柱子,分别是height[2]=3和height[7]=3,列4的柱子高度height[4]=1;列4的雨水高度h=min(height[2],height[7])-height[4]=1; 即若想求取每个位置的雨水高度,找到该位置左右两边各自的最高柱子高度,选取其中较小的高度,再减去当前柱子高度,就是该位置雨水高度。

代码如下:

//双指针法(时间O(n^2),空间O(1))
class Solution {public int trap(int[] height) {int len=height.length;int rainSum=0;for(int i=0;i<len;i++){//最左最右无雨水,跳过if(i==0||i==len-1)continue;//分别记录左、右边最高柱高度int lHight=height[i],rHeight=height[i];//向左走,找最高;向右走,找最高for(int l=i-1;l>=0;l--)lHight=Math.max(lHight,height[l]);for(int r=i+1;r<len;r++)rHeight=Math.max(rHeight,height[r]);//计算当前位置所在列的雨水量int h=Math.min(lHight,rHeight)-height[i];if(h>0)rainSum+=h;//只有雨水高度大于0才累加}return rainSum;}
}

动态规划

从前面的双指针法可以知道,我们只要知道了当前位置左右最高的柱子高度就可以求取当前位置的列雨水量,在双指针法里面是针对每个位置都去遍历求取左右最高柱高度,这样每个位置都需要时间O(n),其实可以节省这部分的时间,只要分别设置左右最高柱数组,记录每个位置左右边最高柱高度,这样在总时间复杂度O(n)即可求出宋雨水量,但是空间会上升到O(n);

代码如下:

//动态规划(时间O(n),空间O(n))
class Solution {public int trap(int[] height) {int len=height.length;int[] maxLeft=new int[len];int[] maxRight=new int[len];//找每个位置左边最高柱高度maxLeft[0]=height[0];for(int i=1;i<len;i++)maxLeft[i]=Math.max(height[i],maxLeft[i-1]);//找每个位置右边最高柱高度maxRight[len-1]=height[len-1];for(int i=len-2;i>=0;i--)maxRight[i]=Math.max(height[i],maxRight[i+1]);int h,rainSum=0;for(int i=1;i<len-1;i++){//计算每个位置雨水列高度h=Math.min(maxLeft[i],maxRight[i])-height[i];if(h>0)rainSum+=h;}return rainSum;}
}

单调栈

单调栈的解析比较复杂和麻烦,不过代码随想录里的讲解深入浅出,很适合理解:
链接在此建议用这个理解。

代码如下:

//单调栈(时间O(n),空间O(n))
class Solution {public int trap(int[] height) {int len=height.length;Deque<Integer> stack=new LinkedList<>();int rainSum=0;stack.push(0);for(int i=1;i<len;i++){if(height[i]<height[stack.peek()])stack.push(i);else if(height[i]==height[stack.peek()]){//stack.pop();//可以不加,效果一样,便于理解stack.push(i);}else{while(!stack.isEmpty()&&height[i]>height[stack.peek()]){int mid=height[stack.pop()];if(!stack.isEmpty()){int w=i-stack.peek()-1;int h=Math.min(height[stack.peek()],height[i])-mid;rainSum+=h*w;}}stack.push(i);}}return rainSum;}
}

可以写得简洁,不过为了方便理解,不太推荐这个写法:

//单调栈(时间O(n),空间O(n))--简洁写法
class Solution {public int trap(int[] height) {int len=height.length;Deque<Integer> stack=new LinkedList<>();int rainSum=0;stack.push(0);for(int i=1;i<len;i++){while(!stack.isEmpty()&&height[i]>height[stack.peek()]){int mid=height[stack.pop()];if(!stack.isEmpty()){int w=i-stack.peek()-1;int h=Math.min(height[stack.peek()],height[i])-mid;rainSum+=h*w;}}stack.push(i);}return rainSum;}
}

力扣 84. 柱状图中最大的矩形

原题链接

为了解决这题,首先提出一种暴力解法,方便后面理解单调栈的解法。通常暴力方法可以枚举矩形的高或者宽:

枚举宽:第一重for循环定位矩形宽度左边界,第二重for循环定位矩形宽度右边界,同时第二重循环还要找出宽度范围内的高度的最小值作为矩形的高度,不断更新矩形面积最大值:

//官方c++题解,暴力枚举宽度时间O(n^2)
class Solution {public:int largestRectangleArea(vector<int>& heights) {int n = heights.size();int ans = 0;// 枚举左边界for (int left = 0; left < n; ++left) {int minHeight = INT_MAX;// 枚举右边界for (int right = left; right < n; ++right) {// 确定高度minHeight = min(minHeight, heights[right]);// 计算面积ans = max(ans, (right - left + 1) * minHeight);}}return ans;}
};

枚举高:这个方法是等下改进为单调栈解法的基础,一重for循环定位每个矩形的高, 然后在这重循环里面,以这个高度为中心,向数组两边扩散,找到左右两边的直到遇到高度小于 h 的柱子,就确定了矩形的左右边界。

class Solution {public:int largestRectangleArea(vector<int>& heights) {int n = heights.size();int ans = 0;for (int mid = 0; mid < n; ++mid) {// 枚举高int height = heights[mid];int left = mid, right = mid;// 确定左右边界while (left - 1 >= 0 && heights[left - 1] >= height) {--left;}while (right + 1 < n && heights[right + 1] >= height) {++right;}// 计算面积ans = max(ans, (right - left + 1) * height);}return ans;}
};

单调栈解法

提炼一下枚举高度的暴力解法:

  • 枚举i号柱子的高度height[i]作为当前矩形的高h;
  • 向两侧扩展,使扩展到的柱子高度不小于h,直到左右两端遇到小于高度h 的柱子为止。这也就是说:要找到左右两侧第一个小于高度h的柱子。找到之后,这左右两根柱子之间(不包括这两根柱子)的柱子宽度就是当前矩形的宽度。

看到“找到左右两侧第一个小于高度h的柱子”是不是很熟悉,这和单调栈的适用范围很像,一般“一维数组里面,需要寻找一个元素左边或者右边第一个比自己大或者小的元素的位置”就可以考虑使用单调栈,那么这题就可以用单调栈来试试。

同时这题也可以类比 42. 接雨水,42中是找每个柱子左右两侧第一个大于该柱子高度的柱子,而这题是找柱子左右两侧第一个小于该柱子高度的柱子。

在42中,找每个柱子左右两侧第一个大于该柱子高度的柱子,单调栈采用自顶向下从小到大的递增顺序,那么本题里面,找柱子左右两侧第一个小于该柱子高度的柱子,单调栈就应该采用自顶向下从大到小的递减顺序

栈内存储的同42,也是数组下标i。

因为只有栈内保持从大到小的顺序,才能保证栈顶元素在遇到比自己小的高度时,可以弹出,并且此时弹出的栈顶元素i,就是作为当前矩形高度的h=height[i]的下标;

遍历到的小于栈顶元素的当前元素,就是第一个小于矩形高度h的柱子的下标right;

栈顶元素弹出后,下一个新的栈顶元素,就是当前高度height[i]左边第一个小于高度h的柱子的下标left(因为栈内从大到小排序);

所以矩形的宽度w=right-left-1;

这样就找到了矩形的宽和高,可以计算矩形面积了。

代码思路和42.接雨水差不多:

代码如下:

//单调栈解法(时间O(n),空间O(n))
class Solution {public int largestRectangleArea(int[] heights) {Deque<Integer> stack=new ArrayDeque<>();int[] nums=new int[heights.length+2];//数组扩容,首尾加一个0//将heights数组索引为0开始,复制到nums数组下标为1的位置,复制的长度为heights的长度System.arraycopy(heights, 0, nums, 1, heights.length);int area=0;stack.push(0);for(int i=1;i<nums.length;i++){if(nums[i]>nums[stack.peek()]){stack.push(i);}else if(nums[i]==nums[stack.peek()]){stack.pop();stack.push(i);}else{while(nums[i]<nums[stack.peek()]){int mid=stack.peek();stack.pop();int left=stack.peek();int right=i;int w=right-left-1;area=Math.max(area,w*nums[mid]);}stack.push(i);}}return area;}
}

首尾加入两个0是起到“哨兵作用”:

有了这两个柱形:

  • 左边的柱形(第 1 个柱形)由于它一定比输入数组里任何一个元素小,它肯定不会出栈,因此栈一定不会为空;
  • 右边的柱形(第 2 个柱形)也正是因为它一定比输入数组里任何一个元素小,它会让所有输入数组里的元素出栈(第 1 个哨兵元素除外)。

动态规划解法

这题和42一样,也可以先动态规划预处理找到每个元素左右两边第一个小于它高度的元素:

//动态规划-预处理左右第一个小于的元素,时间O(n),空间O(n)
class Solution {public int largestRectangleArea(int[] heights) {int length = heights.length;int[] minLeftIndex = new int [length];int[] maxRigthIndex = new int [length];// 记录左边第一个小于该柱子的下标minLeftIndex[0] = -1 ;for (int i = 1; i < length; i++) {int t = i - 1;// 这里不是用if,而是不断向右寻找的过程while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];minLeftIndex[i] = t;}// 记录每个柱子 右边第一个小于该柱子的下标maxRigthIndex[length - 1] = length;for (int i = length - 2; i >= 0; i--) {int t = i + 1;while(t < length && heights[t] >= heights[i]) t = maxRigthIndex[t];maxRigthIndex[i] = t;}// 求和int result = 0;for (int i = 0; i < length; i++) {int sum = heights[i] * (maxRigthIndex[i] - minLeftIndex[i] - 1);result = Math.max(sum, result);}return result;}
}

力扣 901. 股票价格跨度

原题链接

代码如下:

//官方单调栈,时间O(Q),Q是调用next操作的次数
class StockSpanner {//两个栈同步入栈、出栈Deque<Integer> prices;//价格栈Deque<Integer> weights;//跨度栈public StockSpanner() { prices = new LinkedList<>(); //这题用链表作为底层实现效率更高weights = new LinkedList<>(); //因为不断有栈顶的增删操作} public int next(int price) { int w = 1; //跨度包含自身,初始为1//只有当栈非空,并且当前价格大于等于栈顶价格while (!prices.isEmpty() && prices.peek() <= price) { prices.pop(); //把所有小于当前价格的栈顶价格出栈w += weights.pop(); //再把栈内小于当前价格的那几个价格的跨度天数,累加到当前价格这一天}//栈空或者当前价格大于栈顶价格时,当前价格直接入栈prices.push(price); weights.push(w); //并且把这一天的累加天数跨度入栈return w; }
}/*** Your StockSpanner object will be instantiated and called as such:* StockSpanner obj = new StockSpanner();* int param_1 = obj.next(price);*/

如果对官方题解的解释不是很明白,可以再参考这个题解,相比官方题解会更加清晰。


力扣 316. 去除重复字母

原题链接
同1081. 不同字符的最小子序列

这题也是用到单调栈,不过需要在贪心的思想基础上使用单调栈。接下来就介绍一下这题的贪心思想:

贪心思想

首先明确题目要求:

  • ①去除重复的字符
  • ②使字符串字典序最小(但是当该种字符只有一个的时候它必须留下
  • 保留原来字符的相对顺序

考虑一个字符串字典序什么时候最小?

当然是字典序越小的排越前面,整体的字典序就越小,换句话说,也就是保持升序的时候会更小,那么遍历字符串,如果后一个元素字典序小于当前元素的字典序(也就是出现逆序–s[i+1]<s[i]),就删除当前更大的字符s[i].

这样就可以使用单调栈,栈顶到栈底保持元素递减顺序,遍历字符串:当前元素若大于栈顶元素,入栈;当前元素若小于栈顶元素,栈顶元素就出栈;

但是这样只能保证题目要求③保留原来字符的相对顺序;虽然可以使字典序最小,但是就算该种字符只有一个的时候,后面如果碰到了更小的元素,也会被弹出栈顶删除,不满足要求②;当前元素和栈内已有元素出现重复之时,也不会被删除,不满足要求①

为了解决这两个问题,可以增设两个数组:

  • nums数组:下标nums[0…25]分别表示小写的字母a-z。先遍历一遍字符串,统计每种字符的数量,再遍历字符串的时候,每次遍历当前字符的时候,先把nums中该字符的数量-1,表示剩余的该种字符数量-1;要弹出栈顶元素之前,先判断一下栈顶字符在nums中的剩余数量,只有剩余数量大于等于1(说明栈外还有剩)时,才可以弹出栈顶元素。nums用于解决要求②
  • boolean型的isCharInStack数组:下标nums[0…25]分别表示小写的字母a-z。为true时表示栈内有该元素,为false时表示栈内没有该元素。这个数组用在遍历字符串的时候,如果当前元素在栈内已存在,直接跳过;在当前元素入栈后,对应值置true;出栈后,对应值置false;isCharInStack用于解决要求①

代码如下:

//单调栈(时间O(n),空间O(m),n为字符串长度,m为字符串中字母种数)
class Solution {public String removeDuplicateLetters(String s) {int[] nums=new int[26];//记录每种字母数量for(int i=0;i<s.length();i++)++nums[s.charAt(i)-'a'];//单调栈,最后剩余的元素就是去重后的字符串(自底到顶)Deque<Character> stack=new ArrayDeque<>();// 统计字符是否在栈内boolean[] isCharInStack=new boolean[26]; //遍历字符串for(int i=0;i<s.length();i++){char cur=s.charAt(i);//cur表示当前字符nums[cur-'a']--;//遍历到了,剩余字符串的这种字符数量就-1//如果栈内已有这个字符,直接跳过(去重)if(isCharInStack[cur-'a'])continue;//当栈内非空,且当前字符小于栈顶,并且剩下没遍历的字符中还有和栈顶字符一样的while(!stack.isEmpty()&&cur<stack.peek()&&nums[stack.peek()-'a']>=1){//栈顶元素就可以出栈,并且设置该字符不在栈内isCharInStack[stack.pop()-'a']=false;}//当前元素入栈,并设置该字符在栈内stack.push(cur);isCharInStack[cur-'a']=true;}StringBuilder sb=new StringBuilder();while(!stack.isEmpty()){//last是指栈底元素sb.append(stack.removeLast());}return sb.toString();}
}

力扣刷题记录-单调栈相关题目相关推荐

  1. 力扣刷题记录-回溯算法相关题目

    首先介绍一下回溯算法 回溯通常在递归函数中体现,本质也是一种暴力的搜索方法,但可以解决一些用for循环暴力解决不了的问题,其应用有: 1.组合问题: 例:1 2 3 4这些数中找出组合为2的组合,有1 ...

  2. 力扣刷题记录--哈希表相关题目

    当遇到需要快速判断一个元素是否出现在集合里面的时候,可以考虑哈希法,牺牲一定的空间换取查找的时间. java常用的哈希表有HashMap.HashSet以及用数组去模拟哈希,这几种方法各有优劣. 数组 ...

  3. 力扣刷题-python-单调栈(单调递减栈、单调递增栈)

    文章目录 1.单调栈 2.单调递减栈 3.单调递增栈 4.总结 1.单调栈 通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,要用单调栈. 2.单调递减栈 单调递减栈,栈 ...

  4. 力扣刷题记录-动态规划问题总结

    百度百科里对于动态规划问题是这样解释的: 在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果.因此各个阶段 ...

  5. 力扣刷题记录--位运算问题

    这里写目录标题 一.n&(n-1) 1. 求一个数的二进制表示中的1的个数 力扣 191. 位1的个数 AcWing 801. 二进制中1的个数 2. 判断一个数是否是2的方幂 二.n& ...

  6. 力扣刷题记录_字符串(自学)

    字符串 一.字符串 1.反转字符串(力扣344) 2.反转字符串 II(力扣541) 3.替换空格(剑指 Offer 05) 4.翻转字符串里的单词(力扣151) 5.左旋转字符串(剑指 Offer ...

  7. python力扣刷题记录——204. 计数质数

    题目: 统计所有小于非负整数 n 的质数的数量. 方法一: 暴力法 class Solution:def countPrimes(self, n: int) -> int:count = 0if ...

  8. 力扣刷题记录---二分法

    一般的二分查找应用的地方都是在一个单调有序序列里面进行值的搜索,,用中间点进行区域划分,当中间值大于目标值target,说明目标值在左区域,反之则在右区域.这样不断缩小区域,每次搜索区域都只要当前范围 ...

  9. 力扣刷题记录---快排算法

    AcWing 785. 快速排序 对快排算法思想就不描述了,针对快排递归过程中边界的取值做了总结: x为每次递归中,选取的基准数(枢轴) 如果x = q[i]或者x = q[l + r >> ...

最新文章

  1. 如何在TypeScript中删除数组项?
  2. 如何使用Swift获取App版本和内部版本号?
  3. 基于Java Swing的仪表盘实现
  4. 硬件工程师面试经历2015---面试篇
  5. 如何在一个站点里使用两个Web.sitemap 或是多个Web.sitemap?
  6. updatePanle总结
  7. 第一节 9布尔运算符
  8. cdn是什么和作用有些
  9. sessionstorage,localstorage和cookie
  10. creo管道设计教程_Creo7.0设计探索在管道设计的应用
  11. Win10下VB6.0开发之串口通信基础(一)控件属性篇
  12. java反射机制(4)动态代理初探
  13. Linux操作命令提示符
  14. 分辨率,像素,像素密度易懂
  15. LoadRunner 录制IE 8卡死
  16. 2142. The Number of Passengers in Each Bus I
  17. Islands UVA - 1665
  18. Ubuntu16.04安装搜狗输入法的正确姿势
  19. matplotlib绘制两个图形及网格、透明度、图例、颜色等
  20. 郭德纲恶心别人的台词大全

热门文章

  1. 双线程操作控制--笔记
  2. 人教版走进论坛计算机课教案,人教版信息技术五下第1课《认识Excel》教案
  3. 汽车诊断协议 - KWP2000
  4. 从Oracle读取数据并用python处理过程记录(构建BARRA因子遇到的问题)
  5. 计算机毕设(附源码)JAVA-SSM酒店入住管理系统
  6. almaLinux上使用podman容器安装centos
  7. 《缠中说禅108课》95:修炼自己
  8. 跨境电商市场分析有哪些?中国卖家跨境电商未来的机会在哪里?
  9. Windows批处理常用命令
  10. 中国电子信息制造业投资策略分析及十四五需求前景预测报告2022-2028年版