目录
实验六 排序
实验目的
研究和分析各种排序算法的时间复杂度。
掌握各种算法的设计、实现和分析。
实验内容
1.补充QuickSort类的quickSort(int [] list)、quickSort(int[] list,int first,int last)、partition(int []list ,int first,int last)三个方法,实现从小到大的快速排序。其中:
- quickSort(int [] list)为快速排序的调用入口。
- quickSort(int[] list,int first,int last)将会递归调用,把原数组list分成左右两段进行快速排序。
- partition(int []list ,int first,int last)对分段实现快速排序,形成从小到大的有序数列。
- 运行测试类QuickSortTest,并通过测试。
2.首先实现类Heap中的add(E newObject)、remove()方法,完成堆的设计与实现;其次实现HeapSort类的heapSort(E[] list)方法,进行堆的排序。其中:
- 类Heap实现的是大根堆。
- add(E newObject)为往其中添加数据,并对堆进行调整。
- remove()为移出堆中第一个元素,并构成新的堆。
- heapSort(E[] list)调用以上两个方法,实现列表从小到大的排序。
- 运行测试类HeapSortTest,并通过测试。
实验代码框架
QuickSort类
public class QuickSort {
public void quickSort(int [] list){
}
public static void quickSort(int[] list,int first,int last){
}
public static int partition(int []list ,int first,int last){
}
}
QuickSortTest类
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class QuickSortTest {
QuickSort q=new QuickSort();
@Before
public void setUp() throws Exception {
System.out.println("begin");
}
@After
public void tearDown() throws Exception {
System.out.println("End and success");
}
@Test
public void quickSort() {
for(int k=0;k<10;k++){
int []result=new int[100];
int []list=new int[100];
for(int i=0;i<result.length;i++){
int x=(int)(Math.random()*100);
result[i]=x;
list[i]=x;
}
q.quickSort(list);
Arrays.sort(result);
assertArrayEquals(result,list);
}
}
}
Heap类
public class Heap<E extends Comparable<E>> {
private java.util.ArrayList<E> list=new java.util.ArrayList<>();
public Heap(){}
public Heap(E[] objects){
}
public void add(E newObject){
}
public E remove(){
return null;
}
public int getSize(){ }
}
HeapSort类
public class HeapSort {
public <E extends Comparable<E>> void heapSort(E[] list){
}
public static void main(String[] args) {
}
}
HeapSortTest类
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class HeapSortTest {
HeapSort h=new HeapSort();
@Before
public void setUp() throws Exception {
System.out.println("begin");
}
@After
public void tearDown() throws Exception {
System.out.println("End and success");
}
@Test
public void heapSort() {
for(int k=0;k<10;k++){
Integer []result=new Integer[100];
Integer []list=new Integer[100];
for(int i=0;i<result.length;i++){
int x=(int)(Math.random()*100);
result[i]=x;
list[i]=x;
}
h.heapSort(list);
Arrays.sort(result);
assertArrayEquals(result,list);
}
}
}
实验设计的思路与考量
平常我们常接触的算法是插入排序、冒泡排序等等,对于快速排序、堆排序、基数排序等算法并不熟悉,并且不熟悉他们的实现过程,因此实验选择考察快速排序、堆的实现、堆排序这三个方面。
快速排序
1.快速排序的工作机制为,该算法在数组中选择一个称为基准(pivot)的元素,将数组分为两部分,使得第一部分中的所有元素都小于或等于基准元素,而第二部分中的所有元素都大于基准元素。对第一部分递归地应用快速排序算法,然后对第二部分递归地应用快速排序算法。
2.快速排序的图解
图来源于参考书《Java语言程序设计与数据结构(进阶篇)》
3.在实现时,为方便假定将数组的第一个元素选为基准元素。因此,在最佳情况下,基准元素每次将数组划分为规模大致相等的两个部分;在最差情况下,基准元素每次会将数组划分为一个大的子数组和另外一个空数组。
4.快速排序的T(n)=O(nlogn)。
5.优化:该算法的每次划分都将基准元素放在了恰当的位置。基准元素的选择会影响算法的性能。在理想情况下,应该选择能平均划分两部分的基准元素,可以从这个方面考虑优化。
堆的性质
堆排序使用的是二叉堆,它首先将所有的元素添加到一个堆上,然后不断一处最大的元素以获得一个排好序的线性表。
- 形状属性:它是一棵完全二叉树。
- 堆属性:每个结点大于或等于他的任意一个孩子。
- 在同一个存储空间操作,大根堆得到升序排列的元素,小根堆得到降序排列的元素。
堆的存储
如果堆的大小是已知的,可以将堆存储在一个数组中。但是未知堆的大小时,最好使用ArrayList来存储,因此本实验在堆的实现上使用了ArrayList。
如果数组/ArrayList的存储起始下标为0,对于位置i处的节点,它的左子节点在位置2i+1处,它的右子节点在位置2i+2处,而它的父节点在位置(i-1)/2处。
堆的实现
- 方法add(E newObject)将一个对象追加到树中,如果该对象大于他的父节点,就互换他们。此过程持续到该新对象成为根节点,或者新对象不大于它的父节点。
- 方法remove()删除并返回根节点,并且放在数组的最后。为保持堆的特征,该方法将最后的对象移到根节点处,如果该对象小于它的较大的子节点,就互换它们。此过程持续到最后一个对象成为叶子节点,或者该对象不小于它的子节点。
使用Heap类进行排序
首先使用Heap类创建一个对象,使用add方法将所有元素添加到队中,然后使用remove方法从堆中删除所有元素。