Multi-tree and Priority Ordering Algorithm - java

I would like advice on an algorithm for ordering data.
Problem
I need to sort a list of records (POJOs) based on several criteria.
Parent child relationship (no inheritance, just properties)
A unique ordering id (unique within a generational level of each tree).
Each record has the following properties:
ID - uniquely identifies each record
Parent ID - Identifies the record's parent. If same as id, this is a top level root node
Order Number - orders children within the same generation/family.
Example Records. They are not always in nice numerical order ;)
And after they are sorted:
Please note, record 14 is before record 5 because of its Order Number (priority).
In my dataset, there are approximately 3,000 records, and usually no more than 3 or 4 levels of depth.
Strategy
Start with two lists:
1. The list that contains the records to be ordered.
2. An empty list to hold all sorted records
Step 1: Loop through the records and grab all top level nodes (id=parent id), sort them by Order Number, then add them to the second list.
Step 2: For each node in list 2, loop through list 1 and grab all children, sort them by Order Number, then insert them in list 2 directly after their parent.
Note that pretty much all the sorting happens in step 2.
Here is a run through.
Questions
I believe step 2 will make the algorithm complexity at least O(n²).
What are the pros and cons of this algorithm versus something like recursion?
Is there a more efficient algorithm to use?

It's late but i think i got it :)
List<Pojo> list = Arrays.asList(
new Pojo(1,1,1),new Pojo(2,1,1),
new Pojo(3,2,1),new Pojo(4,3,1),new Pojo(5,2,3),
new Pojo(6,5,1),new Pojo(7,6,1),new Pojo(8,1,2),
new Pojo(9,9,2),new Pojo(10,9,2),new Pojo(11,9,1),
new Pojo(12,11,1),new Pojo(13,13,3),new Pojo(14,2,2)
);
First sort by parent and orderNumber:
Collections.sort(list, (a,b) -> {
if ( a.id == a.pId && a.pId == b.pId ) return -1;
if ( b.id == b.pId && a.pId == b.pId ) return 1;
// 100000... a number bigger there largest order#
int aa = 100000 * a.pId + a.oNo;
int bb = 100000 * b.pId + b.oNo;
if ( aa > bb ) return 1;
if ( aa < bb ) return -1;
return 0;
});
Then loop forward and for each line traverse back to where another pojo has the same parentId or where the other pojo's id is equal to the traversing pojo's parentId:
for ( int fw = 1 ; fw < list.size() ; fw ++ )
{
Pojo a = list.get(fw);
if ( a.id != a.pId )
{
for ( int bw = fw - 1 ; bw >= 0 ; bw -- )
{
Pojo b = list.get(bw);
if ( a.pId != b.pId && a.pId != b.id )
{
list.set(bw, a);
list.set(bw+1, b);
}
else
{
break;
}
}
}
}

Here is a solution which relies on tree traversal as referred to in the comment above.This gives a linear time performance which will become more relevant as the dataset size increases.
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.function.*;
public class PriorityTree {
static class Node<T extends Comparable> implements Comparable{
private T data;
private Node<T> parent;
private Collection<Node<T>> children;
BiPredicate<Node<T>,T> addNodePredicate;
UnaryOperator<T> paddingElement;
public Node(T data, Node<T> parent, Collection<Node<T>> children,BiPredicate<Node<T>,T> addNodePredicate,UnaryOperator<T> paddingElement) {
this.data = data;
this.parent = parent;
this.children = children;
this.addNodePredicate = addNodePredicate;
this.paddingElement = paddingElement;
}
public Node(T data, Node<T> parent) {
this(data,parent,new TreeSet<Node<T>>(),parent.addNodePredicate,parent.paddingElement);
}
public boolean add(T element) {
if (this.addNodePredicate.test(this,element)) {
this.children.add(new Node<T>(element, this));
return true;
} else {
for (Node<T> node : this.children) {
if(node.add(element)) return true;
}
return false;
}
}
private String print(String indent){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(indent);
stringBuilder.append(data);
stringBuilder.append("\n");
for (Node<T> node : this.children) {
stringBuilder.append(node.print(indent+" "));
}
return stringBuilder.toString();
}
#Override
public String toString() {
return print("");
}
#Override
public int compareTo(Object o) {
return this.data.compareTo(((Node<T>)o).data);
}
}
static class Pojo implements Comparable{
int id;
int parentId;
final int orderNo;
#Override
public String toString() {
return "id=["+id + "],parentId=["+parentId+"],orderNo=["+orderNo+"]";
}
public Pojo(int id, int parentId, int orderNo) {
this(orderNo);
this.parentId = parentId;
this.id = id;
}
public Pojo(int orderNo) {
this.orderNo = orderNo;
}
public int getId() {
return id;
}
public int getParentId() {
return parentId;
}
public int getOrderNo() {
return orderNo;
}
public int compareTo(Object that) {
if (that == null )return 1;
if ( that instanceof Pojo)
return Integer.compare(this.getOrderNo(),((Pojo)that).getOrderNo());
else return 1;
}
static Pojo ROOT= new Pojo(0,0,0);
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pojo pojo = (Pojo) o;
if (id != pojo.id) return false;
if (parentId != pojo.parentId) return false;
return orderNo == pojo.orderNo;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + parentId;
result = 31 * result + orderNo;
return result;
}
}
private static List<Pojo> readInObjects(String csvFile){
final List<Pojo> list = new ArrayList<Pojo>();
try{
final FileReader fileReader = new FileReader(csvFile);
final BufferedReader br = new BufferedReader(fileReader);
String line;
while (( line = br.readLine()) != null) {
final String[] valueArray = line.split(",");
final int id = Integer.parseInt(valueArray[0]);
final int parentId = Integer.parseInt(valueArray[1]);
final int orderNo = Integer.parseInt(valueArray[2]);
final Pojo pojo = new Pojo(id,parentId,orderNo);
list.add(pojo);
}
}catch (IOException e) {
e.printStackTrace();
}
return list;
}
public static void main(String[] args){
final List<Pojo> values = readInObjects("values.txt");
final Node<Pojo> tree = new Node<>(Pojo.ROOT, null, new TreeSet<>(), (pojoNode, pojo) -> pojoNode.data.getId()==0 && pojo.getId()==pojo.getParentId() ||pojoNode.data.getId() == pojo.getParentId(),(p)->new Pojo(0,p.getId(),0));
values.stream().forEach(pojo -> tree.add(pojo));
System.out.println(tree.toString());
}
}
values.txt has the following entries
1,1,1
2,1,1
3,2,1
4,3,1
5,2,3
6,5,1
7,6,1
8,1,2
9,9,2
10,9,2
11,9,1
12,11,1
13,13,3
14,2,2
Output looks like
id=[1],parentId=[1],orderNo=[1]
id=[2],parentId=[1],orderNo=[1]
id=[3],parentId=[2],orderNo=[1]
id=[4],parentId=[3],orderNo=[1]
id=[14],parentId=[2],orderNo=[2]
id=[5],parentId=[2],orderNo=[3]
id=[6],parentId=[5],orderNo=[1]
id=[7],parentId=[6],orderNo=[1]
id=[8],parentId=[1],orderNo=[2]
id=[9],parentId=[9],orderNo=[2]
id=[11],parentId=[9],orderNo=[1]
id=[12],parentId=[11],orderNo=[1]
id=[10],parentId=[9],orderNo=[2]
id=[13],parentId=[13],orderNo=[3]

