排序算法整理

1.简单排序

简述

希尔排序是插入排序的改进版 
package SortLearn;
//选择排序 核心思想  依次选择出最小的元素放在对应的位置 需要比较N^2次 交换N次 即使是已经排好序的数组 依然需要这些次的比较和交换 
public class easySort<T extends Comparable<T>> extends Sort<T> {
	public void sort(T[] nums){
		int N = nums.length;
		for(int i=0;i<N-1;i++){
			int min = i;
			for(int j=i;j<N;j++){
				if(less(nums[j],nums[min])){
					min=j;
				}
			}
			swap(nums,i,min);
		}
	}
}
//冒泡排序 比较每个相邻的元素 使得最大的元素移动到最后 直到均从小到大排序
class Bubble<T  extends Comparable<T>> extends Sort<T>{
	public void sort(T[] nums){
		int N = nums.length;
		boolean hasSorted = false;
		for(int i=N-1;i>0&&hasSorted;i++){
			hasSorted=true;
			for(int j=0;j<i;j++){
				if(less(nums[j+1],nums[j])){
					swap(nums,j,j+1);
					hasSorted=false;
				}
			}
			if(hasSorted==true)
				return;
		}
	}
}
//插入排序 核心思想:将当前元素插入到左侧已经排序的数组中 使得左侧的数组依然 有序 注意每次只能交换相邻元素 比较与交换次数与当前数组的次序有关系
class  Intertion<T  extends  Comparable<T>> extends Sort<T>{
	public void sort(T []nums){
		int N=nums.length;
		for(int i = 1;i<N;i++){
			for(int j=i;j>0&&less(nums[j],nums[j-1]);j--){
				swap(nums,j,j-1);
			}
		}
	}
}

//希尔排序  由于对于大规模的数组  插入排序很慢 因为每一次只交换相邻的元素
//希尔排序使用查毒排序对间隔h的次序进行排序 通过 不断减小h 最后使得h=1;
class Shell<T extends  Comparable<T>> extends  Sort<T>{
	public void sort(T[]nums){
		int N = nums.length;
		int h=1;
		while(h<N/3){
			h=h*3+1;
		}
		while(h>=1){
		for(int i=h;i<N;i++){//从第i个开始考虑排序 h是最先开始考虑的那个
			for(int j=i;j>=h&&less(nums[j],nums[j-h]);j=j-h){
				swap(nums,j,j-h);
			}
		}
		h=h/3;
		}
	}
}

2.归并排序

简述

归并排序需要额外的空间 将数组分成两部分 分别排序 然后再归并
package SortLearn;
//归并排序 是稳定的排序 需要申请额外的空间
public class MergeSort<T extends Comparable<T>> extends Sort<T> {
	protected  T[] aux;
	protected void merge(T[] nums,int l,int m,int h){
		int i=l,j=m+1;//nums是需要排序的数组 m 表示数组的中间位置
		for(int k=l;k<=h;k++){
			aux[k]=nums[k];//将数组copy到辅助数组
		}
		for(int k=l;k<=h;k++){
			if(i>m){
				nums[k]=aux[j++];
			}else if(j>h){
				nums[k]=aux[i++];
			}else if(aux[i].compareTo(nums[j])<0){//先进行这一步 保证 稳定性
				nums[k]=aux[i++];
			}else{
				nums[k]=aux[j++];
			}
		}
	}
}
//自顶向下并归排序 将问题分成两个子问题 O(NlogN)
class Up2DownMergerSort<T extends Comparable<T>>extends MergeSort<T>{
	public void sort(T[]nums){
		sort(nums,0,nums.length-1);
	}
	private void sort(T[]nums,int l,int h){
		if(l<h){
			int m=l+(h-l)/2;
			sort(nums,l,m);
			sort(nums,m+1,h);
			merge(nums,l,m,h);
		}
	}
}
//自底向上归并 先归并那些微型数组
class Down2UpMergeSort<T extends Comparable<T>> extends MergeSort<T>{
	public void sort(T[] nums){
		int N = nums.length;
		aux =(T[])new Comparable[N];
		for(int sz=1;sz<N;sz=sz*2){//小组 从一个一个开始 
			for(int l=0;l<N-sz;l+=sz*2){//第一个小组第一个小标是0 第二组的第一个小标是2*sz+l
				merge(nums,l,l+sz-1,Math.min(l+sz+sz-1,N-1));				
			}
		}
	}
}

3.快排

简述

快排需要调用递归 需要辅助栈 是不稳定的排序 留意partition部分 小于arr[0]的在左边 大于的在右边 递归调用  
package SortLearn;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

