Dijkstra’s Single Source Shortest Path Algorithm in Java and DFS/BFS

I find that there are not a lot of good examples of this with heaps so here is my implementation as a coding example (in java).

First let me define the underlying Vertex/Edge data structure with the following classes:

public class Edge{
    public double weight;
    public Vertex destination;

    public Edge(Vertex destination, double weight) {
        this.destination = destination;
        this.weight = weight;
    }
}

public class Vertex implements Comparable<Vertex>{

    public String name;
    public ArrayList<Edge> adjacentEdges; //list of outgoing edges
    public Double minDistance = Double.POSITIVE_INFINITY; //used for SSP

    public Vertex(String name) {
        this.name = name;
        this.adjacentEdges = new ArrayList<Edge>();
    }

    public void addEdge(Vertex dest, double cost){
        adjacentEdges.add(new Edge(dest, cost));
    }

    @Override
    public int compareTo(Vertex o) {
        return (int)(minDistance - o.minDistance); //to be used for heaps
    }
}

The graph class creates an adjacency list for nodes provided and a hashmap to prevent node duplication.

public class Graph {

    public ArrayList<Vertex> adjacenyList;
    HashMap<String, Vertex> vertexMap;

    public Graph() {
        this.adjacenyList = new ArrayList<Vertex>();
        vertexMap = new HashMap<String, Vertex>();
    }

    public void addNodes(String vertex, String destination, double distance){
        //check if vertex already exists
        Vertex src = getNodeOrCreate(vertex);
        Vertex dest = getNodeOrCreate(destination);
        //add edge to vertex
        src.addEdge(dest, distance);
    }

    private Vertex getNodeOrCreate(String node){
        Vertex v;
        if(vertexMap.containsKey(node)){
            v = vertexMap.get(node);
        }else{
            v = new Vertex(node);
            vertexMap.put(node, v);
            adjacenyList.add(v);
        }
        return v;
    }

    public Vertex getVertexByName(String name){
        return vertexMap.get(name);
    }

    public void printAdjacencyList(){
        for(Vertex v : adjacenyList){
            System.out.print("Vertex: " + v.name);
            for(Edge e : v.adjacentEdges){
                System.out.print(" " + e.destination.name + "/" + e.weight);
            }
            System.out.println();
        }
    }

    //algorithms

    public ArrayList<Vertex> depthFirstSearch(Vertex start) {
        Vertex head = start;
        Stack<Vertex> stack = new Stack<Vertex>();
        ArrayList<Vertex> result = new ArrayList<Vertex>(); //visited
        stack.push(head);
        while (!stack.isEmpty()) {
            head = stack.pop();
            if (!result.contains(head)) {
                result.add(head);
                for (Edge e : head.adjacentEdges) {
                    stack.push(e.destination);
                }
            }
        }
        return result;
    }

    public ArrayList<Vertex> breadthFirstSearch(Vertex start){
        Queue<Vertex> queue = new LinkedList<Vertex>();
        ArrayList<Vertex> result = new ArrayList<Vertex>(); //visited
        Vertex head = start;
        queue.add(head);
        result.add(head);
        while(!queue.isEmpty()){
            head = queue.poll();
            for (Edge e : head.adjacentEdges){
                if(!result.contains(e.destination)){
                    queue.add(e.destination);
                    result.add(e.destination);
                }
            }
        }
        return result;
    }

    public Double djksraShortestPath(Vertex src, Vertex dest){
        //reset mins
        resetMins();
        PriorityQueue<Vertex> heap = new PriorityQueue<Vertex>();
        //init all edges as infinity
        src.minDistance = 0.0;
        for(Vertex v : adjacenyList){
            heap.add(v);
        }

        Vertex next;
        while(!heap.isEmpty()){
            next = heap.poll(); //extract min
            for(Edge e : next.adjacentEdges){
                Double cost = next.minDistance + e.weight;
                if(cost < e.destination.minDistance){
                    e.destination.minDistance = cost;
                }
            }
        }
        return dest.minDistance;
    }
    public Double djksraShortestPath(String src, String dest){

        return djksraShortestPath(getVertexByName(src), getVertexByName(dest));
    }

