2017-2018-1 20162308 实验二 树
实验二 树
实验内容
实验二 实现二叉树
参考教材p375,完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)。用JUnit或自己编写驱动类对自己实现的LinkedBinaryTree进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息。课下把代码推送到代码托管平台
import java.util.*;
public class MyLinkedBinaryTree<T> implements BinaryTree<T>
{
protected BTNode<T> root;
public MyLinkedBinaryTree()
{
root = null;
}
public MyLinkedBinaryTree (T element)
{
root = new BTNode<T>(element);
}
public MyLinkedBinaryTree (BTNode rootNode){
root = rootNode;
}
public MyLinkedBinaryTree (T element, MyLinkedBinaryTree<T> left,
MyLinkedBinaryTree<T> right)
{
root = new BTNode<T>(element);
root.setLeft(left.root);
root.setRight(right.root);
}
@Override
public T getRootElement()
{
if (root == null){
return null;
}
return root.getElement();
}
@Override
public MyLinkedBinaryTree<T> getLeft()
{
if (root == null){
return null;
}
MyLinkedBinaryTree<T> result = new MyLinkedBinaryTree<T>();
result.root = root.getLeft();
return result;
}
@Override
public T find (T target)
{
BTNode<T> node = null;
if (root != null){
node = root.find(target);
}
if (node == null){
return null;
}
return node.getElement();
}
@Override
public int size()
{
int result = 0;
if (root != null){
result = root.count();
}
return result;
}
@Override
public Iterator<T> inorder()
{
ArrayIterator<T> iter = new ArrayIterator<T>();
if (root != null){
root.inorder (iter);
}
return iter;
}
@Override
public Iterator<T> levelorder()
{
MyLinkedQueue<BTNode<T>> queue = new MyLinkedQueue<BTNode<T>>();
ArrayIterator<T> iter = new ArrayIterator<T>();
if (root != null)
{
queue.enqueue(root);
while (!queue.isEmpty())
{
BTNode<T> current = queue.dequeue();
iter.add (current.getElement());
if (current.getLeft() != null){
queue.enqueue(current.getLeft());
}
if (current.getRight() != null){
queue.enqueue(current.getRight());
}
}
}
return iter;
}
@Override
public Iterator<T> iterator()
{
return inorder();
}
@Override
public MyLinkedBinaryTree<T> getRight() {
if (root == null){
return null;
}
MyLinkedBinaryTree<T> result = new MyLinkedBinaryTree<T>();
result.root = root.getRight();
return result;
}
@Override
public boolean contains (T target) {
Iterator<T> iter = this.inorder();
while (iter.hasNext()){
if (target.equals(iter.next())){
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {return root==null; }
@Override
public String toString() {
Iterator iter = levelorder();
return iter.toString();
}
@Override
public Iterator<T> preorder() {
ArrayIterator<T> iter = new ArrayIterator<>();
if (root!=null){
root.preorder(iter);
}
return iter;
}
@Override
public Iterator<T> postorder() {
ArrayIterator<T> iter = new ArrayIterator<>();
if (root!=null){
root.postorder(iter);
}
return iter;
}
}
实现了LinkedBinaryTree的getRight,contains,toString,preorder,postorder等方法
实验二 中序先序序列构造二叉树
基于LinkedBinaryTree,实现基于(中序,先序)序列构造唯一一棵二㕚树的功能,比如教材P372,给出HDIBEMJNAFCKGL和ABDHIEJMNCFGKL,构造出附图中的树。用JUnit或自己编写驱动类对自己实现的功能进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息。课下把代码推送到代码托管平台
import java.util.HashMap;
public class exp2 {
final static char[] inorder = "HDIBEMJNAFCKGL".toCharArray();
final static char[] preorder = "ABDHIEJMNCFGKL".toCharArray();
public static void main(String[] args) {
MyLinkedBinaryTree<Character> root= new MyLinkedBinaryTree(genetree(preorder,inorder));
System.out.println(root);
}
public static BTNode genetree(char[] pre, char[] in) {
if (pre == null) return null;
HashMap<Character, Integer> map = geneMap(in);
return preIn(pre, 0, pre.length - 1, in, 0, in.length - 1, map);
}
public static HashMap<Character,Integer> geneMap(char[] charArray){
HashMap<Character,Integer> temp = new HashMap<>();
int len = charArray.length;
for (int i = 0; i < len;i++){
temp.put(charArray[i],i);
}
return temp;
}
public static BTNode preIn(char[] pre, int preindex, int pj, char[] in, int inindex, int nj, HashMap<Character, Integer> position) {
if (preindex > pj) return null;
BTNode head = new BTNode(pre[preindex]);
int index = position.get(pre[preindex]);
head.left = preIn(pre, preindex + 1, preindex + index - inindex, in, inindex, index - 1, position);
head.right = preIn(pre, preindex + index - inindex + 1, pj, in, index + 1, nj, position);
return head;
}
}
使用递归的方法,从根结点的两边开始构造子树,并如此循环。
实验二 决策树
完成PP16.6。提交测试代码运行截图,要全屏,包含自己的学号信息。课下把代码推送到代码托管平台
import java.util.Scanner;
public class BackPainExpert
{
private MyLinkedBinaryTree<String> tree;
//-----------------------------------------------------------------
// Sets up the diagnosis question tree.
//-----------------------------------------------------------------
public BackPainExpert()
{
String e1 = "一共有七种动物(小猫、小狗、小仓鼠、小鸭子、小鱼、小乌龟、大熊猫),\n你想好的动物能游泳吗?";
String e2 = "你想好的动物萌吗?";
String e3 = "你想好的动物能在水里生活吗?";
String e4 = "你想好的动物会中暑吗?";
String e5 = "你想好的动物灵活吗?";
String e6 = "你想好的动物能在陆地上生活吗?";
String e7 = "那一定是小鸭子吧🦆";
String e8 = "🐼PANDA";
String e9 = "🐱 喵喵喵~";
String e10 = "🐶汪汪汪";
String e11 = "🐭吱吱吱";
String e12 = "🐬";
String e13 = "🐢";
MyLinkedBinaryTree<String> n2, n3, n4, n5, n6, n7, n8, n9,
n10, n11, n12, n13;
n8 = new MyLinkedBinaryTree<String>(e8);
n9 = new MyLinkedBinaryTree<String>(e9);
n4 = new MyLinkedBinaryTree<String>(e4, n8, n9);
n10 = new MyLinkedBinaryTree<String>(e10);
n11 = new MyLinkedBinaryTree<String>(e11);
n5 = new MyLinkedBinaryTree<String>(e5, n10, n11);
n12 = new MyLinkedBinaryTree<String>(e12);
n13 = new MyLinkedBinaryTree<String>(e13);
n6 = new MyLinkedBinaryTree<String>(e6, n12, n13);
n7 = new MyLinkedBinaryTree<String>(e7);
n2 = new MyLinkedBinaryTree<String>(e2, n4, n5);
n3 = new MyLinkedBinaryTree<String>(e3, n6, n7);
tree = new MyLinkedBinaryTree<String>(e1, n2, n3);
}
//-----------------------------------------------------------------
// Follows the diagnosis tree based on user responses.
//-----------------------------------------------------------------
public void diagnose()
{
Scanner scan = new Scanner(System.in);
MyLinkedBinaryTree<String> current = tree;
System.out.println ("So, you're having back pain.");
while (current.size() > 1)
{
System.out.println (current.getRootElement());
if (scan.nextLine().equalsIgnoreCase("N"))
current = current.getLeft();
else
current = current.getRight();
}
System.out.println (current.getRootElement());
}
}
设计了一个猜小动物的游戏。
实验二 表达式树
完成PP16.8。提交测试代码运行截图,要全屏,包含自己的学号信息。课下把代码推送到代码托管平台。
import java.util.ArrayList;
import java.util.Stack;
public class ExpressionTree {
private String exp = "";
private BTNode root;
private Integer result;
public void geneTree(String s) {
ArrayList<String> oper = new ArrayList();
ArrayList<BTNode<String>> num = new ArrayList();
for (int i = 0; i < s.length(); i++) {
char c = s.toCharArray()[i];
if (c >= '0' && c <= '9') {
exp += c + " ";
} else {
num.add(new BTNode<String>(exp));
exp = "";
oper.add(c + " ");
}
}
num.add(new BTNode(exp));
while (oper.size() > 0) {
BTNode left = num.remove(0);
BTNode right = num.remove(0);
String o = oper.remove(0);
BTNode node = new BTNode(o, left, right);
num.add(0, node);
}
root = num.get(0);
}
public String toStr() {
this.s = "";
inorder(root);
return s;
}
private ArrayList<String> getStringList(String str) {
ArrayList<String> result = new ArrayList<String>();
String num = "";
for (String item : str.split(" ")) result.add(item);
return result;
}
//求逆波兰表达式
private ArrayList<String> getRPN(ArrayList<String> inOrderList) {
ArrayList<String> RPN = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
for (String item : inOrderList) {
if (Character.isDigit(item.charAt(0))) RPN.add(item);
else {
while (!stack.isEmpty() && compare(stack.peek(), item)) RPN.add(stack.pop());
stack.push(item);
}
}
while (!stack.isEmpty()) RPN.add(stack.pop());
return RPN;
}
//计算后缀表达式
private Integer calculate(ArrayList<String> RPN) {
Stack stack = new Stack();
for (String item : RPN) {
if (Character.isDigit(item.charAt(0))) stack.push(Integer.parseInt(item));
else {
Integer back = (Integer) stack.pop();
Integer front = (Integer) stack.pop();
Integer res = 0;
switch (item.charAt(0)) {
case '+': {
res = front + back;
break;
}
case '-': {
res = front - back;
break;
}
case '*': {
res = front * back;
break;
}
case '/': {
res = front / back;
break;
}
}
stack.push(res);
}
}
return (Integer) stack.pop();
}
//比较运算符等级
private static boolean compare(String peek, String cur) {
if ("*".equals(peek) && ("/".equals(cur) || "*".equals(cur) || "+".equals(cur) || "-".equals(cur))) {
return true;
} else if ("/".equals(peek) && ("/".equals(cur) || "*".equals(cur) || "+".equals(cur) || "-".equals(cur))) {
return true;
} else if ("+".equals(peek) && ("+".equals(cur) || "-".equals(cur))) {
return true;
} else if ("-".equals(peek) && ("+".equals(cur) || "-".equals(cur))) {
return true;
}
return false;
}
private ArrayList<String> getStringLst(String str) {
ArrayList<String> result = new ArrayList<String>();
String num = "";
for (String item : str.split(" ")) result.add(item);
return result;
}
public Integer getResult(){
ArrayList strList = getStringList(toStr());
ArrayList rpn = getRPN(strList);
return calculate(rpn);
}
private String s;
public void inorder(BTNode node) {
if (node.getLeft() != null) {
inorder(node.getLeft());
}
this.s += node.getElement();
if (node.getRight() != null) {
inorder(node.getRight());
}
}
}
public class ExpressionTreeTest{
public static void main(String[] args) {
ExpressionTree et = new ExpressionTree();
et.geneTree("1+1-5-6");
System.out.println(et.toStr());
System.out.println(et.getResult());
}
}
实验二 二叉搜索树
完成PP17.1。提交测试代码运行截图,要全屏,包含自己的学号信息。课下把代码推送到代码托管平台。
public class LinkedBinarySearchTree<T extends Comparable<T>>
extends MyLinkedBinaryTree<T> implements BinarySearchTree<T>
{
//-----------------------------------------------------------------
// Creates an empty binary search tree.
//-----------------------------------------------------------------
public LinkedBinarySearchTree()
{
super();
}
public LinkedBinarySearchTree (BSTNode node){
root = node;
}
//-----------------------------------------------------------------
// Creates a binary search tree with the specified element at its
// root.
//-----------------------------------------------------------------
public LinkedBinarySearchTree (T element)
{
root = new BSTNode<T>(element);
}
//-----------------------------------------------------------------
// Adds the specified element to this binary search tree.
//-----------------------------------------------------------------
public void add (T item)
{
if (root == null)
root = new BSTNode<T>(item);
else
((BSTNode)root).add(item);
}
//-----------------------------------------------------------------
// Removes and returns the element matching the specified target
// from this binary search tree. Throws an ElementNotFoundException
// if the target is not found.
//-----------------------------------------------------------------
public T remove (T target)
{
BSTNode<T> node = null;
if (root != null)
node = ((BSTNode)root).find(target);
if (node == null)
return null;
root = ((BSTNode)root).remove(target);
return node.getElement();
}
//-----------------------------------------------------------------
// The following methods are left as programming projects.
//-----------------------------------------------------------------
public T findMin() {
BTNode<T> temp = root;
while (temp.getLeft()!=null){
temp = temp.getLeft();
}
return temp.getElement();
}
public T findMax() {
BTNode<T> temp = root;
while (temp.getRight()!=null){
temp = temp.getRight();
}
return temp.getElement();
}
}
实现了findMin和findMax方法。
实验二 源码分析
参考http://www.cnblogs.com/rocedu/p/7483915.html对Java中的红黑树(TreeMap,HashMap)进行源码分析,并在实验报告中体现分析结果。
Entry<K, V> ceilingEntry(K key)
K ceilingKey(K key)
void clear()
Object clone()
Comparator<? super K> comparator()
boolean containsKey(Object key)
NavigableSet<K> descendingKeySet()
NavigableMap<K, V> descendingMap()
Set<Entry<K, V>> entrySet()
Entry<K, V> firstEntry()
K firstKey()
Entry<K, V> floorEntry(K key)
K floorKey(K key)
V get(Object key)
NavigableMap<K, V> headMap(K to, boolean inclusive)
SortedMap<K, V> headMap(K toExclusive)
Entry<K, V> higherEntry(K key)
K higherKey(K key)
boolean isEmpty()
Set<K> keySet()
Entry<K, V> lastEntry()
K lastKey()
Entry<K, V> lowerEntry(K key)
K lowerKey(K key)
NavigableSet<K> navigableKeySet()
Entry<K, V> pollFirstEntry()
Entry<K, V> pollLastEntry()
V put(K key, V value)
V remove(Object key)
int size()
SortedMap<K, V> subMap(K fromInclusive, K toExclusive)
NavigableMap<K, V> subMap(K from, boolean fromInclusive, K to, boolean toInclusive)
NavigableMap<K, V> tailMap(K from, boolean inclusive)
SortedMap<K, V> tailMap(K fromInclusive)
以上是TreeMap对外提供的API接口(来自:https://www.cnblogs.com/skywang12345/p/3310928.html),我们先尝试使用TreeMap,并观察其在内存中的存储方式。
TreeMap<Integer, String> tmap = new TreeMap<Integer, String>();
tmap.put(1, "语文");
tmap.put(3, "英语");
tmap.put(2, "数学");
tmap.put(4, "政治");
tmap.put(5, "历史");
tmap.put(6, "地理");
tmap.put(7, "生物");
tmap.put(8, "化学");
for(Entry<Integer, String> entry : tmap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
使用单步跟踪,查看tmap的存储。
阅读上述代码中所用到的put方法。
public V put(K key, V value) {
TreeMapEntry<K,V> t = root;
if (t == null) {//检查当前树中是否存在一个结点
if (comparator != null) {//如果用户提供了comparator
if (key == null) {
comparator.compare(key, key);//在key为空值时,根据comparator比较key
}
} else {
if (key == null) {
throw new NullPointerException("key == null");//如果用户没有提供comparator,且key值为空,则抛出空指针异常
} else if (!(key instanceof Comparable)) {
throw new ClassCastException(
"Cannot cast" + key.getClass().getName() + " to Comparable.");
}//如果用户没有提供comparator,且key对象没有实现Comparable接口,抛出异常
}
root = new TreeMapEntry<>(key, value, null);//完成类型检查后,新建根节点
size = 1;//此时TreeMap的size显然为1
modCount++;
return null;
}
int cmp;
TreeMapEntry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
//如果用户提供了comparator,并从根节点开始,寻找新插入的节点合适的位置,如果这个位置已经有节点,则更新值,否则新建一个节点。
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
//如果用户没有提供comparator,若key没有实现comparable接口或key为空,则抛出异常,否则,从根节点开始,寻找新插入的节点合适的位置,如果这个位置已经有节点,则更新值,否则新建一个节点。
}
TreeMapEntry<K,V> e = new TreeMapEntry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
//调整二叉树的平衡性
size++;
modCount++;
return null;
}
在上述的代码中,我插入了注释,主要的思路就是如果存在old value就更新值,不存在就新建一个节点,最后调整红黑树的平衡性。下面是get方法的源码分析,get方法主要依赖于getEntry方法,因此我对getEntry方法也进行了分析
final TreeMapEntry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);//这个方法原理上和下面的遍历一样,就是使用了用户提供的comparator对key进行比较
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
TreeMapEntry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}//根据顺序,对数进行遍历
return null;
}
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);//如果p为空,则返回null,否则返回entry内的value
}