分别实现插入排序、选择排序、堆排序、合并排序、快速排序,以不同规模(100,1000,2000,5000,10000,100000个数据)的随机数作为测试数据集,分别设置比较操作计数器,验证各个算法随着测试数据集增加比较次数的变化趋势。
1.堆排序
package a1排序算法的性能比较;
//构建堆的比较次数:length / 2 - 1
//排序的比较次数:length - 1
//总比较次数为 (length / 2 - 1) + (length - 1)。
public class HeapSort {
//交换
private static void swap(int []arr, int a, int b) {
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
//2.堆的排序
public static int Sort(int[] arr) {
int root;
int comparisons=0;
int length = arr.length;
//1.先建堆-从最后一个非叶子结点开始
//***为什么从最后一个非叶子节点开始建堆呢?
// 因为在完全二叉树中,叶子节点不需要堆化(它们没有子节点),
// 所以从最后一个非叶子节点开始向上堆化是必要的。
// 每个非叶子节点都可能有两个子节点,我们需要确保这些子节点满足最大堆的性质。
//循环是递减的,从最后一个非叶子节点开始,一直堆化到根节点(索引为0)。这样,当循环结束时,整个数组就被转换成了一个最大堆。
// n / 2 - 1 计算的是最后一个非叶子节点的索引
for(root=length-1;root>=0;root--){
//heapify 函数确保以 root 为根的子树满足最大堆的性质,即根节点的值大于或等于其所有子节点的值。
comparisons+=heapify(arr,length,root);
}
//2.排序
//排序
//在建堆完成后,数组的最大值会被放在数组的第一个位置(索引为0)
for (root = length - 1; root > 0; root--) {
//将建好的最大堆的堆顶元素(最大值)与数组中的最后一个元素进行交换 这样就完成了排序的作用
swap(arr, root,0);
//每从堆中取出一个元素 就要维持剩余堆的完整性 需要维护剩余的堆
heapify(arr, root, 0);
comparisons += heapify(arr, root, 0);
}
return comparisons;
}
//1.堆化
private static int heapify(int[] arr,int length,int root) {
int comparisons = 0;
int parent=root;
int left=2*root+1;//计算左子树的索引
int right=2*root+2;//计算右子树的索引
//1.先更新
//检查左子节点是否存在 且 其值大于父节点的值
if(left<length&&arr[parent]<arr[left]){
//满足条件的话,说明左子节点的值比父节点的值大,更新父节点的值
parent=left;
}
comparisons++; // Count the comparison
//检查右子节点是否存在 且 其值小于父节点的值
if(right<length&&arr[parent]<arr[right]){
parent=right;
}
comparisons++; // Count the comparison
//2.后交换
//如果 parent 不等于 root,说明找到了一个子节点的值大于父节点的情况
if(parent!=root){
//不等于说明上面的判断已经将根节点的索引与子节点的索引完成交换
// -现在需要交换节点的值
swap(arr,parent,root);
//并递归地对新的 parent 节点进行堆化
heapify(arr, length, parent);
comparisons += heapify(arr, length, parent);
}
return comparisons;
}
}
2.插入排序
package a1排序算法的性能比较;
import java.math.BigInteger;
public class InsertSort {
public static long Sort(int[] data) {
long comparisons = 0;
for (int i = 1; i < data.length; i++) {
int key = data[i];
int j = i - 1;
// 比较 key 和前面所有元素,如果 key 小于前面元素,则将前面元素向后移动
// 直到找到 key 的正确位置
while (j >= 0 && data[j] > key) {
comparisons++;
data[j + 1] = data[j];
j = j - 1;
}
data[j + 1] = key;
}
return comparisons;
}
}
3.归并排序
package a1排序算法的性能比较;
public class MergeSort {
public static int Sort(int[] data) {
//定义计数器数组
int []comparisons = {0};
if (data == null || data.length < 2) {
return comparisons[0];
}
int[] temp = new int[data.length];
MergeSort(data, 0, data.length - 1, comparisons);
return comparisons[0];
}
//分治数组
private static void MergeSort(int []arr,int low,int high,int []comparisons){
if (low < high) {
//中间点mid
int mid = low + (high - low) / 2;//-----------------------------易错点
//递归分开数组
MergeSort(arr, low, mid,comparisons); //左子数组
MergeSort(arr, mid+1, high,comparisons);//右子数组
//合并数组
Merge(arr, low, mid, high,comparisons);
}
}
//合并/排序数组
private static void Merge(int[] arr, int low, int mid, int high,int []comparisons) {
//计算左侧数组长度
int left = mid - low + 1;
//计算右侧数组长度
int right = high - mid;
//创建临时数组
int [] L= new int[left+1];
int [] R= new int[right+1];
//将分治开的左侧数组拷贝到新数组L中
for (int i = 0; i < left; i++) {
L[i] = arr[low + i];
}
//将分治开的右侧数组拷贝到新数组R中
for (int j = 0; j < right; j++) {
R[j] = arr[mid + 1 + j];
}
int i = 0;// 初始索引left子数组
int j = 0; // 初始索引right子数组
int k = low; // 初始索引合并的arr数组
//对左右两边数组进行排序
while (i < left && j < right) {
//如果左边数组的值小于右边数组的值
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
}//如果右边数组的值小于左边数组的值
else {
arr[k] = R[j];
j++;
}
k++;
comparisons[0]++;
}
//拷贝L的剩余元素
while (i < left) {
arr[k] = L[i];
i++;
k++;
}//拷贝R的剩余元素
while (j < right) {
arr[k] = R[j];
j++;
k++;
}
}
}
4.快速排序
package a1排序算法的性能比较;
public class QuicklySort {
//计数器方法
public static int Sort(int[] data) {
int []comparisons = {0};
quickly(data, 0, data.length - 1, comparisons);
return comparisons[0];
}
//快速排序
private static void quickly(int []arr,int low,int high,int []comparisons){
if(low<high){
//寻找基元素
int mid=part(arr,low,high,comparisons);
quickly(arr,low,mid-1,comparisons);
quickly(arr,mid+1,high,comparisons);
}
}
//寻找基准元素
private static int part(int []arr,int low,int high,int []comparisons){
int i = low, j = high;
//基准元素
int piovt = arr[low];
while (i < j) {
comparisons[0]++;
//基准元素右侧值小于等于基准元素 将其放到基准元素的左边
while (i < j && arr[j] >= piovt) {
//如果遇到的元素>=piovt则j--向左移动 找下一个值
j--;
}
arr[i] = arr[j];
//基准元素左侧值大于基准元素 将其放到基准元素的左边
while (i<j && arr[i]<piovt) {
i++;
}
arr[j] = arr[i];
}
// 将基准元素放到最终位置
arr[i] = piovt;
//返回最终基准元素所在的位置
return i;
}
}
5.选择排序
package a1排序算法的性能比较;
public class SelectSort {
public static int Sort(int[] data) {
int comparisons = 0;
for (int i = 0; i < data.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < data.length; j++) {
comparisons++;
if (data[j] < data[minIndex]) {
minIndex = j;
}
}
int temp = data[minIndex];
data[minIndex] = data[i];
data[i] = temp;
}
return comparisons;
}
}
重点:::比较器和随机数据的添加
package a1排序算法的性能比较;
import java.util.Random;
public class GenRan {
public static void main(String[] args) {
int[] sizes = {100, 1000, 2000, 5000, 10000, 100000};
for (int size : sizes) {
//获取生成的随机数
int[] data = generateRandomData(size);
int[] insertionData = data.clone();
int[] selectionData = data.clone();
int[] heapData = data.clone();
int[] mergeData = data.clone();
int[] quickData = data.clone();
//创建排序对象
InsertSort Insert=new InsertSort();//插入排序
SelectSort Select=new SelectSort();//选择排序
HeapSort Heap =new HeapSort();//选择排序
MergeSort Merge=new MergeSort();//归并排序
QuicklySort Quick=new QuicklySort();//快速排序
//每种排序算法的比较次数
System.out.println("数据个数: " + size);
System.out.println("插入排序比较次数:" + Insert.Sort(insertionData));
System.out.println("选择排序比较次数:" + Select.Sort(selectionData));
System.out.println("堆排序比较次次数:" + Heap.Sort(heapData));
System.out.println("归并排序比较次数:" + Merge.Sort(mergeData));
System.out.println("快速排序比较次数:" + Quick.Sort(quickData));
System.out.println();
}
}
//生成随机数
public static int[] generateRandomData(int size) {
int[] data = new int[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
data[i] = random.nextInt();
}
return data;
}
}
数据的统计与图像绘制
1.最终五大排序的比较次数
数据个数: 100
插入排序比较次数:2553
选择排序比较次数:4950
堆排序比较次次数:468
归并排序比较次数:541
快速排序比较次数:168
数据个数: 1000
插入排序比较次数:242114
选择排序比较次数:499500
堆排序比较次次数:4810
归并排序比较次数:8749
快速排序比较次数:2396
数据个数: 2000
插入排序比较次数:990583
选择排序比较次数:1999000
堆排序比较次次数:9594
归并排序比较次数:19379
快速排序比较次数:5233
数据个数: 5000
插入排序比较次数:6149350
选择排序比较次数:12497500
堆排序比较次次数:23908
归并排序比较次数:55262
快速排序比较次数:14566
数据个数: 10000
插入排序比较次数:25421573
选择排序比较次数:49995000
堆排序比较次次数:47772
归并排序比较次数:120417
快速排序比较次数:31501
数据个数: 100000
插入排序比较次数:2499151592
选择排序比较次数:704982704
堆排序比较次次数:478696
归并排序比较次数:1536305
快速排序比较次数:390800
2.将上述数据绘制成折线图