1 算法思想

算法分类

搜索算法主要分为:

暴力搜索+剪枝,枚举,广度优先搜索,深度优先搜索,二分查找,哈希查找, A*算法,两边向中间逼近,从中间向两边扩散等

1.1枚举

枚举: 最直白的搜索方式,依次尝试搜索空间中的所有解。可以在搜索过程中通过加强条件约束来减少搜索范围图。

例如: 百鸡问题

1.2广度优先搜索(BFS)

含义:遍历解答树时使每次状态转移时扩展出尽可能多的状态,并按照各状态出现顺序依次扩展它们。

表现:在解答树上表现为树的层次遍历。

适用:可用于求解最优问题。因为其搜索到的状态总是按照某个关键字递增(例如时间,倒杯次数等)。一旦问题中出现最少,最短,最优等关键字,

就需要考虑是否使用广度优先搜索。

实现: 一般用队列实现,用结构体保存每个状态,用标记数组防止无效搜索。

实现过程:

1)定义结构体用于保存每个状态,定义标记数组防止无效搜索

2)初始化第一个元素,并将该元素塞入队列中,设置第一个元素为已经访问

3)只要队列非空,得到并弹出队头元素,扩展得到新的元素,

对每个新元素,判断其如果满足约束条件并且是未遍历过的,

则更新新元素的状态,并将新元素塞入队列,设置新元素为已经访问过,

如果新元素是所求解,则直接返回

剪枝: 剪去解答树上不可能存在答案的子树。

1.3深度优先搜索(DFS)

含义: 优先遍历层次更深的状态,直到遇到一个状态节点不再拥有子树,则返回上一层,

访问其未被访问过的子树,直到解答树中所有状态被遍历完成。

适用: 深度优先搜索缺少广度优先搜索按层次递增顺序遍历的特性,深度优先搜索到的状态不再具有最优特性,深度优先搜索更多求解的是有解或者无解的问题。

实现: 通常使用递归实现。

实现过程:

1)扩展得到新元素,如果新元素不符合约束条件,则过滤该新元素;

2)基于当前状态更新得到新元素状态,判断新元素状态是否等于所求状态,如果是,设置结果标记为成功并直接返回;

3)否则,设置新元素为已经访问,递归处理,设置新元素为未访问(因为后续状态全部遍历完成,需要退回上层状态),如果结果标记为成功,则停止搜索

1.2 特点

1.3适用

1.4通用解法

广度优先搜索(BFS)算法:

1)定义结构体用于保存每个状态,定义标记数组防止无效搜索

2)初始化第一个元素,并将该元素塞入队列中,设置第一个元素为已经访问

3)只要队列非空,得到并弹出队头元素,扩展得到新的元素,

对每个新元素,判断其如果满足约束条件并且是未遍历过的,

则更新新元素的状态,并将新元素塞入队列,设置新元素为已经访问过,

如果新元素是所求解,则直接返回

深度优先搜索(DFS)算法:

1)扩展得到新元素,如果新元素不符合约束条件,则过滤该新元素;

2)基于当前状态更新得到新元素状态,判断新元素状态是否等于所求状态,如果是,设置结果标记为成功并直接返回;

3)否则,设置新元素为已经访问,递归处理,设置新元素为未访问(因为后续状态全部遍历完成,需要退回上层状态),如果结果标记为成功,则停止搜索

1.5经典例题讲解

广度优先搜索(BFS):

胜利大逃亡

我被魔王抓走,城堡是A*B*C的立方体,即A个B*C的矩阵,我被关在(0,0,0)位置,出口在(A-1,B-1,C-1),魔王在T分钟内回到城堡,

我每分钟能移动1个坐标。若走到出口恰遇魔王,也算成功。请输出多少分钟可以离开,不能则输出-1

代码:

typedef struct Stat                //定义结构体用于保存每个状态

{

int x,y,z;//坐标

int t;//从(0,0,0)到达该坐标的时间

}Stat;//保存当前节点的状态

int maze[N][N][N];//用于标识每一个坐标是0:路,1:墙

bool mark[N][N][N];// 定义标记数组防止无效搜索,用于标识该坐标是否已经搜索过,false:未搜索过,true:搜索过,便于剪枝

int goNext[][3] =

{1,0,0,

-1,0,0,

0,1,0,

0,-1,0,

0,0,1,

0,0,-1};//用于进行下一次6个可达状态的遍历

queue<Stat> queueStat;

//进行深度遍历,无需判断超时

int BFS(int a,int b,int c)

{

//只要队列不空,说明仍有状态需要遍历

while(!queueStat.empty())

{

//弹出当前状态,进行状态迁移

Stat stat = queueStat.front();

queueStat.pop();

//遍历6种状态,扩展得到新的元素

int x,y,z;

for(int i = 0 ; i < 6 ; i++)

{

x = stat.x + goNext[i][0];

y = stat.y + goNext[i][1];

z = stat.z + goNext[i][2];

//对每个新元素,判断其如果满足约束条件并且是未遍历过

// 判断是否仍在围墙中

//易错,这里城堡的位置是不能为a,因为这是数组,这样做就超过了

//if(x < 0 || x > a || y < 0 || y > b || z < 0 || z > c)

if(x <0 || x >= a || y < 0 || y >=b || z < 0 || z >= c )

{

continue;

}

//如果已经遍历过,则跳过

if(true==mark[x][y][z])

{

continue;

}

//如果下一个是墙,则跳过

if(1==maze[x][y][z])

{

continue;

}

//更新新元素的状态,并将新元素塞入队列,设置新元素为已经访问过

Stat statTemp;

statTemp.x = x;

statTemp.y = y;

statTemp.z = z;

statTemp.t = stat.t + 1;//所耗时间进行累加

//易错,更新剪枝状态

mark[x][y][z] = true;

//将新状态放入队列

queueStat.push(statTemp);

//如果新元素是所求解,则直接返回

//判断是否已经到达终点,则返回其所耗时间

if(a-1==x && b-1==y && c-1==z)

{

return statTemp.t;

}

}//for

}//while

return -1;//如果一直没有找到,返回-1

}