//快速排序 核心思想:选择一个数 将小于这个数的都放在左边 大于这个数的都放在右边 然后递归调用
//快排属于原地快排  但是由于使用了递归 使用了辅助栈 当数组本来就是有序的情况下  比较次数高达N^2/2 因此有了快排前的 shuffle
public class QuickSort<T extends Comparable<T>> extends Sort<T> {
	public void sort(T[]nums){
		shuffle(nums);
		sort(nums,0,nums.length-1);
	}	
	public void sort(T[]nums,int l,int h){
		if(h<=l){
			return;
		}
		int j=partition(nums,l,h);//切分点
		sort(nums,l,j-1);
		sort(nums,j+1,h);
	}
	private void shuffle(T[] nums){
		List<Comparable> list = Arrays.asList(nums);
		Collections.shuffle(list);
		list.toArray(nums);
	}
	private int partition(T[]nums,int l,int h){
		int i= l,int j=h+1;
		T v=nums[l];
		while(true){
			while(less(nums[++i],v)&& (i!=h));//寻找第一个大于v的i
			while(less(v,nums[--j])&&(j!=l));//寻找第一个小于v的j 注意j要先减一 如果是1234这样的 j会减到指向1的位置
			if(i>=j){//没有的话 就返回 1234这样的 就直接结束while
				break;
			}
			swap(nums,i,j);//如果i指向了 大于v的 j
			
		}
		swap(nums,l,j);
		return j;
	}
}
/* 算法改进
 * 1.对于小数组 插入排序要比快排的性能要好 当快递递归到小数组的时候 可以切换到插入排序 
 * 2.三个数取中间 最好的情况是每次都能取数组的中位数作为切分元素 但是计算中位数的代价较高 一般取三个数 取中间的那个
 *3.三向切分 当有大量重复元素的时候 分成三部分 分别是 大于 等于 小于
*/
class ThreeWayQuikSort<T extends Comparable<T>>extends QuickSort<T>{
	public void sort(T[] nums,int l,int h){
		if(h<=l){
			return;
		}
		int lt =l,i=l+1,gt=h;
		T v =nums[l];
		while(i<=gt){
			int cmp =nums[i].compareTo(v);
			if(cmp<0){
				swap(nums,i++,lt++);//交换后 第i个位置是lt了 而lt位置是v 所以i++
			}else if(cmp>0){
				swap(nums,i,gt--);//交换后 第i个位置是最后一个 还没有与v比较 所以不能i++
			}else{
				i++;
			}
		}
		sort(nums,l,lt-1);
		sort(nums,gt+1,h);
	}
}
/*
 * 基于切分的快速选择算法  快四排序的partition 会返回一个整数j 此时a[j]就是数组的第j大的元素
 */
class Select{
	public int select(T[]nums,int k){
		int l=0,h=nums.length-1;
		while(h>l){
			int j=partition(nums,l,h);
			if(j==k){
				return nums[k];
			}else  if(j>k){
				h=j-1;
			}else{
				l=j+1;
			}
		}
		return nums[k];
	}
}

4.堆排序

简介

首先理解什么是堆 最大堆与最小堆 上浮与下沉的思想 排序的时候 用根节点与最后一个交换 再让它下沉 再次形成一个堆	
步骤1生成堆 步骤2 取根节点与最后一个交换 3.根节点下沉 重新生成堆 此时N=N-1
package SortLearn;
//堆 堆是一个完全 二叉树 分为最大堆(最顶最大)和最小堆 最大堆即使堆顶的元素最大 两个节点都比它小
//堆可以用数组表示 位置K的节点的父节点在K/2  父节点的两个子节点的位置是2k和2k+1
public class Heap<T extends Comparable<T>> {
	private T[] heap;
	private int N=0;
	public  Heap(int maxN){
		this.heap =(T[])new Comparable[maxN+1];//最大堆容量
	}
	public boolean isEmpty(){
		return N==0;
	}
	public int size(){
		return N;
	}
	private boolean less(int i,int j){
		return heap[i].compareTo(heap[j])<0;
	}
	private void swap(int i,int j){
		T t =heap[i];
		heap[i]=heap[j];
		heap[j]=t;		
	}
	/*
	 * 上浮 在最大堆中 当一个节点比父节点大的时候 需要和父节点交换位置 然后再继续比较
	 */
	private void swim(int k){
		while(k>1 &&(less(k/2,k))){//不是根节点 并且比父节点大 上浮
			swap(k/2,k);
			k=k/2;
		}
	}
	/*
	 * 下浮  在最大堆中 当父节点比子节点小的时候需要 将父节点下沉
	 */
	private void sink(int k){
		while(2*k<=N){
			int j =2*k;
			if(j<N &&less(j,j+1)){
				j++;
			}
			if(!less(k,j)){
				break;
			}
			swap(k,j);
			k=j;
		}
	}
	/*
	 * 插入元素 插入到末尾 然后再上浮到合适位置
	 */
	public void innsert(T v){
		heap[++N]= v;
		swim(N);
	}
	/*
	 * 删除最大元素
	 */
	public T delMax(){
		T max =heap[1];
		swap(1,N--);
		sink(1);
		heap[++N]=null;
		return max;		
	}
}

/*
 * 堆排序 构建堆的 当只有一个节点的时候 不用排序 因此从下往上看 叶子节点不用看 直接看叶子节点的上一个
 * 从第一个非叶子节点N/2开始 一直到根节点 进行下浮操作 最后就形成了堆 
 * 最对最大堆 第一个就是最大的 将它与最后一个交换 最大的就放在了最后 再对此时的根节点下沉 注意此时的N=N-1
 * 找次大的 指导最后一个
 */
class HeapSort<T extends Comparable<T>>extends Sort<T>{
	//数组从第一个位置开始
	public void sort(T[] nums){
		int N=nums.length-1;
		for(int i =N/2;i>1;i++)}//形成堆
			sink(nums,i,N);
		}
		while(N>1){
			swap(nums,1,N--);
			sink(nums,1,N);
		}
	}
	private void sink(T[] nums,int i,int N){		
		while(2*i<=N){
			int j = 2*i;
			if(j<N&&less(nums,j,j+1)){
				j++;
			}
			if(!less(nums,i,j)){
				break;
			}
			swap(nums,i,j);
			i=j;
		}
	}
	public boolean less(T[]nums,int i,int j){
		return nums[i].compareTo(nums[j])<0;
	}
}

5.排序总结

归并排序稳定 但是空间复杂度是N 堆排序空间复杂度是1 但是不稳定 
 java.util.Arrays.sort(),对于原始数据类型使用三向切分的快速排序,对于引用类型使用归并排序 考虑了稳定性

排序算法总结

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值