算法导论之2.3-7练习题

题目:给出一个Θ(nlgn)时间的算法。判断在集合S中,是否存在两个元素的和为x。


算法导论的教师手册解法如下:

1.对集合S排序。
2.创建集合T = {z : z = x − y ,y ∈ S}。
3.对集合T排序。
4.去除S和T中的重复元素。
5.按照从小到大顺序或者从大到小顺序合并两个集合
6.对于合并以后的集合而言,当且仅当集合中的相邻位置出现相等元素,则存在,否则不存在。

这个解法的大致含义:假设集合T中存在一个元素w,那么w = x - y,而集合S中存在y,由于集合中已经没有重复的元素,如果集合S与集合T中的元素合并,相邻位置出现相等元素, 则表明集合S中存在w,因此w + y = x,则存在。


由于所需时间复杂度为O(nlogn),所以我们用归并排序,Java代码如下:

private static void mergeSort(int[] arr, int lo, int hi) {
		if (lo < hi) {
			int mi = (lo + hi) / 2;
			mergeSort(arr, lo, mi);
			mergeSort(arr, mi + 1, hi);
			rewriteMerge(arr, lo, mi, hi);
		}
	}

	// 不使用哨兵的方法
	private static void rewriteMerge(int[] arr, int lo, int mi, int hi) {
		int frontLength = mi - lo + 1;
		int backLength = hi - mi;

		int[] l = new int[frontLength];
		int[] r = new int[backLength];

		for (int i = 0; i < frontLength; i++)
			l[i] = arr[lo + i];

		for (int i = 0; i < backLength; i++)
			r[i] = arr[mi + 1 + i];

		int lp = 0;
		int rp = 0;

		for (int i = lo; i <= hi; i++) {
			if (lp == frontLength)
				break;
			if (rp == backLength) {
				arr[i] = l[lp];
				lp++;
				continue;
			}
			if (l[lp] < r[rp]) {
				arr[i] = l[lp];
				lp++;
			} else {
				arr[i] = r[rp];
				rp++;
			}
		}
	}

So step one 的时间复杂度为O(nlogn)。

我把step two and step three 合并在一个方法中了,Java代码如下:

private static int[] createAndSort(int[] a, int aLength, int x) {
		int[] aCopy = new int[aLength];
		for (int i = 0; i < aCopy.length; i++)
			// 我将第三步合并到这,由于a数组是有序的,通过x减去a中的最大值,放在a副本中的最小位置上
			aCopy[i] = x - a[aLength - 1 - i];
		return aCopy;
	}

上面这个方法时间复杂度为O(n)。

step 4 Java代码如下:

private static int[] distinctArray(int[] a) {
		//定义一个临时的数组
		int[] temp = new int[a.length];
		
		//将数组中的重复元素都置为整数的最小值
		for (int i = 0; i < a.length - 1; i++)
			if (a[i] == a[i + 1])
				a[i] = Integer.MIN_VALUE;

		//定义指向temp的指针,去除数组a中的重复值,将其保存到数组temp中
		//temp数组中的后面的元素有可能为空
		int pot = 0;
		for (int i = 0; i < a.length; i++)
			if (a[i] != Integer.MIN_VALUE)
				temp[pot++] = a[i];

		if (pot != a.length) { //如果有重复元素,去除temp数组中的空元素
			int[] result = new int[pot];
			for (int i = 0; i < result.length; i++)
				result[i] = temp[i];
			return result;
		} else //如果没有重复元素,则直接返回temp数组
			return temp;
	}

上面这个方法时间复杂度为O(n)。

Step 5 Java代码如下:

private static int[] mergeDoubleArray(int[] a, int[] aCopy) {
		int alength = a.length;
		int acopylength = aCopy.length;

		int[] ret = new int[alength + acopylength];

		for (int i = 0; i < alength; i++)
			ret[i] = a[i];

		for (int i = 0; i < acopylength; i++)
			ret[alength + i] = aCopy[i];

		rewriteMerge(ret, 0, a.length - 1, ret.length - 1);

		return ret;
	}

上面这个方法时间复杂度为O(n)。

Step 6 Java代码如下:

private static boolean validate(int[] result) {
		boolean isExist = false;
		for (int i = 0; i < result.length - 1; i++) {
			if (result[i] == result[i + 1])
				return true;
		}
		return isExist;
	}

上面这个方法时间复杂度为O(n)。

主方法Java代码如下:

public static void main(String[] args) {
		int[] a = { 7, 4, 3, 34, 56, 34, 1 };
		int aLength = a.length;
		int x = 12;

		// step one
		mergeSort(a, 0, aLength - 1);

		// step two and step three
		int[] aCopy = createAndSort(a, aLength, x);

		// step four
		a = distinctArray(a);
		aCopy = distinctArray(aCopy);

		// step five
		int[] result = mergeDoubleArray(a, aCopy);

		// step six
		boolean isExist = validate(result);

		System.out.println(isExist);
}

综上所述:总共所需时间为:O(nlogn) + O(n) + O(n) + O(n) + O(n) = O(nlogn)


方案二如下:

public static void main(String[] args) {
	int[] b = { 6,6,1};
		int bLength = b.length;
		int y = 12;
		
		//用归并排序为数组b排序
		mergeSort(b, 0, bLength - 1);
		
		for (int i = 0; i < b.length; i++) {
			Integer res = IterativeBinarySearch(b, y - b[i], 0, b.length - 1);
			if ( res != -1 && res.intValue() != i ) {
				System.out.println(true);
				return;
			}
		}
		
		System.out.println(false);
}

二分查找算法,我就不列在这里了。这个算法很简单,算法时间复杂度为O(nlogn)。

但我个人觉得,如果令x = 12,而集合S中存在一个元素6(2个元素6正确),这种算法将会导致错误的结果。希望大家看看有什么好的建议来解决这个Bug,期待您的评论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值