求解最长上升子序列
使用动态规划和贪心两种算法
最长上升子序列问题,就是对于一个有顺序的数列, 从中按顺序选择的尽可能元素 (可以不连续) 构成的新数列是单调递增的.
最长上升子序列问题在许多与图论,算法,随机矩阵理论, 表示论相关的研究都会涉及, 比如排列图中的最大团是由 ' 定义该图的排列中最长的递减子序列 ' 定义的, 求最长的递减子序列在计算复杂度上 (通过对所有数取它的负数) 等同于求最长的递增子序列. 因此,最长递增子序列算法可用于有效地解决排列图中的分团问题
动态规划
动态规划其实非常好想到。设 f [i] 为以第 i 个数字结尾的最长上升子序列,
则 for j in range(1, i): if nums[i] > nums[j]: f[i] = max(f[j] + 1, f[i])
.
贪心
动态规划的复杂度还是有点高,有没有什么更好的办法。毕竟理论上, 我只要扫描一遍数组,这个序列长度就是一定的.
首先考虑的肯定是贪心。要想得到最长上升子序列, 这个序列的上升速度必须有,但是要尽可能的慢, 这样子后面才有更大的上升空间。要想让上升速度慢, 每次加到序列末尾的数字必须尽可能的小。当然,也必须大于前一个数字.
那我们就需要记录一下这个最小值方便以后更新. 因为序列是一个数一个数添加起来的,所以每个长度的末尾最小值都需要记录. 把这个数组叫做 tail_min.
从定义可以得到: tail_min 是单调增的。可以用反证法证明.
接下来的事情就简单了, 我们一次遍历原数组中的每个元素更新 tail_min 里的值. 为了方便查找当前最长的序列,再维护一个 longest_length 变量.
这样,对于每个数 num, 如果 num > tail_min [longest_length],
则 tail_min[++longest_length] = num
.
否则,
查找满足 for i in range(1, longest_length): tail_min[i-1] < num < tail_min[i]
的 i,
令 tail_min[i] = num
. 这里可以使用二分查找优化速度.
最后输出 longest_length 就可以啦。时间复杂度是 O (nlogn), 空间复杂度为 O (n)
def longest_increasing_subsequence(nums): |
unsigned long long int longest_increasing_subsequence( |