题目1:给你一个矩阵,里面有‘#’,表示障碍,‘.’表示空地,‘S’表示人开始的位置,‘E’表示箱子的预期位置,‘0’表示箱子的初始位置,你的任务是把箱子从‘0’推到‘E’。
注意不能将箱子推到‘#’上,也不能将箱子推出边界;
现在给你游戏的初始样子,你需要输出最少几步能够完成游戏,如果不能完成,则输出 - 1.
输入描述:
第一行为 2 个数字,n,mn,m,表示游戏盘面大小有nn 行mm 列 (5
输出描述:
一个数字,表示最少几步能完成游戏,如果不能,输出 - 1.
样例输入:
3 63 6
.S#..E.S#..E
.#.0...#.0..
............
样例输出:
11
首先可以看出是一个bfs 问题,推箱子的时候首先人要站到箱子紧挨的四个方向的格子中,才能把箱子推向某一个方向,因此,两个bfsbfs,第一个bfsbfs 搜索箱子的位置,第二个bfs 搜索人的位置到箱子紧挨的四个方向的格子要走的步数,大致方向就是这样,下面说下细节.
到终点时,箱子可以从四个方向推到终点,因此要维护一个最小值,当然了,箱子可能并不一定可以从四个方向推到终点,这个你自己去判一下就好了;
我用了优先队列做优化,原因是人到箱子的四个方向的步数大小不一,因此我们优先选择总步数最小的扩展;
在第二个bfsbfs 中,要把箱子当成障碍物;
走过的地方不能再走,但是这里位置的唯一性要由人的位置和箱子的位置共同确定,因此要开一个 4 维数组,不过我们通过hashhash 可以降到 2 维;
我们把方向按:上 - 0,右 - 1, 下 - 2,左 - 3 编号,为的就是在编码的时候可以方便统一地求人要走的那个位置和箱子即将推到的那个位置.
#include using namespace std;const int dirx[] = {-1, 0, 1, 0};const int diry[] = {0, 1, 0, -1};int n, m;struct Point { int x, y; Point() {} Point(int x, int y) : x(x), y(y) {} bool operator == (const Point &other) const { return x == other.x && y == other.y; }};struct Node { Point peo, box; int step; Node() {} Node(Point peo, Point box, int step = 0) : peo(peo), box(box), step(step) {} bool operator < (const Node &other) const { return step > other.step; }};struct Peo { Point point; int step; Peo() {} Peo(Point point, int step = 0) : point(point), step(step) {}};bool check(int x, int y){ return x >= 0 && x < n && y >= 0 && y < m;}int bfs(Point src, Point des, Point box, vector<string> &mp){ queue que; vector<vector<bool> > used(n, vector<bool>(m, false)); que.push(src); used[src.x][src.y] = true; int ret = -1; while (!que.empty()) { auto now = que.front(); que.pop(); if (now.point == des) { ret = now.step; break; } for (int k = 0; k < 4; k++) { int tx = now.point.x + dirx[k]; int ty = now.point.y + diry[k]; if (check(tx, ty) && mp[tx][ty] == '.' && !(Point(tx, ty) == box) && !used[tx][ty]) que.emplace(Point(tx, ty), now.step + 1), used[tx][ty] = true; } } return ret;}int main(){ // freopen("in", "r", stdin); while (cin >> n >> m) { string str; vector<string> mp; Point initS, initE, initZ; for (int i = 0; i < n; i++) { cin >> str; mp.push_back(str); for (int j = 0; j < str.size(); j++) { if (str[j] == 'S') { initS = Point(i, j); mp[i][j] = '.'; break; } } for (int j = 0; j < str.size(); j++) { if (str[j] == 'E') { initE = Point(i, j); mp[i][j] = '.'; break; } } for (int j = 0; j < str.size(); j++) { if (str[j] == '0') { initZ = Point(i, j); mp[i][j] = '.'; break; } } } vector<vector<bool> > used(n * m + 1000, vector<bool>(n * m + 1000, false)); priority_queue que; que.emplace(initS, initZ); used[initS.x * n + initS.y][initZ.x * n + initZ.y] = true; int ans = 0x3f3f3f3f; int cnt = 0; for (int k = 0; k < 4; k++) if (check(initE.x + dirx[k], initE.y + diry[k]) && mp[initE.x + dirx[k]][initE.y + diry[k]] == '.') ++cnt; while (!que.empty()) { auto now = que.top(); que.pop(); if (now.box == initE) { cnt--; ans = min(now.step, ans); if (cnt == 0) break; else continue; } for (int k = 0; k < 4; k++) { int tmpSx = now.box.x + dirx[k]; int tmpSy = now.box.y + diry[k]; int tmpZx = now.box.x + dirx[(k + 2) % 4]; int tmpZy = now.box.y + diry[(k + 2) % 4]; if (!check(tmpSx, tmpSy) || !check(tmpZx, tmpZy)) continue; if (mp[tmpSx][tmpSy] != '.') continue; if (mp[tmpZx][tmpZy] != '.') continue; if (used[now.box.x * m + now.box.y][tmpZx * m + tmpZy]) continue; int step = bfs(now.peo, Point(tmpSx, tmpSy), now.box, mp); if (step == -1) continue; que.emplace(now.box, Point(tmpZx, tmpZy), step + now.step + 1); used[now.box.x * m + now.box.y][tmpZx * m + tmpZy] = true; } } cout << (ans == 0x3f3f3f3f ? -1 : ans) << endl; } return 0;}
题目2:O (1) 时间内删除链表节点
假设链表......---A--B--C--D....,要删除 B。一般的做法是遍历链表并记录前驱节点,修改指针,时间为 O (n)。删除节点的实质为更改后驱指针指向。这里,复制 C 的内容至 B (此时 B,C 同时指向 D),删除节点 C,即达到间接删除节点 B 的目的。
倘若 B 是链尾节点。则需要线性遍历寻找前驱节点。
时间复杂度为 O (1)
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
if(!*pListHead||!pListHead || !pToBeDeleted)
return;
// 非链表尾指针
if(pToBeDeleted->m_pNext != NULL)
{
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_nKey = pNext->m_nKey;
pToBeDeleted->m_pNext = pNext->m_pNext;
delete pNext;
pNext = NULL;
}
else if(pListHead==pToBeDeleted){
delete *pListHead;
*pListHead=NULL;
pToBeDelete=NULL;
}
else
{
ListNode* pNode = *pListHead;
while(pNode->m_pNext != pToBeDeleted)
{
pNode = pNode->m_pNext;
}
// deleted pToBeDeleted
pNode->m_pNext = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;//重要,释放所属空间后,指针置空
}
}
题目3:在屏幕坐标系(屏幕左上角是(0,0)点,向下 y 坐标增大,向右 x 坐标增大)定义矩阵 rect,求两个矩形的交集(无交集时返回 null)
输入描述:
输入两个矩形, x1 y1 w1 h1 x2 y2 w2 h2
输出描述:
交集的矩形(x y w h),无交集时输出 null
示例输入:
0 0 200 200 100 100 100 100
输出:
100 100 100 100
重点是判断两个矩形相交的边界条件。
#include
using namespace std;
int main() {
int a[4];
int b[4];
for (int i = 0; i < 4; i++) {
cin >> a[i];
}
for (int i = 0; i < 4; i++) {
cin >> b[i];
}
int x1 = a[0];
int x2 = a[0] + a[2];
int y1 = a[1];
int y2 = a[1] + a[3];
int x3 = b[0];
int x4 = b[0] + b[2];
int y3 = b[1];
int y4 = b[1] + b[3];
if ((x3 >= x1 && x3 < x2) && (y3 >= y1 && y3 < y2)) {
int w1 = x2 >= x4 ? x4 : x2;
int h1 = y2 >= y4 ? y4 : y2;
w1 -= x3;
h1 -= y3;
cout << x3 << " " << y3 << " " << w1 << " " << h1 << endl;
}
else if((x1 >= x3 && x1 < x4) && (y1 >= y3 && y1 < y4))
{
int w1 = x2 >= x4 ? x4 : x2;
int h1 = y2 >= y4 ? y4 : y2;
w1 -= x3;
h1 -= y3;
cout << x1 << " " << y1 << " " << w1 << " " << h1 << endl;
}
else
{
cout << "null" << endl;
}
return 0;
}
题目4:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
1
2
3
示例 2:
输入: "cbbd"
输出: "bb"
遍历字符串,以每个字符作为中心词,往两边延伸判断是否左右两边字符是否相等。每次只需要维护延伸得到的最长回文子串。算法所需要时间为,空间复杂度 。
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n == 1:
return s
max_s = ''
# 匹配‘aba’型回文
for i in range(1, n):
p, q = i - 1, i + 1
while p >= 0 and q < n:
if s[p] != s[q]:
break
p -= 1
q += 1
max_s = max(max_s, s[p+1:q], key=len)
# 匹配‘abba’型回文
for i in range(0, n):
p, q = i, i + 1
while p >= 0 and q < n:
if s[p] != s[q]:
break
p -= 1
q += 1
max_s = max(max_s, s[p+1:q], key=len)
return max_s
题目5:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。如果是返回 true,否则返回 false。
例如:
输入5、7、6、9、11、10、8 true
输入7、4、6、5 false
二叉查找树:
左右子树均为二叉查找树,在此基础上,左子树的最大值 (即最右节点) 小于和右子树的最小值 (即最左节点) 大于根节点。
1)使用递归。结束条件,仅有<=2个节点的序列一定是查找树。
2)以小于根节点的第一个节点作为左子树的根节点,划分左右子树。递归判断子树是否为查找树。
3)在此条件上,满足左子树最大值仍小于根节点值(步骤2中,保证右子树全部大于根节点,故右子树的最小值情况不用判断)
bool verify(int *data,int length){//length为序列长度
if(length<=2)
return true;
else{
bool left=false,right=false;
int j=length-2;
int* root=data+length-1;
for(;j>=0&&*(data+j)>*root;j--);
left=verify(data,j+1);
right=verify(data+j+1,length-j-2);
if(left&&right&&(j<=0||*(data+j-1)//左子树仅有1个点或者没有节点的情况,不用判断左子树的最小值
return true;
}
return false;
}
sizeof 此时可用来判断数组的长度。
int data[]={7,4,6,5};
bool flag=verify(data,sizeof(data)/sizeof(int));
题目6:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。问总共有多少条不同的路径?
说明:m 和 n 的值均不超过 100。
输入: m = 3, n = 2
输出: 3
从左上角开始,总共有 3 条路径可以到达右下角。
向右 -> 向右 -> 向下
向右 -> 向下 -> 向右
向下 -> 向右 -> 向右
#include
using namespace std;
int uniquePaths(int m, int n) {
int arr[101][101];
for(int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) {
if (i == 1 || j == 1)
arr[i][j] = 1;
else {
arr[i][j] = arr[i - 1][j] + arr[i][j - 1];
}
}
return arr[m][n];
}
int main() {
int m, n, res;
cin >> m >> n;
cout << uniquePaths(m, n) << endl;
return 0;
}
题目7:给定一个整数数组 a,其中1≤a[i]≤n(n 为数组长度), 其中有些元素出现 两次而其他元素出现一次。找到所有出现两次的元素。
你可以不用到任何额外空间并在 O(n) 时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
思路:这道题的难点在于题目限制了不能使用额外的空间,并且要求了时间复杂度。假如没有这两条限制,可使用的方式很多,比如采用字典保存数值和出现次数,或者先排序再遍历数组等等。
既然有了额外的限制,就要从题目中找到额外的信息帮助解决问题。题目中有一个很有用的信息:1≤a[i]≤n,这意味着我们可以将 a[i]-1 看做数组的索引,访问a[a[i]−1]。那么对于出现两次的数字, 以这个数字为索引的元素也会被访问两次。那么我们只要找到方法识别哪些元素被访问多次。
既然不能使用额外的空间,只能在原数组上做文章。遍历数组 a,每经过一个数字a[i],以 (a[i]-1)%n 为索引,将对应位置的数字 加上 n。如果这个数字多次出现,对应位置的数字最终结果必然会大于 2*n。因此只需要第二次遍历数组,找出数值超过 2*n 的元素 对应索引,加上 1,就是原始出现两次的元素。
整个算法过程如图所示:
from typing import List
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
n = len(nums)
res = []
for i in range(n):
nums[(nums[i] - 1) % n] += n
for i in range(n):
if nums[i] > 2 * n:
res.append(i + 1)
return res
if __name__ == '__main__':
s = Solution()
test_data = [4, 3, 2, 7, 8, 2, 3, 1]
print(s.findDuplicates(test_data))
题目8:有 4 种面值(面值为 1, 4, 12, 21)的邮票很多枚,从中最多任取 5 张进行组合,求邮票最大连续组合值
#define N 5#define M 5int k, Found, Flag[N];int Stamp[M] = {0, 1, 4, 12, 21};// 在剩余张数 n 中组合出面值和 Valueint Combine(int n, int Value){ if(n >= 0 && Value == 0){ Found = 1; int Sum = 0; for(int i=0; i"" flag[i]= Sum += Stamp[Flag[i]]; printf("%d ", Stamp[Flag[i]]); } printf("\tSum=%d\n\n", Sum); }else for(int i=1; i0; i++) if(Value-Stamp[i] >= 0){ Flag[k++] = i; Combine(n-1, Value-Stamp[i]); Flag[--k] = 0; } return Found;}int main(int argc, char* argv[]){ for(int i=1; Combine(N, i); i++, Found=0); return getchar();}
计算机视觉常见面试题型介绍及解答
第一期 | 第二期 | 第三期 | 第四期 | 第五期 |
第六期 | 第七期 | 第八期 | 第九期 | 第十期 | 第11期
腾讯算法工程师笔试真题介绍及解析汇总合集
第一期 | 第二期 | 第三期 | 第四期 | 第五期
阿里算法岗位最新编程题介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期
第六期 | 第七期
华为研发工程师编程题型介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期 |
第六期 | 第七期 | 第八期 | 第九期 | 第十期 |
字节跳动校招研发岗位笔试编程题型介绍及解析
第一期 | 第二期 | 第三期 | 第四期