04-树6 Complete Binary Search Tree

根据给定的序列构建一棵既是完全二叉树又是二叉搜索树,并输出层序遍历序列。首先对序列排序,然后利用树的特性确定根节点,通过循环而非递归方式找到左右子树的根节点,最终实现层序遍历。
摘要由CSDN通过智能技术生成

题目:

A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:The left subtree of a node contains only nodes with keys less than the node’s key.The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.Both the left and right subtrees must also be binary search trees.A Complete Binary Tree (CBT) is a tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right.Now given a sequence of distinct non-negative integer keys, a unique BST can be constructed if it is required that the tree must also be a CBT. You are supposed to output the level order traversal sequence of this BST.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤1000). Then Ndistinct non-negative integer keys are given in the next line. All the numbers in a line are separated by a space and are no greater than 2000.

Output Specification:

For each test case, print in one line the level order traversal sequence of the corresponding complete binary search tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.

Sample Input:

10
1 2 3 4 5 6 7 8 9 0

Sample Output:

6 3 8 1 5 7 9 0 2 4

题目理解

大概就是输入的这个序列,能唯一地组成一棵既是完全二叉树,又是二叉搜索树的树。要求输出这棵树的层序遍历。

思路

因为既是完全二叉树又是二叉搜索树,那么两者的特征这棵树也有。完全二叉树可以凭借节点的数量来确定树的形状,二叉搜索树每个节点的左子树所有节点一定小于该节点,右子树一定大于。凭借这两点可以发现这种树的根节点很好确认。
发现可以不用递归,就用循环去做了。先将输入序列由小到大进行排序,存储在数组a中(意外发现这是中序的顺序x),然后利用树的特征算出左右子树的大小,在a中找到根节点。接下来对左右子树做同样的操作,得到左右子树的根节点。
因为没用递归所以换了结构体来存储根节点的信息,比如该节点左右子树的大小,该节点的值与该节点于a中的下标。循环写起来就没有递归简洁了但是我实在太怕爆栈啊啥的…

代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#pragma warning(disable:4996)
#define MAXN 1005
int N, a[MAXN];
typedef struct TreeNode *PtrT;
struct TreeNode
{
 int index;
 int leftsize;
 int rightsize;
 int value;
};
int less(int i, int j)
{
 return i < j;
}
void exch(int *a, int i, int j)
{
 int t;
 t = *(a + i);
 *(a + i) = *(a + j);
 *(a + j) = t;
}
void Sort(int *a,int N)
{
 int h = 1, i, j;
 while (h < N / 3)
  h = h * 3 + 1;
 while (h >= 1)
 {
  for (i = h; i < N; i++)
   for (j = i; j >= h && less(a[j], a[j - h]); j -= h)
    exch(a, j, j - h);
  h = h / 3;
 }
}
PtrT FindHead(int *a, int Size, int Pos)
{
 PtrT T;
 T = (PtrT)malloc(sizeof(struct TreeNode));
 if (Size == 1)
 {
  T->value = a[Pos];
  T->index = Pos;
  T->leftsize = 0;
  T->rightsize = 0;
 }
 else
 {
  int k, lev, head, indH;
  for (k = 0; pow(2, k) - 1 <= Size; k++);
  lev = Size - pow(2, k - 1) + 1;
  if (lev < pow(2, k - 2))
   head = (Size + lev) / 2;
  else head = pow(2, k - 1) - 1;
  indH = Pos + head;
  T->value = a[indH];
  T->index = indH;
  T->leftsize = head;
  T->rightsize = Size - 1 - head;
 }
 return T;
}
int main()
{
 scanf("%d", &N);
 TreeNode T[MAXN];
 for (int i = 0; i < N; i++)
  scanf("%d", a + i);
 Sort(a, N);
 T[0] = *(FindHead(a, N, 0));
 for (int i = 1; i < N; i++)
 {
  int indexF, sizeT, posL;
  if (i % 2)
  {
   indexF = i / 2;
   sizeT = T[indexF].leftsize;
   posL = T[indexF].index - sizeT;
  }
  else
  {
   indexF = (i - 1) / 2;
   sizeT = T[indexF].rightsize;
   posL = T[indexF].index + 1;
  }
  T[i] = *(FindHead(a, sizeT, posL));
 }
 int i;
 for (i = 0; i < N - 1; i++)
  printf("%d ", T[i].value);
 printf("%d\n", T[i].value);
 /*for (i = 0; i < N - 1; i++)
 {
  printf("value:%d index:%d ls:%d rs:%d\n", T[i].value, T[i].index, T[i].leftsize, T[i].rightsize);
 }
 printf("value:%d index:%d ls:%d rs:%d\n", T[i].value, T[i].index, T[i].leftsize, T[i].rightsize);*/
 system("pause");
 return 0;
}

之后又在网上查到一种方法,比较抽象但是真滴很简洁。
将层序遍历的顺序(即下标)用中序输出,得到数组b。则有层序输出为:c[b[i]]=a[i]
画个图,可能会清楚一点
在这里插入图片描述

想通了大概就是,为了层序输出,需要把层序输出的顺序和树的节点对应上,即树的节点在层序输出中的下标对应上。将下标以中序输出,与节点的中序输出同步上了,即知道了层序输出的每个下标对应了哪个节点。最后按下标的层序(即由小到大)输出其对应的节点,也就是树的层序输出(好罗嗦哦)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值