深度优先搜索(DFS)

Temple of the bone

有一个N*M的迷宫,起点S,终点D,墙X和地面,'.'表示路,0秒时,我从S出发,

每秒能走到4个与其相邻位置的任意一个,行走之后不能再次走入。

问:是否存在一条路径使主人公刚好在T秒走到D

代码

typedef struct Stat

{

int x,y;//横纵坐标

int t;//耗费时间

}Stat;

char maze[N][N];//保存迷宫元素

bool success;//设置是否找到的成功标记

//走下一个位置的数组

int goNext[][2] =

{0,1,

-1,0,

1,0,

0,-1

};

//深度优先搜索

void DFS(int x,int y,int t,int n,int m,int tLimit)

{

int i ;

// 扩展得到新元素

for(i = 0 ; i < 4 ; i ++)

{

int iXNext = x + goNext[i][0];

int iYNext = y + goNext[i][1];

// 如果新元素不符合约束条件,则过滤该新元素

//判定有无超过迷宫位置

if(iXNext < 1 || iXNext > n || iYNext < 1 || iYNext > m)

{

continue;

}

//判定是否是墙

if('X'==maze[iXNext][iYNext])

{

continue;

}

// 基于当前状态更新得到新元素状态,判断新元素状态是否等于所求状态,如果是,设置结果标记为成功并直接返回

//判定是否到达终点,并且时间要符合

if('D'==maze[iXNext][iYNext] && tLimit==(t + 1))

{

//易错,需要设置成功标记

success = true;

return;

}

//设置新元素为已经访问,递归处理,设置新元素为未访问(因为后续状态全部遍历完成,需要退回上层状态),如果结果标记为成功,则停止搜索

maze[iXNext][iYNext] = 'X';

//递归调用

DFS(iXNext,iYNext,t+1,n,m,tLimit);

//若其后续状态全部遍历完毕,返回上层状态,因为要搜索后续状态,因此再将墙改为普通状态

maze[iXNext][iYNext] = '.';

//易错,判断是否搜索成功

if(true==success)

{

return;

}

}//for

//如果一直遍历不到,则返回-1

return;

}

2 搜索系列

类别-编号

题目

遁去的1

46

Valid Perfect Square

Given a positive integer num, write a function which returns True if num is a perfect square else False.

Note: Do not use any built-in library function such as sqrt.

Example 1:

Input: 16

Returns: True

Example 2:

Input: 14

Returns: False

分析:给定一个正整数,判定它是否是一个完全平方数。不能使用内置的sqrt。

输入:

1

16

14

输出:

true

true

false

Leecode

https://blog.csdn.net/qingyuanluofeng/article/details/59481236

分析:给定一个正整数,判定它是否是一个完全平方数。不能使用内置的sqrt。

暴力破解:让i从1开始遍历判断数i*i是否等于给定数,如果i*i < 给定数,i累加;

如果i*i = 给定数,找到;i*i > 给定数,说明不是。

在此基础上可以优化为二分搜索。

让i每次翻倍查找。

如果初始令low = 1 , high = num

mid * mid > num,说明mid大了,high = mid - 1

mid * mid = num,找到了,返回true

mid * mid < num, 说明mid小了,low = mid + 1

时间复杂度为O(logN)

关键:

1 在此基础上可以优化为二分搜索。

让i每次翻倍查找。

如果初始令low = 1 , high = num

mid * mid > num,说明mid大了,high = mid - 1

mid * mid = num,找到了,返回true

mid * mid < num, 说明mid小了,low = mid + 1

代码:

class Solution {

public:

bool isPerfectSquare(int num) {

int low = 1;

int high = num;

long long mid;

long long result;

while(low < high)

{

mid = (long long)(low + (high - low) / 2);

result = mid * mid;

if(result > num)

{

high = mid - 1;

}

else if(result < num)

{

low = mid + 1;

}

else

{

return true;

}

}

result = (long long) low * (long long) low;

if(result == num)

{

return true;

}

else

{

return false;

}

}

};

47

Water and Jug Problem

You are given two jugs with capacities x and y litres. There is an infinite amount of water supply available. You need to determine whether it is possible to measure exactly z litres using these two jugs.

If z liters of water is measurable, you must have z liters of water contained within one or both buckets by the end.

Operations allowed:

Fill any of the jugs completely with water.

Empty any of the jugs.

Pour water from one jug into another till the other jug is completely full or the first jug itself is empty.

Example 1: (From the famous "Die Hard" example)

Input: x = 3, y = 5, z = 4

Output: True

Example 2:

Input: x = 2, y = 6, z = 5

Output: False

分析:此题是给定两个容量分别为x和y的杯子,问是否能够测量出容积为z的水。

输入:

3(x) 5(y) 4(z)

2 6 5

输出:

true

false

Leecode

https://blog.csdn.net/qingyuanluofeng/article/details/59745816

分析:此题是给定两个容量分别为x和y的杯子,问是否能够测量出容积为z的水。

这是一道递归的题目,一旦找到解就退出,不是求最优解,应该是深度优先搜索。

来分析杯子之间是如何进行状态转移的:

这里为了分析方便,我们假设x >= y

设杯子1的容积为x,杯子1当前盛放的水体积为v1;

杯子2的容积为y,杯子2当前盛放的水体积为v2;

1】把杯子1的水倒入杯子2中,

1.1】假如v1 <= y - v2,则直接把杯子1的水全部倒入杯子2,此时杯子1:v1=0,杯子2:v2 += v1

1.2】假如v1 > y - v2,则会把杯子2倒满,杯子1:v1 -= (y-v2) , v2 = y

2】把杯子1的水倒掉,v1 = 0

3】把杯子1的水加满,v1 = x

4】把杯子2的水倒入杯子1中

4.1】假如v2 <= x - v1,则杯子2为空,杯子1:v1 += v2, v2 = 0

4.2】假如v2 > x - v1 , 则杯子1倒满,杯子1:v1 = x , v2 -= (x - v1)

5】把杯子2的水倒掉,v2=0

6】把杯子2的水加满,v2=y

对6种倒水情况进行的递归处理,直至:v1或者v2为z即可

