遞歸與循環

文章目錄

    • 遞歸
      • TestRecursiveListRemoveNode
      • TestRecursiveListRemoveNode2
    • 循環
      • TestWhileLoopListRemoveNode
      • TestWhileLoopListRemoveNode2

遞歸

關鍵理解這幾點:
1、求解基本問題
2、將原問題拆分為小問題,直至基本問題(難點)
3、借助設定的遞歸函數的語義,解決原問題
(在將原問題拆分成小問題時,可以借助設定的遞歸函數的語義,把設定的遞歸函數當作1個已實現的子函數,然后在遞歸函數中將原問題拆解成小問題,最終解決原問題)

TestRecursiveListRemoveNode

遞歸的技巧就是:你得先定義這個遞歸方法的目標功能,然后在遞歸的實現方法中可以使用這個遞歸方法,接著組織自己的邏輯,接著找出邊界條件,還需要在遞歸方法實現中調用定義的遞歸方法,來驅動下一步的執行!!!

  • 實現功能:使用 遞歸的方式 刪除單向鏈表中指定值的節點
  • 遞歸的方式將單向鏈表內容,使用字符串的形式輸出

/*** 刪除單向鏈表中指定值的節點*/
public class TestRecursiveListRemoveNode {/*** 單向鏈表節點*/static class ListNode {private Integer val;private ListNode next;public ListNode(Integer val) {this.val = val;}@Overridepublic String toString() {return "ListNode="+ String.valueOf(val);}}public static void main(String[] args) {ListNode listNode0 = new ListNode(6);ListNode listNode0_1 = new ListNode(6);ListNode listNode1 = new ListNode(1);ListNode listNode2 = new ListNode(2);ListNode listNode2_1 = new ListNode(6);ListNode listNode3 = new ListNode(3);ListNode listNode4 = new ListNode(4);ListNode listNode4_1 = new ListNode(6);ListNode listNode5 = new ListNode(5);ListNode listNode6 = new ListNode(6);ListNode listNode7 = new ListNode(6);ListNode listNode8 = new ListNode(7);List<ListNode> list = new ArrayList<ListNode>(){{add(listNode0);add(listNode0_1);add(listNode1);add(listNode2);add(listNode2_1);add(listNode3);add(listNode4);add(listNode4_1);add(listNode5);add(listNode6);add(listNode7);add(listNode8);}};// 組織鏈表結構// 這里不循環到list.size(),可以減少判斷for (int i = 0; i < list.size() - 1; i++) {list.get(i).next = list.get(i + 1);}// 2種輸出指定頭節點的鏈表的方法// 方法1:把結果值傳入,然后不斷遞歸(遞歸方法無返回值)// 方法2:把控遞歸方法的含義,在遞歸方法中處理邊界,并使用已定義的遞歸方法(遞歸方法有返回值)System.out.println(printAllList1(listNode0));System.out.println(printAllList2(listNode0));// 遞歸的缺陷:當元素越多的情況下,方法調用棧的深度就深,甚至棧內存溢出ListNode listNode = removeElement(listNode1, 6); // (遞歸方法有返回值)System.out.println(printAllList1(listNode));System.out.println(printAllList2(listNode));/*輸出:6->6->1->2->6->3->4->6->5->6->6->76->6->1->2->6->3->4->6->5->6->6->71->2->3->4->5->71->2->3->4->5->7*/}// 遞歸的技巧就是:你得先定義這個遞歸方法的目標功能,然后在遞歸的實現方法中可以使用這個遞歸方法,//               接著組織自己的邏輯,接著找出邊界條件,還需要在遞歸方法實現中調用定義的遞歸方法,來驅動下一步的執行!!!// 先定義當前這個方法的目標功能就是:傳入1個鏈表的頭節點,返回1個鏈表的頭節點,并且這個鏈表頭節點的后續節點中都不包含指定的值private static ListNode removeElement(ListNode head, int val) {// (遞歸的技巧就是:在實現遞歸的時候,遞歸方法的下面都是要假設傳入的head有可能是第一次傳入的,也有可能是后面的遞歸調用傳入的,要有這個意識!!!)// 如果head是null, 則直接返回,因為沒有處理的必要了if (head == null) {return null;}// 如果頭節點的值就是指定的值,那么去除掉這個頭節點,把頭節點的下一個節點作為新的頭節點,// 將新的頭節點傳入給定義的遞歸方法,根據定義的遞歸方法本來含義,其實只要把新的頭節點傳給這個遞歸方法就解決了if (head.val == val) {ListNode next = head.next;return removeElement(next, val);}// 至此,說明head頭節點不是指定的值// 拿到頭節點的下1個節點ListNode next = head.next;// 如果頭節點的下1個節點不是空節點if (next != null) {// 如果頭節點的下1個節點的值就是目標值,那就讓頭節點指向下1個節點的下1個節點,// 不過在這里,頭節點的下1個節點的下1個節點,仍然需要經過遞歸方法的處理,所以這里又調用了一遍if (next.val == val) {head.next = removeElement(next.next, val);next.next = null;} else {// 如果頭節點的下1個節點的值不是目標值,那就繼續調用遞歸方法正常處理頭節點的下1個節點的下1個節點next.next = removeElement(next.next, val);}}// 如果頭節點的下1個節點就是空節點,那就直接返回頭節點就行了// 如果頭節點的下1個節點不是空節點,那經過上面的處理后,這里也直接返回頭節點就行了return head;}private static String printAllList2(ListNode head) {// 如果頭節點是空的,那就返回空字符串if (head == null) {return "";}// 傳過來的頭節點必定不為空// 拿到頭節點的下1個節點ListNode next = head.next;// 如果頭節點的下1個節點為空,那就直接返回這個頭節點了if (next == null) {return String.valueOf(head.val);} else {// 如果頭節點的下1個節點不為空,那就需要拼接上后面節點對應的結果了,由于當前遞歸方法的定義就是輸出指定節點的鏈表,所以這里直接調用遞歸的方法了!return head.val + "->" + printAllList2(next);}}private static String printAllList1(ListNode listNode) {// 先構造結果StringBuilder sb = new StringBuilder();// 遞歸處理結果(此處,遞歸方法無返回值)printAllList1(listNode, sb, 0);// 遞歸處理完成后,返回結果return sb.toString();}// 該遞歸方法定義是:根據傳入的指定節點,將這個節點及這個節點之后的所有節點都處理到sb上,形成 ->{節點值} 的形式private static void printAllList1(ListNode listNode, StringBuilder sb, int count) {// (遞歸的技巧就是:在實現遞歸的時候,遞歸方法的下面都是要假設傳入的head有可能是第一次傳入的,也有可能是后面的遞歸調用傳入的,要有這個意識!!!)// 傳入的節點為空,則不需要處理if (listNode == null) {return;}// 當count為0時,是第一次進入,因此前面不需要->if (count != 0) {sb.append("->");}// 標識是否第一次進入遞歸方法count++;// 將值設置的sb上sb.append(listNode.val);// 如果listNode還有下1個節點, 則繼續處理下1個節點,// 由于我們已經定義的遞歸方法,正好就是將 這個節點及這個節點之后的所有節點都處理到sb上,形成 ->{節點值} 的形式,// 因此這里又調用了遞歸方法if (listNode.next != null) {printAllList1(listNode.next, sb, count);}}}

TestRecursiveListRemoveNode2

比上面更簡潔。

關鍵理解這幾點:
1、求解基本問題
2、將原問題拆分為小問題,直至基本問題(難點)
3、借助設定的遞歸函數的語義,解決原問題

public class TestRecursiveListRemoveNode2{/*** 單向鏈表節點*/static class ListNode {private Integer val;private ListNode next;public ListNode(Integer val) {this.val = val;}@Overridepublic String toString() {return "ListNode="+ String.valueOf(val);}}public static void main(String[] args) {ListNode listNode0 = getListNode();System.out.println(printAllList(listNode0));ListNode handledListNode = removeElements(listNode0, 6);System.out.println(printAllList(handledListNode));}public static ListNode removeElements(ListNode head, int val) {if (head != null) {if (head.val == val) {// 由于頭節點是給定的值,所以拋棄頭節點,此時,這里,借助 定義的遞歸函數的宏觀語義,只需要處理head.next即可返回return removeElements(head.next, val);} else {// 由于頭節點不是給定的值,所以對頭節點的下1個節點處理,此時,這里 借助定義的遞歸函數的宏觀語義,只需要處理head.next即可返回head.next = removeElements(head.next, val);return head;}}return null;/*// 與上面相同, 但這里更能體現出,在解決原問題的時候,是如何借助設定的遞歸方法,將原問題拆分成基礎問題的if (head == null) {return null;}ListNode res = removeElements(head.next, val);if (head.val == val) {return res;} else {head.next = res;return head;}*//*// 這整的有點高級了,意思和上面是相同的if (head == null) {return null;}head.next = removeElements(head.next, val);return head.val == val ? head.next : head;*/}private static String printAllList(ListNode head) {// 如果頭節點是空的,那就返回空字符串if (head == null) {return "";}// 傳過來的頭節點必定不為空// 拿到頭節點的下1個節點ListNode next = head.next;// 如果頭節點的下1個節點為空,那就直接返回這個頭節點了if (next == null) {return String.valueOf(head.val);} else {// 如果頭節點的下1個節點不為空,那就需要拼接上后面節點對應的結果了,由于當前遞歸方法的定義就是輸出指定節點的鏈表,所以這里直接調用遞歸的方法了!return head.val + "->" + printAllList(next);}}private static ListNode getListNode() {ListNode listNode0 = new ListNode(6);ListNode listNode0_1 = new ListNode(6);ListNode listNode1 = new ListNode(1);ListNode listNode2 = new ListNode(2);ListNode listNode2_1 = new ListNode(6);ListNode listNode3 = new ListNode(3);ListNode listNode4 = new ListNode(4);ListNode listNode4_1 = new ListNode(6);ListNode listNode5 = new ListNode(5);ListNode listNode6 = new ListNode(6);ListNode listNode7 = new ListNode(6);ListNode listNode8 = new ListNode(7);List<ListNode> list = new ArrayList<ListNode>(){{add(listNode0);add(listNode0_1);add(listNode1);add(listNode2);add(listNode2_1);add(listNode3);add(listNode4);add(listNode4_1);add(listNode5);add(listNode6);add(listNode7);add(listNode8);}};// 組織鏈表結構// 這里不循環到list.size(),可以減少判斷for (int i = 0; i < list.size() - 1; i++) {list.get(i).next = list.get(i + 1);}return listNode0;}}

循環

TestWhileLoopListRemoveNode

