普通视图

发现新文章,点击刷新页面。
昨天以前首页

边反转的最小路径总成本

2026年1月23日 10:56

方法一:Dijkstra

思路与算法

$\textit{Dijkstra}$」是一种常用的求解最短路径的算法。在本题中,我们需要求解从 $0$ 到 $n - 1$ 的最短路,特殊之处在于每个节点有一次反转其相邻边的机会。在 $\textit{Dijkstra}$ 算法中,每个点最多只会被遍历一次,因此我们不需要考虑题目中的特殊条件:

  1. 每个节点都有「最多可使用一次」的开关
  2. 反转仅对那一次移动有效

具体的,我们将每条边 $[x, y, w]$ 的反向边 $[y, x, 2w]$ 加入图中,然后使用 $\textit{Dijkstra}$ 算法求解 $0$ 到 $n - 1$ 的最短路即可。

需要注意的是,$\textit{Dijkstra}$ 可以使用最小堆来进行优化,每次从堆中获取当前未到达的点集合中距离最小的点,然后根据该点去松弛其余未到达点的距离。整个过程每个点可能进堆多次,但只有第一次出堆需要处理,这样可以保证复杂度是 $O(m\log m)$,其中 $n$ 是点的个数,$m$ 是边的个数。

代码

###C++

class Solution {
    using PII = pair<int, int>;
public:
    int minCost(int n, vector<vector<int>>& edges) {
        vector<vector<PII>> g(n);
        for (auto &e : edges) {
            int x = e[0], y = e[1], w = e[2];
            g[x].emplace_back(y, w);
            g[y].emplace_back(x, 2 * w);
        }

        vector<int> d(n, INT_MAX);
        vector<bool> v(n, false);
        priority_queue<PII, vector<PII>, greater<PII>> q;
        d[0] = 0;
        q.emplace(0, 0);

        while (!q.empty()) {
            int x = q.top().second;
            q.pop();
            if (x == n - 1) {
                return d[x];
            }
            // 只有第一次出堆需要去松弛其他点
            if (v[x]) {
                continue;
            }
            v[x] = 1;

            for (auto &[y, w] : g[x]) {
                if (d[x] + w < d[y]) {
                    d[y] = d[x] + w;
                    q.emplace(d[y], y);
                }
            }
        }
        return -1;
    }
};

###Python

class Solution:
    def minCost(self, n: int, edges: List[List[int]]) -> int:
        g = [[] for _ in range(n)]
        for x, y, w in edges:
            g[x].append((y, w))
            g[y].append((x, 2 * w))
        
        dist = [inf] * n
        visited = [False] * n
        dist[0] = 0
        heap = [(0, 0)]  # (距离, 节点)
        
        while heap:
            cur_dist, x = heapq.heappop(heap)
            
            if x == n - 1:
                return cur_dist
            
            # 已经处理过
            if visited[x]:
                continue
            visited[x] = True
            
            # 松弛邻居
            for y, w in g[x]:
                new_dist = cur_dist + w
                if new_dist < dist[y]:
                    dist[y] = new_dist
                    heapq.heappush(heap, (new_dist, y))
        
        return -1

###Rust

use std::collections::BinaryHeap;

impl Solution {
    pub fn min_cost(n: i32, edges: Vec<Vec<i32>>) -> i32 {
        let n = n as usize;
        let mut g = vec![vec![]; n];
        for e in edges {
            let (x, y, w) = (e[0] as usize, e[1] as usize, e[2]);
            g[x].push((y as i32, w));
            g[y].push((x as i32, 2 * w));
        }
        
        let mut dist = vec![i32::MAX; n];
        let mut visited = vec![false; n];
        let mut heap = BinaryHeap::new();  // 最大堆,但存负值
        
        dist[0] = 0;
        heap.push((0, 0));  // (-距离, 节点)
        
        while let Some((neg_d, node)) = heap.pop() {
            let d = -neg_d;
            let node = node as usize;
            
            if node == n - 1 {
                return d;
            }
            
            if visited[node] {
                continue;
            }
            visited[node] = true;
            
            for &(next, weight) in &g[node] {
                let next_idx = next as usize;
                let new_dist = d + weight;
                if new_dist < dist[next_idx] {
                    dist[next_idx] = new_dist;
                    heap.push((-new_dist, next));
                }
            }
        }
        
        -1
    }
}