1如果杯子1水非空,并且杯子2水非满,才可以将杯子1水倒入杯子2

如果杯子1水非满,并且杯子2水非空,才可以将杯子2水倒入杯子1

如果有1个杯子水空了,如何操作?可以将水倒入另一个杯子

如果有一个杯子水满了,如何操作?要么将水满的杯子清空

举例:3 5 4

1】2号杯子装满5,然后从2号杯子到1号杯子,1号杯子:3,2号杯子:2

2】把1号杯子清空,2号杯子的水倒入一号杯子,1号杯子:2,2号杯子:0

3】把2号杯子倒满,然后将2号杯子的水倒入1号杯子,直至1号杯子倒满,1号杯子:3,2号杯子4

里面涉及到可能将一个杯子倒满,或者清空。但是倒满必须在两个杯子中水的体积之和< 给定值的前提下,

清空杯子,必须在两个杯子水的体积之和>=给定值的前提下。

参考解法:http://blog.csdn.net/jmspan/article/details/51756978

https://leetcode.com/problems/water-and-jug-problem/?tab=Solutions

1    两个瓶子可以量出的水是两个瓶子容量的最大公约数

最大公约数定理:如果x,y的最大公约数为d,那么对于任意a,b

ax+by=d,

则若z=n*d,那么必定z可以通过x,y测量出来。

2

//递归出口,注意,这里是要求测量出z,应该不是要求两个中有一个为z

if(v1 + v2 == z)

{

return true;

}

3 两次倒满杯子 + 两次清空杯子 + 两次相互倾倒

两次倒满杯子满足原来是不满

两次清空杯子满足原来是非空

两次倾倒满足:如果杯子1倒入杯子2,则杯子1非空,杯子2非满,否则死循环

倾倒可以直接用绝对值表示

int pour = min(v1 , y - v2);//牛逼,直接计算倒出的量

if( dfs(x , v1 - pour , y , v2 + pour , z , visited ) )

代码:

class Solution {

public:

//求两个数的公约数: 16 , 24,用两者中较大值 % 较小值 = 结果,较大值=较小值,较小值=结果,直到较小值为0

int GCD(int x , int y)

{

if(x < y)

{

int temp = x;

x = y;

y = temp;

}

while(y)

{

int temp = x % y;

x = y;

y = temp;

}

return x;

}

bool canMeasureWater(int x, int y, int z) {

//任意一个杯子体积为z或者两个杯子体积和为z,都是可以的

if(z == x || z == y || x + y == z)

{

return true;

}

if(x < 0 || y < 0 || z < 0 || x + y < z)

{

return false;

}

int gcdNum = GCD(x , y);

//如果z % x和y的最大公约数 为0,说明就是

if(0 == z % gcdNum )

{

return true;

}

else

{

return false;

}

}

bool dfs(int x , int v1 , int y , int v2 , int z, vector< vector<bool> >& visited)

{

//对v1和v2进行参数检查,如果其中有一个小于0,或者超过各自最大容积,则直接返回

//如果某个为0

if(v1 < 0 || v1 > x || v2 < 0 || v2 > y)

{

return false;

}

//递归出口,注意,这里是要求测量出z,应该不是要求两个中有一个为z

if(v1 + v2 == z)

{

return true;

}

//如果当前已经访问过,则直接返回

if(visited.at(v1).at(v2))

{

return false;

}

visited.at(v1).at(v2) = true;

//倒满杯子1

if(v1 < x && dfs(x , x , y , v2 , z , visited) )

{

return true;

}

//倒满杯子2

if(v2 < y && dfs(x , v1 , y , y , z , visited))

{

return true;

}

//清空杯子1

if(v1 > 0 && dfs(x , 0 , y , v2 , z , visited))

{

return true;

}

//清空杯子2

if(v2 > 0 && dfs(x , v1 , y , 0 , z , visited))

{

return true;

}

//从杯子1倒入杯子2,必须满足杯子1非空,杯子2非满。如果不添加限制条件,会变成死循环

if(v1 > 0 && v2 < y)

{

int pour = min(v1 , y - v2);//牛逼,直接计算倒出的量

if( dfs(x , v1 - pour , y , v2 + pour , z , visited ) )

{

return true;

}

}

//从杯子2倒入杯子1

if(v2 > 0 && v1 < x)

{

int pour = min(v2 , x - v1);

if(dfs(x , v1 + pour , y , v2 - pour , z , visited) )

{

return true;

}

}

return false;

}

bool canMeasureWater2(int x, int y, int z) {

if(z == x || z == y)

{

return true;

}

if(x < 0 || y < 0 || z < 0)

{

return false;

}

//初始的时候有3种给定方法:分别都装满,只装满其中一个。如果全部装满,无法倾倒,不行

//只能装满其中一个

vector< vector<bool> > visited(x + 1 , vector<bool>(y + 1 , false));

bool result1 = dfs(x , x , y , 0 , z , visited);

return result1;

}

};

48

Guess Number Higher or Lower

We are playing the Guess Game. The game is as follows:

I pick a number from 1 to n. You have to guess which number I picked.

Every time you guess wrong, I'll tell you whether the number is higher or lower.

You call a pre-defined API guess(int num) which returns 3 possible results (-1, 1, or 0):

-1 : My number is lower

1 : My number is higher

0 : Congrats! You got it!

Example:

n = 10, I pick 6.

Return 6.

分析:实际上就是猜数字游戏。给定n,求猜别人拿的是哪个数字。

Leecode

https://blog.csdn.net/qingyuanluofeng/article/details/59752076

int guess(int num);

class Solution {

public:

int guessNumber(int n) {

int high = n;

int low = 1;

int mid;

while(low < high)

{

mid = low + (high - low) / 2;

int guessValue = guess(mid);

//实际数值小于mid

if(-1 == guessValue)

{

high = mid - 1;

}

else if(1 == guessValue)

{

low = mid + 1;

}

else

{

return mid;

}

}

return low;

}

};

49

如何从数组中找出满足 a+b=c+d 的两个数对

给定一个数组,找出数组中是否有两个数对(a, b)和(c, d), 使得 a+b=c+d,其中, a、b、c和d

