简体   繁体   中英

I can iterate over more than three elements in my BinarySearchTree and can't iterate through them in the right order

I copied exactly the BinarySearchTree class from the java textbook data structures third ed. by william collins. I went to try and implement the tree in an address book class, but when I add elements and try to iterate through them, the embedded iterator class in the BinarySearchTree class and some methods it uses only shows three: the root and its two children for some reason. It also may be a problem with the add method but I know that at least the code in each loop in the add method for adding entries always executes because the size of the tree is incremented each time I add any entry, only like I said, when I try and iterate through the tree, no matter how many elements I tried to add (which is correctly represented when the size method is called) only the root and it's two children are printed out. I made sure multiple times all the methods I have been using in the program are exactly the same as the ones the textbook has in the binarysearchtree class it gives us. I have been reviewing them for hours trying to see what is wrong, but everything I have seems to me like it should work, I don't know why it is not. Can some one please help me?

Thank you!

Here is the Binary Search Tree Class

package cs302project1;


import java.util.AbstractSet;
import java.util.Iterator;
import java.util.NoSuchElementException;


public class BinarySearchTree<E> extends AbstractSet<E> {

    protected Entry<E> root;

    protected int size;

    public BinarySearchTree(){
        root=null;
        size=0;
    }

    public BinarySearchTree(BinarySearchTree<? extends E> otherTree){
        root=copy(otherTree.root,null);

    }

    //recursive copy method
    protected Entry<E> copy(Entry<? extends E> p,Entry<E> parent){
        if(p!=null)
        {
            Entry<E> q=new Entry<E>(p.element,parent);
            q.left=copy(p.left,q);
            q.right=copy(p.right,q);
            return q;
        }
        return null;
    }

    //size method
    public int size(){
        return size;
    }

    //iterator method
    public Iterator<E> iterator() {
        return new TreeIterator();
    }

    public boolean add(E element) {
        if(root==null){
            root=new Entry(element,null);
            size++;
            return true;
        } else {
            Entry<E> temp=root;
            int comp;
            while(true){
                comp=((Comparable)element).compareTo(temp.element);
                if(comp==0)
                    return false;
                if(comp<0){
                    if(temp.left!=null) 
                        temp=temp.left;
                    else{
                        temp.left=new Entry<E> (element,temp);
                        size++;
                        return true;                        
                    }
                } else if (temp.right!=null)
                    temp=temp.right;
                    //here theres another else im not sure what it does
                    //oh now I see make temp.right a new entry
                    else
                        temp.right=new Entry<E> (element,temp);
                        size++;
                        return true;
                }
            }
        }

    public boolean contains(Object obj) {
        /*
        Entry<E> temp = root;
        int comp;
        if(obj==null)
            throw new NullPointerException();
        while (temp != null){
            comp = ((Comparable)obj).compareTo(temp.element);
            if (comp == 0)
                return true;
            if(comp<0)
                temp=temp.left;
            else
                temp=temp.right;
        }
        return false;
        */

        //the above is the original contains but it is the exact same method
        //as the get entry, so I can rewrite it now in one line

        return getEntry(obj)!=null;
    }


    protected Entry<E> successor(Entry<E> e){
        if(e==null) 
            return null;
        else if (e.right!=null){
            //in this case successor(next element in an inorder traversal) is          the 
            //left most node in the right subtree of e
            Entry<E> p = e.right;
            while(p.left!=null)
                p=p.left;
            return p;
        }
        //if there is no right child than go up tree to left as far as can go 
        //then go to parent
        else{
            Entry<E> p=e.parent;
            Entry<E> ch=e;
            while(p!=null&&ch==p.right){
                ch=p;
                p=p.parent;
            }
            return p;
        }
    }

    //method delete entry 
    //deletes a certain Entry
    protected Entry<E> deleteEntry (Entry<E> p){
        size--;
        //if p has two elements than replace ps element with successors element than make p a reference 
        //to that successor
        //is needed for remove method also used in iterator class
        if(p.left!=null&&p.right!=null){
            Entry<E> s=successor(p);
            p.element=s.element;
            p=s;
        }
         //here p has no children or one child
        Entry<E> replacement;
        if(p.left!=null)
            replacement=p.left;
        else
            replacement=p.right;
        // so if there was a child than link replacement to parent
        if(replacement!=null)
        {
            replacement.parent=p.parent;
            if(p.parent==null)
                root=replacement;
            else if(p==p.parent.left)
                p.parent.left=replacement;
            else
                p.parent.right=replacement;
        }
        //if theres no children and the only element is the root, just set root to null
        else if(p.parent==null)
            root=null;
        //otherwise just set the left or right in parent in the entry to null
        else
        {
            if(p==p.parent.left)
                p.parent.left=null;
            else
                p.parent.right=null;
        }
        return p;
    }