Here is the working sorting example in C++, hope this helps
#include <cstdint>
#include <vector>
#include <iostream>
struct TreeElement {
uint32_t id;
uint32_t idx;
uint32_t parent;
[[nodiscard]]
bool operator==(const TreeElement& other) const noexcept {
return id == other.id && idx == other.idx && parent == other.parent;
}
};
void elem_swap(TreeElement& first, TreeElement& second) {
TreeElement tmp = first;
first = second;
second = tmp;
}
// returns number of processed elements
uint32_t emplace_children(TreeElement* elements, size_t count, uint32_t parent) {
if(!count) {
return 0;
}
uint32_t i = 0;
for(; i < count; ++i) {
uint32_t lowestChildIdx = 0;
bool found = false;
for(uint32_t j = i; j < count; ++j) {
if(elements[j].parent == parent) {
if(!found || elements[j].idx <= elements[lowestChildIdx].idx) {
lowestChildIdx = j;
}
found = true;
}
}
if(!found) {
return i;
}
elem_swap(elements[i], elements[lowestChildIdx]);
i += emplace_children(elements + i + 1, count - i - 1, elements[i].id);
}
return i;
}
int main() {
std::vector<TreeElement> elements = {TreeElement{.id = 11, .idx = 03, .parent = 6},
TreeElement{.id = 10, .idx = 2, .parent = 6},
TreeElement{.id = 1, .idx = 0, .parent = 0},
TreeElement{.id = 6, .idx = 2, .parent = 2},
TreeElement{.id = 5, .idx = 1, .parent = 2},
TreeElement{.id = 4, .idx = 0, .parent = 2},
TreeElement{.id = 2, .idx = 0, .parent = 1},
TreeElement{.id = 9, .idx = 1, .parent = 6},
TreeElement{.id = 8, .idx = 0, .parent = 6},
TreeElement{.id = 3, .idx = 1, .parent = 1},
TreeElement{.id = 7, .idx = 0, .parent = 4},
TreeElement{.id = 12, .idx = 1, .parent = 9}};
std::vector<TreeElement> goal = {TreeElement{.id = 1, .idx = 0, .parent = 0},
TreeElement{.id = 2, .idx = 0, .parent = 1},
TreeElement{.id = 4, .idx = 0, .parent = 2},
TreeElement{.id = 7, .idx = 0, .parent = 4},
TreeElement{.id = 5, .idx = 1, .parent = 2},
TreeElement{.id = 6, .idx = 2, .parent = 2},
TreeElement{.id = 8, .idx = 0, .parent = 6},
TreeElement{.id = 9, .idx = 1, .parent = 6},
TreeElement{.id = 12, .idx = 1, .parent = 9},
TreeElement{.id = 10, .idx = 2, .parent = 6},
TreeElement{.id = 11, .idx = 03, .parent = 6},
TreeElement{.id = 3, .idx = 1, .parent = 1}};
emplace_children(elements.data(), elements.size(), 0);
for(size_t i = 0 ; i < elements.size(); ++i) {
if(elements[i] == goal[i]) {
std::cout << "index " << i << " passed" << std::endl;
} else {
std::cout << "index " << i << " failed" << std::endl;
}
}
}

Related

How to delete an item in the array and keeping the order the same?

