22章 开发高效算法

1.编写一个程序,提示用户输入一个字符串,然后显示最大连续递增的有序子字符串。分析你的程序的时间复杂度。

import java.util.Scanner;

public class Test {
	public static void main(String[] args) {
		System.out.println("请输入字符串:");
		Scanner scanner = new Scanner(System.in);
		String s = scanner.nextLine();
		scanner.close();
		char pre_c = s.charAt(0);
		int m = 1, n = 1, mi = 1;
		for (int i = 1; i < s.length(); i++) {
			if (s.charAt(i) > pre_c)
				++n;
			else {
				if (m < n) {
					m = n;
					mi = i;
				}
				n = 1;
			}
			pre_c = s.charAt(i);
		}
		System.out.println("最大连续递增子串为:" + (n > m ? s.substring(s.length() - n) : s.substring(mi - m, mi)));
	}
}

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)

2.编写一个程序,提示用户输入一个字符串,然后显示最大连续递增的有序字符子序列。分析你的程序的时间复杂度。

import java.util.Scanner;

public class Test {
	public static void main(String[] args) {
		System.out.println("请输入字符串:");
		Scanner scanner = new Scanner(System.in);
		String s = scanner.nextLine();
		scanner.close();
		if (s.isEmpty()) {
			System.out.println("最大连续递增的有序字符子序列为:");
			return;
		}
		int n = s.length();
		String[] d = new String[n]; // d[i] 表示第 i 个字符结尾的子序列
		for (int i = 0; i < n; i++) {
			char c = s.charAt(i);
			d[i] = String.valueOf(c);
			for (int j = 0; j < i; j++)
				if (s.charAt(j) < c && d[j].length() >= d[i].length())
					d[i] = d[j] + c;
		}
		String res = d[0];
		for (int i = 1; i < n; i++)
			if (d[i].length() > res.length())
				res = d[i];
		System.out.println("最大连续递增的有序字符子序列为:" + res);
	}
}

(动态规划)时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( n ) O(n) O(n)

3.编写一个时间复杂度为 O ( n ) O(n) O(n)的程序,提示用户输入两个字符串,然后检测第二个字符串是否为第一个字符串的子串。假定在字符串中相邻的字符是不同的。(不要使用String类中的indexOf方法)

import java.util.Scanner;

public class Test {
	public static void main(String[] args) {
		System.out.println("请输入两个字符串");
		Scanner scanner = new Scanner(System.in);
		String s1 = scanner.nextLine();
		String s2 = scanner.nextLine();
		scanner.close();
		int i = KMP(s1, s2);
		System.out.println(i == -1 ? "不是子串!" : "在下标" + i + "处成功匹配!");
	}

	public static int KMP(String s, String p) {
		int[] next = KMP_next(p);
		int i = 0, j = 0, n = s.length(), m = p.length();
		while (i < n) {
			if (s.charAt(i) == p.charAt(j)) {
				++i;
				++j;
			} else if (j > 0)
				j = next[j - 1];
			else
				++i;
			if (j == m) return i - j;
		}
		return -1;
	}

	private static int[] KMP_next(String p) {
		int n = p.length(), i = 1, j = 0;
		int[] a = new int[n];
		while (i < n)
			if (p.charAt(j) == p.charAt(i))
				a[i++] = ++j;
			else if (j == 0)
				a[i++] = 0;
			else
				j = a[j - 1];
		return a;
	}
}

4.改进程序清单22-15中的Boyer-Moore算法实现,在 O ( 1 ) O(1) O(1)的时间内测试不匹配字符位于模式中的哪里,使用模式中所有字符组成的规则集。如果测试为假,算法可以将模式划过不匹配字符。

5.编写一个时间复杂度为 O ( n ) O(n) O(n)的程序,提示用户输入一个以0结束的整数序列,找出同样数字的最长连续子序列。

import java.util.Scanner;

