数据结构--排序

简单排序

1、Comparable接口

java中提供的含比较规则的接口
在这里我们以学生类为例:

定义一个学生类Student,具有年龄age和姓名username两个属性,并通过Comparable接口提供比较规则;
实现Comparable接口,重写compareTo()方法

public class Student implements Comparable<Student>{
	private String username;
	private int age;
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public String toString() {
		return "Student [username=" + username + ", age=" + age + "]";
	}
	
	@Override
	public int compareTo(Student arg0) {
		// TODO Auto-generated method stub
		return this.getAge()-arg0.getAge();
	}
}

2、冒泡排序

排序原理:

  1. 比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置
  2. 对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值
  3. 对最后位置最大元素之前的元素继续进行1.2.两步

排序前:[4, 5, 6, 3, 2, 1]
排序后:[1, 2, 3, 4, 5, 6]
时间复杂度:O(N^2)

public class Bubble {
	/*
	 * 对数组a中的元素进行排序(注意:数组a的类型Comparable,所以参数数组必须是引用数据类型,并实现Comparable接口
	 */
	public static void sort(Comparable[] a) {
		for(int i=a.length-1;i>0;i--) {
			for(int j=0;j<i;j++) {
				//比较索引j和索引j+1处的值
				if(greater(a[j], a[j+1])) {
					exch(a,j,j+1);
				}
			}
		}
	}
	
	/*
	 * 比较v元素是否大于w元素
	 */
	private static boolean greater(Comparable v,Comparable w) {
		return v.compareTo(w)>0;
	}

	/*
	 * 数组元素i和j交换位置
	 */
	private static void exch(Comparable[] a,int i,int j) {
		Comparable temp;
		temp=a[i];
		a[i]=a[j];
		a[j]=temp;
	}
}

3、选择排序

排序原理

  1. 每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引处的值为最小值,最后可以找到最小值所在的索引
  2. 交换第一个索引处和最小值所在的索引处的值

在这里插入图片描述
排序前:[4,6,8,7,9,2,10,1]
排序后:[1,2,4,6,7,8,9,10]
时间复杂度:O(N^2)

public class Selection {
	/*
	 *  对数组a中的元素进行排序
	 */
	public static void sort(Comparable[] a) {
		for(int i=0;i<a.length-1;i++) {
			//定义一个变量,记录最小元素所在的索引,默认为参与选择排序的第一个元素所在位置
			int minIndex=i;
			for(int j=i+1;j<a.length;j++) {
				//需要比较最小索引minIndex处的值和j索引处的值
				if(greater(a[minIndex],a[j])) {
					minIndex=j;
				}
			}
			//交换最小元素所在索引minIndex处的值和i索引处的值
			exch(a,i,minIndex);
		}
	}
	
	/*
	 * 比较v元素是否大于w元素
	 */
	private static boolean greater(Comparable v,Comparable w) {
		return v.compareTo(w)>0;
	}
	
	/*
	 * 数组元素i和j交换位置
	 */
	private static void exch(Comparable[] a,int i,int j) {
		Comparable temp;
		temp=a[i];
		a[i]=a[j];
		a[j]=temp;
	}
}

4、插入排序

排序原理:

  1. 把所有元素分成两组,已排序的和未排序的
  2. 找到未排序的组中的第一个元素,向已排序的组中进行插入
  3. 倒序遍历已排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素# 高级排序
  4. 把待插入的元素放到这个位置的下1位,并将其余元素向后移1位

    排序前:[4,3,2,10,12,1,5,6]
    排序后:[1,2,3,4,5,6,10,12]
    时间复杂度:O(N^2)
public class Insertion {
	/*
	   *  对数组a中的元素进行排序
	 */
	public static void sort(Comparable[] a) {
		for(int i=1;i<a.length;i++) {
			
			for(int j=i;j>0;j--) {
				//比较索引j处的值和索引j-1处的值,如果索引j-1处的值比索引j处的值大,则交换数据,如果不大,说明找到合适的位置,退出内层循环
				if(greater(a[j-1], a[j])) {
					exch(a, j-1, j);
				}else {
					break;
				}
			}
		}
	}
	
	/*
	 * 比较v元素是否大于w元素
	 */
	private static boolean greater(Comparable v,Comparable w) {
		return v.compareTo(w)>0;
	}
	
	/*
	 * 数组元素i和j交换位置
	 */
	private static void exch(Comparable[] a,int i,int j) {
		Comparable temp;
		temp=a[i];
		a[i]=a[j];
		a[j]=temp;
	}
}

高级排序

1、希尔排序又称“缩小增量排序”

排序原理:

  1. 选定一个增长量h,按照增长量h作为分组的依据,对数据进行分组
  2. 对分好组的每一组数据完成插入排序
  3. 减少缩进量,最小减为1,重复2

    排序前:[9,1,2,5,7,4,8,6,3,5]
    排序后:[1,2,3,4,5,5,6,7,8,9]
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;i++) {
				//2.2把待插入的元素插入到有序数列中
				for(int j=i;j>=h;j=j-h) {
					
					//2.2.1待插入元素是a[j],比较a[j]和a[j-h]
					if(greater(a[j-h], a[j])) {
						//交换元素
						exch(a, j-h, j);
					}else {
						//待插入元素已经找到了合适的位置,结束循环
						break;
					}	
				}
			}
			//减小h的值
			h=h/2;
		}
	}
	
	/*
	 * 比较v元素是否大于w元素
	 */
	private static boolean greater(Comparable v,Comparable w) {
		return v.compareTo(w)>0;
	}
	
	/*
	 * 数组元素i和j交换位置
	 */
	private static void exch(Comparable[] a,int i,int j) {
		Comparable temp;
		temp=a[i];
		a[i]=a[j];
		a[j]=temp;
	}
}