I'm working on this project that allows me to add and delete elements In the array. When I delete elements in the array there would be a zero in that space and the code should shift the values after the deleted value to take its place. For Example: In the Array {1, 2, 3, 4, 5}. I choose to delete 3. My output should be {1, 2, 4, 5}. Instead my output is {1, 2, 5, 4}. Could someone help me figure out why it would do that? And how to rectify it?
import java.util.Scanner;
import java.util.Arrays;
public class IntBag2 {
private static final int INITIAL_SIZE = 20;
private static int[] bag;
private int capacity;
public IntBag2() {
bag = new int[INITIAL_SIZE];
}
public IntBag2(int capacity) {
bag = new int[capacity];
}
public boolean add(int item) {
if (capacity == bag.length)
return false;
bag[capacity++] = item;
return true;
}
public boolean delete(int item) {
for (int i = 0; i < capacity; i++) {
if (bag[i] == item) {
bag[i] = bag[--capacity];
return true;
}
}
return false;
}
#Override
public String toString() {
String result = "Bag: ";
for (int i = 0; i < capacity; i++)
result += bag[i] + " ";
return result;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
IntBag2 intBag = new IntBag2();
boolean done = false;
while (!done) {
System.out.println("1. Add an Item to the Array");
System.out.println("2. Delete an item in the Array");
System.out.println("3. toString");
switch (input.nextInt()) {
case 1:
System.out.println("Add an Item to the Array");
System.out.println(intBag.add(input.nextInt()));
break;
case 2:
System.out.println("Delete Item of Array");
System.out.println(intBag.delete(input.nextInt()));
break;
case 3:
System.out.println("toString");
System.out.println(intBag.toString());
break;
}
}
input.close();
}
}
A simpler implementation of delete is possible allowing to "delete" all entries in the array equal to the given item and shift the remaining ones to the front efficiently:
public boolean delete(int item) {
System.out.println("deleting " + item); // for debug purposes
int oldCapacity = capacity;
for (int i = 0, j = 0; i < oldCapacity; i++) {
if (bag[i] != item) {
bag[j++] = bag[i];
} else {
capacity--;
}
}
System.out.println("new capacity = " + capacity); // for debug
// or use Arrays.fill(bag, capacity, oldCapacity, -1); instead of the loop
for (int i = capacity; i < oldCapacity; i++) {
bag[i] = -1; // mark free entries with -1 in the tail
}
return oldCapacity == capacity;
}
Also, method toString should use StringBuilder if multiple concatenation are used in loop, or for brevity the following method print using utility methods from Arrays may be implemented:
public void print() {
System.out.println(Arrays.toString(Arrays.copyOf(bag, capacity)));
}
Test:
IntBag ibag = new IntBag(10);
ibag.add(1);
ibag.add(3);
ibag.add(3);
ibag.add(2);
ibag.add(1);
ibag.print();
ibag.delete(2);
ibag.print();
ibag.delete(1);
ibag.print();
Output:
[1, 3, 3, 2, 1]
deleting 2
new capacity = 4
[1, 3, 3, 1]
deleting 1
new capacity = 2
[3, 3]
Update
"Deletion" of only the first entry without creation of new array may be implemented as follows:
skip all elements until item is detected or end of bag is reached
if item found, shift the remaining elements by 1, write -1 to the last element, return true
return false otherwise
public boolean deleteFirst(int item) {
System.out.println("deleting first " + item);
int id = 0;
while (id < capacity && bag[id] != item) id++;
if (id < capacity && bag[id] == item) {
while (++id < capacity) {
bag[id - 1] = bag[id];
}
bag[--capacity] = -1;
return true;
}
return false;
}
Test:
IntBag ibag = new IntBag(10);
ibag.add(1); ibag.add(3); ibag.add(1); ibag.add(2); ibag.add(1);
ibag.print();
ibag.deleteFirst(1); ibag.print();
ibag.deleteFirst(1); ibag.print();
ibag.deleteFirst(1); ibag.print();
Output:
[1, 3, 1, 2, 1]
deleting first 1
[3, 1, 2, 1]
deleting first 1
[3, 2, 1]
deleting first 1
[3, 2]
With the line bag[i] = bag[--capacity]; you are basically getting the last item in your array and place it in the location of the deleted one.
Given that you are using int[] (instead of Integer[]) we can't assign an index of the array as null. The best we can do is assigning it -1 or create a new array.
I decided to create a new array from scratch. The following will do the trick.
public class IntBag2 {
private static final int INITIAL_SIZE = 20;
private static int[] bag;
private int capacity;
public IntBag2() {
bag = new int[INITIAL_SIZE];
}
public IntBag2(int capacity) {
bag = new int[capacity];
}
public boolean add(int item) {
if (capacity == bag.length)
return false;
bag[capacity++] = item;
return true;
}
public boolean delete(int item) {
int[] newBag = new int[capacity];
int newCapacity = capacity;
boolean deleted = false;
for (int i = 0, j = 0; i < capacity; i++) {
if (bag[i] == item && !deleted) {
deleted = true;
newCapacity = capacity - 1;
} else {
newBag[j++] = bag[i];
}
}
bag = newBag;
capacity = newCapacity;
return deleted;
}
#Override
public String toString() {
String result = "Bag: ";
for (int i = 0; i < capacity; i++)
result += bag[i] + " ";
return result;
}
}

BFS algorithm doesn't end while trying to solve 15 puzzle JAVA

