DFS和BFS笔记(二):深度优先搜索(C语言实现)

临近学期末总(预)结(习)一下程序设计课。

笔记梳理和课堂练习题完成的题解总结。


搜索算法实际上是根据初始条件和扩展规则构造一棵“解答树”并寻找符合目标状态的节点的过程。搜索是“通用解题方法”,在算法和人工智能领域有重要的地位。但是搜索有局限性和自身灵活性,是最难学最难用的算法之一。

【学习目标】面对问题:1.很快建立状态空间 2.提出一个合理算法 3.简单估计时空性能

搜索的分类

1盲目搜索:

·广度优先搜索(Breadth First Search)

·深度优先搜索(Depth First Search)

·纯随机搜索、重复式搜索、迭代加深搜索、迭代加宽搜索、柱型搜索……

2启发式搜索

广度优先搜索(BFS)

QAQ上一篇:https://blog.csdn.net/m0_51588059/article/details/117562541

深度优先(DFS)

1)构造一个顶点元素类型的,初始状态时其内的元素只有根节点;

2)将栈中的栈顶元素与目标元素比较,如果是,停止寻找;否则,继续执行程序;

3)移除栈顶元素,并将栈顶元素的后继节点依次入栈中;

4)如果栈为空,查找失败;否则,转向第二步。

算法思路

1、创建一个空栈stack(用来存放节点)和一个空列表visit(用来存放已访问的节点)

2、依次将起始点及邻接点加入stack和visit中

3、pop出栈中最后进入的节点,从图中获取该节点的邻接点

4、如果邻接点不在visit中,则将该邻接点加入stack和visit中

5、输出pop出的节点

6、重复3、4、5,直至栈为空

栈:先进后出,只有一边开口存取数据,称开口的那一端为“栈顶”,封死的那一端为“栈底”

https://www.cnblogs.com/vacation/p/5179457.html
(1).选择合适角度定义结点状态
     选择合适的角度来定义结点状态,是设计搜索算法重要的一步,往往搜索算法可以从不同角度进行搜索,哪个角度更容易描述结点状态(结点定义),哪个角度搜索的层次更明确(搜索深度),哪个角度状态间的转换关系更容易实现(产生式),哪个角度可以搜索的效率更高(剪枝)等等,这是我们选择合适角度定义状态需要综合考虑的问题。
(2).产生式
     所谓产生式,即从当前结点状态变换到下一结点状态的关系式。每个结点产生新结点的个数实际上就是该结点的搜索宽度。有的产生式很简单很直接,例如全排列问题,直接穷举可选的数就行了,有的产生式需要稍加变换,例如,在骑士巡游问题中马有8种跳法,每一个结点就可以最多扩展出8个新结点,每个新结点都是由原来结点坐标加上一个增量得到的,所以可以用for语句枚举8个方向的坐标增量就可以了。产生式的好坏,也可以直接影响程序的效率。
(3).扩展条件
     即满足什么条件结点才可以向下扩展产生新结点,也就是说满足什么条件才可以继续向下搜索。这个扩展条件往往是约束搜索树规模的重要一环,很多搜索问题的优化都在这一环节进行剪枝。
(4). 目标状态
     确定正确的目标状态,即满足什么条件输出方案。
  一种情况是要搜索出所有目标结点或任意一个目标结点,一种是要搜索出最优的目标结点,如果需要输出具体方案,还要使用数组记录下来每一步的搜索过程。
(5). 状态的保存和恢复
     如果扩展子结点的过程需要用全局变量或变量形参保存结点状态,则子程序返回调用处后必须恢复其值。

过河问题

某人要带一条狗、一只鸡、一箩米过河,但小船除需要人划外,最多只能载一物过河,而 当人不在场时,狗要咬鸡、鸡要吃米 。问此人应如何过河 ?

状态转移模型和图论模型。

分析

·穷举状态,同时需判断动作的有效性。

 ·广度优先搜索。

·需要避免出现重复状态导致死循环。

·动作的优先级:过河优先带物;回返优先带货物。

建模:

建立(x1,x2,x3,x4)作为状态模型,四个位置代表(人,狗,鸡,米),用0代表起始位置,1代表终点位置。我们需要(0,0,0,0)->(1,1,1,1)。

过程:

流程图:

代码:

同学说我的思路怪怪的疑似上课没有认真听讲。纯用C真难受我一定马上滚去学习C++。