###Java

class Solution {
    public int minCost(int n, int[][] edges) {
        List<int[]>[] g = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            g[i] = new ArrayList<>();
        }
        
        for (int[] e : edges) {
            int x = e[0], y = e[1], w = e[2];
            g[x].add(new int[]{y, w});
            g[y].add(new int[]{x, 2 * w});
        }
        
        // Dijkstra算法
        int[] d = new int[n];
        boolean[] visited = new boolean[n];
        Arrays.fill(d, Integer.MAX_VALUE);
        d[0] = 0;
        
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        pq.offer(new int[]{0, 0}); // [距离, 节点]
        
        while (!pq.isEmpty()) {
            int[] current = pq.poll();
            int dist = current[0];
            int x = current[1];
            
            if (x == n - 1) {
                return dist;
            }
            
            if (visited[x]) {
                continue;
            }
            visited[x] = true;
            
            for (int[] neighbor : g[x]) {
                int y = neighbor[0];
                int w = neighbor[1];
                
                if (dist + w < d[y]) {
                    d[y] = dist + w;
                    pq.offer(new int[]{d[y], y});
                }
            }
        }
        
        return -1;
    }
}

###C#

using System;
using System.Collections.Generic;

public class Solution {
    public int MinCost(int n, int[][] edges) {
        var g = new List<(int node, int weight)>[n];
        for (int i = 0; i < n; i++) {
            g[i] = new List<(int, int)>();
        }
        
        foreach (var e in edges) {
            int x = e[0], y = e[1], w = e[2];
            g[x].Add((y, w));
            g[y].Add((x, 2 * w));
        }
        
        int[] dist = new int[n];
        bool[] visited = new bool[n];
        Array.Fill(dist, int.MaxValue);
        dist[0] = 0;
        
        var pq = new PriorityQueue<(int dist, int node), int>();
        pq.Enqueue((0, 0), 0);
        
        while (pq.Count > 0) {
            var current = pq.Dequeue();
            int currentDist = current.dist;
            int x = current.node;
            
            if (x == n - 1) {
                return currentDist;
            }
            
            if (visited[x]) {
                continue;
            }
            visited[x] = true;
            foreach (var neighbor in g[x]) {
                int y = neighbor.node;
                int w = neighbor.weight;
                
                if (currentDist + w < dist[y]) {
                    dist[y] = currentDist + w;
                    pq.Enqueue((dist[y], y), dist[y]);
                }
            }
        }
        
        return -1;
    }
}

###Go

func minCost(n int, edges [][]int) int {
    g := make([][][2]int, n)
    for _, e := range edges {
        x, y, w := e[0], e[1], e[2]
        g[x] = append(g[x], [2]int{y, w})
        g[y] = append(g[y], [2]int{x, 2 * w})
    }
    
    // Dijkstra算法
    dist := make([]int, n)
    visited := make([]bool, n)
    for i := range dist {
        dist[i] = math.MaxInt32
    }
    dist[0] = 0
    
    pq := make(PriorityQueue, 0)
    heap.Init(&pq)
    heap.Push(&pq, &Item{node: 0, distance: 0})
    
    for pq.Len() > 0 {
        current := heap.Pop(&pq).(*Item)
        x := current.node
        currentDist := current.distance
        if x == n-1 {
            return currentDist
        }
        
        if visited[x] {
            continue
        }
        visited[x] = true
        
        for _, neighbor := range g[x] {
            y := neighbor[0]
            w := neighbor[1]
            
            if currentDist + w < dist[y] {
                dist[y] = currentDist + w
                heap.Push(&pq, &Item{node: y, distance: dist[y]})
            }
        }
    }
    
    return -1
}

type Item struct {
node     int
distance int
index    int
}

type PriorityQueue []*Item

func (pq PriorityQueue) Len() int           { 
    return len(pq) 
}

func (pq PriorityQueue) Less(i, j int) bool { 
    return pq[i].distance < pq[j].distance 
}

func (pq PriorityQueue) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
    pq[i].index = i
    pq[j].index = j
}