I am trying to create a solver for 15 puzzle using BFS algorithm in Java. The problem is that when I launch the program it ends in endless loop. I have tried with simple input states, for example swaping only 2 numbers from solved state and it worked. It seems to have difficulty with more complex problems.
I checked everything, if the queues are filled with correct states, if the zero indexes are fine withing the states. It looks like it should work just fine but it doesn't. Here is my code:
Program.java
package Puzzle;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Program {
public static final int[][] solved = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 0 } };
public static final State solvedState = new State(solved, 4, 4, new Point(3, 3));
static Solution solution;
public static void main(String[] args) throws IOException {
if(args.length != 5)
{
System.out.println("Wrong number of arguments in configuration. Given args:");
int nr = 0;
for (var arg : args)
{
System.out.println(nr++ + ": " + arg);
}
return;
}
State initialState = ReadInitialStateFromFile(args[2]);
switch (args[0])
{
case "bfs":
solution = new BFS(initialState, args[1]);
break;
case "dfs":
//solution = new DFS(initialState, args[1]);
break;
case "astr":
//solution = new AStar(initialState, args[1]);
break;
default:
System.out.println("incorrect method parameter");
return;
}
long starttime = System.nanoTime();
String solutionString = solution.FindSolution();
long endtime = System.nanoTime();
long elapsedTime = endtime - starttime;
long elapsedTimeMS = elapsedTime/1000000;
int solutionLength = solutionString != "No solution found!" ? solutionString.length() : -1;
WriteResultState(args[3], solutionString);
WriteAdditionalInfo(args[4],
solutionLength,
solution.numberOfVisitedStates,
solution.numberOfProcessedStates,
solution.maxDepth,
elapsedTimeMS
);
}
public static State ReadInitialStateFromFile(String filename) throws IOException {
String data = null;
int height = -1;
int width = -1;
Point point = new Point();
int [][] puzzle = null;
BufferedReader br = new BufferedReader(new FileReader(filename));
try {
data = br.readLine();
if(data == null || data.length() != 3)
{
throw new Exception("Dimentions are not correct");
}
String[] dimentions = data.split(" ");
height = Integer.parseInt(dimentions[0]);
width = Integer.parseInt(dimentions[1]);
puzzle = new int[width][height];
for(int i=0; i<width;i++){
String values[] = br.readLine().split(" ");
if(values.length != width)
{
throw new Exception(String.format("Values in row {0} are not correct", i));
}
for (int j = 0; j < height; j++)
{
int value = Integer.parseInt(values[j]);
//System.out.println(value);
if(value == 0)
{
point = new Point(i, j);
System.out.println("zero point" + point.toString());
}
puzzle[i][j] = value;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
br.close();
}
return new State(puzzle, height, width, point);
}
private static void WriteResultState(String resultStatePath, String solution) throws IOException {
int resultLenght = solution != "No solution found!" ? solution.length() : -1;
FileWriter fw = new FileWriter(resultStatePath);
fw.write(resultLenght);
if(resultLenght != -1)
fw.write(solution.toUpperCase());
fw.close();
}
private static void WriteAdditionalInfo(String additionalInfoPath, int resultLength, int numberOfVisitedStates, int numberOfProcessedStates, int maxDepth, long calculationTime) throws IOException {
FileWriter fw = new FileWriter(additionalInfoPath);
fw.write(resultLength);
fw.write(numberOfProcessedStates);
fw.write(numberOfVisitedStates);
fw.write(maxDepth);
fw.write(String.valueOf(calculationTime));
}
}
State.java
package Puzzle;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class State {
public State previousState;
public List<State> nextStates;
public int[][] puzzle;
public char move;
public String moveSet;
public static int width, height;
public Point zeroIndex;
public int depth = 0;
public State(int[][] p, int _height, int _width, Point _zeroIndex) {
nextStates = new ArrayList<State>();
puzzle = new int[_height][_width];
width = _width;
height = _height;
puzzle = Arrays.copyOf(p, p.length);
zeroIndex = _zeroIndex;
moveSet = "";
}
public State(State state)
{
moveSet = state.moveSet;
nextStates = new ArrayList<State>();
puzzle = new int[state.height][state.width];
previousState = state.previousState;
for(int i=0; i < state.height;i++){
for(int j =0; j<state.width; j++)
{
this.puzzle[i][j] = state.puzzle[i][j];
}
}
//this.puzzle = Arrays.copyOf(state.puzzle, state.puzzle.length);
zeroIndex = new Point(state.zeroIndex);
}
public State Move(char direction)
{
State newState = new State(this);
newState.move = direction;
newState.moveSet += direction;
newState.previousState = this;
newState.depth = depth + 1;
switch (direction)
{
case 'u':
int tempu = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x - 1][zeroIndex.y];
newState.puzzle[zeroIndex.x - 1][zeroIndex.y] = tempu;
newState.zeroIndex.x--;
break;
case 'd':
int tempd = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x + 1][zeroIndex.y];
newState.puzzle[zeroIndex.x + 1][zeroIndex.y] = tempd;
newState.zeroIndex.x++;
break;
case 'l':
int templ = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x][zeroIndex.y-1];
newState.puzzle[zeroIndex.x][zeroIndex.y-1] = templ;
newState.zeroIndex.y--;
break;
case 'r':
int tempr = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x][zeroIndex.y + 1];
newState.puzzle[zeroIndex.x][zeroIndex.y + 1] = tempr;
newState.zeroIndex.y++;
// System.out.println(newState.zeroIndex.toString());
// System.out.println(this.zeroIndex.toString());
break;
default:
break;
}
return newState;
}
public void GenerateNextStates(String order)
{
char [] chars = order.toCharArray();
for(char direction : chars)
{
if(IsMovePossible(direction) == true && IsNotGoingBack(direction) == true)
{
this.nextStates.add(this.Move(direction));
}
}
}
public boolean IsMovePossible(char direction)
{
if ((!"udlr".contains(Character.toString(direction))) ||
(zeroIndex.x == 0 && direction == 'u') ||
(zeroIndex.x == height - 1 && direction == 'd') ||
(zeroIndex.y == 0 && direction == 'l') ||
(zeroIndex.y == width - 1 && direction == 'r'))
{
return false;
}
return true;
}
public void Print()
{ for(int i = 0; i < height;i++)
{
for (int j = 0; j < width; j++)
{
System.out.println(puzzle[i][j] + " ");
}
System.out.println();
}
}
public boolean IsSolution()
{
int correctValue = 1;
for (int i = 0; i < State.height; i++)
{
for (int j = 0; j < State.width; j++)
{
if (i == State.height - 1 && j == State.width - 1)
{
break;
}
if (puzzle[i][j] != correctValue++)
{
return false;
}
}
}
return true;
}
public boolean IsPuzzleSame(State other)
{
for (int i = 0; i < State.height; i++)
{
for (int j = 0; j < State.width; j++)
{
if(this.puzzle[i][j] != other.puzzle[i][j])
{
return false;
}
}
}
return true;
}
private boolean IsNotGoingBack(char direction)
{
if((this.move == 'l' && direction == 'r') ||
(this.move == 'u' && direction == 'd') ||
(this.move == 'r' && direction == 'l') ||
(this.move == 'd' && direction == 'u'))
{
//System.out.println("znaleziono powrót");
return false;
}
return true;
}
}
Solution.java
package Puzzle;
import java.util.Queue;
public abstract class Solution {
public static State solutionState;
public static String neighborhoodSearchOrder;
public static int numberOfVisitedStates;
public static int numberOfProcessedStates;
public static int maxDepth;
public abstract String FindSolution();
protected boolean IsPuzzleStateNew(Queue<State> q, State newState)
{
for (State state : q)
{
if (state.IsPuzzleSame(newState))
{
return false;
}
}
return true;
}
}
BFS.java
package Puzzle;
import java.util.*;
public class BFS extends Solution {
public BFS(State _initialState, String _neighborhoodSearchOrder)
{
this.solutionState = _initialState;
this.neighborhoodSearchOrder = _neighborhoodSearchOrder.toLowerCase();
}
#Override
public String FindSolution() {
Queue<State> toVisit = new LinkedList<State>();
Queue<State> visited = new LinkedList<State>();
String solutionString = "";
boolean solutionReady = false;
toVisit.add(solutionState);
int z = 0;
while(toVisit.size() > 0)
{
// System.out.println("visited");
// for(int i=0; i<visited.size();i++){
// System.out.println("visited size: " + visited.size());
// }
//
//System.out.println(toVisit.size());
State currentState = toVisit.remove();
visited.add(currentState);
System.out.println("Numer iteracji: " + z);
//currentState.Print();
if(currentState.depth > maxDepth)
{
maxDepth = currentState.depth;
}
if(currentState.IsSolution())
{
solutionString = currentState.moveSet;
solutionReady = true;
break;
}
currentState.GenerateNextStates(neighborhoodSearchOrder);
Queue<State> allStates = new LinkedList<State>();
allStates.addAll(toVisit);
allStates.addAll(visited);
// for (State state: currentState.nextStates){
// System.out.println(state.zeroIndex.toString());
// state.Print();
// }
for (State state : currentState.nextStates)
{
if(IsPuzzleStateNew(allStates, state))
{
toVisit.add(state);
}
}
allStates.clear();
z++;
}
numberOfVisitedStates = visited.size() + toVisit.size();
numberOfProcessedStates = visited.size();
System.out.println(numberOfVisitedStates);
System.out.println(numberOfProcessedStates);
return solutionReady ? solutionString : "No solutionString found";
}
}
I have spent so much time troubleshooting this and I am honestly hopeless right now. Thanks for help in advance.
I tested the code on 4x4 puzzles. It solved correctly puzzles requiring up to 14 steps, but took a long time.
I stopped a 15 steps puzzle solve run after 19 hours.
To speed the code up I introduced a hashCode() method in State.
Implementing hashCode() allowed me to change the costly if(IsPuzzleStateNew(allStates, state)) test by using a Set for
visited and made isPuzzleStateNew and isPuzzleSame redundant.
I also refactored isSolution to make it more generic and faster.
Times changed dramatically and puzzles up to 16 steps run witn no issues:
11 steps puzzle went down from 13 seconds to 0.2 sec
12 steps puzzle went down from 56 seconds to 0.2
13 steps puzzle went down from 100 seconds to 0.2
14 steps puzzle went down from 340 seconds to 0.4
15 steps puzzle stopped after 19 hours 0.6
16 steps puzzle takes 1.2
Testing puzzles which require 35 and 80 steps crash, from reasons that still needs to be investigated.
The following in an mre as you are expected to post when asking or answering (you can copy-paste the wntire code into Program.java and run):
import java.awt.Point;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
public class Program {
public static int[][] solve, solved;
public static State solvedState, initialState;
static Solution solution;
public static void main(String[] args) throws IOException {
solved = new int[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 0 } };
solvedState = new State(solved);
//run various tests (consider writing a JUnit test)
int[] testcases = {1, 4, 10, 11, 12, 13, 14, 15, 16, 35, 80};
for(int test : testcases) {
System.out.println("########## "+test+" ##########");
testCase(test);
solution = new BFS(initialState, solvedState, "udlr");
long starttime = System.nanoTime();
String solutionString = solution.findSolution();
long elapsedTimeMS = (System.nanoTime() - starttime)/1000000;
if(test != solutionString.length()) {
System.err.println("Test "+ test + " wrong length "+ solutionString.length() );
}
int solutionLength = solutionString != "No solution found!" ? solutionString.length() : -1;
WriteResultState(solutionString);
WriteAdditionalInfo(
solutionLength,
solution.getNumberOfVisitedStates(),
solution.getNumberOfProcessedStates(),
solution.getMaxDepth(),
elapsedTimeMS
);
}
}
private static void testCase(int numberOfTestCase){
switch (numberOfTestCase){
case 1:
solve = new int[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 0, 15 } };
break;
default: case 4: //4 steps to solve
solve = new int[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 0, 10, 11, 12 }, { 9, 13, 14, 15 } };
break;
case 10: // 10 steps to solve
solve = new int[][] { { 1, 3, 4, 8 }, { 6, 2, 7, 0 }, { 5, 10, 11, 12 }, { 9, 13, 14, 15 } };
break;
case 11:// 11 steps to solve
solve = new int[][] { { 1, 3, 4, 8 }, { 6, 2, 0, 7 }, { 5, 10, 11, 12 }, { 9, 13, 14, 15 } };
break;
case 12:// 12 steps to solve
solve = new int[][] { { 1, 3, 4, 8 }, { 6, 0, 2, 7 }, { 5, 10, 11, 12 }, { 9, 13, 14, 15 } };
break;
case 13:// 13 steps to solve
solve = new int[][] { { 1, 3, 4, 8 }, { 6, 10, 2, 7 }, { 5, 0, 11, 12 }, { 9, 13, 14, 15 } };
break;
case 14:// 14 steps to solve
solve = new int[][] { { 5, 1, 2, 4 }, { 6, 10, 3, 7 }, { 0, 14, 12, 8 }, { 9, 13, 11, 15 } };
//solve = new int[][] { { 1, 7, 2, 4},{ 9, 5, 3, 0},{ 6, 10, 12, 8},{ 13, 14, 11, 15} };
break;
case 15: // 15 steps to solve
solve = new int[][] { { 1, 7, 2, 0},{ 9, 5, 3, 4},{ 6, 10, 12, 8},{ 13, 14, 11, 15} };
break;
case 16:// 16 steps to solve
solve = new int[][] { { 0, 2, 11, 3 }, { 1, 6, 7, 4 }, { 5, 9, 12, 8 }, { 13, 10, 14, 15 } };
break;
case 35:// 35 steps to solve
solve = new int[][] { { 1, 10, 15, 4 }, { 13, 6, 3, 8 }, { 2, 9, 12, 7 }, { 14, 5, 0, 11 } };
break;
case 80:// 80 steps to solve. max possible steps for 15 puzzle
solve = new int[][] { { 0, 12, 9, 13 }, { 15, 11, 10, 14 }, { 3, 7, 2, 5 }, { 4, 8, 6, 1 } };
break;
}
initialState = new State(solve);
}
private static void WriteResultState(/*String resultStatePath,*/ String solution) throws IOException {
int resultLenght = solution != "No solution found!" ? solution.length() : -1;
System.out.println("solution length: "+resultLenght);
if(resultLenght != -1) {
System.out.println(solution.toUpperCase());
}
}
private static void WriteAdditionalInfo(/*String additionalInfoPath,*/int resultLength, int numberOfVisitedStates,
int numberOfProcessedStates, int maxDepth, long calculationTime) throws IOException {
System.out.println("Length: "+resultLength);
System.out.println("Processed states: "+numberOfProcessedStates);
System.out.println("Visited states : "+ numberOfVisitedStates);
System.out.println("Depth: "+maxDepth);
System.out.println("Time: "+String.valueOf(calculationTime));
}
}
class State {
//use private fields. access with getters / setters
private State previousState;
private final List<State> nextStates;
private final int[][] puzzle;
private char move;
private String moveSet;
private final int width, height; //should not be static
private final Point zeroIndex;
private int depth = 0;
public State(int[][] puzzle) {
nextStates = new ArrayList<>();
this.puzzle = puzzle;
width = puzzle[0].length;
height = puzzle.length;
zeroIndex = get0Location();
if(zeroIndex == null) throw new IllegalArgumentException("No 0 is puzzle");
moveSet = "";
}
public State(int[][] puzzle, int height, int width, Point zeroIndex) {
nextStates = new ArrayList<>();
this.width = width;
this.height = height;
this.puzzle = puzzle;
this.zeroIndex = zeroIndex;
moveSet = "";
}
public State(State state)
{
moveSet = state.moveSet;
nextStates = new ArrayList<>();
width = state.width;
height = state.height;
puzzle = new int[state.height][state.width];
previousState = state.previousState;
for(int i=0; i < state.height;i++){
puzzle[i] = Arrays.copyOf(state.puzzle[i], state.puzzle[i].length);
}
zeroIndex = new Point(state.zeroIndex);
}
public State move(char direction)
{
State newState = new State(this);
newState.move = direction;
newState.moveSet += direction;
newState.previousState = this;
newState.depth = depth + 1;
switch (direction)
{
case 'u':
int tempu = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x - 1][zeroIndex.y];
newState.puzzle[zeroIndex.x - 1][zeroIndex.y] = tempu;
newState.zeroIndex.x--;
break;
case 'd':
int tempd = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x + 1][zeroIndex.y];
newState.puzzle[zeroIndex.x + 1][zeroIndex.y] = tempd;
newState.zeroIndex.x++;
break;
case 'l':
int templ = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x][zeroIndex.y-1];
newState.puzzle[zeroIndex.x][zeroIndex.y-1] = templ;
newState.zeroIndex.y--;
break;
case 'r':
int tempr = newState.puzzle[zeroIndex.x][zeroIndex.y];
newState.puzzle[zeroIndex.x][zeroIndex.y] = newState.puzzle[zeroIndex.x][zeroIndex.y + 1];
newState.puzzle[zeroIndex.x][zeroIndex.y + 1] = tempr;
newState.zeroIndex.y++;
break;
}
return newState;
}
public void generateNextStates(String order)
{
char [] chars = order.toCharArray();
for(char direction : chars)
{
if(isMovePossible(direction) == true && isNotGoingBack(direction) == true)
{
nextStates.add(this.move(direction));
}
}
}
public boolean isMovePossible(char direction)
{
if (!"udlr".contains(Character.toString(direction)) ||
zeroIndex.x == 0 && direction == 'u' ||
zeroIndex.x == height - 1 && direction == 'd' ||
zeroIndex.y == 0 && direction == 'l' ||
zeroIndex.y == width - 1 && direction == 'r')
return false;
return true;
}
#Override
public String toString(){
StringBuilder sb = new StringBuilder();
for(int i = 0; i < height;i++)
{
for (int j = 0; j < width; j++)
{
sb.append(String.format("%1$4s", puzzle[i][j]));
}
sb.append("\n");
}
return sb.toString();
}
private boolean isNotGoingBack(char direction)
{
if(move == 'l' && direction == 'r' ||
move == 'u' && direction == 'd' ||
move == 'r' && direction == 'l' ||
move == 'd' && direction == 'u')
return false;
return true;
}
private Point get0Location() {
for (int row = 0; row < height; row++){
for (int col = 0; col < width ; col++){
if (puzzle[row][col] == 0) return new Point(row, col);
}
}
return null;
}
List<State> getNextStates() {
return nextStates;
}
String getMoveSet() {
return moveSet;
}
int getDepth() {
return depth;
}
int[][] getPuzzle(){
return puzzle;
}
#Override
public int hashCode() {
StringBuilder sb = new StringBuilder();
for(int[] row : puzzle) {
for(int i : row) {
sb.append(i);
}
}
return sb.toString().hashCode();
}
}
abstract class Solution {
protected State initialState, solutionState;
protected String neighborhoodSearchOrder;
protected int numberOfVisitedStates;
protected int numberOfProcessedStates;
protected int maxDepth;
public abstract String findSolution();
protected boolean isSolution(State state)
{
return Arrays.deepEquals(state.getPuzzle(), solutionState.getPuzzle());
}
public int getNumberOfVisitedStates() {
return numberOfVisitedStates;
}
public int getNumberOfProcessedStates() {
return numberOfProcessedStates;
}
public int getMaxDepth() {
return maxDepth;
}
}
class BFS extends Solution {
private State currentState;
private long starttime ;
public BFS(State initialState,State solutionState, String neighborhoodSearchOrder)
{
this.initialState = initialState;
this.solutionState = solutionState;
this.neighborhoodSearchOrder = neighborhoodSearchOrder.toLowerCase();
}
#Override
public String findSolution() {
starttime = System.nanoTime();
Queue<State> toVisit = new LinkedList<>();
Set<State> visited = new HashSet<>();
String solutionString = "";
boolean solutionReady = false;
numberOfVisitedStates = 0;
numberOfProcessedStates = 0;
maxDepth = 0;
toVisit.add(initialState);
visited.add(initialState);
while(toVisit.size() > 0)
{
currentState = toVisit.remove();
numberOfProcessedStates++;
if(currentState.getDepth() > maxDepth)
{
maxDepth = currentState.getDepth();
System.out.println("Processed: "+ numberOfProcessedStates +" nodes, depth: " + maxDepth+ " in "
+ (System.nanoTime() - starttime)/1000000000 + " seconds " );
}
if(isSolution(currentState))
{
solutionString = currentState.getMoveSet();
solutionReady = true;
break;
}
currentState.generateNextStates(neighborhoodSearchOrder);
for (State state : currentState.getNextStates())
{
if( visited.add(state))
{
toVisit.add(state);
}
}
}
numberOfVisitedStates = numberOfProcessedStates+ toVisit.size();
//System.out.println("End state: "+ "\n" + currentState);
return solutionReady ? solutionString : "No solutionString found";
}
}
For further improvement I would recommend:
-Check for solvability before attempting to solve.
-Check if visited is needed at all.
Side note: Please see Java naming conventions.