#include <stdio.h>
#include <stdlib.h>
typedef struct lineStack{
    int place[4];
    struct lineStack * next;
}lineStack;
//pop之后,更新place为top 
lineStack* top(lineStack * stack,int a[]){
	if (stack->place!=NULL) {//非空 
		a[0]=stack->place[0];a[2]=stack->place[2];
		a[1]=stack->place[1];a[3]=stack->place[3];
      //  printf("now_stack_top:%d,%d,%d,%d\n",a[0],a[1],a[2],a[3]);   
    }
    else
	{
    	printf("null\n");	
	}
    return stack;
}
//因为后进先出,所以stack永远是最后一个 
lineStack* push(lineStack * stack,int a[]){
	//printf("push:%d,%d,%d,%d\n",a[0],a[1],a[2],a[3]);
    lineStack * line=(lineStack*)malloc(sizeof(lineStack));
    line->place[0]=a[0];line->place[1]=a[1];
	line->place[2]=a[2];line->place[3]=a[3];
    line->next=stack;//新节点 
    stack=line;//更新 
    return stack;
}
lineStack * pop(lineStack * stack){
    if (stack->next!=NULL) {
        lineStack * p=stack;//stack是最后一个的位置 
        stack=stack->next;
		printf("pop:%d,%d,%d,%d\n",p->place[0],p->place[1],p->place[2],p->place[3]);
        free(p);
    }
	else
        printf("null\n");
    return stack;
}
//移动方法(人,狗,鸡,米)
//按照1-2-3-4-null的顺序,每次最多push一个入stack  
lineStack*  move(lineStack * stack,int place[4],int visit[100][4],int temp[4],int count_j) 
{
	//f1
	int flog;
	if(place[0]==place[1])
	{
		temp[0]=1-place[0];temp[1]=1-place[1];temp[2]=place[2];temp[3]=place[3];
		flog=1;
		if(temp[0]==0&&(temp[2]*temp[1]==1||temp[2]*temp[3]==1)||temp[0]==1&&(temp[2]+temp[1]==0||temp[2]+temp[3]==0))
		{	//printf("now:%d %d %d %d\n",temp[0],temp[1],temp[2],temp[3]);
			goto next2;//方案不可行
		} 
		for(int i=0;i<count_j;i++)
		{
			if(temp[0]==visit[i][0]&&temp[1]==visit[i][1]&&temp[2]==visit[i][2]&&temp[3]==visit[i][3])
				flog=0;//节点已经出现
		}
		if(flog)
		{
			stack=push(stack,temp);//new.push.
			return stack; 
		}
	}//f2 
	next2:;
	if(place[0]==place[2])
	{
		temp[0]=1-place[0];temp[1]=place[1];temp[2]=1-place[2];temp[3]=place[3];
		if(temp[0]==0&&(temp[2]*temp[1]==1||temp[2]*temp[3]==1)||temp[0]==1&&(temp[2]+temp[1]==0||temp[2]+temp[3]==0))
		{//	printf("now:%d %d %d %d\n",temp[0],temp[1],temp[2],temp[3]);
			goto next3;//方案不可行 
			}
		flog=1;
		for(int i=0;i<count_j;i++)
		{
			if(temp[0]==visit[i][0]&&temp[1]==visit[i][1]&&temp[2]==visit[i][2]&&temp[3]==visit[i][3])
				flog=0;
		}
		if(flog)
		{
			stack=push(stack,temp);//new.push.
			return stack; 
		}
	}//f3
	next3:;
	if(place[0]==place[3])
	{
		temp[0]=1-place[0];temp[1]=place[1];temp[2]=place[2];temp[3]=1-place[3];
		if(temp[0]==0&&(temp[2]*temp[1]==1||temp[2]*temp[3]==1)||temp[0]==1&&(temp[2]+temp[1]==0||temp[2]+temp[3]==0))
		{//printf("now:%d %d %d %d\n",temp[0],temp[1],temp[2],temp[3]);
		goto next4;//方案不可行 
		}
		flog=1;
		for(int i=0;i<count_j;i++)
		{
			if(temp[0]==visit[i][0]&&temp[1]==visit[i][1]&&temp[2]==visit[i][2]&&temp[3]==visit[i][3])
				flog=0; 
		}
		if(flog)
		{
			stack=push(stack,temp);//new.push.
			return stack; 
		}
	}//f4
	next4:;
		temp[0]=1-place[0];temp[1]=place[1];temp[2]=place[2];temp[3]=place[3];
		flog=1;
		if(temp[0]==0&&(temp[2]*temp[1]==1||temp[2]*temp[3]==1)||temp[0]==1&&(temp[2]+temp[1]==0||temp[2]+temp[3]==0))
		{//printf("now:%d %d %d %d\n",temp[0],temp[1],temp[2],temp[3]);
		goto next5;//方案不可行
		} 
		for(int i=0;i<count_j;i++)
		{
			if(temp[0]==visit[i][0]&&temp[1]==visit[i][1]&&temp[2]==visit[i][2]&&temp[3]==visit[i][3])
				flog=0;
		}
		if(flog)
		{
			stack=push(stack,temp);//new.push.
			return stack; 
		}
	next5:;
	temp[0]=temp[1]=temp[2]=temp[3]=-1; 
	return stack; //null
}