func (pq *PriorityQueue) Push(x interface{}) {
    n := len(*pq)
    item := x.(*Item)
    item.index = n
    *pq = append(*pq, item)
}

func (pq *PriorityQueue) Pop() interface{} {
    old := *pq
    n := len(old)
    item := old[n-1]
    item.index = -1
    *pq = old[0 : n-1]
    return item
}

###C

#define MIN_QUEUE_SIZE 64

typedef struct Element {
    int data[2];
} Element;

typedef bool (*compare)(const void *, const void *);

typedef struct PriorityQueue {
    Element *arr;
    int capacity;
    int queueSize;
    compare lessFunc;
} PriorityQueue;

Element *createElement(int x, int y) {
    Element *obj = (Element *)malloc(sizeof(Element));
    obj->data[0] = x;
    obj->data[1] = y;
    return obj;
}

static bool less(const void *a, const void *b) {
    Element *e1 = (Element *)a;
    Element *e2 = (Element *)b;
    return e1->data[0] > e2->data[0];
}

static bool greater(const void *a, const void *b) {
    Element *e1 = (Element *)a;
    Element *e2 = (Element *)b;
    return e1->data[0] < e2->data[0];
}

static void memswap(void *m1, void *m2, size_t size){
    unsigned char *a = (unsigned char*)m1;
    unsigned char *b = (unsigned char*)m2;
    while (size--) {
        *b ^= *a ^= *b ^= *a;
        a++;
        b++;
    }
}

static void swap(Element *arr, int i, int j) {
    memswap(&arr[i], &arr[j], sizeof(Element));
}

static void down(Element *arr, int size, int i, compare cmpFunc) {
    for (int k = 2 * i + 1; k < size; k = 2 * k + 1) {
        if (k + 1 < size && cmpFunc(&arr[k], &arr[k + 1])) {
            k++;
        }
        if (cmpFunc(&arr[k], &arr[(k - 1) / 2])) {
            break;
        }
        swap(arr, k, (k - 1) / 2);
    }
}

PriorityQueue *createPriorityQueue(compare cmpFunc) {
    PriorityQueue *obj = (PriorityQueue *)malloc(sizeof(PriorityQueue));
    obj->capacity = MIN_QUEUE_SIZE;
    obj->arr = (Element *)malloc(sizeof(Element) * obj->capacity);
    obj->queueSize = 0;
    obj->lessFunc = cmpFunc;
    return obj;
}

void heapfiy(PriorityQueue *obj) {
    for (int i = obj->queueSize / 2 - 1; i >= 0; i--) {
        down(obj->arr, obj->queueSize, i, obj->lessFunc);
    }
}

void enQueue(PriorityQueue *obj, Element *e) {
    // we need to alloc more space, just twice space size
    if (obj->queueSize == obj->capacity) {
        obj->capacity *= 2;
        obj->arr = realloc(obj->arr, sizeof(Element) * obj->capacity);
    }
    memcpy(&obj->arr[obj->queueSize], e, sizeof(Element));
    for (int i = obj->queueSize; i > 0 && obj->lessFunc(&obj->arr[(i - 1) / 2], &obj->arr[i]); i = (i - 1) / 2) {
        swap(obj->arr, i, (i - 1) / 2);
    }
    obj->queueSize++;
}

Element* deQueue(PriorityQueue *obj) {
    swap(obj->arr, 0, obj->queueSize - 1);
    down(obj->arr, obj->queueSize - 1, 0, obj->lessFunc);
    Element *e =  &obj->arr[obj->queueSize - 1];
    obj->queueSize--;
    return e;
}

bool isEmpty(const PriorityQueue *obj) {
    return obj->queueSize == 0;
}

Element* front(const PriorityQueue *obj) {
    if (obj->queueSize == 0) {
        return NULL;
    } else {
        return &obj->arr[0];
    }
}

void clear(PriorityQueue *obj) {
    obj->queueSize = 0;
}

int size(const PriorityQueue *obj) {
    return obj->queueSize;
}

void freeQueue(PriorityQueue *obj) {
    free(obj->arr);
    free(obj);
}

typedef struct AdjNode {
    int vertex; 
    int weight;
    struct AdjNode *next;
} AdjNode;

