数组指针和它的兄弟们

数组指针,enmmmmmm, 直接上图哈哈哈哈

 

1. 数组指针

2.指针数组

3.函数指针

4.函数指针函数 和 指向函数指针函数 的指针

 

说啥都没有列子重要:

定义函数指针

void fun()
{
    printf("hello\n");
}

int main()
{
    void(*pfun)() = &fun;
    (*pfun)();
    system("pause");
    return 0;
}

定义一个函数指针数组

void fun1()
{
    printf("hello fun1\n");
}
void fun2()
{
    printf("hello fun2\n");
}
void fun3()
{
    printf("hello fun3\n");
}
void fun4()
{
    printf("hello fun4\n");
}
int main()
{
    void(*pfun1)()=&fun1;
    void(*pfun2)()=&fun2;
    void(*pfun3)()=&fun3;
    void(*pfun4)()=&fun4;
    void(*pfunarr[4])();
    pfunarr[0]=&pfun1;
    pfunarr[1]=&pfun2;
    pfunarr[2]=&pfun3;
    pfunarr[3]=&pfun4;
    pfunarr[0]();
    pfunarr[1]();
    pfunarr[2]();
    pfunarr[3]();
    system("pause");
    return 0;
}

定义一个指向函数指针数组的指针

void(*(*ppfunarr)[4])();

 

如果有不对的地方,可以评论告诉我,望指导!

<think>我们正在处理一个二叉树表示法转换的问题。 双亲表示法:每个节点存储其父节点的索引(或指针),通常还存储节点数据。根节点的父节点索引为-1(或NULL)。 孩子兄弟表示法:每个节点包含三个域:数据(data)、第一个孩子节点(firstChild)下一个兄弟节点(rightSib)。 转换思路: 1. 在双亲表示法中,通常我们有一个节点数组,每个节点包含数据父节点的索引(或位置)。 2. 我们需要构建孩子兄弟表示法的二叉树。在这种表示法中,每个节点可能有多个孩子,但通过第一个孩子兄弟指针组织成树。 步骤: a. 首先,我们根据双亲表示法构建节点数组(孩子兄弟表示法的节点),每个节点包含数据,并初始化其孩子兄弟指针为NULL。 b. 遍历双亲表示法数组,对于每个节点(除了根节点),找到其父节点。 - 如果当前节点是其父节点的第一个孩子(即父节点还没有第一个孩子),则将父节点的firstChild指向当前节点。 - 否则,将当前节点添加到父节点的孩子链表的末尾(也就是兄弟链表的末尾)。注意:孩子兄弟表示法中,同一个父节点的所有孩子组成一个链表,第一个孩子通过父节点的firstChild指向,然后第一个孩子的兄弟指向第二个孩子,以此类推。 但是,在双亲表示法中,我们没有记录节点的孩子顺序。因此,我们需要额外的信息来确定同一个父节点的多个孩子之间的顺序。 问题:在双亲表示法的节点中,通常不存储孩子节点的顺序。因此,转换时我们需要约定顺序(例如,按照节点在数组中的顺序,或者按照某个关键字排序)。如果没有指定顺序,我们可以按照数组中的顺序来连接兄弟节点。 假设我们按照双亲表示法数组中的顺序来构建孩子兄弟表示法: 我们可以这样做: 1. 创建一个指针数组,每个元素是一个指向孩子兄弟节点的指针。 2. 初始化每个节点,并分配内存(如果需要动态分配)。 3. 再次遍历数组(排除根节点),对于每个节点i(索引为i),找到其父节点索引p。 - 将节点i链接到父节点p的孩子链表的末尾(即作为最后一个兄弟)。 注意:根节点没有父节点,所以单独处理。 为了高效地将节点添加到孩子链表的末尾,我们可以为每个父节点维护一个指向其孩子链表中最后一个兄弟节点的指针(尾指针)。这样,我们不需要每次从头遍历。 算法步骤: 输入:双亲表示法的节点数组`parent_rep`,其中每个元素是一个结构体,包含`data``parent_index`(根节点的parent_index为-1)。 输出:孩子兄弟表示法的根节点。 步骤: 1. 创建两个数组(长度与节点数相同): - `nodes`: 存储已创建的孩子兄弟表示法节点的指针(或引用)。 - `last_child`: 存储每个节点(作为父节点时)的最后一个孩子节点的指针(初始为NULL)。 2. 初始化: for i in range(0, n): nodes[i] = new TreeNode(data: parent_rep[i].data, firstChild: NULL, rightSib: NULL) last_child[i] = NULL // 表示该节点还没有孩子 3. 设置根节点:遍历找到父节点索引为-1的节点,即为根节点。同时,我们需要记录根节点的索引。 4. 再次遍历所有节点(除了根节点): for i in range(0, n): if parent_rep[i].parent_index == -1: // 跳过根节点 continue p_index = parent_rep[i].parent_index // 当前节点i的父节点索引 // 将当前节点nodes[i]链接到父节点nodes[p_index]的孩子链表的末尾 if last_child[p_index] == NULL: // 说明父节点还没有任何孩子,那么当前节点应该是父节点的第一个孩子 nodes[p_index]->firstChild = nodes[i] last_child[p_index] = nodes[i] // 更新父节点的最后一个孩子指针为当前节点 else: // 父节点已经有孩子了,将当前节点链接到最后一个孩子的rightSib last_child[p_index]->rightSib = nodes[i] last_child[p_index] = nodes[i] // 更新最后一个孩子指针 5. 返回根节点(nodes[root_index]) 注意:这种方法假设双亲表示法数组中的节点顺序不影响树的结构(因为我们按照数组顺序链接兄弟节点)。但实际上,同一个父节点的多个孩子之间的顺序在双亲表示法中没有显式给出,因此我们按照数组中的顺序进行链接。如果题目要求孩子顺序按照某个特定规则,则可能需要先排序再链接。 下面我们用C语言实现这个转换(假设双亲表示法数组为`ParentRepNode parent_array[]`,节点数量为`n`): */ #include <stdio.h> #include <stdlib.h> // 双亲表示法节点结构 typedef struct { char data; // 节点数据 int parent_index; // 父节点索引(根节点为-1) } ParentRepNode; // 孩子兄弟表示法节点 typedef struct TreeNode { char data; struct TreeNode *firstChild; // 指向第一个孩子 struct TreeNode *rightSib; // 指向下一个兄弟 } TreeNode; // 函数:将双亲表示法转换为孩子兄弟表示法 TreeNode* parentToChildSibling(ParentRepNode parent_array[], int n) { if (n == 0) return NULL; // 创建节点指针数组最后一个孩子指针数组 TreeNode** nodes = (TreeNode**)malloc(n * sizeof(TreeNode*)); TreeNode** last_child = (TreeNode**)malloc(n * sizeof(TreeNode*)); // 用于记录每个节点的最后一个孩子节点(即当前孩子链表的最后一个节点) int root_index = -1; // 初始化节点 for (int i = 0; i < n; i++) { nodes[i] = (TreeNode*)malloc(sizeof(TreeNode)); nodes[i]->data = parent_array[i].data; nodes[i]->firstChild = NULL; nodes[i]->rightSib = NULL; last_child[i] = NULL; // 初始化为NULL // 如果是根节点,记录根索引 if (parent_array[i].parent_index == -1) { root_index = i; } } // 如果没有根节点,返回NULL if (root_index == -1) { free(nodes); free(last_child); return NULL; } // 遍历所有节点,构建孩子兄弟链表 for (int i = 0; i < n; i++) { // 跳过根节点(因为根节点没有父节点) if (parent_array[i].parent_index == -1) { continue; } int p_index = parent_array[i].parent_index; // 当前节点的父节点索引 // 将当前节点i链接到父节点p_index的孩子链表末尾 if (last_child[p_index] == NULL) { // 父节点还没有孩子,当前节点成为第一个孩子 nodes[p_index]->firstChild = nodes[i]; last_child[p_index] = nodes[i]; } else { // 父节点已有孩子,将当前节点链接到最后一个孩子的兄弟指针 last_child[p_index]->rightSib = nodes[i]; last_child[p_index] = nodes[i]; } } // 释放辅助数组 free(last_child); // 注意:nodes数组不能释放,因为返回的树中还需要使用这些节点 // 但是调用者需要在使用完树后释放所有节点,这里我们只free数组指针 TreeNode* root = nodes[root_index]; free(nodes); // 释放节点指针数组(但节点本身未释放) return root; } // 注意:调用者需要在使用完树后释放节点内存。这里可以写一个销毁树的函数。 void freeTree(TreeNode* root) { if (root == NULL) return; // 先释放孩子子树 freeTree(root->firstChild); // 再释放兄弟子树 freeTree(root->rightSib); // 最后释放当前节点 free(root); } /* 测试示例: 假设双亲表示法数组如下: 索引: 0,1,2,3,4 data: A,B,C,D,E parent_index: -1,0,0,1,1 (索引0是根节点A,BC是A的孩子,DE是B的孩子) 按照数组顺序,我们构建节点。然后: 节点0(A)是根节点。 节点1(B):父节点是0(A),此时last_child[0]为NULL,所以A的firstChild指向B,last_child[0]更新为B。 节点2(C):父节点是0(A),此时last_child[0]是B(非空),所以将B的rightSib指向C,然后last_child[0]更新为C。 节点3(D):父节点是1(B),last_child[1]为NULL,所以B的firstChild指向D,last_child[1]更新为D。 节点4(E):父节点是1(B),last_child[1]是D(非空),所以D的rightSib指向E,然后last_child[1]更新为E。 这样,树的结构是: A / \ B C / \ D E 在孩子兄弟表示法中: A: firstChild指向B,B的rightSib指向C;B: firstChild指向D,D的rightSib指向E;C: firstChild=NULL,rightSib=NULL;DE的firstChildrightSib均为NULL。 注意:上面的转换依赖于双亲表示法数组中的节点顺序。如果我们改变数组顺序,例如将节点2(C)放在节点1(B)之前,那么C就会成为A的第一个孩子,而B则成为C的兄弟(即C的rightSib指向B)。因此,顺序会影响兄弟的次序。 如果题目要求孩子节点按照某种顺序(如按字母序),那么我们就需要在链接之前对同一个父节点的孩子节点进行排序。但双亲表示法本身不提供顺序,所以需要额外信息或排序依据。 以上代码实现了转换,并假设顺序按照数组索引顺序。 如果用户需要其他语言的实现,可以根据这个思路编写。 下面我们提供一段测试代码,用于打印孩子兄弟表示法的树(以层次结构打印): */ // 打印树(辅助函数,按层打印) void printTree(TreeNode* root) { if (root == NULL) return; // 使用队列进行层次遍历(这里用数组模拟队列) TreeNode* queue[100]; // 假设最多100个节点 int front = 0, rear = 0; queue[rear++] = root; while (front < rear) { int level_size = rear - front; for (int i = 0; i < level_size; i++) { TreeNode* node = queue[front++]; printf("%c ", node->data); // 将该节点的第一个孩子以及孩子的兄弟们加入队列 TreeNode* child = node->firstChild; while (child != NULL) { queue[rear++] = child; child = child->rightSib; } } printf("\n"); } } // 测试 int main() { // 构建双亲表示法数组(示例) ParentRepNode parent_array[5] = { {'A', -1}, {'B', 0}, {'C', 0}, {'D', 1}, {'E', 1} }; TreeNode* root = parentToChildSibling(parent_array, 5); printf("层次遍历结果:\n"); printTree(root); // 释放树 freeTree(root); return 0; } /* 运行测试代码,预期输出: 层次遍历结果: A B C D E 注意:实际代码中需要处理内存分配失败的情况,这里为了简洁省略了。 */ /* 相关问题: 1. 双亲表示法有什么优缺点? 2. 孩子兄弟表示法适用于什么场景? 3. 如何将孩子兄弟表示法转换回双亲表示法? */ </think>### 双亲表示法转孩子兄弟表示法的二叉树代码实现 #### 算法思路 1. **双亲表示法**:每个节点存储数据值父节点索引(根节点的父索引为-1) 2. **孩子兄弟表示法**:每个节点包含: - 数据域 `data` - 指向第一个孩子的指针 `firstChild` - 指向下一个兄弟的指针 `nextSibling` 3. **转换步骤**: - 创建节点数组并初始化 - 遍历所有节点,连接父子关系: - 若父节点无孩子,当前节点设为第一个孩子 - 若父节点已有孩子,将当前节点设为最后一个孩子的兄弟 #### C++代码实现 ```cpp #include <iostream> #include <vector> using namespace std; // 双亲表示法节点 struct ParentNode { char data; int parentIdx; }; // 孩子兄弟表示法节点 struct TreeNode { char data; TreeNode* firstChild; TreeNode* nextSibling; TreeNode(char d) : data(d), firstChild(nullptr), nextSibling(nullptr) {} }; TreeNode* convert(vector<ParentNode>& nodes) { if (nodes.empty()) return nullptr; // 创建节点指针数组 vector<TreeNode*> treeNodes(nodes.size()); TreeNode* root = nullptr; // 初始化所有节点 for (int i = 0; i < nodes.size(); ++i) { treeNodes[i] = new TreeNode(nodes[i].data); if (nodes[i].parentIdx == -1) root = treeNodes[i]; } // 构建父子兄弟关系 for (int i = 0; i < nodes.size(); ++i) { int parentIdx = nodes[i].parentIdx; if (parentIdx == -1) continue; // 跳过根节点 TreeNode* parent = treeNodes[parentIdx]; TreeNode* current = treeNodes[i]; if (!parent->firstChild) { // 第一个孩子 parent->firstChild = current; } else { // 找到最后一个兄弟 TreeNode* sibling = parent->firstChild; while (sibling->nextSibling) { sibling = sibling->nextSibling; } sibling->nextSibling = current; } } return root; } // 打印树结构(辅助函数) void printTree(TreeNode* root, int depth = 0) { if (!root) return; for (int i = 0; i < depth; ++i) cout << " "; cout << root->data << endl; printTree(root->firstChild, depth + 1); printTree(root->nextSibling, depth); } // 测试示例 int main() { // 双亲表示法示例:A(根)->B->C->D vector<ParentNode> nodes = { {'A', -1}, // 根节点 {'B', 0}, // A的孩子 {'C', 0}, // A的孩子 {'D', 1} // B的孩子 }; TreeNode* root = convert(nodes); cout << "孩子兄弟表示法结构:" << endl; printTree(root); return 0; } ``` #### 输出示例 ``` 孩子兄弟表示法结构: A B D C ``` #### 关键点说明 1. **时间复杂度**:$O(n^2)$(链表查找可优化为$O(n)$) 2. **空间复杂度**:$O(n)$ 3. **父子关系建立**: - 当父节点的`firstChild`为空时,直接设置为第一个孩子 - 否则遍历兄弟链表到末尾添加新节点 4. **根节点处理**:父索引为-1的节点即为根节点 #### 优化方向 1. 为每个父节点维护尾指针,可将时间复杂度优化到$O(n)$ 2. 支持动态节点添加处理异常输入 3. 添加内存释放函数防止泄漏
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值