I've been working for hours trying to order a linked list of strings alphabetically (dictionary-like). The given string is lowercase only.
For example, input of: "hello my name is albert" will be sorted in the list as: Node 1: albert,
Node 2: hello,
Node 3: is,
etc..
My code so far reads a string like the example above and insert it as nodes - unordered.
I've searched in the web for ways to sort a linked list alphabetically with good performance, and I found Merge Sort can be usefull.
I've changed the merge sort to work for string using compareTo() but my code returns nullPointerException error in the following line:
if(firstList._word.compareTo(secondList._word) < 0){
I'm looking for help to fix the following code or another way for sorting a linked list alphabetically (without Collection.sort)
My full code is (after trying to add the merge sort to work with my code):
public class TextList
{
public WordNode _head;
public TextList()
{
_head = null;
}
public TextList (String text)
{
this._head = new WordNode();
int lastIndex = 0;
boolean foundSpace = false;
String newString;
WordNode prev,next;
if (text.length() == 0) {
this._head._word = null;
this._head._next = null;
}
else {
for (int i=0;i<text.length();i++)
{
if (text.charAt(i) == ' ') {
newString = text.substring(lastIndex,i);
insertNode(newString);
// Update indexes
lastIndex = i;
// set to true when the string has a space
foundSpace = true;
}
}
if (!foundSpace) {
//If we didnt find any space, set the given word
_head.setWord(text);
_head.setNext(null);
}
else {
//Insert last word
String lastString = text.substring(lastIndex,text.length());
WordNode lastNode = new WordNode(_head._word,_head._next);
_head.setNext(new WordNode(lastString,lastNode));
}
sortList(_head);
}
}
private void insertNode(String word)
{
//Create a new node and put the curret node in it
WordNode newWord = new WordNode(_head._word,_head.getNext());
//Set the new information in the head
_head._word = word;
_head.setNext(newWord);
}
private WordNode sortList(WordNode start) {
if (start == null || start._next == null) return start;
WordNode fast = start;
WordNode slow = start;
// get in middle of the list :
while (fast._next!= null && fast._next._next !=null){
slow = slow._next; fast = fast._next._next;
}
fast = slow._next;
slow._next=null;
return mergeSortedList(sortList(start),sortList(fast));
}
private WordNode mergeSortedList(WordNode firstList,WordNode secondList){
WordNode returnNode = new WordNode("",null);
WordNode trackingPointer = returnNode;
while(firstList!=null && secondList!=null){
if(firstList._word.compareTo(secondList._word) < 0){
trackingPointer._next = firstList; firstList=firstList._next;
}
else {
trackingPointer._next = secondList; secondList=secondList._next
;}
trackingPointer = trackingPointer._next;
}
if (firstList!=null) trackingPointer._next = firstList;
else if (secondList!=null) trackingPointer._next = secondList;
return returnNode._next;
}
public String toString() {
String result = "";
while(_head.getNext() != null){
_head = _head.getNext();
result += _head._word + ", ";
}
return "List: " + result;
}
public static void main(String[] args) {
TextList str = new TextList("a b c d e a b");
System.out.println(str.toString());
}
}
In the past i have made a method to sort strings alphabetically in an array as school HW, so umm here it is:
private void sortStringsAlphabetically(){
for (int all = 0; all < names.length; all++) {
for (int i = all + 1; i < names.length; i++) {
if (names[all].compareTo(names[i]) > 0) {
String tmp = names[i];
names[i] = names[all];
names[all] = tmp;
}
}
}
}
This piece of code works for Arrays and specifically for an array of names. You can tweak it to work with the list, it is very simple especially if we consider the wide range of methods in the List interface and all it's implementations.
Cheers.
If you don't wanna to have a huge code who gets every first letter of the word and sort them, do it with Collection.sort()
I don't know what is the proplem on Collection.sort() so use it
Here is a short code, that does exactually this what you want to:
String test = "hello my name is albert";
test = test.replaceAll(" ", "\n");
String[] te = test.split("\n");
List<String> stlist = new ArrayList<String>();
for(String st : te) {
stlist.add(st);
}
Collections.sort(stlist);
Regarding NPE you said it is probably because you are having an null string in head at first and keep adding this in insert method.
this._head = new WordNode();
Also the adding last element is also not proper. Just reuse the insert method like below
insertNode(text.substring(lastIndex,text.length()));
These are the ones I thought having problem when you are converting string to lined list
You can use the below code to handle the first null
private void insertNode(String word) {
if (this._head == null) {
this._head = new WordNode(word, null);
} else {
WordNode newWord = new WordNode(_head._word, _head.getNext());
_head._word = word;
_head.setNext(newWord);
}
}
Related
I'm trying to put the text file into a linkedlist and count how many duplicate words are in the linked list.
Here is my base code.
public class Node{
private Node next;
private String data;
private int Dup_Counter= 0;
public Node(){
this.next = null;
this.data = data;
this.Dup_Counter = 0;
}
public String fiile_Reader() throws FileNotFoundException {
File file = new File("/Users/djhanz/IdeaProjects/datalab2/pg174.txt"); //reading a plain text file
Scanner scan = new Scanner(file);
String fileContent = ""; // initalizing an empty string to put scanned string text file
while (scan.hasNextLine()) {
fileContent = fileContent.concat(scan.nextLine() + "\n"); // scan and put in into string object
}
fileContent = fileContent.replaceAll("\\p{Punct}", ""); // remove all the punctuation characters
fileContent = fileContent.toLowerCase();
return fileContent;
}
public void insert() throws FileNotFoundException{
Node cursor = head;
Single_LL Linked_L = new Single_LL();
String file_content = Linked_L.fiile_Reader();
String[] splitted_File = file_content.split(" ");
for(int i=0 ; i<splitted_File.length; i++){
Linked_L.add(splitted_File[i]);
}
}
public int Word_Counter(String word){
String compare =word;
Node cursor = head;
int counter = 0;
while(cursor!=null){
if (cursor.data.equals(compare)){
counter++;
}
cursor = cursor.next;
}
return counter;
}
public void Dup_Count(){
Node cursor = head.next;
while (cursor != null){
if(head.data == cursor.data){
head.Dup_Counter++;
break;
}
cursor = cursor.next;
System.out.println(cursor.Dup_Counter);
}
head = head.next;
}
public String dup_num(){
Node cursor = head;
String rtn = "";
while (cursor!= null){
if(cursor.Dup_Counter > 20 ){
rtn += cursor.data + " -> ";
}
cursor = cursor.next;
}
return rtn;
}
public static void main(String[] args) throws FileNotFoundException {
Program1 test = new Program1();
String file_content = test.fiile_Reader();
Single_LL Linked_L = new Single_LL();
String[] splitted_File = file_content.split(" ");
int spli_len = splitted_File.length;
for(int i =0; i< spli_len; i++){
Linked_L.add(splitted_File[i]);
}
My approach is that I added anthoer variable in Node Class called dup_counter.
Function Dup_Count() is looping through the linked list and when it sees that duplicate it updates the Node's dup_counter variable.
I'm trying to find words that appeared more than 20 times and dup_num() is my approach to do this. Looping through the linkedlist and if the Node's dup_counter is more than 20 add it to the string and return it. However, Dup_Count() is not in fact updating the dup_count value. Insertion worked fine but I can't seem to find what is wrong with my dup_counter. Can someone please help me fix the bug?
I would recommend trying to simplify your task using a Map as follows
Somehow get all of the words into a collection Collection<String> words. This should be easy to do using the reader code you already have.
Now, to count the number of occurrences of each word, we can use a Map:
Map<Integer, Long> counter = words.stream()
.collect(Collectors.groupingBy(p -> p, Collectors.counting()));
Now you wanted to find all of the words that occurred more than 20 times, you could evaluate
Set<String> wordsOccuringManyTimes = counter
.entrySet().stream()
.filter(e -> e.getValue() > 20)
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
And if you wanted to get the total sum of all duplicates, you could simply evaluate
int duplicateCount = counter.values().stream().mapToInt(x -> x - 1).sum();
I am trying to do a programming problem a day on leetcode to improve my programming and am having issues adding to a LinkedList in the problem below. I was wondering if anyone could give me some pointers. I know my answer isn't the most efficient, I just wanted to start somewhere and work myself up. Everything within the method is stuff I did so far. I really appreciate any help.
///
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
Here's a picture with an example for a possible output:
https://imgur.com/a/g9rlb
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode l3 = new ListNode(-1);
ListNode curr = new ListNode(-1);
ListNode newNode = new ListNode(-1);
// Take numbers from linkedList and store in strings
String s1 = "";
String s2 = "";
// String values after being reveresed in the right direction.
String sR1 = "";
String sR2 = "";
while(l1 != null) {
s1 += l1.val;
l1 = l1.next;
}
while(l2 != null) {
s2 += l2.val;
l2 = l2.next;
}
//check
System.out.println(s1);
System.out.println(s2);
//reverse the string;
for(int i = s1.length()-1; i >= 0; i--) {
sR1 += s1.charAt(i);
}
for(int j = s2.length()-1; j >= 0; j--) {
sR2 += s2.charAt(j);
}
//Adding the numbers together to get the final value.
int n3 = Integer.parseInt(sR1) + Integer.parseInt(sR2);
//Converting ints to string so i can parse them into characters that will eventually be parsed into an int to return back to the LinkedList
String fin = Integer.toString(n3);
System.out.println(fin);
//adding the values to my final linked list that i'd be returning here. This is the part that isn't working.
for(int i = 0; i < fin.length()-1; i++){
String s = String.valueOf(fin.charAt(i));
int num = Integer.parseInt(s);
newNode = new ListNode(num);
if(l3.val == -1) {
l3 = newNode;
}
else {
curr = l3;
while(curr.next != null){
curr = curr.next;
}
curr.val = num;
}
}
return l3;
}
Maybe something like this? The concept here is straight forward.
The requirement is to reverse the nodes and adding them. Which means you just need to choose the right data structure to meet this requirement, which provides you last in first out? Stack. Now that you have you data in a stack just pop the items from the stack and add it up and get your expected result.
There are many ways to solve this, Using an ArrayList, using LinkedList, or plain old arrays, but try to correlate the problem with a known data structure and addressing it that way would give you meaningful output consistently. I could have just pointed you to the concept but having this code will help you think on how to address a specific problem based on the user requirement. Cheers, hope it helps.
import java.util.Scanner;
import java.util.Stack;
public class AddTuple {
public static void main(String[] args) {
Stack<Integer> leftTuple = new Stack<Integer>();
Stack<Integer> rightTuple = new Stack<Integer>();
populateTuple(leftTuple, rightTuple, 3);
Stack<Integer> result = addTuples(leftTuple, rightTuple);
System.out.print("Output: {");
int i = 0;
while (!result.isEmpty()) {
if (i != 0) {
System.out.print(", ");
}
System.out.print(result.pop());
i++;
}
System.out.println("}");
}
private static void populateTuple(Stack<Integer> leftTuple, Stack<Integer> rightTuple, int count) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("Input: ");
String input = scanner.nextLine();
if (input == null || !input.contains("+") || !input.contains("{")) {
throw new RuntimeException("Usage: {x,y,z} + {a,b,c}");
}
String[] operandSplit = input.split("\\+");
String left = operandSplit[0].trim();
String right = operandSplit[1].trim();
left = left.replaceAll("\\{", "");
left = left.replaceAll("\\}", "");
right = right.replaceAll("\\{", "");
right = right.replaceAll("\\}", "");
String[] leftSplit = left.split(",");
String[] rightSplit = right.split(",");
for (int i = 0; i < leftSplit.length; i++) {
leftTuple.push(Integer.parseInt(leftSplit[i].trim()));
}
for (int i = 0; i < rightSplit.length; i++) {
rightTuple.push(Integer.parseInt(rightSplit[i].trim()));
}
} finally {
scanner.close();
}
}
private static Stack<Integer> addTuples(Stack<Integer> leftTuple, Stack<Integer> rightTuple) {
Stack<Integer> result = new Stack<Integer>();
int carryForward = 0;
while (!leftTuple.isEmpty()) {
int addition = leftTuple.pop() + rightTuple.pop() + carryForward;
if (addition > 9) {
carryForward = 1;
addition = 10 - addition;
}
result.push(addition);
}
return result;
}
}
I want to determine whether two single-linked lists are identical or not.
If they are identical, the program should print matched letters.
Examples:
murmur and tartar are identical, because they both have the same pattern "abcabc".
AAABBCbbaaa and 11122322111 are identical
Matched letters:
A ↔ 1
B ↔ 2
C ↔ 3
I must use ONLY single linked list.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Please enter a string:");
String linked1=scanner.nextLine();
System.out.println();
System.out.print("Please enter another string:");
String linked2=scanner.nextLine();
SingleLinkedList SLL1 = new SingleLinkedList();
SingleLinkedList SLL2 = new SingleLinkedList();
for (int i = 0; i < linked1.length(); i++) {
char a=linked1.charAt(i);
a = Character.toLowerCase(a);
SLL1.addToEnd(a);
}
for (int i = 0; i < linked2.length(); i++) {
char a=linked2.charAt(i);
a = Character.toLowerCase(a);
SLL2.addToEnd(a);
}
public class SingleLinkedList{
private Node head;
public SingleLinkedList()
{
head = null;
}
public boolean isEmpty(){
return head == null;
}
public void addToEnd(Object dataToAdd)
{
Node newNode = new Node(dataToAdd);
if(head == null)
{
head = newNode;
}
else
{
Node temp = head;
while(temp.getLink() != null)
{
temp = temp.getLink();
}
temp.setLink(newNode);
}
}
public String display()
{
String output = "";
Node temp = head;
while(temp != null)
{
output += temp.getData() + " ";
temp = temp.getLink();
}
return output;
}
}
You can do it, but it's a tedious work and I haven't got the strength to write the actual code.
You should encode (not encrypt) your sequences.
A trivial way of encoding your list is to iterate it's values, each value you pick must be checked against a seq of already encountered values.
If the value is not present on the check seq, you add it and go on.
At the end of this you will have a seq of unique values ordered as you found them.
Then, for each element of your seq you find which is the position in your check set, and build a new seq of those positions.
You repeat it for both your sequences, and then you can match your encoded sequences.
You should create an indexOf method for your seq to simplify your program.
I am studying some Suffix tree implementation and here is one reference implementation, and question is how "indexes" (refer line 19) is used for class SuffixTreeNode? I am not sure if "indexes" is useful and I think probably we just need to keep all nodes and their children character value? Not find too much values of "indexes" is used for class SuffixTreeNode.
Please feel free to correct me. Any insights are appreciated.
public class SuffixTree {
SuffixTreeNode root = new SuffixTreeNode();
public SuffixTree(String s) {
for (int i = 0; i < s.length(); i++) {
String suffix = s.substring(i);
root.insertString(suffix, i);
}
}
public ArrayList<Integer> getIndexes(String s) {
return root.getIndexes(s);
}
}
public class SuffixTreeNode {
HashMap<Character, SuffixTreeNode> children = new
HashMap<Character, SuffixTreeNode>();
char value;
ArrayList<Integer> indexes = new ArrayList<Integer>();
public SuffixTreeNode() { }
public void insertString(String s, int index) {
indexes.add(index);
if (s != null && s.length() > 0) {
value = s.charAt(0);
SuffixTreeNode child = null;
if (children.containsKey(value)) {
child = children.get(value);
} else {
child = new SuffixTreeNode();
children.put(value, child);
}
String remainder = s.substring(1);
child.insertString(remainder, index);
}
}
public ArrayList<Integer> getIndexes(String s) {
if (s == null || s.length() == 0) {
return indexes;
} else {
char first = s.charAt(0);
if (children.containsKey(first)) {
String remainder = s.substring(1);
return children.get(first).getIndexes(remainder);
}
}
return null;
}
}
public class Question {
public static void main(String[] args) {
String testString = “mississippi”;
String[] stringList = {“is”, “sip”, “hi”, “sis”};
SuffixTree tree = new SuffixTree(testString);
for (String s : stringList) {
ArrayList<Integer> list = tree.getIndexes(s);
if (list != null) {
System.out.println(s + “: “ + list.toString());
}
}
}
}
indexes is surely needed for the implementation you are looking at of Suffix Tree (there are multiple versions of suffix tree some more optimized than others). The indexes variable plays an integral part in returning the indices where the sub-string (is, sip, hi, sis) exist in the original string (mississippi) back to the calling method. getIndexes returns indexes in its base case this is how you get the list of occurrences of each sub-string. see below output
is: [1, 4]
sip: [6]
sis: [3]
My code is throwing an error that I've never seen before. So hey! I guess I'm learning ;) Anyway, I did some reading and generally this error is thrown when a list that is being iterated over is modified mid-iteration. However, I'm pretty sure I'm not modifying it. While the error is being thrown on partition(), if I don't assign a new value for currentList in updateCurrentList() (by commenting out the code), the program no longer throws the error. These two functions are called one after the other in my play() method, however the list iteration should be complete by the time the change is made. What am I missing? Do I have to close down the iterator somehow?
package hangman;
import java.io.*;
import java.util.*;
import javax.swing.JOptionPane;
public class Hangman {
private Map<String, List<String>> wordPartitions; // groups words according to positions of guessed letter
private List<String> currentList; // remaining possible words that fit the information given so far
Set<Character> wrongGuesses; // holds all the "wrong" guesses so far
StringBuilder guessString; // current state of the word being guessed
String justHyphens; // for checking whether a guess was "wrong"
// initialize fields
// currentList should contain all (and only) words of length wordLength
// justHyphens and guessString should consist of wordLength hyphens
public Hangman(int wordLength) throws FileNotFoundException {
this.currentList = new ArrayList<String>();
addWords(wordLength);
wrongGuesses = new HashSet();
for(int i = 0; i < wordLength; i++) {
justHyphens += "-";
}
guessString = new StringBuilder();
wordPartitions = new HashMap();
}
private void addWords(int wordLength) throws FileNotFoundException {
Scanner words = new Scanner(new File("lexicon.txt"));
String word = "";
while(words.hasNext()) {
word = words.next();
if (word.length() == wordLength) {
currentList.add(word);
}
}
}
// main loop
public void play() {
char choice;
do {
choice = getUserChoice();
partition(choice);
updateCurrentList(choice);
} while (!gameOver());
endMessage();
}
// display the guessString and the missed guesses
// and get the next guess
private char getUserChoice() {
//generate a string from the incorrect choices char list
String wrong = "";
char letter;
if(!wrongGuesses.isEmpty()) {
Iterator<Character> letters = wrongGuesses.iterator();
letter = letters.next();
while(letters.hasNext()) {
letter = letters.next();
wrong += ", " + letter;
}
}
String letterStr = JOptionPane.showInputDialog("Incorrect choices: "+ wrong +"\n Tested letters: "+ guessString.toString() +"\nplease input a letter.");
return letterStr.charAt(0);
}
// use wordPartitions to partition currentList using
// keys returned by getPartitionKey()
private void partition(char choice) {
String word = "";
String key = "";
List<String> tempList = new ArrayList<String>();
Iterator<String> words = currentList.iterator();
//Generate a key for each word and add to appropriate arraylist within map.
while(words.hasNext()) {
word = words.next();
key = getPartitionKey(word, choice);
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
tempList.add(word);
wordPartitions.put(key, tempList);
} else {
tempList.clear();
tempList.add(word);
wordPartitions.put(key, new ArrayList<String>());
}
}
}
// update currentList to be a copy of the longest partition
// if choice was "wrong", add choice to wrongGuesses
// if choice was "right", update guessString
private void updateCurrentList(char choice) {
String key = findLongestList();
currentList = wordPartitions.get(key);
if(key.equals(justHyphens)) {
wrongGuesses.add(choice);
} else {
addLetterToGuessString(guessString, choice, key);
}
}
private String findLongestList() {
Set<String> keySet = wordPartitions.keySet();
Iterator<String> keys = keySet.iterator();
String maxKey = "";
int maxKeyLength = 0;
List<String> tempList;
String tempKey = "";
while(keys.hasNext()) {
tempKey = keys.next();
tempList = wordPartitions.get(tempKey);
if(tempList.size() > maxKeyLength) {
maxKeyLength = tempList.size();
maxKey = tempKey;
}
}
return maxKey;
}
// checks for end of game
private boolean gameOver() {
return false;
}
// display the guessString and the missed guesses
// and print "Congratulations!"
private void endMessage() {
JOptionPane.showMessageDialog(null, "Congrats, yo!");
}
// returns string with '-' in place of each
// letter that is NOT the guessed letter
private String getPartitionKey(String s, char c) {
String word = "";
String letter = Character.toString(c);
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == c) {
word += letter;
} else {
word += "-";
}
}
return word;
}
// update guessString with the guessed letter
private void addLetterToGuessString(StringBuilder guessString, char letter, String key) {
for(int i = 0; i < key.length(); i++) {
if(key.charAt(i) != '-') {
guessString.setCharAt(i, key.charAt(i));
}
}
}
}
The problem is that you are modifying a collection while you are iterating over it.
The collection is currentList, you are iterating over it in partition(). You modify it when you add a word to tempList here:
key = getPartitionKey(word, choice);
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
tempList.add(word);
wordPartitions.put(key, tempList);
} else {
Why ? Because previously you called updateCurrentList() from play():
do {
choice = getUserChoice();
partition(choice);
updateCurrentList(choice);
} while (!gameOver());
And you updated currentList:
String key = findLongestList();
currentList = wordPartitions.get(key);
So, if the key returned by getPartitionKey(word, choice) is the same as the key previously returned by findLongestList(), currentListwill be the same as tempList, and so you will be modifying the collection you are iterating over.
The solution ? If tempList is the same as currentList, don't add the word to it (it already have the word, by definition). So, you can rewrite your if-else like that (I removed some useless code):
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
} else {
wordPartitions.put(key, new ArrayList<String>());
}
if (tempList!=currentList) {
tempList.add(word);
}