高级数据结构 | 二叉树顺存储及其遍历

二叉树除过使用链式存储外还可以使用线性存储,这里我们使用数组模拟这一过程。

如下所示为一颗二叉树,其中根节点为 0 11,其中0是我们人为加上的序号,通过序号之间的关系把二叉树存放到数组中。

5 81
0 11
1 45
2 63
3 22
4 57
6 55

对于以上二叉树,我们使用数组进行存储:int arr[] = { 11,45,63,22,57,81,55 };

0 1 2 3 4 5 6
11 45 63 22 57 81 55

他们之间的关系如下:

  • 根节点与左右孩子的关系,令根节点的下标i
    • 左孩子:下标= 2i+1
      例如:根结点 i=0,左孩子=array[1]=45
    • 右孩子:下标= 2i+2
      例如:根结点 i=0,右孩子=array[2]=45
  • 孩子结点与根节点的关系,令左孩子下标为 j,右孩子为k。(其中k=j+1)
    • 左->根节点:下标 = (j-1)/2
    • 右->根节点:下标 = (k-1)/2
  • i 下标所在的层次为 l o g 2 i + 1 log_2^{i+1}

递归——先序中序后序遍历

typedef int ElemType;
#define	END -1		// 输入时以-1结束


// 先序遍历
void PreOder(ElemType* parr, int i, int n)
{
	if (i < n && parr[i] != END)
	{
		std::cout << parr[i] << " ";
		PreOder(parr, 2 * i + 1, n);
		PreOder(parr, 2 * i + 2, n);
	}

}
void PreOder_Ar(ElemType* parr, int n)
{
	if (NULL == parr) return;
	PreOder(parr, 0, n);
	std::cout << std::endl;
}


// 中序遍历
void InOder(ElemType* parr, int i,int n)
{
	if (i < n && parr[i] != END)
	{
		InOder(parr, 2 * i + 1, n);
		std::cout << parr[i] << " ";
		InOder(parr, 2 * i + 2, n);
	}

}
void InOder_Ar(ElemType* parr, int n)
{
	if (NULL == parr) return;
	InOder(parr, 0, n);
	std::cout << std::endl;
}


// 后序遍历
void PastOder(ElemType* parr, int i, int n)
{
	if (i < n && parr[i] != END)
	{
		PastOder(parr, 2 * i + 1, n);
		PastOder(parr, 2 * i + 2, n);
		std::cout << parr[i] << " ";
	}

}
void PastOder_Ar(ElemType* parr, int n)
{
	if (NULL == parr) return;
	PastOder(parr, 0, n);
	std::cout << std::endl;
}



int main()
{
	/*
			31
		23		 12
	66		  5    17
  70  62	    88    55
	
	*/
	int arr[] = { 31,23,12,66,-1,5,17,70,62,-1,-1,-1,88,-1,55 };
	int n = sizeof(arr) / sizeof(arr[0]);
	PreOder_Ar(arr, n);
	InOder_Ar(arr,n);
	PastOder_Ar(arr, n);
	std::cout << std::endl;
	return 0;
}

非递归——先序中序后序

void NicePreOrder(ElemType* parr, int n)
{
	if (NULL == parr || n < 1) return;
	std::stack<int> st;
	int i = 0;			// 根结点下标
	while (!st.empty() || i < n)
	{
		while (i < n && parr[i] != END)	// 向左入栈
		{
			st.push(i);
			std::cout << parr[i] << " ";
			i = 2 * i + 1;
		}
		i = st.top(); st.pop();
		i = 2 * i + 2;
	}
	std::cout << std::endl;
}

void NiceInOrder(ElemType* parr, int n)
{
	if (NULL == parr || n < 1) return;
	std::stack<int> st;
	int i = 0;			// 根结点下标
	while (!st.empty() || i < n)
	{
		while (i < n && parr[i] != END)	// 向左入栈
		{
			st.push(i);
			i = 2 * i + 1;
		}
		i = st.top(); st.pop();
		std::cout << parr[i] << " ";
		i = 2 * i + 2;
	}
	std::cout << std::endl;
}

void NicePastOrder(ElemType* parr, int n)
{
	if (NULL == parr || n < 1) return;
	std::stack<int> st;		// 存放下标
	int i = 0, flg = 0;		// flg 用于标记访问过的子树
	while (!st.empty() || i < n)
	{
		while (i < n && parr[i] != END)
		{
			st.push(i);
			i = 2 * i + 1;
		}

		i = st.top(); st.pop();

		int r = 2 * i + 2;		/* 右孩子下标 */
		// 判断右子树是否遍历过(空or遍历过)
		if (r >= n || parr[r] == END || r == flg)
		{
			std::cout << parr[i] << " ";
			flg = i;
			i = n;		/* 左右子树已经遍历完,出栈 */
		}
		else
		{
			st.push(i);		// 继续入栈
			i = r;
		}
	}
	std::cout << std::endl;
}

层次遍历

顺序每层从左至右遍历。

void LeveOrder(ElemType* parr, int n)
{
	if (NULL == parr || n < 1) return;
	std::queue<int> que;
	que.push(0);
	int i = 0;
	while (!que.empty() && i < n)
	{
		i = que.front(); que.pop();
		std::cout << parr[i] << " ";
		if (2 * i + 1 < n && parr[2*i + 1] != END)
		{
			que.push(2 * i + 1);
		}
		if (2 * i + 2 < n && parr[2*i + 2] != END)
		{
			que.push(2 * i + 2);
		}
	}
	std::cout << std::endl;
}

左右交替层次遍历。

void LeveOrder_Z(ElemType* parr, int n)
{
	if (NULL == parr || n < 1) return;
	std::stack<int> st1;
	std::stack<int> st2;
	st1.push(0);
	int i = 0;
	while (!st1.empty() || !st2.empty())
	{
		while (!st1.empty())	// 从左向右入栈
		{
			i = st1.top(); st1.pop();
			std::cout << parr[i] << " ";
			if (2 * i + 1 < n && parr[2 * i + 1] != END)
			{
				st2.push(2 * i + 1);
			}
			if (2 * i + 2 < n && parr[2 * i + 2] != END)
			{
				st2.push(2 * i + 2);
			}
		}
		while (!st2.empty())	// 从右向左入栈
		{
			i = st2.top(); st2.pop();
			std::cout << parr[i] << " ";
			if (2 * i + 2 < n && parr[2 * i + 2] != END)
			{
				st1.push(2 * i + 2);
			}
			if (2 * i + 1 < n && parr[2 * i + 1] != END)
			{
				st1.push(2 * i + 1);
			}
		}
	}

	std::cout << std::endl;
}

其实通过以上几段代码,我们就能发现,无论是链式的存储结构还是顺序的存储结构,他们都能表示一棵二叉树。并且在对二叉树的一些操作上的算法也是极其相似的。以上几段代码都是脱胎于之前博文中的链式二叉树的遍历算法。因此我们可以照猫画虎,按照以往分析的经验对这些算法进行实现 。

原创文章 131 获赞 128 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_43919932/article/details/105568363