Algorithm之3sum closest及负数在Java中的表示

[b][size=medium][color=green]由一道算法题想到的[/color][/size][/b]

[b]1、16. 3Sum Closest[/b]


Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

For example, given array S = {-1 2 1 -4}, and target = 1.

The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).


代码:



import java.util.Arrays;

public class Solution {
public int threeSumClosest(int[] num, int target) {
int min = num[0] + num[1] + num[num.length -1];
Arrays.sort(num);
for(int i = 0; i < num.length -2; i++){
int lo = i + 1, hi = num.length - 1;
while(lo < hi){
int sum = num[i] + num[lo] + num[hi];
if(Math.abs(sum - target) < Math.abs(min - target))
min = sum;
if(sum >= target) hi--;
else lo++;
}
}
return min;
}
}




时间复杂度为 O(n^2)
为了加快运算效率,我想保存前一个的状态值,记为: preDelta
如果当前 delta 与 preDelta 的符号相同,且 delta 比 preDelta 的绝对值大,
则 break;

但是,在使用异或运算符( preDelta^delta )时,总是得不到想要的结果。
于是回查了一下负数在Java中是如何表示的。又回顾了一下基础知识。

——————————————————————————————————————

[quote]
[b]什么是2的补码?[/b]

它是一种数值的转换方法,要分二步完成:
第一步,每一个二进制位都取相反值,0变成1,1变成0。比如,00001000的相反值就是11110111。
第二步,将上一步得到的值加1。11110111就变成11111000。
所以,00001000的2的补码就是11111000。也就是说,-8在计算机(8位机)中就是用11111000表示。
不知道你怎么看,反正我觉得很奇怪,为什么要采用这么麻烦的方式表示负数,更直觉的方式难道不好吗?


[b]2的补码的好处[/b]

首先,要明确一点。计算机内部用什么方式表示负数,其实是无所谓的。只要能够保持一一对应的关系,就可以用任意方式表示负数。所以,既然可以任意选择,那么理应选择一种最方便的方式。
2的补码就是最方便的方式。它的便利体现在,所有的加法运算可以使用同一种电路完成。
还是以-8作为例子。
假定有两种表示方法。一种是直觉表示法,即10001000;另一种是2的补码表示法,即11111000。请问哪一种表示法在加法运算中更方便?
随便写一个计算式,16 + (-8) = ?
16的二进制表示是 00010000,所以用直觉表示法,加法就要写成:
 00010000
+10001000
---------
 10011000
可以看到,如果按照正常的加法规则,就会得到10011000的结果,转成十进制就是-24。显然,这是错误的答案。也就是说,在这种情况下,正常的加法规则不适用于正数与负数的加法,因此必须制定两套运算规则,一套用于正数加正数,还有一套用于正数加负数。从电路上说,就是必须为加法运算做两种电路。
现在,再来看2的补码表示法。
 00010000
+11111000
---------
100001000
可以看到,按照正常的加法规则,得到的结果是100001000。注意,这是一个9位的二进制数。我们已经假定这是一台8位机,因此最高的第9位是一个溢出位,会被自动舍去。所以,结果就变成了00001000,转成十进制正好是8,也就是16 + (-8) 的正确答案。这说明了,2的补码表示法可以将加法运算规则,扩展到整个整数集,从而用一套电路就可以实现全部整数的加法。

[/quote]
——————————————————————————————————————


所有的数在Java中[b]都是[/b]以其二进制的[b]补码[/b]形式存放的。

正数的补码是其自身
负数的补码是:

负数的绝对值按位取反后 + 1


可以看到:(-0)


/*

在做减法的情况下:


00000000
=
11111111

+ 00000001

---------------------------------------

100000000

舍掉最高位的溢出,结果还是 0


因为是做减法(加一个负数),如果不够减需要借 1 位。
所以把负数拆分成: 绝对值取反 + 1 这种形式来表示。
把这种表示方式称为:负数的二进制表示法的补码。简称:二进制补码

得到的结果就是代表正数的负值——负数。
然后就可以对负值进行加法运算了。
奇怪的是,这样做加法运算,居然就是正确的。


下面的步骤可以实现:正数 + 负的正数 = 0

1、正数 + 正数各位取反 = 最大值(所有位为 1)

2、(所有位为 1) + 1 = 0 (舍去溢出位)

--------------------------------------------------

综合起来:

正数 + 正数各位取反 + 1 = 0

正数 + (正数各位取反 + 1) = 0

正数 + (补码) = 0

正数 + (负数) = 0


--------------------------------------------------

上面只是 正数 + 负数 ,(两数绝对值相等)
还有 大正数 + 负小正数,
还有 负数 + 负数 的情况,不在此证明。

*/



这种表示法可以使计算器只用加法一种电路就可以完成加减乘除四种运算。
(除法是减法,乘法是加法)


引用:

https://leetcode.com/problems/3sum-closest/

http://www.ruanyifeng.com/blog/2009/08/twos_complement.html


-
发布了279 篇原创文章 · 获赞 4 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章