Recursion calculation for how many level and numbers of children it has

The source is:
id, pid,name
1, 0, a
2, 1, b
3, 1, c
The result we expected is:
id,pid,name,upnum,uplevel,downum,downlevel
1, 0, a, 0, 0, 2, 1
2, 1, b, 1, 1, 0, 0
3, 1, c, 1, 1, 0, 0
Here,name is the name of person,id identify each person,pid means parent id,for example,the person a is the superior of the person b.
upnum means how many superiors he has totally,uplevel means how many level of superiors he has,downnum and downlevel is almost like this
For get this result,I think I have two way
1.Use database,like oralce,I use connect by and nocycle,everything is ok.But for each person,I must run the "connect by" sql again,it seems slow.
And we must install a oracle on the client side,some client do not like it.
If we use h2 or some embed database,can we use the feature of nocycle in oracle?but I guess it is slow too.Or we should make of index of id?
2.Use java hashMap to store the relationships of id and pid,but when the data become large,there may be a out of memory exception.How to write the code?
What is the best way?
Or is there a better way?like some graph algorithm,or graph-db(database)?
There is no real need for a database. A simple iteration and a recursive function can do the analysis needed :
import java.util.ArrayList;
import java.util.List;
public class Graph {
private final List<Node> nodes;
private final Node root;
//Graph assumes unique id > 0, and only root with pid = 0
Graph(Node root){
this.root = root;
nodes = new ArrayList<>();
nodes.add(root);
};
void add(Node node){
nodes.add(node);
}
void analyze(){
//sort by pid so iteration goes from top level down
nodes.sort( (n1,n2) -> Integer.compare(n1.getPid(), n2.getPid()) );
for(Node node : nodes){
Node parent = getNode(node.getPid());
if (parent == null ) {
continue; //skip root
}
node.setUpLevel(parent.getUpLevel()+1); //add 1 to parent value
node.setUpNum(node.getUpNum() +1); //increment by 1
parent.setDowNum(parent.getDowNum() +1); //increment by 1
updateHigherLevels(node);
}
}
//recursively update higher levels
private void updateHigherLevels(Node node) {
Node parent = getNode(node.getPid());
if(parent == null) return;
parent.setDownLevel(node.getDownLevel() + 1);
updateHigherLevels(parent);
}
void print(){
//sort by id for nice printing
nodes.sort( (n1,n2) -> Integer.compare(n1.getId(), n2.getId()) );
String format = "\n%2s %3s %4s %5s %7s %7s %8s";
System.out.printf(format,"id","pid","name","upnum","uplevel", "downnum" , "downlevel");
for(Node node : nodes){
System.out.printf(format, node.getId(), node.getPid(), node.getName(), node.getUpNum(), node.getUpLevel()
, node.getDowNum(), node.getDownLevel());
}
}
Node getNode(int id){
for(Node node : nodes){
if(node.getId() == id) return node;
}
return null;
}
public static void main(String[] args) {
//make graph
Graph graph = new Graph(new Node(1, 0, "a"));
graph.add(new Node(2, 1, "b"));
graph.add(new Node(3, 1, "c"));
graph.add(new Node(4, 2, "d"));
graph.add(new Node(5, 2, "e"));
graph.analyze();
graph.print();
}
}
class Node {
private final int id,pid;
private int upnum = 0, uplevel = 0, downum = 0, downlevel = 0;
private final String name;
Node(int id, int pid, String name) {
super();
this.id = id;
this.pid = pid;
this.name = name;
}
int getId() { return id; }
int getPid() { return pid; }
String getName() { return name; }
int getUpNum() { return upnum; }
void setUpNum(int upnum) { this.upnum = upnum; }
int getUpLevel() { return uplevel; }
void setUpLevel(int uplevel) { this.uplevel = uplevel; }
int getDowNum() { return downum; }
void setDowNum(int downum) { this.downum = downum; }
int getDownLevel() { return downlevel; }
void setDownLevel(int downlevel) { this.downlevel = downlevel; }
}
Output:

Data Structure for keeping frequency count of pairwise data?

I have a table with hundred' of record where a field is paired with a similar field based on an id. I want to know what is a good data structure for keeping frequency counts for the number of times a pair has appeared together irrespective of the order they appeared in.
Sample data:
ID Feature
5 F1
5 F2
6 F1
6 F2
7 F3
7 F1
7 F2
8 F1
9 F1
10 F1
The sample output is:
F1 F2 F3
F1 0 3 1
F2 3 0 1
F3 1 1 0
One option is to sort all features and use a 2-dimensional int array to represent the pairwise data but then 2/3's of the array is useless/duplicate. For example array[i][i] = 0 and array[i][j] = array[j][i]. Given that I have hundreds of features, this approach won't work.
I thought of using a map but then the key needs to represent a pair e.g. (F1,F3). I am hoping for other solutions too. If there are none I will use a map.
Create a class, say MyPair to use for hash keys that stores pairs of your items and overrides Object#equals(...) (and Object#hashCode()) so that order doesn't matter (e.g. by ordering lexicographically).
Create a Map<MyPair,Integer> to store the frequency count of your pairs.
class MyPair {
public final String feature1;
public final String feature2;
public MyPair(String s1, String s2) {
// Order features so comparison is order-independent.
if (s1.compareTo(s2) <= 0) { // TODO: null check
feature1 = s1;
feature2 = s2;
} else {
feature1 = s2;
feature2 = s1;
}
}
#Override public int hashCode() {
return (s1 + s2).hashCode(); // TODO: cache for performance.
}
#Override public boolean equals(that) {
return (that instanceof MyPair)
&& (that.feature1.equals(this.feature1))
&& (that.feature2.equals(this.feature2));
}
}
Then can hash pairs as expected:
Map<MyPair,Integer> freq = new HashMap<MyPair,Integer>();
MyPair pair1 = new MyPair("F1", "F2");
freq.get(pair1); // => null
freq.put(pair1, 1);
MyPair pair2 = new MyPair("F2", "F1");
freq.get(pair2); // => 1
This is simple algorithm. I assume that data is initially sorted. It is not maybe written as good as I wanted to be, but It must only shows you the proper path :)
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class NeighborListExample {
static class Pair {
private String feature;
private int cnt = 1;
Pair(String feature) {
this.feature = feature;
}
void incr() {
cnt++;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((feature == null) ? 0 : feature.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pair other = (Pair) obj;
if (feature == null) {
if (other.feature != null)
return false;
} else if (!feature.equals(other.feature))
return false;
return true;
}
#Override
public String toString() {
return "(" + feature + ", " + cnt + ")";
}
}
static Map<String, List<Pair>> feature2neighbors = new HashMap<>();
private static int getId(Object[][] data, int i) {
return ((Integer) data[i][0]).intValue();
}
private static String getFeature(Object[][] data, int i) {
return data[i][1].toString();
}
private static void processFeatures(String[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
if (i != j) {
List<Pair> pairs = feature2neighbors.get(array[i]);
if (pairs == null) {
pairs = new LinkedList<>();
feature2neighbors.put(array[i], pairs);
}
Pair toAdd = new Pair(array[j]);
int index = pairs.indexOf(toAdd);
if (index == -1) {
pairs.add(toAdd);
} else {
pairs.get(index).incr();
}
}
}
}
}
static void print(Map<String, List<Pair>> feature2neighbors) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, List<Pair>> e : feature2neighbors.entrySet()) {
builder.append(e.getKey()).append(" -> ");
Iterator<Pair> it = e.getValue().iterator();
builder.append(it.next().toString());
while(it.hasNext()) {
builder.append(" ").append(it.next().toString());
}
builder.append("\n");
}
System.out.println(builder.toString());
}
public static void main(String[] args) {
//I assume that data is sorted
Object[][] data = { { 5, "F1" }, //
{ 5, "F2" }, //
{ 6, "F1" }, //
{ 6, "F2" }, //
{ 7, "F3" }, //
{ 7, "F1" }, //
{ 7, "F2" }, //
{ 8, "F1" }, //
{ 9, "F1" }, //
{ 10, "F1" }, //
};
List<String> features = new LinkedList<>();
int id = getId(data, 0);
for (int i = 0; i < data.length; i++) {
if (id != getId(data, i)) {
processFeatures(features.toArray(new String[0]));
features = new LinkedList<>();
id = getId(data, i);
}
features.add(getFeature(data, i));
}
print(feature2neighbors);
}
}
Out:
F1 -> (F2, 3) (F3, 1)
F3 -> (F1, 1) (F2, 1)
F2 -> (F1, 3) (F3, 1)