2、归并排序

在谈归并之前,我们先要知道递归和分治法是什么:
递归: 定义方法时,在方法内部调用方法本身.
注意: 不能无限的调用自己,必须要有边界条件,能够让递归结束.因为每一次递归调用都会在栈内存中开辟新的空间,重新执行方法,如果递归的层级太深,很容易造成内存溢出,会报错:StackOverflowError(栈内存溢出错误)

public void show( ){
	System.out.println("aaaa");
	show( );
}

分治法:
将已有序的子序列合并,得到完全有序的序列,
即先使每个子序列有序,再使子序列段间有序,
若将两个有序表合并成一个有序表,称为二路归并
排序原理采用分治法

  1. 尽可能的将一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止
  2. 将相邻的两个子组进行合并成一个有序的大组
  3. 不断重复步骤2,直到最终只有一个组为止

    排序前:[8,4,5,7,1,3,6,2]
    排序后:[1,2,3,4,5,6,7,8]
    时间复杂度:O(nlogn)
    缺点: 需要申请额外的数组空间,导致空间复杂度提升,是典型的以空间换时间的操作
public class Merge {
     private  static Comparable[] assist;
	/*
	 * 比较v元素是否大于w元素
	 */
	private static boolean less(Comparable v,Comparable w) {
		return v.compareTo(w)<0;
	}
	
	
	/*
	 * 数组元素i和j交换位置
	 */
	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.初始化辅助数组assist
		assist=new Comparable[a.length];
		//2.定义一个lo变量和hi变量,分别记录数组中最小的索引和最大的索引
		int lo=0;
		int hi=a.length-1;
		//3.调用sort重载方法完成数组a中,从索引lo到索引hi的元素的排序
		sort(a,lo,hi);
	}
	
	/*
	 * 对数组a中从lo到hi的元素进行排序
	 */
	private static void sort(Comparable[] a,int lo,int hi) {
		//做安全性校验
		if(lo>=hi) {
			return;
		}
		//对lo到hi之间的数据进行分组(两组)   
		int mid=lo+(hi-lo)/2;
		//分别对每组数据进行排序
		sort(a,lo,mid);
		sort(a,mid+1,hi);
		//再把两个组中的数据进行归并
		merge(a,lo,mid,hi);
	}
	
	/*
	 * 对数组中,从lo到mid为一组,从mid到hi为一组,对两组数据进行归并
	 */
	private static void merge(Comparable[] a,int lo,int mid,int hi) {
		//定义3个指针
		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指针没有走完,那么顺序移动p2指针,把对应的元素放到辅助数组的对应索引处
		while(p2<=hi) {
			assist[i++]=a[p2++];
		}
		//把辅助数组中的元素拷贝到原数组中
		System.arraycopy(assist, lo, a, lo, hi-lo+1);
	}
}

3、快速排序

快速排序算法中,需要对数组先进性切分。其切分原理
把一个数组切分成两个数组的基本思想:

  1. 找一个基准值,用两个指针分别指向数组的头部和尾部;
  2. 先从尾部向头部开始搜索一个比基准值小的元素,搜索到即停止,并记录指针位置;
  3. 再从头部向尾部开始搜索一个比基准值大的元素,搜索到即停止,并记录指针位置;
  4. 交换当前左边指针和右边指针位置的元素;
  5. 重复2,3,4步,直到左边指针的值大于右边指针的值(相遇)停止。

排序原理:

1.首先设定一个分界值,通过该分界值将数组分成左右两部分
2. 将大于或等于分界值的数据放到右边数组,小于的放到左边数组
3. 左右两边数组单独进行排序。重复步骤2.
4. 重复上述过程,即递归定义。通过递归将左右两侧排好序,再递归至一个数组

在这里插入图片描述
排序前:[6,1,2,7,9,3,4,5,8]
排序后:[1,2,3,4,5,6,7,8,9]

时间复杂度:最优(平均情况:O(nlogn) 最坏:O(n^2)

public class Quick {
	private  static Comparable[] assist;
	/*
	 * 比较v元素是否大于w元素
	 */
	private static boolean less(Comparable v,Comparable w) {
		return v.compareTo(w)<0;
	}
	
	/*
	 * 数组元素i和j交换位置
	 */
	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.初始化辅助数组assist
		assist=new Comparable[a.length];
		//2.定义一个lo变量和hi变量,分别记录数组中最小的索引和最大的索引
		int lo=0;
		int hi=a.length-1;
		//3.调用sort重载方法完成数组a中,从索引lo到索引hi的元素的排序
		sort(a,lo,hi);
	}
	
	/*
	 * 对数组a中从lo到hi的元素进行排序
	 */
	private static void sort(Comparable[] a,int lo,int hi) {
		//做安全性校验
		if(lo>=hi) {
			return;
		}
		//需要对数组中的lo索引到hi索引处的元素进行分组
		int partition=partition(a, lo, hi);//返回是分组的分界值所在的索引,分界值位置变换后的索引
		
		//让左子组有序
		sort(a,lo,partition-1);
		//让右子组有序
		sort(a,partition+1,hi);
	}
	
	//对数组a中,从索引lo到索引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) {
					break;
				}
			}
			
			//再从左往右扫描,移动指针left,找到一个比分界值大的元素,停止
			while(less(a[++left], key)) {
				if(left==hi) {
					break;
				}
			}
			
			//判断left>=right,如果是,则证明元素扫描完毕,结束循环,如果不是,则交换元素
			if(left>=right) {
				break;
			}else {
				exch(a, left, right);
			}
		}
		
		//交换分界值
		exch(a, right, lo);
		return right;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值