《算法(第四版)》1.4.15快速3-sum,练习题学习小结

使用一个线性级别的算法(而非基于二分查找的线性对数级别的算法)实现TwoSumFaster来计算一排序的数组中和为0的整数对的数量。用相同的思想为3-sum问题给出一个平方级别的算法。

线性级别的算法增长的数量级为N,常见的是使用一层for循环。对于顺序排列的数组,只要从数组两端向中间逼近匹配即可。若a[head] + a[tail] > 0,则tail – ;若a[head] + a[tail] < 0,则head ++。就好比两个人从两地出发的相遇问题,两人的总行程为数组长度。关键代码如下:

public long twoCount(int[] a) {
		Arrays.sort(a);
		int N = a.length;
		long cnt = 0;
		for(int i = 0, head = 0, tail = N - 1; i < N; i ++) {
			if(a[head] + a[tail] >= 0) {
				if(a[head] + a[tail] == 0)
					cnt ++;
				tail --;
			} else
				head ++;
		}
		return cnt;
}

对于3-sum,同理,先确定第一个数字a[i](第一层for循环),再从头a[i + 1]和尾a[N-1]中确定另外两个数字(第二层for循环)。该算法是平方级别的算法,增长的数量级为N2。关键代码如下:

public long threeCount(int[] a) {
		Arrays.sort(a);
		int N = a.length;
		long cnt = 0;
		for(int i = 0; i < N; i ++) {
			for(int j = i + 1, head = i + 1, tail = N - 1; j < N; j++) {
				if(a[head] + a[tail] + a[i] >= 0) {
					if(a[head] + a[tail] + a[i] == 0)
						cnt ++;
					tail --;
				} else
					head ++;
			}
		}
		return cnt;
}

以上程序在求和时未考虑int溢出的情况,在计数的时候存在溢出的可能,故cnt的类型为long。threeCount毕竟是平方级别的算法,解决100万个整数(1Mintts.txt)花费了半个多小时的时间。
在这里插入图片描述


特别感谢网友emergency_rose的指正!
存在缺陷1:当数列中出现重复元素的时候,上述的方法将可能出现遗漏某些整数对的情况。因为tail --总是出现在head ++之前,这时数列“尾部”出现重复元素时,可以正常计算整数对;而当数列“头部”出现重复元素时,将出现遗漏整数对的情况。所以,当head ++操作时需要判断a[head ++] == a[head],若重复,则整数对直接加上与a[head]匹配的对数。
存在缺陷2:当head与tail相遇后,for循环应该停止,否则整数对将出现重复。
则有twoCount代码如下,3-sum同理。

public long twoCount(int[] a) {
		Arrays.sort(a);
		int N = a.length;
		long cnt = 0;
		long emp = 0;
		for(int i = 0, head = 0, tail = N - 1; i < N && head > tail; i ++) {
			if(a[head] + a[tail] >= 0) {
				if(a[head] + a[tail] == 0) {
					cnt ++; emp ++;
				}
				tail --;
			} else if(a[head] == a[head ++]){
				cnt += emp; emp = 0;
			}
		}
		return cnt;
}

特别感谢网友m0_56157806的指正!
当重复元素大于2使,emp会被过早清零,则有

public static long twoCount(int[] a) {
        Arrays.sort(a);
        int N = a.length;
        long cnt = 0;
        long emp = 0;
        for(int i = 0, head = 0, tail = N - 1; i < N && head < tail; i ++) {
            if(a[head] + a[tail] >= 0) {
                if(a[head] + a[tail] == 0) {
                    cnt ++; emp ++;
                }
                tail --;
            } else if(a[head] == a[head +1]){
                cnt += emp;
                if(head+2 > tail && a[head + 1] != a[head + 2])
                    emp = 0;
                head ++;
            }
        }
        return cnt;
    }

《算法(第四版)》部分练习题索引

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值