Java Challenge in LinkedList

Question 4:
Given an integer array, convert it into a linked list with each node containing one sequence.
Sample Input : [1, 3, 4, 5, 8, 9, 11, 13, 14, 15, 16, 20, 23, 30,31,32]
Sample Linked List : [1] -> [3,4,5] -> [8,9] -> [11] -> [13,14,15,16]->[20]->[23]->[30,31,32]
The question may seems to be easy but it's bit difficult to answer.Can anyone write code for the above in Java without using Collection or LinkedList ?
Below is the code for detecting sequence (which might help).
class Sequence {
public static String detectSequence(int seq[]) {
String result = "";
for (int i = 0; i < seq.length - 1; i++) {
if (seq[i + 1] == seq[i] + 1) {
result += seq[i] + " ";
if (i != seq.length - 2) {
if (seq[i + 1] != seq[i + 2] - 1) {
result += seq[i + 1];
}
}
} else {
result += " ";
}
}
if (seq[seq.length - 1] == seq[seq.length - 2] + 1) {
result += seq[seq.length - 1];
}
return result;
}
}
class Question1 {
public static void main(String[] cla) {
int seqArray[] = {
4, 1, 2, 3, 4, 5, 8, 4, 7, 4, 5, 6, 7, 7, 7, 7, 7, 10, 11, 13, 1, 2, 3, 4
};
String res = Sequence.detectSequence(seqArray);
System.out.println(res);
}
}
just to give you a start...
public YourLinkedList splitToSequences(int[] array) {
YourLinkedList list = new YourLinkedList();
if(array.length > 0) {
YourSequence sequence = new YourSequence();
int currentNumber;
int lastNumber = array[0];
sequence.add(lastNumber);
for(int index = 1; index < array.length; index++) {
currentNumber = array[index];
if(currentNumber != lastNumber + 1) { // curentNumber breaks the sequence
list.add(sequence); // save the old sequence to list
sequence = new YourSequence(); // and start a new one
}
sequence.add(currentNumber);
}
list.add(sequence);
}
return list;
}
Now go and figure out your linked list and sequence classes and do the printing stuff...
A minimalistic implementation of a linked list
public class MyLinkedList<T1> {
private MyLinkedListItem<T1> first = null;
private MyLinkedListItem<T1> last = null;
public MyLinkedList() {
}
public void add(T1 item) {
MyLinkedListItem<T1> newItem = new MyLinkedListItem<T1>(item);
if (first == null) {
first = newItem;
} else {
last.setNext(newItem);
}
last = newItem;
}
#Override
public String toString() {
StringBuffer buffer = new StringBuffer();
if(first != null) {
MyLinkedListItem<T1> current = first;
while(current.hasNext()) {
buffer.append(current.toString());
buffer.append(" -> ");
current = current.getNext();
}
buffer.append(current.toString());
}
return buffer.toString();
}
private class MyLinkedListItem<T2> {
private T2 data;
private MyLinkedListItem<T2> next = null;
public MyLinkedListItem(T2 data) {
this.data = data;
}
public boolean hasNext() {
return next != null;
}
public MyLinkedListItem<T2> getNext() {
return next;
}
public void setNext(MyLinkedListItem<T2> next) {
this.next = next;
}
#Override
public String toString() {
return data.toString();
}
}
}
First the code that splits the array in several chunks can be written using an Iterator to avoid to mix this algorithm with the code that creates the linked list.
Then there is a way to implement a simple linked list in a functional fashion (so the whole list is immutable).
Obviously, this code is unreadable but fun !
import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.IntStream;
public class LinkedConsecutiveInts {
private static Iterator<int[]> iterator(int[] array) {
return new Iterator<int[]>() {
private int index;
#Override
public boolean hasNext() {
return index < array.length;
}
#Override
public int[] next() {
int first = array[index];
int value = first + 1;
int i = 1;
for(; index + i < array.length && array[index + i] == value++; i++) {
// empty
}
index += i;
return IntStream.range(first, first + i).toArray();
}
};
}
interface Seq<T> {
void forEach(Consumer<? super T> consumer);
default <U> Seq<U> map(Function<? super T, ? extends U> mapper) {
return consumer -> forEach(e -> consumer.accept(mapper.apply(e)));
}
default String joining(String separator) {
StringBuilder builder = new StringBuilder();
forEach(e -> builder.append(e).append(separator));
if (builder.length() != 0) {
builder.setLength(builder.length() - separator.length());
}
return builder.toString();
}
static <T> Seq<T> from(Iterator<? extends T> it) {
if (!it.hasNext()) {
return consumer -> { /* empty */ };
}
T element = it.next();
Seq<T> next = from(it);
return consumer -> { consumer.accept(element); next.forEach(consumer); };
}
}
public static void main(String[] args) {
int[] values = { 1, 3, 4, 5, 8, 9, 11, 13, 14, 15, 16, 20, 23, 30, 31, 32 };
Seq<int[]> seq = Seq.from(iterator(values));
System.out.println(seq.map(Arrays::toString).joining(" -> "));
}
}

Categories

Resources