一些有趣的算法

本文专门记录一些有趣的算法。

计算二进制数中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();   
}  
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值