算法Homework
内容一 几种排序的比较
- 分别针对随机生成的若干组整数序列(比 如规模为1000个数,10000个数,100000 个数)进行排序,排序算法使用四种方法, 至少包括以下三种经典方法:插入排序算 法、合并排序算法和快速排序算法、近些年提出的比较新颖的排序算法
- 统计各种情况下排序所耗费的时间
- 改为降序
- 10个数,在使用快速排序进行排序时,尝试给出数组的演化情况
- 稳定性与空间性能
选择的第四个排序
侏儒排序
侏儒排序(英语:Gnome Sort)或愚人排序(英语:Stupid Sort)最初在2000年由伊朗计算机工程师Hamid Sarbazi-Azad提出,他称之为“愚人排序”。此后Dick_Grund也描述了这一算法,称其为“侏儒排序”。此算法类似于插入排序,但是移动元素到它该去的位置是通过一系列类似冒泡排序的移动实现的。从概念上讲侏儒排序非常简单,甚至不需要嵌套循环。
解释
下面是侏儒排序的伪代码
procedure gnomeSort(a[]):
pos := 0
while pos < length(a):
if (pos == 0 or a[pos] >= a[pos-1]):
pos := pos + 1
else:
swap a[pos] and a[pos-1]
pos := pos - 1
样例
给定一个未排序的数组a = [5, 3, 2, 4],侏儒排序在while循环中执行以下步骤。粗体表示pos变量当前所指的元素。
当前数组 | 下一步操作 |
---|---|
[5, 3, 2, 4] | a[pos] < a[pos-1],交换 |
[3, 5, 2, 4] | a[pos] >= a[pos-1],pos自增 |
[3, 5, 2, 4] | a[pos] < a[pos-1],交换;pos > 1,pos自减 |
[3, 2, 5, 4] | a[pos] < a[pos-1],交换;pos <= 1,pos自增 |
[2, 3, 5, 4] | a[pos] >= a[pos-1],pos自增 |
[2, 3, 5, 4] | a[pos] < a[pos-1],交换;pos > 1,pos自减 |
[2, 3, 4, 5] | a[pos] >= a[pos-1],pos自增 |
[2, 3, 4, 5] | a[pos] >= a[pos-1],pos自增 |
[2, 3, 4, 5] | pos == length(a),完成 |
随机数可能用到的拓展知识:
Arrays.stream(ints) 将基本类型数组转换为基本类型流。 int[ ] => IntStream
.boxed() 将基本类型流转换为对象流。 => Stream< Integer >
.collect(Collectors.toList()) 将对象流收集为集合。 => List< Integer >
.toArray(Integer[ ]::new) 将对象流转换为对象数组。=> Integer[ ]
.mapToInt(Integer::valueOf) 将对象流转换成基本类型流。=> IntStream
.toArray() 将基本类型流转换为基本类型数组。 => int[ ]
list.stream() 将列表装换为对象流。List< Integer > => Stream< Integer >
实验过程:
一. 测试性能
1.随机数类,里面有两个方法用于生成随机数
第一个是真随机:int类型里所有整数随机,用于真正测试排序性能
第二个是100以内的随机数:(因为用第一个测试的时候发现生成的数字的位数都太长了,看着费劲)用于简单验证排序的正确性
import java.util.Random;
import java.util.stream.IntStream;
/**
* @author SJ
* @date 2020/10/14
*/
public class RandomUtil {
//真正的int类型的全范围random
public static int[] generateRandomNum(int length){
Random random = new Random();
IntStream ints = random.ints(length);
//int流转为 int数组
return ints.toArray();
}
//int范围内随机生成的数字都太长了,写一个100以内的随机数
public static int[] randomBetween100(int length){
int[] nums=new int[length];
for (int i = 0; i < nums.length; i++) {
nums[i]=(int)(Math.random()*100);
}
return nums;
}
}
- 把四种排序算法封装成了一个类
import java.util.Arrays;
/**
* @author SJ
* @date 2020/10/14
*/
public class SortCompare {
//插入排序
/**
* 假设第一个有序,从前往后遍历依次将得到的数字插入前面的有序序列中
* 不需要辅助空间
*/
public static void insertSort(int[] test) {
int[] nums = Arrays.copyOf(test, test.length);
for (int i = 1; i < nums.length; i++) {
int current = nums[i];//记录待插入数据
int preIndex = i - 1;//记录有序数组最后一个数字的位置
//挪位
while (preIndex >= 0 && current < nums[preIndex]) {
nums[preIndex + 1] = nums[preIndex];
preIndex--;
}
nums[preIndex + 1] = current;
}
System.out.println(Arrays.toString(nums));
}
//归并排序
/**
* 二路归并排序
* 包含1.两个有序数组合并成一个有序数组
* 2.递归
*/
//设定辅助数组长度
private static int[] temp;
public SortCompare(int length) {
temp = new int[length];
}
//不要在merge函数里构造新数组,因为merge函数会被多次调用,影响性能
private static void merge(int[] nums, int left, int right) {
;
for (int i = left; i <= right; i++) {
temp[i] = nums[i];
}
int middle = (left + right) / 2;
int l = left;
int r = middle + 1;
for (int i = left; i <= right; i++) {
//左边数组里的数字已经合并完,右边还有剩余
if (l == middle + 1 && r < right + 1)
nums[i] = temp[r++];
//右边数组里的数字已经合并完,左边还有剩余
else if (r == right + 1 && l < middle + 1)
nums[i] = temp[l++];
else if (temp[l] <= temp[r])
nums[i] = temp[l++];
else {
nums[i] = temp[r++];
}
}
}
private static void Sort(int[] nums, int left, int right) {
if (left < right) {
Sort(nums, left, (left + right) / 2);
Sort(nums, (left + right) / 2 + 1, right);
merge(nums, left, right);
}
}
public static void mergeSort(int[] test) {
int[] nums = Arrays.copyOf(test, test.length);
Sort(nums, 0, nums.length - 1);
System.out.println(Arrays.toString(nums));
}
//快速排序
/**
* 快速排序
* 以数列第一个基准数,记录下基准数的值,并挖坑,从后往前找比基准数小的转移到坑中,
* 从前往后找比基准数大的填入新坑
* 最后low与high相遇时将基准数填入该处
* 即完成一趟快排。
* 一趟快排的结果是:基准数前面的数字都比它小,基准数后面的数字都比它大
* 然后递归即可
*/
private static void quicksort(int[] nums, int low, int high) {
int base = nums[low];
int l = low;
int h = high;
while (l < h) {
while (l < h && nums[h] >= base)
h--;
if (nums[h] < base)
nums[l++] = nums[h];
while (l < h && nums[l] <= base)
l++;
if (nums[l] > base)
nums[h--] = nums[l];
}
nums[l] = base;
//一开始没加退出条件,陷入死循环了 栈溢出
if (low < l)
quicksort(nums, low, h - 1);
if (high > h)
quicksort(nums, l + 1, high);
}
public static void quickSortUtil(int[] test) {
int[] nums = Arrays.copyOf(test, test.length);
quicksort(nums, 0, nums.length - 1);
System.out.println(Arrays.toString(nums));
}
//侏儒排序
/**
* 在把大的往后挪的同时
* 指针所指的数字的前面那些总是是有序的,中途有交换所以会打乱前面的排序,所以指针会向前挪作调整
*/
public static void stupidSort(int[] test) {
int[] nums = Arrays.copyOf(test, test.length);
int pos = 0;
while (pos < nums.length) {
//如果指针所指的数字大于或者等于前一个,证明有序,指针往后挪
if (pos == 0 || nums[pos] >= nums[pos - 1])
pos++;
//如果前面的数字比自己大,证明无序,就与前面的交换,然后调整
else {
int temp = nums[pos];//辅助空间就要这一个temp
nums[pos] = nums[pos - 1];
nums[pos - 1] = temp;
pos--;
}
}
System.out.println(Arrays.toString(nums));
}
}
- 测试
首先用简单数据测试一下写得各个方法有没有问题
import java.util.Arrays;
import java.util.Scanner;
/**
* @author SJ
* @date 2020/10/14
*/
public class TestValidity {
public static void main(String[] args) {
//如果想在同一个main方法里测试,测试的时候需要注意,要用同一个数组测试不同的排序方法,需要在每个方法里新建副本,用副本测试
//因为java数组是传引用,在每个方法里排序,会直接作用到原数组,再用这个排好序的数组测试下面的排序方法就没有意义了
//也可以选择在junit里测
System.out.println("用小数据测试一下排序代码的正确性:");
System.out.println("输入数组要测试的数组长度;");
Scanner scanner=new Scanner(System.in);
int i = scanner.nextInt();
int[] nums=RandomUtil.randomBetween100(i);
System.out.println("随机生成的数组为:");
System.out.println(Arrays.toString(nums));
System.out.println("输入数组为:"+Arrays.toString(nums));
System.out.println("插入排序结果:");
SortCompare.insertSort(nums);
System.out.println("输入数组为:"+Arrays.toString(nums));
System.out.println("合并排序结果:");
//把合并排序的辅助数组new在外面了,可以提高性能
SortCompare sortCompare = new SortCompare(i);
SortCompare.mergeSort(nums);
System.out.println("输入数组为:"+Arrays.toString(nums));
System.out.println("快速排序结果:");
SortCompare.quickSortUtil(nums);
System.out.println("输入数组为:"+Arrays.toString(nums));
System.out.println("侏儒排序结果:");
SortCompare.stupidSort(nums);
}
}
结果:
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe"...
用小数据测试一下排序代码的正确性:
输入数组要测试的数组长度;
20
随机生成的数组为:
[53, 82, 88, 47, 55, 4, 86, 38, 88, 0, 38, 51, 60, 70, 42, 32, 77, 40, 73, 38]
输入数组为:[53, 82, 88, 47, 55, 4, 86, 38, 88, 0, 38, 51, 60, 70, 42, 32, 77, 40, 73, 38]
插入排序结果:
[0, 4, 32, 38, 38, 38, 40, 42, 47, 51, 53, 55, 60, 70, 73, 77, 82, 86, 88, 88]
输入数组为:[53, 82, 88, 47, 55, 4, 86, 38, 88, 0, 38, 51, 60, 70, 42, 32, 77, 40, 73, 38]
合并排序结果:
[0, 4, 32, 38, 38, 38, 40, 42, 47, 51, 53, 55, 60, 70, 73, 77, 82, 86,