public class Test {
	public static void main(String[] args) {
		int pre = 0, index = -1;
		int current, currentIndex = 0, currentCount = 1;
		int maxNum = 0, maxIndex = 0, maxCount = 1;
		Scanner scanner = new Scanner(System.in);
		while ((current = scanner.nextInt()) != 0) {
			++index;
			if (current == pre) {
				++currentCount;
				continue;
			}
			if (currentCount > maxCount) {
				maxNum = pre;
				maxIndex = currentIndex;
				maxCount = currentCount;
			}
			currentIndex = index;
			currentCount = 1;
			pre = current;
		}
		scanner.close();
		System.out.printf("The longest same number sequence starts at index %d with %d values of %d.", maxIndex, maxCount, maxNum);
	}
}

6.编写一个程序,使用程序清单22-3和程序清单22-4中的算法,求下标从40到45的每两个连续的斐波那契数的GCD,并求其执行时间。

7.22.8节介绍了一个使用分治法求最近点对的算法。实现这个算法,使其满足下面的要求:

  • 定义一个名为Pair的类,其数据域p1p2表示两个点,名为getDistance()的方法返回这两个点之间的距离。
  • 实现下面的方法:

7

import java.awt.geom.Point2D;

public class Pair {
	public Point2D p1, p2;

	public Pair(Point2D p1, Point2D p2) {
		this.p1 = p1;
		this.p2 = p2;
	}

	public double getDistance() {
		return Point2D.distance(p1.getX(), p1.getY(), p2.getX(), p2.getY());
	}

	public static Pair getClosestPair(double[][] points) {
		Closest2DPointsFinder<double[]> finder = new Closest2DPointsFinder<>(points) {
			@Override
			public double getX(double[] point) {
				return point[0];
			}

			@Override
			public double getY(double[] point) {
				return point[1];
			}
		};
		return finder.getClosestDistancePair();
	}

	public static Pair getClosestPair(Point2D[] points) {
		Closest2DPointsFinder<Point2D> finder = new Closest2DPointsFinder<>(points) {
			@Override
			public double getX(Point2D point) {
				return point.getX();
			}

			@Override
			public double getY(Point2D point) {
				return point.getY();
			}
		};
		return finder.getClosestDistancePair();
	}

	public static double distance(Point2D p1, Point2D p2) {
		return Point2D.distance(p1.getX(), p1.getY(), p2.getX(), p2.getY());
	}

	public static double distance(double x1, double y1, double x2, double y2) {
		return Point2D.distance(x1, y1, x2, y2);
	}
}
import java.awt.geom.Point2D;
import java.util.Comparator;

public abstract class Closest2DPointsFinder<E> extends ClosestPointsFinder<E> {
	public Closest2DPointsFinder(E[] points, Comparator<E> comparator) {
		super(points);
		super.init(comparator);
	}

	public Closest2DPointsFinder(E[] points) {
		super(points);
		super.init(new Comparator<E>() {
			@Override
			public int compare(E o1, E o2) {
				if (getX(o1) > getX(o2)) return 1;
				if (getX(o1) == getX(o2))
					if (getY(o1) > getY(o2))
						return 1;
					else if (getY(o1) == getY(o2))
						return 0;
				return -1;
			}
		});
	}

	public abstract double getY(E e);

	public Point2D toPoint2D(E e) {
		return new Point2D.Double(getX(e), getY(e));
	}

	@Override
	public double getDistance(E e1, E e2) {
		return Point2D.distance(getX(e1), getY(e1), getX(e2), getY(e2));
	}

	public Pair getClosestDistancePair() {
		ClosestPointsFinder<E>.PointsPairRef ref = getClosestDistance();
		return new Pair(toPoint2D(points[ref.p1Index]), toPoint2D(points[ref.p2Index]));
	}
}
import java.util.Arrays;
import java.util.Comparator;

public abstract class ClosestPointsFinder<E> {
	protected E[] points;

