前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实现一个基本的计算器来计算一个简单的字符串表达式 s 的值

实现一个基本的计算器来计算一个简单的字符串表达式 s 的值

原创
作者头像
大发明家
发布2021-12-15 15:33:15
2K0
发布2021-12-15 15:33:15
举报
文章被收录于专栏:技术博客文章

基本计算器

题目:实现一个基本的计算器来计算一个简单的字符串表达式 s 的值。

示例 1:undefined 输入:s = "1 + 1"undefined 输出:2undefined 示例 2:undefined 输入:s = " 2-1 + 2 "undefined 输出:3undefined 示例 3:undefined 输入:s = "(1+(4+5+2)-3)+(6+8)"undefined 输出:23undefined 提示:undefined 1 <= s.length <= 3 * 105undefined s 由数字、'+'、'-'、'('、')'、和 ' ' 组成undefined s 表示一个有效的表达式

思路:这个题挺简单的。虽然是困难难度,但是类似的我做过,就是各种字符串分情况处理。用一个flag记录+还是-。然后括号里的先计算。大概思路就这样,我直接去敲代码试试了。

第一版代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    public int calculate(String s) {
代码语言:txt
复制
        boolean flag = true;//true是加,false是减
代码语言:txt
复制
        int first = 0;//第一个加数
代码语言:txt
复制
        int temp = 0;
代码语言:txt
复制
        for(int i = 0;i<s.length();i++){
代码语言:txt
复制
            while (i<s.length() && Character.isDigit(s.charAt(i))){
代码语言:txt
复制
                temp = temp*10+(s.charAt(i)-'0');
代码语言:txt
复制
                i++;
代码语言:txt
复制
            }
代码语言:txt
复制
            if(i == s.length()) break;
代码语言:txt
复制
            //当前到了+-,那么要把之前的都计算清楚了
代码语言:txt
复制
            if(s.charAt(i) == '+' || s.charAt(i) == '-'){
代码语言:txt
复制
                if(flag){
代码语言:txt
复制
                    first += temp;
代码语言:txt
复制
                }else {
代码语言:txt
复制
                    first -= temp;
代码语言:txt
复制
                }
代码语言:txt
复制
                temp = 0;
代码语言:txt
复制
                flag = s.charAt(i) == '+'?true:false;
代码语言:txt
复制
            }else if(s.charAt(i) == '('){
代码语言:txt
复制
                StringBuffer sb = new StringBuffer();
代码语言:txt
复制
                int z = 1;//遇到左括号了,所以左括号数量+1
代码语言:txt
复制
                while (z != 0){
代码语言:txt
复制
                    i++;
代码语言:txt
复制
                    char c = s.charAt(i);
代码语言:txt
复制
                    sb.append(c);
代码语言:txt
复制
                    if(c == '('){
代码语言:txt
复制
                        z++;
代码语言:txt
复制
                    }else if(c == ')'){
代码语言:txt
复制
                        z--;
代码语言:txt
复制
                    }
代码语言:txt
复制
                }
代码语言:txt
复制
                //到这里说明z为0,也就是说是一个完整的括号中的内容了。直接计算
代码语言:txt
复制
                if(flag){
代码语言:txt
复制
                    first += calculate(sb.substring(0,sb.length()-1).toString());
代码语言:txt
复制
                }else {
代码语言:txt
复制
                    first -= calculate(sb.substring(0,sb.length()-1).toString());
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return flag?first+temp:first-temp;
代码语言:txt
复制
    }
代码语言:txt
复制
}

说真的,我觉得性能应该还好,优化点的话应该是StringBuffer这块吧?改下去试试。

emmmm...从百分之五到百分之七,也算是进步了?修改版代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    public int calculate(String s) {
代码语言:txt
复制
        boolean flag = true;//true是加,false是减
代码语言:txt
复制
        int first = 0;//第一个加数
代码语言:txt
复制
        int temp = 0;
代码语言:txt
复制
        for(int i = 0;i<s.length();i++){
代码语言:txt
复制
            while (i<s.length() && Character.isDigit(s.charAt(i))){
代码语言:txt
复制
                temp = temp*10+(s.charAt(i)-'0');
代码语言:txt
复制
                i++;
代码语言:txt
复制
            }
代码语言:txt
复制
            if(i == s.length()) break;
代码语言:txt
复制
            //当前到了+-,那么要把之前的都计算清楚了
代码语言:txt
复制
            if(s.charAt(i) == '+' || s.charAt(i) == '-'){
代码语言:txt
复制
                if(flag){
代码语言:txt
复制
                    first += temp;
代码语言:txt
复制
                }else {
代码语言:txt
复制
                    first -= temp;
代码语言:txt
复制
                }
代码语言:txt
复制
                temp = 0;
代码语言:txt
复制
                flag = s.charAt(i) == '+'?true:false;
代码语言:txt
复制
            }else if(s.charAt(i) == '('){
代码语言:txt
复制
                int start = i+1;
代码语言:txt
复制
                int z = 1;//遇到左括号了,所以左括号数量+1
代码语言:txt
复制
                while (z != 0){
代码语言:txt
复制
                    i++;
代码语言:txt
复制
                    char c = s.charAt(i);
代码语言:txt
复制
                    if(c == '('){
代码语言:txt
复制
                        z++;
代码语言:txt
复制
                    }else if(c == ')'){
代码语言:txt
复制
                        z--;
代码语言:txt
复制
                    }
代码语言:txt
复制
                }
代码语言:txt
复制
                //到这里说明z为0,也就是说是一个完整的括号中的内容了。直接计算
代码语言:txt
复制
                if(flag){
代码语言:txt
复制
                    first += calculate(s.substring(start,i));
代码语言:txt
复制
                }else {
代码语言:txt
复制
                    first -= calculate(s.substring(start,i));
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return flag?first+temp:first-temp;
代码语言:txt
复制
    }
代码语言:txt
复制
}

怎么说呢,我不知道是不是因为我递归的原因,反正是性能还是不咋地。。。直接去看看性能第一的代码吧:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    int index = 0;
代码语言:txt
复制
    public int calculate(String s) {
代码语言:txt
复制
        int sign = 1;
代码语言:txt
复制
        int res = 0;
代码语言:txt
复制
        int num = 0;
代码语言:txt
复制
        while (index < s.length()) {
代码语言:txt
复制
            char c = s.charAt(index);
代码语言:txt
复制
            if (c >= '0' && c <= '9') {
代码语言:txt
复制
                num = num*10 + c - '0';
代码语言:txt
复制
            } else if (c == '+' || c == '-') {
代码语言:txt
复制
                res += sign * num;
代码语言:txt
复制
                num = 0;
代码语言:txt
复制
                sign = c == '+'?1:-1;
代码语言:txt
复制
            } else if (c == '(') {
代码语言:txt
复制
                index++;
代码语言:txt
复制
                res += sign*calculate(s);
代码语言:txt
复制
                sign = 1;
代码语言:txt
复制
            } else if (c == ')') {
代码语言:txt
复制
                res += sign*num;
代码语言:txt
复制
                return res;
代码语言:txt
复制
            }
代码语言:txt
复制
            index++;
代码语言:txt
复制
        }
代码语言:txt
复制
        return res+num*sign;
代码语言:txt
复制
    }
代码语言:txt
复制
}

我觉得吧,思路是大同小异的,你看前几步代码都差不多,但是为什么写出来差别这么大呢!!!其实这里也用到了递归。只不过没有多余遍历。而是每一次(括号进递归,)括号退出。

看完人家写的恍然大悟,自己当时没想到。这个题其实麻烦为主,不算很难,下一题了。

森林中的兔子

题目:森林中,每个兔子都有颜色。其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色。我们将这些回答放在 answers

数组里。返回森林中兔子的最少数量。

示例:undefined 输入: answers = 1, 1, 2undefined 输出: 5undefined 解释:undefined 两只回答了 "1" 的兔子可能有相同的颜色,设为红色。undefined 之后回答了 "2" 的兔子不会是红色,否则他们的回答会相互矛盾。undefined 设回答了 "2" 的兔子为蓝色。undefined 此外,森林中还应有另外 2 只蓝色兔子的回答没有包含在数组中。undefined 因此森林中兔子的最少数量是 5: 3 只回答的和 2 只没有回答的。undefined 输入: answers = 10, 10, 10undefined 输出: 11undefined 输入: answers = []undefined 输出: 0undefined 说明:undefined answers 的长度最大为1000。undefined answersi 是在 0, 999 范围内的整数。

**思路:这个题有个小技巧:回答一样的可能是同一个颜色。但是这里有个坑:回答人数不能超过总数。所以说这里要判断两次。比如说第一个例子:1 1

2是这个答案。但是如果1 1 1 2的话,那么要多加两个兔子了,三个回答1的,可能两个颜色相同。剩下的我们最大可能看成颜色相同,比如1 1 1 1

2的话可以和1 1 1 2一样、差不多就这个思路,我去实现下试试。**

附上第一版代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    public int numRabbits(int[] answers) {
代码语言:txt
复制
        int[] d = new int[1000];
代码语言:txt
复制
        for(int i : answers) d[i]++;
代码语言:txt
复制
        int ans = 0;
代码语言:txt
复制
        for(int i = 0;i<d.length;i++){
代码语言:txt
复制
            if(d[i] == 0) continue;
代码语言:txt
复制
            //有种情况是这些人虽然是一样的但是也不能都是一个颜色,不然颜色超了,所以这里用num判断颜色数
代码语言:txt
复制
            int n = d[i];
代码语言:txt
复制
            int num = n/(i+1);
代码语言:txt
复制
            if(n%(i+1) != 0){
代码语言:txt
复制
                ans += (num+1)*(i+1);
代码语言:txt
复制
            }else {//如果等于0的话则正好划分这么多阵营
代码语言:txt
复制
                ans += n;
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return ans;
代码语言:txt
复制
    }
代码语言:txt
复制
}

总而言之这个题没什么坑,按照我上面的思路代码性能也还好。所以这个题就这么过了。最近这几道题做的很顺啊。

判断二分图

题目:存在一个 无向图 ,图中有 n 个节点。其中每个节点都有一个介于 0 到 n - 1 之间的唯一编号。给你一个二维数组 graph

,其中 graphu 是一个节点数组,由节点 u 的邻接节点组成。形式上,对于 graphu 中的每个 v ,都存在一条位于节点 u 和节点 v

之间的无向边。该无向图同时具有以下属性:

代码语言:txt
复制
不存在自环(graph[u] 不包含 u)。
代码语言:txt
复制
不存在平行边(graph[u] 不包含重复值)。
代码语言:txt
复制
如果 v 在 graph[u] 内,那么 u 也应该在 graph[v] 内(该图是无向图)
代码语言:txt
复制
这个图可能不是连通图,也就是说两个节点 u 和 v 之间可能不存在一条连通彼此的路径。
二分图 定义:如果能将一个图的节点集合分割成两个独立的子集 A 和 B ,并使图中的每一条边的两个节点一个来自 A 集合,一个来自 B

集合,就将这个图称为 二分图 。如果图是二分图,返回 true ;否则,返回 false 。

题目截图

**思路:这个题感觉有点类似之前的分圈子的游戏。本身每一条线都要一头在一边。所以下标本身代表的值和下标对应的数组中元素的值一定是在两个圈子里。同时有可能分多个圈子,所以这里不能从第一个元素开始一直往下扒。而且其实每一个元素在计算时有三种状态:0

无划分,

1划分到第一个分组,2划分到第二个分组。其实我们在遍历中可以很容易判断:如果某一个集合中(一个集合中元素应该在一个分组。并且与下标所在分组正好不同)存在和下标相同的分组则说明分不了,直接false。否则继续往下判断,看能不能都走一遍。如果所有元素都可以分好,那么就是true,否则false。思路比较清晰,我去实现下试试。**

第一版代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    int[] c;
代码语言:txt
复制
    boolean flag;
代码语言:txt
复制
    public boolean isBipartite(int[][] graph) {
代码语言:txt
复制
        flag = true;
代码语言:txt
复制
        c = new int[graph.length];
代码语言:txt
复制
        for(int i = 0;i<graph.length&&flag;i++){
代码语言:txt
复制
            if(c[i] == 0) isOk(i,1,graph);
代码语言:txt
复制
        }
代码语言:txt
复制
        return flag;
代码语言:txt
复制
    }
代码语言:txt
复制
    public void isOk(int temp,int red,int[][] graph){
代码语言:txt
复制
        c[temp] = red;
代码语言:txt
复制
        int cur = c[temp]==1?2:1;//因为一条无向边两端要在不同阵营,所以下标是1的话,这里的值都是2
代码语言:txt
复制
        for(int i:graph[temp]){
代码语言:txt
复制
            if(c[i] == 0){
代码语言:txt
复制
                //把当前节点看做是2,看能不能跑通
代码语言:txt
复制
                isOk(i,cur,graph);
代码语言:txt
复制
                if(!flag) return;
代码语言:txt
复制
            }else if(c[i] == red){
代码语言:txt
复制
                flag = false;
代码语言:txt
复制
                return;
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
}

其实这个题我前不久才做过类似的,所以这里占了便宜。简单来说就是用染色的方式实现的。我记得之前做个题目是给花染色。有点类似这个题目。都是下标值本身和其对应数组的值一定要相反。这个dfs的规律就是一次1.一次2这样循环来的。多看看代码debug跑跑就行了。继续下一题了。

K站中转内最便宜的航班

题目:有 n 个城市通过 m 个航班连接。每个航班都从城市 u 开始,以价格 w 抵达 v。现在给定所有的城市和航班,以及出发城市 src

和目的地 dst,你的任务是找到从 src 到 dst 最多经过 k 站中转的最便宜的价格。 如果没有这样的路线,则输出 -1。

题目截图

提示:undefined n 范围是 1, 100,城市标签从 0 到 n - 1undefined 航班数量范围是 0, n * (n - 1) / 2undefined 每个航班的格式 (src, dst, price)undefined 每个航班的价格范围是 1, 10000undefined k 范围是 0, n - 1undefined 航班没有重复,且不存在自环

思路:其实这个题我觉得深搜就可以了,每一个可选项都顺着往下走,遇到超过k次的就减枝。然后在k次内从s到e的费用记下来,取最小值,over~我想的还是挺美的。然后至于所有的起始点,我个人打算用map构单项图。反正思路是有了,我去实现下试试。

第一版代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    int ans = Integer.MAX_VALUE;
代码语言:txt
复制
    int k;
代码语言:txt
复制
    int end;
代码语言:txt
复制
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
代码语言:txt
复制
        Map<Integer,Map<Integer,Integer>> map = new HashMap<>();
代码语言:txt
复制
        for(int [] i :flights){
代码语言:txt
复制
            Map<Integer,Integer> sub = map.get(i[0]);
代码语言:txt
复制
            if(sub == null) sub = new HashMap<>();
代码语言:txt
复制
            sub.put(i[1],i[2]);
代码语言:txt
复制
            map.put(i[0],sub);
代码语言:txt
复制
        }
代码语言:txt
复制
        k = K;
代码语言:txt
复制
        end = dst;
代码语言:txt
复制
        dfs(map,new HashSet<Integer>(),0,src);
代码语言:txt
复制
        return ans == Integer.MAX_VALUE?-1:ans;
代码语言:txt
复制
    }
代码语言:txt
复制
    public void dfs(Map<Integer,Map<Integer,Integer>> map,Set<Integer> s,Integer money,Integer src){
代码语言:txt
复制
        if(src == end) {//src等于end说明到了终点,判断最小花费
代码语言:txt
复制
            ans = Math.min(ans,money);
代码语言:txt
复制
            return;
代码语言:txt
复制
        }
代码语言:txt
复制
        //最多经过k个中转站,起始站不算。所以中间最多只能经过起始点个数-1个站点。否则就不能往下走了。
代码语言:txt
复制
        if(s.size()>=k+1) return;
代码语言:txt
复制
        Map<Integer,Integer> sub = map.get(src);
代码语言:txt
复制
        if(sub == null) return;//这条路走不通了。
代码语言:txt
复制
        for(Integer key:sub.keySet()){
代码语言:txt
复制
            //去重:这条路线已经走过这个节点了,不能回去了
代码语言:txt
复制
            if(s.contains(key)) continue;
代码语言:txt
复制
            //剪枝:当前的花费已经大于已有最小值,直接pass
代码语言:txt
复制
            if(money+sub.get(key)>=ans) continue;
代码语言:txt
复制
            Set<Integer> set = new HashSet<>();
代码语言:txt
复制
            set.addAll(s);
代码语言:txt
复制
            set.add(key);
代码语言:txt
复制
            dfs(map,set,money+sub.get(key),key);
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
}

可能,我没超时就已经是上天保佑了吧。。。感觉性能应该还是卡在了set这块了。因为n最大99。我感觉这里用数组都比用set强,也有可能是这里可以用回溯吧。反正我感觉是我写的有问题了。。我去看看性能第一的代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
代码语言:txt
复制
        // dp[i][k]是经过k个中转站后到达站 i 的最小费用
代码语言:txt
复制
        int[][] dp = new int[n][K + 1];
代码语言:txt
复制
        // 循环初始化整个二维数组。
代码语言:txt
复制
        for(int i = 0; i < n; ++i) Arrays.fill(dp[i], Integer.MAX_VALUE);
代码语言:txt
复制
        // 利用flights中的信息初始化src可直达的班次
代码语言:txt
复制
        for(int[] flight : flights) {
代码语言:txt
复制
            if(flight[0] == src){
代码语言:txt
复制
                dp[flight[1]][0] = flight[2];
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        // 循环初始化数组中dst == src的行
代码语言:txt
复制
        for(int i = 0; i <= K; i++){
代码语言:txt
复制
            dp[src][i] = 0;
代码语言:txt
复制
        }
代码语言:txt
复制
        //动态规划状态转移方程,开始填表
代码语言:txt
复制
        //直达的已经初始化了(即k = 0的情况),现在从k = 1 的开始,即只有一个中转站开始
代码语言:txt
复制
        for(int k = 1; k <= K; k++){
代码语言:txt
复制
            for(int[] flight : flights){
代码语言:txt
复制
                //结合题目理解
代码语言:txt
复制
                if(dp[flight[0]][k - 1] != Integer.MAX_VALUE){
代码语言:txt
复制
                    dp[flight[1]][k] = Math.min(dp[flight[1]][k], dp[flight[0]][k - 1] + flight[2]);
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return dp[dst][K] == Integer.MAX_VALUE? -1: dp[dst][K];
代码语言:txt
复制
    }
代码语言:txt
复制
}

这个题原来可以用dp啊。。看了人家写的才发现这个题用dp确实挺合适。。而且我特意挑了一个注释写的很全的代码贴出来的。dp其实也是一步一步往下走,但是因为记住了每一步,所以有一些重复数据是不用来回来去计算的了。比如我上文中:

1,2,4 1,4 1,5,4这种情况下,三种方式走到了4这个节点,然后要重复计算三次4往下走的路。

但是用dp的话就可以省略了。dp——记录中间过程的递归。

其实看了人家的代码我就又又又恍然大悟了,但是自己下意识的想法就是递归,哎,我差不多是没救了,下一题吧。

多米诺和托米诺平铺

题目:有两种形状的瓷砖:一种是 2x1 的多米诺形,另一种是形如 "L" 的托米诺形。两种形状都可以旋转。
XX <- 多米诺
XX <- "L" 托米诺
X
给定 N 的值,有多少种方法可以平铺 2 x N 的面板?返回值 mod 10^9 +

7。(平铺指的是每个正方形都必须有瓷砖覆盖。两个平铺不同,当且仅当面板上有四个方向上的相邻单元中的两个,使得恰好有一个平铺有一个瓷砖占据两个正方形。)

示例:undefined 输入: 3undefined 输出: 5undefined 解释:undefined 下面列出了五种不同的方法,不同字母代表不同瓷砖:undefined XYZ XXZ XYY XXY XYYundefined XYZ YYZ XZZ XYY XXYundefined 提示:undefined N 的范围是 1, 1000

_思路:这个题乍一看有点蒙,但是稍微一看示例其实就能发现是个典型的dp题目了。首先我觉得这个示例3用的很有用。因为正好按照两个砖的摆放,3应该是个比较大的可能了。比如再往下输入是4.也就是在3的基础上多加一个竖条,只有多米诺可以实现,往前后者往后竖着贴一层。也就是结果是3的结果

2(因为每一种可能都可以往前或者往后加一个竖着的多米诺。)另外如果每多两个,都会有一种的相比于之前多一种可能:。_*

不得不说我在推导递推公式的时候直接发现规律了。虽然为什么不懂。。下面是计算过程:

找规律

结果是前一个数*2 + 前三个数。从4开始有规律的。我去试试这个规律是不是对的。

!!!!!就这么ac了,而且性能超好,这里一定要截图纪念一下!

ac了

然后附上代码:

代码语言:txt
复制
class Solution {
代码语言:txt
复制
    public int numTilings(int N) {
代码语言:txt
复制
        if(N<3) return N;
代码语言:txt
复制
        long[] dp = new long[N+1];
代码语言:txt
复制
        dp[0] = 0l;
代码语言:txt
复制
        dp[1] = 1l;
代码语言:txt
复制
        dp[2] = 2l;
代码语言:txt
复制
        dp[3] = 5l;
代码语言:txt
复制
        for(int i = 4;i<=N;i++){
代码语言:txt
复制
            dp[i] = (dp[i-1]*2 + dp[i-3])%1000000007;
代码语言:txt
复制
        }
代码语言:txt
复制
        return (int)dp[N];
代码语言:txt
复制
    }
代码语言:txt
复制
}

感觉逆推为什么的话可能更容易一些吧。。。我去琢磨琢磨为什么是上一个*2+上上上一个。

好吧,我果断没琢磨出来,所以这里看了题解,并且看题解都找了好几个才懂,大概的思路下面一点点说:

我们求dpn时

  1. 考虑从dpn-1基础上补2×1列图案,只有用1个多米诺的1种方案;
  2. 考虑从dpn-2基础上补2×2列图案,只有用2个多米诺的1种方案;(注意,补的部分一定要不可从某列中间断开,否则会重复!比如两个竖着放的多米诺这种补法一定会和上面的一个竖着的方案重复)
  3. 考虑从dpn-x基础上补2×2列图案,只有用2个托米诺+y个多米诺拼的2种方案;(x为2,3,4,……,n)

所以递推公式为

  • dpn=dpn-1+dpn-2+(dp0+dp1+...+dpn-3)*2
  • 再结合dpn-1=dpn-2+dpn-3+(dp0+dp1+...+dpn-4)*2
  • 可得dpn=dpn-1*2+dpn-3

然後这样才算是勉强反推回来了。。。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本计算器
  • 森林中的兔子
  • 判断二分图
  • K站中转内最便宜的航班
  • 多米诺和托米诺平铺
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档