Sudoku 数独 Dancing Links模板
List
https://code.google.com/p/sudoku-dlx/source/checkout
DLX.java
import java.util.ArrayList;
import java.util.List;
public class DLX
{
private static final int ROW = 4096 + 50;
private static final int COL = 1024 + 50;
private static final int N = 4 * 9 * 9;
private static final int m = 3;
DLXNode row[] = new DLXNode[ROW];
DLXNode col[] = new DLXNode[COL];
DLXNode head;
private int n;
private int num = 2;
private int size[] = new int[COL];
int data[][] = new int[9][9];
List solutions;
public DLX(int r, int c)
{
n = m * m;
head = new DLXNode(r, c);
head.U = head.D = head.L = head.R = head;
for (int i = 0; i < c; ++i)
{
col[i] = new DLXNode(r, i);
col[i].L = head;
col[i].R = head.R;
col[i].L.R = col[i].R.L = col[i];
col[i].U = col[i].D = col[i];
size[i] = 0;
}
for (int i = r - 1; i > -1; --i)
{
row[i] = new DLXNode(i, c);
row[i].U = head;
row[i].D = head.D;
row[i].U.D = row[i].D.U = row[i];
row[i].L = row[i].R = row[i];
}
}
public void addNode(int r, int c)
{
DLXNode p = new DLXNode(r, c);
p.R = row[r];
p.L = row[r].L;
p.L.R = p.R.L = p;
p.U = col[c];
p.D = col[c].D;
p.U.D = p.D.U = p;
++size[c];
}
public void addNode(int i, int j, int k)
{
int r = (i * n + j) * n + k;
addNode(r, i * n + k - 1);
addNode(r, n * n + j * n + k - 1);
addNode(r, 2 * n * n + block(i, j) * n + k - 1);
addNode(r, 3 * n * n + i * n + j);
}
int block(int x, int y)
{
return x / m * m + y / m;
}
public void cover(int c)
{
if (c == N)
return;
col[c].delLR();
DLXNode R, C;
for (C = col[c].D; C != col[c]; C = C.D)
{
if (C.c == N)
continue;
for (R = C.L; R != C; R = R.L)
{
if (R.c == N)
continue;
--size[R.c];
R.delUD();
}
C.delLR();
}
}
public void resume(int c)
{
if (c == N)
return;
DLXNode R, C;
for (C = col[c].U; C != col[c]; C = C.U)
{
if (C.c == N)
continue;
C.resumeLR();
for (R = C.R; R != C; R = R.R)
{
if (R.c == N)
continue;
++size[R.c];
R.resumeUD();
}
}
col[c].resumeLR();
}
public boolean solve(int depth)
{
if (head.L == head)
{
int solution[][] = new int[n][n];
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
solution[i][j] = data[i][j];
solutions.add(solution);
if (solutions.size() == num)
return true;
return false;
}
int minSize = 1 << 30;
int c = -1;
DLXNode p;
for (p = head.L; p != head; p = p.L)
if (size[p.c] < minSize)
{
minSize = size[p.c];
c = p.c;
}
cover(c);
for (p = col[c].D; p != col[c]; p = p.D)
{
DLXNode cell;
p.R.L = p;
for (cell = p.L; cell != p; cell = cell.L)
{
cover(cell.c);
}
p.R.L = p.L;
int rr = p.r - 1;
data[rr / (n * n)][rr / n % n] = rr % n + 1;
if (solve(depth + 1))
return true;
p.L.R = p;
for (cell = p.R; cell != p; cell = cell.R)
resume(cell.c);
p.L.R = p.R;
}
resume(c);
return false;
}
public boolean solve(int data[][])
{
init(data);
return solve(0);
}
public void init(int data[][])
{
solutions = new ArrayList();
int i, j, k;
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
{
if (data[i][j] > 0)
{
addNode(i, j, data[i][j]);
} else
{
for (k = 1; k <= n; ++k)
addNode(i, j, k);
}
}
}
public void setNum(int num)
{
this.num = num;
}
public int getNum()
{
return num;
}
public List getSolutions()
{
return solutions;
}
}
class DLXNode
{
int r,c;
DLXNode U,D,L,R;
DLXNode()
{
r = c = 0;
}
DLXNode(int r, int c)
{
this.r = r;
this.c = c;
}
DLXNode(int r, int c, DLXNode U, DLXNode D, DLXNode L, DLXNode R)
{
this.r = r;
this.c = c;
this.U = U;
this.D = D;
this.L = L;
this.R = R;
}
public void delLR()
{
L.R = R;
R.L = L;
}
public void delUD()
{
U.D = D;
D.U = U;
}
public void resumeLR()
{
L.R = R.L = this;
}
public void resumeUD()
{
U.D = D.U = this;
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.text.*;
public class Sudoku extends JPanel
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int width = 9;
private static final int height = 9;
private int data[][] = new int[height][width];
private String infoStr = "";
JButton solve = new JButton("Solve"), reset = new JButton("Reset"), clean = new JButton("Clean");
BasicArrowButton right = new BasicArrowButton(BasicArrowButton.EAST), left = new BasicArrowButton(BasicArrowButton.WEST);
JTextField sudokus[][] = new JTextField[height][width];
JTextPane info = new JTextPane();
java.util.List solutions;
private int solutionIdx = 0;
Sudoku()
{
setLayout(null);
for (int r = 0; r < 3; ++r)
{
for (int c = 0; c < 3; ++c)
{
JPanel jp = new JPanel();
jp.setLayout(null);
jp.setBounds(r * 95 + 5, c * 95 + 5, 95, 95);
jp.setBorder(new LineBorder(Color.black));
for (int x = 0; x < 3; ++x)
for (int y = 0; y < 3; ++y)
{
int j = r * 3 + x, i = c * 3 + y;
sudokus[i][j] = new JTextField();
sudokus[i][j].setBounds(30 * x + 5, 30 * y + 5, 25, 25);
sudokus[i][j].setDocument(new NumberLenghtLimitedDmt(1));
jp.add(sudokus[i][j]);
}
add(jp);
}
}
solve.setBounds(85, 300, 75, 25);
solve.addActionListener(new SolveAL());
add(solve);
reset.setBounds(175, 300, 75, 25);
reset.addActionListener(new ResetAL());
add(reset);
clean.setBounds(265, 300, 75, 25);
clean.addActionListener(new cleanAL());
add(clean);
left.setVisible(false);
left.setBounds(170, 330, 50, 25);
left.addActionListener(new prevAL());
add(left);
right.setVisible(false);
right.setBounds(230, 330, 50, 25);
right.addActionListener(new nextAL());
add(right);
JScrollPane jsp = new JScrollPane(info);
info.setEditable(false);
jsp.setBounds(300, 5, 140, 285);
jsp.setBorder(new TitledBorder("Info"));
add(jsp);
}
public boolean solve()
{
int i, j;
for (i = 0; i < height; ++i)
{
for (j = 0; j < width; ++j)
{
try
{
data[i][j] = Integer.parseInt(sudokus[i][j].getText());
sudokus[i][j].setEditable(false);
} catch (NumberFormatException e)
{
data[i][j] = 0;
sudokus[i][j].setEnabled(false);
}
}
}
int n = 9;
long startTime = System.currentTimeMillis();
DLX dlx = new DLX(n * n * n + 1, 4 * n * n);
dlx.setNum(5);
dlx.solve(data);
solutions = dlx.getSolutions();
if (solutions.size() > 1)
infoStr += "Find multi solutions.\n";
else if (solutions.size() > 0)
infoStr += "Find one solution.\n";
else
infoStr += "Cannot find a solution.\n";
infoStr += "Used time: " + (System.currentTimeMillis() - startTime) + " ms\n";
return update();
}
public void reset(int data[][])
{
solve.setEnabled(true);
int i, j;
for (i = 0; i < height; ++i)
{
for (j = 0; j < width; ++j)
{
sudokus[i][j].setEnabled(true);
sudokus[i][j].setEditable(true);
if (data[i][j] == 0)
{
sudokus[i][j].setText("");
} else
{
sudokus[i][j].setText(Integer.toString(data[i][j]));
}
}
}
}
boolean update()
{
info.setText(infoStr);
if (solutionIdx >= solutions.size())
return false;
left.setVisible(true);
right.setVisible(true);
int i, j;
int solution[][] = solutions.get(solutionIdx);
for (i = 0; i < height; ++i)
{
for (j = 0; j < width; ++j)
{
sudokus[i][j].setText(Integer.toString(solution[i][j]));
}
}
return true;
}
public static void inFrame(JPanel jp, int width, int height)
{
String title = "Sudoku";
JFrame frame = new JFrame(title);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
frame.getContentPane().add(jp, BorderLayout.CENTER);
frame.setLocationByPlatform(true);
frame.setSize(width, height);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String[] args)
{
int data[][] = {
{ 8, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 3, 6, 0, 0, 0, 0, 0 },
{ 0, 7, 0, 0, 9, 0, 2, 0, 0 },
{ 0, 5, 0, 0, 0, 7, 0, 0, 0 },
{ 0, 0, 0, 0, 4, 5, 7, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 3, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 6, 8 },
{ 0, 0, 8, 5, 0, 0, 0, 1, 0 },
{ 0, 9, 0, 0, 0, 0, 4, 0, 0 },
};
Sudoku sudoku = new Sudoku();
sudoku.reset(data);
inFrame(sudoku, 450, 400);
}
class SolveAL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
infoStr += "Start solve this sudoku.\n";
info.setText(infoStr);
solve.setEnabled(false);
solve();
}
}
class ResetAL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
infoStr += "Reset sudoku data.\n";
info.setText(infoStr);
left.setVisible(false);
right.setVisible(false);
reset(data);
}
}
class cleanAL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
infoStr = "Clean sudoku data.\n";
info.setText(infoStr);
solve.setEnabled(true);
left.setVisible(false);
right.setVisible(false);
int i, j;
for (i = 0; i < height; ++i)
{
for (j = 0; j < width; ++j)
{
sudokus[i][j].setText("");
sudokus[i][j].setEnabled(true);
sudokus[i][j].setEditable(true);
}
}
}
}
class prevAL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
int size = solutions.size();
if(size > 0)
{
solutionIdx = (solutionIdx - 1 + size) % size;
infoStr += "The " + (solutionIdx+1) + "th solution.\n";
update();
}
}
}
class nextAL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
int size = solutions.size();
if(size > 0)
{
solutionIdx = (solutionIdx + 1) % size;
infoStr += "The " + (solutionIdx+1) + "th solution.\n";
update();
}
}
}
class NumberLenghtLimitedDmt extends PlainDocument
{
/**
*
*/
private static final long serialVersionUID = 1L;
private int limit;
public NumberLenghtLimitedDmt(int limit)
{
super();
this.limit = limit;
}
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException
{
if (str == null)
{
return;
}
if ((getLength() + str.length()) <= limit)
{
char[] upper = str.toCharArray();
int length = 0;
for (int i = 0; i < upper.length; i++)
{
if (upper[i] >= '0' && upper[i] <= '9')
{
upper[length++] = upper[i];
}
}
super.insertString(offset, new String(upper, 0, length), attr);
}
}
}
}