	public ClosestPointsFinder(E[] points) {
		this.points = points;
	}

	public void init(Comparator<E> comparator) {
		Arrays.sort(points, comparator);
	}

	public abstract double getDistance(E e1, E e2);

	public double getDistanceByIndex(int index1, int index2) {
		return getDistance(points[index1], points[index2]);
	}

	public abstract double getX(E e);

	public double getXByIndex(int index) {
		return getX(points[index]);
	}

	public class PointsPairRef {
		public int p1Index, p2Index;
		public double distance;

		public PointsPairRef(int p1, int p2, double d) {
			p1Index = p1;
			p2Index = p2;
			distance = d;
		}

		public PointsPairRef(int p1, int p2) {
			p1Index = p1;
			p2Index = p2;
			distance = getDistanceByIndex(p1, p2);
		}

		public PointsPairRef(int p) {
			p1Index = p2Index = p;
			distance = Double.MAX_VALUE;
		}

		public boolean isValid() {
			return p1Index != p2Index;
		}
	}

	private PointsPairRef getClosestDistance(int l, int r) {
		if (l + 1 >= r) return new PointsPairRef(l);
		if (l + 2 == r) return new PointsPairRef(l, l + 1);
		int m = (l + r) >> 1;
		PointsPairRef L = getClosestDistance(l, m), R = getClosestDistance(m + 1, r);
		double d, t;
		int m1, m2;
		if (L.isValid())
			if (R.isValid()) {
				if (L.distance > R.distance) {
					d = R.distance;
					m1 = R.p1Index;
					m2 = R.p2Index;
				} else {
					d = L.distance;
					m1 = L.p1Index;
					m2 = L.p2Index;
				}
				for (int i = binarySearch(l, m, d); i < m; i++) {
					double di = getDistanceByIndex(i, m);
					if (di < d) {
						d = di;
						m1 = i;
						m2 = m;
					}
				}
				for (int i = binarySearch(m + 1, r, d); i < r; i++) {
					double di = getDistanceByIndex(i, m);
					if (di < d) {
						d = di;
						m1 = i;
						m2 = m;
					}
				}
			} else {
				d = getDistanceByIndex(m, R.p1Index);
				m1 = m;
				m2 = R.p1Index;
				for (int i = binarySearch(l, m, d); i < m; i++) {
					double di = getDistanceByIndex(i, m);
					if (di < d) {
						d = di;
						m2 = i;
					}
				}
			}
		else if (R.isValid()) {
			d = getDistanceByIndex(m, L.p1Index);
			m1 = m;
			m2 = L.p1Index;
			for (int i = binarySearch(m + 1, r, d); i < r; i++) {
				double di = getDistanceByIndex(i, m);
				if (di < d) {
					d = di;
					m2 = i;
				}
			}
		} else {
			d = getDistanceByIndex(L.p1Index, m);
			t = getDistanceByIndex(R.p1Index, m);
			if (d > t) {
				d = getDistanceByIndex(L.p1Index, R.p1Index);
				return d > t ? new PointsPairRef(R.p1Index, m, t) : new PointsPairRef(L.p1Index, R.p1Index, d);
			} else {
				t = getDistanceByIndex(L.p1Index, R.p1Index);
				return d > t ? new PointsPairRef(L.p1Index, R.p1Index, t) : new PointsPairRef(L.p1Index, m, d);
			}
		}
		return new PointsPairRef(m1, m2, d);
	}

	private int binarySearch(int l, int r, double key) {
		--r;
		while (l <= r) {
			int m = (l + r) >> 1;
			if (getXByIndex(m) > key)
				r = m - 1;
			else
				l = m + 1;
		}
		return l;
	}

	public PointsPairRef getClosestDistance() {
		return getClosestDistance(0, points.length);
	}
}

