在下面这个文章中的一个问题:
http://topic.csdn.net/u/20110928/15/0ef60bfb-8cb8-471d-a0f0-2b63f6680400.html?seed=1608405708&r=75815377#r_75815377
一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的 相对顺序 比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时间复杂度O(N),空间O(1) 。
想了一种递归的方法:
1、扫描一次数组,找出分界线的位置,p,也就是说,排序完成以后,数组在p以及后面的位置上的所有数字都是正数。
2、第二次扫描由p划分开的两个字数组,这是左边包含的正数的个数和右边包含的负数的个数相等,直接通过一遍扫描可以进行一一对应的互换。为了进行后续的递归操作,这里的互换需要进行特殊的互换,互换的同时对数字取反。
3、对于每一个子数组来说,为了维持数字本来的顺序不变,例如前面的数组刚刚互换过来的负数已经被取反成为了正数,这样只需要递归的对子数组重复1的步骤进行排序即可。
4、所有的递归完成以后,将左边的所有数字置为负数,右边的所有数字置为正数。over
代码如下:
package test;
import java.util.Random;
public class SortFushu {
int negs = 0;
int len = 0;
int first = 0, last = 0;
int start = 0, end = 0;
void sort(int[] d, int start, int end) {
if (start >= end)
return;
len = end - start + 1;
negs = 0;
for (int i = start; i <= end; i++) {
if (d[i] < 0)
negs++;
}
if (negs == 0 || negs == len)
return;
first = start;
last = start + negs;
while (first < start + negs && last <= end) {
if (d[first] > 0 && d[last] < 0) {
int tmp = -d[first];
d[first] = -d[last];
d[last] = tmp;
first++;
last++;
} else {
if (d[first] < 0)
first++;
if (d[last] > 0)
last++;
}
}
int negs1 = negs;
sort(d, start, start + negs - 1);
sort(d, start + negs1, end);
}
void sort(int[] d) {
int tag = 0;
for (int i = 0; i < d.length; i++) {
if (d[i] < 0)
tag++;
}
sort(d, 0, d.length - 1);
for (int i = 0; i < tag; i++) {
d[i] = -1 * Math.abs(d[i]);
}
for (int i = tag; i < d.length; i++) {
d[i] = Math.abs(d[i]);
}
}
void sort1(int[] d) {
int[] d1 = new int[d.length];
System.arraycopy(d, 0, d1, 0, d.length);
for (int i = 0; i < d.length; i++) {
if (d[i] < 0)
negs++;
}
first = 0;
last = negs;
for (int i = 0; i < d1.length; i++) {
if (d1[i] < 0)
d[first++] = d1[i];
else
d[last++] = d1[i];
}
}
public static void main(String[] args) {
// int[] d = { -1, 2, 3, 4, -3, 9, -5, 8, 9, -12, 15 };
int n = 90000000;
int[] d1 = new int[n];
Random r = new Random();
for (int i = 0; i < d1.length; i++) {
d1[i] = 0;
while (d1[i] == 0) {
d1[i] = r.nextInt();
}
}
long time = System.currentTimeMillis();
new SortFushu().sort(d1);
System.out.println(System.currentTimeMillis() - time);
// for (int i = 0; i < d.length; i++) {
// System.out.print(d[i] + "\t");
// }
// System.out.println();
}
}
时间复杂度:F[n]=n+2*f[n/2] O(nlogn)
空间复杂度:G[n]=1+G[n/2] O(logn)
上面的方法sort1实现了一个简单的做法:使用另外的一个一样大小的数组,首先复制,然后通过两次的扫描完成排序
时间复杂度: O(n)
空间复杂度: O(n)
两种算法的比较:
时间上来说:
计算10000000数组大小的排序时间:
sort:4498ms
sort1:239ms
可见sort1要快很多
空间占用来说,对java虚拟机设置最大内存为-Xmx512m
sort:能够计算的最大的数组长度为:8900,0000(数组本身占用的空间为356M)
sort1:能够计算的最大的数组长度为:4350,0000(数组本身占用的空间为174M)
可见sort算法空间利用率更高,相同的条件下更够计算更大的数据。
附录,关于时间复杂度的计算主方法:
http://en.wikipedia.org/wiki/Master_theorem