typedef struct {
    AdjNode **lists; 
    int n;            
} Graph;

AdjNode* createAdjNode(int vertex, int weight) {
    AdjNode *newNode = (AdjNode*)malloc(sizeof(AdjNode));
    newNode->vertex = vertex;
    newNode->weight = weight;
    newNode->next = NULL;
    return newNode;
}

Graph* createGraph(int n) {
    Graph *graph = (Graph*)malloc(sizeof(Graph));
    graph->n = n;
    graph->lists = (AdjNode**)malloc(n * sizeof(AdjNode*));
    for (int i = 0; i < n; i++) {
        graph->lists[i] = NULL;
    }
    
    return graph;
}

void addEdge(Graph *graph, int src, int dest, int weight) {
    AdjNode *newNode = createAdjNode(dest, weight);
    newNode->next = graph->lists[src];
    graph->lists[src] = newNode;
    
    AdjNode *reverseNode = createAdjNode(src, 2 * weight);
    reverseNode->next = graph->lists[dest];
    graph->lists[dest] = reverseNode;
}

void freeGraph(Graph *graph) {
    if (!graph) return;
    
    for (int i = 0; i < graph->n; i++) {
        AdjNode *current = graph->lists[i];
        while (current) {
            AdjNode *temp = current;
            current = current->next;
            free(temp);
        }
    }
    
    free(graph->lists);
    free(graph);
}

int minCost(int n, int** edges, int edgesSize, int* edgesColSize) {
    Graph *graph = createGraph(n);
    for (int i = 0; i < edgesSize; i++) {
        int src = edges[i][0];
        int dest = edges[i][1];
        int weight = edges[i][2];
        addEdge(graph, src, dest, weight);
    }
    
    int *dist = (int *)malloc(n * sizeof(int));
    bool *visited = (bool *)calloc(n, sizeof(bool));
    for (int i = 0; i < n; i++) {
        dist[i] = INT_MAX;
    }
    dist[0] = 0;
    PriorityQueue *pq = createPriorityQueue(less);
    
    Element startElem;
    startElem.data[0] = 0;
    startElem.data[1] = 0;
    enQueue(pq, &startElem);
    
    while (!isEmpty(pq)) {
        Element *current = front(pq);
        int currentDist = current->data[0];
        int x = current->data[1];
        deQueue(pq);

        if (x == n - 1) {
            int result = currentDist;
            free(dist);
            free(visited);
            freeQueue(pq);
            freeGraph(graph);
            return result;
        }
        if (visited[x]) {
            continue;
        }

        visited[x] = true;
        for (AdjNode *neighbor = graph->lists[x]; neighbor != NULL; neighbor = neighbor->next) {
            int y = neighbor->vertex;
            int w = neighbor->weight;
            if (currentDist + w < dist[y]) {
                dist[y] = currentDist + w;
                
                Element newElem;
                newElem.data[0] = dist[y];
                newElem.data[1] = y;
                enQueue(pq, &newElem);
            }
        }
    }
    
    free(dist);
    free(visited);
    freeQueue(pq);
    freeGraph(graph);
    
    return -1;
}

###JavaScript

var minCost = function(n, edges) {
    const g = Array.from({ length: n }, () => []);
    for (const e of edges) {
        const [x, y, w] = e;
        g[x].push([y, w]);
        g[y].push([x, 2 * w]);
    }
    
    const dist = Array(n).fill(Infinity);
    const visited = Array(n).fill(false);
    dist[0] = 0;
    const pq = new PriorityQueue((a, b) => {
        return a[0] < b[0] ? -1 : 1;
    });
    pq.enqueue([0, 0]);
    
    while (!pq.isEmpty()) {
        const [currentDist, x] = pq.dequeue();
        if (x === n - 1) {
            return currentDist;
        }
        
        if (visited[x]) {
            continue;
        }
        visited[x] = true;
        
        for (const [y, w] of g[x]) {
            if (currentDist + w < dist[y]) {
                dist[y] = currentDist + w;
                pq.enqueue([dist[y], y]);
            }
        }
    }
    
    return -1;
};

###TypeScript

