日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

easyui的tree获取父节点_力扣 1519——子数中标签相同的节点数

發(fā)布時(shí)間:2024/4/19 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 easyui的tree获取父节点_力扣 1519——子数中标签相同的节点数 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本題主要在于對(duì)樹(shù)這種數(shù)據(jù)結(jié)構(gòu)的考察,以及深度優(yōu)先遍歷的使用,優(yōu)化時(shí)可以采取空間換時(shí)間的策略。

原題

給你一棵樹(shù)(即,一個(gè)連通的無(wú)環(huán)無(wú)向圖),這棵樹(shù)由編號(hào)從 0 到 n - 1 的 n 個(gè)節(jié)點(diǎn)組成,且恰好有 n - 1 條 edges 。樹(shù)的根節(jié)點(diǎn)為節(jié)點(diǎn) 0 ,樹(shù)上的每一個(gè)節(jié)點(diǎn)都有一個(gè)標(biāo)簽,也就是字符串 labels 中的一個(gè)小寫(xiě)字符(編號(hào)為 i 的 節(jié)點(diǎn)的標(biāo)簽就是 labels[i] )

邊數(shù)組 edges 以 edges[i] = [ai, bi] 的形式給出,該格式表示節(jié)點(diǎn) ai 和 bi 之間存在一條邊。

返回一個(gè)大小為 n 的數(shù)組,其中 ans[i] 表示第 i 個(gè)節(jié)點(diǎn)的子樹(shù)中與節(jié)點(diǎn) i 標(biāo)簽相同的節(jié)點(diǎn)數(shù)。

樹(shù) T 中的子樹(shù)是由 T 中的某個(gè)節(jié)點(diǎn)及其所有后代節(jié)點(diǎn)組成的樹(shù)。

示例 1:

輸入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], labels = "abaedcd"輸出:[2,1,1,1,1,1,1]解釋:節(jié)點(diǎn) 0 的標(biāo)簽為 'a' ,以 'a' 為根節(jié)點(diǎn)的子樹(shù)中,節(jié)點(diǎn) 2 的標(biāo)簽也是 'a' ,因此答案為 2 。注意樹(shù)中的每個(gè)節(jié)點(diǎn)都是這棵子樹(shù)的一部分。節(jié)點(diǎn) 1 的標(biāo)簽為 'b' ,節(jié)點(diǎn) 1 的子樹(shù)包含節(jié)點(diǎn) 1、4 和 5,但是節(jié)點(diǎn) 4、5 的標(biāo)簽與節(jié)點(diǎn) 1 不同,故而答案為 1(即,該節(jié)點(diǎn)本身)。

示例 2:

輸入:n = 4, edges = [[0,1],[1,2],[0,3]], labels = "bbbb"輸出:[4,2,1,1]解釋:節(jié)點(diǎn) 2 的子樹(shù)中只有節(jié)點(diǎn) 2 ,所以答案為 1 。節(jié)點(diǎn) 3 的子樹(shù)中只有節(jié)點(diǎn) 3 ,所以答案為 1 。節(jié)點(diǎn) 1 的子樹(shù)中包含節(jié)點(diǎn) 1 和 2 ,標(biāo)簽都是 'b' ,因此答案為 2 。節(jié)點(diǎn) 0 的子樹(shù)中包含節(jié)點(diǎn) 0、1、2 和 3,標(biāo)簽都是 'b',因此答案為 4 。

示例 3 :

輸入:n = 5, edges = [[0,1],[0,2],[1,3],[0,4]], labels = "aabab"輸出:[3,2,1,1,1]

示例 4:

輸入:n = 6, edges = [[0,1],[0,2],[1,3],[3,4],[4,5]], labels = "cbabaa"輸出:[1,2,1,1,2,1]

示例 5:

輸入:n = 7, edges = [[0,1],[1,2],[2,3],[3,4],[4,5],[5,6]], labels = "aaabaaa"輸出:[6,5,4,1,3,2,1]

