排序
一、简单排序
1.1Comparable接口
需求:
1.定义一个学生类Student ,具有年龄age和姓名username两个属性,并通过Comparable接口提供比较规则;
2.定义测试类Test ,在测试类Test中定义测试方法Comparable getMax(Comparable C1,Comparable C2)完成测试
package com.zhao.sort;
//比较两个学生的年龄
public class TestComparable {
public static void main(String[] args) {
Student student = new Student(18,"李四");
Student student2 = new Student(20,"李四");
Comparable max = getMax(student, student2);
System.out.println(max);
}
public static Comparable getMax(Comparable c1,Comparable c2){
int result = c1.compareTo(c2);
if (result >= 0){
return c1;
}else {
return c2;
}
}
}
class Student implements Comparable<Student>{
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
1.2冒泡排序
排序原理:
1.比较相邻的元素。如果前一个元素比后一个元素大 ,就交换这两个元素的位置。
2.对每一对相邻元素做同样的工作 ,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值。
时间复杂度为(n^2)
类名 | Bubble |
---|---|
构造方法 | Buffle():创建Bubble对象 |
成员方法 | 1.public static void sort(Comparable[] v):对数组内的元素进行排序 2.private static boolean greater(Comparable v,Comparable W);判断v是否大于w 3.private static void exch(Comparable[] a,int i,intj)。交换a数组中,索引和索引j处的值 |
package com.zhao.sort;
import java.util.Arrays;
public class BubbleTest {
public static void main(String[] args) {
Integer[] arr = {5,4,2,3,6,1};
Bubble.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
//冒泡排序
class Bubble{
//对数组内的元素进行排序
public static void sort(Comparable[] v){
for (int i=v.length-1;i>0;i--){
for (int j=0;j<i;j++){
if (greater(v[j],v[j+1])){
exch(v,j,j+1);
}
}
}
}
//判断v是否大于w
private static boolean greater(Comparable v,Comparable w){
return v.compareTo(w) > 0;
}
//交换a数组中,索引和索引j处的值
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i]=a[j];
a[j]=temp;
}
}
优化后
public static Integer[] bubbleSort(Integer[] arr){
int num = 0;
for (int i=0;i< arr.length-1;i++){
boolean flag = false;
for (int j=0;j< arr.length-1-i;j++){
if (arr[j]> arr[j+1]){
num = arr[j];
arr[j] = arr[j+1];
arr[j+1] = num;
flag = true;
}
}
if (!flag){
break;
}else {
flag = false;
}
}
return arr;
}
1.3选择排序
需求:
排序前: {4,6,8,7,9,2,10,1}
排序后: {1,2,4,5,7,8,9,10}
排序原理:
1.每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值,最后可以找到最小值所在的索引
2.交换第一个索引处和最小值所在的索引处的值
时间复杂度为(n^2)
类名 | Selection() |
---|---|
构造方法 | Selection():创建Selection对象 |
成员方法 | 1.public static void sort(Comparable[] v):对数组内的元素进行排序 2.private static boolean greater(Comparable v,Comparable W);判断v是否大于w 3.private static void exch(Comparable[] a,int i,intj)。交换a数组中,索引和索引j处的值 |
代码实现
package com.zhao.sort;
import java.util.Arrays;
public class SelectionTest {
public static void main(String[] args) {
Integer[] arr = {5,4,2,3,6,1};
Selection.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
//选择排序
class Selection{
//对数组内的元素进行排序
public static void sort(Comparable[] v){
for (int i=0;i<= v.length-2;i++){
//定义一一个变量,记录最小元素所在的索引,默认为参与选择排序的第一个元素所在的位置
int minIndex = i;
for (int j=i+1;j<=v.length-1;j++){
if (greater(v[minIndex],v[j])){
minIndex = j;
}
}
exch(v,i,minIndex);
}
}
//判断v是否大于w
private static boolean greater(Comparable v,Comparable w){
return v.compareTo(w) > 0;
}
//交换a数组中,索引和索引j处的值
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i]=a[j];
a[j]=temp;
}
}
优化后:
public static void selectionSort(Integer[] arr){
for (int i=0 ;i< arr.length-1;i++){
int minIndex = i;
int min = arr[i];
for (int j = i+1; j < arr.length ; j++) {
if (min > arr[j]){
min = arr[j];
minIndex = j;
}
}
if (minIndex != i){
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
1.4 插入排序
插入排序( Insertion sort )是一种简单直观且稳定的排序算法。
插入排序的工作方式非常像人们排序一手扑克牌一样。开始时,我们的左手为空并且桌子上的牌面朝下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到-张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较。
排序原理
1.把所有的元素分为两组,已经排序的和未排序的;
2.找到未排序的组中的第一个元素 ,向已经排序的组中进行插入;
3.倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位;
类名 | Insertion() |
---|---|
构造方法 | Insertion():创建Insertion对象 |
成员方法 | 1.public static void sort(Comparable[] v):对数组内的元素进行排序 2.private static boolean greater(Comparable v,Comparable W);判断v是否大于w 3.private static void exch(Comparable[] a,int i,intj)。交换a数组中,索引和索引j处的值 |
代码实现:
package com.zhao.sort;
import java.util.Arrays;
public class InsertionTest {
public static void main(String[] args) {
Integer[] arr = {5,4,2,3,6,1};
Insertion.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
//插入排序
class Insertion{
//对数组内的元素进行排序
public static void sort(Comparable[] v){
for (int i =1;i< v.length;i++){
for (int j =i;j>0;j--){
//比较索引j处的值和索引j-1处的值,如果索引j-1的值大,则交换数据
if (greater(v[j-1],v[j])){
exch(v,j-1,j);
}else{
break;
}
}
}
}
//判断v是否大于w
private static boolean greater(Comparable v,Comparable w){
return v.compareTo(w) > 0;
}
//交换a数组中,索引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.1希尔排序
排序原理:
1.选定一个增长量h ,按照增长量h作为数据分组的依据,对数据进行分组;
2.对分好组的每一组数据完成插入排序;
3.减小增长量,最小减为1 ,重复第二步操作。
增长量h的确定:增长量h的值每一固定的规则,我们这里采用以下规则:
int h=1
while(h<数组长度/2){
h=2h+1;
}
//循环结束后我们就可以确定h的最大值
h的减小格则为:
h=h/2
类名 | Shell() |
---|---|
构造方法 | Shell():创建Bubble对象 |
成员方法 | 1.public static void sort(Comparable[] v):对数组内的元素进行排序 2.private static boolean greater(Comparable v,Comparable W);判断v是否大于w 3.private static void exch(Comparable[] a,int i,intj)。交换a数组中,索引和索引j处的值 |
代码实现
package com.zhao.sort;
import java.util.Arrays;
public class ShellTest {
public static void main(String[] args) {
Integer[] arr = {5,4,2,3,6,1,5,10,55,66};
Bubble.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
//希尔排序
class Shell{
//对数组内的元素进行排序
public static void sort(Comparable[] v){
//1.根据数组v的长度,确定增长量h的初始值
int h =1;
while (h<v.length/2){
h= h*2+1;
}
//2.希尔排序
while(h>=1){
//排序
//2.1 找到待插入的元素
for (int i = h;i<v.length;i++){
//2.2把待插入的元素插入到有序数列中
for (int j=i;j>=h;j=j-h){
//待插入的元素是a[j],比较a[j]和[j-h]
if (greater(v[j-h],v[j])){
//交换元素
exch(v,j-h,j);
}else{
//待插人元素已经找到了合适的位置,结束循环;
break;
}
}
}
h /= 2;
}
}
//判断v是否大于w
private static boolean greater(Comparable v,Comparable w){
return v.compareTo(w) > 0;
}
//交换a数组中,索引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.2 快速排序
快速排序(Quicksort) 是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列.
代码实现
package com.zhao.sort;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {1,66,85,2,3,5,6,-4};
sort(arr,0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr,int left,int right){
int l_index = left;//左下标
int r_index = right;//右下标
//中轴值
int pivot = arr[(left+right)/2];
int temp = 0;//临时变量,作为交换时使用
//while循环的目的是让比pivot小的放到左边,大的放在右边
while (l_index < r_index){
while (arr[l_index] < pivot){//在pivot的左边找,找到大于pivot值,才退出
l_index += 1;
}
while (arr[r_index] > pivot){//在pivot的右边找,找到小于pivot值,才退出
r_index -= 1;
}
if (l_index >= r_index){
break;
}
//交换
temp = arr[l_index];
arr[l_index] = arr[r_index];
arr[r_index] = temp;
//如果交换完后,发现这个arr[l_index] == pivot值相等r--,前移
if (arr[l_index] == pivot){
r_index -= 1;
}
//如果交换完后,发现这个arr[r_index] == pivot值相等,后移
if (arr[r_index] == pivot){
l_index += 1;
}
}
if (l_index == r_index){
l_index += 1;
r_index -= 1;
}
if (left < r_index){
sort(arr, left, r_index);
}
if (right > l_index){
sort(arr,l_index,right);
}
}
}
2.3归并排序
归并排序(MERGE- SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide- and conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案修补在一起,即分而治之)。
代码实现
package com.zhao.sort;
import java.util.Arrays;
public class MergetSort {
public static void main(String[] args) {
int[] arr = {1,5,8,3,2,9,10};
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
System.out.println(Arrays.toString(arr));
}
//分 + 和方法
public static void mergeSort(int[] arr,int left,int right , int[] temp){
if (left < right){
int mid = (left+right)/2;//中间索引
//向左递归进行分解
mergeSort(arr, left, mid, temp);
//向右递归进行分解
mergeSort(arr,mid+1,right,temp);
//合并
merge(arr,left,mid,right,temp);
}
}
/**
* 合并
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 中转数组
*/
public static void merge(int[] arr,int left,int mid,int right , int[] temp){
int i = left;//初始化i, 左边有序序列的初始索引
int j = mid+1;//初始化j,右边有序序列的初始索引
int t = 0;// 指向temp数组的当前索引
//(-)
// 先把左右两边(有序)的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕为止
while (i <= mid && j <= right){
if (arr[i] <= arr[j]){
temp[t] = arr[i];
t += 1;
i += 1;
}else {
temp[t] = arr[j];
t += 1;
j += 1;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到temp
while (i <= mid){
temp[t] = arr[i];
t += 1;
i += 1;
}
while (j <= right){
temp[t] = arr[j];
t += 1;
j += 1;
}
//(三)
//将temp数组的元素拷贝到arr
//并不是每次都拷贝所有
t = 0;
int tempLeft = left;
while (tempLeft <= right){
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
2.4基数排序
基数排序(桶排序)介绍:
- 基数排序(radixsort) 属于“分配式排序”(distributionsort) ,又称“桶子法”(bucket sort)或binsort, 顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
- 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
- 基数排序(Radix Sort)是桶排序的扩展
- 基数排序是1887年赫尔曼何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较.
基数排序的说明:
- 基数排序是对传统桶排序的扩展,速度很快
- 基数排序是经典的空间换时间的方式,占用内存很大,当对海量数据排序时,容易造成OutOfMemoryError。
- 基数排序时稳定的。[注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,
r[i]=r[i]
, 且r[i]
在r[j]
之前, 而在排序后的序列中,r[i]
仍在r[j]
之 前,则称这种排序算法是稳定的;否则称为不稳定的] - 有负数的数组,我们不用基数排序来进行排序
代码实现
package com.zhao.sort;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = {122,56,12,89,563,555};
radixSort(arr);
}
public static void radixSort(int[] arr){
//1.得到数组中最大的数的位数
int max = arr[0];//假设第一个数就是最大数
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max){
max = arr[i];
}
}
//得到最大数是几位数
int maxLength = (max+"").length();
//定义一个二维数组,表示10个桶,每个桶就是一个二维数组
//1.二维数组包含10个一维数组
//2.为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr。length
//3.明确,基数排序是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
// 比如: bucketElementCounts[0] ,记录的就是bucket[0] 桶的放入数据个数
int[] bucketCount = new int[10];
for (int i = 0,n = 1; i < maxLength; i++,n *= 10) {
//针对每个元素的对应位进行排序
for (int j = 0; j < arr.length; j++) {
int digitOfElement = (arr[j]/n)%10;//取出每一个元素的对应位的值
//放入相应的桶中
bucket[digitOfElement][bucketCount[digitOfElement]] = arr[j];
bucketCount[digitOfElement]++;
}
//取出数据,放入到原数组
int index = 0;
//遍历每一个桶,并将桶中的数据,放入到原数组
for (int k =0 ; k<bucketCount.length ; k++){
//如果桶中有数
if (bucketCount[k] != 0){
//循环该桶及第k个一维数组
for (int l = 0; l < bucketCount[k]; l++) {
//取出数据放入到arr
arr[index++] = bucket[k][l];
}
}
//第i+1轮处理后,需要将每个bucketElementCounts[k] = 0 !!!
bucketCount[k] = 0;
}
System.out.println("第"+(i+1)+"次,对每个位的排序处理"+ Arrays.toString(arr));
}
}
}
2.5堆排序
堆排序基本介绍
- 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为0(nlogn),它也是不稳定排序。
- 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆,注意:没有要求结点的左孩子的值和右孩子的值的大小关系。>
- 大顶堆特点:
arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
- 大顶堆特点:
- 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
- 小顶堆特点:
arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
- 小顶堆特点:
- 一般升序采用大顶堆,降序采用小顶堆
堆排序的基本思想是:
- 将待排序序列构造成一个大项堆
- 此时,整个序列的最大值就是堆项的根节点。
- 将其与末尾元素进行交换,此时末尾就为最大值。
- 然后将剩余n-1个元素重新构造成一-个堆, 这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
可以看到在构建大项堆的过程中,元素的个数逐渐减少,最后就得到一个有序序列了.
代码实现:
package com.zhao.tree;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {122,56,12,89,563,555};
heapSort(arr);
System.out.println(Arrays.toString(arr));
heapSort2(arr);
System.out.println(Arrays.toString(arr));
}
//堆排序升序
public static void heapSort(int[] arr){
int temp = 0;
System.out.println("升序堆排序");
//1.将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
for (int i = arr.length/2-1; i >= 0 ; i--) {
adjustHeap(arr,i, arr.length);
}
//2.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
//3.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
for (int j = arr.length-1 ; j > 0 ; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr,0,j);
}
}
//堆排序降序
public static void heapSort2(int[] arr){
int temp = 0;
System.out.println("降序堆排序");
//1.将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
for (int i = arr.length/2-1; i >= 0 ; i--) {
adjust(arr,i, arr.length);
}
//2.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
//3.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
for (int j = arr.length-1 ; j > 0 ; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjust(arr,0,j);
}
}
/**
* 将一个数组(二叉树),调整成一个大顶堆
* @param arr 待调整的数组
* @param i 表示非叶子节点在数组中的索引
* @param length 表示对多少个元素进行调整,length逐渐减少
*/
public static void adjustHeap(int[] arr,int i ,int length){
int temp = arr[i];//取出当前元素的值,保存在临时变量
//开始调整
//1. j = i*2+1 j是i节点的左子节点
for (int j = i*2+1; j < length ; j = j*2+1) {
if (j+1 < length && arr[j]<arr[j+1]){//左子节点的值小于右子节点的值
j++; // j 指向右子节点
}
if (arr[j] > temp){//子节点比父节点大
arr[i] = arr[j];//把大的值赋给当前节点
i = j; // i 指向 j
}else {
break;
}
}
//当for循环结束后,我们已经将以i为父结点的树的最大值,放在了最顶(局部)
arr[i] = temp;//循环结束把较小的值付给子节点
}
/**
* 将一个数组(二叉树),调整成一个小顶堆
* @param arr 待调整的数组
* @param i 表示非叶子节点在数组中的索引
* @param length 表示对多少个元素进行调整,length逐渐减少
*/
public static void adjust(int[] arr,int i ,int length){
int temp = arr[i];
for (int j = i*2+1; j < length; j=j*2+1) {
if (j+1<length && arr[j] > arr[j+1]){//左子节点的值大于右子节点的值
j++;
}
if (arr[j]<temp){//子节点比父节点小
arr[i] = arr[j];//把大的值赋给当前节点
i = j; // i 指向 j
}else {
break;
}
}
//当for循环结束后,我们已经将以i为父结点的树的最小值,放在了最顶(局部)
arr[i] = temp;//循环结束把较大的值付给子节点
}
}