搜索else

目录

〇,前言

一,数组和集合基于计算的搜索

CodeForces 702B Powers of Two

力扣 137. 只出现一次的数字 II

力扣 260. 只出现一次的数字 III

力扣 268. 缺失数字

力扣 2965. 找出缺失和重复的数字

力扣 面试题 17.19. 消失的两个数字

二,数组和集合的搜索else

力扣 35. 搜索插入位置

力扣 805. 数组的均值分割

力扣 1658. 将 x 减到 0 的最小操作数

剑指 Offer 03. 数组中重复的数字

二,二维表格搜索

力扣 764. 最大加号标志

力扣 1391. 检查网格中是否存在有效路径


〇,前言

DFS、BFS一般用来解决树和图的搜索问题,对象空间是不是良序的,

而数组和集合的搜索,对象空间是良序的。

DFS、BFS一般把对象空间搜索一遍就能得到答案,比较复杂的问题往往复杂度在于如何高效剪枝,

而数组和集合的搜索,往往搜索一遍还得不到答案,

比如寻找2个数的和为给定数的所有数对,需要两层的嵌套搜索,暴力时间是O(n^2),

一些技巧,比如二分等,可以用在数组和集合的搜索问题上,但不能用于DFS、BFS

一,数组和集合基于计算的搜索

CodeForces 702B Powers of Two

题目:

Description

You are given n integers a1, a2, ..., an. Find the number of pairs of indexes i, j (i < j) that ai + aj is a power of 2 (i. e. some integer xexists so that  ai + aj = 2^x).

Input

The first line contains the single positive integer n (1 ≤ n ≤ 10^5) — the number of integers.

The second line contains n positive integers a1, a2, ..., an (1 ≤ ai ≤ 10^9).

Output

Print the number of pairs of indexes i, j (i < j) that ai + aj is a power of 2.

Sample Input

Input
4
7 3 2 1
Output
2
Input
3
1 1 1
Output
3

#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
 
int list1[100001];
int list2[100001];
int n;
 
long long f(int m)
{
	for (int i = 0; i < n; i++)list2[i] = m - list1[n-1-i];
	long long sum = 0;
	long long temp1, temp2;
	for (int i = 0, j = 0; i < n && j < n;)
	{
		if (list1[i] < list2[j])i++;
		else if (list1[i] > list2[j])j++;
		else
		{
			temp1 = 1;
			temp2 = 1;
			while (i + 1 < n && list1[i + 1] == list2[j])
			{
				i++;
				temp1++;
			}
			while (j + 1 < n && list2[j + 1] == list1[i])
			{
				j++;
				temp2++;
			}
			sum += temp1*temp2;
			i++;
			j++;
		}
	}
	return sum;
}
 
int main()
{
	cin >> n;
	int max = 0;
	for (int i = 0; i < n; i++)cin >> list1[i];
	sort(list1, list1 + n);
	int mi = 1;
	long long sum = 0;
	for (int i = 0; i < 30; i++)
	{
		mi *= 2;
		sum += f(mi);
	}
	for (int i = 0; i < n; i++)
	if (list1[i] == (list1[i] & (-list1[i])))sum--;
	cout << sum / 2;
	return 0;
}


力扣 136. 只出现一次的数字(基于计算的搜索)

题目:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4

代码:

class Solution {
public:
	int singleNumber(vector<int>& nums) {
		int ans = 0;
		for (auto it = nums.begin(); it != nums.end(); it++)ans ^= *it;
		return ans;
	}
};

力扣 137. 只出现一次的数字 II

题目:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,3,2]
输出: 3
示例 2:

输入: [0,1,0,1,0,1,99]
输出: 99

代码:

class Solution {
public:
	long long yiHuoAtBase3(long long a, long long b)
	{
		for (long long k = 1; k <= b; k *= 3){
			a -= (a / k % 3 - (a / k + b / k) % 3)*k;
		}
		return a;
	}
	int singleNumber(vector<int>& nums) {
		long long ans1 = 0, ans2 = 0;
		for (auto it = nums.begin(); it != nums.end(); it++){
			long long tem = *it;
			if (tem >= 0){
				ans1 = yiHuoAtBase3(ans1, tem);
			}
			else{
				ans2 = yiHuoAtBase3(ans2, -tem);
			}
		}
		return ans1-ans2;
	}
};

力扣 260. 只出现一次的数字 III

题目:

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]
注意:

结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

代码:

class Solution {
public:
	vector<int> singleNumber(vector<int>& nums) {
		int s1, s2;
		long long s = 0;
		for (int i = 0; i < nums.size(); i++)s ^= nums[i];
		s = s & -s;
		s1 = 0, s2 = 0;
		for (int i = 0; i < nums.size(); i++)
		{
			if (s&nums[i])s1 ^= nums[i];
			else s2 ^= nums[i];
		}
		return vector<int>{s1, s2};
	}
};

力扣 268. 缺失数字

题目:

给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。

示例 1:

输入: [3,0,1]
输出: 2
示例 2:

输入: [9,6,4,2,3,5,7,0,1]
输出: 8
说明:
你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?

代码:

class Solution {
public:
	int missingNumber(vector<int>& nums) {
		int n = nums.size();
		int ans = 0;
		for (int i = 1; i <= n; i++)ans ^= (i^nums[i - 1]);
		return ans;
	}
};

力扣 2965. 找出缺失和重复的数字

给你一个下标从 0 开始的二维整数矩阵 grid,大小为 n * n ,其中的值在 [1, n2] 范围内。除了 a 出现 两次b 缺失 之外,每个整数都 恰好出现一次 。

任务是找出重复的数字a 和缺失的数字 b 。

返回一个下标从 0 开始、长度为 2 的整数数组 ans ,其中 ans[0] 等于 a ,ans[1] 等于 b 。

 

示例 1:

输入:grid = [[1,3],[2,2]]
输出:[2,4]
解释:数字 2 重复,数字 4 缺失,所以答案是 [2,4] 。

示例 2:

输入:grid = [[9,1,7],[8,9,2],[3,4,6]]
输出:[9,5]
解释:数字 9 重复,数字 5 缺失,所以答案是 [9,5] 。

 

提示:

  • 2 <= n == grid.length == grid[i].length <= 50
  • 1 <= grid[i][j] <= n * n
  • 对于所有满足1 <= x <= n * n 的 x ,恰好存在一个 x 与矩阵中的任何成员都不相等。
  • 对于所有满足1 <= x <= n * n 的 x ,恰好存在一个 x 与矩阵中的两个成员相等。
  • 除上述的两个之外,对于所有满足1 <= x <= n * n 的 x ,都恰好存在一对 i, j 满足 0 <= i, j <= n - 1 且 grid[i][j] == x 。
class Solution {
public:
    vector<int> findMissingAndRepeatedValues(vector<vector<int>>& grid) {
        set<int>s;
        int a=0,n=grid.size(),ss=-(n*n+1)*n*n/2;
        for(auto v:grid){
            for(auto x:v){
                if(s.find(x)!=s.end())a=x;
                s.insert(x);
                ss+=x;
            }
        }
        return vector<int>{a,a-ss};
    }
};

力扣 面试题 17.19. 消失的两个数字

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

以任意顺序返回这两个数字均可。

示例 1:

输入: [1]
输出: [2,3]

示例 2:

输入: [2,3]
输出: [1,4]

提示:

  • nums.length <= 30000
class Solution {
public:
	vector<int> missingTwo(vector<int>& nums) {
		int s1, s2;
		long long s = 0;
		for (int i = 0; i < nums.size(); i++)s ^= nums[i];
		for (int i = 1; i <= nums.size() + 2; i++)s ^= i;
		s = s & -s;
		s1 = 0, s2 = 0;
		for (int i = 0; i < nums.size(); i++)
		{
			if (s&nums[i])s1 ^= nums[i];
			else s2 ^= nums[i];
		}
		for (int i = 1; i <= nums.size() + 2; i++) {
			if (s&i)s1 ^= i;
			else s2 ^= i;
		}
		return vector<int>{s1, s2};
	}
};

二,数组和集合的搜索else

力扣 35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排列数组
  • -104 <= target <= 104
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        return lower_bound(nums.begin(),nums.end(),target)-nums.begin();
    }
};

力扣 805. 数组的均值分割

给定你一个整数数组 nums

我们要将 nums 数组中的每个元素移动到 A 数组 或者 B 数组中,使得 A 数组和 B 数组不为空,并且 average(A) == average(B) 。

如果可以完成则返回true , 否则返回 false  。

注意:对于数组 arr ,  average(arr) 是 arr 的所有元素除以 arr 长度的和。

示例 1:

输入: nums = [1,2,3,4,5,6,7,8]
输出: true
解释: 我们可以将数组分割为 [1,4,5,8] 和 [2,3,6,7], 他们的平均值都是4.5。

示例 2:

输入: nums = [3,1]
输出: false