提示:

  • 1 <= n <= 10^5
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • labels.length == n
  • labels 僅由小寫(xiě)英文字母組成

原題 url:https://leetcode-cn.com/problems/number-of-nodes-in-the-sub-tree-with-the-same-label

解題

首次嘗試

這道題是要讓我們計(jì)算:在子樹(shù)中,和當(dāng)前節(jié)點(diǎn)字符相同的節(jié)點(diǎn)個(gè)數(shù)。

那么我們就必然需要構(gòu)建樹(shù)中各個(gè)節(jié)點(diǎn)的關(guān)系,那么就需要記錄父子節(jié)點(diǎn)的關(guān)系,因?yàn)槭瞧胀ǖ臉?shù),一個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)可能有多個(gè),因此我用LinkedList[] tree這樣一個(gè)數(shù)組進(jìn)行存儲(chǔ),其中tree[i]代表節(jié)點(diǎn) i 的所有子節(jié)點(diǎn)。

至于求相同節(jié)點(diǎn)的個(gè)數(shù),我想著可以從根節(jié)點(diǎn) 0 開(kāi)始逐個(gè)遍歷,先獲取其第一層子節(jié)點(diǎn),再根據(jù)第一層子節(jié)點(diǎn)逐個(gè)獲取,可以采用廣度優(yōu)先遍歷的形式。

讓我們看看代碼:

class Solution { public int[] countSubTrees(int n, int[][] edges, String labels) { // 構(gòu)造樹(shù) LinkedList[] tree = new LinkedList[n]; for (int[] edge : edges) { // edge[0]的子節(jié)點(diǎn) LinkedList child = tree[edge[0]]; if (child == null) { child = new LinkedList<>(); tree[edge[0]] = child; } // 增加子節(jié)點(diǎn) child.add(edge[1]); } // 結(jié)果 int[] result = new int[n]; // 遍歷并計(jì)算 for (int i = 0; i < n; i++) { // 需要遍歷的字符 char cur = labels.charAt(i); // 該節(jié)點(diǎn)的子樹(shù)中與該字符相同的節(jié)點(diǎn)數(shù) int curCount = 0; // 廣度優(yōu)先遍歷 LinkedList searchList = new LinkedList<>(); searchList.add(i); while(!searchList.isEmpty()) { int index = searchList.removeFirst(); if (cur == labels.charAt(index)) { curCount++; } // 找出該節(jié)點(diǎn)的子樹(shù) if (tree[index] == null) { continue; } searchList.addAll(tree[index]); } result[i] = curCount; } return result; }}

提交之后,發(fā)現(xiàn)有錯(cuò)誤。錯(cuò)誤的情況是:

輸入:4[[0,2],[0,3],[1,2]]"aeed"輸出:[1,2,1,1]預(yù)期:[1,1,2,1]

根據(jù)這樣輸入,我構(gòu)造出的樹(shù)是:

1 0 / 2 3

但根據(jù)預(yù)期結(jié)果反推出來(lái)的樹(shù)是:

0 / 2 3 /1

那么輸入中最后給出的[1,2]就不是從父節(jié)點(diǎn)指向子節(jié)點(diǎn),也就是輸入中給出的邊關(guān)聯(lián)的節(jié)點(diǎn)順序,是任意的。

那我們的樹(shù)究竟該如何構(gòu)造呢?

雙向記錄構(gòu)造樹(shù)

既然我們?cè)跇?gòu)造樹(shù)的時(shí)候,無(wú)法直接得出父子關(guān)系,那么就將對(duì)應(yīng)兩個(gè)節(jié)點(diǎn)同時(shí)記錄另一個(gè)節(jié)點(diǎn)。

