自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

二叉樹各種遍歷真的很難?大sai帶你拿捏!

開發(fā) 前端
很多時(shí)候我們需要使用非遞歸的方式實(shí)現(xiàn)二叉樹的遍歷,非遞歸枚舉相比遞歸方式的難度要高出一些,效率一般會(huì)高一些,并且前中后序枚舉的難度呈一個(gè)遞增的形式,非遞歸方式的枚舉有人停在非遞歸后序,有人停在非遞歸中序,有人停在非遞歸前序(這就有點(diǎn)拉胯了啊兄弟)。

[[424431]]

前言

大家好,我是bigsai,好久不見,甚是想念!

今天帶大家征服二叉樹的前中后序遍歷,包含遞歸和非遞歸方式,學(xué)到就是賺到!

很多時(shí)候我們需要使用非遞歸的方式實(shí)現(xiàn)二叉樹的遍歷,非遞歸枚舉相比遞歸方式的難度要高出一些,效率一般會(huì)高一些,并且前中后序枚舉的難度呈一個(gè)遞增的形式,非遞歸方式的枚舉有人停在非遞歸后序,有人停在非遞歸中序,有人停在非遞歸前序(這就有點(diǎn)拉胯了啊兄弟)。

我們回顧遞歸,它底層其實(shí)是維護(hù)一個(gè)棧,將數(shù)據(jù)存到棧中,每次拋出棧頂?shù)臄?shù)據(jù)進(jìn)行處理(也就是遞歸、dfs的方向化、極端化枚舉特征非常明顯),我們駕馭遞歸的時(shí)候更重要的是掌握上下層之間的邏輯關(guān)系。

而非遞歸方式我們除了需要掌握上下層的邏輯關(guān)系之外,要手動(dòng)的處理各種條件變更的細(xì)節(jié), 遞歸是一個(gè)一來一回的過程,如果我們的邏輯只是在單趟的來或者回中還好,有時(shí)候甚至要自己維護(hù)來和回的狀態(tài),所以邏輯上難度還是比較大的。

二叉樹的前序遍歷

二叉樹的前序遍歷是最簡單的,其枚舉方式就是一個(gè)最簡單的dfs,學(xué)會(huì)了二叉樹的前序遍歷,那么后面入門dfs就容易很多。

二叉樹的前序遍歷的枚舉規(guī)則為:根結(jié)點(diǎn) ---> 左子樹 ---> 右子樹,也就是給定一棵樹,輸出操作當(dāng)前節(jié)點(diǎn),然后枚舉左子樹(左子樹依然按照根左右的順序進(jìn)行),最后枚舉右子樹(右子樹也按照根左右的順序進(jìn)行),這樣得到的一個(gè)枚舉序列就是二叉樹的前序遍歷序列(也叫先序)。

前序遍歷在二叉樹樹的順序可以看下圖(紅色箭頭指向的表示需要訪問的,可以看出從父節(jié)點(diǎn)枚舉下來第一次就要被訪問)。

在具體實(shí)現(xiàn)的方式上,有遞歸方式和非遞歸方式實(shí)現(xiàn)。

遞歸

遞歸方式實(shí)現(xiàn)的二叉樹前序遍歷很簡單,遞歸我們只需要考慮初始情況、結(jié)束邊界、中間正常點(diǎn)邏輯。

初始情況:從root根節(jié)點(diǎn)開始枚舉,函數(shù)執(zhí)行傳入root根節(jié)點(diǎn)作為參數(shù)。

結(jié)束邊界:節(jié)點(diǎn)的左(或右)子節(jié)點(diǎn)為null那么就停止對應(yīng)節(jié)點(diǎn)的遞歸執(zhí)行。

正常點(diǎn)邏輯:先處理當(dāng)前點(diǎn)(存儲或輸出),遞歸調(diào)用枚舉左子樹(如果不為null),遞歸調(diào)用枚舉右子樹(如果不為null)。

