您的位置:首页 > 其它

快速排序、堆排序、归并排序简单比较

2017-11-26 11:54 295 查看
快速排序、堆排序和归并排序都是平均时间复杂度为O(nlog(n))的算法。有关其原理介绍已经有很多。今天写了个简单的实现,用JFreeChart做了个简单的性能比较。懒得保存在本地,上传一下,以供日后回看。

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.data.category.DefaultCategoryDataset;

import java.awt.*;
import java.util.*;

public class Solution {

public static void main(String[] args) {
int time = 400;
StandardChartTheme mChartTheme = new StandardChartTheme("CN");
mChartTheme.setLargeFont(new Font("黑体", Font.BOLD, 20));
mChartTheme.setExtraLargeFont(new Font("宋体", Font.PLAIN, 15));
mChartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 15));
ChartFactory.setChartTheme(mChartTheme);
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for (int i=0; i<time; i++) {
int len = (i+1)*800;
int scale = len*2;
int[] arr = new int[len];
for (int j=0; j<len; j++) {
arr[j] = (int)Math.floor(Math.random()*scale);
}
Calendar begin = Calendar.getInstance();
quickSort(arr, 0, len-1);
Calendar q = Calendar.getInstance();
dataset.addValue(q.getTimeInMillis()-begin.getTimeInMillis(), "快速排序", ""+len);
heapSort(arr);
Calendar h = Calendar.getInstance();
dataset.addValue(h.getTimeInMillis()-q.getTimeInMillis(), "堆排序", ""+len);
mergeSort(arr);
Calendar m = Calendar.getInstance();
dataset.addValue(m.getTimeInMillis()-h.getTimeInMillis(), "归并排序", ""+len);
}
JFreeChart chart = ChartFactory.createLineChart("排序算法性能比较","数组长度","排序耗时(ms)", dataset);
CategoryPlot plot = (CategoryPlot)chart.getPlot();
ChartFrame chartFrame = new ChartFrame("", chart);
chartFrame.pack();
chartFrame.setVisible(true);
}

/**
* 归并排序
* @param arr
*/
public static void mergeSort(int[] arr) {
int[] tmp = new int[arr.length];
divideArray(arr, 0, arr.length, tmp);
}

/**
* 将arr分成有序的
* @param arr 被divided的数组
* @param left 左边界(包含)
* @param right 右边界(不包含)
* @param tmp 存放合并信息的临时数组
*/
private static void divideArray(int[] arr, int left, int right, int[] tmp) {
if (left < right-1) {  // 保证至少有两个元素
int mid = (left+right) / 2;
divideArray(arr, left, mid, tmp);
divideArray(arr, mid, right, tmp);
mergeArray(arr, left, mid, right, tmp);
}
}

/**
* 假设arr中相邻的两块[begin, first_end)和[first_end, second_end)都是分别有序的,
* 则该函数使得[begin, second_end)有序。
* @param arr 数组
* @param begin 被调整子数组的开始(包含)
* @param first_end
* @param second_end 被调整子数组的结束(不包含)
*/
public static void mergeArray(int[] arr, int begin, int first_end, int second_end, int[] tmp) {
int f_index = begin;
int s_index = first_end;
int t = 0;
while(f_index < first_end && s_index < second_end) {
if (arr[f_index] <= arr[s_index]) {
tmp[t] = arr[f_index];
f_index++;
} else {
tmp[t] = arr[s_index];
s_index++;
}
t++;
}
while(f_index<first_end) {
tmp[t] = arr[f_index];
f_index++;
t++;
}
while(s_index<second_end) {
tmp[t] = arr[s_index];
s_index++;
t++;
}
t = 0;
while (begin < second_end) {
arr[begin] = tmp[t];
begin++;
t++;
}
}

/**
* 堆排序
* @param arr
*/
public static void heapSort(int [] arr) {
int len = arr.length;
// 由于默认arr[]是无序的,所以先从最下层往上构建二叉堆。
// 从最下层往上的顺序的原因是这样可以保证每次只需要调整节点i的位置即可。
for (int i=(len-1)/2; i>=0; i--) {
heapAdjust(arr, i, len-1);
}
// 然后,循环梳理最大堆。每次梳理,都会将最大元素放到数组末尾
int end = len-1;
for (int i=0; i<len; i++) {
int tmp = arr[0];
arr[0] = arr[end];
arr[end] = tmp;
end--;
heapAdjust(arr, 0, end);
}
}

/**
* 将二叉堆调整为最大堆。
* 这个函数的假设是:arr中除了arr[parent]这个根节点以外,arr[parent]下面的直到arr[length]的子树都是最大堆。
* 所以目的是将arr[parent]的值通过在[parent, length]范围内的调整,重新梳理二叉堆。
* @param arr 二叉堆
* @param parent 需要调整的子树的根的索引值。如果是全堆调整,则该值为0.
* @param length 需要调整的子树的叶子的最大索引值。如果是全堆调整,则该值为arr.length
*/
private static void heapAdjust(int[] arr, int parent, int length) {
int top_pre = arr[parent];  // 保存临时的顶节点值
int bigger = 2*parent + 1; // parent位置元素的较大子节点的索引,默认左子节点。
while (bigger <=length) {
// 将索引值调整到左右子节点中值较大的节点上去。
if (bigger+1<=length && arr[bigger]<arr[bigger+1]) {
// 如果parent位置元素的右子节点存在,并且其值大于左子节点
bigger++; // 则将索引值调整到右节点上去。
}
// 如果根节点的值小于其左右子节点中的较大值
if (arr[bigger] <= top_pre) {
break; // 则说明顺序正确,无需调整。
} else {
// 否则,说明顺序不正确,将较大子节点的值赋给根节点
arr[parent] = arr[bigger];
// 然后将较大子节点作为根节点,较大子节点的左子节点作为较大子节点。将初始parent的值接着向下传递比较。
parent = bigger;
bigger = 2*parent+1;
}
}
// 将初始parent的值,按照最大堆的规则传递到了最深的地方。
arr[parent] = top_pre;
}

/**
* 快速排序
* @param arr
* @param left 左边起始(包含)
* @param right 右边起始(包含)
*/
public static void quickSort(int [] arr, int left, int right) {
if (left > right) {
return;
}
int i = left;
int j = right;
int base = arr[i];
while (i != j) {
// 先从右边开始找
while(arr[j]>=base && i<j) {
j--;
}
while(arr[i]<=base && i<j) {
i++;
}
// 交换位置
if (i<j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// while过后,将基准换到i的位置上,而且此时i==j
arr[left] = arr[i];
arr[i] = base;

quickSort(arr, left, i-1);
quickSort(arr, i+1, right);
}

}


结果如图:


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