581. Shortest Unsorted Continuous Subarray

本文介绍了一种高效算法,用于找出给定数组中最短的无序连续子数组,并详细解析了该算法的实现原理及步骤。通过一次遍历,结合最大值与最小值跟踪,实现了O(n)的时间复杂度。
Given an integer array, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order, too.

You need to find the shortest such subarray and output its length.

Example 1:

Input: [2, 6, 4, 8, 10, 9, 15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order.

题目分析:

给了我们一个数组,让我们求最短的无序连续子数组,根据题目中的例子也不难分析出来是让我们找出数组中的无序的部分。

这道题非常的好,想了半天 最后又反复读题看到升序才有了思路,先将原数组全部升序后,找出第一个不同于排序后的坐标和找出最后一个不同的坐标,相减就是其中最短子数组的长度了。但是我的方法有一种特殊的情况,就是数组本身就是升序的,所以最后要判断一下。

看最优解中,发现真是太牛了。这种方法很叼啊,是O(n)的时间复杂度加上O(1)的空间复杂度,我觉得这实际上是我的那种方法进行空间上的优化的结果,用两个变量mx和mn来代替上面的有序数组,仔细来分析发现,最小值mn初始化为数组的最后一个数字,最大值mx初始化为了第一个数字,然后我们从第二个数字开始遍历,mx和nums[i]之间取较大值赋值给mx,然后比较此时mx和nums[i]之间的大小关系,如果mx大于nums[i],就把i赋值给end,那么我们想如果第一个数字小于第二个,mx就会赋值为第二个数字,这时候mx和nums[i]就相等了,不进行任何操作,这make sense,因为说明此时是有序的。mn和nums[n-1-i]之间取较小值赋给mn,然后比较此时mn和nums[n-1-i]之间的大小关系,如果mn小于nums[n-1-i],就把n-1-i赋值给start,那么什么时候会进行赋值呢,是当倒数第二个数字大于最后一个数字,这样mn还是最后一个数字,而nums[n-1-i]就会大于mn,这样我们更新start。我们可以看出start是不断往前走的,end是不断往后走的,整个遍历完成后,start和end就分别指向了最短无序连续子数组的起始和结束位置。

class Solution {
    public int findUnsortedSubarray(int[] A) {

      int n = A.length, start= -1, end = -2, mn = A[n-1], mx = A[0];
        for (int i=1;i<n;i++) {
            mx = Math.max(mx, A[i]);
            mn = Math.min(mn, A[n-1-i]);
            if (A[i] < mx) end = i;
            if (A[n-1-i] > mn) start= n-1-i;
        }
        return end - start+ 1;


        //My Personal Idea
       /*int[] n = nums.clone();
       Arrays.sort(n);
       int len = nums.length;
       int start = 0,end = 0;
        for (int i=0,j=len-1;i<len&&j>=0;i++,j--) {
            if(nums[i] != n[i]){
                end = i;
            }
            if(nums[j] != n[j]){
                start = j;
            }
        }
        if (start - end == 0){
            return 0;
        }else{
            return end - start +1;
        }*/

    }
}
<think>我们正在讨论的是networkx中的路径函数nx.shortest_path的用法。根据用户提供的引用内容,我们可以总结如下: 引用[1]指出:shortest_path函数用于寻找路径,shortest_path_length用于求路径长度。函数调用形式为: shortest_path(G, source=None, target=None, weight=None) shortest_path_length(G, source=None, target=None, weight=None) 引用[2]说明:当图中两个节点间存在多条边时,使用nx.shortest_path(G, source, target, weight='length')会根据指定的权重(例如'length')找到源节点和目标节点之间权重小的路径。 引用[3]展示了使用nx.all_pairs_shortest_path_length计算全图路径长度的例子,不过这个函数是计算所有节点对之间的路径长度。 用户现在想了解nx.shortest_path函数的用法解释。 因此,我们需要详细解释nx.shortest_path函数的参数、行为以及使用示例。 函数签名: nx.shortest_path(G, source=None, target=None, weight=None, method='dijkstra') 参数说明: - G:NetworkX图 - source:源节点,如果未指定,则返回所有节点对的路径(以字典形式) - target:目标节点,如果未指定,则返回从源节点到所有其他节点的路径(以字典形式) - weight:边权重的属性名。默认None,表示所有边权重为1(即按边数计算路径)。指定该参数则使用边权重。 - method:算法选择。默认是'dijkstra',对于无权图可以使用'bfs'(广度优先搜索),对于带权图还可以使用'bellman-ford'。注意,如果权重为负,只能使用'bellman-ford'。 返回值: - 如果指定了source和target,则返回从source到target的路径(节点列表)。 - 如果只指定了source,则返回一个字典,键为目标节点,值为从source到该目标节点的路径(节点列表)。 - 如果只指定了target(但通常需要同时指定source和target,或者都不指定,或者只指定source),实际上当target不指定时,返回的是以source为起点的所有路径。如果source和target都不指定,则返回所有节点对的路径(以字典的字典形式:{source: {target: 路径列表}})。 注意:当图中存在多条路径时,该函数默认只返回其中一条(具体哪条取决于算法实现,通常是先找到的一条)。 我们还可以通过示例来展示其用法。 示例1:计算两个节点之间的路径(无权图) ```python import networkx as nx # 创建图 G = nx.Graph() G.add_edges_from([(1,2), (1,3), (2,4), (3,4), (4,5)]) # 计算节点1到节点5的路径 path = nx.shortest_path(G, source=1, target=5) print(path) # 输出: [1, 2, 4, 5] 或 [1,3,4,5] 取决于哪条先被找到(实际上nx.shortest_path在无权图中使用bfs,返回的顺序可能取决于节点邻居的顺序) ``` 示例2:计算两个节点之间的路径(带权图) ```python import networkx as nx # 创建带权图 G = nx.Graph() G.add_weighted_edges_from([(1,2,2.0), (1,3,1.5), (2,4,1.0), (3,4,3.0), (4,5,0.5)]) # 计算节点1到节点5的路径(按权重) path = nx.shortest_path(G, source=1, target=5, weight='weight') print(path) # 输出: [1, 2, 4, 5] 因为这条路径的总权重为2+1+0.5=3.5,而另一条路径1-3-4-5的权重是1.5+3+0.5=5.0,所以选择第一条 ``` 示例3:获取从单个源节点到所有节点的路径 ```python import networkx as nx G = nx.Graph() G.add_edges_from([(1,2), (1,3), (2,4), (4,5)]) # 以节点1为源节点 paths = nx.shortest_path(G, source=1) # paths是一个字典,键是目标节点,值是从1到该节点的路径列表 print(paths) # 输出可能类似于:{1: [1], 2: [1,2], 3: [1,3], 4: [1,2,4], 5: [1,2,4,5]} ``` 示例4:获取所有节点对之间的路径 ```python import networkx as nx G = nx.Graph() G.add_edges_from([(1,2), (1,3), (2,4), (4,5)]) all_pairs = nx.shortest_path(G) # 不指定source和target # all_pairs是一个字典,键是源节点,值是一个字典(键为目标节点,值为路径列表) print(all_pairs[1][5]) # 输出节点1到节点5的路径:[1,2,4,5] ``` 注意事项: 1. 如果图中存在负权边,必须指定method='bellman-ford',并且确保没有负权环(否则会报错)。 2. 如果两个节点之间不可达,则会抛出NetworkXNoPath异常(当指定了source和target时)或者在不指定target时返回的字典中不包含该目标节点(或包含但路径为空?实际上不可达的节点不会出现在返回的字典中,因为根本找不到路径)。 引用[2]中提到了当两个节点间有多条边时,会按照指定的权重属性选择小权重的边。实际上,在NetworkX中,两个节点之间只能有多条边(即MultiGraph)的情况下,shortest_path函数默认会使用任意一条边?但注意,在MultiGraph中,当指定权重时,算法会取两个节点之间所有边中权重小的那条边的权重作为这两个节点之间的边权重。如果不指定权重,则每条边的权重为1,那么两个节点之间无论有几条边,都相当于一条边(因为边数还是1,即按一条边计算)。具体行为可以查看文档,但一般情况下,我们使用简单图(Graph)即可。 根据引用[2]的解释,当指定权重时,它会根据权重找到小权重的路径。在MultiGraph中,两个节点之间的多条边会被视为一条边,其权重为所有边中权重的小值?还是可以累积?实际上,在路径计算中,两个节点之间即使有多条边,我们也只关心小权重的那条边,因为我们要求的是路径上所有边的权重之和小。因此,在MultiGraph中,两个节点之间有多条边时,算法会自动使用权重小的那条边来计算(前提是权重属性存在且指定了weight参数)。 因此,我们可以总结: - 在简单图中,两个节点之间多一条边(无向图)或两条边(有向图,方向相反),所以直接使用。 - 在多重图中,使用路径算法时,两个节点之间的边权重取所有边中权重小的那条(等效于在两个节点间只保留权重小的那条边)。 后,我们列出相关问题。 </think>### `nx.shortest_path` 函数详解 `nx.shortest_path()` 是 NetworkX 中用于计算图中节点间路径的核心函数,支持多种算法和场景。以下基于引用内容详细解释其用法: --- #### 1. **基本语法与参数说明** ```python path = nx.shortest_path(G, source=None, target=None, weight=None, method='dijkstra') ``` 参数解析: - **`G`**:NetworkX 图对象(支持 `Graph`, `DiGraph`, `MultiGraph` 等) - **`source`**:路径起点节点(默认为 `None`,计算全局路径) - **`target`**:路径终点节点(默认为 `None`,计算起点到所有节点的路径) - **`weight`**:边权重属性名(默认为 `None` 表示无权图,使用权重要指定属性名如 `'length'`[^2]) - **`method`**:算法选择(默认 `'dijkstra'`,其他选项包括 `'bellman-ford'`, `'bfs'` 等) 返回值: - 若指定 `source` 和 `target`:返回节点列表表示的路径(如 `[1, 2, 4, 5]`) - 若仅指定 `source`:返回字典 `{target: 路径列表}` - 若未指定起点/终点:返回嵌套字典 `{source: {target: 路径列表}}` --- #### 2. **典型使用场景** ##### (1) 两点间路径(引用[^1]) ```python # 计算节点1到节点5的路径(无权图) path = nx.shortest_path(G, source=1, target=5) # 输出可能为: [1, 2, 4, 5] # 带权图(使用边属性'length'作为权重) weighted_path = nx.shortest_path(G, source=1, target=5, weight='length') ``` ##### (2) 单源路径(起点到所有节点) ```python # 计算节点1到所有可达节点的路径 paths_from_1 = nx.shortest_path(G, source=1) # 输出: {2: [1,2], 3: [1,3], 4: [1,2,4], 5: [1,2,4,5]} ``` ##### (3) 全局路径(引用[^3]) ```python # 计算所有节点对之间的路径 all_pairs_paths = nx.shortest_path(G) # 输出: {1: {2: [1,2], 3: [1,3], ...}, 2: {1: [2,1], 4: [2,4], ...}} ``` --- #### 3. **多重边处理机制(引用[^2])** 当节点间存在多条边时: 1. 若未指定 `weight`,默认选择**边数少**的路径(即少跳数) 2. 若指定 `weight`,选择**权重总和小**的路径 ```python # 图中(2,4)有两条边:权重分别为3和7 nx.shortest_path(G, source=1, target=4, weight='weight') # 自动选择权重更小的路径:可能为[1,2,4],而非[1,5,6,4] ``` --- #### 4. **算法选择指南** | **算法** (`method`) | **适用场景** | **时间复杂度** | |----------------------|----------------------------------|------------------------| | `'dijkstra'` (默认) | 非负权重图 | $O(|E| + |V|\log|V|)$ | | `'bellman-ford'` | 含负权重的图(需无负权环) | $O(|V||E|)$ | | `'bfs'` | 无权图(等效于 Dijkstra 无权特例)| $O(|V|+|E|)$ | --- #### 5. **关联函数对比** | **函数** | **返回内容** | **典型应用** | |-----------------------------------|----------------------------|-------------------------------| | `nx.shortest_path()` | 完整节点路径序列 | 路径可视化/节点遍历 | | `nx.shortest_path_length()`[^1] | 路径长度(跳数或权重和) | 路径成本分析 | | `nx.all_pairs_shortest_path_length()`[^3] | 全局路径长度字典 | 网络平均路径计算 | --- #### 6. **完整示例:带权路径** ```python import networkx as nx # 创建带权图 G = nx.Graph() G.add_weighted_edges_from([(1,2,2), (2,3,1), (5,6,2), (1,5,3), (2,4,7)]) # 计算节点1->4的路径(按权重) path = nx.shortest_path(G, source=1, target=4, weight='weight') print("路径节点序列:", path) # 输出: [1, 2, 4] 或 [1,5,6,4](取决于权重) # 计算路径总权重 path_length = nx.shortest_path_length(G, source=1, target=4, weight='weight') print("路径总权重:", path_length) # 输出: 2+7=9 或 3+2+? ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值