算法題1:二元查找樹轉換成有序雙向鏈表

在CSDN上發現一個很不錯的算法博客:http://blog.csdn.net/v_JULY_v。博客主人無私地整理了各大公司面試的算法題,在此非常感謝。今天,整整一天,才完成了第一個算法題,可見我的專業基礎多麼地薄弱。革命尚未成功,同志需要繼續努力。

現將一天的勞動成功發表於此,以示鼓勵。

題目:
輸入一棵二元查找樹,將該二元查找樹轉換成一個排序的雙向鏈表。
要求不能創建任何新的結點,只調整指針的指向。
   10
  / /
  6  14
 / / / /
4  8 12 16
 轉換成雙向鏈表
4=6=8=10=12=14=16。
 
 首先我們定義的二元查找樹 節點的數據結構如下:
 struct BSTreeNode
{
  int m_nValue; // value of node
  BSTreeNode *m_pLeft; // left child of node
  BSTreeNode *m_pRight; // right child of node
};

算法思想: 
找到有序雙向鏈表中節點的左右指針節點在二元查找樹中的位置
對應關係如下:
有序雙向鏈表中節點的左指針:二元查找樹中對應節點的左子樹中最大節點
有序雙向鏈表中節點的右指針:二元查找樹中對應節點的右子樹中最小節點
程序實現:
1)按照先序遍歷方式遍歷二元查找樹,
2)遍歷的同時根據上述算法思想更新節點的左右指針,
3)爲了得到雙向鏈表,根據以下關係:
p->m_pLeft = q  ==》 q->m_pRight = p;
p->m_pRight = q ==》 q->m_pLeft = p;
  補齊指針。
4)二元查找樹遍歷結束時,最終形成一個雙向鏈表。

程序源碼:

tree.h

/********************************
tree.h : 定義樹的頭文件
********************************/
#ifndef __TREE_H__
#define __TREE_H__

typedef int  ElemType;

struct BSTreeNode{
	ElemType m_nValue;
	struct BSTreeNode *m_pLeft;
	struct BSTreeNode *m_pRight;
};

typedef struct BSTreeNode *pBSTreeNode;
typedef struct BSTreeNode *pSearchTree;

pSearchTree Insert(ElemType x,pSearchTree T);
void InOrderTree(pSearchTree T);
void PrintList(pBSTreeNode pHead);
void TreeToList(pSearchTree pT);
pBSTreeNode GetListHead(pSearchTree pRoot);

#endif

tree.c

#include <stdio.h>
#include <stdlib.h>
#include "tree.h"

int counter = 0;                 //統計節點遍歷的次數

// 打印雙向鏈表
void PrintList(pBSTreeNode pHead)
{
	if (NULL == pHead)
	{
		return;
	}

	printf("\n%11s: ","List");
	pBSTreeNode p = pHead;
	do 
	{
		printf("%d, ", p->m_nValue);	
		p = p->m_pRight;
	}while(p != NULL);
	printf("\n");
}

/*創建新節點*/
pBSTreeNode NewNode(ElemType x)
{
	pBSTreeNode new_node;
	new_node = (pBSTreeNode)malloc(sizeof(struct BSTreeNode));
  if ( NULL == new_node){
  	printf("malloc error\n");
  	return NULL;
  }else{
  	new_node->m_nValue = x;
  	new_node->m_pLeft = new_node->m_pRight = NULL;
  }
	
	return new_node;
}

/*在二叉樹中插入節點*/
pSearchTree Insert(ElemType x, pSearchTree T)
{
	if (NULL == T)
	{
		T = NewNode(x); //創建新節點
	}
	else {
		if ( x < T->m_nValue){ //如果新插入的節點小於當前節點,則插入其左節點
			T->m_pLeft = Insert(x, T->m_pLeft); 
		} 
		else{
			if ( x > T->m_nValue){
				T->m_pRight = Insert(x, T->m_pRight);//如果大於當前節點,則插入其右節點
			}
			else{
				printf("%d is already exsited\n",x); //不允許重複節點
			}	
		}
	}	
	return T;
}