根據(jù)題目中給出的條件:樹(shù)的根節(jié)點(diǎn)為節(jié)點(diǎn) 0。這樣我們?cè)诒闅v的時(shí)候,就從 0 開(kāi)始,只要 0 關(guān)聯(lián)的節(jié)點(diǎn),一定是 0 的子節(jié)點(diǎn)。將這些節(jié)點(diǎn)進(jìn)行標(biāo)記,這樣再遞歸訪問(wèn)接下來(lái)的節(jié)點(diǎn)時(shí),如果是標(biāo)記過(guò)的,則說(shuō)明是父節(jié)點(diǎn),這樣就可以明確父子節(jié)點(diǎn)關(guān)系了。

至于遍歷的時(shí)候,因?yàn)檫@次我們是不知道父子節(jié)點(diǎn)關(guān)系的,所以無(wú)法直接采用廣度優(yōu)先遍歷,換成深度優(yōu)先遍歷。

讓我們看看代碼:

class Solution { // 總節(jié)點(diǎn)數(shù) int n; // 樹(shù) Map> tree; // 字符串 String labels; // 最終結(jié)果 int[] result; public int[] countSubTrees(int n, int[][] edges, String labels) { this.n = n; this.labels = labels; result = new int[n]; LinkedList list; // 雙向構(gòu)造樹(shù)的關(guān)系 tree = new HashMap<>(n / 4 * 3 + 1); for (int[] edge : edges) { // 添加映射關(guān)系 list = tree.computeIfAbsent(edge[0], k -> new LinkedList<>()); list.add(edge[1]); list = tree.computeIfAbsent(edge[1], k -> new LinkedList<>()); list.add(edge[0]); } // 深度優(yōu)先搜索 dfs(0); return result; } public int[] dfs(int index) { // 當(dāng)前子樹(shù)中,所有字符的個(gè)數(shù) int[] charArray = new int[26]; // 開(kāi)始計(jì)算,標(biāo)志該節(jié)點(diǎn)已經(jīng)計(jì)算過(guò) result[index] = 1; // 獲得其關(guān)聯(lián)的節(jié)點(diǎn) List nodes = tree.get(index); // 遍歷 for (int node : nodes) { // 如果該節(jié)點(diǎn)已經(jīng)訪問(wèn)過(guò) if (result[node] > 0) { continue; } // 遞歸遍歷子節(jié)點(diǎn) int[] array = dfs(node); for (int i = 0; i < 26; i++) { charArray[i] += array[i]; } } // 將當(dāng)前節(jié)點(diǎn)的值計(jì)算一下 charArray[labels.charAt(index) - 'a'] += 1; result[index] = charArray[labels.charAt(index) - 'a']; return charArray; }}

提交OK,執(zhí)行用時(shí)136ms,超過(guò)36.71%,內(nèi)存消耗104.5MB,超過(guò)91.38%。

時(shí)間復(fù)雜度上,應(yīng)該是要研究dfs方法中的兩個(gè)for循環(huán),外層肯定是每個(gè)節(jié)點(diǎn)都遍歷一遍,內(nèi)層還需要遍歷26個(gè)英文字母,也就是O(n)。

空間復(fù)雜度上,最大的應(yīng)該就是存儲(chǔ)節(jié)點(diǎn)映射關(guān)系的tree了,里面實(shí)際上就是 2n 個(gè)節(jié)點(diǎn)(因?yàn)槊織l邊對(duì)應(yīng)的兩個(gè)節(jié)點(diǎn)都會(huì)互相存一次對(duì)方),因此也就是O(n)。

雖然過(guò)了,但執(zhí)行速度很慢,可以進(jìn)一步優(yōu)化。

用空間換時(shí)間

針對(duì)我上面的解法,其中tree我是用的Map,雖然其get方法理論上是O(n),但畢竟涉及 hash,可以優(yōu)化成數(shù)組。

至于每次取節(jié)點(diǎn)對(duì)應(yīng)的字符所用的charAt方法,具體其實(shí)是:

public char charAt(int index) { if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } return value[index]; }