int main()
{
	lineStack * stack=NULL,*p;//stack 
	//标记头节点位置。
	int place[4]={0,0,0,0};//起始状态
	int visit[100][4]={0};//存放是否出现过 
	visit[0][0]=0;visit[0][1]=0;visit[0][2]=0;visit[0][3]=0;//起始元素进入visit 
	stack=push(stack,place);
	int count_j=1;
	int temp[4]={0};
	while(1) //进入循环
	{	
		//把当前项邻接项push
		p=stack;//记录位置,判断是否push成功 
		//printf("stack_top:%d %d %d %d \n",stack->place[0],stack->place[1],stack->place[2],stack->place[3]) ;
		stack=move(stack,place,visit,temp,count_j);
		//printf("top:%d %d %d %d\n",temp[0],temp[1],temp[2],temp[3]);	
		if(temp[0]==1&&temp[1]==1&&temp[2]==1&&temp[3]==1)
		{
			printf("过河方案:\n");
			for(lineStack*temp=stack;temp->next!=NULL;temp=temp->next)
				printf("(%d %d %d %d) ",temp->place[0],temp->place[1],temp->place[2],temp->place[3]);
			printf("\n");
			//更新
			place[0]=visit[count_j][0]=temp[0];place[1]=visit[count_j][1]=temp[1];
			place[2]=visit[count_j][2]=temp[2];place[3]=visit[count_j][3]=temp[3];
			count_j++;
			//继续循环 
			stack=pop(stack);
			top(stack,place);
			continue;
		}
			
		if(p==stack)
		{//到头了 
			if(stack->next==NULL)
			{	//全部遍历
				return 0; 
			}
			stack=pop(stack);
			top(stack,place);
			printf("place_after_pop:%d %d %d %d\n",place[0],place[1],place[2],place[3]);
		}
		else
		{//更新visit+place
			place[0]=visit[count_j][0]=temp[0];place[1]=visit[count_j][1]=temp[1];
			place[2]=visit[count_j][2]=temp[2];place[3]=visit[count_j][3]=temp[3];
			printf("place_after_push:%d %d %d %d\n",place[0],place[1],place[2],place[3]);
			count_j++;
		}
	}	
}

 

问题:

·没有办法找到第二条道路,用visit存放已经遍历过的节点,是为了避免重复,但是涉及不应该包括成功的那一条路的节点,(通往成功的路只有一条节点(0,1,0,1)是必不可少的)。

·分析是穷举不到位。

没有遍历完全这一棵树。我真的不会改,没有找到C的只有C++的·,欠着本道题。

PLUS:C语言栈stack总结

(我发现百度百科竟然有对于这个的定义而且写的真的及其全面连实现算法实现都有:https://baike.baidu.com/item/%E6%A0%88/12808149

·线性表一种。一边开口存取数据“栈顶”,封死的一端为“栈底”取数据。

·先进后出的结构。包括顺序链式栈两种。

·栈一共四个功能:1入栈(push)2判满(isFull)3出栈(pop)4判空(isEmpty)。

·要避免出现“上溢”和“下溢”。

·链栈一般不需要创建头结点,头结点会增加程序的复杂性,只需要创建一个头指针就可以了。


参考:

·https://blog.csdn.net/weixin_45705162/article/details/108296417

·https://blog.csdn.net/t11383/article/details/91129812

·https://blog.csdn.net/nail_candy/article/details/90741681

·http://data.biancheng.net/view/9.html

·https://www.cnblogs.com/vacation/p/5179457.html

·https://blog.csdn.net/jjzhoujun2010/article/details/6856164

·https://blog.csdn.net/m0_37961948/article/details/80245008


成熟是一种明亮而不刺眼的光辉,一种圆润而不腻耳的音响,一种不再需要对别人察言观色的从容,一种终于停止向周围申诉求告的大气,一种不理会哄闹的微笑,一种洗刷了偏激的淡漠,一种无须声张的厚实,一种并不陡峭的高度。——《苏东坡突围》

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值