排序算法
注:本文章是看韩顺平老师将的数据结构java,以及在网上查找的其他文章,以及我的理解总结的;有说的不明白的地方,可以在评论区告诉我,我会更新和修改。
排序算法的分类
冒泡排序
基本介绍
思路
代码实现
public class BubblingSort {
public static void main(String[] args) {
//定义一个数组
int[] Arr = {-1,-9,6,58,99,55};
int temp;
//冒泡排序
for(int i=0;i<Arr.length-1;i++) {
for(int j = 0;j<Arr.length-i-1;j++) {
if(Arr[j]>Arr[j+1]) {
temp = Arr[j];
Arr[j] = Arr[j+1];
Arr[j+1] = temp;
}
}
System.out.printf("第%d次后",i);
System.out.println(Arrays.toString(Arr));
}
}
}
代码优化
public class BubblingSort {
public static void main(String[] args) {
//测试一手,输入800000个数据
int[] Arr = new int[80000];
for(int i=0;i<80000;i++) {
Arr[i] = (int)(Math.random()*80000);
}
//定义时间变量
Date date1 = new Date();
SimpleDateFormat s1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String Date_1= s1.format(date1);
System.out.println("排序前的时间:"+Date_1);
BubblingS(Arr);
Date date2 = new Date();
String Date_2= s1.format(date2);
System.out.println("排序后的时间:"+Date_2);
}
//封装成方法
public static void BubblingS(int[] Arr) {
int temp;
//冒泡排序
boolean loop = false;//定义监管loop,如果在一次排序中如果不交换,则表示数组排序已经完成,便可退出
for(int i=0;i<Arr.length-1;i++) {
for(int j = 0;j<Arr.length-i-1;j++) {
if(Arr[j]>Arr[j+1]) {
loop = true;
temp = Arr[j];
Arr[j] = Arr[j+1];
Arr[j+1] = temp;
}
}
if(!loop) {//在一次排序中,一次也没交换
break;
}else {
loop=false;//如果交换了,重新把loop改为false;
}
//System.out.printf("第%d次后",i);
//System.out.println(Arrays.toString(Arr));
}
}
}
测试80000个数据排序时间
选择排序
基本介绍
给出一个数组Arr,首先从【0——n-1】中找出一个最小值,并与第一个数(Arr[0])交换 ,然后再从【1——n-1】中找出一个最小值,并与第二个数(Arr[1])交换…………,依次进行,直至最后,但最后一个不用找 换,所以值循环n-1次
思路
代码实现
//包装成方法
public static void ChoiceS_(int[] Arr) {
for (int j = 0; j < Arr.length - 1; j++) {
int mintemp = Arr[j];
int index = j;
for (int i = j; i < Arr.length; i++) {
if (Arr[i] < mintemp) {
mintemp = Arr[i];
index = i;
}
}
Arr[index] = Arr[j];
Arr[j] = mintemp;
//System.out.println("第" + (j + 1) + "次排序" + Arrays.toString(Arr));
}
}
测试80000个数据的时间
比冒泡吊多了
插入排序
基本介绍
把数组Arr看成一个顺序序列,一个乱序序列,顺序序列一开始就一个为数组开头,乱序是剩下的。每次插入,取乱序序列第一个值,按照顺序查入顺序数列(后移吊)
思路
代码实现
//包装成方法
public static void ChoiceS_(int[] Arr) {
for (int j = 0; j < Arr.length - 1; j++) {
int mintemp = Arr[j];
int index = j;
for (int i = j; i < Arr.length; i++) {
if (Arr[i] < mintemp) {
mintemp = Arr[i];
index = i;
}
}
Arr[index] = Arr[j];
Arr[j] = mintemp;
//System.out.println("第" + (j + 1) + "次排序" + Arrays.toString(Arr));
}
}
测试80000个数据
希尔排序
基本介绍
把一个数组根据下标分为int gap =(length/2)组,分别对每组进行排序,排序完在接着分为gap/2组,在分别对每组进行排序,…………直到gap等于1,排序结束。
图解思路
代码实现
第一中是交换法实现,就是每两个数进行比较,如果条件符合就交换。这种方法速度慢。
第二钟是移动法,类似于插入排序,速度块,比较吊。
package com.hanfei.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class ShellSort_1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建测试数组
// 测试一手,输入800000个数据
int[] Arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
Arr[i] = (int) (Math.random() * 800000);
}
// 定义时间变量
Date date1 = new Date();
SimpleDateFormat s1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String Date_1 = s1.format(date1);
System.out.println("排序前的时间:" + Date_1);
ShellS_2(Arr);
Date date2 = new Date();
String Date_2 = s1.format(date2);
System.out.println("排序后的时间:" + Date_2);
}
// 交换法的希尔排序
public static void ShellS(int[] Arr) {
// 定义辅助变量
int temp = 0;
int count = 0;
for (int gap = Arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < Arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (Arr[j] > Arr[j + gap]) {
temp = Arr[j + gap];
Arr[j + gap] = Arr[j];
Arr[j] = temp;
}
}
}
count++;
System.out.println("第" + count + "次" + Arrays.toString(Arr));
}
}
// 移位法的查入排序
public static void ShellS_2(int[] Arr) {
int count = 0;
for (int gap = Arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < Arr.length; i++) {
int temp = Arr[i];
int index = i - gap;
if (Arr[i] < Arr[index]) {
while (index >= 0 && temp < Arr[index]) {
Arr[index + gap] = Arr[index];
index -= gap;
}
Arr[index + gap] = temp;
}
}
count++;
//System.out.println("第" + count + "次" + Arrays.toString(Arr));
}
}
}
移动法比较800000个数据,仅仅只需两秒。80000只需不到一秒
快速排序
基本介绍
以数组{6,3,7,1,9,4,8,5,2,10}为例。 1.为了便于理解,就以6作为节点,开始排序时low等于6,high等于10 2.从由右往左找比节点6小的数就是2,从左往右找比节点6大的数就是7,把这两个数交换(这样查找的目的是为了将大的数放后面,较小的数放前面) 交换后: 3.交换后,high接着往左找比节点6小的数,同时low接着往右找比6大的数,分别是5和9,交换low和high 交换后: 4.接着相同操作,high往左找到比6小的数4,low往右找大于节点6的数时,low与high相遇,那么本轮查找就结束。接着节点6与low和high的相遇点4交换。这样比6小的数都在其前面,比6da的数都在其后面。 交换后: 5.一轮操作结束后,原数组被节点6“一分为二”,分别是比6都小的4,3,2,1,5和比6都大的8,9,7,10两个数组。接着就将这两个数组重复上面的操作,操作完成后理论上是数组被“一分为四”,四个数组再进行上面的操作如此循环
注意:上面讲的是以左端(6)为中轴,我写的代码是以右端(10)为中轴写的;
代码实现
package com.hanfei.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class ShellSort_1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建测试数组
// 测试一手,输入800000个数据
int[] Arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
Arr[i] = (int) (Math.random() * 800000);
}
// 定义时间变量
Date date1 = new Date();
SimpleDateFormat s1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String Date_1 = s1.format(date1);
System.out.println("排序前的时间:" + Date_1);
ShellS_2(Arr);
Date date2 = new Date();
String Date_2 = s1.format(date2);
System.out.println("排序后的时间:" + Date_2);
}
// 交换法的希尔排序
public static void ShellS(int[] Arr) {
// 定义辅助变量
int temp = 0;
int count = 0;
for (int gap = Arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < Arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (Arr[j] > Arr[j + gap]) {
temp = Arr[j + gap];
Arr[j + gap] = Arr[j];
Arr[j] = temp;
}
}
}
count++;
System.out.println("第" + count + "次" + Arrays.toString(Arr));
}
}
// 移位法的查入排序
public static void ShellS_2(int[] Arr) {
int count = 0;
for (int gap = Arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < Arr.length; i++) {
int temp = Arr[i];
int index = i - gap;
if (Arr[i] < Arr[index]) {
while (index >= 0 && temp < Arr[index]) {
Arr[index + gap] = Arr[index];
index -= gap;
}
Arr[index + gap] = temp;
}
}
count++;
//System.out.println("第" + count + "次" + Arrays.toString(Arr));
}
}
}
注意:当选最后一个为中轴数的时候,需要先从左边找比他大的数,因为最后左边找到的最后一个数要与中轴值交换
归并排序
基本介绍
归并排序主要思想是分而治之,先将数组逐步分开,在排序合并(很吊)
图解思路
代码实现
package com.hanfei.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class MergeSort {
public static void main(String[] args) {
int[] Arr = new int[8000000];
int[] temp = new int[Arr.length];
// 测试一手,输入800000个数据
for (int i = 0; i < 8000000; i++) {
Arr[i] = (int) (Math.random() * 800000);
}
// 定义时间变量
Date date1 = new Date();
SimpleDateFormat s1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String Date_1 = s1.format(date1);
System.out.println("排序前的时间:" + Date_1);
MerGe_1(Arr, 0, Arr.length-1, temp);
Date date2 = new Date();
String Date_2 = s1.format(date2);
System.out.println("排序后的时间:" + Date_2);
}
// 分和和
//left代表数组最左边下边,初始0
//right代表数组最右边下标,初始Arr.length-1
//Arr,排序数组
//temp 辅助数组
public static void MerGe_1(int[] Arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
// 向左边分
MerGe_1(Arr, left, mid, temp);
// 向右分
MerGe_1(Arr, mid + 1, right, temp);
Merge_(Arr, left, mid, right, temp);
}
}
// 写和并的过程
public static void Merge_(int[] Arr, int left, int mid, int right, int[] temp) {
int l = left;
int r = mid + 1;
int t = 0;// temp数组辅助变量
// 比较两个有序数组按顺序存入temp中
while (l <= mid && r <= right) {
// 如果左测数组值比右边小就存左边的,否则存右边的
if (Arr[l] <= Arr[r]) {
temp[t] = Arr[l];
t += 1;
l += 1;
} else {
temp[t] = Arr[r];
t += 1;
r += 1;
}
}
// 将剩余值存入temp
// 先判断左边
while (l <= mid) {
temp[t] = Arr[l];
t += 1;
l += 1;
}
// 再判断右边
while (r <= right) {
temp[t] = Arr[r];
t += 1;
r += 1;
}
// 最后将temp数组的值赋值给Arr
t = 0;// 将t指向第一个
int tempLeft = left;
while (tempLeft <= right) {
Arr[tempLeft] = temp[t];
tempLeft++;
t++;
}
}
}
基数排序
基本介绍
将数组中的数,按照不同位数的大小存入对应的桶中(二维数组),先按个位-十位-……等等依次,每次存储一次后,都要按桶中的顺序在输回原数组。(说的不好!下次一定)。
图解思路
代码实现
package com.hanfei.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class RedixSort {
public static void main(String[] args) {
// 测试一手,输入800000个数据
int[] Arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
Arr[i] = (int) (Math.random() * 800000);
}
// 定义时间变量
Date date1 = new Date();
SimpleDateFormat s1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String Date_1 = s1.format(date1);
System.out.println("排序前的时间:" + Date_1);
Redix_1(Arr);
Date date2 = new Date();
String Date_2 = s1.format(date2);
System.out.println("排序后的时间:" + Date_2);
}
// 排序方法
public static void Redix_1(int[] Arr) {
int max = Arr[0];
// 去数组中最大的数
for (int i = 0; i < Arr.length; i++) {
if (Arr[i] > max) {
max = Arr[i];
}
}
int maxlength = (max + "").length();
// 创建二维数组,做桶元素,定义十个桶存储各个位数的数
// 10代表0-9十个数
// 不知道一个桶存放几个元素,所以按照最环情况处理
int[][] bucket = new int[10][Arr.length];
// 定义一维数组,当作每个桶的指针
int[] bucketcount = new int[10];
for (int r = 0, n = 1; r < maxlength; r++, n *= 10) {
// 第一次存储
// 遍历所给数组
for (int i = 0; i < Arr.length; i++) {
// 判断个位数是什么
int weishuDate = Arr[i] / n % 10;
bucket[weishuDate][bucketcount[weishuDate]] = Arr[i];
// 该位置的指针自增
bucketcount[weishuDate]++;
}
int index = 0;
// 从桶中依次取出数据
for (int j = 0; j < 10; j++) {
if (bucketcount[j] != 0) {
for (int l = 0; l < bucketcount[j]; l++) {
Arr[index] = bucket[j][l];
index++;
}
}
// 靠这条语句控制数组数据
bucketcount[j] = 0;
}
}
}
}
排序算法的比较
堆排序
基本介绍
代码实现
package com.hanfei.sort;
import java.util.Arrays;
import org.omg.PortableServer.ID_ASSIGNMENT_POLICY_ID;
public class HeapSort {
public static void main(String[] args) {
int[] Arr = {20000,3,323,-2,56,-56};
for(int i = Arr.length/2-1;i<0;i--) {
adjustHeap(Arr, i, Arr.length);
}
for (int j = Arr.length-1; j > 0; j--) {
int temp = Arr[j];
Arr[j] = Arr[0];
Arr[0] = temp;
/*为什么从0开始?
因为在第一次构建大顶堆后让堆顶元素和末尾元素进行交换
而对于其他的非叶子结点所对应的子树都是大顶堆就无需调整,
只需要堆顶元素(下标为0的非叶子结点)的子树调整成大顶堆
*/
adjustHeap(Arr,0,j);
}
System.out.println(Arrays.toString(Arr));
}
//将完全二叉树转化为大堆顶
/**
*
* @param Arr 要转化的数组
* @param i 从哪个结点开始转化
* @param length 转化数组的长度
*/
public static void adjustHeap(int[] Arr,int i,int length) {
/*
* 取出当前叶子结点的值
*/
int temp = Arr[i];
for(int k = i*2+1;k<length;k = i*2+2) {
if(k+1<length&&Arr[k]<Arr[k+1]) {//如果右结点大于左节点
k++;//k指向右节点
}
if(temp<Arr[k]) {
Arr[i] = Arr[k];
i = k;
}else {
break;
}
Arr[k]=temp;
}
}
}