提示:

  • 1 <= nums.length <= 30
  • 0 <= nums[i] <= 104

class Solution {
public:
	bool splitArraySameAverage(vector<int>& nums) {
		if (nums.size() < 2)return false;
		double s = GetVecSum(nums);
		vector<int>v(nums.size());
		for (int i = 0; i < nums.size(); i++)v[i] = nums[i]* nums.size() - s;
		vector<int>vs(1);
		vs[0] = 0;
		for (int i = 0; i < v.size() / 2; i++) {
			int len = vs.size();
			for (int j = 0; j < len; j++)vs.push_back(vs[j] + v[i]);
		}
		vector<int>vs2(1);
		vs2[0] = 0;
		for (int i = v.size() / 2; i < v.size(); i++) {
			int len = vs2.size();
			for (int j = 0; j < len; j++)vs2.push_back(vs2[j] + v[i]);
		}
		map<int, int>m;
		for (int i = 1; i < vs.size();i++)m[vs[i]] = 1;
		for (int i = 1; i < vs2.size() - 1; i++)if (vs2[i] == 0 || m[-vs2[i]])return true;
		return m[0]|| vs2.back() == 0;
	}
};

力扣 1658. 将 x 减到 0 的最小操作数

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

示例 1:

输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
示例 2:

输入:nums = [5,6,7,8,9], x = 4
输出:-1
示例 3:

输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
 

提示:

1 <= nums.length <= 105
1 <= nums[i] <= 104
1 <= x <= 109

class Solution {
public:
	int minOperations(vector<int>& nums, int x) {
		int s = 0;
		map<int, int>ms;
		for (int i = 0; i < nums.size(); i++)s += nums[i], ms[s] = i;
		x = s - x, s = 0;
		int ans = -1;
		if (x == 0)ans = 0;
		if (x == nums[0])ans = 1;
		if (ms[x] > 0)ans = ms[x]+1;
		for (int i = 0; i < nums.size(); i++) {
			s += nums[i];
			if (ms[s + x] > i)ans = max(ans, ms[s + x] - i);
		}
		if (ans == -1)return -1;
		return nums.size() - ans;
	}
};

剑指 Offer 03. 数组中重复的数字

找出数组中重复的数字。


在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 
 

限制:

2 <= n <= 100000

第一个代码:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]-i)
            {
                int j=nums[i];
                if(nums[i]==nums[j])return nums[i];
                nums[i]^=nums[j]^=nums[i]^=nums[j];
            }
        }
        return -1;
    }
};

AC了,但是总感觉不太对,然后我自己找出来了反例:

[2, 3, 1, 0,1]

修正代码:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        for(int i=0;i<nums.size();i++)
        {
            while(nums[i]-i)
            {                
                int j=nums[i];
                if(nums[i]==nums[j])return nums[i];
                nums[i]^=nums[j]^=nums[i]^=nums[nums[i]];
            }
        }
        return -1;
    }
};

二,二维表格搜索

力扣 764. 最大加号标志

在一个 n x n 的矩阵 grid 中,除了在数组 mines 中给出的元素为 0,其他每个元素都为 1mines[i] = [xi, yi]表示 grid[xi][yi] == 0

返回  grid 中包含 1 的最大的 轴对齐 加号标志的阶数 。如果未找到加号标志,则返回 0 。

一个 k 阶由 1 组成的 “轴对称”加号标志 具有中心网格 grid[r][c] == 1 ,以及4个从中心向上、向下、向左、向右延伸,长度为 k-1,由 1 组成的臂。注意,只有加号标志的所有网格要求为 1 ,别的网格可能为 0 也可能为 1 。

示例 1:

输入: n = 5, mines = [[4, 2]]
输出: 2
解释: 在上面的网格中,最大加号标志的阶只能是2。一个标志已在图中标出。

示例 2:

输入: n = 1, mines = [[0, 0]]
输出: 0
解释: 没有加号标志,返回 0 。

提示:

  • 1 <= n <= 500
  • 1 <= mines.length <= 5000
  • 0 <= xi, yi < n
  • 每一对 (xi, yi) 都 不重复

思路:2个方向单独算即可,时间复杂度是O(n^2)