8.编写一个程序,找出不大于10 000 000 000的所有素数。大概有455 052 511个这样的素数。你的程序应该满足下面的要求:

  • 应该将这些素数都存储在一个名为PrimeNumber.dat的二进制数据文件中。当找到一个新素数时,将该数字追加到这个文件中。
  • 为了判断一个新的数是否为素数,程序应该从数据文件加载这些素数到一个大小为10000的long型数组。如果数组中没有任何数是这个新数的除数,继续从该数据文件中读取接下来的10000个素数,直到找到除数或者读取完文件中的所有数字。如果没找到除数,这个新的数字就是素数。
  • 因为执行该程序要花很长时间,所以应该把它作为UNIX机器上的一个批处理任务来运行。如果机器被关闭或重启,程序应该使用二进制数据文件中存储的素数来继续,而不是从零开始启动。
import java.io.*;

public class PrimeFinder {
	private int bufferSize;
	private RandomAccessFile primesFile;
	private long currentNumber;
	private long maxNumber;
	private long[] buffer;

	public PrimeFinder(String fileName, long maxNumber, int bufferSize) throws IOException {
		this.maxNumber = maxNumber;
		this.bufferSize = bufferSize;
		try {
			primesFile = new RandomAccessFile(fileName, "rw");
			if (primesFile.length() == 0L) {
				currentNumber = 3L;
				primesFile.writeLong(2L);
			} else
				try {
					primesFile.seek(primesFile.length() - Long.BYTES);
					currentNumber = primesFile.readLong();
				} catch (IOException e) {
					currentNumber = 3L;
					primesFile.writeLong(2L);
				}
		} catch (FileNotFoundException e) {
			File file = new File(fileName);
			file.createNewFile();
			primesFile = new RandomAccessFile(file, "rw");
			currentNumber = 3L;
			primesFile.writeLong(2L);
		} finally {
			buffer = new long[bufferSize];
		}
	}

	public PrimeFinder(String fileName, long maxNumber) throws IOException {
		this(fileName, maxNumber, 10000);
	}

	public PrimeFinder(String fileName) throws IOException {
		this(fileName, 10000000000L);
	}

	public PrimeFinder() throws IOException {
		this("PrimeNumber.dat");
	}

	private static boolean isnPrimeInBuffer(long number, long[] buffer, int size) {
		for (int i = 0; i < size; i++)
			if (number % buffer[i] == 0L)
				return true;
		return false;
	}

	private boolean isPrime() throws IOException {
		int currentBufferSize = 0;
		primesFile.seek(0L);
		boolean isnEOF = true;
		do {
			do
				try {
					buffer[currentBufferSize] = primesFile.readLong();
				} catch (EOFException e) {
					isnEOF = false;
					break;
				}
			while (++currentBufferSize < bufferSize);
			if (isnPrimeInBuffer(currentNumber, buffer, currentBufferSize)) return false;
			currentBufferSize = 0;
		} while (isnEOF);
		return true;
	}

	public void findPrimes() throws IOException {
		while (currentNumber < maxNumber) {
			if (isPrime()) primesFile.writeLong(currentNumber);
			currentNumber += 2;
		}
		primesFile.close();
	}
}

9.编写一个程序,利用卷包裹算法为点集找到凸包。

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

public abstract class GiftWrapper<E> {
	public abstract double getX(E e);

	public abstract double getY(E e);

	public Point2D toPoint2D(E e) {
		return new Point2D.Double(getX(e), getY(e));
	}