是不同的元素。如果有多个答案,打印任意一个即可。例如给定数组:

[3, 4, 7, 10, 20, 9, 8],可以找到两个数对(3, 8)和(4, 7),使得

3+8=4+7

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/91351998

关键:

1 书上解法

建立<数对和,数对>的字典即可

遍历所有数对就是O(n^2)

代码:

class Pair(object):

def __init__(self, first, second):

self.first = first

self.second = second

def findEqualPairs(data):

if not data:

return

length = len(data)

i = 0

sumToPair = dict()

while i < length:

j = i + 1

while j < length:

first = data[i]

second = data[j]

sum = first + second

if sum in sumToPair:

pair = sumToPair[sum]

info = "find equal pairs, ({first}, {second}), ({fir}, {sec})".format(

first=first,

second=second,

fir=pair.first,

sec=pair.second

)

print info

else:

pair = Pair(first, second)

sumToPair[sum] = pair

j += 1

i += 1

50

如何找出旋转数组的最小元素

把一个有序数组最开始的若干个元素搬到数组的末尾,称之为数组的旋转。

输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如:

数组[3,4,5,1,2]为数组[1,2,3,4,5]的一个旋转,该数组的最小值为1。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/92384916

关键:

1 书上解法

mid = (low + high)/2

若前大于后,则后必定是最小值,即满足:

若arr[mid-1] > arr[mid],则arr[mid]是最小

若arr[mid] > arr[mid+1],则arr[mide+1]是最小

若前半部分数组中,第一个元素大于最后一个元素,则在左半区域,即

arr[low] > arr[mid],则在左半部分

若后半部分数组中,第一个元素大于最后一个元素,则在右半区域,即

arr[mid] > arr[high],则在右半部分;

若arr[low] == arr[mid]并且arr[mid] == arr[high],则直接寻找最小值

2 我没想到

如果前后两个数组都是有序的,则可能存在

[1,0,1,1,1,1]这种情况,这种情况需要直接遍历所有。

另外可以通过直接比较arr[mide-1],arr[mid],arr[mid+1]等

去除只有两个元素比较的限制

代码:

def findMinInRotaedArray(array, begin, end):

if not array:

return

if begin > end:

return

middle = (begin + end) / 2

if middle >= 1 and array[middle - 1] > array[middle]:

return array[middle]

if middle <= len(array) - 2 and array[middle] > array[middle + 1]:

return array[middle + 1]

if array[begin] > array[middle]:

result = findMinInRotaedArray(array, begin, middle)

elif array[middle] > array[end]:

result = findMinInRotaedArray(array, middle, end)

elif array[begin] == array[middle] and array[middle] == array[end]:

result = min(array)

else:

result = None

return result

51

如何求数组中两个元素的最小距离

给定一个数组,数组中含有重复元素,给定两个数字num1和num2,求这两个数字在

数组中出现的位置的最小距离。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/92727422

关键:

1 书上解法

令num1出现的位置为pos1,令num2出现的位置为pos2。

每次出现num1,则更新pos1,并计算|pos1 - pos2|

每次出现num2,则更新pos2,并计算|pos1 - pos2|

2 我之所以没想到

是因为忘记了可以在出现num1或者num2的时候,通过更新

位置,计算位置下标值。

这个实际就是搜索中求包含若干个搜索字符串最近的文章内容。

代码:

def getMinDistance(array, num1, num2):

if not array:

return

pos1 = -1

pos2 = -1

minValue = float('inf')

size = len(array)

i = 0

while i < size:

if array[i] == num1:

pos1 = i

# 注意,只有pos1和pos2都有值才有比较的意义

if pos2 >= 0:

minValue = min(minValue, pos1 - pos2)

if array[i] == num2:

pos2 = i

if pos1 >= 0:

minValue = min(minValue, pos2 - pos1)

i += 1

return minValue

52

如何求数组中绝对值最小的数

有1个升序排列的数组,数组中可能有正数、负数或0,求数组中元素的绝对值最小的数,

例如:数组[-10, -5, -2, 7, 15, 50],该数组中绝对值最小的数是-2。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/92801267

分析:

看上去绝对值最小的数会尽可能分布在数组的中间位置。

假如用二分法,问题的关键就是如何确认缩小搜索的空间。

分为几种情况:

1)整个数组都>=0,那么绝对值最小的数就是array[0]

2)整个数组都<=0, 那么绝对值最小的数就是array[-1]

3)如果数组中有正数,也有负数,那么如果我能找到最后一个负数到最后一个正数中的这段范围,

则可以遍历获取其中绝对值最小的元素。

那么问题的关键就是:

如何找到数组中的分界点,如果分界点为0,那么即为所求;

1) 如果a[mid] > 0.

1.1) 如果a[mid - 1] < 0, 则比较a[mid]和a[mid-1]的绝对值即可

1.2) 如果a[mid-1] = 0,则0即为所求

1.3) 如果a[mid - 1] > 0,则在左半部分寻找

2) 如果a[mid] < 0.

2.1) 如果a[mid + 1] > 0, 则比较a[mid]和a[mid+1]的绝对值即可

2.2) 如果a[mid + 1] = 0,则0即为所求

2.3) 如果a[mid + 1] < 0,则在右半部分寻找

关键:

1 我之所以没想到

是因为没有理清如何寻找分界点的逻辑,

实际就是采用二分法,对a[mid]大于0,小于,等于0的各种判定

代码:

def findAbsMinNumber(array, low, high):

if not array:

return

if low > high:

return

if array[0] >= 0:

return array[0]

if array[-1] <= 0:

return array[-1]

size = high - low

mid = size / 2 + low

if 0 == array[mid]:

return 0

elif array[mid] > 0:

if mid - 1 < 0:

return array[mid]

if array[mid - 1] < 0:

result = array[mid] if abs(array[mid]) < abs(array[mid - 1]) else array[mid - 1]

return result

elif 0 == array[mid - 1]:

return 0

else:

result = findAbsMinNumber(array, low, mid - 1)

return result

else:

if mid + 1 > size:

return array[mid]

if array[mid + 1] > 0:

result = array[mid] if abs(array[mid]) < abs(array[mid + 1]) else array[mid + 1]

return result

elif 0 == array[mid + 1]:

return 0

else:

result = findAbsMinNumber(array, mid + 1, high)

return result

53

如何寻找最多的覆盖点

坐标轴上从左到右依次的点为a[0], a[1], a[2], ..., a[n-1],

设一根木棒的长度为L,求L最多能覆盖坐标轴的几个点?

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/93401148

关键:

1 书上解法

更简单的方法,

实际题目想求

a[j] - a[i] <= L and a[j+1] - a[i] > L这两个条件下,

j - i + 1的最大值

设定两个下标i, j。

i从0开始,j从1开始,如果a[j] - a[i] <= L,则j+=1,并记录经过的点数,

否则j-=1,记录经过的我电视-1;回到刚好满足条件的时候,将满足条件的

最大值和记录的最大值比较;然后令i+=1, j+=1

2 我之所以没想到

是忘记了关于升序数组中需要通过夹逼法则,

来求一些问题,通过改变两个下标来求值

代码:

def findMaxPointsEasy(array, L):

if not array or L is None:

return

size = len(array)

i = 0

j = 1

maxCount = 0

# 注意,包含的起始点是a[i], a[j]所以是两个

count = 2

start = 0

while i < size and j < size:

while j < size and array[j] - array[i] <= L:

j += 1

count += 1

j -= 1

count -= 1

if count > maxCount:

maxCount = count

start = i

i += 1

j += 1

return maxCount, start

54

如何求解迷宫问题

给定一个大小为N*N的迷宫,一只老鼠需要从迷宫的左上角(

对应矩阵的[0][0])走到迷宫的右下角(对应矩阵的[N-1][N-1]),

老鼠只能向两方向移动:向右或向下。在迷宫中,0表示没有路

(是死胡同),1表示有路。例如: 给定下面的迷宫:

1   0   0   0

1   1   0   1

0   1   0   0

1   1   1   1

途中标粗的路径就是一条合理的路径。

请给出算法来找到这么一条合理路径。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/93660114

分析:

应该是搜索问题。搜索分为深度优先搜索和广度优先搜索。

广度优先搜索用于求解: 最优值问题

深度优先搜索用于求解: 是否有解问题,不具有最优特性

这道题目应该用深度优先搜索做(这里应该是回溯)。

搜索停止的条件为:

i=N-1, j=N-1

广度优先搜索算法的主要步骤如下:

判断当前位置如果无效,则直接返回;

如果当前位置就是待查找位置,返回结果;

否则,根据当前位置,构造下一个查找的位置,

递归对下一个查找位置进行搜索。

如何保存路径。

可以用一个栈,

关键:

1 我缺少了一个回溯的方式

设置已经访问过的标记为1,没有访问过的为0

回溯算法的模板为:

找到,则打印结果

设置标记

递归

清除标记

......

if i == rowLen - 1 and j == colLen - 1:

path[i][j] = 1

return True

path[i][j] = 1

rightResult = bfsMaze(maze, i + 1, j, rowLen, colLen, path)

if rightResult:

return True

downResult = bfsMaze(maze, i, j + 1, rowLen, colLen, path)

if downResult:

return True

path[i][j] = 0

......

2 我之所以没有想到

是忘记了回溯法实际就是一种高级递归,但是有状态,

通过: 找到目标打印,设置标记,递归,清除标记4步骤实现

代码:

def bfsMaze(maze, i, j, rowLen, colLen, path):

# 如果找到所求的点

if i == rowLen - 1 and j == colLen - 1:

path[i][j] = 1

return True

if not maze:

return False

if i < 0 or i >= rowLen or j < 0 or j >= colLen:

return False

# 如果当前不能行走,即对应元素的值为0,返回False

if 0 == maze[i][j]:

return False

# 回溯: 设置标记,表示该位置已经访问过

path[i][j] = 1

# 构造所有下一个位置

# 向右是: i += 1, j不变;

# 向下是: i不变, j += 1

rightResult = bfsMaze(maze, i + 1, j, rowLen, colLen, path)

if rightResult:

return True

downResult = bfsMaze(maze, i, j + 1, rowLen, colLen, path)

if downResult:

return True

# 说明路径不可行,清除之前设置的标记

path[i][j] = 0

return False

55

如何从三个有序数组中找出它们的公共元素

给定以非递减顺序排序的三个数组,找出这三个数组中的所有公共元素。例如,

给出下面三个数组:

ar1 = [2, 5, 12, 20, 45, 85]

ar2 = [16, 19, 20, 85, 200]

ar3 = [3, 4, 15, 20, 39, 72, 85, 190]

那么这三个数组的公共元素为[20, 85]。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/93662500

分析:

可以分别令三个数组遍历下标为i,j,k

初始都为0,

然后如果ar1[i] = ar2[j] = ar3[k]

那么ar1[i]就是公共元素;

否则,从3个元素中找出最小的两个元素所在的数组对应的

遍历下标,让遍历下标都+1.

继续重复上述循环,直到遍历完所有数组

def findCommonElements(ar1, ar2, ar3):

results = []

if not ar1 or not ar2 or not ar3:

return results

len1 = len(ar1)

len2 = len(ar2)

len3 = len(ar3)

i = 0

j = 0

k = 0

while i < len1 and j < len2 and k < len3:

if ar1[i] == ar2[j] and ar1[i] == ar3[k]:

results.append(ar1[i])

i += 1

j += 1

k += 1

else:

maxValue = max(ar1[i], ar2[j], ar3[k])

if ar1[i] != maxValue:

i += 1

if ar2[j] != maxValue:

j += 1

if ar3[k] != maxValue:

k += 1

return results

56

如何判断两个字符串是否为换位字符串

换位字符串是指组成字符串的字符相同,但位置不同。例如:由于字符串"aaaabbc" 与 "abcbaaa"就是相同的字符所组的,因此它们是换位字符。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/94303630

关键:

1 书上解法

每个字符是ASCII,长度为256,可以实现声明这样一个数组来做

