劍指offer--數據結構之二叉樹(1、4、6、11、12、27、48、50、60)

offer1

輸入一棵二元查找樹,將該二元查找樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只調整指針的指向。

二元查找樹的中序遍歷就是已經排序的序列,改成雙向鏈表,可考慮,先把左子樹改造成雙向鏈表,保存好最後一個指針,然後將鏈表最後節點後驅指向跟節點,則根節點加入雙向鏈表,然後在將右子樹改造成雙向鏈表。和中序遍歷不同的是,需要一個指針保存雙向鏈表的最後一個節點。

代碼:

void ConvertNode(BSTNode* pNode, BSTNode* &pLastNodeList)
{
	if (pNode)
	{
		ConvertNode(pNode->lchild, pLastNodeList);

		pNode->lchild=pLastNodeList;
		if (pLastNodeList!=NULL)
			pLastNodeList->rchild=pNode;
		pLastNodeList=pNode;

		ConvertNode(pNode->rchild, pLastNodeList);
	}
}


BSTNode* ConvertTreeToList(BSTNode* pNode)
{
	BSTNode* pLastNodeList=NULL;
	ConvertNode(pNode, pLastNodeList);

	//找到鏈表的頭
	while (pNode->lchild!=NULL)
		pNode = pNode->lchild;

	return pNode;
}


offer4

題目:輸入一個整數和一棵二元樹。從樹的根結點開始往下訪問一直到葉結點所經過的所有結點形成一條路徑。打印出和與輸入整數相等的所有路徑。

可以從根節點開始,每訪問一個節點就壓入棧中,然後訪問棧頂元素的左子樹,再訪問右子樹,遞歸操作,當棧頂元素的左右子樹爲空時,檢查棧中元素的和,符合要求則輸出。

代碼:

void Find(BTnode* T, Stack* S, int sum)
{
	if (T)
	{
		push(S, T->data);
		//棧中存在空閒的棧底,用來保存當前棧的和,每次push都會更新
		//檢查路徑是否符合
		if (sum==S->bottom->data && T->pLchild==NULL && T->pRchild==NULL)
		{
			Snode* p=S->top;
			while (p!=S->bottom)
			{
				printf("%3d ",p->data);
				p=p->pNext;
			}
			printf("\n");
		}


		Find(T->pLchild, S, sum);
		Find(T->pRchild, S, sum);

		pop(S);
		//訪問玩以該節點爲根的二叉樹後,要pop該根節點,然後訪問下一個
	}
}

offer6

輸入一個整數數組,判斷該數組是不是某二元查找樹的後序遍歷的結果。如果是返回true,否則返回false

二元查找樹就是二叉排序樹,類似於快速排序的操作,首先判斷整個數組是否是二叉排序樹(即存在一個位置,左邊的值全部小於等於最末元素(根節點),右邊的值全部大於等於最末元素),符合條件後,再分別判斷左右部分是否符合條件,

代碼:

bool Check(int* a, int low, int high)
{
	if (!a || low<high)
		return false;
	//只有一個元素,肯定是排序樹
	if (low==high)
		return true;

	int i,j,k;
	//找到一個位置,左邊的全部小於根節點
	for (i=low; a[i]<=a[high]&& i<=high; i++);

	//檢查該位置右邊,是否全部大於根節點
	for (j=i; j<high && a[j]>=a[high]; j++);
	//符合條件,繼續檢查左右部分
	if (j==high)
		return Check(a,low,i-1)&&Check(a,i,high-1);
	else
		return false;
}

offer11

輸入一顆二元查找樹,將該樹轉換爲它的鏡像,即在轉換後的二元查找樹中,左子樹的結點都大於右子樹的結點。用遞歸和循環兩種方法完成樹的鏡像轉換。

遞歸好理解:從根節點開始,交換左右孩子,然後將左孩子當作根節點遞歸,然後右孩子。

代碼:

void Mirror_1(BSTNode* T)
{
	if (T)
	{
		BSTNode* p=T->lchild;
		T->lchild=T->rchild;
		T->rchild=p;
		Mirror_1(T->lchild);
		Mirror_1(T->rchild);
	}
}

循環則類似層序遍歷,需要一個隊列,從跟節點開始,出隊,交換左右孩子,左右孩子入隊,直到隊列爲空

代碼:

void Mirror_2(BSTNode* T)
{
	if (!T)
		return;

	BSTNode* pTemp=NULL;
	Queue* pQueue=CreatQueue();
	EnQueue(pQueue, T);
	
	while (DeQueue(pQueue, pTemp))
	{
		if (pTemp)
		{
			BSTNode* p=pTemp->lchild;
			pTemp->lchild=pTemp->rchild;
			pTemp->rchild=p;

			EnQueue(pQueue, pTemp->lchild);
			EnQueue(pQueue, pTemp->rchild);
		}
	}
}

offer12

輸入一顆二元樹,從上往下按層打印樹的每個結點,同一層中按照從左往右的順序打印。

就是層序遍歷,代碼:

void (*pvisit)(BSTNode*);

void visit(BSTNode* T)
{
    printf("%3d ",T->data);
}

void CengTraverseBST(BSTNode* T, void (*pvisit)(BSTNode*))
{
	if (!T)
		return;

	BSTNode* pTemp=NULL;
	Queue* pQueue=CreatQueue();
	EnQueue(pQueue, T);

	while (DeQueue(pQueue, pTemp))
	{
		if (pTemp)
		{
			visit(pTemp);
			EnQueue(pQueue, pTemp->lchild);
			EnQueue(pQueue, pTemp->rchild);
		}
	}
}
使用了下函數指針,可以怎加移植性。


offer27

輸入一棵二元樹的根結點,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

用遞歸非常簡單,代碼:

int DeepBtree(BTnode *pB)
{
	if (!pB)
		return 0;
	int lDeep=DeepBtree(pB->pLchild);
	int rDeep=DeepBtree(pB->pRchild);
	return lDeep>rDeep? 1+lDeep: 1+rDeep;
}

offer48

輸入二叉樹中的兩個結點,輸出這兩個結點在數中最低的共同父結點。

想找一個能夠用遍歷解決的思路,始終想不出來。原博客中是保存好兩個節點的路徑,然後從路徑開始,找到第一個不同的節點的前驅,那麼就是共同的父節點了。

http://zhedahht.blog.163.com/blog/static/25411174201081263815813/

以後寫代碼。



offer50

輸入兩棵二叉樹AB,判斷樹B是不是A的子結構。

可以先遍歷樹A,找到某個和B根節點相等的節點,然後繼續遍歷該節點的左右子樹是否完全符合,是返回真,

否則就繼續在A中尋找下一個和B根節點相等的節點。

以後寫代碼

//檢查以 pTree1爲根節點的樹是否等於Tree2。
bool CheckTree2Nodes(BTnode* pTree1, BTnode* pTree2)
{
	//兩個都爲空樹,包含
	if (!pTree1 && !pTree2)
		return true;
	//Tree1爲空,Tree2不空,不包含
	if (!pTree1)
		return false;
	//Tree1不空,Tree2空,說明Tree2已經檢查到某個路徑的末尾,包含
	if (pTree1 && !pTree2)
		return true;
	//連個根節點相等,檢查左右子樹的節點是否符合
	if (pTree1->data == pTree2->data)
		return CheckTree2Nodes(pTree1->pLchild, pTree2->pLchild) && CheckTree2Nodes(pTree1->pRchild, pTree2->pRchild);
	return false;
}


//檢查以 pTree1爲根節點的樹是否包含Tree2。
bool Tree2InTree1(BTnode* pTree1, BTnode* pTree2)
{
	if (!pTree1 && !pTree2)
		return true;
	if (!pTree1)
		return false;
	//前序遍歷樹,如果發現Tree1中某節點和pTree2根節點相等
	//檢查檢查以 pTree1爲根節點的樹是否等於Tree2。
	if (pTree1->data == pTree2->data)
	{
		if (CheckTree2Nodes(pTree1, pTree2))
			return true;
	}
	//沒有找到相等的結點,繼續遍歷
	return Tree2InTree1(pTree1->pLchild, pTree2) || Tree2InTree1(pTree1->pRchild, pTree2);
}




offer60

輸入一棵二叉樹的根結點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意結點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。


類似後序遍歷的操作,也類似求樹深度的操作,先判斷左右子樹是否是平衡樹,並且需要變量返回其深度信息,然後在判斷左右子樹深度差,差值小於等於1,那麼就是平衡的,遞歸操作。

代碼:

bool IsBalanced(BTnode* pBtree, int &deep)
{
	if (!pBtree)
	{
		deep=0;
		return true;
	}

	int left, right;
	//遞歸判斷左右子樹深度
	if (IsBalanced(pBtree->pLchild, left) && IsBalanced(pBtree->pRchild, right))
	{
		if (abs(left-right)<=1)
		{
			deep=1+(left>right? left: right);
			return true;
		}
	}
	return false;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章