    private void resetMins(){
        for(Vertex v : adjacenyList){
            v.minDistance = Double.POSITIVE_INFINITY;
        }
    }
}

Its always important to use test frameworks to test code, but since this is a simple console application I do all the verification in main.


    public static void complicatedGraph(){
        //complicated graph
        Graph weightedGraph = new Graph();
        weightedGraph.addNodes("A","B", 20);
        weightedGraph.addNodes("A","D", 80);
        weightedGraph.addNodes("A","G", 90);
        weightedGraph.addNodes("B","F", 10);
        weightedGraph.addNodes("E","B", 50);
        weightedGraph.addNodes("E","G", 30);
        weightedGraph.addNodes("G","A", 20);
        weightedGraph.addNodes("D","G", 20);
        weightedGraph.addNodes("D","C", 10);
        weightedGraph.addNodes("F","D", 40);
        weightedGraph.addNodes("F","C", 10);
        weightedGraph.addNodes("C","F", 50);
        weightedGraph.addNodes("C","D", 10);
        weightedGraph.addNodes("C","H", 20);
        weightedGraph.printAdjacencyList();

        ArrayList<Vertex> bfs = weightedGraph.breadthFirstSearch(weightedGraph.getVertexByName("A"));
        ArrayList<Vertex> dfs = weightedGraph.depthFirstSearch(weightedGraph.getVertexByName("A"));
        System.out.print("BFS ");
        printVertexList(bfs);
        System.out.print("DFS ");
        printVertexList(dfs);
        
        System.out.println("");
        System.out.println("Shortest distance between A and H is " + weightedGraph.djksraShortestPath("A", "H"));
        System.out.println("Shortest distance between A and E is " + weightedGraph.djksraShortestPath("A", "E"));
        System.out.println("Shortest distance between D and H is " + weightedGraph.djksraShortestPath("D", "H"));

    }

    public static void simpleGraph(){
        Graph graph = new Graph();
        graph.addNodes("A","B", 1);
        graph.addNodes("A","D", 1);
        graph.addNodes("A","G", 1);
        graph.addNodes("B","E", 1);
        graph.addNodes("B","F", 1);
        graph.addNodes("E","B", 1);
        graph.addNodes("E","G", 1);
        graph.addNodes("G","E", 1);
        graph.addNodes("G","A", 1);
        graph.addNodes("D","A", 1);
        graph.addNodes("D","F", 1);
        graph.addNodes("F","B", 1);
        graph.addNodes("F","C", 1);
        graph.addNodes("C","F", 1);
        graph.addNodes("C","H", 1);
        graph.addNodes("H","C", 1);
        graph.printAdjacencyList();
        ArrayList<Vertex> bfs = graph.breadthFirstSearch(graph.getVertexByName("A"));
        ArrayList<Vertex> dfs = graph.depthFirstSearch(graph.getVertexByName("A"));
        System.out.print("BFS ");
        printVertexList(bfs);
        System.out.print("DFS ");
        printVertexList(dfs);

        System.out.println("Shortest distance between A and H is " + graph.djksraShortestPath("A", "H"));
    }

    public static void main(String [ ] args){
        simpleGraph();
        complicatedGraph();
    }

The output:

Simple Graph
Vertex: A B/1.0 D/1.0 G/1.0
Vertex: B E/1.0 F/1.0
Vertex: D A/1.0 F/1.0
Vertex: G E/1.0 A/1.0
Vertex: E B/1.0 G/1.0
Vertex: F B/1.0 C/1.0
Vertex: C F/1.0 H/1.0
Vertex: H C/1.0
BFS A B D G E F C H
DFS A G E B F C H D
Shortest distance between A and H is 4.0
Complicated Graph
Vertex: A B/20.0 D/80.0 G/90.0
Vertex: B F/10.0
Vertex: D G/20.0 C/10.0
Vertex: G A/20.0
Vertex: F D/40.0 C/10.0
Vertex: E B/50.0 G/30.0
Vertex: C F/50.0 D/10.0 H/20.0
Vertex: H
BFS A B D G F C H
DFS A G D C H F B
Shortest distance between A and H is 60.0
Shortest distance between A and E is Infinity
Shortest distance between D and H is 30.0