	// 生成的列表是原数组元素的浅拷贝
	public ArrayList<E> getConvexHull(E[] points) {
		int n = points.length;
		if (n < 4) return new ArrayList<>(List.of(points));
		ArrayList<E> list = new ArrayList<>();
		int firstIndex = getRightLowestPointIndex(points), preIndex = firstIndex;
		E prePoint = points[firstIndex];
		do {
			list.add(prePoint);
			int i = preIndex == 0 ? 1 : 0;
			double mx = getX(points[i]) - getX(prePoint), my = getY(points[i]) - getY(prePoint);
			for (int j = i + 1; j < n; j++) {
				if (j == preIndex) continue;
				double jx = getX(points[j]) - getX(prePoint), jy = getY(points[j]) - getY(prePoint);
				if (onRight(mx, my, jx, jy)) {
					i = j;
					mx = jx;
					my = jy;
				}
			}
			prePoint = points[preIndex = i];
		} while (preIndex != firstIndex);
		return list;
	}

	public ArrayList<Point2D> getConvexHullPoint2D(E[] points) {
		int n = points.length;
		if (n < 4) {
			ArrayList<Point2D> list = new ArrayList<>();
			for (E e : points) list.add(toPoint2D(e));
			return list;
		}
		ArrayList<Point2D> list = new ArrayList<>();
		int firstIndex = getRightLowestPointIndex(points), preIndex = firstIndex;
		E prePoint = points[firstIndex];
		do {
			list.add(toPoint2D(prePoint));
			int i = preIndex == 0 ? 1 : 0;
			double mx = getX(points[i]) - getX(prePoint), my = getY(points[i]) - getY(prePoint);
			for (int j = i + 1; j < n; j++) {
				if (j == preIndex) continue;
				double jx = getX(points[j]) - getX(prePoint), jy = getY(points[j]) - getY(prePoint);
				if (onRight(mx, my, jx, jy)) {
					i = j;
					mx = jx;
					my = jy;
				}
			}
			prePoint = points[preIndex = i];
		} while (preIndex != firstIndex);
		return list;
	}

	// 判断向量2是否在向量1的右侧
	private static boolean onRight(double vector1X, double vector1Y, double vector2X, double vector2Y) {
		// 旋转坐标系使得第1个向量位于第1象限
		if (vector1X < 0)
			if (vector1Y < 0) {
				vector1X = -vector1X;
				vector1Y = -vector1Y;
				vector2X = -vector2X;
				vector2Y = -vector2Y;
			} else {
				double t = vector1Y;
				vector1Y = -vector1X;
				vector1X = t;
				t = vector2Y;
				vector2Y = -vector2X;
				vector2X = t;
			}
		else if (vector1Y < 0) {
			double t = vector1X;
			vector1X = -vector1Y;
			vector1Y = t;
			t = vector2X;
			vector2X = -vector2Y;
			vector2Y = t;
		}
		if (vector2X < 0)
			if (vector2Y > 0)
				return false;
			else
				return vector2X * vector1Y > vector2Y * vector1X;
		else if (vector2Y < 0)
			return true;
		else {
			if (vector2X == 0 && vector1X == 0) return vector2Y > vector1Y;
			vector1Y *= vector2X;
			vector2Y *= vector1X;
			return vector1Y == vector2Y ? vector2X > vector1X : vector1Y > vector2Y;
		}
	}

	private int getRightLowestPointIndex(E[] points) {
		int n = points.length;
		int r = 0;
		for (int i = 1; i < n; i++)
			if (getY(points[i]) < getY(points[r]) || getY(points[i]) == getY(points[r]) && getX(points[i]) > getX(points[r]))
				r = i;
		return r;
	}
}

测试程序:

import java.awt.geom.Point2D;
import java.util.ArrayList;

public class Test {
	public static void main(String[] args) {
		double[][] points = {{1, 2.4}, {2.5, 2}, {1.5, 34.5}, {5.5, 6}, {6, 2.4}, {5.5, 9}};
		ArrayList<Point2D> res = new GiftWrapper<double[]>() {
			@Override
			public double getX(double[] point) {
				return point[0];
			}

			@Override
			public double getY(double[] point) {
				return point[1];
			}
		}.getConvexHullPoint2D(points);
		for (Point2D p : res) System.out.printf("(%f,%f)\n", p.getX(), p.getY());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zsc_118

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值