分治法-最大子数组问题(从文件中读取数组,并输出结果)

学习算法导论的时候,有一个买股票的例子,我觉得挺有意思的,书上是这么说的:

    假定你获得了投资挥发性化学公司的机会。与其生产的化学制品一样,这家公司的股票价格也是不稳定的。你被准许可以在某个时刻买进一股该公司的股票,并在之后某个日期将其卖出,买进卖出都是在当天交易结束后进行。为了补偿这一限制,你可以了解股票将来的价格。你的目标是最大化收益。图4-1给出了17天内的股票价格。第0天的股票价格是每股100美元,你可以在此之后任何时间买进股票。你当然希望“低价买进.高价卖出”—在最低价格时买进股票,之后在最高价格时卖出,这样可以最大化收益。但遗憾的是,在一段给定时期内,可能无法做到在最低价格时买进股票,然后在最高价格时卖出.例如,在图4-1中,最低价格发生在第7
天,而最高价格发生在第1天—最高价在前,鼓低价在后。

其实就是求最大连续子数组的问题,求在连续的买入股票,使得收益最大,当然是用暴力求解的方法也可以求出来,时间复制度为O(n^2)。不过有更好的方法,那就是分治法,时间复杂度为O(nlgn)。

最大连续子数组问题我们老师当做实验,是要求通过代码实现出来的,我用的是java实现。

分治法分三步:

1. 分:可以将数组分为两部分,要尽量均匀,所以最好是从中间分,结果可以能在前半数组,可能在后半数组,也可能跨前后数组

2. 治:我们可以递归求解前半数组最大值,递归求解后半数组最大值,再求解跨前后数组最大值

3. 合并:将求得的值返回,比较求解到的三个最大值,返回其中的最大值

首先需要定义一个返回的数据结构:

public static class Result{
    public int left;
    public int right;
    public int sum;
}

然后实现求解跨前后数组最大值

public static Result find_max_crossing_subarray(int[] a, int low, int mid, int high) {
		int left_sum = -1;
		int max_left = 0;
		int right_sum = -1;
		int max_right = 0;
		int sum = 0;
		for(int i = mid; i >= 0; i--) {
			sum += a[i];
			if(sum > left_sum) {
				left_sum = sum;
				max_left = i;
			}
		}
		sum = 0;
		for(int j = mid + 1; j < high; j++) {
			sum += a[j];
			if(sum > right_sum) {
				right_sum = sum;
				max_right = j;
			}
		}
		Result result = new Result();
		result.left = max_left;
		result.right = max_right;
		result.sum = left_sum + right_sum;
		return result;
	}

最后递归实现求解前半数组最大值,后半数组最大值

public static Result find_maximun_subarray(int[] a, int low, int high) {
		Result leftres = new Result();
		Result rightres = new Result();
		Result crossres = new Result();
		if(low == high) {
			Result result = new Result();
			result.left = low;
			result.right = high;
			result.sum = a[low];
			return result;
		}
		
		int mid = (low + high)/2;
		leftres = find_maximun_subarray(a, low, mid);
		rightres = find_maximun_subarray(a, mid + 1, high);
		crossres = find_max_crossing_subarray(a, low, mid, high);
		if(leftres.sum > rightres.sum && leftres.sum > crossres.sum) {
			return leftres;
		}
		else if(rightres.sum > leftres.sum && rightres.sum > crossres.sum) {
			return rightres;
		}
		else {
			return crossres;
		}
	}

实验时,老师要求随机生成1000个随机数,从.txt文件里读取。

要知道数组里有负数,最大连续子数组才有意义,不然最大的肯定是整个数组的和,所以需要随机生成含正负数的数组

public static int random(int num1, int num2) {
    Random random = new Random();
    return (Math.abs(random.nextInt()) % (num1 - num2)) + num1;
}

首先生成随机数,写入txt文件

String filepath = "E:\\input.txt"; 
    File file = new File(filepath); 
    try {
        PrintWriter writer = new PrintWriter(new BufferedWriter(new
        OutputStreamWriter(new FileOutputStream(file), "gbk"))); 
        Random random = new Random(); 
        for(int i = 0; i < 1000; i++) {
        int n = random(-100, 100);
        writer.write(Integer.toString(n) + " ");
    } 
    writer.flush(); 
    writer.close(); 
    }catch (Exception e) {
        e.printStackTrace();
}

