转移的份额
据机构数据显示,今年前六周,苹果公司在中国的 iPhone 销量同比下降 24%,而华为手机销量增加 64%。
华为手机销量主要受 Mate60 影响,可见随着华为的芯片危机逐步缓解,华为会持续侵蚀以苹果为首的高端机器的市场份额。
与去年相比,今年前六周,华为市场份额从 9.4% 增加至 16.5% ,而苹果份额从 19% 下滑至 15.7%。
这还真就是网友喊的:遥遥领先 🤣
...
回归主线。
之前分享过两次「华为 OD」的算法原题,有读者反馈说有点难。
题目变难,其实还是一个候选人供过于求的问题。
这次分享一道难度稍稍合理的「华为 OD」的算法原题。
题目描述
平台:LeetCode
题号:1743
存在一个由 n
个不同元素组成的整数数组 nums
,但你已经记不清具体内容,好在你还记得 nums
中的每一对相邻元素。
给你一个二维整数数组 adjacentPairs
,大小为 n - 1
,其中每个 <math xmlns="http://www.w3.org/1998/Math/MathML"> a d j a c e n t P a i r s [ i ] = [ u i , v i ] adjacentPairs[i] = [u_i, v_i] </math>adjacentPairs[i]=[ui,vi] 表示元素 <math xmlns="http://www.w3.org/1998/Math/MathML"> u i u_i </math>ui 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> v i v_i </math>vi 在 nums
中相邻。
题目数据保证所有由元素 nums[i]
和 nums[i+1]
组成的相邻元素对都存在于 adjacentPairs
中,存在形式可能是 [nums[i], nums[i+1]]
,也可能是 [nums[i+1], nums[i]]
。这些相邻元素对可以「按任意顺序」出现。
返回原始数组 nums
。如果存在多种解答,返回其中任意一个即可。
示例 1:
css
输入:adjacentPairs = [[2,1],[3,4],[3,2]]
输出:[1,2,3,4]
解释:数组的所有相邻元素对都在 adjacentPairs 中。
特别要注意的是,adjacentPairs[i] 只表示两个元素相邻,并不保证其 左-右 顺序。
示例 2:
css
输入:adjacentPairs = [[4,-2],[1,4],[-3,1]]
输出:[-2,4,1,-3]
解释:数组中可能存在负数。
另一种解答是 [-3,1,4,-2] ,也会被视作正确答案。
示例 3:
lua
输入:adjacentPairs = [[100000,-100000]]
输出:[100000,-100000]
提示:
- <math xmlns="http://www.w3.org/1998/Math/MathML"> n u m s . l e n g t h = = n nums.length == n </math>nums.length==n
- <math xmlns="http://www.w3.org/1998/Math/MathML"> a d j a c e n t P a i r s . l e n g t h = = n − 1 adjacentPairs.length == n - 1 </math>adjacentPairs.length==n−1
- <math xmlns="http://www.w3.org/1998/Math/MathML"> a d j a c e n t P a i r s [ i ] . l e n g t h = = 2 adjacentPairs[i].length == 2 </math>adjacentPairs[i].length==2
- <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 < = n < = 1 0 5 2 <= n <= 10^5 </math>2<=n<=105
- <math xmlns="http://www.w3.org/1998/Math/MathML"> − 1 0 5 < = n u m s [ i ] , u i , v i < = 1 0 5 -10^5 <= nums[i], u_i, v_i <= 10^5 </math>−105<=nums[i],ui,vi<=105
- 题目数据保证存在一些以
adjacentPairs
作为元素对的数组
单向构造(哈希表计数)
根据题意,由于所有的相邻关系都会出现在 <math xmlns="http://www.w3.org/1998/Math/MathML"> n u m s nums </math>nums 中,假设其中一个合法数组为 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s ans </math>ans,长度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n。
那么显然 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s [ 0 ] ans[0] </math>ans[0] 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s [ n − 1 ] ans[n - 1] </math>ans[n−1] 在 <math xmlns="http://www.w3.org/1998/Math/MathML"> n u m s nums </math>nums 中只存在一对相邻关系,而其他 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s [ i ] ans[i] </math>ans[i] 则存在两对相邻关系。
因此我们可以使用「哈希表」对 <math xmlns="http://www.w3.org/1998/Math/MathML"> n u m s nums </math>nums 中出现的数值进行计数,找到"出现一次"的数值作为 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s ans </math>ans 数值的首位,然后根据给定的相邻关系进行「单向构造」,为了方便找到某个数其相邻的数是哪些,我们还需要再开一个「哈希表」记录相邻关系。
Java 代码:
Java
class Solution {
public int[] restoreArray(int[][] adjacentPairs) {
int m = adjacentPairs.length, n = m + 1;
Map<Integer, Integer> cnts = new HashMap<>();
Map<Integer, List<Integer>> map = new HashMap<>();
for (int[] ap : adjacentPairs) {
int a = ap[0], b = ap[1];
cnts.put(a, cnts.getOrDefault(a, 0) + 1);
cnts.put(b, cnts.getOrDefault(b, 0) + 1);
List<Integer> alist = map.getOrDefault(a, new ArrayList<>());
alist.add(b);
map.put(a, alist);
List<Integer> blist = map.getOrDefault(b, new ArrayList<>());
blist.add(a);
map.put(b, blist);
}
int start = -1;
for (int i : cnts.keySet()) {
if (cnts.get(i) == 1) {
start = i;
break;
}
}
int[] ans = new int[n];
ans[0] = start;
ans[1] = map.get(start).get(0);
for (int i = 2; i < n; i++) {
int x = ans[i - 1];
List<Integer> list = map.get(x);
for (int j : list) {
if (j != ans[i - 2]) ans[i] = j;
}
}
return ans;
}
}
C++ 代码:
C++
class Solution {
public:
vector<int> restoreArray(vector<vector<int>>& adjacentPairs) {
unordered_map<int, int> cnts;
unordered_map<int, vector<int>> map;
for(auto& pair : adjacentPairs){
int a = pair[0], b = pair[1];
cnts[a]++, cnts[b]++;
map[a].push_back(b);
map[b].push_back(a);
}
int start;
for(auto& i : cnts) {
if(i.second == 1){
start = i.first;
break;
}
}
int n = adjacentPairs.size() + 1;
vector<int> ans(n);
ans[0] = start;
ans[1] = map[start][0];
for(int i = 2; i < n; i++){
int x = ans[i - 1];
for(int j : map[x])
if(j != ans[i-2]) ans[i] = j;
}
return ans;
}
};
Python 代码:
Python
class Solution:
def restoreArray(self, adjacentPairs: List[List[int]]) -> List[int]:
cnts = defaultdict(int)
map = defaultdict(list)
for ap in adjacentPairs:
a, b = ap[0], ap[1]
cnts[a] += 1
cnts[b] += 1
map[a].append(b)
map[b].append(a)
start = next(i for i in cnts if cnts[i] == 1)
n = len(adjacentPairs) + 1
ans = [0] * n
ans[0] = start
ans[1] = map[start][0]
for i in range(2, n):
x = ans[i - 1]
for j in map[x]:
if j != ans[i - 2]:
ans[i] = j
return ans
TypeScript 代码:
TypeScript
function restoreArray(adjacentPairs: number[][]): number[] {
const cnts: {[key: number]: number} = {};
const map: {[key: number]: number[]} = {};
for(let pair of adjacentPairs){
let a: number = pair[0], b: number = pair[1];
cnts[a] = !cnts[a] ? 1 : cnts[a] + 1;
cnts[b] = !cnts[b] ? 1 : cnts[b] + 1;
if(!map[a]) map[a] = [];
if(!map[b]) map[b] = [];
map[a].push(b);
map[b].push(a);
}
let start: number;
for(let key in cnts){
if(cnts[key] == 1){
start = Number(key);
break;
}
}
const n: number = adjacentPairs.length + 1;
const ans: number[] = Array(n).fill(0);
ans[0] = start;
ans[1] = map[start][0];
for(let i = 2; i<n; i++){
let x: number = ans[i-1];
for(let j of map[x]){
if(j !== ans[i-2]) ans[i] = j;
}
}
return ans;
};
- 时间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)
- 空间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)
双向构造(双指针)
在解法一中,我们通过「哈希表」计数得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s ans </math>ans 首位元素作为起点,进行「单向构造」。
那么是否存在使用任意数值作为起点进行的双向构造呢?
答案是显然的,我们可以利用 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s ans </math>ans 的长度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 < = n < = 1 0 5 2 <= n <= 10^5 </math>2<=n<=105,构造一个长度 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 0 6 10^6 </math>106 的数组 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q(这里可以使用 static
进行加速,让多个测试用例共享一个大数组)。
这里 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q 数组不一定要开成 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 e 6 1e6 </math>1e6 大小,只要我们 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q 大小大于 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s ans </math>ans 的两倍,就不会存在越界问题。
从 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q 数组的 中间位置 开始,先随便将其中一个元素添加到中间位置,使用「双指针」分别往「两边拓展」(l
和 r
分别指向左右待插入的位置)。
当 l
指针和 r
指针之间已经有 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n 个数值,说明整个 <math xmlns="http://www.w3.org/1998/Math/MathML"> a n s ans </math>ans 构造完成,我们将 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ l + 1 , r − 1 ] [l + 1, r - 1] </math>[l+1,r−1] 范围内的数值输出作为答案即可。
Java 代码:
Java
class Solution {
static int N = (int)1e6+10;
static int[] q = new int[N];
public int[] restoreArray(int[][] adjacentPairs) {
int m = adjacentPairs.length, n = m + 1;
Map<Integer, List<Integer>> map = new HashMap<>();
for (int[] ap : adjacentPairs) {
int a = ap[0], b = ap[1];
List<Integer> alist = map.getOrDefault(a, new ArrayList<>());
alist.add(b);
map.put(a, alist);
List<Integer> blist = map.getOrDefault(b, new ArrayList<>());
blist.add(a);
map.put(b, blist);
}
int l = N / 2, r = l + 1;
int std = adjacentPairs[0][0];
List<Integer> list = map.get(std);
q[l--] = std;
q[r++] = list.get(0);
if (list.size() > 1) q[l--] = list.get(1);
while ((r - 1) - (l + 1) + 1 < n) {
List<Integer> alist = map.get(q[l + 1]);
int j = l;
for (int i : alist) {
if (i != q[l + 2]) q[j--] = i;
}
l = j;
List<Integer> blist = map.get(q[r - 1]);
j = r;
for (int i : blist) {
if (i != q[r - 2]) q[j++] = i;
}
r = j;
}
int[] ans = new int[n];
for (int i = l + 1, idx = 0; idx < n; i++, idx++) {
ans[idx] = q[i];
}
return ans;
}
}
C++ 代码:
C++
#define N 1000010
class Solution {
public:
vector<int> restoreArray(vector<vector<int>>& adjacentPairs) {
int m = adjacentPairs.size(), n = m + 1;
unordered_map<int, vector<int>> map;
for(auto& pair : adjacentPairs){
int a = pair[0], b = pair[1];
map[a].push_back(b);
map[b].push_back(a);
}
int l = N / 2, r = l + 1;
int s = adjacentPairs[0][0];
vector<int> q(N, 0);
q[l--] = s;
q[r++] = map[s][0];
if (map[s].size() > 1) q[l--] = map[s][1];
while ((r - 1) - (l + 1) + 1 < n){
vector<int> list = map[q[l + 1]];
int j = l;
for(auto& i : list){
if(i != q[l + 2]) q[j--] = i;
}
l = j;
list = map[q[r - 1]];
j = r;
for(auto& i : list){
if(i != q[r - 2]) q[j++] = i;
}
r = j;
}
vector<int> ans(n);
for(int i = l + 1, idx = 0; idx < n; ++i, ++idx){
ans[idx] = q[i];
}
return ans;
}
};
- 时间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)
- 空间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)
我是宫水三叶,每天都会分享算法知识,并和大家聊聊近期的所见所闻。
欢迎关注,明天见。
更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