C#循环解数独(非递归)

思路:(默认空白的地方是0,填入数据为1~9)

   1.在空白的地方填入一个数字,我们从1开始填入,每填入一个数,我们进行冲突检测,如果没有冲突,下个空白处继续当前流程

    (为了优化,我们可以提前判定哪些数据可以填,不用从1开始到9依次填充)

   2.当遇到冲突,当前数据清0,回到空白状态,并返回到上一个填数据的地方,将数据+1 (原本填1,现在填2),当数据填到9后,还是冲突,再回退到上一个进行操作

   3.冲突检测,行、列、3*3小格子中是否有重复数字,如果有表示冲突。

 

开始:

  我们用List<List<int>> 表示9*9的数据。暂时用List<List<string>>来封装数据。

List<List<string>> dataList = new List<List<string>>();
dataList.Add(new[] { "5", "3", ".", ".", "7", ".", ".", ".", "." }.ToList());
dataList.Add(new[] { "6", ".", ".", "1", "9", "5", ".", ".", "." }.ToList());
dataList.Add(new[] { ".", "9", "8", ".", ".", ".", ".", "6", "." }.ToList());
dataList.Add(new[] { "8", ".", ".", ".", "6", ".", ".", ".", "3" }.ToList());
dataList.Add(new[] { "4", ".", ".", "8", ".", "3", ".", ".", "1" }.ToList());
dataList.Add(new[] { "7", ".", ".", ".", "2", ".", ".", ".", "6" }.ToList());
dataList.Add(new[] { ".", "6", ".", ".", ".", ".", "2", "8", "." }.ToList());
dataList.Add(new[] { ".", ".", ".", "4", "1", "9", ".", ".", "5" }.ToList());
dataList.Add(new[] { ".", ".", ".", ".", "8", ".", ".", "7", "9" }.ToList());

  其中"." 表示空白地方

  上面的数据,进行处理后,可以转为List<List<int>>类型

List<List<int>> data = new List<List<int>>();
foreach (List<string> list in dataList)
{
       data.Add(list.Select(it => Convert.ToInt32(it == "." ? "0" : it)).ToList());
}

  

首先我们把所有的要填数据的地方记录下来,可以用链表的方式实现,方便找到上一个空白点以及下一个空白点,方便实现回退

1.定义结构,用于存放行列,以及哪些数据可以填写

class Node
{
        public int i;
        public int j;
        public List<int> CanSet;
        public Node front;
        public Node next;
}

2.写个方法,实现某个空白位置,可以填写哪些数据(传入行列)

private List<int> GetCanSet(int a, int b)
{
    //把当前行的所有数据记下来,把当前列的也记下来
    List<int> list = new List<int>();
    //把当前行的数据取出来
    for (int j = 0; j < 9; j++)
    {
        if (data[a][j] != 0 && !list.Contains(data[a][j]))
            list.Add(data[a][j]);
    }
    //把当前列的数据取出来
    for (int i = 0; i < 9; i++)
    {
        if (data[i][b] != 0 && !list.Contains(data[i][b]))
            list.Add(data[i][b]);
    }
    //把当前3*3格子内的数据取出来
    int x = a / 3 * 3;
    int y = b / 3 * 3;
    for (int i = x; i < x + 3; i++)
    {
        for (int j = y; j < y + 3; j++)
        {
            if (data[i][j] != 0 && !list.Contains(data[i][j]))
                list.Add(data[i][j]);
        }
    }

    List<int> returnValue = new List<int>();
    for (int i = 1; i <= 9; i++)
    {
        if (!list.Contains(i))
        {
            returnValue.Add(i);
        }
    }
    return returnValue;
}                            

3.初始化节点

  

bool isFirst = true;
Node rootNode = null;
Node lastNode = null;
//将所有的要存放的数据记录一下,以及他的下一级是多少
for (int i = 0; i < 9; i++)
{
    for (int j = 0; j < 9; j++)
    {
        if (data[i][j] == 0)
        {
            if (isFirst)
            {
                Node n = new Node { i = i, j = j };
                n.CanSet = GetCanSet(i, j);
                rootNode = n;
                lastNode = n;
                isFirst = false;
            }
            else
            {
                Node n = new Node { i = i, j = j };
                n.CanSet = GetCanSet(i, j);
                n.front = lastNode;
                lastNode.next = n;
                lastNode = n;
            }
        }
    }
}

4.实现一个方法,用于冲突判断