function minCost(n: number, edges: number[][]): number {
    const g: [number, number][][] = Array.from({ length: n }, () => []);
    for (const e of edges) {
        const [x, y, w] = e;
        g[x].push([y, w]);
        g[y].push([x, 2 * w]);
    }
    
    const dist: number[] = Array(n).fill(Infinity);
    const visited: boolean[] = Array(n).fill(false);
    dist[0] = 0;
    const pq = new PriorityQueue<[number, number]>((a, b) => {
        return a[0] < b[0] ? -1 : 1;
    });
    pq.enqueue([0, 0]);
    
    while (!pq.isEmpty()) {
        const [currentDist, x] = pq.dequeue()!;
        if (x === n - 1) {
            return currentDist;
        }
        if (visited[x]) {
            continue;
        }
        visited[x] = true;
        
        for (const [y, w] of g[x]) {
            if (currentDist + w < dist[y]) {
                dist[y] = currentDist + w;
                pq.enqueue([dist[y], y]);
            }
        }
    }
    
    return -1;
}

复杂度分析

  • 时间复杂度:$O(n + m\log m)$,其中 $m$ 是边的数量,$n$ 是点的数量。
  • 空间复杂度:$O(n + m)$。

最小绝对差

2022年7月3日 09:07

方法一:排序 + 一次遍历

思路与算法

首先我们对数组 $\textit{arr}$ 进行升序排序。这样一来,拥有「最小绝对差」的元素对只能由有序数组中相邻的两个元素构成。

随后我们对数组 $\textit{arr}$ 进行一次遍历。当遍历到 $\textit{arr}[i]$ 和 $\textit{arr}[i+1]$ 时,它们的差为 $\delta = \textit{arr}[i+1] - \textit{arr}[i]$。我们使用一个变量 $\textit{best}$ 存储当前遇到的最小差,以及一个数组 $\textit{ans}$ 存储答案:

  • 如果 $\delta < \textit{best}$,那么说明我们遇到了更小的差值,需要更新 $\textit{best}$,同时 $\textit{ans}$ 清空并放入 $\textit{arr}[i]$ 和 $\textit{arr}[i+1]$;

  • 如果 $\delta = \textit{best}$,那么我们只需要将 $\textit{arr}[i]$ 和 $\textit{arr}[i+1]$ 放入答案数组即可。

代码

###C++

class Solution {
public:
    vector<vector<int>> minimumAbsDifference(vector<int>& arr) {
        int n = arr.size();
        sort(arr.begin(), arr.end());

        int best = INT_MAX;
        vector<vector<int>> ans;
        for (int i = 0; i < n - 1; ++i) {
            if (int delta = arr[i + 1] - arr[i]; delta < best) {
                best = delta;
                ans = {{arr[i], arr[i + 1]}};
            }
            else if (delta == best) {
                ans.emplace_back(initializer_list<int>{arr[i], arr[i + 1]});
            }
        }

        return ans;
    }
};

###Java

class Solution {
    public List<List<Integer>> minimumAbsDifference(int[] arr) {
        int n = arr.length;
        Arrays.sort(arr);

        int best = Integer.MAX_VALUE;
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        for (int i = 0; i < n - 1; ++i) {
            int delta = arr[i + 1] - arr[i];
            if (delta < best) {
                best = delta;
                ans.clear();
                List<Integer> pair = new ArrayList<Integer>();
                pair.add(arr[i]);
                pair.add(arr[i + 1]);
                ans.add(pair);
            } else if (delta == best) {
                List<Integer> pair = new ArrayList<Integer>();
                pair.add(arr[i]);
                pair.add(arr[i + 1]);
                ans.add(pair);
            }
        }

        return ans;
    }
}

###C#

public class Solution {
    public IList<IList<int>> MinimumAbsDifference(int[] arr) {
        int n = arr.Length;
        Array.Sort(arr);

        int best = int.MaxValue;
        IList<IList<int>> ans = new List<IList<int>>();
        for (int i = 0; i < n - 1; ++i) {
            int delta = arr[i + 1] - arr[i];
            if (delta < best) {
                best = delta;
                ans.Clear();
                IList<int> pair = new List<int>();
                pair.Add(arr[i]);
                pair.Add(arr[i + 1]);
                ans.Add(pair);
            } else if (delta == best) {
                IList<int> pair = new List<int>();
                pair.Add(arr[i]);
                pair.Add(arr[i + 1]);
                ans.Add(pair);
            }
        }

        return ans;
    }
}

