详解Java中使用泛型实现快速排序算法的方法
作者:飞翔的猫咪 发布时间:2022-04-28 09:47:00
标签:Java,快速排序
快速排序算法概念
快速排序一般基于递归实现。其思路是这样的:
1.选定一个合适的值(理想情况中值最好,但实现中一般使用数组第一个值),称为“枢轴”(pivot)。
2.基于这个值,将数组分为两部分,较小的分在左边,较大的分在右边。
3.可以肯定,如此一轮下来,这个枢轴的位置一定在最终位置上。
4.对两个子数组分别重复上述过程,直到每个数组只有一个元素。
5.排序完成。
基本实现方式:
public static void quickSort(int[] arr){
qsort(arr, 0, arr.length-1);
}
private static void qsort(int[] arr, int low, int high){
if (low < high){
int pivot=partition(arr, low, high); //将数组分为两部分
qsort(arr, low, pivot-1); //递归排序左子数组
qsort(arr, pivot+1, high); //递归排序右子数组
}
}
private static int partition(int[] arr, int low, int high){
int pivot = arr[low]; //枢轴记录
while (low<high){
while (low<high && arr[high]>=pivot) --high;
arr[low]=arr[high]; //交换比枢轴小的记录到左端
while (low<high && arr[low]<=pivot) ++low;
arr[high] = arr[low]; //交换比枢轴小的记录到右端
}
//扫描完成,枢轴到位
arr[low] = pivot;
//返回的是枢轴的位置
return low;
}
使用泛型实现快排算法
下面设计一个QuickSort类,包含了静态函数sort(),可以对任意类型数组进行排序。如果为对象类型数组,则该对象类型必须实现Comparable接口,这样才能使用compareTo函数进行比较。
使用了最基本的快排算法,没有进行优化处理。
源代码如下:
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
public class QuickSort {
@SuppressWarnings("unchecked")
//对上述快排函数原型修改,使其可以对任意对象类型数组进行排序。这个函数为内部使用,外部排序函数接口为sort(),sort函数要求对象必须实现Comparable接口,可以提供编译时类型检测,见后文。
private static void quickSort(Object[] in,int begin, int end) {
if( begin == end || begin == (end-1) ) return;
Object p = in[begin];
int a = begin +1;
int b = a;
for( ; b < end; b++) {
//该对象类型数组必须实现Comparable接口,这样才能使用compareTo函数进行比较
if( ((Comparable<Object>)in[b]).compareTo(p) < 0) {
if(a == b){a++; continue;}
Object temp = in[a];
in[a] = in[b];
in[b] = temp;
a++;
}
}
in[begin] = in[a-1];
in[a-1] = p;
if( a-1 > begin){
quickSort(in,begin, a);
}
if( end-1 > a ) {
quickSort(in,a, end);
}
return;
}
//使用泛型,对任意对象数组排序,该对象类型数组必须实现Comparable接口
public static <T extends Comparable<? super T>> void sort(T[] input){
quickSort(input,0,input.length);
}
//添加对List对象进行排序的功能,参考了Java中的Java.util.Collections类的sort()函数
public static <T extends Comparable<? super T>> void sort(List<T> list){
Object[] t = list.toArray();//将列表转换为数组
quickSort(t,0,t.length); //对数组进行排序
//数组排序完成后再写回到列表中
ListIterator<T> i = list.listIterator();
for (int j=0; j<t.length; j++) {
i.next();
i.set((T)t[j]);
}
}
//由于Java中原始数据类型(int、double、byte等)无法使用泛型,所以只能使用函数重载机制实现对这些原始类型数组(int[]、double[]、byte[]等)的排序。这里为了共用同一个排序函数,利用原始类型的(AutoBoxing,UnBoxing)机制将其封装为对应对象类型,组成新的对象数组,排序后再解封装,这样的缺点是需要额外的转换步骤、额外的空间保存封装后的数组。另一种方式是将排序代码复制到各个重载函数中,官方API中的Java.util.Arrays这个类中的sort()函数就是使用这种方法,可以从Arrays类的源代码看出。
public static void sort(int[] input){
Integer[] t = new Integer[input.length];
for(int i = 0; i < input.length; i++){
t[i] = input[i];//封装
}
quickSort(t,0,t.length);//排序
for(int i = 0; i < input.length; i++){
input[i] = t[i];//解封装
}
}
//double[]数组的重载函数
public static void sort(double[] input){
Double[] t = new Double[input.length];
for(int i = 0; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,0,t.length);
for(int i = 0; i < input.length; i++){
input[i] = t[i];
}
}
//byte[]数组的重载函数
public static void sort(byte[] input){
Byte[] t = new Byte[input.length];
for(int i = 0; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,0,t.length);
for(int i = 0; i < input.length; i++){
input[i] = t[i];
}
}
//short[]数组的重载函数
public static void sort(short[] input){
Short[] t = new Short[input.length];
for(int i = 0; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,0,t.length);
for(int i = 0; i < input.length; i++){
input[i] = t[i];
}
}
//char[]数组的重载函数
public static void sort(char[] input){
Character[] t = new Character[input.length];
for(int i = 0; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,0,t.length);
for(int i = 0; i < input.length; i++){
input[i] = t[i];
}
}
//float[]数组的重载函数
public static void sort(float[] input){
Float[] t = new Float[input.length];
for(int i = 0; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,0,t.length);
for(int i = 0; i < input.length; i++){
input[i] = t[i];
}
}
//测试用的main函数
public static void main(String[] args) {
//生产一个随机数组成的int[]数组,用来测试
int LEN = 10;
int[] input = new int[LEN];
Random r = new Random();
System.out.print("int[] before sorting: ");
for(int i = 0; i < input.length; i++) {
input[i] = r.nextInt(10*LEN);
System.out.print(input[i] + " ");
}
System.out.println();
System.out.print("int[] after sorting: ");
sort(input);
for(int i : input) {
System.out.print(i + " ");
}
System.out.println();
//生成一个字符串数组,用来测试
String[] s = new String[]{"b","a","e","d","f","c"};
System.out.print("String[] before sorting: ");
for(int i = 0; i < s.length; i++) {
System.out.print(s[i] + " ");
}
System.out.println();
System.out.print("String[] after sorting: ");
sort(s);
for(int i = 0; i < s.length; i++) {
System.out.print(s[i] + " ");
}
System.out.println();
//生成一个字符串列表,用来测试
List<String> l = new LinkedList<String>();
s = new String[]{"b","a","e","d","f","c"};
System.out.print("LinkedList<String> before sorting: ");
for (int j=0; j<s.length; j++) {
l.add(s[j]);
System.out.print(s[j] + " ");
}
System.out.println();
sort(l);
System.out.print("LinkedList<String> after sorting: ");
for (String ts : l) {
System.out.print(ts + " ");
}
System.out.println();
}
}
运行main函数测试,从输出可以看出QuickSort类工作正常:
int[] before sorting: 65 48 92 26 3 8 59 21 16 45
int[] after sorting: 3 8 16 21 26 45 48 59 65 92
String[] before sorting: b a e d f c
String[] after sorting: a b c d e f
LinkedList<String> before sorting: b a e d f c
LinkedList<String> after sorting: a b c d e f