/// <summary>
/// 行列校验是否冲突
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
private bool CheckNode(Node node)
{
    int value = data[node.i][node.j];
    //当前行校验 
    for (int j = 0; j < 9; j++)
    {
        if (node.j == j)
        {
            continue;
        }
        if (data[node.i][j] == value)
        {
            return false;
        }
    }
    //当前列校验
    for (int i = 0; i < 9; i++)
    {
        if (node.i == i)
            continue;
        if (data[i][node.j] == value)
            return false;
    }

    //校验小格子
    int x = node.i / 3 * 3;
    int y = node.j / 3 * 3;
    for (int i = x; i < (x + 3); i++)
    {
        for (int j = y; j < (y + 3); j++)
        {
            if (node.i == i && node.j == j)
                continue;
            if (data[i][j] == value)
                return false;
        }
    }
    //校验通过
    return true;
}

 5.循环每个空白节点填入数据

Node node = rootNode; //找到根节点,从第一个空位置开始填数据
while (true)  //这里写死循环,下面有退出条件
{
    //当前节点为空,两种情况1.最后一个填完  2.已经回退到第一个节点
    //这两种情况,表示填数据结束,退出循环
    if (node == null)  
        break;
    int value = data[node.i][node.j]; //得到当前节点原本的数据
    int indexof = node.CanSet.IndexOf(value);//找到原来的数据在可放数据的哪个位置
    indexof++; //索引位置+1
    //如果数字原来就已经是可选范围的最后一个值,那么就应该回退
    if (indexof == node.CanSet.Count)
    {
        data[node.i][node.j] = 0;
        node = node.front;//回退上一个节点
        continue;
    }
    value = node.CanSet[indexof];
    data[node.i][node.j] = value;
    if (CheckNode(node))   //冲突检测
    {
        node = node.next;//没有冲突,就再下一个位置填数据
        continue;
    }
 }            

 

完整代码:

    internal class Program
    {
        static void Main(string[] args)
        {
            List<List<string>> data = new List<List<string>>();
            data.Add(new[] { "5", "3", ".", ".", "7", ".", ".", ".", "." }.ToList());
            data.Add(new[] { "6", ".", ".", "1", "9", "5", ".", ".", "." }.ToList());
            data.Add(new[] { ".", "9", "8", ".", ".", ".", ".", "6", "." }.ToList());
            data.Add(new[] { "8", ".", ".", ".", "6", ".", ".", ".", "3" }.ToList());
            data.Add(new[] { "4", ".", ".", "8", ".", "3", ".", ".", "1" }.ToList());
            data.Add(new[] { "7", ".", ".", ".", "2", ".", ".", ".", "6" }.ToList());
            data.Add(new[] { ".", "6", ".", ".", ".", ".", "2", "8", "." }.ToList());
            data.Add(new[] { ".", ".", ".", "4", "1", "9", ".", ".", "5" }.ToList());
            data.Add(new[] { ".", ".", ".", ".", "8", ".", ".", "7", "9" }.ToList());

            Sudoku sudoku = new Sudoku(data);
            foreach (List<string> list in sudoku.Result)
            {
                Console.WriteLine(string.Join(",", list));
            }
            sudoku.Solve();
            Console.WriteLine("-----------------");
            foreach (List<string> list in sudoku.Result)
            {
                Console.WriteLine(string.Join(",", list));
            }


            Console.ReadLine();
        }
    }
    public class Sudoku
    {
        List<List<int>> data;
        public Sudoku(List<List<string>> data)
        {
            List<List<int>> data1 = new List<List<int>>();
            foreach (List<string> list in data)
            {
                data1.Add(list.Select(it => Convert.ToInt32(it == "." ? "0" : it)).ToList());
            }
            this.data = data1;
        }

        public List<List<string>> Result
        {
            get
            {
                List<List<string>> result = new List<List<string>>();
                foreach (List<int> list in data)
                {
                    result.Add(list.Select(it => Convert.ToString(it)).ToList());
                }
                return result;
            }
        }

        public void Solve()
        {
            bool isFirst = true;
            Node rootNode = null;
            Node lastNode = null;
            //将所有的要存放的数据记录一下,以及他的下一级是多少
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (data[i][j] == 0)
                    {
                        if (isFirst)
                        {
                            Node n = new Node { i = i, j = j };
                            n.CanSet = GetCanSet(i, j);
                            rootNode = n;
                            lastNode = n;
                            isFirst = false;
                        }
                        else
                        {
                            Node n = new Node { i = i, j = j };
                            n.CanSet = GetCanSet(i, j);
                            n.front = lastNode;
                            lastNode.next = n;
                            lastNode = n;
                        }
                    }
                }
            }
            Node node = rootNode;
            while (true)
            {
                if (node == null)
                    break;
                int value = data[node.i][node.j];
                int indexof = node.CanSet.IndexOf(value);
                indexof++;
                //如果数字原来就已经是可选范围的最后一个值,那么就应该回退
                if (indexof == node.CanSet.Count)
                {
                    data[node.i][node.j] = 0;
                    node = node.front;
                    continue;
                }
                value = node.CanSet[indexof];
                data[node.i][node.j] = value;
                if (CheckNode(node))
                {
                    node = node.next;
                    continue;
                }
            }
        }


        private List<int> GetCanSet(int a, int b)
        {
            //把当前行的所有数据记下来,把当前列的也记下来
            List<int> list = new List<int>();
            //把当前行的数据取出来
            for (int j = 0; j < 9; j++)
            {
                if (data[a][j] != 0 && !list.Contains(data[a][j]))
                    list.Add(data[a][j]);
            }
            //把当前列的数据取出来
            for (int i = 0; i < 9; i++)
            {
                if (data[i][b] != 0 && !list.Contains(data[i][b]))
                    list.Add(data[i][b]);
            }
            //把当前3*3格子内的数据取出来
            int x = a / 3 * 3;
            int y = b / 3 * 3;
            for (int i = x; i < x + 3; i++)
            {
                for (int j = y; j < y + 3; j++)
                {
                    if (data[i][j] != 0 && !list.Contains(data[i][j]))
                        list.Add(data[i][j]);
                }
            }

            List<int> returnValue = new List<int>();
            for (int i = 1; i <= 9; i++)
            {
                if (!list.Contains(i))
                {
                    returnValue.Add(i);
                }
            }
            return returnValue;
        }



        /// <summary>
        /// 行列校验是否冲突
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool CheckNode(Node node)
        {
            int value = data[node.i][node.j];
            //当前行校验 
            for (int j = 0; j < 9; j++)
            {
                if (node.j == j)
                {
                    continue;
                }
                if (data[node.i][j] == value)
                {
                    return false;
                }
            }
            //当前列校验
            for (int i = 0; i < 9; i++)
            {
                if (node.i == i)
                    continue;
                if (data[i][node.j] == value)
                    return false;
            }

            //校验小格子
            int x = node.i / 3 * 3;
            int y = node.j / 3 * 3;
            for (int i = x; i < (x + 3); i++)
            {
                for (int j = y; j < (y + 3); j++)
                {
                    if (node.i == i && node.j == j)
                        continue;
                    if (data[i][j] == value)
                        return false;
                }
            }
            //校验通过
            return true;
        }


      
    }

    class Node
    {
        public int i;
        public int j;
        public List<int> CanSet;
        public Node front;
        public Node next;
    }

 

