Java之排序算法学习

Java之排序算法学习

辅助

​ 待排序的元素需要实现 Java 的 Comparable 接口,该接口有 compareTo() 方法,可以用它来判断两个元素的大小关系。使用辅助函数 less() 和 swap() 来进行比较和交换的操作,使得代码的可读性和可移植性更好。

public abstract class Sort<T extends Comparable<T>>{
    public abstract void Sort(T[] num);
    
    protected boolean less(T v, T w) {//比较大小
        return v.compareTo(w) < 0;
    }
    
    protected void swap(T[] a, int i, int j) {//交换位置
        T t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

选择排序

​ 从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。

//选择排序
public class Selection<T extends Comparable<T>> extends Sort<T>{
    public void sort(T[] nums) {
        int len = nums.length;
        for(int i= 0;i<len;i++){
            int min =i;
            for(int j= i+1;j<len;j++){
                if(less(nums[i],num[min])){
                    min=i;
                }
            }
            swap(nums, i, min);
        }
    }
}

冒泡排序

从左到右不断交换相邻逆序的元素,在一轮的循环之后,可以让未排序的最大元素上浮到右侧。

在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。

//冒泡排序
public class Bubble<T extends Comparable<T>> extends Sort<T> {
	public void sort(T[] nums) {
        int len = nums.length;
        for(int i= 0;i<len-1;i++){
            for(int j= 0;j<len-1-i;j++){
                if(less(nums[j],nums[j+1])){
                   swap(nums, j, j+1);
                }
            } 
        }
    }
}
//冒泡排序提前终止排序
public class Bubble<T extends Comparable<T>> extends Sort<T> {
	public void sort(T[] nums) {
        int len = nums.length;
        boolean flag =false; 
        for(int i= 0;i<len-1&& !flag;i++){
            flag = true;
            for(int j= 0;j<len-1-i;j++){
                if(less(nums[j],nums[j+1])){
                   flag = false;
                   swap(nums, j, j+1);
                }
            } 
        }
    }
}

插入排序

每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左侧数组依然有序。

插入排序的时间复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么逆序较少,需要的交换次数也就较少,时间复杂度较低

//插入排序
public class Insertion<T extends Comparable<T>> extends Sort<T> {
	public void sort(T[] nums) {
        int len = nums.length;
        for(int i= 0;i<len;i++){
            for(int j= i;j>0;j--){
                if(less(nums[j], nums[j - 1])){
                    swap(nums, j, j - 1);
                }
            }
        }
    }
}

希尔排序

对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,每次只能将逆序数量减少 1。希尔排序的出现就是为了解决插入排序的这种局限性,它通过交换不相邻的元素,每次可以将逆序数量减少大于 1。

public class Shell<T extends Comparable<T>> extends Sort<T> {
     public void sort(T[] nums) {
         int len = nums.length;
         int h = 1; 
         //计算开始间隔大小
         while(h<len/3){
             h= 3 * h + 1;
         }
         while(h>= 1){
             for(int i= 0;i<len-h;i++){
                 for(int j= 0;j<len-h;j=j+h){
                     if(less(nums[j], nums[j+h])){
                         swap(nums, j, j + h);
                     }
                 }
             }
             h= h / 3;
         }
     }
}

归并排序

归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。

public class MergeSort<T extends Comparable<T>> extends Sort<T> {
	
	public void sort(T[] nums) {	
		sort(nums,0,nums.length-1);
	}
	
	
	public void sort(T[] nums,int low,int hight) {
		int mid = (low+hight)/2;
		if(low<hight) {
			sort(nums,low,mid);
			sort(nums,mid+1,hight);
		}
		merge(nums,low,mid,hight);
	}
	public void merge(T[] nums,int low,int mid,int hight) {
		Integer[] temp = new Integer[hight-low+1];
		int i= low;
        int j = mid+1;
        int k=0;
     // 把较小的数先移到新数组中
        while(i<=mid && j<=hight){
            if((int)nums[i]<(int)nums[j]){
                temp[k++] = (int)nums[i++];
            }else{
                temp[k++] = (int)nums[j++];
            }
        }
        // 把左边剩余的数移入数组 
        while(i<=mid){
            temp[k++] = (int)nums[i++];
        }
        // 把右边边剩余的数移入数组
        while(j<=hight){
            temp[k++] = (int)nums[j++];
        }
        // 把新数组中的数覆盖nums数组
        for(int x=0;x<temp.length;x++){
        	nums[x+low] = (T)temp[x];
        }
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Integer[] num = {4,1,3,8,5,7,20};
		MergeSort<Integer> et = new MergeSort<Integer>();
		et.sort(num);
		for(int number:num) {
			System.out.print(number+" ");
		}
	}

}

快速排序

  • 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;
  • 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class QuickSort<T extends Comparable<T>> extends Sort<T> {
	@Override
    public void sort(T[] nums) {
        shuffle(nums);
        sort(nums, 0, nums.length - 1);
    }
	
	
	private void sort(T[] nums, int i, int j) {
		// TODO Auto-generated method stub
		if(j<=i) {
			return;
		}
		int p = partition(nums, i, j);
		sort(nums, i, p - 1);
        sort(nums, p + 1, j);
		
	}


	private int partition(T[] nums, int l, int h) {
		// TODO Auto-generated method stub
		int i = l, j = h + 1;
		T v = nums[l];
		while (true) {
	        while (less(nums[++i], v) && i != h) ;
	        while (less(v, nums[--j]) && j != l) ;
	        if (i >= j)
	            break;
	        swap(nums, i, j);
	    }
		swap(nums, l, j);
		return j;
	}


	private void shuffle(T[] nums) {
		// TODO Auto-generated method stub
		List<Comparable> list = Arrays.asList(nums);
        Collections.shuffle(list);
        list.toArray(nums);
	}


	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Integer[] num = {4,1,3,8,5,7,20};
		QuickSort<Integer> et = new QuickSort<Integer>();
		et.sort(num);
		for(int number:num) {
			System.out.print(number+" ");
		}
	}

}

堆排序

​ 堆中某个节点的值总是大于等于或小于等于其子节点的值,并且堆是一颗完全二叉树。堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。


public class HeapSort<T extends Comparable<T>> extends Sort<T> {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Integer[] num = {4,1,3,8,5,7,20};
		HeapSort<Integer> et = new HeapSort<Integer>();
		et.sort(num);
		for(int number:num) {
			System.out.print(number+" ");
		}
	}

	@Override
    public void sort(T[] nums) {
        int N = nums.length - 1;
        for (int k = N / 2; k >= 1; k--)
            sink(nums, k, N);

        while (N > 1) {
            swap(nums, 1, N--);
            sink(nums, 1, N);
        }
    }

    private void sink(T[] nums, int k, int N) {
        while (2 * k <= N) {
            int j = 2 * k;
            if (j < N && less(nums, j, j + 1))
                j++;
            if (!less(nums, k, j))
                break;
            swap(nums, k, j);
            k = j;
        }
    }

    private boolean less(T[] nums, int i, int j) {
        return nums[i].compareTo(nums[j]) < 0;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值