第四十五课 递归的思想与应用(下)

g函数返回后,f函数对应的栈中的数据没有任何变化,这就是回溯算法的核心。

可以这样思考,先逆序打印从第二个节点开始的子表,最后再将第一个节点打印出来。

 1 #include <iostream>
 2 #include <cstring>
 3 #include "DTString.h"
 4 
 5 using namespace std;
 6 using namespace DTLib;
 7 
 8 struct Node
 9 {
10     int value;
11     Node* next;
12 };
13 
14 Node* create_list(int v, int len)  // v:数据元素从哪一个之开始。 len:长度
15 {
16     Node* ret = NULL;
17     Node* slider = NULL;
18 
19     for(int i=0; i<len; i++)
20     {
21         Node* n = new Node();
22 
23         n->value = v++;
24         n->next = NULL;
25 
26         if( slider == NULL )
27         {
28             slider = n;
29             ret = n;
30         }
31         else
32         {
33             slider->next = n;
34             slider = n;
35         }
36     }
37 
38     return ret;
39 }
40 
41 void destroy_list(Node* list)
42 {
43     while( list )
44     {
45         Node* del = list;
46 
47         list = list->next;
48 
49         delete del;
50     }
51 }
52 
53 void print_list(Node* list)
54 {
55     while( list )
56     {
57         cout << list->value << "->";
58 
59         list = list->next;
60     }
61 
62     cout << "NULL" << endl;
63 }
64 
65 
66 
67 void r_print_even(Node* list)
68 {
69     if( list != NULL)
70     {
71         r_print_even(list->next);
72 
73         if( (list->value % 2) == 0 )
74         {
75             cout << list->value << endl;
76         }
77     }
78 }
79 
80 int main()
81 {
82     Node* list = create_list(2, 5);
83 
84     print_list(list);
85 
86     r_print_even(list);
87 
88     destroy_list(list);
89 
90     return 0;
91 }

 逆序打印栈的增长与退栈示意图:

 退栈打印的过程就是回溯的过程。

 递归调用的时候只是先将参数保存在栈上,这时这个参数还没有用到,只是让指针指向了相应的节点,退栈的时候才用到。

类似于走迷宫,走到一个路口时先做个标记,这个标记暂时不用,回来的时候再用。根据标记找来时的路。

回溯的本质就是做标记,这样方便回退。

八皇后:

放皇后的时候只需要关注箭头的方向,因为其他的方向还没有放任何东西。

当我们发现某一行的任何一个位置都不能放皇后的时候,就开始要回溯了。因为,这证明了上一行放置皇后的地方是错误的。

 当我们发现第i行的任何一个位置都不能放置皇后了,这就证明了第i-1行放置的位置是错误的。

如果发现第i-1行也没有地方可以放置皇后了,这意味着还要继续回退,退到i-2行,这就是回溯。

 

理论上,每放置一个皇后要判断8个方向,但是我们从第一行开始放置皇后,有规律的放,因此,只需要考虑三个方向。

