[2진 트리 검색 - Medium]

import java.security.SecureRandom;

/**
 * <PRE>
 * 1. 개요
 * 
 * 이진트리 구조는 RootNode가 2개의 하위 Node를 가지고, 
 * 하위 Node가 또 2개의 하위 Node를 갖는, 확장 구조이다.
 * 이진트리에 데이터를 검색하고 저장하는 알고리즘을 완성해야한다.
 * 
 * 규칙
 * 
 * <검색>
 * - Node보다 작은 경우, 왼쪽 하위 Node를 검색한다.
 * - 왼쪽 하위 Node의 값이 있는 경우 value와 같은 지 비교한다.
 * - 왼쪽 하위 Node의 값이 없는 경우, 검색은 실패한 것이다.
 * - Node보다 큰 경우, 오른쪽 하위 Node를 검색한다.
 * - 오른쪽 하위 Node의 값이 있는 경우 value와 같은 지 비교한다.
 * - 왼쪽 하위 Node의 값이 없는 경우, 검색은 실패한 것이다.	
 *
 * <저장>
 * - 첫번째 입력된 값은 Root Node의 값이 된다.
 * - 두번째 이후로 입력된 값들은 RootNode의 하위 Node들에 저장된다.
 * - 입력된 값들이 해당 Node의 값보다 작은 경우 왼쪽 하위 Node에 저장된다.
 * - 입력된 값들이 해당 Node의 값보다 큰 경우 오른 하위 Node에 저장된다.
 * - 저장하려는 Node에 값이 이미 있는 경우 해당 Node의 하위 Node에 값을 저장한다.
 * 
 * 2. 예상결과
 * Random 값을 사용하기 때문에 실행 결과는 매번 달라진다.
 * 
 * Tree has no Node.
 * Search Failed.[64]
 * Search Failed.[21]
 * Search Failed.[97]
 * Search Failed.[67]
 * Search Failed.[45]
 * Search Failed.[3]
 * Search Failed.[58]
 * Search Failed.[37]
 * Search Failed.[63]
 * Lv 0: 30
 *		Lv 1: 27
 *			Lv 2: 14
 *				Lv 3: 10
 *					Lv 4: null
 *					Lv 4: null
 *				Lv 3: 20
 *					Lv 4: 19
 *						Lv 5: null
 *						Lv 5: null
 *					Lv 4: null
 *			Lv 2: null
 *		Lv 1: 39
 *			Lv 2: 32
 *				Lv 3: null
 *				Lv 3: null
 *			Lv 2: 41
 *				Lv 3: null
 *				Lv 3: null
 *
 * </PRE>
 *
 * @author    박명하
 */
public class Test08 {

