數組描述子節點的缺點
如果有這麼一顆奇葩的樹,大多數節點的孩子數爲1-2個,但是有一個節點的孩子數是100個。
因爲我們使用數組描述子節點,所以描述子節點的數組得定義爲struct TreeNode* children[100];
。
也就是說,除了有一個充分利用了數組分配的空間,其他的都造成了極大浪費,毫無疑問不合理。
使用鏈表描述
孩子也是一個一維的集合,特點個數不確定,符合這種特點的數據結構就是鏈式線性表了(簡稱鏈表)。
我們再來仔細觀察下樹的結構圖,來嘗試定義節點的數據結構:
- 首先一個節點得有一個數據區域,保存節點的數據信息,最簡單就是保存一個int數據。
- 每個節點有多個孩子,這個可以使用一個鏈表來描述,因爲鏈表指定了頭指針,就能把整個鏈表帶出來,所以可以定義節點爲。
typedef struct {
int data;//數據區域
TreeNode* node;//保存孩子中的頭結點
}TreeNode;
上面的節點數據結構乍一看沒啥問題,對於節點1來說,data區域保存1,node的data保存2,node的node指向3。
但是2下面的5和6呢,已經沒有地方存儲了。
我們再次仔細分析下節點,實際上每個節點除了要通過一個鏈表保存兄弟節點,還需要一個鏈表保存孩子節點。
於是結構應該爲:
typedef struct {
int data;//數據區域
TreeNode* brother;//保存兄弟節點 如果爲NULL表示無
TreeNode* son;//保存孩子節點 如果爲NULL表示無
}TreeNode;
也就是說,我們的存儲結構實際上是,同樣能表達含義一模一樣的樹。
代碼實現
嗯,這個代碼已經有一定理解難度了,我也是寫了大概幾十分鐘才搞定的,C語言真是魅力無窮吭,噴香!
/*
* 使用鏈表描述子節點的普通樹實現
* 作者:熊貓大大
* 時間:2019-10-16
*/
#include <stdio.h>
typedef struct {
int data;//數據區域
struct TreeNode* brother;//保存兄弟節點 如果爲NULL表示無
struct TreeNode* son;//保存孩子節點 如果爲NULL表示無
}TreeNode;
//爲樹的當前節點添加子節點
int addChild(TreeNode* curNode, int data)
{
//分配新節點
TreeNode* newNode= (TreeNode*)malloc(sizeof(TreeNode));
newNode->data = data;
newNode->brother = NULL;
newNode->son = NULL;
//找到當前節點的最後一個字節點
TreeNode* lastSonNode = curNode->son;//指向第一個子節點
if (lastSonNode == NULL) //第一個子節點即爲空
{
curNode->son = newNode;
return 1;
}
while (lastSonNode->brother != NULL) //找到最後一個節點
{
lastSonNode = lastSonNode->brother;
}
lastSonNode->brother = newNode;//將新節點掛在最後一個節點後面
return 1;
}
//遍歷當前節點與子節點
void printTree(TreeNode *node)
{
printf("父節點:%d",node->data);
//遍歷打印子節點
printf("----子節點:");
TreeNode *p = node->son;
if (p == NULL) //無子節點
{
printf("無\n");
return 1;
}
if (p != NULL)
{
printf("%d ",p->data);
}
while(p->brother!=NULL)
{
p = p->brother;
printf("%d ", p->data);
}
printf("\n");
//遍歷遞歸子節點
TreeNode *q = node->son;
if (q != NULL)
{
printTree(q);
}
while (q->brother != NULL)
{
q = q->brother;
printTree(q);
}
}
//----------------------------------------------------------------------------------------------------測試入口區域
int main()
{
//設定根節點
TreeNode root;
root.data = 1;//根節點數據區域
root.brother = NULL;//根節點無兄弟節點
root.son = NULL;//一開始也沒有子節點
//爲1節點添加2/3/4子節點
addChild(&root,2);
addChild(&root, 3);
addChild(&root, 4);
//爲2號節點添加5/6子節點
TreeNode *node2 = root.son;
addChild(node2, 5);
addChild(node2, 6);
//爲3號節點添加7子節點
TreeNode *node3 = node2->brother;
addChild(node3, 7);
//爲4號節點添加8子節點
TreeNode *node4 = node3->brother;
addChild(node4, 8);
printTree(&root);
return 1;
}
執行結果
毫無疑問,執行結果驗證了代碼的真實性,但是輸出順序稍微有點亂。
主要是因爲現在的輸出是順着一個節點,將其子節點輸出完,才輸出另外節點的邏輯,如果不好理解下個斷點走走就是了。