算法24-两个岛之间的最短路径

题目:
矩阵中的的点1代表陆地,0代表海洋,求两个由1组成的岛之间的最短路径,规定只能走上下左右,不能斜着走,求联通两个岛的最短路径

分析:
1 要探清岛的边界,从每一个节点是1的位置开始,上下左右移动知道找到边界0
2 从岛边界第一个0开始,往外依次标记距离
3 实现上可以将二维压缩成一维

在这里插入图片描述

在这里插入图片描述

代码:

package main
import (
	"fmt"
	// "sync"

)
//当前位置发现了1,就把这一片的1全部拿取,遇到0就停,实际上形成了一个小岛
// m二维数组,i,j 二维index,N二维行总数,M二维列总数,curs当前为1的而二维坐标压缩成一维后的index、records二维压缩为一维
//main函数控制了从[i][j]==1开始查找岛的边界,一轮相当于完成一个岛的边界探寻,
//保证传入的curs和tmp是不同的数组,底层指针是不一样的
//
func reflcet(m [][]int, N,M,i,j int,curs *[]int,tmp *[]int,index int) int{
	// fmt.Printf("ref     icurs=============%v\n",curs)
	// fmt.Printf("ref     tmp=============%v\n",tmp)
	// fmt.Printf("address of slice %p add of Arr %p\n", &curs, &tmp)
	if i<0 || j<0 || i==N || j==M || m[i][j]!=1{
		return index
	}	
	m[i][j]=2
	idx:=i*M+j
	(*tmp)[idx]=1//对tmp地址上的这个index操作,一定要用括号外面括起来
	// fmt.Printf("after label      tmp=============%v\n",tmp)
	
	(*curs)[index]=idx
	index++
	// fmt.Printf("after label       icurs=============%v\n",curs)
	// fmt.Printf("after   label slice %p add of Arr %p\n", &curs, &tmp)
	index=reflcet(m,N,M,i-1,j,curs,tmp,index)
	index=reflcet(m,N,M,i+1,j,curs,tmp,index)
	index=reflcet(m,N,M,i,j-1,curs,tmp,index)
	index=reflcet(m,N,M,i,j+1,curs,tmp,index)
	return index
	
}
//
//宽度优先遍历curs里面的点,从curs里面拿到index,再从tmp[1,0,1]里面找到对应在二维的上下左右==0的点,再在tmp找到映射把它标为广播的距离v
//next 下一次广播的队列[],v当前广播的距离1,2,3  ,size就是上面得到的curs的有效数值长度
//					queuesize=bfs(m , N,M,v ,queuesize,curs,tmp,next)

func bfs(m [][]int, N,M,v ,size int,curs,tmp,next []int) int{
	// var once sync.Once

	fmt.Printf("curs=============%v\n",curs)
	// fmt.Printf("curs=============%v\n",next)

	fmt.Printf("vvvvvvvv=============%v\n",v)
	//next记录了每一轮要标记的上下左右共有多少个点,
	//因为下面使用的时候开始就是++,如果初始为0的时候就是从1开始,这样记录next的时候就会直接从1开始,把index=0算在内
	//因此直接初始为-1,上下左右谁先对它++是无法确定的,不管谁先开始,++就变成了0,这样就解决了每次把0算在内的问题
	//最后return的时候-1就不会越界
	nexti:=-1
	//从哪些点扩散,从curs的坐标,长度就是size,标记完这些点返回
	for i:=0;i<size;i++{
		// fmt.Printf("curs[i]=============%v\n",curs[i])
		//将一维的的点映射到二维再映射到一维,找到二维上下左右的的爱你对应的一维的坐标
		//确定二维对应的边界,再映射到一维
		up:=0
		if curs[i]<M{
			up=-1
		}else{
			up=curs[i]-M
		}
		down:=0
		if curs[i]+M>=N*M{
			down=-1
		}else{
			down=curs[i]+M
		}
		left:=0
		if curs[i]%M==0{
			left=-1
		}else{
			left=curs[i]-1
		}
		right:=0
		if curs[i]%M==M-1{
			right=-1
		}else{
			right=curs[i]+1
		}
		//这些点是0才扩散,不是0就是已经标记过了的点
		if up!=-1 && tmp[up]==0{
			//注意++一定是满足标记条件才累加
			tmp[up]=v
			nexti++
			next[nexti]=up
		}
		if down!=-1 && tmp[down]==0{
			nexti++
			tmp[down]=v
			next[nexti]=down
		}
		if left!=-1 && tmp[left]==0{
			nexti++
			tmp[left]=v
			next[nexti]=left
		}		
		if right!=-1 && tmp[right]==0{
			nexti++
			tmp[right]=v
			next[nexti]=right
		}
	}
	// fmt.Printf("records =============%v\n",tmp)
	// fmt.Printf("next =============%v\n",next)
	// fmt.Printf("nextiiiiiiiii =============%v\n",nexti)
	//返回下一次要标记的点的长度
	return nexti+1
}

