/*
* 文 件 名: Sort.java
* 修改时间: 2012-12-27
*/
package sort;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
/**
* 各种排序算法
*
* @version [版本号, 2012-12-27]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class Sort
{
/**
* 主函数
*
* @param args
* @see [类、类#方法、类#成员]
*/
public static void main(String[] args)
{
Scanner scanner = new Scanner(System.in);
Random random = new Random();
System.out.println("请输入排序的整数个数:");
int n = scanner.nextInt();
int[] array = new int[n];
for (int i = 0; i < n; i++)
{
array[i] = random.nextInt(1000);
}
// 初始化完毕
// System.out.println(shellSort(array).toString());
System.out.println(shell(array).toString());
// System.out.println(insert(array).toString());
System.out.println(merge(array).toString());
System.out.println(quickSort(array).toString());
System.out.println(arraySort(array).toString());
// System.out.println(shell(array).toString());
// System.out.println(bubble(array).toString());
// System.out.println(choose(array).toString());
// System.out.println(insert(array).toString());
// int[] result = quickSort(array).result;
// for (int i = 0; i < result.length; i++)
// {
// System.out.print(result[i] + ",");
// }
}
/**
*
* @param iArray
*/
public static Result shellSort(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("网上希尔排序");
int i, j, h = 1, temp;
while (h <= n / 3)
h = h * 3 + 1;
while (h > 0)
{
for (i = h; i < n; i += h)
{
temp = arrayTemp[i];
j = i;
while (j >= h && arrayTemp[j - h] >= temp)
{
arrayTemp[j] = arrayTemp[j - h];
j -= h;
}
arrayTemp[j] = temp;
}
h = (h - 1) / 3;
}
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 冒泡排序
*
* @param array
* @see [类、类#方法、类#成员]
*/
public static Result bubble(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("冒泡排序");
result.count = n;
for (int i = n - 1; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
if (arrayTemp[j] > arrayTemp[j + 1])
{
int temp = arrayTemp[j];
arrayTemp[j] = arrayTemp[j + 1];
arrayTemp[j + 1] = temp;
result.move++;
}
result.compare++;
}
}
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 选择排序算法
*
* @return
* @see [类、类#方法、类#成员]
*/
public static Result choose(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("选择排序");
result.count = n;
for (int i = 0; i < n - 1; i++)
{
int pos = i;
for (int j = i + 1; j < n; j++)
{
if (arrayTemp[i] > arrayTemp[j])
{
pos = j;
}
result.compare++;
}
if (pos != i)
{
int temp;
temp = array[i];
array[i] = array[pos];
array[pos] = temp;
result.move++;
}
}
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 插入排序算法
*
* @param array
* @return
* @see [类、类#方法、类#成员]
*/
public static Result insert(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("插入排序");
result.count = n;
int temp, j;
for (int i = 1; i < n; i++)
{
temp = arrayTemp[i];
for (j = i - 1; j >= 0 && arrayTemp[j] > temp; j--)
{
arrayTemp[j + 1] = arrayTemp[j];
}
arrayTemp[j + 1] = temp;
}
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 归并排序算法,使用递归实现
*
* @param array
* @return
* @see [类、类#方法、类#成员]
*/
public static Result merge(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
int[] arrayExtra = new int[n];// 额外的数组
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("归并排序");
result.count = n;
result.space += n;
merge(arrayTemp, 0, arrayTemp.length - 1, result, arrayExtra);
result.result = arrayTemp;
result.time = System.currentTimeMillis() - result.time;
return result;
}
/**
* 归并排序节约内存版,这种方法在递归过程中申请额外内存,可能造成内存 不足,但是速度和下面那种差不多
*/
// private static void merge(int[] array, int start, int stop, Result
// result) {
// int n = stop - start + 1;
// if (n > 1) {
//
// merge(array, start, start + n / 2 - 1, result);
// merge(array, start + n / 2, stop, result);
// int[] temp = new int[n];
// result.space += n;
// int l = start;
// int r = start + n / 2;
// for (int i = 0; i < n; i++) {
// if (l == start + n / 2) {
// temp[i] = array[r++];
// } else if (r == stop + 1) {
// temp[i] = array[l++];
// } else if (array[l] > array[r]) {
// temp[i] = array[r++];
// } else if (array[l] < array[r]) {
// temp[i] = array[l++];
// }
// result.compare++;
// result.move++;
// }
// System.arraycopy(temp, 0, array, start, n);
// result.move++;
// }
// }
/**
* 归并排序更节约内存版,该方法一次性申请一块和原数组同样大的内存,不用在递归过程中申请,可以节约内存
*/
private static void merge(int[] array, int start, int stop, Result result, int[] arrayExtra)
{
int n = stop - start + 1;
if (n > 1)
{
merge(array, start, start + n / 2 - 1, result, arrayExtra);
merge(array, start + n / 2, stop, result, arrayExtra);
int l = start;
int r = start + n / 2;
for (int i = start; i <= stop; i++)
{
if (l == start + n / 2)
{
arrayExtra[i] = array[r++];
}
else if (r == stop + 1)
{
arrayExtra[i] = array[l++];
}
else if (array[l] >= array[r])
{
arrayExtra[i] = array[r++];
}
else if (array[l] <= array[r])
{
arrayExtra[i] = array[l++];
}
result.compare++;
result.move++;
}
System.arraycopy(arrayExtra, start, array, start, n);
result.move++;
}
}
/**
* 原地归并算法。。。
* 不需要额外数组,类似插入算法,但不是单个元素的移动,是整体移动,不知道效率如何 需要少量的额外内存
* 在Java中这种方式的效率太低了,还不如一次性申请大内存好,如果有C++的内存反转的话 空间复杂度为O(1)
*/
private static void merge(int[] array, int start, int stop, Result result)
{
int n = stop - start + 1;
if (n > 1)
{
merge(array, start, start + n / 2 - 1, result);
merge(array, start + n / 2, stop, result);
int l = start;
int r = start + n / 2;
int index = r;
while (l < r && r <= stop)
{
// 先找到左边的插入点l,右边的内存块需要查到该元素之前
if (array[l] > array[r])
{
// 再找到右边的区间r到index
while (index <= stop)
{
if (index == stop || array[index + 1] > array[l])
{
//这里是最重要的反转内存操作,由于Java语言的限制,只能使用最小临时内存区实现
//首先比较一下左边待移动的内存块和右边待移动的内存块的大小
int lSize = r - l;
int rSize = index - r + 1;
if (lSize > rSize)
{
int[] temp = new int[rSize];
result.space += rSize;
//将小内存块存入临时内存中
System.arraycopy(array, r, temp, 0, rSize);
//然后移动大的内存块
System.arraycopy(array, l, array, l + rSize, lSize);
//最后将临时内存中得数据还原
System.arraycopy(temp, 0, array, l, rSize);
result.move += 3;
}
else
{
int[] temp = new int[lSize];
result.space += lSize;
//将小内存块存入临时内存中
System.arraycopy(array, l, temp, 0, lSize);
//然后移动大的内存块
System.arraycopy(array, r, array, l, rSize);
//最后将临时内存中得数据还原
System.arraycopy(temp, 0, array, index - lSize + 1, lSize);
result.move += 3;
}
l += rSize;
r = ++index;
}
else
{
index++;
}
}
}
else
{
l++;
}
}
}
}
/**
* 希尔排序,插入排序的改进版本
* @param array
* @return
* @see [类、类#方法、类#成员]
*/
public static Result shell(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("希尔排序");
result.count = n;
//希尔排序算法,其实就是插入排序算法的改进版本,其中有个增量,插入排序其实就是增量为1的希尔排序
//希尔排序增量从大到小最后减为1进行插入排序即可
//先求出第一个增量
int increment = 1;
while (increment < n)
{
if (3 * increment + 1 > n)
{
break;
}
else
{
increment = 3 * increment + 1;
}
}
while (increment >= 1)
{
//每条线都是从h开始,下一个元素为h+increment,直到最后一个元素索引小于n
for (int i = increment; i < n; i++)
{
int temp = arrayTemp[i];
int j;
for (j = i - increment; j >= 0 && arrayTemp[j] > temp; j -= increment)
{
arrayTemp[j + increment] = arrayTemp[j];
}
arrayTemp[j + increment] = temp;
}
increment = (increment - 1) / 3;
}
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 快速排序算法
* @param array
* @return
* @see [类、类#方法、类#成员]
*/
public static Result quickSort(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("快速排序");
result.count = n;
//这里是具体的算法
quickSort(arrayTemp, 0, n - 1);
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 快速排序的递归算法,总是选取right索引处的值作为枢纽
* @param array
* @param left
* @param right
* @see [类、类#方法、类#成员]
*/
private static void quickSort(int[] array, int left, int right)
{
if (left < right)
{
int partition = partition(array, left, right);
quickSort(array, left, partition - 1);
quickSort(array, partition + 1, right);
}
}
/**
* 划分算法,关键字为数组第一个元素
* @param array
* @param left
* @param right
* @return
* @see [类、类#方法、类#成员]
*/
private static int partition(int[] array, int left, int right)
{
int i = left, j = right;
int pivot = array[i];
while (i < j)
{
while (i < j && array[j] >= pivot)
{
j--;
}
if (i < j)
{
array[i++] = array[j];
}
while (i < j && array[i] <= pivot)
{
i++;
}
if (i < j)
{
array[j--] = array[i];
}
}
array[i] = pivot;
return i;
}
/**
* JAVA自带的排序算法
* @param array
* @return
* @see [类、类#方法、类#成员]
*/
public static Result arraySort(int[] array)
{
if (null == array)
{
throw new NullPointerException();
}
int n = array.length;
int[] arrayTemp = new int[n];
System.arraycopy(array, 0, arrayTemp, 0, n);
Result result = new Result("java自带排序");
result.count = n;
Arrays.sort(arrayTemp);
result.time = System.currentTimeMillis() - result.time;
result.result = arrayTemp;
return result;
}
/**
* 求得归并排序n个元素所需的额外内存
*
* @param n
* @return
* @see [类、类#方法、类#成员]
*/
public static int getMergeMemory(int n)
{
if (n <= 1)
{
return 0;
}
else
{
return n + getMergeMemory(n / 2) + getMergeMemory(n - n / 2);
}
}
static class Result
{
String name;
long time;
long compare;
long move;
long count;
int[] result;
long space;// 额外内存开销
public Result(String name)
{
this.name = name;
time = System.currentTimeMillis();
compare = 0;
move = 0;
space = 0;
}
/** {@inheritDoc} */
@Override
public String toString()
{
return name + "[额外内存=" + space + ",元素个数=" + count + ",比较次数=" + compare + ", 移动次数=" + move + ", 消耗时间="
+ time + "]";
}
}
}
不用说,归并排序的效率肯定最高,但是额外的内存开销也很大,本人机器最多能排1480万-1490万条数据,和机器内存以及虚拟机内存相关,时间大约在5秒-6秒,冒泡,选择和插入的效率就不用看了,数量级刚到10万时间已经突破30秒,再大的数量级肯定以指数级增长了。。
下面总结下:1、冒泡、选择、插入排序算法都不需要额外的内存开销,但是效率很低,比较和移动的次数太多,导致排10W以上的数据时间很长,都在半分钟左右,排100W的数据估计可以等几个小时了。。
2、普通归并排序的效率极高,1500W的数据量排序,时间在5秒左右,但是需要额外的一倍内存。
3、使用Java不能实现原地归并排序,因为控制不了内存地址,无法做内存的反转,C++的原地归并比普通归并慢大约30倍,如果内存要求高,而时间要求不是很高的情况下,使用原地归并也是不错的选择。
4、希尔排序不需要额外内存,而且也没有递归,个人感觉是比较好的排序方式,大数据量排序性能也不错。
5、快速排序虽然没有额外内存,但是用到了递归,500W以上的数据量会造成堆栈溢出,而且在数据倒序情况下性能退化的厉害,不是很稳定,需要改进基准值的设置。
6、JAVA自带的排序算法是所有排序算法中性能最好的,他是改进的快速排序算法,而且当数据元素个数小于7时,使用了插入排序消除了一部分递归操作。2200W数据量才1800毫秒。。这是别的排序算法无法达到的。