  • 實現功能:使用 循環的方式 刪除單向鏈表中指定值的節點
  • 循環的方式將單向鏈表內容,使用字符串的形式輸出
/*** 刪除單向鏈表中指定值的節點*/
public class TestWhileLoopListRemoveNode {/*** 單向鏈表節點*/static class ListNode {private Integer val;private ListNode next;public ListNode(Integer val) {this.val = val;}@Overridepublic String toString() {return "ListNode="+ String.valueOf(val);}}public static void main(String[] args) {ListNode listNode0 = new ListNode(6);ListNode listNode0_1 = new ListNode(6);ListNode listNode1 = new ListNode(1);ListNode listNode2 = new ListNode(2);ListNode listNode2_1 = new ListNode(6);ListNode listNode3 = new ListNode(3);ListNode listNode4 = new ListNode(4);ListNode listNode4_1 = new ListNode(6);ListNode listNode5 = new ListNode(5);ListNode listNode6 = new ListNode(6);ListNode listNode7 = new ListNode(6);ListNode listNode8 = new ListNode(7);List<ListNode> list = new ArrayList<ListNode>(){{add(listNode0);add(listNode0_1);add(listNode1);add(listNode2);add(listNode2_1);add(listNode3);add(listNode4);add(listNode4_1);add(listNode5);add(listNode6);add(listNode7);add(listNode8);}};// 組織鏈表結構// 這里不循環到list.size(),可以減少判斷for (int i = 0; i < list.size() - 1; i++) {list.get(i).next = list.get(i + 1);}// 使用循環(規避了遞歸可能引起的棧內存溢出的問題)System.out.println(printAllList(listNode0));// 使用循環(規避了遞歸可能引起的棧內存溢出的問題)ListNode node = removeElements(listNode0, 6);System.out.println(printAllList(node));}private static ListNode removeElements(ListNode head, int val) {// 去除可能存在的相連多個指定值的頭節點while (head != null && head.val == val) {ListNode next = head.next;head.next = null;head = next; // head向后推移1位,繼續循環}if (head == null) {return null;}// 將head值保存到prev,以開啟循環處理(循環處理的技巧)ListNode prev = head;while (prev.next != null) {// 拿到prev的下1個節點 nextListNode next = prev.next;if (next.val == val) { // 此處可借助循環移除多個連續相連的指定值的節點prev.next = next.next; // prev指向的下1個節點時,跳過指定值的節點,而指向下1個節點next.next = null;// 此處也繼續循環(并且注意此時prev值未變,繼續處理prev的下1個節點)} else {// 當prev的next不為指定值的節點時,prev向后推移1位,以繼續循環prev = prev.next;}}return head;}private static String printAllList(ListNode head) {if (head == null) {return "";}StringBuilder sb = new StringBuilder();sb.append(head.val);// 將head值保存到prev,以開啟循環處理(循環處理的技巧)ListNode prev = head;while (prev.next != null) {sb.append("->" + prev.next.val);prev = prev.next;}return sb.toString();}}

TestWhileLoopListRemoveNode2