###Python

class Solution:
    def minimumAbsDifference(self, arr: List[int]) -> List[List[int]]:
        n = len(arr)
        arr.sort()

        best, ans = float('inf'), list()
        for i in range(n - 1):
            if (delta := arr[i + 1] - arr[i]) < best:
                best = delta
                ans = [[arr[i], arr[i + 1]]]
            elif delta == best:
                ans.append([arr[i], arr[i + 1]])
        
        return ans

###C

static inline int cmp(const void *pa, const void *pb) {
    return *(int *)pa - *(int *)pb;
}

int** minimumAbsDifference(int* arr, int arrSize, int* returnSize, int** returnColumnSizes){
    qsort(arr, arrSize, sizeof(int), cmp);
    int best = INT_MAX;
    int **ans = (int **)malloc(sizeof(int *) * (arrSize - 1));
    int pos = 0;
    for (int i = 0; i < arrSize - 1; ++i) {
        int delta = arr[i + 1] - arr[i];
        if (delta < best) {
            best = delta;
            for (int j = 0; j < pos; j++) {
                free(ans[j]);
            }
            pos = 0;
            ans[pos] = (int *)malloc(sizeof(int) * 2);
            memcpy(ans[pos], arr + i, sizeof(int) * 2);
            pos++;
        }
        else if (delta == best) {
            ans[pos] = (int *)malloc(sizeof(int) * 2);
            memcpy(ans[pos], arr + i, sizeof(int) * 2);
            pos++;
        }
    }
    *returnSize = pos;
    *returnColumnSizes = (int *)malloc(sizeof(int) * pos);
    for (int i = 0; i < pos; i++) {
        (*returnColumnSizes)[i] = 2;
    }
    return ans;
}

###JavaScript

var minimumAbsDifference = function(arr) {
    const n = arr.length;
    arr.sort((a, b) => a - b);

    let best = Number.MAX_VALUE;
    let ans = [];
    for (let i = 0; i < n - 1; ++i) {
        let delta = arr[i + 1] - arr[i];
        if (delta < best) {
            best = delta;
            ans = [];
            const pair = [];
            pair.push(arr[i]);
            pair.push(arr[i + 1]);
            ans.push(pair);
        } else if (delta === best) {
            const pair = [];
            pair.push(arr[i]);
            pair.push(arr[i + 1]);
            ans.push(pair);
        }
    }

    return ans;
};

###go

func minimumAbsDifference(arr []int) (ans [][]int) {
    sort.Ints(arr)
    for i, best := 0, math.MaxInt32; i < len(arr)-1; i++ {
        if delta := arr[i+1] - arr[i]; delta < best {
            best = delta
            ans = [][]int{{arr[i], arr[i+1]}}
        } else if delta == best {
            ans = append(ans, []int{arr[i], arr[i+1]})
        }
    }
    return
}

复杂度分析

  • 时间复杂度:$O(n \log n)$,其中 $n$ 是数组 $\textit{arr}$ 的长度。排序需要的时间为 $O(n \log n)$,遍历需要的是时间为 $O(n)$,因此总时间复杂度为 $O(n \log n)$。

  • 空间复杂度:$O(\log n)$,即为排序需要使用的栈空间。这里不计入返回值需要使用的空间。

学生分数的最小差值

2022年2月9日 10:18

方法一:排序

思路与算法

要想最小化选择的 $k$ 名学生中最高分和最低分的差值,我们一定是在排好序后的数组中连续地进行选择。这是因为在选择时,如果跳过了某个下标 $i$,那么在选择完毕后,将其中的最高分替换成 $\textit{nums}[i]$,最高分一定不会变大,与最低分的差值同样也不会变大。因此,一定存在有一种最优的选择方案,是连续选择了有序数组中的 $k$ 个连续的元素。

