给定一个整数数组 arr[],长度为 n,我们需要找出三个元素 a[i] < a[j] < a[k] 且下标 i < j < k。要求时间复杂度为 O(n)。如果存在多个这样的三元组,输出任意一个即可。
示例:
- 输入:
arr[] = [12, 11, 10, 5, 6, 2, 30],输出:5, 6, 30(因为 5 < 6 < 30,且在数组中保持顺序) - 输入:
arr[] = [1, 2, 3, 4],输出:1, 2, 3 - 输入:
arr[] = [4, 3, 2, 1],输出:No such triplet exists
目录
- [朴素方法 - O(n) 时间,O(n) 空间](#朴素方法 - O(n) 时间,O(n) 空间)
- [期望方法 - O(n) 时间,O(1) 空间](#期望方法 - O(n) 时间,O(1) 空间)
朴素方法 - O(n) 时间,O(n) 空间
核心思想:找到一个元素,它在数组左边有比它小的元素,右边有比它大的元素。
- 创建辅助数组
smaller[0..n-1],smaller[i]存储左侧比arr[i]小的元素的索引,如果没有则为 -1。 - 创建辅助数组
greater[0..n-1],greater[i]存储右侧比arr[i]大的元素的索引,如果没有则为 -1。 - 最后遍历
smaller[]和greater[],找到索引i使得smaller[i] != -1且greater[i] != -1。
C++ 实现
cpp
#include <iostream>
#include <vector>
using namespace std;
vector<int> find3Numbers(vector<int> &arr) {
int n = arr.size();
vector<int> smaller(n, -1);
int min = 0;
for (int i = 1; i < n; i++) {
if (arr[i] <= arr[min])
min = i;
else
smaller[i] = min;
}
vector<int> greater(n, -1);
int max = n - 1;
for (int i = n - 2; i >= 0; i--) {
if (arr[i] >= arr[max])
max = i;
else
greater[i] = max;
}
for (int i = 0; i < n; i++) {
if (smaller[i] != -1 && greater[i] != -1)
return {arr[smaller[i]], arr[i], arr[greater[i]]};
}
return {};
}
int main() {
vector<int> arr = {12, 11, 10, 5, 6, 2, 30};
vector<int> res = find3Numbers(arr);
for (int x : res)
cout << x << " ";
return 0;
}
Java 实现
java
import java.util.*;
class GfG {
static List<Integer> find3Numbers(int[] arr) {
int n = arr.length;
int[] smaller = new int[n];
Arrays.fill(smaller, -1);
int min = 0;
for (int i = 1; i < n; i++) {
if (arr[i] <= arr[min])
min = i;
else
smaller[i] = min;
}
int[] greater = new int[n];
Arrays.fill(greater, -1);
int max = n - 1;
for (int i = n - 2; i >= 0; i--) {
if (arr[i] >= arr[max])
max = i;
else
greater[i] = max;
}
for (int i = 0; i < n; i++) {
if (smaller[i] != -1 && greater[i] != -1)
return Arrays.asList(arr[smaller[i]], arr[i], arr[greater[i]]);
}
return new ArrayList<>();
}
public static void main(String[] args) {
int[] arr = {12, 11, 10, 5, 6, 2, 30};
List<Integer> res = find3Numbers(arr);
for (int x : res)
System.out.print(x + " ");
}
}
Python 实现
python
def find3Numbers(arr):
n = len(arr)
smaller = [-1] * n
min_idx = 0
for i in range(1, n):
if arr[i] <= arr[min_idx]:
min_idx = i
else:
smaller[i] = min_idx
greater = [-1] * n
max_idx = n - 1
for i in range(n - 2, -1, -1):
if arr[i] >= arr[max_idx]:
max_idx = i
else:
greater[i] = max_idx
for i in range(n):
if smaller[i] != -1 and greater[i] != -1:
return [arr[smaller[i]], arr[i], arr[greater[i]]]
return []
arr = [12, 11, 10, 5, 6, 2, 30]
res = find3Numbers(arr)
for x in res:
print(x, end=" ")
C# 实现
csharp
using System;
using System.Collections.Generic;
class GfG {
static List<int> find3Numbers(int[] arr) {
int n = arr.Length;
int[] smaller = new int[n];
for (int i = 0; i < n; i++) smaller[i] = -1;
int min = 0;
for (int i = 1; i < n; i++) {
if (arr[i] <= arr[min])
min = i;
else
smaller[i] = min;
}
int[] greater = new int[n];
for (int i = 0; i < n; i++) greater[i] = -1;
int max = n - 1;
for (int i = n - 2; i >= 0; i--) {
if (arr[i] >= arr[max])
max = i;
else
greater[i] = max;
}
for (int i = 0; i < n; i++) {
if (smaller[i] != -1 && greater[i] != -1)
return new List<int> { arr[smaller[i]], arr[i], arr[greater[i]] };
}
return new List<int>();
}
static void Main() {
int[] arr = {12, 11, 10, 5, 6, 2, 30};
List<int> res = find3Numbers(arr);
foreach (int x in res)
Console.Write(x + " ");
}
}
说到数组的递增三元组,刚刷完这道题是不是觉得O(n)的指针技巧很巧妙?但光靠脑补指针移动,很多同学面试时还是容易卡住。这里安利一个宝藏网站------图码,它提供了60多种数据结构和算法的交互式动画可视化。
你可以直接把这道题的代码贴进去,或者自定义数组数据,看指针如何一步步移动。对于正在准备408考研 或数据结构期末考试 的同学,它还能上传C/C++/Java/Python代码进行可视化解析,甚至7×24小时选中代码就能问AI。
想彻底吃透这类面试必考题?赶紧去图码体验一下,让抽象的逻辑动起来。
图码-数据结构与算法交互式可视化平台
访问网站:https://totuma.cn
JavaScript 实现
javascript
function find3Numbers(arr) {
const n = arr.length;
const smaller = new Array(n).fill(-1);
let min = 0;
for (let i = 1; i < n; i++) {
if (arr[i] <= arr[min])
min = i;
else
smaller[i] = min;
}
const greater = new Array(n).fill(-1);
let max = n - 1;
for (let i = n - 2; i >= 0; i--) {
if (arr[i] >= arr[max])
max = i;
else
greater[i] = max;
}
for (let i = 0; i < n; i++) {
if (smaller[i] !== -1 && greater[i] !== -1)
return [arr[smaller[i]], arr[i], arr[greater[i]]];
}
return [];
}
const arr = [12, 11, 10, 5, 6, 2, 30];
const res = find3Numbers(arr);
for (const x of res) {
console.log(x + " ");
}
输出: 5 6 30
期望方法 - O(n) 时间,O(1) 空间
思路:先找到两个元素 arr[i] < arr[j] 且 i < j,然后在后续遍历中找 arr[k] > arr[j]。我们可以在一次遍历中完成:
- 维护
first和second,分别表示当前遇到的最小元素和次小元素(但大于first)。 - 同时用
prevFirst记录first更新前的值,以便在找到third时正确输出。 - 当遇到一个元素大于
second时,就找到了三元组{prevFirst, second, x}。
C++ 实现
cpp
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
vector<int> find3Numbers(vector<int> &arr) {
int n = arr.size();
if (n < 3) return {};
int first = INT_MAX, second = INT_MAX, prevFirst = INT_MAX;
for (int i = 0; i < n; i++) {
int x = arr[i];
if (x <= first) {
first = x;
} else if (x <= second) {
second = x;
prevFirst = first;
} else {
return {prevFirst, second, x};
}
}
return {};
}
int main() {
vector<int> arr = {12, 11, 10, 5, 6, 2, 30};
vector<int> res = find3Numbers(arr);
for (int x : res)
cout << x << " ";
return 0;
}
Java 实现
java
import java.util.*;
class GfG {
static List<Integer> find3Numbers(int[] arr) {
int n = arr.length;
if (n < 3) return new ArrayList<>();
int first = Integer.MAX_VALUE;
int second = Integer.MAX_VALUE;
int prevFirst = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
int x = arr[i];
if (x <= first) {
first = x;
} else if (x <= second) {
second = x;
prevFirst = first;
} else {
return Arrays.asList(prevFirst, second, x);
}
}
return new ArrayList<>();
}
public static void main(String[] args) {
int[] arr = {12, 11, 10, 5, 6, 2, 30};
List<Integer> res = find3Numbers(arr);
for (int x : res)
System.out.print(x + " ");
}
}
Python 实现
python
def find3Numbers(arr):
n = len(arr)
if n < 3:
return []
first = float('inf')
second = float('inf')
prevFirst = float('inf')
for x in arr:
if x <= first:
first = x
elif x <= second:
second = x
prevFirst = first
else:
return [prevFirst, second, x]
return []
arr = [12, 11, 10, 5, 6, 2, 30]
res = find3Numbers(arr)
for x in res:
print(x, end=" ")
C# 实现
csharp
using System;
using System.Collections.Generic;
class GfG {
static List<int> find3Numbers(int[] arr) {
int n = arr.Length;
if (n < 3) return new List<int>();
int first = int.MaxValue;
int second = int.MaxValue;
int prevFirst = int.MaxValue;
for (int i = 0; i < n; i++) {
int x = arr[i];
if (x <= first) {
first = x;
} else if (x <= second) {
second = x;
prevFirst = first;
} else {
return new List<int> { prevFirst, second, x };
}
}
return new List<int>();
}
static void Main() {
int[] arr = {12, 11, 10, 5, 6, 2, 30};
List<int> res = find3Numbers(arr);
foreach (int x in res)
Console.Write(x + " ");
}
}
JavaScript 实现
javascript
function find3Numbers(arr) {
const n = arr.length;
if (n < 3) return [];
let first = Number.MAX_VALUE;
let second = Number.MAX_VALUE;
let prevFirst = Number.MAX_VALUE;
for (let i = 0; i < n; i++) {
let x = arr[i];
if (x <= first) {
first = x;
} else if (x <= second) {
second = x;
prevFirst = first;
} else {
return [prevFirst, second, x];
}
}
return [];
}
const arr = [12, 11, 10, 5, 6, 2, 30];
const res = find3Numbers(arr);
console.log(res.join(" "));
输出: 5 6 30
总结
- 朴素方法:使用两个辅助数组,时间 O(n),空间 O(n)。
- 期望方法:一次遍历,维护最小和次小,时间 O(n),空间 O(1)。
两种方法都能高效解决问题,但期望方法更优,适合面试中展示优化能力。
希望这篇博客能帮你掌握递增三元组的查找技巧!如果你有其他解法,欢迎留言讨论~ 🚀