Word Prediction App Part 2: Adding Some O(nlogn) Searching to the List Filter.

The previous list filter utilized brute force in effort to focus on figuring out how Apple designed its MVC structure. Part 2 of the code only changes the model.

Here is the header:


@property (nonatomic, readwrite, strong) NSArray *tableData;
@property (nonatomic, readwrite, strong) NSArray *filteredList;

- (NSArray *)sortWithSquence:(NSString *)sequence;
- (NSArray *)getSubArray:(NSArray *)array bySeq:(NSString *)charSeq;
- (NSInteger) binarySearchForMutableStringArray:(NSArray *)array bySeq:(NSString *)charSeq findLast:(BOOL)last;

I made the arrays normal NSArrays as NSMutableArrays should only really be used if the model data is going to be changed. Since the idea is just to create smaller subarrays, NSArrays are more suitable.

Here is the main code now:


#import "ListModel.h"

@implementation ListModel

@synthesize tableData = _tableData;
@synthesize filteredList = _filteredList;

- (id) init
{
    if(self == [super init])
    {
        NSLog(@"Creating table data");
        
        // Path to the plist (in the application bundle)
        NSString *path = [[NSBundle mainBundle] pathForResource:
                          @"words" ofType:@"plist"];
        
        // Build the array from the plist
        _tableData  = [[NSArray alloc] initWithContentsOfFile:path];
        
        NSLog(@"Created %ld rows", _tableData.count);
    }
    return self;
}


- (NSArray *)sortWithSquence:(NSString *)sequence
{
    if([sequence isEqualToString:@""]) //if no filter, return the regular list
    {
        return _tableData;
    }
    return [self getSubArray:_tableData bySeq:sequence];
}

- (NSArray *)getSubArray:(NSArray *)array bySeq:(NSString *)charSeq
{
    NSInteger first = [self binarySearchForMutableStringArray:array bySeq:charSeq findLast:NO];
    NSInteger last = [self binarySearchForMutableStringArray:array bySeq:charSeq findLast:YES];
    if(last-first >= 0 && (last != -1 || first != -1))
    {
        NSLog(@"Range (%ld, %ld)", first, last);
        return [array subarrayWithRange:NSMakeRange(first, last-first+1)];
    }
    else
    {
        NSLog(@"No matches found");
        return nil;
    }
}

//binary search implemented with character sequences
- (NSInteger) binarySearchForMutableStringArray:(NSArray *)array bySeq:(NSString *)charSeq findLast:(BOOL)last
{
    NSInteger low = 0;
    NSInteger high = [array count];
    NSInteger result = -1;
    while(low <= high)
    {
        NSInteger mid = (low + high)/2;
        NSString *midObj = [array objectAtIndex:mid]; // get middle
        NSComparisonResult compare = [midObj compare:charSeq options:NSCaseInsensitiveSearch range:NSMakeRange(0, charSeq.length)];
        if(compare == NSOrderedAscending)
        {
            low = mid + 1;
        }
        else if(compare == NSOrderedDescending)
        {
            high = mid - 1;
        }
        else
        {
            result = mid; // key found
            if(last)
            {
                low = mid + 1;
            }
            else
            {
                high = mid - 1;
            }
        }
    }
    return result; //no occurance
}


@end

To move away from the ugly brute force method, I utilized the binary search algorithm. The idea is to find the first occurrence of a character sequence, find the last, determine the range and give back a new spliced array pointer. Using ranges, we can use various points to just point to different sections of the array. This takes away the need to create any new arrays.

In part 3, I will optimize the list by storing the filtered values as the character sequence increases. For example

