我想对整数进行排序。因此,我将它们解释为长度为32的二进制字符串。现在,我可以为每个组件应用bucketsort了。这是我在Java中的实现:
public static void sort(List<Integer> numbers)
{
Queue<Integer> tmp =new LinkedList<Integer>();
for (int i = 0; i < numbers.size(); i++) {
tmp.offer(numbers.get(i));
}
List<Integer>[] bucket = new ArrayList[2];
for (int i = 0; i < bucket.length; i++) {
bucket[i] = new ArrayList<Integer>();
}
for (int k = 32; k > 0; k--) {
// Clear
for (int i = 0; i < bucket.length; i++) {
bucket[i].clear();
}
while (!tmp.isEmpty()) {
Integer firstel =tmp.element();
String el =String.valueOf(firstel);
if (el.charAt(k - 1) == 0) {
bucket[0].add(firstel);
} else {
bucket[1].add(firstel);
}
tmp.remove();
}
for (int i = 0; i < bucket.length; i++) {
for (Integer j : bucket[i]) {
tmp.add(j);
}
}
}
}
我不确定我的代码是否正确处理了这些二进制字符串。我是否必须将列表编号中的整数转换为二进制字符串? 注意:仅用于练习。时间复杂度没有更深的认识。
答案 0 :(得分:1)
首先让我们重写一下,因为不需要Queue或tmp arraylist等。第一步,一个存根类,使我们可以编写更少的代码:
private class Numlist extends ArrayList<Integer> {
Numlist() { super(); }
}
完成,我们不再需要到处写ArrayList<Integer>
。
现在,java会执行“自动装箱”,因此您存储Integer的所有内容都可以int
执行,反之亦然。这很方便,因为我们可以丢弃所有字符串内容。如果我们只关心位比较,就不需要它:
public void sort(Numlist numbers) {
// No copying `numbers` to a `tmp` queue: just work with it directly.
Numlist zeroes, ones;
for (int k = 1; k < 32; k++) {
// Build this step's masking value, which we can use to
// find the value of individual bits by using bitwise AND.
// Also note that we _know_ this is a safe integer operation.
mask = (int) Math.pow(2,k);
// just allocate new lists; it's about as fast as clearing.
zeroes = new Numlist();
ones = new Numlist();
// "scatter": now we empty the numbers list one element at a
// time, and then we'll fill it back up after we emptied it.
while (!numbers.isEmpty()) {
int e = numbers.remove(0);
if ((e & mask) == mask) {
ones.add(e);
} else {
zeroes.add(e);
}
}
// "gather"
for (int i: zeroes) { numbers.add(i); }
for (int i: ones) { numbers.add(i); }
}
}
通过重写,一切正常。我们删除了很多冗长的细节,这使代码工作变得更容易推断,并且删除了整个“ int到字符串到子字符串,然后进行char比较”,这意味着出错的地方更少了。
在测试方面,将您的函数添加到以下类中:
import java.lang.Math;
import java.util.ArrayList;
public class Test {
// private class goes here
public static void main(String[] argv) { new Test(); }
public Test() {
Numlist list = new Numlist();
list.add(10);
list.add(77810);
list.add(4);
list.add(100);
list.add(1);
list.add(290387423);
list.add(23423);
sort(list);
System.out.println(list);
}
// function goes here
}
并完成:javac
将很高兴地对其进行编译,执行应产生[1, 4, 10, 100, 23423, 77810, 290387423]
您还将注意到我们没有使用for (int k = 31; k > 0; k--)
,但是我们正在使用for (int k=1; k<32; k++)
...为什么?有区别吗?
这有很大的不同
通过将掩码从b000 ... 001设置为b100 ... 000,我们保证尽管“取回值”,它们的相对“小于当前位”的顺序仍然保留。
如果以另一种方式从b1000 ... 000到b000 ... 001运行蒙版,则在每一步我们都将撤消刚刚设置的顺序,结果根本不是排序列表:[1, 4, 100, 77810, 10, 290387423, 23423]
**编辑**:屏蔽为何起作用?
整数类型byte
,char
,int
和long
分别只是1、2、4和8个字节的位模式,因此所有这些已经是“二进制数”,我们只需要一种访问单个位的方法,我们可以使用bitwise masking来实现。
为了掩盖“除特定位之外的所有内容”,您可以采用某些位模式的按位与,以及仅设置一个位的模式,我们可以很容易地创建 ,因为这些仅仅是“ 2的幂”。
例如,要检查数字23中的位,我们可以使用以下检查:
23 & 1 2 4 8 16 32
b0 1 & 1=1 0=0 0=0 0=0 0=0 0=0
b1 1 & 0=0 1=1 0=0 0=0 0=0 0=0
b2 1 & 0=0 0=0 1=1 0=0 0=0 0=0
b3 0 & 0=0 0=0 0=0 1=0 0=0 0=0
b4 1 & 0=0 0=0 0=0 0=0 1=1 0=0
b5 0 & 0=0 0=0 0=0 0=0 0=0 1=0
在这里,我们可以看到数字23,二进制10111
以及被2的幂乘以23:1所掩盖的结果为1,所以我们知道第一位已设置。我们看到23&2产生2,所以我们知道第二位被设置。对于4,相同,但23和8的结果为0。我们知道第四位未设置 。
因此,我们可以使用按位AND来检查任何长度的位模式:如果(number & mask) == mask
我们知道该掩码的位已设置。如果结果改为0,则表明该位未设置。
此外,请注意,&
与&&
是不相同:&
是按位AND运算,对两个位之间的每个位执行AND操作员的左侧和右侧。 &&
运算符为逻辑 AND,要求左侧和右侧为布尔值。逻辑“与”实际上是“单个与”,而按位“与”则是“与要测试的位一样多的与”运算。
答案 1 :(得分:-1)
Java已经提供了一个方便的api作为Collections框架的一部分。 Collections.sort(numbers); 这将按升序对整数进行排序。如果需要不同的订购,则可以使用另一个带有Comparator的API。