So here is my dart version of java's huffman algorithm. I have a problem with getCodeForChar function, it says what return type of String can't be null, and even if for every return i added +'h'. How to deal with null-safety at that case?
Originally the function is:
public String getCodeForCharacter(Character ch, String parentPath) {
if (content == ch) {
return parentPath;
} else {
if (left != null) {
String path = left.getCodeForCharacter(ch, parentPath + 0);
if (path != null) {
return path;
}
}
if (right != null) {
String path = right.getCodeForCharacter(ch, parentPath + 1);
if (path != null) {
return path;
}
}
}
return null;
}
And my full code:
import 'dart:collection';
void main() {
String text = 'where there,s a will there,s a way';
SplayTreeMap? frequencies = countFrequency(text);
frequencies.forEach((key, value) {
print('$key \t $value');
});
var sk = frequencies.values.toList();
print(sk);
List<CodeTreeNode> codeTreeNode = [];
for (String? c in frequencies.keys) {
codeTreeNode.add(new CodeTreeNode.simple(c, frequencies[c]));
}
CodeTreeNode tree = huffman(codeTreeNode);
SplayTreeMap<String, String> codes = SplayTreeMap();
for (String c in frequencies.keys) {
//codes.addAll(c, (value) => tree.getCodeForChar(c, ""));
codes.addAll({c: tree.getCodeForChar(c, "")});
}
codes.forEach((key, value) {
print('$key \t $value');
});
}
SplayTreeMap countFrequency(String text) {
SplayTreeMap? myMap = SplayTreeMap<String, int>();
int tempCount = 0;
// for (int i = 0; i < text.length; i++) {
// String c = text[i];
// int count = myMap[];
// //myMap[int count]
// myMap[c] = count != -1 ? count + 1 : 1;
// }
for (int i = 0; i < text.length; i++) {
String c = text[i];
if (myMap.containsKey(c)) {
//tempCount++;
//myMap[text[i]] = tempCount;
tempCount = myMap[c] + 1;
myMap.update(c, (value) => tempCount);
} else {
myMap[c] = 1;
}
//счетчик = мапа[i] +1;
//заменить эту валью на счетчик
}
return myMap;
}
CodeTreeNode huffman(List<CodeTreeNode> codeTreeNodes) {
while (codeTreeNodes.length > 1) {
codeTreeNodes.sort();
CodeTreeNode left = codeTreeNodes.removeAt(codeTreeNodes.length - 1);
CodeTreeNode right = codeTreeNodes.removeAt(codeTreeNodes.length - 1);
CodeTreeNode parent =
CodeTreeNode.hard(null, right.weight + left.weight, left, right);
codeTreeNodes.add(parent);
}
return codeTreeNodes[0];
}
class CodeTreeNode implements Comparable<CodeTreeNode> {
String? content;
//List<Node> children = [];
int weight;
CodeTreeNode? left;
CodeTreeNode? right;
CodeTreeNode.simple(this.content, this.weight);
CodeTreeNode.hard(this.content, this.weight, this.left, this.right);
// Node(String content, int weight){(this.content, this.weight)
// }
#override
int compareTo(CodeTreeNode other) {
// TODO: implement compareTo
//throw UnimplementedError();
return other.weight - weight;
}
String getCodeForChar(String ch, String parentPath) {
if (content == ch) {
return 'h'+ parentPath;
} else {
if (left != null) {
String path = left.getCodeForChar(ch, parentPath + '0');
if (path != null) {
return 'h'+path;
}
}
if (right != null) {
String path = right.getCodeForChar(ch, parentPath + '1');
if (path != null) {
return 'h'+ path;
}
}
}
return 'hui';
}
}
Inserting you code int DartPad shows two errors.
The method 'getCodeForChar' can't be unconditionally invoked because the receiver can be 'null'.
Fixing this is easy: Insert the bang operator ('!'):
String path = left!.getCodeForChar(ch, parentPath + '0');
See also Working with nullable fields
Related
i'm using TextFomater to make my TextField as a dynamic monetary textfield, using the following code:
public class MoneyTextFieldOperator implements UnaryOperator<TextFormatter.Change>
{
final char seperatorChar = '.';
final Pattern p;
int length;
public MoneyTextFieldOperator(int length) {
this.length=length;
this.p = Pattern.compile("[0-9. ]*");
}
#Override
public TextFormatter.Change apply(final TextFormatter.Change c) {
if (c.isContentChange() && c.getControlNewText().length() > length) {
return null;
}
else {
if (!c.isContentChange()) {
return c;
}
final String newText = c.getControlNewText();
if (newText.isEmpty()) {
return c;
}
if (!this.p.matcher(newText).matches()) {
return null;
}
final int suffixCount = c.getControlText().length() - c.getRangeEnd();
int digits = suffixCount - suffixCount / 4;
final StringBuilder sb = new StringBuilder();
if (digits % 3 == 0 && digits > 0 && suffixCount % 4 != 0) {
sb.append('.');
}
for (int i = c.getRangeStart() + c.getText().length() - 1; i >= 0; --i) {
final char letter = newText.charAt(i);
if (Character.isDigit(letter)) {
sb.append(letter);
if (++digits % 3 == 0) {
System.out.println("digits : "+digits);
sb.append('.');
}
}
}
if (digits % 3 == 0) {
sb.deleteCharAt(sb.length() - 1);
}
sb.reverse();
final int length = sb.length();
c.setRange(0, c.getRangeEnd());
c.setText(sb.toString());
c.setCaretPosition(length);
c.setAnchor(length);
return c;
}}
And calling it as follow :
montantTextField.setTextFormatter(new TextFormatter(new MoneyTextFieldOperator(10)));
the issue is that it's valable just for integer numbers, and i want make it now valable for decimal numbers, so when the user type 254648,32
it converts it to 254.648,32
here is a proposed solution using numberformat :
public class MoneyTextFieldOperator implements UnaryOperator<TextFormatter.Change> {
Pattern p;
int length;
public MoneyTextFieldOperator(int length) {
this.length = length;
this.p = Pattern.compile("[0-9. ,]*");
}
#Override
public TextFormatter.Change apply(TextFormatter.Change c) {
Pattern acceptedKeywords = Pattern.compile("[0-9 . ,]*");
int wholeNumberLength = 9;
int decimalPartLength = 2;
String newText = c.getControlNewText();
String wholeNumber;
String decimalPart;
if (newText.contains(",")) {
wholeNumber = newText.substring(0, newText.indexOf(","));
decimalPart = newText.substring(newText.indexOf(",") + 1, newText.length());
} else {
wholeNumber = newText;
decimalPart = "";
}
Matcher matcher = acceptedKeywords.matcher(newText);
boolean isMatchingWithAcceptedKeywords = matcher.matches();
String letter = c.getText();
if (newText.isEmpty()) {
return c;
} else if (!c.isContentChange()) {
return c;
} else if (countChar(c.getControlNewText(), ',') > 1) {
return null;
} else if (!isMatchingWithAcceptedKeywords) {
return null;
} else if (wholeNumber.replace(".", "").length() > wholeNumberLength || decimalPart.length() > decimalPartLength) {
return null;
} else if (c.getControlNewText().charAt(0)==',') {
return null;
}
else {
c.setRange(0, c.getControlText().length());
if (newText.contains(",")) {
c.setText(NumbersTools.getMonetaryFormat(Integer.parseInt(wholeNumber.replace(".", ""))) + "," + decimalPart);
} else {
c.setText(NumbersTools.getMonetaryFormat(Integer.parseInt(wholeNumber.replace(".", ""))));
}
c.setCaretPosition(c.getText().length());
c.setAnchor(c.getText().length());
return c;
}
}
public int countChar(String string, char charactere) {
int x = 0;
for (int i = 0; i <= string.length() - 1; ++i) {
if (string.charAt(i) == charactere) {
++x;
}
}
return x;
}
and you use it as follow :
textField.setTextFormatter(new TextFormatter<Object>(new MoneyTextFieldOperator(10)));
I almost have a working Huffman algorithm but I'm having trouble figuring out how to implement it / or make it work together with the Code class. It is my method generateHuffmanCode that needs to be working with the Code class.
I don't know if I'm missing something or if I'm just using the wrong approach.
My Huffman class
package code;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import code.Code;
import application.TCode;
class Node{
Character ch;
Integer freq;
Node left = null, right = null;
Node(Character ch, Integer freq){
this.ch = ch;
this.freq = freq;
}
public Node (Character ch, Integer freq, Node left, Node right) {
this.ch = ch;
this.freq = freq;
this.left = left;
this.right = right;
}
}
public class Huffman {
public static void encode(Node root, String str, Map<Character, String> huffmanCode) {
if (root == null) {
return;
}
if(isLeaf(root)) {
huffmanCode.put(root.ch, str.length() > 0 ? str : "1");
}
encode(root.left, str + '0', huffmanCode);
encode(root.right, str + '1', huffmanCode);
}
public static int decode(Node root, int index, StringBuilder sb) {
if(root == null) {
return index;
}
index++;
if(isLeaf(root)) {
System.out.println(root.ch);
}
index++;
root = (sb.charAt(index) == '0') ? root.left : root.right;
index = decode(root, index, sb);
return index;
}
public static boolean isLeaf(Node root) {
return root.left == null && root.right == null;
}
public static void generateHuffmanCode(Code c) {
// if(c == null || c.length() == 0) {
// return;
// }
Map<Character, double> freq = new HashMap<>(); // why can't I use a double in Map<Character, double>??
for(char s : probability.toCharArry()){
freq.put(s, freq.getOrDefault(s, 0) + 1);
}
PriorityQueue<Node> pq;
pq = new PriorityQueue<>(Comparator.comparingInt(l -> l.freq));
for(var entry: freq.entrySet()) {
pq.add(new Node(entry.getKey(), entry.getValue()));
}
while (pq.size() != 1) {
Node left = pq.poll();
Node right = pq.poll();
int sum = left.freq + right.freq;
pq.add(new Node(null, sum, left, right));
}
Node root = pq.peek();
Map<Character, String> huffmanCode = new HashMap<>();
encode(root, "", huffmanCode);
System.out.println("Huffman Codes are: " + huffmanCode);
System.out.println(probability);
StringBuilder sb = new StringBuilder();
for(char s : probability.toCharArray()) {
sb.append(huffmanCode.get(s));
}
System.out.println("The endcoded String is: " + sb);
System.out.println("The decoded String is: " );
if (isLeaf(root)) {
while (root.freq-- > 0) {
System.out.println(root.ch);
}
} else {
int index = -1;
while(index < sb.length() - 1) {
index = decode(root, index, sb);
}
}
}
public static void main(String[] args) {
generateHuffmanCode(Code probability);
// Code.CodeItem[] ci = {
// new Code.CodeItem("A", 0.12),
// new Code.CodeItem("B", 0.19),
// new Code.CodeItem("C", 0.40),
// new Code.CodeItem("D", 0.13),
// new Code.CodeItem("E", 0.16)
// };
// Code c = new Code(ci);
}
}
And my Code class
package code;
public final class Code {
private CodeItem[] item = null;
public final static class CodeItem {
private String symbol;
private double probability; // the sum of all probabilities must be approx. 1
private String encoding; // a string containing only '0' and '1'
public CodeItem(String symbol, double probability, String encoding) {
this.symbol = symbol.trim();
this.probability = probability;
this.encoding = encoding;
if (!is01() || this.symbol == null || this.symbol.length() == 0 || this.probability < 0.0)
throw new IllegalArgumentException();
}
public CodeItem(String symbol, double probability) {
this(symbol, probability, null);
}
public String getSymbol() {
return symbol;
}
public double getProbability() {
return probability;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
public boolean is01() {
if (encoding == null || encoding.length() == 0)
return true;
for (int i = 0; i < encoding.length(); ++i)
if ("01".indexOf(encoding.charAt(i)) < 0)
return false;
return true;
}
}
public Code(CodeItem[] codeItem) {
if (codeItem == null || codeItem.length == 0)
throw new IllegalArgumentException();
double sum = 0.0;
for (int i = 0; i < codeItem.length; ++i) {
sum += codeItem[i].probability;
if (codeItem[i].probability == 0.0)
throw new IllegalArgumentException();
}
if (Math.abs(sum - 1.0) > 1e-10)
throw new IllegalArgumentException();
item = new CodeItem[codeItem.length];
for (int i = 0; i < codeItem.length; ++i)
item[i] = codeItem[i];
}
public boolean is01() {
for (int i = 0; i < item.length; ++i)
if (!item[i].is01())
return false;
return true;
}
public double entropy() {
double result = 0.0;
for(int i = 0; i < item.length; ++i)
result += item[i].probability * (-Math.log(item[i].probability) / Math.log(2.0));
return result;
}
public double averageWordLength() {
double result = 0.0;
for(int i = 0; i < item.length; ++i)
result += item[i].encoding.length() * item[i].probability;
return result;
}
public boolean isPrefixCode() {
for(int i = 1; i < item.length; ++i)
for(int j = 0; j < i; ++j)
if (item[i].encoding.startsWith(item[j].encoding) || item[j].encoding.startsWith(item[i].encoding))
return false;
return true;
}
public int size() {
return item.length;
}
public CodeItem getAt(int index) {
return item[index];
}
public CodeItem getBySymbol(String symbol) {
for (int i = 0; i < item.length; ++i) {
if (item[i].symbol.equals(symbol))
return item[i];
}
return null;
}
public String toString() {
String result = "";
for(int i = 0; i < item.length; ++i) {
result += item[i].symbol + " (" + item[i].probability + ") ---> " + item[i].encoding + "\n";
}
return result.substring(0, result.length()-1);
}
}
I have to answer the following question as a working code:
Design an algorithm for the following operations for a binary tree BT, and show the worst-case running times for each implementation:
preorderNext(x): return the node visited after node x in a pre-order traversal of BT.
postorderNext(x): return the node visited after node x in a post-order traversal of BT.
inorderNext(x): return the node visited after node x in an in-order traversal of BT.
which I answered using the following code:
class Node {
public Node parent, left, right;
int data;
public Node(int data)
{
this.data = data;
}
public String toString() {
return "" + this.data;
}
}
public class BinaryTree {
Node root;
BinaryTree() {
root = null;
}
public static Node preorderNext(Node v){
if(v.left != null) {
return v.left;
}
if(v.right != null) {
return v.right;
}
Node k = v.parent;
while (k != null && (k.right == null || k.right.equals(v))) {
v = k;
k = k.parent;
}
if(k == null) {
return null;
}
return k.right;
}
public static Node postorderNext(Node v){
Node k = v.parent;
if(k == null) {
return null;
}
if (k.right != null && k.right.equals(v)) {
return k;
}
if(k.right == null) {
return k;
}
k = k.right;
while (k.left != null || k.right != null) {
if(k.left != null) {
k = k.left;
}
else {
k = k.right;
}
}
return k;
}
public static Node inorderNext(Node v) {
if(v.right != null) {
Node k = v.right;
while (k.left != null) {
k = k.left;
}
return k;
}
if(v.parent == null) {
return null;
}
Node k = v.parent;
if(k.left != null && k.left.equals(v)) {
return k;
}
while (k != null && (k.right == null || k.right.equals(v))) {
v = k;
k = k.parent;
}
return k;
}
public static void main(String[] args) {
Node[] n = new Node[9];
for (int i = 1; i <= 9; i++) {
n[i-1] = new Node(i);
}
n[0].left = n[1];
n[0].right = n[2];
n[1].left = n[3];
n[1].right = n[4];
n[3].right = n[8];
n[2].left = n[5];
n[5].left = n[6];
n[6].right = n[7];
for(int i=1; i<=9; i++){
System.out.print("inOrderNext("+i+") = " + BinaryTree.inorderNext(n[i - 1]) + "\t");
System.out.print("preOrderNext("+i+") = " + BinaryTree.preorderNext(n[i - 1]) + "\t");
System.out.print("postOrderNext("+i+") = " + BinaryTree.postorderNext(n[i - 1]) + "\n");
}
}
}
Not 100% confident about the main method and postorderNext() since the output is always null (which it shouldn't)
inOrderNext(1) = 7 preOrderNext(1) = 2 postOrderNext(1) = null
inOrderNext(2) = 5 preOrderNext(2) = 4 postOrderNext(2) = null
inOrderNext(3) = null preOrderNext(3) = 6 postOrderNext(3) = null
inOrderNext(4) = 9 preOrderNext(4) = 9 postOrderNext(4) = null
inOrderNext(5) = null preOrderNext(5) = null postOrderNext(5) = null
inOrderNext(6) = null preOrderNext(6) = 7 postOrderNext(6) = null
inOrderNext(7) = 8 preOrderNext(7) = 8 postOrderNext(7) = null
inOrderNext(8) = null preOrderNext(8) = null postOrderNext(8) = null
inOrderNext(9) = null preOrderNext(9) = null postOrderNext(9) = null
any ideas on what went wrong for postorderNext()
I am trying to return the depth of the string in the radix tree after finding the string in the radix tree but I am always getting the value 2.
How can I keep storing the value of counter after incrementing it and get the recursiv method depth(nextNodeEdge, restString) called several time?
Code:
private int depth(TrieNode node, String s) {
int count = 1;
String communsubString = checkEdgeString(node.getNext(), s);
String restString = s.substring(communsubString.length());
if (node.getNext() != null && !node.getNext().isEmpty()) {
for (TrieNode nextNodeEdge : node.getNext()) {
if (nextNodeEdge.getEdge().equals(communsubString)) {
count++;
if (!restString.isEmpty()) {
count = depth(nextNodeEdge, restString);
} else {
System.out.println("Found");
}
}
}
}
return count;
}
You could just change:
count = depth(nextNodeEdge, restString);
to
count += depth(nextNodeEdge, restString);
The other solution is to make your count Mutable.
Use a Mutable<Integer> as your count and pass it up the recusrion stack.
class Mutable<T> {
T it;
public Mutable(T it) {
this.it = it;
}
public T getIt() {
return it;
}
public void setIt(T it) {
this.it = it;
}
}
private int depth(TrieNode node, String s) {
return depth(new Mutable<Integer>(0), node, s).getIt();
}
private Mutable<Integer> depth(Mutable<Integer> count, TrieNode node, String s) {
count.setIt(count.getIt() + 1);
String communsubString = checkEdgeString(node.getNext(), s);
String restString = s.substring(communsubString.length());
if (node.getNext() != null && !node.getNext().isEmpty()) {
for (TrieNode nextNodeEdge : node.getNext()) {
if (nextNodeEdge.getEdge().equals(communsubString)) {
count.setIt(count.getIt() + 1);
if (!restString.isEmpty()) {
depth(count, nextNodeEdge, restString);
} else {
System.out.println("Found");
}
}
}
}
return count;
}
This solution works for me. I needed to add break after this line count += depth(nextNodeEdge, restString); and in the else block count++;
Code
private int depth(TrieNode node, String s) {
int count = 1;
String communsubString = checkEdgeString(node.getNext(), s);
String restString = s.substring(communsubString.length());
if (node.getNext() != null && !node.getNext().isEmpty()) {
for (TrieNode nextNodeEdge : node.getNext()) {
if (nextNodeEdge.getEdge().equals(communsubString)) {
if (!restString.isEmpty()) {
count += depth(nextNodeEdge, restString);
break;
} else {
System.out.println("Found");
count++;
}
}
}
}
return count;
}
I was designing a simple parser(it works on a simple version of Shunting Yard Algorithm). Here's my code(I haven't dealt with associativity).
public class Parser {
String stack[] = new String[50];
String res = "";
int top = 0;
Operator o1 = new Operator("", 0, 0);
public String parse(String x) {
push("(");
x = x + ")";
for (int i = 0; i < x.length(); i++) {
if (o1.isNumber(x.charAt(i) + "")) {
res = res + x.charAt(i);
} else if (x.charAt(i) == '(') {
push("(");
} else if (o1.isOperator("" + x.charAt(i))) {
if (top != -1) {
while ((top != -1) && (o1.isOperator(stack[top]))) {
int m = o1.getOperatorIndex(stack[top]);
int mp = o1.op[m].prec;
int xp = o1.op[o1.getOperatorIndex("" + x.charAt(i))].prec;
if (m >= xp) {
res = res + stack[top];
}
top--;
}
}
push("" + x.charAt(i));
} else {
if (top != -1) {
while ((top != -1) && (stack[top] != ")")) {
if (o1.isOperator(stack[top])) {
res = res + stack[top];
}
top--;
}
}
}
}
return res;
}
public void push(String m) {
if (top != 49) {
stack[top] = m;
top++;
} else {
System.out.println("Overflow");
}
}
}
I guess you won't need the Operator class's code. When I try executing parse("1+2"), it just returns 12 and not the + sign. What is wrong? and yes, o[0] is +,o[1] is -,o[2] is*, o[3] is /