“A” will provide a list of all words that start with “A”. When the user types “AP”, I will have the algorithm index the previously spliced list to increase the speed at which it finds the next character sequence. If the user hits backspace, the previous list can be provided if available in memory, if not then the algorithm will have to re-index the main list. This will optimize performance and speed as the user gets closer and closer to the word they wish to type.

Part 4 will then look into using Core Data to better store the pre-processed data and finally part 5 will use the list filter logic to create a fast word predictor.

Word Prediction App Part 1b: Morphing the TableView Application to a Word Prediction App (Some ideas)

In the previous application I wrote, I explored the apple IOS GUI and MVC structure. To take the app further I decided it would be cool to turn it into a word prediction application.

There are 2 parts to predicting words, 1 is determining which word could fill in the sequence and 2 is learn it. Now learning algorithms and providing training data is a whole other game and field but definitely something I look forward to trying in the future. For now the goal is simple, use the character sequence provided in our “filter” to organize our words by best fit. The current plist I have contains approximately 200,000 words. Now according to some google searching, there are over a million to actually choose from so the app maybe a bit behind in word choices. Nevertheless it is still a fun task to try!

So, how what do we do once we have a character sequence provided? Well here is how I think it could work.

If we have a character sequence “TH” we want to filter our list to a sub-list that only contains the characters TH as a prefix.

Here is some sample code:

- (NSMutableArray *)sortWithSquence:(NSString *)sequence
{
    if([sequence isEqualToString:@""]) //if no filter, return the regular list
    {
        return _tableData;
    }

    sequence = [sequence uppercaseString]; //the sequence will be case sensitive and the list is all uppercase
    //remake the filtered list
    [_filteredList removeAllObjects];

    for(NSString *str in _tableData) //loop through entire set each time for now ..
    {
        if([str hasPrefix:sequence]) //create a sub group of words that contain the prefix
        {
            [_filteredList addObject:str];
        }
    }
    return _filteredList;
}

Now the boring thing to do would be to return the top 3 elements in the new filtered list and offer them as options. When searching for “the” the top 3 searches are “the”, “thea”, “theacea”.

Now I don’t think these are really common options and since I do not have commonality statistics to sort by I am going to only fine elements that contain the sequence and are of the same length of the sequence. If no words are of the same length I will add 1 to the length and look again. I will repeat the process until I find words that fall under the shortest length and fill a mini group of 3. In theory this should help me find words like “then” and “their”.

Now in the following code you will notice I first create a sub-list then perform the little algorithm over the sub-list. I will later have my list organized into subarrays based on the first letter. This will greatly reduce sorting through unwanted areas. Ideally there will be a sub-array for each letter of the alphabet and the words will be sorted by shortest to longest length for ease of prediction.

- (NSMutableArray *)sortWithSquence:(NSString *)sequence
{
    if([sequence isEqualToString:@""]) //if no filter, return the regular list
    {
        return _tableData;
    }

    sequence = [sequence uppercaseString];
    NSUInteger lenCompare = [sequence length]; //get an inital length and add one to predict next possible word
    NSMutableArray *tempFilteredList = [[NSMutableArray alloc] init];
    //remake the filtered list
    [_filteredList removeAllObjects];

    for(NSString *str in _tableData) //loop through entire set each time for now ..
    {
        if([str hasPrefix:sequence])
        {
            [tempFilteredList addObject:str];
        }
    }

    NSInteger repeatCounter = 0;
    while([_filteredList count] < 3) //keep going till we have 3 elements     {         NSUInteger curListSize = [_filteredList count];         for(NSString *str in tempFilteredList) //loop through the new smaller filtered list         {             //ok we have the sequence lets check if its the right length             if([str length] == lenCompare)             {                 NSLog(@"Found match: %@", str);                 [_filteredList addObject:str];                 //stop when we have 3 matches                 if([_filteredList count] == 3)                 {                     return _filteredList;                 }             }         }         lenCompare++; //go up by 1 length         if(curListSize == [_filteredList count] && repeatCounter > 1)
        {
            //no new matches found with inc compare size, stopping
            NSLog(@"No new matches found for %@", sequence);
            return _filteredList;
        }
        else
        {
            NSLog(@"Not enough matches found, inc lencompare");
        }
        repeatCounter++;

    }
    return _filteredList;