	public static void main( String[] args ) {

		try {
			// STEP 1. TEST 데이터 준비
			Tree tree = new Tree();
			int cnt = 10;

			// STEP 2. 해당 값이 없는 경우 tree에 추가한다.(1~100)
			for ( int inx = 0 ; inx < cnt ; inx++ ) {
				SecureRandom rd = SecureRandom.getInstance( "SHA1PRNG" );
				int tempNum = rd.nextInt( 100 );
				if ( !tree.searchValue( tempNum ) ) {
					tree.addNode( tempNum );
				}
			}

			// STEP 3. 결과 출력
			tree.printAllNode();

		} catch ( Exception e ) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * <PRE>
	 * Node Class
	 * 하나의 Node는 Value와 하위 Node 2개(이진트리)를 갖는다.
	 * </PRE>
	 */
	public static class Node {
		// Node Value
		private int value = 0;
		
		// Node Value 보다 작은 값이 저장되는 하위 Node
		private Node left = null;

		// Node Value 보다 큰 값이 저장되는 하위 Node
		private Node right = null;

		public int getValue() {
			return value;
		}

		public void setValue( int value ) {
			this.value = value;
		}

		public Node getLeft() {
			return left;
		}

		public void setLeft( Node left ) {
			this.left = left;
		}

		public Node getRight() {
			return right;
		}

		public void setRight( Node right ) {
			this.right = right;
		}
	}

	/**
	 * 
	 * <PRE>
	 * 이진 트리 구조 Class
	 * </PRE>
	 */
	public static class Tree {
		private Node root = null;

		/**
		 * 
		 * <PRE>
		 * Tree에 Value를 추가
		 * </PRE>
		 * 
		 * @param value : 추가하려는 값
		 */
		public void addNode( int value ) {
			// STEP 1. root Node가 없는 경우
			if ( root == null ) {
				Node node = new Node();
				node.setValue( value );
				root = node;

			// STEP 2. root Node가 있는 경우				
			} else {
				addNode( value, root );
			}
		}

		/**
		 * 
		 * <PRE>
		 * 특정 Node에 Value를 추가
		 * </PRE>
		 * 
		 * @param value : 추가하려는 값
		 * @param node : 대상 Node
		 */
		public void addNode( int value, Node node ) {

			// STEP 1. 추가하려는 값이 Node의 값보다 작은 경우
			if ( value < node.value ) {

				// STEP 1-1. 왼쪽 Node 가 Null이면 새로운 Node 추가
				if ( node.getLeft() == null ) {
					Node leftNode = new Node();
					leftNode.setValue( value );
					node.setLeft( leftNode );

				// STEP 1-2. 왼쪽 Node 가 Null이 아니면 하위 Node 이동
				} else {
					addNode( value, node.getLeft() );
				}

			// STEP 2. 추가하려는 값이 Node의 값보다 큰 경우	
			} else {

				// STEP 2-1. 오른쪽 Node 가 Null이면 새로운 Node를 추가
				if ( node.getRight() == null ) {
					Node rightNode = new Node();
					rightNode.setValue( value );
					node.setRight( rightNode );

				// STEP 2-2. 오른쪽 Node 가 Null이 아니면 하위 Node로 이동
				} else {
					addNode( value, node.getRight() );
				}
			}
		}

		/**
		 * 
		 * <PRE>
		 * 전체 Tree의 Node들을 출력한다. 
		 * </PRE>
		 *
		 */
		public void printAllNode() {

			// STEP 1. RootNode가 있는 경우
			if ( root != null ) {
				printNode( root, 0 );

			// STEP 2. RootNode가 없는 경우	
			} else {
				System.out.println( "Node is None." );
			}
		}

		/**
		 * 
		 * <PRE>
		 * Node들을 Hierarchy 구조로 출력한다.
		 * </PRE>
		 * 
		 * @param node : 출력할 Node
		 * @param cnt : 출력 level
		 */
		public void printNode( Node node, int cnt ) {
			for ( int inx = 0 ; inx < cnt ; inx++ ) {
				System.out.print( "\t" );
			}
			if ( node != null ) {
				System.out.println( "Lv " + cnt + ": " + node.getValue());
				cnt++;
				printNode( node.getLeft(), cnt );
				printNode( node.getRight(), cnt );
			} else {
				System.out.println( "Lv " + cnt + ": null" );
			}
		}

		/**
		 * 
		 * <PRE>
		 * 전체 Tree에서 해당 값이 있는지 조회한다.
		 * </PRE>
		 * 
		 * @param value : 검색하려는 값
		 * @return 검색 결과
		 */
		public boolean searchValue( int value ) {
			
			// STEP 1. RootNode가 없는 경우
			if ( root == null ) {
				System.out.println( "Tree has no Node." );
				return false;
			}
			
			// STEP 2. RootNode가 있는 경우
			return searchValue( value, root );
		}

		/**
		 * 
		 * <PRE>
		 * 해당 Node에서 해당 값이 있는지 조회한다.
		 * </PRE>
		 * 
		 * @param value : 검색하려는 값
		 * @param node : 해당 Node
		 * @return 검색 결과
		 */
		public boolean searchValue( int value, Node node ) {
			boolean result = false;
			
			// STEP 1. 값을 찾은 경우 
			if ( value == node.value ) {
				System.out.println( "Search Sucess.[" + value + "]" );
				return true;
				
			// STEP 2. 검색하려는 값이 Node 값보다 작은 경우	
			} else if ( value < node.value ) {

				// STEP 2-1. 하위 Node에 값이 있는 경우 더 검색한다.
				if ( node.getLeft() != null ) {
					searchValue( value, node.getLeft() );
				} else {
					System.out.println( "Search Failed.[" + value + "]" );
					return result;
				}

			// STEP 3. 검색하려는 값이 Node 값보다 작은 경우
			} else if ( value > node.value ) {
				if ( node.getRight() != null ) {
					searchValue( value, node.getRight() );
				} else {
					System.out.println( "Search Failed.[" + value + "]" );
					return result;
				}
			}
			return result;
		}
	}

}