2.1 希尔排序
希尔排序API设计
代码实现
public class Shell {
public static void sort(Comparable[] a){
//1.根据数组a的长度,确定增长量h的初始值
int h =1;
while (h<a.length/2){
h=h*2+1;
}
//2.希尔排序
while (h>=1){
//排序
//2.1,找到待插入的元素
for (int i = h;i<a.length-1;i++){
//2.2.把待插入的元素插入到有序数组中
for (int j=i;j>=h;j-=h){
if (greater(a[j-h],a[j])){
exch(a,j-h,j);
}else {
break;
}
}
}
//减小h
h=h/2;
}
}
private static boolean greater(Comparable v,Comparable w){
return v.compareTo(w)>0;
}
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
希尔排序和插入排序性能比较测试代码:
public class ShellTest {
public static void main(String[] args) throws IOException {
ArrayList<Integer> arrayList = new ArrayList<>();
BufferedReader fr = new BufferedReader(new FileReader("D:\\JAVA\\代码\\algorithm\\src\\reverse_arr.txt"));
String line;
while ((line=fr.readLine())!=null){
arrayList.add(Integer.valueOf(line));
}
fr.close();
//把集合转换成数组
Integer[] integers = new Integer[arrayList.size()];
arrayList.toArray(integers);
inserttest(integers);
shelltest(integers);
}
public static void inserttest(Integer[] a){
long start = System.currentTimeMillis();
Insertion.sort(a);
long end = System.currentTimeMillis();
System.out.println("插入排序:"+(end-start)+"毫秒");
}
public static void shelltest(Integer[] a){
long start = System.currentTimeMillis();
Shell.sort(a);
long end = System.currentTimeMillis();
System.out.println("希尔排序:"+(end-start)+"毫秒");
}
}
测试结果
2.2 归并排序
2.2.1 递归
注意事项:
2.2.2 归并排序
归并排序API设计
归并原理:
public class Merge {
private static Comparable[] assist;//归并所需要的辅助数组
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
/*
/对数组a中元素进行排序
*/
public static void sort(Comparable[] a){
//1.初始化辅助数组assit
assist = new Comparable[a.length];
//2.定义一个lo变量和hi变量,分别记录数组中的最小和最大索引
int lo = 0;
int hi = a.length-1;
//3.调用重载方法完成lo到hi索引的元素的排序
sort(a,lo,hi);
}
/*
对数组a中从lo到hi的元素进行排序
*/
private static void sort(Comparable[] a,int lo,int hi){
//安全性校验
if (hi<=lo){
return;
}
//对lo到hi之间的数据进行分为两个组
int mid = lo+(hi-lo)/2;
//分别对每一组数据进行排序
sort(a,lo,mid);
sort(a,mid+1,hi);
//再把两个组的数据进行归并
merge(a,lo,mid,hi);
}
//归并操作
private static void merge(Comparable[] a,int lo,int mid,int hi){
//定义三个指针
int i=lo;//辅助数组的第一个索引
int p1=lo;//左子组
int p2=mid+1;//右子组
//遍历,移动p1指针和p2指针,比较对应索引处的值,找出最小的那个,放到辅助数组的对应索引处
while (p1<=mid && p2<=hi){
//比较对应索引处的值
if (less(a[p1],a[p2])){
assist[i++] = a[p1++];
}else {
assist[i++] = a[p2++];
}
}
//遍历,如果p1的指针没有走完,那么顺序移动p1指针......
while (p1<=mid){
assist[i++] = a[p1++];
}
//遍历,如果p2的指针没有走完,那么顺序移动p1指针......
while (p2<=hi){
assist[i++] = a[p2++];
}
//把辅助数组中的元素拷贝到原数组中
for (int index=lo;index<=hi;index++){
a[index]=assist[index];
}
}
}
归并排序时间复杂度分析
2.3 快速排序
排序原理
1.首先设定一个分界值,通过该分界值将数组分成左右两部分;
2.将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中各元素都小于
或等于分界值,而右边部分中各元素都大于或等于分界值;
3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两
部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当
左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。
API设计
代码实现
public class Quick {
//对数组元素进行排序
public static void sort(Comparable[] a) {
int lo = 0;
int hi = a.length-1;
sort(a,lo,hi);
}
//对a数组中从通过索引lo到hi之间的元素进行排序
private static void sort(Comparable[] a, int lo, int hi) {
//安全性校验
if (hi<=lo){
return;
}
//需要对数组中lo索引到hi索引处的元素进行分组(左子组和右子组)
int partition =partition(a,lo,hi);//返回的是分组的分界值所在的索引,分界值变换位置后的索引
//让左子组有序
sort(a,lo,partition-1);
//让右子组有序
sort(a,partition+1,hi);
}
public static int partition(Comparable[] a, int lo, int hi) {
//确定分界值
Comparable key = a[lo];
//定义两个指针,分别指向待切分元素的最小索引处和最大索引处的下一个位置
int left=lo;
int right=hi+1;//指向最大索引的下一个
//切分
while (true){
//先从右往左扫描,移动right指针,找到一个比分界值小的元素,停止
while (less(key,a[--right])){
if (right==lo){ //一直扫描的开头也没有找到小于key的值
break;
}
}
//再从左往右扫描,移动left指针,找到一个比分界值大的元素,停止
while (less(a[++left],key)){//一直扫描到结尾也没有找到大于key的值
if (left==hi){
break;
}
}
//判断left>=right,如果是,则证明扫描完毕,结束循环,如果不是,则交换元素即可
if (left>=right){
break;
}else {
exch(a,left,right);
}
}
exch(a,lo,right); //将lo处的分界值交换到right(或left)的位置
return right;
}
/*
数组元素i和j交换位置
*/
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
/*
比较v元素是否小于w元素
*/
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
}
快速排序和归并排序的区别:
快速排序的时间复杂度分析:
2.4 排序的稳定性
稳定性的定义:
数组arr中有若干元素,其中A元素和B元素相等,并且A元素在B元素前面,如果使用某种排序算法排序后,能够保
证A元素依然在B元素的前面,可以说这个该算法是稳定的。
稳定性的意义:
如果一组数据只需要一次排序,则稳定性一般是没有意义的,如果一组数据需要多次排序,稳定性是有意义的。例
如要排序的内容是一组商品对象,第一次排序按照价格由低到高排序,第二次排序按照销量由高到低排序,如果第
二次排序使用稳定性算法,就可以使得相同销量的对象依旧保持着价格高低的顺序展现,只有销量不同的对象才需
要重新排序。这样既可以保持第一次排序的原有意义,而且可以减少系统开销。
常见排序算法的稳定性: