# LeetCode 1483. Kth Ancestor of a Tree Node
contributed by < ddj5523fa >
## 簡介:
1.[Leetcode測試連結](https://leetcode.com/problems/kth-ancestor-of-a-tree-node/)
2.此題解題方法在於 Binary lifting (倍增)
> 程式碼解題上,個人寫出來時,像是DP。
3.發現Leetcode上,測試問題:
> memory在free方面,即使有memory 沒被釋放,依然測試通過。
4.改寫了兩個程式碼檔,與原本測驗上程式碼進行比較。
:::danger
注意共筆書寫規範,中英文之間用一個半形空白區隔
:notes: jserv
:::
---
## 解題:
### 一.首先需先建立一個公式:
1.A(X,k)是求X的第k個祖先
2.A(X,0)第0個祖先,也就是自己,所以A(X,0)=X
3.沒有該祖先時候標-1
再來我們可以把A(X,k)拆分:
A(X,k)=A(A(X,t),k-t),1 <= t < k 。
=>以此func.已知得到了一個DP結構。
### 二.Binary lifting (倍增)
將上述整理出的DP結構給用上,只是每次不再是減1來去往過去尋找,而是以2的t次方。
=>所以此為為何建Tree時候,每個node都是紀錄2的次方的祖先。
------>往下看舉例
### 三.Tree中我們以Worst Case作為例子(也就是Skew_Tree):
表格:
Skew_Tree,以0為root
表格的j格子是紀錄:第2^j個祖先!
|i \\ j|0|1|2|3|4|
|-|-|-|-|-|-|-|
|0|-1|-1|-1|-1|-1|
|1|0|-1|-1|-1|-1|
|2|1|0|-1|-1|-1|
|3|2|1|-1|-1|-1|
|4|3|2|0|-1|-1|
|5|4|3|1|-1|-1|
|6|5|4|2|-1|-1|
|7|6|5|3|-1|-1|
|8|7|6|4|0|-1|
|9|8|7|5|1|-1|
|10|9|8|6|2|-1|
而舉例找 A(10,5) (5的binary=101)
(A(10,5是找10的第5個祖先))
A(10,5)=A(A(10,1),4) ---->[1]
=A(9,4)
=5
以binary來看:
(建立個表格)
|Node|K|Binary|描述|
|-|-|-|-|
|10|5|101|因為101的最小bit為1,所以取1,得上式子[1]|
|9|4|100|因為100最大bit為1,十進位是4,可以查表格直接得5! <得解!>
|5|0|0|
A(5,0)=5 =>在一的時候就整理過的公式。
---
[以上方法之參考網址](https://www.youtube.com/watch?v=Vvk4xjLfk84&feature=emb_logo&ab_channel=HuaHua)
---
## 程式碼:
原版本在測驗四第二題中的程式碼會建立出上方例子的表格。
可以發現最後一Col全-1。因此對程式碼進行改進:
### 法一
改進的地方為第16行與21行。
```cpp=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
int **parent;
int max_level;
int n;
} TreeAncestor;
TreeAncestor *treeAncestorCreate(int n, int *parent, int parentSize)
{
TreeAncestor *obj = malloc(sizeof(TreeAncestor));
obj->parent = malloc(n * sizeof(int *));
obj->n=n;
int max_level = 32 - __builtin_clz(n); //
for (int i = 0; i < n; i++)
{
obj->parent[i] = malloc(max_level * sizeof(int)); //
memset(obj->parent[i], -1, max_level * sizeof(int));
}
for (int i = 0; i < parentSize; i++)
obj->parent[i][0] = parent[i];
for (int i = 0; i < parentSize; i++)
{
for (int j = 1; j<max_level; j++)
{
if(obj->parent[i][j + (-1)] == -1)
break;
else
obj->parent[i][j] = obj->parent[obj->parent[i][j - 1]][j - 1];
}
}
obj->max_level = max_level; //
return obj;
}
void treeAncestorFree(TreeAncestor *obj)
{
for (int i = 0; i < obj->n; i++)
free(obj->parent[i]);
free(obj->parent);
free(obj);
}
int treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k)
{
int max_level = obj->max_level;
for (int i = 0; i < max_level && node != -1; ++i)
if (k & (1<<i))
node = obj->parent[node][i];
return node;
}
int main()
{
const int n=7;
//Creating a skew_tree :0->1->2->3->4->->5->6->7->8->9->10.
//Skew-tree is worst case:
int *parent;
parent = calloc(n, sizeof(int));
parent[0]=-1;
parent[1]=0;
parent[2]=0;
parent[3]=1;
parent[4]=1;
parent[5]=2;
parent[6]=2;
for(int i=0; i<n; i++)
{
//parent[i]=i-1;
printf("%d\n",parent[i]);
}
TreeAncestor *obj=treeAncestorCreate(n,parent,7);
int anc=treeAncestorGetKthAncestor(obj, 3, 1);
printf("A(x,k)=%d\n",anc);
anc=treeAncestorGetKthAncestor(obj, 5, 2);
printf("A(x,k)=%d\n",anc);
anc=treeAncestorGetKthAncestor(obj, 6, 3);
printf("A(x,k)=%d\n",anc);
treeAncestorFree(obj);
free(parent);
return 0;
}
```
==Bug提醒==
其中在```void treeAncestorFree ```func.裡面,一開始我的struct沒加入int n,因此誤寫成了obj->Maxlevel (如下:)
```cpp=
for (int i = 0; i < obj->Maxlevel; i++)
free(obj->parent[i]);
```
此段程式碼與上方完整程式碼的14行malloc地方比較可得知這裡memory並沒有成功全釋放掉!
但是LeetCode的Submit卻是會成功的
### 法二
法一只是省略了最後一Col的空間,但實際上,
建立的Tree可以只需要:
|i |Ancestor
|-|-|
|0|{}|
|1|{0}|
|2|{1,0}|
|3|{2,1}|
|4|{3,2,0}|
|5|{4,3,1}|
|6|{5,4,2}|
|7|{6,5,3}|
|8|{7,6,4,0}|
|9|{8,7,5,1}|
|10|{9,8,6,2}|
空間是呈現一個三角狀。
所以使用了 int max_level = 32 - __builtin_clz(i);
與for迴圈做出空間的省略。
但分析出來,時間有稍多了一點,具體看下個分類的(比較)
```cpp=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
int **ancestor ;
int max_level;
} TreeAncestor;
TreeAncestor *treeAncestorCreate(const int n,int *parent,int parentSize)
{
TreeAncestor *obj = malloc(sizeof(TreeAncestor));
obj->ancestor = malloc(n * sizeof(int *));
obj->ancestor[0] = malloc(1 * sizeof(int));
obj->ancestor[0][0] = -1;
for (int i = 1; i < n; i++)
{
int max_level = 32 - __builtin_clz(i);
//printf("i=%d, level=%d\n",i,max_level);
obj->ancestor[i] = malloc(max_level * sizeof(int));
memset(obj->ancestor[i], -1, max_level*sizeof(int));
}
for (int i = 0; i < parentSize; i++)
obj->ancestor[i][0] = parent[i];
/*
for(int i=0; i<n; i++)
{
int max_level = 32 - __builtin_clz(i);
for (int j = 0; j < max_level; j++)
{
printf("i=%d,j=%d, ancestor[i][j]=%d\n",i,j,obj->ancestor[i][j]);
}
}
*/
obj->max_level=32 - __builtin_clz(n-1);
for (int j = 1;; j++)
{
if(j>obj->max_level)
break;
for (int i = 1; i < parentSize; i++)
{
if(obj->ancestor[i][j + (-1)] == -1)
break;
if(j<(32 - __builtin_clz(i)))
{
obj->ancestor[i][j] = obj->ancestor[i][j -1] == -1
? -1
: obj->ancestor[obj->ancestor[i][j - 1]][j - 1];
}
else
continue;
}
}
/*
for(int i=0; i<n; i++)
{
int max_level = 32 - __builtin_clz(i);
for (int j = 0; j < max_level; j++)
{
printf("i=%d,j=%d, ancestor[i][j]=%d\n",i,j,obj->ancestor[i][j]);
}
}
*/
return obj;
}
int treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k)
{
int max_level = 32 - __builtin_clz(node);
for (int i = 0; i < max_level ; ++i)
if (k & (1<<i))
node = obj->ancestor[node][i];
return node;
}
void treeAncestorFree(TreeAncestor *obj)
{
for (int i = 0; i < obj->max_level; i++)
free(obj->ancestor[i]);
free(obj->ancestor);
free(obj);
}
int main()
{
const int n=10000;
//Creating a skew_tree :0->1->2->3->4->->5->6->7->8->9->10.
//Skew-tree is worst case:
int *parent;
parent = calloc(n, sizeof(int));
/*parent[0]=-1;
parent[1]=0;
parent[2]=0;
parent[3]=1;
parent[4]=1;
parent[5]=2;
parent[6]=2;
*/
for(int i=0; i<n; i++)
{
parent[i]=i-1;
//printf("%d\n",parent[i]);
}
TreeAncestor *obj=treeAncestorCreate(n,parent,10000);
/*
int anc=treeAncestorGetKthAncestor(obj, 3, 1);
printf("A(x,k)=%d\n",anc);
anc=treeAncestorGetKthAncestor(obj, 5, 2);
printf("A(x,k)=%d\n",anc);
anc=treeAncestorGetKthAncestor(obj, 6, 3);
printf("A(x,k)=%d\n",anc);
*/
for(int i=0;i<1000;i++)
{
int k=treeAncestorGetKthAncestor(obj, 1000, i);
printf("A(1000,%d)=%d\n",i,k);
}
treeAncestorFree(obj);
free(parent);
return 0;
}
```
==補充==法二程式碼有記憶體誤用的問題,目前正在Debug!
---
## 程式碼在此:
[GitHub](https://github.com/ddj5523fa/LeetCode_Kth)
---
## 比較:
用Valgrind確認記憶體狀況的比較:
1.Origin:
![](https://i.imgur.com/rgIG0My.png)
2.改過的第一版(法一):
![](https://i.imgur.com/nwQ60OE.png)
2.2法一中 treeAncestorFree Func.有誤但leetcode平台卻測試成功:
![](https://i.imgur.com/ksvSj00.png)
3.法二:有某部分的操作不當:
![](https://i.imgur.com/SQp2Rwv.png)