跳舞链解数独
---恢复内容开始---
#include <stdio.h> #include <malloc.h> #include <time.h> typedef struct DLX_node//节点定义 { struct DLX_node* up; struct DLX_node* down; struct DLX_node* left; struct DLX_node* right; int is_head; int index; }d_node,*pd_node; int row = 0; struct node_heap { int cul_value; int position_index; }; pd_node head; pd_node* p_head; pd_node stack[81]; int stack_index = 0; struct node_heap mutual_index[324]; int current_heap_number = 323; int available_column = 323; int position_index[324]; #define shift_base 0x80000000 int a[9][9] = { {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} }; void insert_row(int i, int j, int value)//进行行插入的函数,用来组成网格 { pd_node temp_node_one, temp_node_two, temp_node_three,temp_node_four; int current_index; current_index = 81 * i + 9 * j + value-1; temp_node_one = (pd_node) malloc(sizeof(struct DLX_node)); temp_node_two = (pd_node) malloc(sizeof(struct DLX_node)); temp_node_three = (pd_node) malloc(sizeof(struct DLX_node)); temp_node_four = (pd_node) malloc(sizeof(struct DLX_node)); temp_node_one->right = temp_node_two; temp_node_two->right = temp_node_three; temp_node_three->right = temp_node_four; temp_node_four->right = temp_node_one; temp_node_one->left = temp_node_four; temp_node_two->left = temp_node_one; temp_node_three->left = temp_node_two; temp_node_four->left = temp_node_three; temp_node_one->is_head = 0; temp_node_two->is_head = 0; temp_node_three->is_head = 0; temp_node_four->is_head = 0; temp_node_one->down = p_head[i * 9 + value - 1 ]; temp_node_one->up = temp_node_one->down->up; temp_node_one->up->down = temp_node_one; temp_node_one->down->up = temp_node_one; mutual_index[i * 9 + value - 1].cul_value += 1; temp_node_two->down = p_head[j * 9 + value - 1 + 81]; temp_node_two->up = temp_node_two->down->up; temp_node_two->up->down = temp_node_two; temp_node_two->down->up = temp_node_two; mutual_index[j * 9 + value + 81 - 1].cul_value += 1; temp_node_three->down = p_head[162 + ((i / 3) * 3 + j/ 3) * 9 + value - 1]; temp_node_three->up = temp_node_three->down->up; temp_node_three->up->down = temp_node_three; temp_node_three->down->up = temp_node_three; mutual_index[162 + ((i / 3) * 3 + j/ 3) * 9 + value - 1].cul_value+= 1; temp_node_four->down = p_head[243 + i * 9 + j]; temp_node_four->up =temp_node_four->down->up; temp_node_four->up->down = temp_node_four; temp_node_four->down->up = temp_node_four; mutual_index[243 + i * 9 + j].cul_value += 1; temp_node_one->index = temp_node_two->index = temp_node_three->index=temp_node_four->index = current_index; row++; } void swap_heap(int index_one, int index_two)//交换在堆中的两个元素的值,及相关数据索引 { int intermidate_one, intermidate_two; intermidate_one = mutual_index[index_one].cul_value; intermidate_two = mutual_index[index_one].position_index; mutual_index[index_one].cul_value = mutual_index[index_two].cul_value; mutual_index[index_one].position_index = mutual_index[index_two].position_index; mutual_index[index_two].cul_value = intermidate_one; mutual_index[index_two].position_index = intermidate_two; position_index[mutual_index[index_two].position_index] = index_two; position_index[mutual_index[index_one].position_index] = index_one; } void heap_initial()//初始化堆 { int k, i = 0; int current_min; for (i = (current_heap_number-1)/2; i >= 0; i--) { k = i; while (2 * k + 1 <= current_heap_number) { current_min = mutual_index[k].cul_value; current_min = current_min < mutual_index[2 * k + 1].cul_value ? current_min : mutual_index[2 * k + 1].cul_value; if (2 * k + 2 <= current_heap_number) { current_min = current_min < mutual_index[2 * k + 2].cul_value ? current_min : mutual_index[2 * k + 2].cul_value; } if (current_min == mutual_index[k].cul_value) { break; } else { if (current_min == mutual_index[2 * k + 1].cul_value) { swap_heap(k, 2 * k + 1); k = 2 * k + 1; } else { swap_heap(k, 2 * k + 2); k = 2 * k + 2; } } } } } void creat_dlx_sudoku(int input_matrix[9][9])//利用矩阵来建立十字网格 { int i, j,k; unsigned int row_position[9]; unsigned int column_position[9]; unsigned int small_position[9]; unsigned int optional_value; pd_node temp_swap_node; for (i = 0; i < 324; i++) { mutual_index[i].cul_value = 0; mutual_index[i].position_index = i; position_index[i] = i; } for (i = 0; i < 9; i++) { row_position[i] = 0; column_position[i] = 0; small_position[i] = 0; } for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { if (input_matrix[i][j] != 0) { row_position[i] = row_position[i] | (shift_base >> (input_matrix[i][j]-1)); column_position[j] = column_position[j] | (shift_base >> (input_matrix[i][j]-1)); small_position[(i/3)*3 +j/3] = small_position[(i/3)*3 + j/3] | (shift_base >> (input_matrix[i][j]-1)); } } } p_head = (pd_node*) malloc(324 * sizeof(struct DLX_node*)); for (i = 0; i < 324; i++) { p_head[i] = (pd_node) malloc(sizeof(struct DLX_node)); p_head[i]->up = p_head[i]; p_head[i]->down = p_head[i]; p_head[i]->is_head =1; } for (i = 0; i < 324; i++) { p_head[i]->right = p_head[(i + 1) % 324]; p_head[i]->left = p_head[(i + 323) % 324]; } temp_swap_node = (pd_node) malloc(sizeof(struct DLX_node)); temp_swap_node->down = temp_swap_node->up = temp_swap_node; temp_swap_node->is_head = 1; temp_swap_node->left = p_head[323]; temp_swap_node->right = p_head[0]; p_head[323]->right = temp_swap_node; p_head[0]->left = temp_swap_node; head = temp_swap_node; for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { if (input_matrix[i][j] != 0) { insert_row(i, j, input_matrix[i][j]); } else { optional_value = row_position[i] | column_position[j] | small_position[(i / 3) * 3 + j / 3]; optional_value = ~optional_value; for (k = 0; k < 9; k++) { if (((shift_base >> k)&optional_value)) { insert_row(i, j, k+1); } else { //do nothing } } } } } heap_initial(); } void delete_minimal()//删除堆中最小的元素 { int k; int current_min; if (current_heap_number != 0) { swap_heap(0, current_heap_number);//交换最高元素与最低元素 current_heap_number--;//然后将堆的大小进行缩减 k = 0; while (2 * k + 1 <= current_heap_number)//然后,下面便是一些维护性的工作,用来维护最小堆 { current_min = mutual_index[k].cul_value; current_min = current_min < mutual_index[2 * k + 1].cul_value ? current_min : mutual_index[2 * k + 1].cul_value; if (2 * k + 2 <= current_heap_number) { current_min = current_min < mutual_index[2 * k + 2].cul_value ? current_min : mutual_index[2 * k + 2].cul_value; } if (current_min == mutual_index[k].cul_value) { return; } else { if (current_min == mutual_index[2 * k + 1].cul_value) { swap_heap(k, 2 * k + 1); k = 2 * k + 1; } else { swap_heap(k, 2 * k + 2); k = 2 * k + 2; } } } } else//如果只剩下一个元素,那就不需要进行交换,直接将堆元素的个数降低一 { current_heap_number = -1; } } void heap_renew(int target_position, int new_value)//对于第target_position列,进行度数更新 { int heap_target_position,k,current_min; heap_target_position = position_index[target_position];//这个是这一列在堆中所在的位置 k = heap_target_position; if (new_value < mutual_index[k].cul_value)//如果值是减少的,就直接进行赋值,然后维护堆的性质 { mutual_index[k].cul_value = new_value; while (k > 0 && (mutual_index[(k - 1) / 2].cul_value > mutual_index[k].cul_value))//维护堆 { swap_heap((k - 1) / 2, k); k = (k - 1) / 2; } if (new_value == 0)//如果是赋值为0,则从堆中进行删除,因为我们每次操纵一个元素,所以最多会有一个元素为0,所以肯定是最小值。 { delete_minimal(); } } else//对于值增大的情况 { mutual_index[k].cul_value = new_value; if (new_value == 1)//如果新的值是1,则把这个元素重新加入堆中 { current_heap_number++;//扩大堆的范围,我们可以证明重新加入堆中的元素一定是排在堆的末尾,当然条件是删除与插入的顺序是对应相反的 while (k > 0 && (mutual_index[(k - 1) / 2].cul_value > mutual_index[k].cul_value))//由于新的值是1,所以不可能比上一个数大 { swap_heap((k - 1) / 2, k); k = (k - 1) / 2; } } else//如果不是1,说明已经在堆中,所以不需要扩大堆的范围,直接赋值之后进行维护堆结构就行 { while (2 * k + 1 <= current_heap_number) { current_min = mutual_index[k].cul_value; current_min = current_min < mutual_index[2 * k + 1].cul_value ? current_min : mutual_index[2 * k + 1].cul_value; if (2 * k + 2 <= current_heap_number) { current_min = current_min < mutual_index[2 * k + 2].cul_value ? current_min : mutual_index[2 * k + 2].cul_value; } if (current_min == mutual_index[k].cul_value) { break; } else { if (current_min == mutual_index[2 * k + 1].cul_value) { swap_heap(k, 2 * k + 1); k = 2 * k + 1; } else { swap_heap(k, 2 * k + 2); k = 2 * k + 2; } } } } } } void node_heap_decrease(pd_node current_node)//对于一个点进行她所在的行的删除,因为一行中一定有四个元素,所以有四列,我们对这四列的度数都进行减少1 { int i, j, k; k = current_node->index % 9; j = (current_node->index / 9) % 9; i = (current_node->index / 81) % 9; heap_renew(i * 9 + k, mutual_index[position_index[i * 9 + k]].cul_value - 1); heap_renew(81 + j * 9 + k, mutual_index[position_index[81 + j * 9 + k]].cul_value - 1); heap_renew(162 + ((i / 3) * 3 + j / 3) * 9 + k, mutual_index[position_index[162 + ((i / 3) * 3 + j / 3) * 9+k]].cul_value - 1); heap_renew(243 + i * 9 + j, mutual_index[position_index[243 + i * 9 + j]].cul_value - 1); } void node_heap_increase(pd_node current_node)//增加与减少的顺序是刚好相反的 { int i, j, k; k = current_node->index % 9; j = (current_node->index / 9) % 9; i = (current_node->index / 81) % 9; heap_renew(243 + i * 9 + j, mutual_index[position_index[243 + i * 9 + j]].cul_value +1); heap_renew(162 + ((i / 3) * 3 + j / 3) * 9 + k, mutual_index[position_index[162 + ((i / 3) * 3 + j / 3) * 9+k]].cul_value + 1); heap_renew(81 + j * 9 + k, mutual_index[position_index[81 + j * 9 + k]].cul_value + 1); heap_renew(i * 9 + k, mutual_index[position_index[i * 9 + k]].cul_value + 1); } void in_stack(pd_node target_to_stack) { pd_node temp_node_one, temp_node_two, temp_node_three,temp_node_four; temp_node_one = target_to_stack->left;//在删除的时候,从当前点的左边开始 while (temp_node_one != target_to_stack)//从左进行遍历 { temp_node_three = temp_node_one->down;//每遇到一个节点,就往下开始遍历 while (temp_node_three != temp_node_one) { if (temp_node_three->is_head != 1)//如果遇到的点不是头部节点,则当前行摘除 { temp_node_four = temp_node_three->right;//在删除行的过程中,我们采取的是从右往左删 while (temp_node_four != temp_node_three)//从右往左的每一个节点,将他的上下关系摘除,注意第一个点是不能删除的,因为我们需要将他们连接起来。 { temp_node_four->up->down = temp_node_four->down; temp_node_four->down->up = temp_node_four->up; temp_node_four = temp_node_four->right; } node_heap_decrease(temp_node_three);//对这一行所占据的三列进行降低度数操作,每一个降低一度 } else//对于是头节点的情况,我们将头节点与两边摘下来,而且对于头节点,我们不需要进行度数操作 { temp_node_three->left->right = temp_node_three->right; temp_node_three->right->left = temp_node_three->left; } temp_node_three = temp_node_three->down;//继续往下遍历 } temp_node_one = temp_node_one->left; } temp_node_three = temp_node_one->down;//然后对与当前列相交的行的进行删除 while (temp_node_three != temp_node_one) { if (temp_node_three->is_head != 1) { temp_node_four = temp_node_three->right; while (temp_node_four != temp_node_three) { temp_node_four->up->down = temp_node_four->down; temp_node_four->down->up = temp_node_four->up; temp_node_four = temp_node_four->right; } node_heap_decrease(temp_node_three); } else { temp_node_three->left->right = temp_node_three->right; temp_node_three->right->left = temp_node_three->left; } temp_node_three = temp_node_three->down; } node_heap_decrease(target_to_stack);//最后对当前行进行删除 stack[stack_index++] = target_to_stack;//然后才是入栈 available_column -= 4; } void out_stack()//出栈的顺序与入栈的顺序是刚好对称相反的 { pd_node temp_node_one, temp_node_two, temp_node_three, temp_node_four; pd_node target_node; available_column += 4; temp_node_one=target_node = stack[stack_index - 1]; temp_node_three =temp_node_one->up; node_heap_increase(temp_node_one); while (temp_node_three != temp_node_one)//先考虑当前列 { if (temp_node_three->is_head == 1) { temp_node_three->right->left = temp_node_three; temp_node_three->left->right = temp_node_three; } else { temp_node_four = temp_node_three->left; while (temp_node_four != temp_node_three) { temp_node_four->up->down = temp_node_four; temp_node_four->down->up = temp_node_four; temp_node_four = temp_node_four->left; } node_heap_increase(temp_node_three); } temp_node_three = temp_node_three->up; } temp_node_one = target_node->right; while (temp_node_one != target_node) { temp_node_three = temp_node_one->up; while (temp_node_three != temp_node_one) { if (temp_node_three->is_head == 1) { temp_node_three->right->left = temp_node_three; temp_node_three->left->right = temp_node_three; } else { temp_node_four = temp_node_three->left; while (temp_node_four != temp_node_three) { temp_node_four->up->down = temp_node_four; temp_node_four->down->up = temp_node_four; temp_node_four = temp_node_four->left; } node_heap_increase(temp_node_three); } temp_node_three = temp_node_three->up; } temp_node_one = temp_node_one->right; } stack_index--; } void print_result()//打印出结果 { int out[9][9] = { 0 }; int i, j, k,current_index; int m, n; for (m = 0; m < stack_index; m++) { current_index = stack[m]->index; k = current_index % 9; current_index /= 9; j = current_index % 9; current_index /= 9; i = current_index; out[i][j] = k+1; } printf("***********************\n"); for (m = 0; m < 9; m++) { for (n = 0; n < 9; n++) { printf("%d ", out[m][n]|a[m][n]); } printf("\n"); } } int find_next()//用来找下一个可以入栈的元素,如果无法入栈或者已经找到了解,则返回并进行回溯操作 { int target_position; pd_node temp_node_one; if (available_column == current_heap_number) { if (available_column == -1) { print_result(); return 2; } else { target_position = mutual_index[0].position_index; temp_node_one = p_head[target_position]; temp_node_one = temp_node_one->down; in_stack(temp_node_one); return 1; } } else { return 0; } } void seek_sudoku() { int find_result = 0; pd_node temp_node_one; while (1) { find_result = find_next(); if (!find_result)//如果无法入栈且目前没有找到解,则出栈 { temp_node_one = stack[stack_index - 1]; out_stack(); temp_node_one = temp_node_one->down; while ((temp_node_one->is_head))//如果当前元素是当前列头节点,则递归出栈 { if (stack_index == 0)//如果栈空,则所有的搜索空间已经搜索完全 返回 { return; } else { temp_node_one = stack[stack_index - 1]; out_stack(); temp_node_one = temp_node_one->down; } } in_stack(temp_node_one);//将所选元素入栈 } else { if (find_result / 2)//如果已经找到结果,则返回,事实上我们可以更改这个逻辑来应对有多个解的情况,并把它全部打印 { return; } } } } int main() { clock_t clock_one,clock_two,clock_three; clock_one = clock(); creat_dlx_sudoku(a); clock_two = clock(); printf("%d mscond passed in creat_dlx_sudoku \n", clock_two - clock_one); seek_sudoku(); clock_three = clock(); printf("%d mscond passed in seek_sudoku\n", clock_three - clock_two); }
重点参考文章
http://www.cnblogs.com/grenet/p/3145800.html 这篇文章讲的是跳舞链的基本介绍,不过遗漏了跳舞链的启发算法
http://www.cnblogs.com/grenet/p/3163550.html 这篇文章讲的是跳舞链在解数独上的引用,那篇文章里面的实现是直接开大数组。
而我这篇里面的实现是使用链表来实现真正的十字链,而且用堆来实现优先级,使得每次取最小元素的时候的复杂度减少,所带来的副作用就是代码行数很大。
---恢复内容结束---