图描述了 Java 中的二叉树遍历
graph describe binary tree traversal in Java
在我的程序中,我使用一个名为 "curNode" 的变量来表示树根。每次我在文本字段中输入 0 或 1 时,curNode 将变为 curNode.left 或 curNode.right,然后新的当前节点变为红色而不是黑色。但它只有在我输入一些东西后才有效,在 init 中,树根没有红色。
这是我的 TriesView.java:
package tree;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Stack;
import javax.swing.*;
public class TriesView extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private Huffman tree; // A binary tree to be displayed
private PaintTree paintTree;
private JTextField pathTxt;
public HuffmanNode curNode;
private Stack<String> txtStack = new Stack<String>();
/** Construct a view for a binary tree */
public TriesView(Huffman tree) {
this.tree = tree; // Set a binary tree to be displayed
curNode = tree.root;
paintTree = new PaintTree();
setUI();
}
/** Initialize UI for binary tree */
private void setUI() {
this.setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(paintTree, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
add(scrollPane, BorderLayout.CENTER);
if(!tree.isEmpty()){
paintTree.setPreferredSize(new Dimension((int)Math.pow(2, (tree.getDepth() + 1)) * paintTree.radius, (tree.getDepth() + 1) * paintTree.vGap + paintTree.radius));
scrollPane.setPreferredSize(new Dimension((int)Math.pow(2, (tree.getDepth() + 1)) * paintTree.radius, (tree.getDepth() + 1) * paintTree.vGap + paintTree.radius));
}
JPanel panel = new JPanel();
pathTxt = new JTextField(10);
panel.add(new JLabel("Enter key path: "));
panel.add(pathTxt);
add(panel, BorderLayout.SOUTH);
pathTxt.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent evt) {
String txt = pathTxt.getText();
txtStack.push(txt);
}
public void keyReleased(KeyEvent evt) {
String txt = pathTxt.getText();
String oldTxt = pathTxt.getText();
if(!txtStack.isEmpty())
oldTxt = txtStack.pop();
if (txt.matches("^[01]*$") != true) {
pathTxt.setText(oldTxt);
JOptionPane.showMessageDialog(null, "Ký tự không hợp lệ");
return;
}
HuffmanNode temp = tree.root;
for(int i = 0; i < txt.length() && temp != null; i++){
if(txt.charAt(i) == '0'){
temp = temp.left;
}else if(txt.charAt(i) == '1'){
temp = temp.right;
}
}
if(temp == null){
JOptionPane.showMessageDialog(null, "Dãy không hợp lệ");
pathTxt.setText(oldTxt);
return;
}
curNode = temp;
scrollPane.repaint();
}
});
}
// Inner class PaintTree for displaying a tree on a panel
class PaintTree extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private int radius = 20; // Tree node radius
private int vGap = 50; // Gap between two levels in a tree
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (tree != null) {
// Display tree recursively
// displayTree(g, tree.root, (int)Math.pow(2, tree.root.height - 1)*radius, 30, (int)Math.pow(2, tree.root.height-2)*radius);
displayTree(g, tree.root, getWidth()/2, 30, getWidth()/4);
}
}
/** Display a subtree rooted at position (x, y) */
private void displayTree(Graphics g, HuffmanNode root,
int x, int y, int hGap) {
// Display the root
if(root == null) return;
Graphics2D ga = (Graphics2D)g;
if(curNode == root){
ga.setPaint(Color.RED);
}else{
ga.setPaint(Color.BLACK);
}
ga.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
ga.setPaint(Color.BLACK);
ga.setColor(Color.WHITE);
ga.drawString(String.valueOf(root.frequency), x - 6, y + 4);
ga.setColor(Color.RED);
ga.drawString(String.valueOf(root.value), x - 6, y + 36);
ga.setColor(Color.BLACK);
if (root.left != null) {
// Draw a line to the left node
connectLeftChild(g, x - hGap, y + vGap, x, y);
ga.drawString("0", x - hGap/2 - 12, y + vGap/2);
displayTree(g, root.left, x - hGap, y + vGap, Math.max(hGap / 2, radius));
}
if (root.right != null) {
// Draw a line to the right node
connectRightChild(g, x + hGap, y + vGap, x, y);
ga.drawString("1", x + hGap/2 + 5, y + vGap/2);
// Draw the right subtree recursively
displayTree(g, root.right, x + hGap, y + vGap, Math.max(hGap / 2, radius));
}
}
public int getRadius(){
return this.radius;
}
public int getVGap(){
return this.vGap;
}
/** Connect a parent at (x2, y2) with
* its left child at (x1, y1) */
private void connectLeftChild(Graphics g,
int x1, int y1, int x2, int y2) {
double d = Math.sqrt(vGap * vGap + (x2 - x1) * (x2 - x1));
int x11 = (int)(x1 + radius * (x2 - x1) / d);
int y11 = (int)(y1 - radius * vGap / d);
int x21 = (int)(x2 - radius * (x2 - x1) / d);
int y21 = (int)(y2 + radius * vGap / d);
g.drawLine(x11, y11, x21, y21);
}
/** Connect a parent at (x2, y2) with
* its right child at (x1, y1) */
private void connectRightChild(Graphics g,
int x1, int y1, int x2, int y2) {
double d = Math.sqrt(vGap * vGap + (x2 - x1) * (x2 - x1));
int x11 = (int)(x1 - radius * (x1 - x2) / d);
int y11 = (int)(y1 - radius * vGap / d);
int x21 = (int)(x2 + radius * (x1 - x2) / d);
int y21 = (int)(y2 + radius * vGap / d);
g.drawLine(x11, y11, x21, y21);
}
}
}
我找到了解决方案:
每次在树中插入或删除节点后重新绘制组件
在我的程序中,我使用一个名为 "curNode" 的变量来表示树根。每次我在文本字段中输入 0 或 1 时,curNode 将变为 curNode.left 或 curNode.right,然后新的当前节点变为红色而不是黑色。但它只有在我输入一些东西后才有效,在 init 中,树根没有红色。 这是我的 TriesView.java:
package tree;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Stack;
import javax.swing.*;
public class TriesView extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private Huffman tree; // A binary tree to be displayed
private PaintTree paintTree;
private JTextField pathTxt;
public HuffmanNode curNode;
private Stack<String> txtStack = new Stack<String>();
/** Construct a view for a binary tree */
public TriesView(Huffman tree) {
this.tree = tree; // Set a binary tree to be displayed
curNode = tree.root;
paintTree = new PaintTree();
setUI();
}
/** Initialize UI for binary tree */
private void setUI() {
this.setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(paintTree, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
add(scrollPane, BorderLayout.CENTER);
if(!tree.isEmpty()){
paintTree.setPreferredSize(new Dimension((int)Math.pow(2, (tree.getDepth() + 1)) * paintTree.radius, (tree.getDepth() + 1) * paintTree.vGap + paintTree.radius));
scrollPane.setPreferredSize(new Dimension((int)Math.pow(2, (tree.getDepth() + 1)) * paintTree.radius, (tree.getDepth() + 1) * paintTree.vGap + paintTree.radius));
}
JPanel panel = new JPanel();
pathTxt = new JTextField(10);
panel.add(new JLabel("Enter key path: "));
panel.add(pathTxt);
add(panel, BorderLayout.SOUTH);
pathTxt.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent evt) {
String txt = pathTxt.getText();
txtStack.push(txt);
}
public void keyReleased(KeyEvent evt) {
String txt = pathTxt.getText();
String oldTxt = pathTxt.getText();
if(!txtStack.isEmpty())
oldTxt = txtStack.pop();
if (txt.matches("^[01]*$") != true) {
pathTxt.setText(oldTxt);
JOptionPane.showMessageDialog(null, "Ký tự không hợp lệ");
return;
}
HuffmanNode temp = tree.root;
for(int i = 0; i < txt.length() && temp != null; i++){
if(txt.charAt(i) == '0'){
temp = temp.left;
}else if(txt.charAt(i) == '1'){
temp = temp.right;
}
}
if(temp == null){
JOptionPane.showMessageDialog(null, "Dãy không hợp lệ");
pathTxt.setText(oldTxt);
return;
}
curNode = temp;
scrollPane.repaint();
}
});
}
// Inner class PaintTree for displaying a tree on a panel
class PaintTree extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private int radius = 20; // Tree node radius
private int vGap = 50; // Gap between two levels in a tree
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (tree != null) {
// Display tree recursively
// displayTree(g, tree.root, (int)Math.pow(2, tree.root.height - 1)*radius, 30, (int)Math.pow(2, tree.root.height-2)*radius);
displayTree(g, tree.root, getWidth()/2, 30, getWidth()/4);
}
}
/** Display a subtree rooted at position (x, y) */
private void displayTree(Graphics g, HuffmanNode root,
int x, int y, int hGap) {
// Display the root
if(root == null) return;
Graphics2D ga = (Graphics2D)g;
if(curNode == root){
ga.setPaint(Color.RED);
}else{
ga.setPaint(Color.BLACK);
}
ga.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
ga.setPaint(Color.BLACK);
ga.setColor(Color.WHITE);
ga.drawString(String.valueOf(root.frequency), x - 6, y + 4);
ga.setColor(Color.RED);
ga.drawString(String.valueOf(root.value), x - 6, y + 36);
ga.setColor(Color.BLACK);
if (root.left != null) {
// Draw a line to the left node
connectLeftChild(g, x - hGap, y + vGap, x, y);
ga.drawString("0", x - hGap/2 - 12, y + vGap/2);
displayTree(g, root.left, x - hGap, y + vGap, Math.max(hGap / 2, radius));
}
if (root.right != null) {
// Draw a line to the right node
connectRightChild(g, x + hGap, y + vGap, x, y);
ga.drawString("1", x + hGap/2 + 5, y + vGap/2);
// Draw the right subtree recursively
displayTree(g, root.right, x + hGap, y + vGap, Math.max(hGap / 2, radius));
}
}
public int getRadius(){
return this.radius;
}
public int getVGap(){
return this.vGap;
}
/** Connect a parent at (x2, y2) with
* its left child at (x1, y1) */
private void connectLeftChild(Graphics g,
int x1, int y1, int x2, int y2) {
double d = Math.sqrt(vGap * vGap + (x2 - x1) * (x2 - x1));
int x11 = (int)(x1 + radius * (x2 - x1) / d);
int y11 = (int)(y1 - radius * vGap / d);
int x21 = (int)(x2 - radius * (x2 - x1) / d);
int y21 = (int)(y2 + radius * vGap / d);
g.drawLine(x11, y11, x21, y21);
}
/** Connect a parent at (x2, y2) with
* its right child at (x1, y1) */
private void connectRightChild(Graphics g,
int x1, int y1, int x2, int y2) {
double d = Math.sqrt(vGap * vGap + (x2 - x1) * (x2 - x1));
int x11 = (int)(x1 - radius * (x1 - x2) / d);
int y11 = (int)(y1 - radius * vGap / d);
int x21 = (int)(x2 + radius * (x1 - x2) / d);
int y21 = (int)(y2 + radius * vGap / d);
g.drawLine(x11, y11, x21, y21);
}
}
}
我找到了解决方案: 每次在树中插入或删除节点后重新绘制组件