时间复杂度为O(N)

注意求字符对应下标需要

ord(char) - ord('0')

def isTranspositionStrings_hash(str1, str2):

if not str1 and not str2:

return True

elif not str1:

return False

elif not str2:

return False

len1 = len(str1)

len2 = len(str2)

if len1 != len2:

return False

size = 256

hashArray = [0 for i in range(size)]

for char in str1:

index = ord(char) - ord('0')

if index > size:

return False

hashArray[index] += 1

for char in str2:

index = ord(char) - ord('0')

if index > size:

return False

if hashArray[index] is None:

return False

hashArray[index] -= 1

for i in range(size):

if hashArray[i] != 0:

return False

return True

57

如何判断两个字符串的包含关系

给定由字母组成的字符串s1和s2,其中,s2中字母的个数少于s1,如何判断s1是否包含

s2? 即出现在s2中的字符在s1中都存在。

例如s1="abcdef", s2="acf",那么s1就包含s2;

如果s1="abcdef", s2="acg",那么s1就不包含s2,因为s2

中有'g',但是s1中没有'g'

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/94305574

分析:

最简单的方法,还是用刚才的哈希数组,

先对字符串1中每个字符进行哈希,

然后对于字符串2中的每个字符判断其在哈希数组中如果不存在,

则不是包含关系

关键:

1 注意需要自己对数组长短进行判断

入参不一定是按照长字符串,短字符串的顺序

def isContained(str1, str2):

if not (str1 and str2):

return False

len1 = len(str1)

len2 = len(str2)

if len1 > len2:

bigStr = str1

smallStr = str2

else:

bigStr = str2

smallStr = str1

size = 256

hashArray = [0 for i in range(size)]

for char in bigStr:

index = ord(char) - ord('0')

if index >= size:

return False

hashArray[index] += 1

for char in smallStr:

index = ord(char) - ord('0')

if index >= size:

return False

if 0 == hashArray[index]:

return False

return True

58

如何判断一个字符串是否包含重复字符

判断一个字符串是否包含重复字符。例如: 'good'就包含重复字符'0',

而'abc'就不包含重复字符

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/94543155

关键:

1 书上解法

由于1个int可以有32bit,

可以用一个int整数上某1位bit是否为1来表示这个数是否存在;

可以申请8个int,每个int有32bit,共有8 * 32=256 bit

如果首次出现,就可以先用该整数 与 bit上数字进行或运算

即可

确定一个数字就用

num = ord(string[i])

index = num / 32

shift = num % 32

flags[index] & (1 << shift)

2 没有想到

是因为忘记bit位是否为1可以表示一个数字是否存在

def isStringHasRepeatedChar(string):

if not string:

return False

hashArray = [0 for i in range(8)]

for char in string:

index = ord(char) / 32

shift = ord(char) % 32

result = hashArray[index] & (1 << shift)

if result != 0:

return True

hashArray[index] |= (1 << shift)

return False

59

如何查找到达目标词的最短链长度

给定一个词典和两个长度相同的"开始"和"目标"的单词。

找到从开始到姆博阿的最小链的长度。如果它存在,

那么这条链中的相邻单词只有一个字符不同,

而链中的每个单词都是有效单词,即它存在于词典中。

可以假设词典中存在目标字,所有词典的长度相同。

例如:

给定一个单词词典为:

[pooN, pbcc, zamc, poIc, pbca, pbIc, poIN]

start = ToolN

target = pbca

输出结果为: 7

因为: TooN(start) - pooN - poIN - poIc - pbIc - pbcc - pbca(target)。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/95906956

关键:

1 书上分析

广度优先搜索,当前搜索到所花费的时间必然<=后面搜索所花费的时间

因为广度优先搜索本质是按层扩展。

需要记录当前新状态的耗时,所以需要一个类

2 没有想起

忘记bfs可以求最优值,并且根据bfd广度优先扩展层序的特点,

当前的结果必然是最优值。

另外需要标记已经访问过的元素,防止无限循环。

入队的元素必须是类,类中的成员变量记录单词链的长度

bfs的模板:

初始元素入队

只要队列非空:

获取队列中当前元素

扩展状态

若新的扩展状态已经访问过,则跳过;

否则,设置新状态对象,更新新状态对象中的成员变量;

将新状态放入队列,设置新状态为已经访问过,

若新状态等于目标状态,则返回结果

3 代码

while not wordQueue.empty():

item = wordQueue.get()

word = item.word

neighbours = wordToNeighbours.get(word, None)

# 遍历所有邻居

for neighbour in neighbours:

# 如果该邻居已经被访问过,跳过

if visited.get(neighbour) == True:

continue

newItem = Item(neighbour)

# 设置新的单词链长度累加

newItem.lens = item.lens + 1

wordQueue.put(newItem)

# 设置防止被重复访问

visited[word] = True

# 如果找到目标,则返回其长度

if neighbour == target:

return newItem.lens

代码

def bfs(start, target, wordToNeighbours, visited):

if not start or not target or not wordToNeighbours:

return -1

wordQueue = Queue.Queue()

item = Item(start)

wordQueue.put(item)

# 这里需要设置访问标记,防止被重复访问

while not wordQueue.empty():

item = wordQueue.get()

word = item.word

neighbours = wordToNeighbours.get(word, None)

# 遍历所有邻居

for neighbour in neighbours:

# 如果该邻居已经被访问过,跳过

if visited.get(neighbour) == True:

continue

newItem = Item(neighbour)

# 设置新的单词链长度累加

newItem.lens = item.lens + 1

wordQueue.put(newItem)

# 设置防止被重复访问

visited[word] = True

# 如果找到目标,则返回其长度

if neighbour == target:

return newItem.lens

# 所有找不到返回-1

return -1

60

如何判断一个自然数是否是某个数的平方

设计一个算法,判断给定的一个数n是否是某个数的平方,不能使用开方运算。

例如16就满足条件,因为它是4的平方,而15则不满足条件,因为不存在

一个数使得其平方的值为15.

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/95918112

分析:

方法1:

二分查找,从0~n/2开始查找

方法2:

事先建立所有数到其开根号的字典

用二分查找实现,时间复杂度为O(logN)

关键:

1 二分查找要注意一种特殊情况

# 为了防止无限循环,low=mid=high-1,直接对这种情况,跳出循环

if low == high - 1:

break

def getSquareRoot(n):

if n < 0:

return -1

high = n / 2 + 1

low = 0

while low < high:

mid = (low + high) / 2

value = mid ** 2

if value == n:

return mid

elif value < n:

low = mid

else:

high = mid

# 为了防止无限循环,low=mid=high-1,直接对这种情况,跳出循环

if low == high - 1:

break

return -1

61

如何在不能使用库函数的条件下计算n的平方根

给定一个数n,求出它的平方根,比如16的平方根是4.要求不能使用库函数。

Python程序员面试算法宝典

https://blog.csdn.net/qingyuanluofeng/article/details/96723712

62

黑白图像

输入一个n*n的黑白图像(1表示黑色,0表示白色),任务是统计其中八连块的个数。如果两个黑盒子有公共边或者公共顶点,就说它们属于同一个八连块。

如图所示,有3个八连块

输入:

6

100100

001010

000000

110000

111000

010100

输出:

3

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47730863

63

迷宫
一个网格迷宫由n行m列单元格组成,每个单元格要么是空地(用1表示),要么是障碍物(用0表示)。你的任务是找一条从起点到终点的最短移动序列,其中UDLR分别

表示往上、下、左、右移动到相邻单元格。任何时候都不能在障碍格中,也不能走到迷宫之外。起点和终点保证是空地。n,m<=100。

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47730893

64

迷宫路径
一个网格迷宫由n行m列单元格组成,每个单元格要么是空地(用1表示),要么是障碍物(用0表示)。你的任务是找一条从起点到终点的最短移动序列,其中UDLR分别

表示往上、下、左、右移动到相邻单元格。任何时候都不能在障碍格中,也不能走到迷宫之外。起点和终点保证是空地。n,m<=100。

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47730937

65

除法

输入正整数,按从小到大的顺序输出所有形如abcde/fghij=n的表达式,其中a~j恰好为数字0~9的一个排列,2<=n<=79.

输入:

62

输出:

79546/01283=62

94736/01528=62

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47731045

66

最大乘积

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47731081

67

分数拆分

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47731123

68

双基回文数

如果一个正整数n至少在两个不同的进位制b1和b2下都是回文数(2<=b1,b2<=10),则称n是双基回文数(注意,回文数不能包含前导0)。输入正整数S<10^6,输出比

S大的最小双基回文数>

输入:1600000(1632994)

输出:1632995

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47731141

69

倒水问题

有装满水的6升杯子、空的3升杯子和一升杯子,3个杯子中都没有刻度。在不使用其他道具的情况下,是否可以量出4升的水呢?

输入:

6(满杯水所在的刻度) 3 1

输出:

(6,0,0)->(3,3,0)->(3,2,1)->(4,2,0)

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47746957

70

八数码问题

编号为1~8的8个正方形滑块被摆成3行3列(有一个格子空留),如图所示。每次可以把与空格相邻的滑块(有公共边才算相邻)移到空格中,而它原来的位置就称为了

新的空格。给定初始局面和目标局面(用0表示空格格),你的任务是计算出最少的移动步数。如果无法达到目标局面,则输-1.

2     6     4     8  1    5

1     3     7       7  3    6

5     8     4     2

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47746983

71

八数码问题之哈希去重

输入:

2 6 4 1 3 7 0 5 8

8 1 5 7 3 6 4 0 2

输出:

31

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47746995

72

八数码问题之stl

1set<State> vis,这样只需要调用if(vis.count(s))来判断s是否在集合vis中,并用vis.insert(s)加入集合,用vis.remove(s)从集合中移除s。

问题:并不是所有类型的State都可以作为set中的元素类型。set的元素必须定义"<"运算符,C语言原生的数组(包括字符数组)却不行。

2如果数组不能转化为整数,自己声明结构体,重载函数调用符比较状态。下面中,整数a和b分别是两个状态在状态数组st中的下标,在比较时直接使用memcpy来比较整个

内存块

输入:

2 6 4 1 3 7 0 5 8

8 1 5 7 3 6 4 0 2

输出:

31

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47747019

73

二分查找

本质:有序表中使用二分查找,log2(1000)

深入:

如果数组中多个元素都是v,上面的函数返回的是中间的一个。能不能呢个求出值等于v的完整区间呢?

下面的程序当v存在时返回它出现的第一个位置。如果不存在,返回这样一个下标i:在此处插入v(原来的元素A[i],A[i+1],..全部往后移动一个位置)后序列仍然有序

思路

排序后:

0 1 3 4 6 7 9 9

输入:

8

1 9 6 3 4 7 9 0

3

8

1 9 6 3 4 7 9 0

5

输出:

2

-1

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47747225

74

二分查找之lowerBound

注意:对于二分查找的一个系列,high都是用数组长度来计算,真正是取不到的

如果数组中多个元素都是v,上面的函数返回的是中间的一个。能不能呢个求出值等于v的完整区间呢?

下面的程序当v存在时返回它出现的第一个位置。如果不存在,返回这样一个下标i:在此处插入v(原来的元素A[i],A[i+1],..全部往后移动一个位置)后序列仍然有序

输入:

8

0 1 3 4 6 7 9 9

5

8

0 1 4 4 4 4 9 9

4

输出:

4

2

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47747247

75

二分查找之upperBound

写一个upperBound程序,当v存在时返回它出现的最后一个位置的后面的一个位置。如果不存在,返回这样一个下标i:在此处插入v(原来的元素A[i],A[i+1],..全部

往后移动一个位置)后序列仍然有序。

输入:

8

0 1 3 4 6 7 9 9

5

8

0 1 3 4 4 4 9 9

4

8

0 3 3 4 4 4 9 9

3

输出:

4

6

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47747257

76

二分查找之范围统计

给出n个整数xi和m个询问,对于每个询问(a,b),输出闭区间[a,b]内的整数xi的个数。

输入:

8