猜你喜欢
- 一、包含与删除两种方法解析1.boolean contains(Object o);判断集合中是否包含某个元素。package com.bj
- 以下内容来自 * ,关于静态类型检查和动态类型检查的解释:•静态类型检查:基于程序的源代码来验证类型安全的过程;•动态类型检查:在程序运行
- spring boot actuator介绍Spring Boot包含许多其他功能,可帮助您在将应用程序推送到生产环境时监视和管理应用程序。
- Java中的阻塞队列1. 什么是阻塞队列?阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空
- 平时开发,基本不改变的常量我们都放在了配置项里,如properties或yml文件里,这个时候为了只在启动时候进行加载。如何做呢?我们通过s
- 目录一 为什么要用锁二 synchronized怎么实现的三 CAS来者何人四synchronized和CAS孰优孰劣轻量级锁重量级锁总结提
- 解决方法:maven的配置文件<mirror> <id>aliyunmaven</id> <mir
- Parallel类是对线程的抽象,提供数据与任务的并行性。类定义了静态方法For和ForEach,使用多个任务来完成多个作业。Paralle
- 前言本文介绍了Object类以及Object类部分方法,toString方法,equals和hashCode方法(重写前和重写后的对比),g
- 前言本文主要给大家介绍了关于C#连接FTP时路径问题的相关内容,分享出来供大家参考学习,话不多说,来一起看看详细的介绍:今天在开发项目时,需
- 题目描述:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将
- 前言笔者因为项目需要自定义相机,所以了解了一下 Android 关于 camera 这块的 API。Android SDK 21(LOLLI
- 定义: SharedPreferences
- 配置宝塔面板javaweb运行环境详解,若出现404nignx错误也可按此教程进行检查1.准备:(解析成功的域名,本地运行完好的项目,宝塔面
- 本文为大家分享了Android实现带动画效果的可点击展开TextView 制作代码,效果图: 收起(默认)效果:点击展开后的效果:源码: 布
- 本文实例为大家分享了C#生成Word文件的具体代码,供大家参考,具体内容如下通过Microsoft.Office.Interop.Word生
- log4j配置失效日志中打印Debug信息最近发布项目的时候发现控制台打印的日志较往常多了很多,仔细一看,debug和info信息也赫然在列
- 前言记录下SpringBoot修改yml配置文件后无需重启服务的方式(打包后生效),效果如下:具体实现实现代码pom.xml<depe
- 目录了解程序集如何在C#.NET中加载程序集,模块和引用.NET中的程序集绑定绑定重定向当问题开始发生时故障排除边注References了解
- 本文实例总结了C#中split用法。分享给大家供大家参考,具体如下:以下是我转载的两个不同的人的,方便大家及自己查阅string s=&qu