  • 實現功能:使用 循環的方式 刪除單向鏈表中指定值的節點(添加虛擬頭節點,統一化操作(簡化代碼))
  • 循環的方式將單向鏈表內容,使用字符串的形式輸出
/*** 刪除單向鏈表中指定值的節點(添加虛擬頭節點)*/
public class TestWhileLoopListRemoveNode2 {/*** 單向鏈表節點*/static class ListNode {private Integer val;private ListNode next;public ListNode(Integer val) {this.val = val;}@Overridepublic String toString() {return "ListNode="+ String.valueOf(val);}}public static void main(String[] args) {ListNode listNode0 = new ListNode(6);ListNode listNode0_1 = new ListNode(6);ListNode listNode1 = new ListNode(1);ListNode listNode2 = new ListNode(2);ListNode listNode2_1 = new ListNode(6);ListNode listNode3 = new ListNode(3);ListNode listNode4 = new ListNode(4);ListNode listNode4_1 = new ListNode(6);ListNode listNode5 = new ListNode(5);ListNode listNode6 = new ListNode(6);ListNode listNode7 = new ListNode(6);ListNode listNode8 = new ListNode(7);List<ListNode> list = new ArrayList<ListNode>(){{add(listNode0);add(listNode0_1);add(listNode1);add(listNode2);add(listNode2_1);add(listNode3);add(listNode4);add(listNode4_1);add(listNode5);add(listNode6);add(listNode7);add(listNode8);}};// 組織鏈表結構// 這里不循環到list.size(),可以減少判斷for (int i = 0; i < list.size() - 1; i++) {list.get(i).next = list.get(i + 1);}// 使用循環(規避了遞歸可能引起的棧內存溢出的問題)System.out.println(printAllList(listNode0));// 使用循環(規避了遞歸可能引起的棧內存溢出的問題)// (添加虛擬頭節點技巧)ListNode node = removeElements(listNode0, 6);System.out.println(printAllList(node));}private static ListNode removeElements(ListNode head, int val) {// 添加1個虛擬頭節點,來去除對head為空的情況的判斷,從而簡化代碼ListNode dummyNode = new ListNode(null);dummyNode.next = head;// 將head值保存到prev,以開啟循環處理(循環處理的技巧)ListNode prev = dummyNode;while (prev.next != null) {// 拿到prev的下1個節點 nextListNode next = prev.next;if (next.val == val) { // 此處可借助循環移除多個連續相連的指定值的節點prev.next = next.next; // 跳過指定值的節點next.next = null;// 此處也繼續循環(并且注意此時prev值未變)} else {// 當prev的next不為指定值的節點時,prev向后推移1位,以繼續循環prev = prev.next;}}// 返回時,也是返回虛擬頭節點的下一個節點return dummyNode.next;}private static String printAllList(ListNode head) {if (head == null) {return "";}StringBuilder sb = new StringBuilder();sb.append(head.val);ListNode prev = head;while (prev.next != null) {sb.append("->" + prev.next.val);prev = prev.next;}return sb.toString();}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87774.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87774.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87774.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

3D魔方游戲

# 3D魔方游戲 這是一個基于Three.js的3D魔方游戲&#xff0c;支持2到6階魔方的模擬操作。 ## 功能特點 - 支持2到6階魔方 - 真實的3D渲染效果 - 鼠標操作控制 - 隨機打亂功能 - 提示功能 - 重置功能 ### 安裝依賴 bash npm install ### 啟動游戲 bash npm start 然…

下載安裝 com0com

下載 在 sourceforge 網站下載安裝器&#xff1a;下載鏈接 安裝完成后可以在設備管理器中看到默認創建的一對虛擬串口 使用串口調試助手收發 使用串口調試助手分別打開。如下圖所示&#xff0c;在端口選擇的下拉列表中可以看到剛才在設備管理器中看到的 COM3 和 COM5 分…

C++ 應用軟件開發從入門到實戰詳解

目錄 1、引言 2、IDE 開發環境介紹 2.1、Visual Studio 2.2、Qt Creator 3、 C語言特性 3.1、熟悉泛型編程 3.2、了解C/C異常處理 3.3、熟練使用STL容器 3.4、熟悉C11新特性 4、Windows 平臺的編程技術與調試技能 4.1、需要掌握的若干編程技術和基礎知識 4.2、需…

Python爬蟲實戰:研究slug相關技術

1. 引言 1.1 研究背景與意義 隨著互聯網技術的快速發展,網絡上的信息量呈爆炸式增長。如何從海量的非結構化數據中提取有價值的信息,成為當前數據科學領域的重要研究方向。網絡爬蟲作為一種自動化數據采集工具,可以高效地獲取網頁內容,為數據分析提供豐富的數據來源。 Sl…

人工智能-基礎篇-18-什么是RAG(檢索增強生成:知識庫+向量化技術+大語言模型LLM整合的技術框架)

RAG&#xff08;Retrieval-Augmented Generation&#xff0c;檢索增強生成&#xff09;是一種結合外部知識檢索與大語言模型&#xff08;LLM&#xff09;生成能力的技術框架&#xff0c;旨在提升生成式AI在問答、內容創作等任務中的準確性、實時性和領域適應性。 1、核心概念 …

CppCon 2018 學習:What do you mean “thread-safe“

什么是“線程安全”&#xff1f; “線程安全”指的是一個函數、方法或代碼塊能夠在多個線程同時執行時&#xff0c;不會出現意外的交互或破壞共享數據&#xff0c;能夠安全地運行。 POSIX 對線程安全的定義很清楚&#xff1a; “一個線程安全的函數可以在多個線程中被安全地并…

熱方程初邊值問題解法

已知公式&#xff1a; u ( x , t ) ∫ ? ∞ ∞ G ( x , y , t ) g ( y ) d y . u(x,t)\int_{-\infty}^{\infty}G(x,y,t)g(y)dy. u(x,t)∫?∞∞?G(x,y,t)g(y)dy. &#xff08;1&#xff09; 其中 G ( x , y , t ) 1 2 k π t e ? ( x ? y ) 2 4 k t G(x,y,t)\frac{1}{2…

怎樣理解:source ~/.bash_profile

場景復現 $ source ~/.bash_profileAnalysis 分析 一句話概括 source ~/.bash_profile “在 當前 終端會話里&#xff0c;立刻執行并加載 ~/.bash_profile 中的所有命令&#xff0c;讓其中定義的環境變量、函數、alias 等即時生效&#xff0c;而無需重新登錄或開新 Shell。…

搜索問答技術概述:基于知識圖譜與MRC的創新應用

目錄 一、問答系統應用分析 二、搜索問答技術與系統 &#xff08;一&#xff09;需求和信息分析 問答需求類型 多樣的數據源 文本組織形態 &#xff08;二&#xff09;主要問答技術介紹 發展和成熟度分析 重點問答技術基礎&#xff1a;KBQA和DeepQA KBQA&#xff08;…

TCP數據的發送和接收

本篇文章結合實驗對 TCP 數據傳輸中的重傳機制、滑動窗口以及擁塞控制做簡要的分析學習。 重傳 實驗環境 這里使用兩臺騰訊云服務器&#xff1a;vm-1&#xff08;172.19.0.3&#xff09;和vm-2&#xff08;172.19.0.6&#xff09;。 超時重傳 首先 vm-1 作為服務端啟動 nc…

python 保存二維數組到本地

Python中保存二維數組有多種方法&#xff0c;以下是常用的幾種方式&#xff1a;1. 使用NumPy&#xff08;推薦&#xff09;import numpy as np# 創建二維數組 arr np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])# 保存為.npy文件&#xff08;NumPy專用格式&#xff09; np.save…

LIN總線通訊中從節點波特率同步原理

波特率同步原理&#xff1a;從節點如何通過0x55校準時鐘&#xff1f; 一、同步場的核心作用&#xff1a;統一“時間標尺” 在LIN總線中&#xff0c;主節點與從節點各自擁有獨立的時鐘源&#xff08;如MCU內部RC振蕩器&#xff09;&#xff0c;但由于制造工藝差異&#xff0c;…

【Unity筆記02】訂閱事件-自動開門

流程 當玩家移動到觸發區域的時候&#xff0c;門自動打開 事件系統 using System; using System.Collections; using System.Collections.Generic; using UnityEngine;public class EventSystem : MonoBehaviour {public static EventSystem Instance { get; private set; }…

控制臺字符動畫

旋轉的立方體 #include <cstdint> #include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <cstdlib> #include <ctime> #include <thread> using namespace std;float angleX .0f; float a…

基于 PyTorch 的貓狗圖像分類實戰

基于 PyTorch 的貓狗圖像分類實戰 項目背景簡介 深度學習框架 PyTorch 因其動態計算圖和靈活易用性&#xff0c;被廣泛應用于圖像分類等計算機視覺任務。在入門計算機視覺領域時&#xff0c;常常以手寫數字識別&#xff08;MNIST&#xff09;作為 “Hello World”&#xff0c…

SwiftUI 7(iOS 26 / iPadOS 26)中玻璃化標簽頁的全新玩法

&#x1f378; Liquid Glass 登場&#xff1a;界面設計煥然一新 WWDC25 可謂驚喜連連&#xff0c;其中最引人矚目的變革之一&#xff0c;莫過于蘋果推出的全新跨平臺設計語言 —— Liquid Glass&#xff08;液態玻璃&#xff09;。這一設計風格涵蓋了從按鈕到導航欄&#xff0…

PDF處理控件Spire.PDF教程:在Java中讀取PDF,提取文本、圖片和表格

在數據驅動的現代開發中&#xff0c;高效處理 PDF 文檔已成為 Java 開發者不可或缺的核心能力。無論是處理各類發票掃描件、業務分析報告&#xff0c;還是包含豐富圖表的技術文檔&#xff0c;掌握 Java 版的 PDF 解析技術都將大幅提升數據處理效率&#xff0c;充分釋放文檔中的…

跨平臺游戲引擎 Axmol-2.7.0 發布

Axmol 2.7.0 版本是一個以錯誤修復和功能改進為主的次要LTS長期支持版本 &#x1f64f;感謝所有貢獻者及財務贊助者&#xff1a;scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox 重大變更 Android Studio 最低版本要求升級至 2025.1.1…

XML 筆記

<image src"hue.gif" width"100" height"auto" align"left"/> <br/> 換行 在 XML 中&#xff0c;<![CDATA[ 和 ]]> 用于定義一個 CDATA 節&#xff08;Character Data Section&#xff09;。CDATA 節是用于將一段…

Python實現優雅的目錄結構打印工具

Python實現優雅的目錄結構打印工具 在軟件開發、系統管理和日常工作中&#xff0c;我們經常需要查看和分析目錄結構。 工具功能概述 這個DirectoryPrinter類提供了以下功能&#xff1a; 遞歸打印目錄結構可配置是否顯示隱藏文件可設置最大遞歸深度自定義縮進和文件/文件夾符…