剛好力扣144二叉樹的前序遍歷可以嘗試ac:

  1. class Solution { 
  2.     List<Integer>value=new ArrayList(); 
  3.     public List<Integer> preorderTraversal(TreeNode root) { 
  4.         qianxu(root); 
  5.         return value; 
  6.     } 
  7.     private void qianxu(TreeNode node) { 
  8.         if(node==null
  9.             return
  10.         value.add(node.val); 
  11.         qianxu(node.left); 
  12.         qianxu(node.right); 
  13.     } 

非遞歸

非遞歸的前序還是非常簡單的,前序遍歷的規(guī)則是:根節(jié)點(diǎn),左節(jié)點(diǎn),右節(jié)點(diǎn)。但是根左方向一直下去,手動(dòng)枚舉又沒有遞歸回的過程,一直下去我們怎么找到回來時(shí)候的右?guī)c(diǎn)呢?

用棧將路過的節(jié)點(diǎn)先存儲,第一次枚舉節(jié)點(diǎn)輸出儲存然后放入棧中,第二次就是被拋出時(shí)候枚舉其右側(cè)節(jié)點(diǎn)。

它的規(guī)則大致為:

一直訪問當(dāng)前節(jié)點(diǎn)并用棧存儲,節(jié)點(diǎn)指向左節(jié)點(diǎn),直到左孩子為null。

拋出棧頂不訪問。如果有右節(jié)點(diǎn),訪問其右節(jié)點(diǎn)重復(fù)步驟1,如有沒右節(jié)點(diǎn),繼續(xù)重復(fù)步驟2拋出。

這樣的一個(gè)邏輯,就會(huì)從根出發(fā)一直先往左訪問,訪問結(jié)束根、左之后再訪問右節(jié)點(diǎn)(子樹),得到一個(gè)完成的前序遍歷的序列。

具體實(shí)現(xiàn)的代碼為:

  1. class Solution { 
  2.   public List<Integer> preorderTraversal(TreeNode root) { 
  3.     List<Integer>value=new ArrayList(); 
  4.     Stack<TreeNode> q1 = new Stack();     
  5.         while(!q1.isEmpty()||root!=null
  6.         { 
  7.             while (root!=null) { 
  8.                 value.add(root.val); 
  9.                 q1.push(root);               
  10.                 root=root.left
  11.             } 
  12.             root=q1.pop();//拋出 
  13.             root=root.right;//準(zhǔn)備訪問其右節(jié)點(diǎn) 
  14.  
  15.         } 
  16.         return value; 
  17.     } 

二叉樹的中序遍歷

二叉樹的中序遍歷出現(xiàn)的頻率還是蠻高的,如果是二叉排序樹相關(guān)問題還是蠻多的,你要知道二叉排序樹的中序遍歷是一個(gè)有序的序列,如果求二叉排序樹的topk問題,非遞歸中序那效率是非常高的。

中序遍歷在二叉樹樹的順序可以看下圖(紅色箭頭指向的表示需要訪問的,可以看出如果子樹為null,那肯定要訪問,否則就是從左子樹回來的時(shí)候才訪問這個(gè)節(jié)點(diǎn))。

遞歸方式

遞歸方式實(shí)現(xiàn)很簡單,其邏輯和前序遞歸相似的,力扣94剛好有二叉樹中序遍歷,這里我直接放代碼:

  1. class Solution { 
  2.    public List<Integer> inorderTraversal(TreeNode root) { 
  3.       List<Integer>value=new ArrayList<Integer>(); 
  4.       zhongxu(root,value); 
  5.       return value; 
  6.   } 
  7.      private void zhongxu(TreeNode root, List<Integer> value) { 
  8.          if(root==null
  9.              return
  10.          zhongxu(root.left, value); 
  11.          value.add(root.val); 
  12.          zhongxu(root.right, value); 
  13.  
  14.     } 

非遞歸方式

非遞歸的中序和前序是非常相似的,前序遍歷的規(guī)則是:根節(jié)點(diǎn),左節(jié)點(diǎn),右節(jié)點(diǎn)。中序遍歷的順序是左節(jié)點(diǎn),根節(jié)點(diǎn),右節(jié)點(diǎn) ,在前序中先根后左其實(shí)是有點(diǎn)覆蓋的關(guān)系(這個(gè)左就是下一個(gè)跟),在其非遞歸枚舉實(shí)現(xiàn)上我們訪問右節(jié)點(diǎn)時(shí)候是先拋出父節(jié)點(diǎn)不訪問,直接訪問父節(jié)點(diǎn)的右節(jié)點(diǎn),如果拋出父節(jié)點(diǎn)訪問這個(gè)父節(jié)點(diǎn),其實(shí)它就是一個(gè)中間順序的節(jié)點(diǎn)。

它的規(guī)則大致為:

枚舉當(dāng)前節(jié)點(diǎn)(不存儲輸出)并用棧存儲,節(jié)點(diǎn)指向左節(jié)點(diǎn),直到左孩子為null。

拋出棧頂訪問。如果有右節(jié)點(diǎn),訪問其右節(jié)點(diǎn)重復(fù)步驟1,如有沒右節(jié)點(diǎn),繼續(xù)重復(fù)步驟2拋出。

這樣的一個(gè)邏輯,就會(huì)形成一個(gè)中序序列,因?yàn)槿~子節(jié)點(diǎn)的左右都為null,這樣的規(guī)則依然滿足中序。

實(shí)現(xiàn)代碼為:

  1. class Solution { 
  2.    public List<Integer> inorderTraversal(TreeNode root) { 
  3.     List<Integer>value=new ArrayList<Integer>(); 
  4.     Stack<TreeNode> q1 = new Stack();     
  5.     while(!q1.isEmpty()||root!=null
  6.     { 
  7.         while (root!=null) { 
  8.             q1.push(root);                 
  9.             root=root.left
  10.         } 
  11.         root=q1.pop();//拋出 
  12.         value.add(root.val); 
  13.         root=root.right;//準(zhǔn)備訪問其右節(jié)點(diǎn) 
  14.  
  15.     } 
  16.     return value; 
  17.   } 

二叉樹的后序遍歷

二叉樹的后序遍歷非遞歸方式實(shí)現(xiàn)起來難度最大的,能夠手寫非遞歸后序,一定能亮瞎面試官的眼!

后序遍歷在二叉樹樹的順序可以看下圖(紅色箭頭指向的表示需要訪問的,可以看出如果子樹為null,那肯定要訪問,否則就是從右子樹回來的時(shí)候才訪問這個(gè)節(jié)點(diǎn))。

遞歸

二叉樹遞歸方式后序遍歷很簡單,跟前序中序的邏輯一樣,在力扣145有后序的code測試大家可以自己嘗試一下。

這里直接放我寫的后序遞歸方式:

  1. class Solution { 
  2.     List<Integer>value=new ArrayList<>(); 
  3.     public List<Integer> postorderTraversal(TreeNode root) { 
  4.         houxu(root); 
  5.         return value; 
  6.     } 
  7.     private void houxu(TreeNode root) { 
  8.         if(root==null
  9.             return
  10.         houxu(root.left); 
  11.         houxu(root.right);//右子樹回來 
  12.         value.add(root.val); 
  13.     } 

非遞歸

非遞歸的后序就稍微難一點(diǎn)了,大家可以回顧一下二叉樹的前序和中序遍歷,其實(shí)都是只用一個(gè)棧直接拋出就可以找到右節(jié)點(diǎn),拋出后棧就空了,但是這個(gè)后序遍歷的順序是 左子樹 ---> 右子樹 --->根節(jié)點(diǎn),也就是處理完右節(jié)點(diǎn),還要再回到根節(jié)點(diǎn)訪問。所以從邏輯結(jié)構(gòu)上和前序、中序的非遞歸實(shí)現(xiàn)方式有一些略有不同。

前序,中序遍歷的順序提取為:

前序: 中入棧—>左入棧—>左孩子入出—>左出棧—>中出棧—>右入棧—>右孩子入出—>右出棧

前序遍歷同一大流程按照入棧順序即形成一個(gè)前序序列

中序: 中入棧—>左入棧—>左孩子入出—>左出棧—>中出棧—>右入棧 —>右孩子入出—>右出棧

中序遍歷同一大流程按照出棧順序即形成一個(gè)中序序列

但是后序的話應(yīng)該怎么考慮呢?

其實(shí)就是在從左孩子到中準(zhǔn)備出棧的時(shí)候,先不出棧記成第一次,再將它放入棧中,如果從右孩子返回那么這個(gè)節(jié)點(diǎn)就是第三次遍歷(第一次訪問中,然后枚舉左返回第二次,枚舉右返回第三次),這時(shí)候?qū)⑵鋻伋鼍托纬梢粋€(gè)后序。

如果不理解這里畫了一個(gè)簡單的圖幫助理解:

思路理解了,怎么實(shí)現(xiàn)呢?最簡單的就是使用一個(gè)hashmap存儲節(jié)點(diǎn)訪問次數(shù)。

附一下個(gè)人實(shí)現(xiàn)的代碼:

  1. class Solution { 
  2.     public List<Integer> postorderTraversal(TreeNode root) { 
  3.         List<Integer> value=new ArrayList(); 
  4.         Stack<TreeNode> q1 = new Stack();     
  5.         Map<TreeNode,Integer >map=new HashMap<>(); 
  6.         while(!q1.isEmpty()||root!=null
  7.         { 
  8.             if (root!=null) { 
  9.                 q1.push(root); 
  10.                 map.put(root, 1); //t.value標(biāo)記這個(gè)值節(jié)點(diǎn)出現(xiàn)的次數(shù) 
  11.                 root=root.left
  12.             } 
  13.             else { 
  14.                 root=q1.peek(); 
  15.                 if(map.get(root)==2) {//第二次訪問,拋出 
  16.                     q1.pop(); 
  17.                     value.add(root.val); 
  18.                     root=null;//需要往上走 
  19.                 } 
  20.                 else { 
  21.                     map.put(root, 2); 
  22.                     root=root.right
  23.                 }      
  24.             } 
  25.           } 
  26.           return value; 
  27.     } 

但是這個(gè)情況如果面試官問你如果有hash沖突怎么辦?雖然這種概率非常小幾乎不會(huì)但是面試官不會(huì)放過你,但是還是要用正統(tǒng)方法來實(shí)現(xiàn)。

那么正統(tǒng)方法怎么解決呢?

也很容易,用一個(gè)pre節(jié)點(diǎn)一直保存上一次拋出訪問的點(diǎn),如果當(dāng)前被拋出的右孩子是pre或者當(dāng)前節(jié)點(diǎn)右為null,那么就將這個(gè)點(diǎn)拋出,否則就將它"回爐重造"一次!

實(shí)現(xiàn)代碼為:

  1. class Solution { 
  2.     public List<Integer> postorderTraversal(TreeNode root) { 
  3.         TreeNode temp=root;//枚舉的臨時(shí)節(jié)點(diǎn) 
  4.         List<Integer>value=new ArrayList<>(); 
  5.         TreeNode pre=null;//前置節(jié)點(diǎn) 
  6.         Stack<TreeNode>stack=new Stack<>(); 
  7.  
  8.         while (!stack.isEmpty()||temp!=null){ 
  9.  
  10.             while(temp!=null){ 
  11.                 stack.push(temp); 
  12.                 temp=temp.left
  13.             } 
  14.             temp=stack.pop(); 
  15.             if(temp.right==pre||temp.right==null)//需要彈出 
  16.             { 
  17.                 value.add(temp.val); 
  18.                 pre=temp
  19.                 temp=null;//需要重新從棧中拋出 
  20.             }else
  21.                 stack.push(temp); 
  22.                 temp=temp.right
  23.             } 
  24.  
  25.         } 
  26.         return value; 
  27.     } 

是不是覺得非常巧妙?那我再說一種騷操作的代碼,你看 左右中,它反過來就是中右左,這不就是一個(gè)反的前序遍歷嘛!所以進(jìn)行一次反的前序遍歷,然后將結(jié)果翻轉(zhuǎn)一下也能得到這個(gè)值啦,當(dāng)然,你使用雙棧同時(shí)翻轉(zhuǎn)也是一樣的道理!

實(shí)現(xiàn)代碼為:

  1. class Solution { 
  2.     public List<Integer> postorderTraversal(TreeNode root) { 
  3.       List<Integer>value=new ArrayList(); 
  4.       Stack<TreeNode> q1 = new Stack();     
  5.       while(!q1.isEmpty()||root!=null
  6.       { 
  7.         while (root!=null) { 
  8.           value.add(root.val); 
  9.           q1.push(root);                 
  10.           root=root.right
  11.         } 
  12.         root=q1.pop();//拋出 
  13.         root=root.left;//準(zhǔn)備訪問其右節(jié)點(diǎn) 
  14.       } 
  15.       Collections.reverse(value);//將結(jié)果翻轉(zhuǎn) 
  16.       return value; 
  17.  
  18.  
  19.     } 

給兩個(gè)序列如何構(gòu)造一棵二叉樹

經(jīng)常會(huì)遇到給兩個(gè)序列確定一個(gè)二叉樹,當(dāng)然這個(gè)序列其中之一必須包含中序遍歷序列。前序、中序確定二叉樹和后序、中序確定一個(gè)二叉樹的原理一致。

前序中序確定一棵二叉樹

根據(jù)一棵樹的前序遍歷與中序遍歷構(gòu)造二叉樹。當(dāng)然也是力扣105的原題。

注意:你可以假設(shè)樹中沒有重復(fù)的元素。

分析:

給定一個(gè)前序序列和一個(gè)中序序列,且里面沒有重復(fù)的元素,如何構(gòu)造一棵二叉樹呢?我們可以先單獨(dú)觀察兩個(gè)序列的特征:

前序遍歷:遍歷規(guī)則為(根,[左側(cè)區(qū)域],[右側(cè)區(qū)域])。

中序遍歷:遍歷規(guī)則為([左側(cè)區(qū)域],根,[右側(cè)區(qū)域])。

其中前序遍歷的左側(cè)區(qū)域和中序遍歷的左側(cè)區(qū)域包含元素的范圍相同,根也是相同的。所以可以進(jìn)行這樣的操作:

根據(jù)前序遍歷的第一個(gè)找到根節(jié)點(diǎn),可以確定根。

通過中序遍歷找到根節(jié)點(diǎn)的值,這樣可以知道左側(cè)區(qū)域和右側(cè)區(qū)域節(jié)點(diǎn)個(gè)數(shù)多少。

根節(jié)點(diǎn)左側(cè)區(qū)域由前中序列確定的左側(cè)區(qū)域確定,根節(jié)點(diǎn)的右側(cè)節(jié)點(diǎn)由前中序序列的右側(cè)區(qū)域確定。

一些操作可以借助這張圖進(jìn)行理解:

具體的實(shí)現(xiàn)上,可以使用一個(gè)HashMap存儲中序存儲的序列,避免重復(fù)計(jì)算。實(shí)現(xiàn)的代碼為:

  1. /** 
  2.  * Definition for a binary tree node. 
  3.  * public class TreeNode { 
  4.  *     int val; 
  5.  *     TreeNode left
  6.  *     TreeNode right
  7.  *     TreeNode(int x) { val = x; } 
  8.  * } 
  9.  */ 
  10. class Solution { 
  11.    public TreeNode buildTree(int[] preorder, int[] inorder) { 
  12.         if(preorder.length==0) 
  13.             return null
  14.         TreeNode root=new TreeNode(preorder[0]); 
  15.         Map<IntegerInteger>map=new HashMap<IntegerInteger>(); 
  16.         for(int i=0;i<inorder.length;i++) 
  17.         { 
  18.             map.put(inorder[i], i); 
  19.         } 
  20.         return buildTree(preorder,0,preorder.length-1, map,0,inorder.length-1); 
  21.     } 
  22.  
  23.     private TreeNode buildTree(int[] preorder, int preStart, int preEnd, Map<IntegerInteger> map, int inStart, int inEnd) { 
  24.         // TODO Auto-generated method stub 
  25.         if(preEnd<preStart||inEnd<inStart) 
  26.             return null
  27.         TreeNode node=new TreeNode(preorder[preStart]); 
  28.         int i=map.get(preorder[preStart]);//節(jié)點(diǎn)的值 
  29.  
  30.         int leftlen=i-inStart;//左面的長度 
  31.         node.left=buildTree(preorder, preStart+1, preStart+1+leftlen, map, inStart, i-1); 
  32.         node.right=buildTree(preorder, preStart+leftlen+1,preEnd, map, i+1, inEnd); 
  33.         return node;         
  34.     } 

中序后序確定一個(gè)二叉樹

根據(jù)一棵樹的中序遍歷與后序遍歷構(gòu)造二叉樹,力扣106題

注意:你可以假設(shè)樹中沒有重復(fù)的元素。

例如,給出

中序遍歷 inorder = [9,3,15,20,7]

后序遍歷 postorder = [9,15,7,20,3]

返回如下的二叉樹:

  1.   3 
  2.  / \ 
  3. 9  20 
  4.   /  \ 
  5.  15   7 

分析:

有了上面的分析,那么通過一個(gè)后序遍歷和中序遍歷去構(gòu)造一棵二叉樹,其實(shí)原理和前面的也是一樣的。

后序遍歷:遍歷規(guī)則為([左側(cè)區(qū)域],[右側(cè)區(qū)域],根)。

中序遍歷:遍歷規(guī)則為([左側(cè)區(qū)域],根,[右側(cè)區(qū)域])。

具體實(shí)現(xiàn)的代碼為:

  1. /** 
  2.  * Definition for a binary tree node. 
  3.  * public class TreeNode { 
  4.  *     int val; 
  5.  *     TreeNode left
  6.  *     TreeNode right
  7.  *     TreeNode(int x) { val = x; } 
  8.  * } 
  9.  */ 
  10. class Solution { 
  11.    public TreeNode buildTree(int[] inorder,int[] postorder) { 
  12.         if(postorder.length==0) 
  13.             return null
  14.         Map<IntegerInteger>map=new HashMap<IntegerInteger>(); 
  15.         for(int i=0;i<inorder.length;i++) 
  16.         { 
  17.             map.put(inorder[i], i); 
  18.         } 
  19.         return buildTree(postorder,0,postorder.length-1, map,0,inorder.length-1); 
  20.     } 
  21.  
  22.     private TreeNode buildTree(int[] postorder, int postStart, int postEnd, Map<IntegerInteger> map, int inStart, int inEnd) { 
  23.         // TODO Auto-generated method stub 
  24.         if(postEnd<postStart||inEnd<inStart) 
  25.             return null
  26.         TreeNode node=new TreeNode(postorder[postEnd]); 
  27.         int i=map.get(postorder[postEnd]); 
  28.         int leftlen=i-inStart; 
  29.         node.left=buildTree(postorder, postStart,postStart+leftlen-1, map, inStart, i-1); 
  30.         node.right=buildTree(postorder, postStart+leftlen,postEnd-1, map, i+1, inEnd);           
  31.     return node;         
  32.     } 

結(jié)語

好了,今天到這里就先介紹完了,但二叉樹的內(nèi)容遠(yuǎn)不止于此,還有非常牛批的Morris遍歷,以及一些二叉樹騷操作技巧??紗栴}后面還會(huì)慢慢總結(jié)分享給大家。

原創(chuàng)不易,如果本文對你有幫助,還請動(dòng)動(dòng)小手,幫忙點(diǎn)個(gè)贊和在看,分享給好友或者朋友圈,謝謝啦!

 

責(zé)任編輯:武曉燕 來源: bigsai
相關(guān)推薦

2020-04-27 07:05:58

二叉樹左子樹右子樹

2022-10-26 23:58:02

二叉樹數(shù)組算法

2023-05-08 15:57:16

二叉樹數(shù)據(jù)結(jié)構(gòu)

2021-04-20 08:37:14

數(shù)據(jù)結(jié)構(gòu)二叉樹

2021-09-15 07:56:32

二叉樹層次遍歷

2009-08-11 13:29:57

C#二叉樹遍歷

2021-01-13 10:03:36

二叉樹層序遍歷層次遍歷

2024-01-23 12:54:00

C++編程語言代碼

2021-04-19 07:47:42

數(shù)據(jù)結(jié)構(gòu)二叉樹Tree

2021-03-17 08:19:22

二叉樹LeetCode

2013-07-15 16:35:55

二叉樹迭代器

2021-09-29 10:19:00

算法平衡二叉樹

2020-09-23 18:25:40

算法二叉樹多叉樹

2021-07-13 11:32:41

二叉樹數(shù)據(jù)結(jié)構(gòu)算法

2021-04-28 20:12:27

數(shù)據(jù)結(jié)構(gòu)創(chuàng)建

2021-05-06 17:46:30

二叉樹數(shù)據(jù)結(jié)構(gòu)

2021-03-22 08:23:29

LeetCode二叉樹節(jié)點(diǎn)

2021-08-27 11:36:44

二叉樹回溯節(jié)點(diǎn)

2021-09-15 07:40:50

二叉樹數(shù)據(jù)結(jié)構(gòu)算法

2018-03-15 08:31:57

二叉樹存儲結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號