0 1 3 4 6 7 9 9

3 9

输出:

6

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47747273

77

非线性方程求根

一次向银行借a元钱,分b月还清。如果需要每月还c元,月利率是多少(按复利率计算)?例如借2000元,分4个月每月还510,则月利率为0.797%。答案应不超过100%。
输入:

2000 4 510

输出:

0.797%

算法竞赛入门经典

https://blog.csdn.net/qingyuanluofeng/article/details/47775495

78

统计书中的单词及出现次数,实现一个数据结构进行存储

编程珠玑

https://blog.csdn.net/qingyuanluofeng/article/details/54647029

参考:
[1]计算机考研--机试指南,王道论坛 组编
[2]剑指offer
[3]算法设计与分析
[4]编程之美
[5]程序员面试金典
[6]leecode
[7]Python程序员面试算法宝典
[8]刘汝佳算法竞赛入门经典
[9]算法导论
[10]编程珠玑

算法 64式 7、搜索算法整理_第4部分_46到60题相关推荐

  1. 算法 64式 7、搜索算法整理_第1部分_1到15题

    1 算法思想 算法分类 搜索算法主要分为: 暴力搜索+剪枝,枚举,广度优先搜索,深度优先搜索,二分查找,哈希查找, A*算法,两边向中间逼近,从中间向两边扩散等 1.1枚举 枚举: 最直白的搜索方式, ...

  2. 算法 64式 7、搜索算法整理_第3部分_31到45题

    1 算法思想 算法分类 搜索算法主要分为: 暴力搜索+剪枝,枚举,广度优先搜索,深度优先搜索,二分查找,哈希查找, A*算法,两边向中间逼近,从中间向两边扩散等 1.1枚举 枚举: 最直白的搜索方式, ...

  3. 算法 64式 7、搜索算法整理_第2部分_16到30题

    1 算法思想 算法分类 搜索算法主要分为: 暴力搜索+剪枝,枚举,广度优先搜索,深度优先搜索,二分查找,哈希查找, A*算法,两边向中间逼近,从中间向两边扩散等 1.1枚举 枚举: 最直白的搜索方式, ...

  4. 算法 64式 8、动态规划算法整理_第1部分_1到15题

    1 算法思想 动态规划 1.1含义 把问题分解成多阶段或多个子问题,顺序求解各个子问题,最后一个子问题就是初始问题的解. 概念 阶段: 问题分成的顺序的几个环节.例如最长递增子序列中每个字符就是一个阶 ...

  5. 算法 64式 8、动态规划算法整理

    1 算法思想 动态规划 1.1含义 把问题分解成多阶段或多个子问题,顺序求解各个子问题,最后一个子问题就是初始问题的解. 概念 阶段: 问题分成的顺序的几个环节.例如最长递增子序列中每个字符就是一个阶 ...

  6. 算法 64式 19、数学算法整理

    1 算法思想 2 数学系列 类别-编号 题目 遁去的一 1 特殊乘法 写个算法,对2个小于1000000000的输入,求结果. 特殊乘法举例: 123 * 45 = 1*4 + 1*5 + 2*4 + ...

  7. 算法 64式 4、回溯算法整理__第1部分_1到13题

    1算法思想 回溯 1.1含义 以深度优先方式搜索问题解的算法称为回溯法. 1.2思想 按照深度优先搜索策略,从根节点出发搜索解空间树,如果某结点不包含问题解,则逐层向祖先结点回溯:否则进入子树. 1. ...

  8. 算法 64式 17、排列组合算法整理

    1算法思想 排列组合 1.1含义 排列: 含义:从元素列表中取出指定个数的元素进行排序 公式:从n个不同元素中取出m个元素的排列个数=n!/(n-m)! 组合: 含义:从元素列表中取出指定个数的元素, ...

  9. 算法 64式 14、排序算法整理_1_1到15题

    1 算法思想 这里将寻找最小/大的前k个数,寻找逆序对,线性时间选择(寻找第k小/大的元素),奇偶/大小写字符分别放在前后部分等和排序相关类型的题目,放在了排序而不是查找中. 1.1含义 排序含义:重 ...

最新文章

  1. discuz 修改积分策略( 在周期中添加每周 )
  2. Linux系统下文件字体乱码的解决方案
  3. Installation error: INSTALL_FAILED_UID_CHANGED 的解决办法
  4. 在您的Maven-Fu包中增加了一些东西
  5. centos7 qt之程序编译 cant start process “cmake”
  6. python有趣的简单代码-python有趣代码
  7. xUtils更新到3.0后的基本使用规则
  8. 《当幸福来敲门》观后感
  9. nls_lang环境变量linux设置,设置NLS_LANG环境变量
  10. 数字孪生 软著登记表 模板
  11. install nginx
  12. 中望3D 2021 插入基准面 - 2实体构面法
  13. 从业多年数据分析师的亲身经验!
  14. 假期最后一天!Go1.16 重磅发布,新版本特性一览
  15. miui10.2.2 或以上的小米手机上照片旋转问题及解决
  16. UE中渲染自发光通道的折衷方案
  17. mpv 终极教程【安装、快捷键、 播放列表jonniek/mpv-playlistmanager】
  18. 一文说清楚pytorch和tensorFlow的区别究竟在哪里
  19. Appium自动化测试框架
  20. bootstrap-fileinput上传文件插件按钮展示隐藏

热门文章

  1. 计算机专业学生如何写一份优秀的校招简历(大三、研二学生请进)
  2. C#高效编程 改进C#代码的50个行之有效的办法(第2版)
  3. 初学者怎么学java编程
  4. git flow 概念
  5. 业务突破!看 Docker 如何帮助 BCG Gamma 打造安全的软件供应链
  6. jmeter安装成功后打不开,提示:Cause: CannotResolveClassException: com.blazemeter.jmeter.threads.concurrency.Conc
  7. 油气开采上百年,技术依旧原始,硬伤在这儿
  8. ZZULIOJ:1135: 算菜价
  9. 招聘海外博士计算机视觉国际,丹麦奥尔堡大学计算机视觉博士后职位
  10. [整理】linux学习笔记(3)