-2

I've been working on trying to plan out how to delete words from a trie. I have an implementation that uses a one-dimensional array on the node which holds the next characters for a word. I understand how I can get rid of a whole word but not account for larger words that contain the smaller word to be removed so trying to delete "bat", "battle", "as" and "any" from the trie below (* indicates end of word) and leaving "battery", "battlefield", "ask" and "anywho":

    root 
    / \
   a   b-a-t*-t-e-r-y*
  / \         |
 n   s*-k*    l-e*-f-i-e-l-d*
 |
 y*-w-h-o*

Below is the trie I have implemented thus far:

public class TrieNode {

    protected char letter = ' ';
    protected TrieNode parentNode = null;
    protected boolean fullWord = false;
    protected TrieNode[] children = new TrieNode[26];

    public TrieNode(char letter, TrieNode parentNode){
        this.letter = letter;
        this.parentNode = parentNode;
    }

    public boolean hasChildren(){
        int index = 0;

        while(index < children.length){
            if(children[index] != null) {
                return true;
            }
            index++;
        }
        return false;
    }

    public TrieNode nodeForLetter(char ch) {
        return children[ch - 97];
    }

    public boolean isEndOfWord() {
        return fullWord;
    }
}

public class Trie implements Iterable<String> {

    private int numOfNodes;
    private int numOfWords;
    private TrieNode root = new TrieNode(' ', null);

    public Trie() {
    }

    public void addWord(String s) {
        if (hasWord(s)) return;

        int index = 0;
        TrieNode iterator = root;

        while(index < s.length()){
            if(iterator.children[s.charAt(index) - 97] == null){
                iterator.children[s.charAt(index) - 97] = new TrieNode(s.charAt(index), iterator);
                numOfNodes++;
            }

            iterator = iterator.children[s.charAt(index) - 97];

            index++;

            if(index == s.length()){
                iterator.fullWord = true;
                numOfWords++;
            }
        }
    }

    // Issues on this one 
    public void deleteWord(String s) {
        if(s.length() == 0) return; 
        // make method to check for empty trie
        else if(!(hasWord(s))) return;
        else {
            TrieNode iterator = root;
            int index = 0;

            while(index < s.length()){
                if(iterator.children[index] != null){
                    /* What would (in pseudo code) need to be put here to account for this */
                }
            }
        }
    }

    public boolean hasWord(String s) {
        TrieNode current = root;

        while(current != null){
            for (int i = 0; i < s.length(); i++) {
                if(current.letter == ' ') return false; // error here probably
                else current = current.children[i];
            }
            if (current.fullWord == true) return true;
            else return false;
        }
        return false;
    }

    public Iterator<String> iterator() {
        return new TrieIterator();  // has basic iterator functions
    }
}

any ideas?

user3362954
  • 1,221
  • 2
  • 15
  • 23
  • So if I understand correctly, if I say delete "bat" I only want to detele "bat" and not "battery" or "combat" or "bats"? – Marshall Tigerus Apr 22 '14 at 17:49
  • Robert Sedgewick gives a bare bones implementation in his book. I recommend reading that chapter and look at [his code](http://algs4.cs.princeton.edu/52trie/). – Giovanni Botta Apr 22 '14 at 17:52
  • Your implementation approach makes this overly complicated. If you used a `Map` instead of a fixed array the problem is much easier (and not restricted to US_ASCII). That said, the way you have things you simply have to check for children and flip the `fullword` indicator if the word you're deleting has children. – Brian Roach Apr 22 '14 at 18:00
  • @MarshallTigerus That is correct. – user3362954 Apr 22 '14 at 21:47
  • @BrianRoach I want to keep the array implementation. – user3362954 Apr 22 '14 at 21:48
  • Looking at this code ... none of it actually works. This revolves around your `hasWord()` method being broken since adding and deleting calls it (which is redundant in the first place since if that method were correct, that's the iteration you need to perform to both operations). I would highly suggest getting inserts to work first before asking how to do deletes. – Brian Roach Apr 22 '14 at 21:58
  • @BrianRoach How would you suggest that? I've been working on this for over a week, and was under the impression that it was working. And are you saying `hasWord` or 'insert' is broken. Your comment was unclear to me. – user3362954 Apr 22 '14 at 22:08

1 Answers1

1
public class TrieNode {

// a letter=='*' means end of word
// a letter=='\0x' means root node
protected char letter = ' ';
protected TrieNode[] children = new TrieNode[26];

public TrieNode(char letter){
    this.letter = letter;
}

public boolean hasChildren(){
    int index = 0;

    while(index < children.length){
        if(children[index] != null) {
            return true;
        }
        index++;
    }
    return false;
}

// don't understand the purpose of this 
public TrieNode nodeForLetter(char ch) {
    return children[ch - 97];
}

public boolean isEndOfWord() {
    return letter == '*';
}

public void deleteWord( String wordToBeDeleted )
{
     // append an asterisk will delete the end of 
     // the word without any special programming
     realDeleteWord( wordToBeDeleted + "*" ); 
}  // deleteWord

// this function will return true if this 
// object has to be deleted 
private boolean realDeleteWord( String wordToBeDeleted )
{
    if( wordToBeDeleted.isEmpty() ) 
        return; // simplest case 
    else
    {
       // split the word in the first character and the rest of 
       // string 
       char firstChar = wordToBeDeleted.charAt(0);
       String restOfWord = substr( wordToBeDeleted, 1, rest_of_string );

       // first you have to treat the rest of the word
       realDeleteWord( restOfWord )

       // next you have to treat the first char 
       // if I don't have any children, I have to delete myself
       if( ! hasChildren() && firstChar == letter ) 
       {
           // PENDING: THIS VERY OBJECT HAS TO BE DELETED, 
           // HOW TO IMPLEMENT THIS? RETURN TRUE TO INFORM THE PARENT?
           return true; 
       }
       else
       {
          // DO NOTHING: either there are children or 
          // this character is not the same as the one 
          // given as parameter
       } // hasChildren()...
    } 
} // realDeleteWord

}

Mmmmm... typical exercise of a programming class, is this the case? Remember, to recurse is divine, to iterate is human.

Raul Luna
  • 1,945
  • 1
  • 17
  • 26