class Solution {
public:
	vector<vector<int>> orderOfLargestLine(vector<vector<int>>& m)
	{
		vector<vector<int>>ans;
		vector<int>v(m[0].size());
		for (int i = 0; i < m.size(); i++) {
			int s = 0, e = 0;
			for (int j = 0; j < m[i].size(); j++) {
				if (m[i][j] == 0) {
					v[j] = 0;
					continue;
				}
				int k = j;
				while (k < m[i].size() && m[i][k] == 1)k++;
				k--;
				for (int t = j; t <= k; t++)v[t] = min(t - j, k - t) + 1;
				j = k;
			}
			ans.push_back(v);
		}
        return ans;
	}
	int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
		vector<vector<int>>m(n, vector<int>(n, 1));
		for (auto mi : mines)m[mi[0]][mi[1]] = 0;
		vector<vector<int>> v = orderOfLargestLine(m);
		auto m2 = Foverturn(m);
		vector<vector<int>> v2 = orderOfLargestLine(m2);
		int ans = 0;
		for (int i = 0; i < v.size(); i++)for (int j = 0; j < v[i].size(); j++)ans = max(ans, min(v[i][j], v2[j][i]));
		return ans;
	}
};

力扣 1391. 检查网格中是否存在有效路径

给你一个 m x n 的网格 grid。网格里的每个单元都代表一条街道。grid[i][j] 的街道可以是:

1 表示连接左单元格和右单元格的街道。
2 表示连接上单元格和下单元格的街道。
3 表示连接左单元格和下单元格的街道。
4 表示连接右单元格和下单元格的街道。
5 表示连接左单元格和上单元格的街道。
6 表示连接右单元格和上单元格的街道。


你最开始从左上角的单元格 (0,0) 开始出发,网格中的「有效路径」是指从左上方的单元格 (0,0) 开始、一直到右下方的 (m-1,n-1) 结束的路径。该路径必须只沿着街道走。

注意:你 不能 变更街道。

如果网格中存在有效的路径,则返回 true,否则返回 false 。

示例 1:

输入:grid = [[2,4,3],[6,5,2]]
输出:true
解释:如图所示,你可以从 (0, 0) 开始,访问网格中的所有单元格并到达 (m - 1, n - 1) 。
示例 2:

输入:grid = [[1,2,1],[1,2,1]]
输出:false
解释:如图所示,单元格 (0, 0) 上的街道没有与任何其他单元格上的街道相连,你只会停在 (0, 0) 处。
示例 3:

输入:grid = [[1,1,2]]
输出:false
解释:你会停在 (0, 1),而且无法到达 (0, 2) 。
示例 4:

输入:grid = [[1,1,1,1,1,1,3]]
输出:true
示例 5:

输入:grid = [[2],[2],[2],[2],[2],[2],[6]]
输出:true
 

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
1 <= grid[i][j] <= 6

思路:

如果左上角不是4,那么就只有一条路出来,后面一直是一条路没有分叉。

如果左上角是4,那就是2条独立的无分叉路。

class Solution {
public:
	int id(int x, int y)
	{
		return x * col + y;
	}
	vector<int> getNeighbor4(int k)
	{
		vector<int>ans;
		if (k >= col)ans.push_back(k - col);
		if (k < (row - 1) * col)ans.push_back(k + col);
		if (k % col)ans.push_back(k - 1);
		if (k % col < col - 1)ans.push_back(k + 1);
		return ans;
	}
	bool isConnect(const vector<vector<int>>& grid, int s, int t)
	{
		if (t - s < 0)return isConnect(grid, t, s);
		int xs = grid[s / col][s % col];
		int xt = grid[t / col][t % col];		
		if (t - s == col)return (xs == 2 || xs == 3 || xs == 4) && (xt == 2 || xt == 5 || xt == 6);
		if (t - s == 1)return (xs == 1 || xs == 4 || xs == 6) && (xt == 1 || xt == 3 || xt == 5);
		return false;
	}
	bool hasValidPath2(const vector<vector<int>>& grid) 
	{
		row = grid.size();
		col = grid[0].size();
		map<int, int>m;
		m[0] = 1;
		int k = 0;
		while (m[id(row - 1, col - 1)] == 0) {
			vector<int> v = getNeighbor4(k);
			bool flag = false;
			for (auto vi : v) {
				if (isConnect(grid, k, vi) && m[vi] == 0) {
					m[vi] = 1, k = vi;
					flag = true;
					break;
				}
			}
			if (!flag)return false;
		}
		return true;
	}
	bool hasValidPath(vector<vector<int>>& grid)
	{
		if (hasValidPath2(grid))return true;
		if (grid[0][0] == 4) {
			grid[0][0] = 1;
			return hasValidPath2(grid);
		}
		return false;
	}
	int row;
	int col;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值