这样一来,我们首先对数组 $\textit{nums}$ 进行升序排序,随后使用一个大小固定为 $k$ 的滑动窗口在 $\textit{nums}$ 上进行遍历。记滑动窗口的左边界为 $i$,那么右边界即为 $i+k-1$,窗口中的 $k$ 名学生最高分和最低分的差值即为 $\textit{nums}[i+k-1] - \textit{nums}[i]$。

最终的答案即为所有 $\textit{nums}[i+k-1] - \textit{nums}[i]$ 中的最小值。

代码

###C++

class Solution {
public:
    int minimumDifference(vector<int>& nums, int k) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        int ans = INT_MAX;
        for (int i = 0; i + k - 1 < n; ++i) {
            ans = min(ans, nums[i + k - 1] - nums[i]);
        }
        return ans;
    }
};

###Java

class Solution {
    public int minimumDifference(int[] nums, int k) {
        int n = nums.length;
        Arrays.sort(nums);
        int ans = Integer.MAX_VALUE;
        for (int i = 0; i + k - 1 < n; ++i) {
            ans = Math.min(ans, nums[i + k - 1] - nums[i]);
        }
        return ans;
    }
}

###C#

public class Solution {
    public int MinimumDifference(int[] nums, int k) {
        int n = nums.Length;
        Array.Sort(nums);
        int ans = int.MaxValue;
        for (int i = 0; i + k - 1 < n; ++i) {
            ans = Math.Min(ans, nums[i + k - 1] - nums[i]);
        }
        return ans;
    }
}

###Python

class Solution:
    def minimumDifference(self, nums: List[int], k: int) -> int:
        nums.sort()
        return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))

###C

#define MIN(a, b) ((a) < (b) ? (a) : (b))

int cmp(const void * pa, const void *pb) {
    return *(int *)pa - *(int *)pb;
}

int minimumDifference(int* nums, int numsSize, int k){
    qsort(nums, numsSize, sizeof(int), cmp);
    int ans = INT_MAX;
    for (int i = 0; i + k - 1 < numsSize; ++i) {
        ans = MIN(ans, nums[i + k - 1] - nums[i]);
    }
    return ans;
}

###JavaScript

var minimumDifference = function(nums, k) {
    const n = nums.length;
    nums.sort((a, b) => a - b);
    let ans = Number.MAX_SAFE_INTEGER;
    for (let i = 0; i < n - k + 1; i++) {
        ans = Math.min(ans, nums[i + k - 1] - nums[i]);
    }
    return ans;
};

###go

func minimumDifference(nums []int, k int) int {
    sort.Ints(nums)
    ans := math.MaxInt32
    for i, num := range nums[:len(nums)-k+1] {
        ans = min(ans, nums[i+k-1]-num)
    }
    return ans
}

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

复杂度分析

  • 时间复杂度:$O(n \log n)$,其中 $n$ 是数组 $\textit{nums}$ 的长度。排序需要的时间为 $O(n \log n)$,后续遍历需要的时间为 $O(n)$。

  • 空间复杂度:$O(\log n)$,即为排序需要使用的栈空间。

数组中最大数对和的最小值

2021年5月30日 00:16

方法一:排序 + 贪心

提示 $1$

数组内只有两个数的情况是平凡的。我们可以考虑数组中只有四个数 $x_1 \le x_2 \le x_3 \le x_4$ 的情况。此时 $(x_1, x_4), (x_2, x_3)$ 的拆分方法对应的最大数对和一定是最小的。

提示 $1$ 解释

我们可以枚举所有的拆分方法。除了上文的拆分方法外还有两种拆分方法:

  • $(x_1, x_3), (x_2, x_4)$

    此时 $x_2 + x_4 \ge x_1 + x_4$ 且 $x_2 + x_4 \ge x_2 + x_3$。

    那么 $\max(x_1+x_3,x_2+x_4) \ge x_2 + x_4 \ge \max(x_1+x_4,x_2+x_3)$。

  • $(x_1, x_2), (x_3, x_4)$

    同样地,$\max(x_1+x_2,x_3+x_4) \ge x_3 + x_4 \ge \max(x_1+x_4,x_2+x_3)$。

提示 $2$

对于 $n$ 个数($n$ 为偶数)的情况,上述的条件对应的拆分方法,即第 $k$ 大与第 $k$ 小组成的 $n / 2$ 个数对,同样可以使得最大数对和最小。

