搜索分析(DFS、BFS、递归、记忆化搜索)
1、线性查找
在数组a[]={0,1,2,3,4,5,6,7,8,9,10}中查找1这个元素。
(1)普通搜索方法,一个循环从0到10搜索,这里略。
(2)递归(从中间向两边)
1 //递归一定要写成记忆化递归 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool vis[11]; 5 int count1=0; 6 7 void search(int n){ 8 count1++; 9 if(n>10||n<0||vis[n]){ 10 //cout<<"back"<<endl; 11 } 12 else if(n==1){ 13 vis[n]=true; 14 cout<<"find"<<endl; 15 } 16 else { 17 vis[n]=true; 18 search(n-1); 19 search(n+1); 20 21 } 22 } 23 24 int main(){ 25 int a[]={0,1,2,3,4,5,6,7,8,9,10}; 26 search(9); 27 cout<<count1<<endl; 28 return 0; 29 30 }
这种方法一定要加标记数组,不然会出现死循环。
其中一个死循环:
search(9)->search(8)->search(9)
而这样的死循环太多了。
其实分析的时候直接把递归的树形图画出来就好了,直观而且方便。
这样带有标记数组的递归,本质上就是记忆化递归。
所以这种环形的递归都可以写成记忆化递归。
(3)递归(从后面向前面)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int count1=0; 5 6 void search(int n){ 7 count1++; 8 if(n>10||n<0){ 9 } 10 else if(n==1){ 11 cout<<"find"<<endl; 12 } 13 else { 14 search(n-1); 15 } 16 } 17 18 int main(){ 19 int a[]={0,1,2,3,4,5,6,7,8,9,10}; 20 search(10); 21 cout<<count1<<endl; 22 return 0; 23 24 }
这种方法是不需要标记数组的,因为递归是线性的,而不是环形的,递归之间没有交叉,不会造成重复访问。
这种和求阶乘的是一样的。
(4)BFS(从中间向两边)
1 #include <bits/stdc++.h> 2 using namespace std; 3 bool vis[11]; 4 int count1=0; 5 queue<int> que; 6 7 void searchBFS(int n){ 8 que.push(n); 9 while(!que.empty()){ 10 count1++; 11 cout<<"count1:"<<count1<<endl; 12 13 int tmp=que.front(); 14 que.pop(); 15 vis[tmp]=true; 16 cout<<"tmp:"<<tmp<<endl; 17 if(tmp==1) { 18 cout<<"find"<<endl; 19 return ; 20 } 21 else{ 22 if(tmp-1>=0&&!vis[tmp-1]) que.push(tmp-1); 23 if(tmp+1<=10&&!vis[tmp+1]) que.push(tmp+1); 24 } 25 } 26 } 27 28 int main(){ 29 int a[]={0,1,2,3,4,5,6,7,8,9,10}; 30 searchBFS(9); 31 cout<<count1<<endl; 32 return 0; 33 34 }
这种BFS也是一定要带标记数组的,所以也可以写成记忆化。
这种BFS如果不带标记数组的话,也是可以得到正确答案的,不过会重复算很多算过的东西。
例如:9 8 10 7 9 9 6 8 8 10 8 10 .........
比如说上面的9就访问了很多次,而由于队列FIFO的特性,所以虽然重复算很多次,还是会得到正确答案。
因为7、6那一支会逐渐到1的。
当然,BFS也可以直接写成线性的,这样也是不需要标记数组的。
其实还是那样,把情况的树形图画出来就很舒服了,直观方便。
二、阶乘
(1)普通求阶乘方法:略。
(2)阶乘的递归实现DFS
阶乘的递归实现
DFS
1 #include <bits/stdc++.h> 2 using namespace std; 3 int jiechen(int n){ 4 if(n==1) return 1; 5 else{ 6 return n*jiechen(n-1); 7 } 8 } 9 int main(){ 10 cout<<jiechen(8)<<endl; 11 return 0; 12 }
从尾直接算到头,不需要标记数组
(2)阶乘的栈实现
1 /* 2 伪码: 3 我们求f(n),f(n)入栈 4 在栈不空的情况下(下面循环) 5 出栈 6 f(n)=f(n-1)*n 7 如果f(n-1)没有被求出来,直接入栈 8 */ 9 10 //对数组而言,我们操作的肯定是下标,而一定不是数组元素的值 11 #include <bits/stdc++.h> 12 using namespace std; 13 stack<int> sta; 14 int a[15]; 15 16 int jiechen(int n){ 17 a[1]=1; 18 sta.push(n); 19 while(!sta.empty()){ 20 int tmp=sta.top(); 21 sta.pop(); 22 //如果a[tmp-1]被计算了 23 if(a[tmp-1]!=0){ 24 a[tmp]=a[tmp-1]*tmp; 25 cout<<tmp<<" "<<a[tmp]<<endl; 26 } 27 else{ 28 sta.push(tmp); 29 sta.push(tmp-1); 30 } 31 } 32 return a[8]; 33 } 34 35 int main(){ 36 cout<<jiechen(8)<<endl; 37 return 0; 38 }
对数组而言,我们操作(存储进栈或者队列或者其它操作)的肯定是下标,而一定不是数组元素的值
其实栈实现和递归实现是一样的,因为递归在计算机内部就是用栈实现的。