您的位置:首页 > 其它

一些有趣的算法

2016-08-04 23:54 309 查看
本文专门记录一些有趣的算法。

计算二进制数中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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息