I also added a repeat counter to prevent continuously searching for new words if none are found.

Now here is a look at the final product:

Word Prediction

This concludes the changes for now, I will branch the textview into a word prediction repo. To it I will add the initial model word organization for faster sorting time. I will add system to make sentences and use the list to help complete the wording.

Note I will not really target core linguistics here but I will add thoughts on what I would do if I had a pretty little(big) database.

[“word”][“type of word”][“commonality”][“linking word”][“most-used-in-conjunction-with-reference”]

Having data like that would allow a predictor to utilize many factors into determining which word is most likely to finish the sequence. The other key factor is learning and from a reading by Ray Kurzweil, his approach is using Hierarchal Hidden Markov Models or HHMM for short. These learning algorithms allow letters, words and sentences (referred to as nodes) to create links to one another. These nodes form branches or links to one another with probability values.

For example:

The character sequence “APP” may form links to “APPLE, “APPLICATION”, “APPLY”. Each link can be described by a probability value.

APP — 0.5 –> APPLE
— 0.3 –> APPLICATION
— 0.3 –> APPLY

Using Markov models we can denote that apple should be the first choice. In essence we are looking for the translation from one state to another state. This is the simpliest version of the Markov model known as Markov Chains.

Here is a nice video explaining .

Word Prediction App Part 1: IOS TableView Filter Application

As one of my first apps in learning how to program in IOS with Objective-C, I decided to take a look into list filtering. Coming from learning Android first, I was interested in how IOS would tackle the entire design process. To keep things simple, the application is a single column, single section, multi-row tableview. This is just a listview in most other frameworks but under the hood it is just a tableview.

Using Apple’s MVC design scheme, this application includes a model that will handle list filtering and creating the initial data from a plist. The model will also provide a pointer to a filtered list which the controller will use to update the view.

All code can be found here: TableView Filter Application BitBucket


Let’s start out with the layout since Apple makes design-first so easily and fun. Here is the layout I chose for the application:

Storyboard Capture

We place a textview as our filter at the top of the application, a label to display all matches we find and finally the tableview as the body of the application.

In our controller class we need to tie our UI to the controller outlets and actions. The IBAction for the textview we are looking for is valueChanged, this will simulate a “key up” event most people are used too.


@interface ListFilterViewController ()

//our outlets for the UI objects
@property (nonatomic, readwrite, weak)IBOutlet UITextField *tvFilter;
@property (strong, nonatomic)IBOutlet UITableView *tableView;
@property (nonatomic, readwrite, weak)IBOutlet UILabel *matchLbl;

//our model objects
@property (strong, nonatomic)ListModel *listModel;
@property (strong, nonatomic)NSMutableArray *filterdList;

//actions from filter
- (IBAction)filterChanged:(UITextField *)sender;

@end

In order to connect the tableview we need to extend the following:

@interface ListFilterViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
//in more complex apps, each should have its own controller 
@end

This will allow us to implement the following 3 methods in our controller:
Utilizing the below, a simple list view should be viewable with 5 rows of “Cell Text”.

//table view

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    //return the sections we need, just 1 for this example since we want a single list
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    //return the number of rows in section, we are just going to put our array size here
    return 5;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //here we create a simple identifier for reusability 
    static NSString *cellID = @"SimpleID";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    
    if (cell == nil) { //check if its nil, if it is we need to create a simple cell with the default style
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
    }
     
    cell.textLabel.text = "Cell Text"; //set the inner cell label text

    return cell; //finally return the individual cell 
}

For the model I created a simple ListModel class that creates an NSMutableArray and initializes with a plist containing approximately 140,000 words.

Here is the .h file

#import <Foundation/Foundation.h>