读取文件并输出

                String filepath = "E:\\input.txt";
		File file = new File(filepath);
		int[] data = null;
		try {
			Reader reader = new InputStreamReader(new FileInputStream(file));
			BufferedReader bf = new BufferedReader(reader);
			String textline = "";
			String str = "";
			while ((textline = bf.readLine()) != null) {
				str += " " + textline;
			}
			String[] arr = str.split(" ");
			data = new int[arr.length];
			for (int i = 1; i < data.length; i++) {
				data[i] = Integer.valueOf(arr[i]);
			}
		} catch (Exception e) {
		}
		int low = 0, high = data.length - 1;
		Result result = find_maximun_subarray(data, low, high);
		System.out.print("最大连续子数组为:[");
		for (int i = result.left; i <= result.right; i++) {
			if (i == result.right) {
				System.out.println(data[i] + "]");
			} else {
				System.out.print(data[i] + ",");
			}
		}
		System.out.println("最大值为:" + result.sum);

最后附上完整代码

package com.dao;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.Arrays;
import java.util.Random;

public class Maxinum_continuous_subarray {

	public static int random(int num1, int num2) {
		Random random = new Random();
		return (Math.abs(random.nextInt()) % (num1 - num2)) + num1;
	}

	public static class Result {
		public int left;
		public int right;
		public int sum;
	}

	public static void main(String[] args) {

		/*
		 * String filepath = "E:\\input.txt"; File file = new File(filepath); try {
		 * PrintWriter writer = new PrintWriter(new BufferedWriter(new
		 * OutputStreamWriter(new FileOutputStream(file), "gbk"))); Random random = new
		 * Random(); for(int i = 0; i < 1000; i++) { int n = random(-100, 100);
		 * writer.write(Integer.toString(n) + " "); } writer.flush(); writer.close();
		 * }catch (Exception e) { e.printStackTrace(); }
		 */

		String filepath = "E:\\input.txt";
		File file = new File(filepath);
		int[] data = null;
		try {
			Reader reader = new InputStreamReader(new FileInputStream(file));
			BufferedReader bf = new BufferedReader(reader);
			String textline = "";
			String str = "";
			while ((textline = bf.readLine()) != null) {
				str += " " + textline;
			}
			String[] arr = str.split(" ");
			data = new int[arr.length];
			for (int i = 1; i < data.length; i++) {
				data[i] = Integer.valueOf(arr[i]);
			}
		} catch (Exception e) {
		}
		int low = 0, high = data.length - 1;
		Result result = find_maximun_subarray(data, low, high);
		System.out.print("最大连续子数组为:[");
		for (int i = result.left; i <= result.right; i++) {
			if (i == result.right) {
				System.out.println(data[i] + "]");
			} else {
				System.out.print(data[i] + ",");
			}
		}
		System.out.println("最大值为:" + result.sum);
	}

	public static Result find_maximun_subarray(int[] a, int low, int high) {
		Result leftres = new Result();
		Result rightres = new Result();
		Result crossres = new Result();
		if (low == high) {
			Result result = new Result();
			result.left = low;
			result.right = high;
			result.sum = a[low];
			return result;
		}

		int mid = (low + high) / 2;
		leftres = find_maximun_subarray(a, low, mid);
		rightres = find_maximun_subarray(a, mid + 1, high);
		crossres = find_max_crossing_subarray(a, low, mid, high);
		if (leftres.sum > rightres.sum && leftres.sum > crossres.sum) {
			return leftres;
		} else if (rightres.sum > leftres.sum && rightres.sum > crossres.sum) {
			return rightres;
		} else {
			return crossres;
		}
	}

	public static Result find_max_crossing_subarray(int[] a, int low, int mid, int high) {
		int left_sum = -1;
		int max_left = 0;
		int right_sum = -1;
		int max_right = 0;
		int sum = 0;
		for (int i = mid; i >= 0; i--) {
			sum += a[i];
			if (sum > left_sum) {
				left_sum = sum;
				max_left = i;
			}
		}
		sum = 0;
		for (int j = mid + 1; j < high; j++) {
			sum += a[j];
			if (sum > right_sum) {
				right_sum = sum;
				max_right = j;
			}
		}
		Result result = new Result();
		result.left = max_left;
		result.right = max_right;
		result.sum = left_sum + right_sum;
		return result;
	}

}

运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值