func main(){

	var m [][]int
	t1:=[]int{1,1,1,0,0}
	t2:=[]int{1,0,1,0,0}
	t3:=[]int{1,0,1,1,0}
	t4:=[]int{0,1,1,0,0}
	t5:=[]int{0,1,0,0,1}
	t6:=[]int{0,0,0,0,1}
	t7:=[]int{0,0,0,1,1}
	m=append(m,t1)
	m=append(m,t2)
	m=append(m,t3)
	m=append(m,t4)
	m=append(m,t5)
	m=append(m,t6)
	m=append(m,t7)
	fmt.Println(m)
	N:=len(m)
	M:=len(m[0])
	for i:=0;i<N;i++{
		fmt.Println(m[i])

	}
	//每个数组的都是独立的变量,不能引用
	curs:=[]int{}
	records:=[][]int{}
	tmp:=[]int{}
	next:=[]int{}
	for i :=0;i<N*M;i++{
		curs=append(curs,0)
		tmp=append(tmp,0)
		next=append(next,0)
	}
	queuesize:=0
	for i:=0;i<N;i++{
		for j:=0;j<M;j++{

			if m[i][j]==1{
				//queuesize:要标记的长度
				queuesize=reflcet(m,N,M,i,j,&curs,&tmp,0)
				v:=1
				//循环标记距离,找到curs中的点对应在records中的位置,从他开始找到它的上下左右的点,标记完返回下一次要标记的点的队列长度
				for queuesize !=0{
					v++
					//queuesize:每标记完一轮记录下一轮要标记的长度,等于0就退出,表示标记完成
					queuesize=bfs(m , N,M,v ,queuesize,curs,tmp,next)
					var temp []int
					//交换curs和next,
					temp=curs
					curs=next
					next=temp
				}
				//record是一个二维数组,以行代表一个岛的辐射距离,将计算好距离的数组添加到records
				records=append(records,tmp)
				//然后将tmp,cues,naxt分别置空,注意一定是三个数组底层地址是不一样的
				temp:=[]int{}
				temp1:=[]int{}
				temp2:=[]int{}

				for i :=0;i<N*M;i++{
					temp=append(temp,0)
					temp1=append(temp1,0)
					temp2=append(temp2,0)
				}
				tmp=temp
				curs=temp1
				next=temp2
			}
		}
	}

	fmt.Println(records)
	shortestway:=records[0][0]+records[1][0]
	for i:=1;i<N*M;i++{
		tmp:=records[0][i]+records[1][i]
		if tmp<shortestway{
			shortestway=tmp
		}
	}

	fmt.Println(shortestway)

}

总结:
1每个数组都有自己的作用,并且是独立的,一个数组不能指向任何另外一个数组
2,标记距离函数山下左右谁先第一个是无法判断的,当next值初始为0的时候++就从1开始,导致0永远算作了下一轮要标记的对象,占了容量而队尾的对象就被忽略了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值