@interface ListModel : NSObject

@property (nonatomic, readwrite, strong) NSMutableArray *tableData;
@property (nonatomic, readwrite, strong) NSMutableArray *filteredList;

- (NSMutableArray *)sortWithSquence:(NSString *)sequence;

@end

And here is the .m file


#import "ListModel.h"

@implementation ListModel

@synthesize tableData = _tableData;
@synthesize filteredList = _filteredList;

- (id) init
{
    if(self == [super init])
    {
        NSLog(@"Creating table data");
        
        // Path to the plist (in the application bundle)
        NSString *path = [[NSBundle mainBundle] pathForResource:
                          @"words" ofType:@"plist"];
        // Build the array from the plist
        _tableData  = [[NSMutableArray alloc] initWithContentsOfFile:path];
        //create a 2nd array that will contain the filtered version 
        _filteredList = [[NSMutableArray alloc] init];
        NSLog(@"Created %lu rows", _tableData.count);
    }
    return self;
}

- (NSMutableArray *)sortWithSquence:(NSString *)sequence
{
    if([sequence isEqualToString:@""]) //if no filter, return the regular list
    {
        return _tableData;
    }
    
    //remake the filtered list
    [_filteredList removeAllObjects];
    
    //O(n)
    //for simplicity purpose, the app first used brute force methods for the array
    //future renditions of this code will utilize core data for data sorting as core data stores the data
    //in memory for quickness and will provide a wrapper to encapsulate the need for arrays
    for(NSString *str in _tableData) //loop through entire set each time for now ..
    {
        NSRange textRange;
        textRange =[str rangeOfString:sequence options:NSCaseInsensitiveSearch];
        
        if(textRange.location != NSNotFound)
        {
            [_filteredList addObject:str];
        }
    }
    return _filteredList;
}

@end

For the filter method, we first check if the sequence is empty and if it is, return the pointer to the NSMutableArray that contains all the words. This prevents us from remaking a list that is full.
Next the filtered list calls a remove all objects method to clear itself (but not deallocate it). Since we have a strong pointer and the object itself is no longer pointing to the previous memory locations, they can be cleaned up. The ARC value should be 0 at this point. To verify this, I ran through each letter to re-create 26 filtered lists and watched the memory inspector provided by XCode.

Memory Utilization

We can see that this filter method does have a high initial memory usage since we start the app around 32mb. But creating/recreating the filtered lists brings it up to about 36mb peak and then drops back down after a short period of time. Using tools like this during your application coding process is very important to capture memory leaks and poor memory usage.


The listmodel will be allocated and initialized within the controller class along with the table data source and delegate in the viewDidLoad event. Notice I have also added the filtered list initializations here as well as defaulted the match label text.


- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"View did load, init of data");
    _listModel = [[ListModel alloc] init];
    _filterdList = _listModel.tableData; //init to data source at first
    _matchLbl.text = [NSString stringWithFormat:@"Matches: %lu", _filterdList.count];
    _tableView.dataSource = self;
    _tableView.delegate = self;
    
}

Next we can setup the filter event to re-filter our array every time the textfield value changes, this is also where the label can be updated.

- (IBAction)filterChanged:(UITextField *)sender
{
    _filterdList = [_listModel sortWithSquence:sender.text]; //get our sorted list
    _matchLbl.text = [NSString stringWithFormat:@"Matches: %lu", _filterdList.count]; //update our label
    [_tableView reloadData]; //reload the table data
}

Finally we can implement the 3 table methods properly:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _filterdList.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //here we create a simple identifier for reusability 
    static NSString *cellID = @"SimpleID";
     
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
     
    if (cell == nil) { //check if its nil, if it is we need to create a simple cell with the default style
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
    }
     
    NSString *cellText = [_filterdList objectAtIndex:indexPath.row]; 
    cell.textLabel.text = cellText; //set the inner cell label text

    return cell;
}

The final product will look like this:

App Sample

App Sample Filtered

Not sure what nonpariello is but it has my name in it so it must be something important.