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;
}
}