    //get entry is needed for remove method
    public Entry<E> getEntry(Object obj) {
        Entry<E> e = root;
        int comp;
        if(obj==null)
            throw new NullPointerException();
        while (e != null){
            comp = ((Comparable)obj).compareTo(e.element);
            if (comp == 0)
                return e;
            else if(comp<0)
                e=e.left;
            else
                e=e.right;
        }
        return null;
    }

    //the remove method
    //needs to use the getEntry and delete entry methods
    public boolean remove(Object obj){
        Entry<E> e=getEntry(obj);
        if(e==null)
            return false;
        deleteEntry(e);
        return true;
    }

    protected static class Entry<E>  {
        protected E element;
        protected Entry<E> left = null, 
                right = null, 
                parent;
        // Initializes this Entry object from element
        // and parent.
        public Entry(E element, Entry<E> parent) {
            this.element = element;
            this.parent = parent;
        } // constructor
        } // class Entry

    //embedded tree iterator
    protected class TreeIterator implements Iterator<E>{
        protected Entry<E> lastReturned=null;
        protected Entry<E> next;
        protected TreeIterator(){
            next=root;
            if(next!=null){
                while(next.left!=null)
                    next=next.left;
            }
        }

        @Override
        public boolean hasNext() {
            // TODO Auto-generated method stub
            return next!=null;
        }

        @Override
        public E next() {
            if (next==null)
                throw new NoSuchElementException();
            lastReturned=next;
            next=successor(next);
            return lastReturned.element;
        }

        //method remove
        //removes the element returned from the most recent call to the next method
        public void remove(){
            if (lastReturned==null)
                throw new IllegalArgumentException();
            if(lastReturned.left!=null&&lastReturned.right!=null)
                next=lastReturned;
            //delete entry last returned
        }
    }

}

Here is the address book class

package cs302project1;

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

import cs302project1.BinarySearchTree.TreeIterator;

public class AddressBook {

    //fields

    //data structure for people

    BinarySearchTree addressBook=new BinarySearchTree();

    ArrayList<Person> list=new ArrayList<Person>();


    //methods

    //enter
    public void enter(String name,String address,String phoneNumber){
        Person person=new Person(name,address,phoneNumber);
        addressBook.add(name);
        list.add(person);
    }

    //delete
    public void delete(String name){
        if(addressBook.contains(name)){
            addressBook.remove(name);
        }
        for(Person person:list){
            if(person.getName().equals(name)){
                list.remove(person);
            }
        }
    }
    //modify
    public void mod(String oldName,String newName,String newAddress,String newPhoneNumber){
        addressBook.remove(oldName);
        for(Person person:list){
            if(person.getName().equals(oldName)){
                list.remove(person);
            }
        }
        Person change=new Person(newName,newAddress,newPhoneNumber);
        addressBook.add(newName);
        list.add(change);
    }
    //search
    public String search(String name){
        TreeIterator itr=(TreeIterator) addressBook.iterator();
        /*
         while(itr.hasNext()){
                String current=(String) itr.next();
                System.out.println(current);
         }
         System.out.println(addressBook.size());
         //*/
        /*
        for(Person person:list)
             System.out.println(person.toString());
        */
        if(addressBook.contains(name)){
            for(Person person:list){
                if(person.getName().equals(name)){
                    return person.toString();
                }
            }
        }
        return null;
    }

    //write to file
    public void write() throws IOException{

        TreeIterator itr=(TreeIterator) addressBook.iterator();
        PrintWriter out = new PrintWriter(new FileWriter("Address Book.txt"));

        out.println("tst");
        out.println(addressBook.size);
        while(itr.hasNext()){
            String current=(String) itr.next();
            /*
            for(Person p:list){
                if(p.getName().equals(current)){
                    out.println(p.toString()+"\n");
                }
            }
            */
            out.println(current);
        }
        out.close();
    }



}

for example, if I add h, f, i and u than try iterate over them and call the size method. The size is four but the only elements that are printed are f, h and i

another example: I add h, f, e and than try to iterate, only f and then h are printed although the size returned is three. It is the same for any entry that extends past the right child of the root: it is not shown.

There are also two other classes in my program I use, I don't think them relevant to my question but I'll include them anyway

package cs302project1;

import java.io.IOException;
import java.util.Scanner;

public class AddressBookDriver {

    //scanner
    Scanner scan=new Scanner(System.in);