/*中序遍歷二叉樹*/
void InOrderTree(pSearchTree T)
{
	if (NULL != T)
	{
		InOrderTree(T->m_pLeft);
		printf("%d, ", T->m_nValue);
		InOrderTree(T->m_pRight);
	}
}

/**********************************************
 查找節點在雙向鏈表中的左指針節點:
 二元查找樹中該節點左子樹中最大的節點
*********************************************/ 
pBSTreeNode FindLeft(pSearchTree pT)
{
	pBSTreeNode p;

	if (NULL == pT){
		return NULL;
	}
	
	if ( NULL == pT->m_pRight){ //如果當前節點不存在右節點,則當前節點就是所找節點
		return pT;
	}
	else{
		p = FindLeft(pT->m_pRight);
	}
	return p;
}

/******************************************
  查找節點在雙向鏈表中對應的右指針節點:
  二元查找樹中該節點右子樹中最小的節點
*******************************************/
pBSTreeNode FindRight(pSearchTree pT)
{
	pBSTreeNode p;

	if (NULL == pT){
		return NULL;
	}
    
	if (NULL == pT->m_pLeft){ //如果當前節點不存在左節點,則當前節點即爲所找節點
		return pT;
	}
	else{
  	p = FindRight(pT->m_pLeft);
  }
	
	return p;
}

/**********************************************
以先序遍歷的方式遍歷二元查找樹,遍歷的同時更新樹
中節點的左右指針,最終得到一個有序雙向鏈表
***********************************************/
void TreeToList(pSearchTree pT)
{
	pSearchTree L,R;
	
	if (NULL == pT)
		return ;

	counter++;

	L = pT->m_pLeft;             //先保存當前節點的左右子樹
	R = pT->m_pRight;

	pT->m_pLeft = FindLeft(L);   //查找當前節點在雙向鏈表中的左指針節點
	pT->m_pRight = FindRight(R); //查找當前節點在雙向鏈表中的右指針節點
        
	TreeToList(L);
	TreeToList(R);

  if (pT->m_pLeft){
  	pT->m_pLeft->m_pRight = pT; //根據左指針,補齊對應的右指針
  }
        
	if (pT->m_pRight){
  	pT->m_pRight->m_pLeft = pT; //根據右指針,補齊對應的左指針
 	}

	return ;
}

/****************************************
從根節點開始,按照左指針的指向依次查找,
一直找到雙向鏈表的頭結點爲止。
函數返回雙向鏈表的頭結點
*****************************************/
pBSTreeNode GetListHead(pBSTreeNode pT)
{
	if (NULL == pT)
  {
  	return NULL;
  }

  pBSTreeNode p = pT;

	while(NULL != p->m_pLeft)
		p = p->m_pLeft;

	return p;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "tree.h"

extern int counter;

int main()
{	
	FILE *fp;
	int c;

	pSearchTree T;
	pBSTreeNode phead; //生成的雙向鏈表的頭結點
	
	T = NULL;

	fp = fopen("in.txt","r");
	if (NULL == fp){
		printf("open file in.txt failed\n");
		exit(0);
	}
	while (fscanf(fp,"%d",&c) != EOF ){
		T = Insert(c,T);
	}

	fclose(fp);

 	printf("InOrderTree: ");
	InOrderTree(T);
	printf("\n");

	TreeToList(T);         /*二元查找樹轉換爲兩個單向鏈表*/
	
	phead=GetListHead(T);  /*得到雙向鏈表的頭指針*/
	PrintList(phead);      /*打印輸出雙向鏈表*/
	
	printf("counter = %d\n",counter);
	return 0;	
}


測試數據in.txt
100 34 98 102 83 2 39 93 32 45 21 35 193 10 6 394 24 95 298 84 18 9  86 12 3 20 11 23 92 89 
測試結果:
InOrderTree: 2, 3, 6, 9, 10, 11, 12, 18, 20, 21, 23, 24, 32, 34, 35, 39, 45, 83, 84, 86, 89, 92, 93, 95, 98, 100, 102, 193, 298, 394, 
       List: 2, 3, 6, 9, 10, 11, 12, 18, 20, 21, 23, 24, 32, 34, 35, 39, 45, 83, 84, 86, 89, 92, 93, 95, 98, 100, 102, 193, 298, 394, 
counter = 30

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