提示 $2$ 解释

我们可以类似 提示 $1$ 对所有数建立全序关系,即 $x_1 \le \cdots \le x_n$。我们需要证明,任意的拆分方法得到的最大数对和一定大于等于给定的拆分方法得到的最大数对和。

我们可以考虑上述命题的充分条件:假设给定拆分方法中的数对和 $x_k + x_{n+1-k}$ 在 $k = k'$ 时最大,那么对于任意的拆分方法,都存在一组 $u, v$ 使得 $x_u + x_v \ge x_{k'} + x_{n+1-k'}$。

我们可以用反证法证明。

同样,我们假设 $u < v$,那么使得 $x_v \ge x_{n+1-k'}$ 的 $v$ 的取值一共有 $k'$ 种。即闭区间 $[n+1-k',n]$ 中的所有整数。对于这些 $v$ 组成的数对,如果想使得 $x_u + x_v < x_{k'} + x_{n+1-k'}$ 恒成立,必须要 $x_u < x_{k'}$。此时需要有 $k'$ 个不同的 $u$ 的取值,但只有闭区间 $[1,k'-1]$ 中的 $k'-1$ 个整数满足 $x_u < x_{k'}$ 的条件,这就产生了矛盾。

因此,一定存在一组 $u, v$ 使得 $x_u + x_v \ge x_{k'} + x_{n+1-k'}$。

思路与算法

根据 提示 $2$,我们需要将 $\textit{nums}$ 排序。排序后,我们遍历每一个第 $k$ 大与第 $k$ 小组成的数对,计算它们的和,并维护这些和的最大值。同样根据 提示 $2$,遍历完成后求得的最大数对和就是满足要求的最小值。

代码

###C++

class Solution {
public:
    int minPairSum(vector<int>& nums) {
        int n = nums.size();
        int res = 0;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < n / 2; ++i) {
            res = max(res, nums[i] + nums[n - 1 - i]);
        }
        return res;
    }
};

###Java

class Solution {
    public int minPairSum(int[] nums) {
        int n = nums.length;
        int res = 0;
        Arrays.sort(nums);
        for (int i = 0; i < n / 2; ++i) {
            res = Math.max(res, nums[i] + nums[n - 1 - i]);
        }
        return res;
    }
}

###C#

public class Solution {
    public int MinPairSum(int[] nums) {
        int n = nums.Length;
        int res = 0;
        Array.Sort(nums);
        for (int i = 0; i < n / 2; ++i) {
            res = Math.Max(res, nums[i] + nums[n - 1 - i]);
        }
        return res;
    }
}

###Python

class Solution:
    def minPairSum(self, nums: List[int]) -> int:
        n = len(nums)
        res = 0
        nums.sort()
        for i in range(n // 2):
            res = max(res, nums[i] + nums[n-1-i])
        return res

###JavaScript

var minPairSum = function(nums) {
    const n = nums.length;
    let res = 0;
    nums.sort((a, b) => a - b);
    for (let i = 0; i < Math.floor(n / 2); i++) {
        res = Math.max(res, nums[i] + nums[n - 1 - i]);
    }
    return res;
};

###go

func minPairSum(nums []int) (ans int) {
    sort.Ints(nums)
    n := len(nums)
    for i, val := range nums[:n/2] {
        ans = max(ans, val+nums[n-1-i])
    }
    return
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

###C

int cmp(int *a, int *b) {
    return *a - *b;
}

int minPairSum(int *nums, int numsSize) {
    int res = 0;
    qsort(nums, numsSize, sizeof(int), cmp);
    for (int i = 0; i < numsSize / 2; ++i) {
        res = fmax(res, nums[i] + nums[numsSize - 1 - i]);
    }
    return res;
}

复杂度分析

  • 时间复杂度:$O(n\log n)$,其中 $n$ 为数组 $\textit{nums}$ 的长度。排序 $\textit{nums}$ 的时间复杂度为 $O(n\log n)$,遍历维护最大数对和的时间复杂度为 $O(n)$。

  • 空间复杂度:$O(\log n)$,即为排序的栈空间开销。

❌
❌