如果判断一个位置的左下角对角线是否已经放置了皇后,那就在这个位置的基础上,在xy坐标上不断的减一。因此,方向数据很重要。左下角方向数据是(-1,-1)。

  1 #include <iostream>
  2 #include <cstring>
  3 #include "DTString.h"
  4 #include "LinkList.h"
  5 
  6 using namespace std;
  7 using namespace DTLib;
  8 
  9 struct Node
 10 {
 11     int value;
 12     Node* next;
 13 };
 14 
 15 Node* create_list(int v, int len)  // v:数据元素从哪一个之开始。 len:长度
 16 {
 17     Node* ret = NULL;
 18     Node* slider = NULL;
 19 
 20     for(int i=0; i<len; i++)
 21     {
 22         Node* n = new Node();
 23 
 24         n->value = v++;
 25         n->next = NULL;
 26 
 27         if( slider == NULL )
 28         {
 29             slider = n;
 30             ret = n;
 31         }
 32         else
 33         {
 34             slider->next = n;
 35             slider = n;
 36         }
 37     }
 38 
 39     return ret;
 40 }
 41 
 42 void destroy_list(Node* list)
 43 {
 44     while( list )
 45     {
 46         Node* del = list;
 47 
 48         list = list->next;
 49 
 50         delete del;
 51     }
 52 }
 53 
 54 void print_list(Node* list)
 55 {
 56     while( list )
 57     {
 58         cout << list->value << "->";
 59 
 60         list = list->next;
 61     }
 62 
 63     cout << "NULL" << endl;
 64 }
 65 
 66 
 67 
 68 void r_print_even(Node* list)
 69 {
 70     if( list != NULL)
 71     {
 72         r_print_even(list->next);
 73 
 74         if( (list->value % 2) == 0 )
 75         {
 76             cout << list->value << endl;
 77         }
 78     }
 79 }
 80 
 81 template <int SIZE>
 82 class QueueSolution : public Object
 83 {
 84 protected:
 85     enum { N  = SIZE + 2};  //设置边界用
 86 
 87     struct Pos : public Object
 88     {
 89         Pos(int px = 0, int py = 0) : x(px),y(py){}
 90         int x;
 91         int y;
 92     };
 93 
 94     int m_chessboard[N][N];
 95     Pos m_direction[3];  //方向数组
 96     LinkList<Pos> m_solution; // 使用链表存储解决方案(放置皇后的位置)
 97     int m_count;
 98 
 99     void init()
100     {
101         m_count = 0;
102 
103         for(int i = 0; i < N; i+=(N-1))
104         {
105             for(int j = 0; j<N; j++)
106             {
107                 m_chessboard[i][j] = 2;
108                 m_chessboard[j][i] = 2;
109             }
110         }
111 
112         for(int i=1; i<=SIZE; i++)
113         {
114             for(int j = 1; j <= SIZE; j++)
115             {
116                 m_chessboard[i][j] = 0;
117             }
118         }
119 
120         m_direction[0].x = -1;
121         m_direction[0].y = -1;
122         m_direction[1].x = 0;
123         m_direction[1].y = -1;
124         m_direction[2].x = 1;
125         m_direction[2].y = -1;
126     }
127 
128     void print()
129     {
130         for(m_solution.move(0); !m_solution.end(); m_solution.next())
131         {
132             cout << "(" << m_solution.current().x << "," << m_solution.current().y << ") ";
133         }
134 
135         cout << endl;
136 
137         for(int i=0; i<N; i++)
138         {
139             for(int j=0; j<N; j++)
140             {
141                 switch(m_chessboard[i][j])
142                 {
143                     case 0 : cout << " " ; break;
144                     case 1 : cout << "#" ; break;  //有皇后
145                     case 2 : cout << "*" ; break;
146                 }
147             }
148 
149             cout << endl;
150         }
151 
152         cout << endl;
153     }
154 
155     bool check(int x, int y, int d)
156     {
157         bool flag = true;
158 
159         do
160         {
161             x += m_direction[d].x;
162             y += m_direction[d].y;
163             flag = flag && (m_chessboard[x][y] == 0);
164         }
165         while( flag );
166 
167         return (m_chessboard[x][y] == 2); //do while退出时,如果到达了边界说明这个方向没有皇后
168     }
169 
170     void run(int j)  //检查第j行有没有可以放置皇后的位置
171     {
172         if( j <= SIZE )
173         {
174             for(int i = 1; i <= SIZE; i++)
175             {
176                 if( check(i, j, 0) && check(i, j, 1) &&check(i, j, 2) )
177                 {
178                     m_chessboard[i][j] = 1;
179 
180                     m_solution.insert(Pos(i, j));
181 
182                     run(j + 1); // 看下一行能不能放皇后,如果返回,意味着i,j位置不能放皇后,要回溯
183 
184                     m_chessboard[i][j] = 0;
185 
186                     m_solution.remove(m_solution.length() - 1);
187 
188                 }
189             }
190         }
191         else
192         {
193             //到这里意味着找到了解决方案
194             m_count++;
195 
196             print();
197         }
198     }
199 public:
200     QueueSolution()
201     {
202         init();
203     }
204 
205     void run()
206     {
207         run(1);
208 
209         cout << "Total  : " << m_count << endl;
210     }
211 };
212 
213 int main()
214 {
215     QueueSolution<8> qs;
216 
217     qs.run();
218 
219     return 0;
220 }

共92种解法。

小结:

 

原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9678384.html