[特殊字符] 数组中的递增三元组:O(n) 时间高效查找,面试必考!

给定一个整数数组 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) 空间

核心思想:找到一个元素,它在数组左边有比它小的元素,右边有比它大的元素。

  1. 创建辅助数组 smaller[0..n-1]smaller[i] 存储左侧比 arr[i] 小的元素的索引,如果没有则为 -1。
  2. 创建辅助数组 greater[0..n-1]greater[i] 存储右侧比 arr[i] 大的元素的索引,如果没有则为 -1。
  3. 最后遍历 smaller[]greater[],找到索引 i 使得 smaller[i] != -1greater[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]。我们可以在一次遍历中完成:

  • 维护 firstsecond,分别表示当前遇到的最小元素和次小元素(但大于 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)。

两种方法都能高效解决问题,但期望方法更优,适合面试中展示优化能力。

希望这篇博客能帮你掌握递增三元组的查找技巧!如果你有其他解法,欢迎留言讨论~ 🚀

相关推荐
逻辑驱动的ken1 小时前
Java高频面试考点场景题26
java·开发语言·面试·职场和发展·求职招聘
今儿敲了吗1 小时前
链表篇(一)——合并两个有序链表
数据结构·笔记·算法·链表
fie88891 小时前
基于BBO算法的网络负载均衡优化(MATLAB实现)
网络·算法·负载均衡
y = xⁿ1 小时前
20天速通LeetCodeday11:二叉树进阶
数据结构·算法
星辰_mya1 小时前
领域驱动设计(DDD)“老中医”治理订单
java·后端·面试·架构
400分1 小时前
langchain踩坑调用大模型记录-搭建人工智能机器人
算法
张元清1 小时前
React 浏览器标签页 UX:用标题、Favicon 和通知把用户拉回来
前端·javascript·面试
Lkstar1 小时前
读完红宝书和YDKJS,我终于搞懂了原型链、闭包和this
javascript·面试
alphaTao1 小时前
LeetCode 每日一题 2026/5/4-2026/5/10
算法·leetcode·职场和发展