一些有趣的算法
2016-08-04 23:54
309 查看
本文专门记录一些有趣的算法。
例题
计算两个int32整数m和n的二进制表达,有多少个位(bit)不同?
解法
此题引申自计算二进制数中有多少个1
深度优先是按照一定的顺序查找完一个分支,再查找另外一个分支,直到找到目标为止。
下面以一个实际的迷宫地图,来看一下深度优先算法在程序中如何实现。迷宫的布局如图二。
游戏规则是:
有一个起始点和终止点,分别位于图中的左上角和右下角。迷宫中白色方格是可通过区域,黑色方格是不可通过区域,迷宫以外的区域也是不可通过区域。
算法思想:
首先定义一些变量来记录游戏中的状态信息:
二维数组:block:记录迷宫的布局。0为可通过,1代表不可通过。
dx、dy:记录移动的方向:左、右、上、下四种情况下x、y坐标需要做的更改。
table二维数组:记录某个坐标上的方格是否到达过。
函数解释:
test:判断是否已经达到目标位置
actOK:判断当前移动方向是否合理,排除不合理的情况:越界、是否有障碍等等。
back:当前路径无法继续下去,执行回退操作,更改相应变量。
DFS:核心函数,执行深度优先遍历操作。在每一个方格点A处,接下来都有4种走法,针对4种方向,依次判断,如果第一种方向合理,则移动到下一格,进行相同的操作。如果继续下去不能走到终点,则最终回退到方格点A,继续第二种方向,重复操作。如果4中方向都无法达到目标,则搜寻失败。
迷宫算法之深度优先搜索
迷宫算法之广度优先搜索
广度优先搜索算法思想:
在这里采用一个辅助数据结构:队列。
(1)顶点v入队列。
(2)当队列非空时则继续执行,否则算法结束。
(3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
(4)查找顶点v的第一个邻接顶点col。
(5)若v的邻接顶点col未被访问过的,则col入队列。
(6)继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)
计算二进制数中1的个数
这道题目的一个解决方案是通过%2来计算,这个比较简单。现在推荐一个快速算法,利用n=n&(n-1);来计算,可以这么解释这个运算的作用:一个数减1以后与自身相与,就消去了最低位的1。对于一个数而言,最后一位如果是1,减1之后前面的位并不受影响,该位的1已经被纳入统计;而如果是0,减1之后该位为1,1&0=0,该位并不受影响,但这带动了前面减1的过程,从而计算前面的位中1的个数。而只要统计过的位数,该位在之后运算中一定为0。所以,其实这个算法是从最低位到最高位按位统计1的个数的。
例题
计算两个int32整数m和n的二进制表达,有多少个位(bit)不同?
解法
public int countBitDiff(int m, int n) { int dif=m^n; //先将二者做异或运算,得到结果; int cnt=0; //这里将不同的位转换成计算1的个数 while(dif!=0){ dif=dif&(dif-1); cnt++; } //统计一个整数dif含有多少个1; return cnt; }
此题引申自计算二进制数中有多少个1
/* * 原理:若n最右边的1在第k个位置,那么n-1之后,第k个位置的数由1变0,k之后的由0变1,k之前的不变。 * 再把n-1和n求& ,会把该整数最右边的1变为0。因此有多少个1,就循环几次。 */ public int count1(int n) { //System.out.println(Integer.toBinaryString(n) ); int count = 0; while (n != 0) { count++; n = n & (n - 1); } return count; }
迷宫算法
迷宫算法属于一种空间路径方法,可以使用深度优先遍历或者广度优先遍历深度优先是按照一定的顺序查找完一个分支,再查找另外一个分支,直到找到目标为止。
下面以一个实际的迷宫地图,来看一下深度优先算法在程序中如何实现。迷宫的布局如图二。
游戏规则是:
有一个起始点和终止点,分别位于图中的左上角和右下角。迷宫中白色方格是可通过区域,黑色方格是不可通过区域,迷宫以外的区域也是不可通过区域。
算法思想:
首先定义一些变量来记录游戏中的状态信息:
二维数组:block:记录迷宫的布局。0为可通过,1代表不可通过。
dx、dy:记录移动的方向:左、右、上、下四种情况下x、y坐标需要做的更改。
table二维数组:记录某个坐标上的方格是否到达过。
函数解释:
test:判断是否已经达到目标位置
actOK:判断当前移动方向是否合理,排除不合理的情况:越界、是否有障碍等等。
back:当前路径无法继续下去,执行回退操作,更改相应变量。
DFS:核心函数,执行深度优先遍历操作。在每一个方格点A处,接下来都有4种走法,针对4种方向,依次判断,如果第一种方向合理,则移动到下一格,进行相同的操作。如果继续下去不能走到终点,则最终回退到方格点A,继续第二种方向,重复操作。如果4中方向都无法达到目标,则搜寻失败。
迷宫算法之深度优先搜索
//迷宫寻路:深度优先算法 #include <iostream> using namespace std; const int width = 10;//宽度 const int height = 10;//高度 //四种移动方向(左、右、上、下)对x、y坐标的影响 //x坐标:竖直方向,y坐标:水平方向 int dx[4] = {0,0,-1,1}; int dy[4] = {-1,1,0,0}; //障碍表 int block[height][width] = { 0,1,0,0,0,0,0,0,0,0, 0,1,1,0,1,1,1,0,0,0, 0,0,0,0,0,0,0,0,0,0, 1,1,1,0,1,0,0,0,0,0, 0,1,0,0,1,0,1,1,1,0, 0,1,0,0,1,1,1,1,1,0, 0,0,0,1,1,0,0,0,1,0, 0,1,0,0,0,0,1,0,1,0, 0,1,1,1,0,1,1,0,1,1, 0,0,0,0,0,0,1,0,0,0, }; const int maxLevels = 1000;//最大移动步数 int maxAct = 4;//移动方向总数 int table[width][height] = {0};//标记是否已达到 int level = -1;//第几步 int levelComplete = 0;//这一步的搜索是否完成 int allComplete = 0;//全部搜索是否已完成 int Act[maxLevels] = {0};//每一步的移动方向(1、2、3、4) int x = 0,y = 0;//现在的坐标 int targetX = height - 1,targetY = width - 1;//目标坐标 void test()//测试是否已达到目标 { if (x == targetX && y == targetY) { levelComplete = allComplete = 1; cout << "Get to destination Success" << endl; } } int actOK()//判断移动方向是否合理 { int nextX = x + dx[Act[level] - 1]; int nextY = y + dy[Act[level] - 1]; if (Act[level] > maxAct)//方向是否错误 return 0; if(nextX >= height || nextX < 0)//x坐标是否越界 return 0; if(nextY >= width || nextY < 0)//y坐标是否越界 return 0; if(table[nextX][nextY] == 1)//是否已达到过 return 0; if(block[nextX][nextY] == 1)//是否有障碍 return 0; x = nextX; y = nextY;//移动 table[nextX][nextY] = 1;//标记已达到 return 1; } void back() { table[x][y] = 0; x -= dx[Act[level - 1] - 1]; y -= dy[Act[level - 1] - 1];//回退到原来的坐标 Act[level] = 0;//清除方向 --level;//回到上一层 } //深度优先搜索 void DFS() { table[x][y] = 1; while(!allComplete) { ++level;//搜索下一步 levelComplete = 0;//这一步的搜索还未完成 while(!levelComplete) { ++Act[level];//改变移动方向 if (actOK())//方向合理 { test(); levelComplete = 1;//该步搜索完成 } else { if (Act[level] > maxLevels)//已经搜索完所有方向 { back();//回退,到上一个分支 } if (level < 0)//全部搜索完,仍然没有搜索到目标 { levelComplete = allComplete = 1;//退出 } } } } } void print() { cout << "The path is " << endl; for (int i = 0;i < height;++i) { for (int j = 0;j < width;++j) { /*cout << table[i][j] << " ";*/ if(table[i][j]) cout << "1 "; else cout << " "; } cout << endl; } } int main(){ DFS(); print(); }
迷宫算法之广度优先搜索
广度优先搜索算法思想:
在这里采用一个辅助数据结构:队列。
(1)顶点v入队列。
(2)当队列非空时则继续执行,否则算法结束。
(3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
(4)查找顶点v的第一个邻接顶点col。
(5)若v的邻接顶点col未被访问过的,则col入队列。
(6)继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)
//迷宫寻路:广度优先搜索 #include <iostream> using namespace std; const int rows = 10;//行数 const int cols = 10;//列数 const int numDirections = 4;//每一步,下一步可以走的方向:4个 //四种移动方向(左、右、上、下)对x、y坐标的影响 //x坐标:竖直方向,y坐标:水平方向 const char dx[numDirections] = {0,0,-1,1}; const char dy[numDirections] = {-1,1,0,0}; //障碍表 char block[rows][cols] = { 0,1,0,0,0,0,0,0,0,0, 0,1,1,0,1,1,1,0,0,0, 0,0,0,0,0,0,0,0,0,0, 1,1,1,0,1,0,0,0,0,0, 0,1,0,0,1,0,1,1,1,0, 0,1,0,0,1,1,1,1,1,0, 0,0,0,1,1,0,0,0,1,0, 0,1,0,0,0,0,1,0,1,0, 0,1,1,1,0,1,1,0,1,1, 0,0,0,0,0,0,1,0,0,0, }; char path[rows][cols] = {0};//记录路径 int startX = 0,startY = 0;//起始点坐标 int endX = rows - 1,endY = cols - 1;//目标点坐标 //保存节点位置坐标的数据结构 typedef struct tagQNode{ char x,y; int parentNode;//父节点索引 }QNode; //打印路径 void printPath() { cout << "Success : the path is " << endl; for (int i = 0;i < rows;++i) { for (int j = 0;j < cols;++j) { if (1 == path[i][j]) { cout << "1 "; } else cout << " "; } cout << endl; } } void BFS() { int num = rows * cols; //利用数组来模拟队列 QNode *queue = (QNode *)malloc(num * sizeof(QNode)); //起始点入队列 queue[0].x = queue[0].y = 0; queue[0].parentNode = -1;//起始点没有父节点 int front = 0,rear = 1;//队列的头和尾 while(front != rear)//队列不为空 { for (int i = 0;i < numDirections;++i) { char nextX,nextY;//下一步的坐标 nextX = queue[front].x + dx[i]; nextY = queue[front].y + dy[i]; //下一个节点可行 if (nextX >= 0 && nextX < rows && nextY >= 0 && nextY < cols && 0 == block[nextX][nextY]) { //寻找到目标点 if (nextX == endX && nextY == endY) { //生成路径 path[nextX][nextY] = 1; int tempParentIndex = front; while(tempParentIndex != -1) { path[queue[tempParentIndex].x][queue[tempParentIndex].y] = 1; tempParentIndex = queue[tempParentIndex].parentNode; } printPath(); } //入栈 queue[rear].x = nextX; queue[rear].y = nextY; queue[rear].parentNode = front; ++rear; //标记此点已被访问 block[nextX][nextY] = 1; } } ++front; } } int main() { BFS(); //printPath(); }