zoukankan      html  css  js  c++  java
  • 38. 最长连续有序子数组

    一. 问题

    给定含 n 个整数的数组,找出连续的有序最长子数组。算法的运行时间是多少?

    二. 思路

    假设有一个序列, data = (1, 2, 3, 4, 2, 1, 3, 0),显然,从元素 1 到元素 4 是升序排列的,并且有 4 个元素。我们为了简单起见,仅查找升序排列的子序列。

    要获得子序列的情况,我们需要三个信息:(1)子序列开始下标(2)子序列结尾下标(3)子序列长度。并且还需要三个辅助变量:(1)中间结果子序列开始下标(2)中间结果子序列结束下标(3)中间结果子序列长度。

    我们从首元素开始,依次向后比较,同时用上面提到的辅助变量记住需要的下标,如果满足条件,则一直向后推;如果不满足条件,则将这些下标记录在结果中。再重置辅助变量,直到比较完所有元素。因为有升序这一条件,我们便可以保证操作不用回退,即一路推下去。

    三. 代码实现

     1 vector<int> max_ordered_sub_sequence(const vector<int>& data) {
     2     vector<int> result;
     3     int start_index = 0, end_index = 0, sequence_len = 1;
     4     int temp_len = 1;
     5 
     6     for (int temp_start = 0, temp_end = 1; temp_end < data.size(); ++temp_end) {
     7         int k = temp_end - 1;
     8         if (data[k] <= data[temp_end]) {
     9             ++temp_len;
    10             continue;
    11         }
    12 
    13         if (temp_len > sequence_len) {
    14             sequence_len = temp_len;
    15             start_index = temp_start;
    16             end_index = temp_end -1 ;
    17             temp_start = temp_end;
    18             temp_len = 1;
    19         } else {
    20             temp_start = temp_end;
    21             temp_len = 1;
    22         }
    23     }
    24 
    25     result.push_back(sequence_len);
    26     result.push_back(start_index);
    27     result.push_back(end_index);
    28 
    29     return result;
    30 }

    (1)代码分析

    第 2 到 4 行,我们进行变量初始化。只有一个元素的时候,前后下标一致,此时子序列中含有 1 个元素。第 8 到 11 行,元素如果升序,则向下推。第 13 到 18 行,算法执行到这里,说明元素已经出现了逆序,并且当前子序列比原来的子序列更长,那么我们要把辅助变量记住,然后进行重置。第 19 到 22 行说明,虽然出现了逆序,但是当前子序列的长度,并没有超过我们已经记录的子序列,此时只需要重置辅助变量。第 25 到 27 行,执行到这里,说明算法执行完毕,将结果放到 vector 中返回即可。

    (2)算法正确性证明

    我们一开始考虑,若序列只包含一个元素,那么最长子序列就是它本身。如果序列长度大于 1,那么进行比较。我们记录的下标只有在需要的时候才会进行更新,也就是逆序的时候。而按照升序排列,当前子序列最后一个元素,一定是比该序列前面元素大(可以相等),走到现在产生了逆序,说明这个新元素比子序列所有元素都要大,此时不用回退,而是可以直接将辅助变量的开始下标,拉到结束位置。如果到达末尾,那么结果必将是所求的最长子序列。算法只遍历一遍序列,显然,时间复杂度是 O(n)。

    (3)额外优化

    我们再更进一步,仔细思考一个小问题:假如前一个子序列是最长的,它的长度过了整个序列的一半。此时它突然遇到一个逆序。这个时候,我们可以直接返回结果,因为后一半元素,就算也是有序的,长度也肯定不会大于前面的子序列。

  • 相关阅读:
    MacOS如何正确配置Idea自带Maven插件的环境变量?(亲测)
    通过Canvas实现画板
    插入排序
    选择排序
    冒泡排序法
    进制
    JDBC(宠物管理系统)
    银行ATM存取款机系统MySQL数据库
    高级查询(二)
    高级查询
  • 原文地址:https://www.cnblogs.com/Hello-Nolan/p/13562943.html
Copyright © 2011-2022 走看看