    //help section
    private static final String cmdList="MAKE SURE YOU SPELL THE COMMANDS THE SAME WAY I DO\n"
            + "type enter to add a new entry\ntype del to delete an entry\ntype mod to modify"
            + " an entry\ntype search to search for an entry\ntype write to write the address book to a file";
    //addressBook
    private AddressBook addressBook = new AddressBook();

    public static void main(String[] args){
        AddressBookDriver driver=new AddressBookDriver();
        driver.run();
    }

    public void run(){
        userInterface();
    }

    //with scanner get commands from user and execute proper methods
    //to add to or edit the binary search tree

    //add the information to it with the scanner too
    public void userInterface(){
        String cmd="";
        while(cmd!="quit"){
            System.out.println("Enter a command (help for command list)");
            cmd=scan.nextLine();
            if(cmd.equals("help"))
                System.out.println("\n"+cmdList);
            else if(cmd.equals("enter")){
                String name,address,phone;
                System.out.println("\nenter a name");
                name=scan.nextLine();
                System.out.println("\nenter an address");
                address=scan.nextLine();
                System.out.println("\nenter a phone number");
                phone=scan.nextLine();
                addressBook.enter(name,address,phone);
            } else if(cmd.equals("del")){
                System.out.println("Type the name of the person you wish to delete");
                String name=scan.nextLine();
                addressBook.delete(name);
            } else if(cmd.equals("search")){
                System.out.println("type the name of the person you wish to search");
                String name=scan.nextLine();
                System.out.println(addressBook.search(name));
            } else if(cmd.equals("mod")){
                System.out.println("enter the name of a person whose information you wish to modify");
                String oldName,newName,newAddress,newPhoneNumber;
                oldName=scan.nextLine();
                System.out.println("enter a new a new name");
                newName=scan.nextLine();
                System.out.println("enter a new address");
                newAddress=scan.nextLine();
                System.out.println("enter a new phone number");
                newPhoneNumber=scan.nextLine();
                addressBook.mod(oldName, newName, newAddress, newPhoneNumber);
            } else if(cmd.equals("search")){
                System.out.println("type the name of person to search for");
                String name=scan.nextLine();
                addressBook.search(name);
                if(name==null)
                    System.out.println("\nThat name is not in the address book");
            } else if(cmd.equals("write")){
                try {
                    addressBook.write();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
    }

}

package cs302project1;

public class Person {

    //fields

    //name

    public String name;
    //address
    public String address;
    //phone number
    public String phoneNumber;

    //methods

    public Person(String name,String address,String phoneNumber){
        this.name=name;
        this.address=address;
        this.phoneNumber=phoneNumber;
    }

    //getname
    public String getName(){
        return name;
    }

    //tostring
    public String toString(){
        return "name: "+name+"\naddress: "+address+"\nphone number: "+phoneNumber;
    }

}

I dropped all lines not essential for the problem:

public boolean add(E element) {
    if(root==null){
    } else {
        while(true){
            if(comp<0){
            } else if (temp.right!=null)
                temp=temp.right;
                else
                    temp.right=new Entry<E> (element,temp);
                    size++;
                    return true;
            }
        }
    }

Have a close look at the last else: Missing parentheses! So it actually behaves as:

public boolean add(E element)
{
    if(root==null)
    {
    }
    else
    {
        while(true)
        {
            if(comp < 0)
            {
            }
            else if (temp.right!=null)
            {
                temp=temp.right;
            }
            else
            {
                temp.right=new Entry<E> (element,temp);
            }
            /**/size++;
            /**/return true;
            } // bad indentation!
        } // bad indentation!
    } // bad indentation!

Compare it with the if(comp < 0) part in your code, I think you will get yourself then what goes wrong...

The last five lines of this method are not correctly indented, I restored it for the size++; return true; size++; return true; via a comment to make you see and left it as was for the closing braces.

Correct indentation could have hinted you already. If you ever run into such problems again, try to use your IDE's formatting function, if anything changes, have a close look, there could be some hints hidden (like size++; return true; getting one level less of indentation).

Some consider it good practice to always place braces around single line blocks of control flow instructions, even if allowed to leave them out (MISRA, for example, even mandates it - OK, it is for C and C++, but you have the identical issue in these languages, too).

I personally prefer the Allman style even for Java code (placing all braces on separate lines - see my second code example). I know, Java convention is different (advantage: you need less lines of code), but I weigh the Allman's style advantage (more easily detecting matching braces) stronger. I know, I'm stirring up a hornets' nest now, people were battling and still are today, which is the best style. So I don't recommend here any one as the better one, just show you an alternative. Decide yourself, you're free to unless your company/client/... imposes you a specific style or you'd be breaking style of an already existing code base.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM