最大子序列和整理,复杂度从O(n^3)到O(n)

求一个序列的子序列的最大值,这是一个经典算法,这里稍作整理。

问题:任给一个整数序列,如{-2, 5, 7, 11, -4, 13, -5, -2, -6, 3, -1, 3},求出这个序列中连续子序列的和的最大值,这个例子中最大值为32,子序列为{5, 7, 11, -4, 13}。

方法一:最简单的暴力法。确立一个起点,一个终点,计算起点到终点的和。下面代码中int brute_force(int* a,int n)实现了该算法,时间复杂度由

计算可得为O(n^3)。

方法二:对暴力法的优化,使用累加的方法,确定一个起点之后,将前面序列的和累加到终点,选取一个最大值。下面代码中int improved_brute_force(int* a, int n),实现了该算法。时间复杂度由

可得为O(n^2)。

方法三:采用分治法的思想,从中间点将序列分成两个子序列,整个序列的最大子序列位置分三种情况(见图,此图来自老师PPT)

1.在左侧子序列中

2.在右侧子序列中

3.包含中间点,两个序列各有一部分。

下面的int divide_conquer(int* a,int start,int end)方法实现了该算法,该递归实现的递归式为T(n)=2T(n/2)+cn。由Master
theorem
可得时间复杂度为O(nlogn).

方法四:采用动态规划的思想,又称为Online algorithm,在线学习算法的定义:线上算法是一种处理输入资料的独特形式,算法开始的时候不需要所有的数据都准备好,而是对逐步输入的数据进行处理,并在输入完成之后输出运算结果。与之相对的是离线算法,假设所有数据在算法开始时都已经准备好,选择排序是离线算法,插入排序是在线算法。用动态规划处理最大子序列长度的思想为用变量max_value保存最大子序列的和,用current_value保存从i=0累加的的和,如果current_value大于max_value则保存,否则放弃,如果current减小到零,则将current_value从0重新开始计因为再讲current_value累加的话只会减少子序列的和。下面的int
dynamic_prom(int* a,int len)实现了该算法。该算法时间复杂度为O(n)

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define len_array 12

/*
求最大子序列的和
*/

//brute force:时间复杂度n^3
int brute_force(int* a,int n){
	int i,j,k;
	int max_value = INT_MIN;
	int current_value;

	for(i=0;i<n;i++){			//以i为起点
		for(j=i;j<n;j++){		//以j为终点
			current_value = 0;
			for(k=i;k<=j;k++){				//求从i到j的子序列的和
				current_value += a[k];
			}
			if(current_value > max_value){
				max_value = current_value;
			}
		}
	}

	return max_value;
}

//提高的brute force, 时间复杂度n^2
int improved_brute_force(int* a, int n){
	int i,j;
	int max_value = INT_MIN;
	int current_value = 0;
	for(i=0;i<n;i++){			//以i为起点
		current_value = 0;		//设置以i为起点的子序列的初始值为0
		for(j=i;j<n;j++){		//以j为终点
			current_value += a[j];
			if(current_value>max_value){
				max_value = current_value;
			}
		}
	}
	return max_value;
}

//分治法,时间复杂度nlog(n)
//从中间点将序列分成两个子序列,整个序列的最大子序列位置分三种情况
//1.在左侧子序列中
//2.在右侧子序列中
//3.包含中间点,两个序列各有一部分
int divide_conquer(int* a,int start,int end){

	int mid;
	int mid_to_left_max = 0;
	int mid_to_right_max = 0;
	int i,current_left=0,current_right=0;
	int side_max,mid_max,max,left_max,right_max;

	if(start==end)
		return a[start];

	mid = (start+end)/2;
	left_max = divide_conquer(a,start,mid);		//寻找左侧子序列的最大值
	right_max = divide_conquer(a,mid+1,end);	//寻找右侧子序列的最大值

	for(i=mid;i>=0;i--){						//寻找以中间点为起点,向左延伸的最大子序列
		current_left += a[i];
		if(current_left>mid_to_left_max){
			mid_to_left_max = current_left;
		}
	}

	for(i=mid;i<=end;i++){						//寻找以中间点为起点,向右延伸的最大子序列
		current_right += a[i];
		if(current_right>mid_to_right_max){
			mid_to_right_max = current_right;
		}
	}

	mid_max = mid_to_left_max+mid_to_right_max-a[mid];
	side_max = left_max>right_max?left_max:right_max;
	max = side_max>mid_max?side_max:mid_max;

	return max;
}

//又称为Online algorithm,在线学习算法的定义:线上算法是一种处理输入资料的独特形式,算法开始的时候
//不需要所有的数据都准备好,而是对逐步输入的数据进行处理,并在输入完成之后输出运算结果。与之相对的是
//离线算法,假设所有数据在算法开始时都已经准备好,选择排序是离线算法,插入排序是在线算法
//用动态规划处理最大子序列长度的思想为用变量max_value保存最大子序列的和,用current_value保存从i=0累加的
//的和,如果current_value大于max_value则保存,否则放弃,如果current减小到零,则将current_value从0重新开始计
//因为再讲current_value累加的话只会减少子序列的和
int dynamic_prom(int* a,int len){
	int current_value = 0;
	int max_value = INT_MIN;
	int i;

	for(i=0;i<len;i++){
		current_value += a[i];
		if(current_value>max_value){	//当前值大于最大值,更新
			max_value = current_value;
		}

		if(current_value<0){			//当前值小于零,从0开始
			current_value = 0;
		}
	}

	return max_value;
}

int main(){
	int a[12] = {-2, 5, 7, 11, -4, 13, -5, -2, -6, 3, -1, 3};

	printf("brute force: %d\n",brute_force(a,len_array));

	printf("improved brute force: %d\n",improved_brute_force(a,len_array));

	printf("divide conquer: %d\n",divide_conquer(a,0,len_array-1));

	printf("dynamic programming: %d\n",dynamic_prom(a,len_array));

	printf("\n");

	return EXIT_SUCCESS;
}
时间: 2024-11-08 19:54:33

最大子序列和整理,复杂度从O(n^3)到O(n)的相关文章

最长单调子序列求解

 本篇博文为追忆曾经写过的算法系列第三篇 温故知新 题目重述 已知一个序列,由随机数构成,求其最长单调子序列. 要求:单调分严格和不严格两种情况,并分别求解并输出一个最长单调子序列和所有符合要求的子序列. 问题分析 本题是求解有约束条件的子序列问题,可用动态规划求解.由于本题是求解最长单调子序列的,包括求一个最长单调子序列和求解所有符合要求的序列,下面将按照这两种情况讨论算法复杂度. 求解一个最长单调子序列的算法复杂度 本题假设单调为递增的情况,序列长度为N(任意大小),即序列S[N].若采

聊聊程序员如何学习英语单词:写了一个记单词的小程序

背景: 关于英文对程序员的重要性,就不多说了! 英语的学习,有很多,今天也不聊多,只聊英语单词! 关于单词的记忆,找过很多方法,下载过很多软件. 如图(其它不好用的都卸载了): 上图算是我以前用过软件,注意,是以前哦~~~ 意思就是没有坚持下来~~~~ 随时间的推移,最后它们还是被我遗忘了~~~ 为什么???不能:坚持!坚持!坚持! 学习思考: 一直在找方法: 1:下载过联想记忆法.背文章记单词,词根,各种视频~~~ 2:连单词的数据库都网上下载了一份了,期望从数据库的直接记忆单词快些~~~ 通

排序 之 堆排序 归并排序

堆的概念 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子结点的值,称为大顶堆:或着每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆. 堆排序 堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法.它的基本思想是,将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是对顶的根结点.将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值.如此反复执行,就可以得到一个

用分治和递归的思想——寻找最大子数组

寻找最大子数组的问题可描述为 输入: 一个数组,一个低位,一个高位 输出: 数组中从低位到高位中,连续和最大是多少 首先能想到的最直接的办法是暴力解决,遍历所有可能的序列组合,如果有n个元素,则需遍历的子序列有,复杂度为n2,稍有些经验的就能马上意识到,有很多重复计算在里面,比如最长的子序列计算,包含了之前所有子序列的计算.接下来我们使用分治的思想求解这个最大子序列,前一篇博文提过,分治的思想是将大问题分解为同等类型的子问题,再将子问题求解,然后合并子问题得出原问题的解,其中用到了递归的思想,因

HDU 4745 Two Rabbits (区间DP)

题意: 两只兔子,在一个由n块石头围成的环上跳跃,每块石头有一个权值ai.开始时两兔站在同一石头上(也算跳1次),一只从左往右跳,一只从右往左跳,两只同时跳,而每跳一次,两只兔子所站的石头的权值都要相等,在一圈内(各自不能越过起点,也不能再次回到起点)它们(单只兔子)最多能跳多少次(1 <= n <= 1000, 1 <= ai <= 1000). 思路: 此题要求的就是最长回文子序列(并不是子串),而最长回文子序列的算法复杂度为O(n*n).但是由于是个环上,所以要挖掘一下环的性

2016暑期选拔第一场20160709

A みねちゃんの修罗场 Time Limit: 5000 mSec    Memory Limit : 1024 KB  Problem Description みねちゃん是个成绩优秀大学二年级学生,本来是和像自己妹妹一般的青梅竹马一起过着普通的大学生活的,但某天却被校内公认的第一美人表白了.然而她的真实意图却是为了骗过众人而需要みねちゃん与她假扮情侣.被掌握了自己的某个"秘密"的みねちゃん被迫假扮"男友"这一角色--然而在此之后他的"未婚妻"也

二路归并 &amp;&amp; 插入归并 &amp;&amp; 原地归并

插入归并 归并排序的时间复杂度为O(nlgn),空间复杂度为O(n): 但是一般来讲,基于从单个记录开始两两归并的排序并不是特别提倡,一种比较常用的改进就是结合插入排序,即先利用插入排序获得较长的有序子序列,然后再两两归并(改进后的归并亦是稳定的,因为插入排序是稳定的).之所以这样改进是有原因的:尽管插入排序的最坏情况是O(n^2),看起来大于归并的最坏情况O(nlgn),但通常情况下,由于插入排序的常数因子使得它在n比较小的情况下能运行的更快一些,因此,归并时,当子问题足够小时,采用插入排序是

查找与排序算法(Searching adn Sorting)

1,查找算法 常用的查找算法包括顺序查找,二分查找和哈希查找. 1.1 顺序查找(Sequential search) 顺序查找: 依次遍历列表中每一个元素,查看是否为目标元素.python实现代码如下: #无序列表 def sequentialSearch(alist,item): found = False pos=0 while not found and pos<len(alist): if alist[pos]==item: found=True else: pos = pos+1 r

[ACM] 九度OJ 合唱队形 (最长递增子序列改版)

题目1131:合唱队形 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1680 解决:520 题目描述: N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, -, K,他们的身高分别为T1, T2, -, TK, 则他们的身高满足T1 < T2 < - < Ti , Ti > Ti+1 > - > TK (1 <= i <=