直接上代码(vs2010环境下调试通过)。
#include "stdafx.h"
#include<malloc.h>
#include<assert.h>
struct TreeNode;
typedef struct TreeNode* Tree;
typedef Tree Position; //树的节点
typedef int ValueType;
struct TreeNode
{
ValueType value;
Position leftChild;
Position rightChild;
};
struct ListNode
{
ValueType value;
ListNode *next;
};
void nodeInit(ListNode *node, ValueType value, ListNode *next)
{
node->value=value;
node->next=next;
}
void initTreeNode(Position pos, ValueType value, Position leftChild, Position rightChild)
{
//pos = (Position) malloc(sizeof(struct TreeNode)); //?
pos->value = value;
pos->leftChild = leftChild;
pos->rightChild = rightChild;
}
//计算树的节点数
int nodesCount(Tree tree)
{
if(tree == NULL)
return 0;
int count = 1; //不为空则至少有一个节点
int leftCount = nodesCount(tree->leftChild);
int rightCount = nodesCount(tree->rightChild);
return count + leftCount + rightCount;
}
/*
前序遍历,数组中的数字顺序与实际遍历顺序不一致,why??因为最先没有返回值,
递归过程中index的值不对。index的值必须要返回,虽然层层递归中会使index的值
增加,但是下一层递归完成返回到上一层时index还是上一层中原来的index值,即
index值退变为一个较小的值,导致不能在数组中正确存储遍历结果
*/
int findWhole_rootFirst_iterate(Tree tree, ValueType result[], int index)
{
if(tree == NULL)
return index;
//先遍历根节点
//printf("%4d",tree->value);
result[index] = tree->value;
index++;
//再遍历左孩子
index = findWhole_rootFirst_iterate(tree->leftChild, result, index);
//后遍历右孩子
index = findWhole_rootFirst_iterate(tree->rightChild, result, index);
return index;
}
/*
前序遍历:遍历的结果序列的第一个是根节点,后面的序列以某一个位置为分界点,前
部分是左子树,后部分是右子树,不可能出现左子树与右子树交替分布的情况,其它遍
历方式遵循同样的规律
*/
ValueType* findWhole_rootFirst(Tree tree)
{
if(tree == NULL) //b保险起见
return NULL;
int count = nodesCount(tree);
ValueType *result = (ValueType*)malloc(sizeof(ValueType)*count);
//int index = 0;
findWhole_rootFirst_iterate(tree, result, 0);
return result;
}
//中序遍历
void findWhole_rootMiddle(Tree tree)
{
if(tree == NULL)
return;
findWhole_rootMiddle(tree->leftChild);
printf("%4d",tree->value);
findWhole_rootMiddle(tree->rightChild);
}
//后续遍历:略
//重建二叉树:根据前序及中序遍历结果构建出二叉树,并输出二叉树的头节点
//length表示遍历结果数组的长度即每次待重建的树的节点数
Tree reconstructTree(ValueType pre[], ValueType mid[], int length)
{
if(pre==NULL || mid == NULL || length <= 0)
return NULL;
ValueType rootValue = pre[0]; //前序遍历的第一个元素就是根节点值
Position root = (Position) malloc(sizeof(struct TreeNode));
assert(root != NULL); //表达式为假时终止程序
initTreeNode(root,rootValue,NULL,NULL);
//迭代的出口
if(length ==1)
return root;
int rootValuePosition=0; //rootValue在中序遍历数组中位置,此位置之前即为左子树,之后即为右子树
while(mid[rootValuePosition] != rootValue)
{
rootValuePosition++;
assert(rootValuePosition < length);//若在mid[]数组中没找到rootValue,则异常
}
//if(rootValuePosition > 0) //因为有了迭代出口,这里的条件不再必要,这也是迭代的通用规律
root->leftChild = reconstructTree(pre+1,mid,rootValuePosition);
//if(rootValuePosition < length-1) //因为有了迭代出口,这里的条件不再必要,这也是迭代的通用规律
root->rightChild = reconstructTree(pre+rootValuePosition+1,mid+rootValuePosition+1,length-rootValuePosition-1);
return root;
}
/*
二叉树转成单链表,有两种方式:
1)先遍历出树的全部节点再创建链表
2)仅改变树的指针,将左子树全移到右子树上返回的是特殊的tree,每个节点只有右子树,相当
于单链表。
下面程序的思路是:对于每个节点,先把左、右子树变成链表,再把左子树接到右子树后面,整个
树的左子树就去掉了,只剩右子树,即成链表。
*/
void treeToLinkList(Tree tree)
{
if(tree == NULL)
return;
treeToLinkList(tree->rightChild);
treeToLinkList(tree->leftChild);
if(tree->rightChild == NULL)
tree->rightChild = tree->leftChild;
else
{
Position rightChild = tree->rightChild;
while(rightChild->rightChild != NULL)
rightChild = rightChild->rightChild;
rightChild->rightChild = tree->leftChild;
}
tree->leftChild = NULL;
}
//单链表转为(完全)二叉树
Tree linkListToTree(ListNode *list, int num)//num>=0为每个节点的序号,左、右子树的序号分别为2*num+1、2*num+2
{
if(list == NULL)
return NULL;
int len=0; //链表长度
ListNode *node = list;
while(node != NULL)
{
len++;
node = node->next; //最先没写这一句!!
}
if(num > len-1)
return NULL;
node = list;
for(int i = 0; i < num; i++)
node = node->next;
Tree tree = (Tree)malloc(sizeof(struct TreeNode));
tree->value = node->value;
tree->leftChild = linkListToTree(list,2*num+1);
tree->rightChild = linkListToTree(list,2*num+2);
return tree;
}
//判断smallTree是不是bigTree的子结构
//方法1:通过遍历结果来比较。但是效率不是最好的,因为当返回结果是true时bigTree可能不需要全部遍历
bool isSubConstruction1(Tree bigTree, Tree smallTree)
{
int len1 = nodesCount(bigTree);
int len2 = nodesCount(smallTree);
if(len1 < len2)
return false;
ValueType *a1 = findWhole_rootFirst(bigTree);
ValueType *a2 = findWhole_rootFirst(smallTree);
for(int i = 0; i < len1-len2+1; i++) //搜索bigTree/a1
{
if(a1[i] = a2[0]) //找到了第一个相等的
{
for(int j = 1; j < len2; j++) //搜索smallTree/a2
{
if(a2[j] != a1[i+j])
break;
if(j == len2-1) //a2[]中直到最后一个数都与a1[]中的书连续相等,说明找到了子结构
return true;
}
}
}
return false;
}
//方法2:对于树结构,往往最有效的就是递归算法!所以这里使用递归比较
//下面的函数是判断的集体实施过程,即判断tree2中的所有节点是否与tree1中对应位置的节点相同
bool treesEqual(Tree tree1, Tree tree2)
{
if(tree1 == NULL)
return false;
if(tree2 == NULL)
return true;
if(tree1->value != tree2->value)
return false;
bool b1 = treesEqual(tree1->leftChild, tree2->leftChild);
bool b2 = treesEqual(tree1->rightChild, tree2->rightChild);
return b1 && b2;
}
/*
1.因为事先不知道小树在大数中相同的那部分的存在性及起点,所以先要找到具体比较
的入口,然后用上面的方法来具体比较;2.因为上一个函数的递归出口条件也可以说作
为下面函数的出口条件,故下面的函数不需要再写出口条件,当然如果不确定就写上——
重复的代码总比bug好!
*/
bool isSubConstruction2(Tree big, Tree small)
{
bool result = false;
result = treesEqual(big,small);
if(!result)
result = isSubConstruction2(big->leftChild,small);
if(!result)
result = isSubConstruction2(big->rightChild,small);
return result;
}
//求二叉树的镜像,即所有节点的左右孩子全左右反过来
Tree mirrorTree(Tree tree)
{
if(tree == NULL)
return NULL;
Tree mirTree = (Tree)malloc(sizeof(TreeNode));
mirTree->value = tree->value;
//mirTree->leftChild = tree->rightChild; //迭代会为左右子树分配新的内存并实现左右子树交换,在这里简单的赋值不行
//mirTree->rightChild = tree->leftChild;
mirTree->leftChild = mirrorTree(tree->rightChild);
mirTree->rightChild = mirrorTree(tree->leftChild);
return mirTree;
}
/*
逐层打印二叉树节点值(从上到下,从左到右)
思路:通过对一个具体的树进行分析,找到入手点:从根节点开始,每打印一个节点,
就把他的左、右孩子放到一个数组中,然后逐次打印出数组中的数即可。那么这个数组
中的元素同时有进有出,数组该定义为多大?数组长度要想增加,就要满足每次进来的
节点值的个数比出去的多,增加最快的情况便是满二叉树的情况--每次出去一个都进来
两个。这种情况下,数组中元素最多的时刻就是最下面一层节点值(数量最多)全部进
入到数组中的时刻( 这之前数组中元素个数在增加,这之后又在减少,故这时刻最多)
下面的函数中,
--printIndex为arr[]中将要输出元素的下标,
--inputIndex为将要输出元素的左孩子(如果存在)放入arr[]中的下标
实际证明,c语言的数组是也可存放结构体的!
*/
void printLayerByLayer_iterate(Tree tree, Tree arr[], int printIndex, int inputIndex)
{
if(printIndex >= inputIndex)
return;
printf("%4d",tree->value);
int nextIndex = inputIndex;
if(tree->leftChild != NULL)
{
arr[nextIndex] = tree->leftChild;
nextIndex++;
}
if(tree->rightChild != NULL)
{
arr[nextIndex] = tree->rightChild;
nextIndex++;
}
printLayerByLayer_iterate((Tree)arr[printIndex+1], arr, printIndex+1, nextIndex);
}
void printLayerByLayer(Tree tree)
{
int count = nodesCount(tree);
if(count == 0)
return;
int len = count - 1;//(count+1)/2; //为方便起见(避免数组中元素的整体移动),将数组长度增大
Tree *a = (Tree*)malloc(sizeof(Tree)*len);
printLayerByLayer_iterate(tree, a, -1, 0); //注意:这里的其实打印索引为-1,因为根节点可以直接输出,不需要存入数组,
//下一个要打印的索引才从0开始
free(a);
}
//证明一个元素互异的数组是不是一个二叉查找树的后序遍历结果
//方法一:利用了数学规律给定实数a、b、c,若在数轴上a、c分布在b的两侧,则(a-c)(b-c)<0。
bool isSearchedTree1(int a[], int startIndex, int endIndex)
{
if(endIndex - startIndex < 2) //在假设条件下,只有两个元素时一定是查找树
return true;
int root =a[endIndex];
int rightStartIndex = startIndex+1; //右子树起始点
for(int i = startIndex; i < endIndex-1; i++)
{
int mul = (root - a[i]) * (root - a[i+1]);
if(mul < 0)
{
//nextStartIndex = i+1;
break;
}
else
rightStartIndex++;
}
for(int i = rightStartIndex; i < endIndex - 1; i++)
{
int mul = (root - a[i]) * (root - a[i+1]);
if(mul < 0)
return false;
}
bool b1 = isSearchedTree(a, startIndex,rightStartIndex-1);
bool b2 = isSearchedTree(a, rightStartIndex, endIndex-1);
return b1 & b2;
}
//方法二:查找树树已定义为左子树节点值<根节点值<右子树节点值,所以判断左右子树直接与根节点比较即可
bool isSearchedTree2(int a[], int len)
{
if(a == NULL || len < 1)
return false;
ValueType root = a[length-1];
int rightStart = 0; //右子树起点下标
for(;rightStart < length-1; rightStart++)
if(a[rightStart] > root);
break;
for(int i = rightStart; i < length-1; i++)
if(a[rightStart] < root)
return false;
//检验左右子树
bool b1 = isSearchedTree2(a, rightStart);
bool b2 = isSearchedTree2(a+rightStart, len-rightStart); //a[i]的地址为a+i
return b1 && b2;
}
/*
给定某个值,在二叉树中试找出一条路径,使各节点的value之和等于该值,然后找出
所有符合的路径这里的路径规定为从根节点到叶节点的路径(否则比较麻烦)
*/
void findPath_traceback(Tree tree, int sum, Tree *result, int index, int currentSum)
{
/*
//这样不能判断是否没找到,应该有一个数来记录搜索的次数,如果全部节点都被搜索还没找到,才能断定没找到
if(index <= 0)
{
printf("没找到和为%d的路径\n\n",sum);
}
*/
if(tree == NULL)
return;
result[index] = tree;
index++;
currentSum += tree->value;
bool isLeaf = tree->leftChild == NULL && tree->rightChild == NULL;
if(currentSum == sum && isLeaf)
{
printf("找到了和为%d的路径:",sum);
for(int i = 0; i < index; i++)
{
Tree path = (Tree)result[i];
printf("%4d",path->value);
}
printf("\n\n");
//return;
}
/*
找到一条路径之后应该继续找下一条,不应该返回。在找到一条路径的情况下:
1.会回溯到tree的上一个节点,继续寻找其它路径;
2.下面的两个递归都不会被执行(因为叶节点的leftChild、rightChild都是NULL,所以只会在没找到路径的情况下执行递归)
*/
int nextIndex = index;
findPath_traceback(tree->leftChild, sum, result, nextIndex, currentSum);
//应在左子树遍历完之后再遍历右子树
findPath_traceback(tree->rightChild, sum, result, nextIndex, currentSum);
//回溯
index--;
currentSum -= tree->value;
}
void findPath(Tree tree, int sum)
{
int count = nodesCount(tree);
if(count == 0)
return;
Tree *result = (Tree*)malloc(sizeof(Tree)*count);
findPath_traceback(tree, sum, result, 0, 0);
free(result);
}