拓展:

如果所有的都是空的呢?那按照之前的规则,每次运行出来的结果是不是都是一样的呢?

我们只需要将可填写的值随机打乱就行,那么每次运行,出来的结果就是不一样的。

  

      private List<int> ListRandom(List<int> myList)
      {
            Random ran = new Random();
            int index = 0;
            int temp = 0;
            for (int i = 0; i < myList.Count; i++)
            {
                index = ran.Next(0, myList.Count - 1);
                if (index != i)
                {
                    temp = myList[i];
                    myList[i] = myList[index];
                    myList[index] = temp;
                }
            }
            return myList;
        }

  改写

private List<int> GetCanSet(int a, int b)
{
  //把当前行的所有数据记下来,把当前列的也记下来
  List<int> list = new List<int>();
  //把当前行的数据取出来
  for (int j = 0; j < 9; j++)
  {
    if (data[a][j] != 0 && !list.Contains(data[a][j]))
      list.Add(data[a][j]);
    }
    //把当前列的数据取出来
    for (int i = 0; i < 9; i++)
    {
      if (data[i][b] != 0 && !list.Contains(data[i][b]))
        list.Add(data[i][b]);
    }
    //把当前3*3格子内的数据取出来
    int x = a / 3 * 3;
    int y = b / 3 * 3;
    for (int i = x; i < x + 3; i++)
    {
      for (int j = y; j < y + 3; j++)
      {
        if (data[i][j] != 0 && !list.Contains(data[i][j]))
        list.Add(data[i][j]);
      }
    }

    List<int> returnValue = new List<int>();
    for (int i = 1; i <= 9; i++)
    {
      if (!list.Contains(i))
      {
        returnValue.Add(i);
      }
    }
    return ListRandom(returnValue);
}

 

posted @ 2022-06-01 13:40  大师兄法号随缘  阅读(74)  评论(0编辑  收藏  举报