每次都會(huì)檢查一次 index,其實(shí)這完全是可以省略的,因此可以提前構(gòu)造好每個(gè)位置對(duì)應(yīng)的值,也用一個(gè)數(shù)組存儲(chǔ)。

讓我們看看新的代碼:

class Solution { // 總節(jié)點(diǎn)數(shù) int n; // 樹(shù) LinkedList[] tree; // 每個(gè)節(jié)點(diǎn)的值(用數(shù)字表示) int[] nodeValueArray; // 最終結(jié)果 int[] result; public int[] countSubTrees(int n, int[][] edges, String labels) { this.n = n; nodeValueArray = new int[n]; result = new int[n]; // 雙向構(gòu)造樹(shù)的關(guān)系 tree = new LinkedList[n]; for (int i = 0; i < n; i++) { tree[i] = new LinkedList<>(); } for (int[] edge : edges) { // 添加映射關(guān)系 tree[edge[0]].add(edge[1]); tree[edge[1]].add(edge[0]); } // 生成節(jié)點(diǎn)的值 for (int i = 0; i < n; i++) { nodeValueArray[i] = labels.charAt(i) - 'a'; } // 深度優(yōu)先搜索 dfs(0); return result; } public int[] dfs(int index) { // 當(dāng)前子樹(shù)中,所有字符的個(gè)數(shù) int[] charArray = new int[26]; // 開(kāi)始計(jì)算,標(biāo)志該節(jié)點(diǎn)已經(jīng)計(jì)算過(guò) result[index] = 1; // 獲得其關(guān)聯(lián)的節(jié)點(diǎn) List nodes = tree[index]; // 遍歷 for (int node : nodes) { // 如果該節(jié)點(diǎn)已經(jīng)訪問(wèn)過(guò) if (result[node] > 0) { continue; } // 遞歸遍歷子節(jié)點(diǎn) int[] array = dfs(node); for (int i = 0; i < 26; i++) { charArray[i] += array[i]; } } // 將當(dāng)前節(jié)點(diǎn)的值計(jì)算一下 charArray[nodeValueArray[index]] += 1; result[index] = charArray[nodeValueArray[index]]; return charArray; }}

提交之后,執(zhí)行用時(shí)是96ms,內(nèi)存消耗是402.2MB。看來(lái)優(yōu)化的效果并不明顯。

研究一下目前最優(yōu)解法

這個(gè)解法真的是巧妙,執(zhí)行用時(shí)20ms,超過(guò)了100%,內(nèi)存消耗76.3MB,超過(guò)了100%。

我在代碼中增加了注釋,方便大家理解。但這樣的寫(xiě)法,研究一下是能夠看懂,但讓我想估計(jì)是永遠(yuǎn)不可能想出來(lái),可以讓大家也一起學(xué)習(xí)和借鑒:

public class Solution { static class Next { Next next; Node node; Next(Next next, Node node) { this.next = next; this.node = node; } } static class Node { /** * 當(dāng)前節(jié)點(diǎn)的index */ final int index; /** * 當(dāng)前節(jié)點(diǎn)對(duì)應(yīng)的字符值(減去'a') */ final int ci; /** * 所有關(guān)聯(lián)的節(jié)點(diǎn) */ Next children; /** * 該節(jié)點(diǎn)的父節(jié)點(diǎn) */ Node parent; /** * 子樹(shù)中和該節(jié)點(diǎn)含有相同字符的節(jié)點(diǎn)總個(gè)數(shù) */ int result; /** * 是否還在隊(duì)列中,可以理解為是否已訪問(wèn)過(guò) */ boolean inQueue; public Node(int index, int ci) { this.index = index; this.ci = ci; this.result = 1; } /** * 從后往前,找到當(dāng)前節(jié)點(diǎn)沒(méi)有訪問(wèn)過(guò)的第一個(gè)子節(jié)點(diǎn) */ Node popChild() { for (; ; ) { // 當(dāng)前節(jié)點(diǎn)的所有關(guān)聯(lián)節(jié)點(diǎn) Next n = this.children; // 如果沒(méi)有,說(shuō)明子節(jié)點(diǎn)都遍歷完了 if (n == null) { return null; } // 從后往前移除關(guān)聯(lián)節(jié)點(diǎn) this.children = n.next; // 返回第一個(gè)沒(méi)有訪問(wèn)過(guò)的節(jié)點(diǎn) if (!n.node.inQueue) { return n.node; } } } /** * 訪問(wèn)了該節(jié)點(diǎn) */ Node enqueue(Node[] cnodes) { // 該節(jié)點(diǎn)標(biāo)記為訪問(wèn)過(guò) this.inQueue = true; // 記錄該節(jié)點(diǎn)的父節(jié)點(diǎn) this.parent = cnodes[ci]; // 那么現(xiàn)在該字符值對(duì)應(yīng)的最高節(jié)點(diǎn),就是當(dāng)前節(jié)點(diǎn)。 // 這樣如果之后也遇到相同字符的子節(jié)點(diǎn),就可以為子節(jié)點(diǎn)賦值其父節(jié)點(diǎn),也就是上面一行是有效的 cnodes[ci] = this; return this; } /** * 退出該節(jié)點(diǎn) */ void dequeue(Node[] cnodes, int[] res) { // 之后會(huì)訪問(wèn)該節(jié)點(diǎn)的兄弟節(jié)點(diǎn),因此父節(jié)點(diǎn)需要重新設(shè)置 cnodes[ci] = this.parent; // 設(shè)置當(dāng)前節(jié)點(diǎn)的值 res[index] = this.result; // 父節(jié)點(diǎn)也可以進(jìn)行累加 if (this.parent != null) { this.parent.result += this.result; } } void link(Node x) { // this節(jié)點(diǎn)和x節(jié)點(diǎn),互相綁定 this.children = new Next(this.children, x); x.children = new Next(x.children, this); } } public int[] countSubTrees(int n, int[][] edges, String labels) { // 構(gòu)造樹(shù) Node[] nodes = new Node[n]; // 每個(gè)節(jié)點(diǎn)對(duì)應(yīng)的字符 for (int i = 0; i < n; i++) { nodes[i] = new Node(i, labels.charAt(i) - 'a'); } // 通過(guò)邊的關(guān)系,將節(jié)點(diǎn)互相綁定 for (int[] es : edges) { nodes[es[0]].link(nodes[es[1]]); } // 最終的結(jié)果 int[] res = new int[n]; // 當(dāng)前訪問(wèn)的節(jié)點(diǎn)下標(biāo) int sz = 0; // 26個(gè)小寫(xiě)英文字母對(duì)應(yīng)的節(jié)點(diǎn)數(shù)組 Node[] cnodes = new Node[26]; // 下面三行可以合并成這一行: // Node node = nodes[sz++] = nodes[0].enqueue(cnodes); nodes[sz] = nodes[0].enqueue(cnodes); // 當(dāng)前訪問(wèn)的節(jié)點(diǎn) Node node = nodes[sz]; // 因?yàn)楫?dāng)前節(jié)點(diǎn)已經(jīng)訪問(wèn)過(guò),自然下標(biāo)需要+1 sz++; for (; ; ) { // 從后往前,找到當(dāng)前節(jié)點(diǎn)沒(méi)有訪問(wèn)過(guò)的第一個(gè)子節(jié)點(diǎn) Node child = node.popChild(); // 如果已經(jīng)全部訪問(wèn)過(guò)了 if (child == null) { // 開(kāi)始計(jì)算 node.dequeue(cnodes, res); if (--sz == 0) { break; } // 回溯到父節(jié)點(diǎn) node = nodes[sz - 1]; } else { // 保證了相鄰節(jié)點(diǎn)一定是父子節(jié)點(diǎn) node = nodes[sz++] = child.enqueue(cnodes); } } return res; }}

總結(jié)

以上就是這道題目我的解答過(guò)程了,不知道大家是否理解了。本題主要在于對(duì)樹(shù)這種數(shù)據(jù)結(jié)構(gòu)的考察,以及深度優(yōu)先遍歷的使用,優(yōu)化時(shí)可以采取空間換時(shí)間的策略。

有興趣的話可以訪問(wèn)我的博客或者關(guān)注我的公眾號(hào)、頭條號(hào),說(shuō)不定會(huì)有意外的驚喜。

https://death00.github.io/

公眾號(hào):健程之道

總結(jié)

以上是生活随笔為你收集整理的easyui的tree获取父节点_力扣 1519——子数中标签相同的节点数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久操这里只有精品 | 香蕉视频首页 | 肉大榛一进一出免费视频 | 日本丰满大乳奶做爰 | 人成网站在线观看 | 亚洲男人的天堂在线视频 | 成人网站免费观看入口 | 国产精品久久久久久亚洲影视 | 成人短视频在线免费观看 | 99国产超薄肉色丝袜交足 | 午夜爱爱免费视频 | 亚洲AV无码久久精品国产一区 | 哺乳期喷奶水丰满少妇 | 欧美一区二区福利视频 | 美国三级视频 | 第四色在线视频 | 一边吃奶一边摸做爽视频 | 91高清在线免费观看 | 爱爱网视频 | 任你操精品视频 | 中文字幕被公侵犯的漂亮人妻 | 中文在线视频观看 | 久久精品国产精品亚洲毛片 | 国产一级特黄aaa大片 | 国产三级av在线 | 国产视频黄 | 久久夜色精品国产欧美乱极品 | 俄罗斯美女av | 国产成年人 | 亚洲欧洲中文 | 不卡一区在线 | 日本一本久久 | xxxxxx国产 | 制服.丝袜.亚洲.另类.中文 | 少妇丰满尤物大尺度写真 | 九九九九热 | 日少妇av | 国产精品成人无码 | 亚洲砖区区免费 | 免费观看成年人视频 | 经典杯子蛋糕日剧在线观看免费 | 亚洲成人伦理 | 久久久香蕉视频 | 99精品人妻国产毛片 | 亚洲美女精品 | 天天干天天透 | 亚洲精品欧美激情 | 97精品一区 | 国产福利专区 | 色呦呦呦呦 | 欧美xxxx8888 | 国产一卡二卡三卡四卡 | 精品国产乱码久久久久 | 涩天堂| 日韩欧美高清在线 | 亚洲调教 | 清纯唯美亚洲 | 亚洲视频黄 | 国产成人无码精品久久久性色 | 精品三级在线观看 | 成人91看片 | 国产精品一区二三区 | 久久99精品久久久久久噜噜 | 中文字幕成人网 | 日本天天操 | 91射射| 亚州国产精品 | 国产寡妇亲子伦一区二区三区四区 | 性激情视频 | 夜夜操夜夜骑 | 久久精品国产99精品国产亚洲性色 | 后进极品美女圆润翘臀 | www.av在线免费观看 | 人体裸体bbb欣赏 | 亚洲AV无码片久久精品 | 日产精品久久久 | 神马九九| 亚洲AV无码一区二区伊人久久 | 日本中文字幕在线播放 | 爱色av网站 | 国产av一区二区三区精品 | 国产日日干 | 成人网在线免费观看 | 国产精品视频99 | 日韩av高清| 秋霞99 | 男生桶女生肌肌 | 99精品视频国产 | 国产一区二区四区 | av成人亚洲| 日本999视频 | 大尺度av在线 | 毛片在线免费视频 | 久久久久久国产精品无码 | 四虎永久免费地址 | 久久中文字幕av | 欧美性受xxxx黒人xyx性爽 | 亚洲欧洲天堂 | 欧美,日韩,国产在线 |