一.冒泡排序
1.1概述
对于一组,多次将数组中的数两两比较,较大或者较小的数向后排(经过一轮比较后,最大/最小的数就会到结尾),循环直至有序排列。
1.2代码实现
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;
public class maopao {
public static void main(String[] args) {
System.out.println("请输入需要排序的数字,数字之间用空格或者回车隔开,输入完数字后输入任意非数字结束输入");
Scanner scanner = new Scanner(System.in); //复习scanner
Double[] input1= new Double[10]; //复习数组的定义 声明 创建
int count=0;
while (scanner.hasNextDouble()){ //复习scanner.xxx 不用if用while
input1[count] = scanner.nextDouble();
count++;
}
scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) { //去掉冗余
input2[i]=input1[i];
}
Double[] output = new Double[count];
output=mp(input2);
System.out.println("排序后的数组为:"+Arrays.toString(output));
}
private static Double[] mp(Double[] a) {
double temp;
// 降序排列
for (int i = 0; i < a.length-1; i++) { //n个数分n-1大轮
for (int j = 0; j < a.length-1-i; j++) { //每小轮有n-1-i次排序,每排一大轮每小轮少比较一次(每大轮出一个最值)
if(a[j]<a[j+1]){
temp=a[j+1];
a[j+1]=a[j];
a[j]=temp;
}
}
}
return a;
}
}
二.选择排序
2.1概述
从第一个数开始,依次和后面的数进行比较,小/大的数往前放(经过一轮最小/大数出现在最前面),之后从第二个数开始上述比较方式,循环至排序结束。
2.2代码实现
import java.util.Arrays;
import java.util.Scanner;
public class xuanze {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[100000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count] = scanner.nextDouble();
count++;
}
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i]=input1[i];
}
//System.out.println(Arrays.toString(input2));
Double[] output = new Double[count];
//output=xz(input2);
output=xz1(input2);
System.out.println("排序后的数是:"+Arrays.toString(output));
}
private static Double[] xz(Double[] a) {
double temp;
for (int i = 0; i < a.length-1; i++) {
for (int j = i; j < a.length-1; j++) {
if(a[j]>a[j+1]){
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
return a;
}
private static Double[] xz1(Double[] a) {
double temp;
for (int i = 0; i < a.length-1; i++) {
for (int j = i+1; j < a.length; j++) {
if(a[j]<a[i]){
temp=a[j];
a[j]=a[i];
a[i]=temp;
}
}
}
return a;
}
}
方法xz和方法xz1是两种不同写法。第一中方法是大轮内小轮比较,第二种方法是两两比较。
三.直接插入排序
3.1概述
将数组中第一个数看成一个数组,插入原数组第二个数,使得这两个数形成有序数组,再在这个有序数组插入原数组的第三个数,使得这三个数形成有序数组,以此类推,直到将原数组排序。
3.2代码实现
import java.util.Arrays;
import java.util.Scanner;
public class charu {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[10000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count]=scanner.nextDouble();
count++;
}
scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i] = input1[i];
}
Double[] output = new Double[count];
output = cr(input2);
//output=cr1(input2);
//output=cr2(input2);
System.out.println("排序后的数组是:"+ Arrays.toString(output));
}
private static Double[] cr(Double[] a) {
double temp = 0;
for (int i = 0; i < a.length-1; i++) { // i定义大轮数
for (int j = i+1; j >0 ; j--) { // j负责插入数的比较
if (a[j]<a[j-1]){
temp = a[j];
a[j]=a[j-1];
a[j-1] = temp;
}
}
}
return a;
}
private static Double[] cr1(Double[] a) {
for (int i = 1; i < a.length; i++) {
int j = i;
while (j>0&&a[j]<a[j-1]){ //j>0在前
Double temp = a[j];
a[j] = a[j-1];
a[j-1]=temp;
j--;
}
}
return a;
}
private static Double[] cr2(Double[] a) {
for (int i=1;i<a.length;i++){
for (int j = i; j >0 ; j--) {
if (a[j]<a[j-1]){
double temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
}
}
}
return a;
}
}
四.希尔排序
4.1概述
希尔排序又称缩小增量排序,基本思想是将原数组按增量ht分组,每个子文件安插入法排序。同样,下一个增量ht/2将文件再分为子文件,再按插入法排序。直到ht=1排好序。该方法的关键是是选择合适的增量。
4.2代码实现
import java.util.Arrays;
import java.util.Scanner;
public class xier {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[10000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count]=scanner.nextDouble();
count++;
}
scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i] = input1[i];
}
Double[] output = new Double[count];
//output = cr(input2);
output=xe(input2);
System.out.println("排序后的数组为:"+ Arrays.toString(output));
}
private static Double[] xe(Double[] a) {
// //克努特序列
// int jiange = 1;
// while (jiange<a.length/3){
// jiange=3*jiange+1;
// }
// for (int h = jiange; h <0; h=(h-1)/3)
// {...... ......}}
for (int h = a.length; h > 0; h=h/2) { //循环h
for (int i = h; i <a.length ; i++) { // 大轮循环 轮数:从第h+1个个数开始到最后一个数
for (int j = i; j-h >-1 ; j=j-h) { //小轮循环 后面的数与前面的数比较(前后差h)
//j-h大于-1保证索引大于等于0 j=j-h 前后两个数索引位置
if (a[j]<a[j-h]){
Double t = a[j];
a[j] = a[j-h];
a[j-h] = t;
}
}
}
}
return a;
}
}
五.快速排序
5.1概述
从数组中取一个数,作为基准数,将比这个数大或者等于的数放到右边,小于的放到左边。(降序),再对左右区间重复上述方法,直到每个区间只有一个数。
5.2代码实现
import java.util.Arrays;
import java.util.Scanner;
public class kuaisu {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[10000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count]=scanner.nextDouble();
count++;
}
scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i] = input1[i];
}
//Double[] output = new Double[count];
//output = cr(input2);
ks(input2,0,input2.length-1);
System.out.println("排序后的数组为:"+ Arrays.toString(input2));
}
private static void ks(Double[] a,int start,int end) {
//快速排序法需要利用前后数的位置,默认起始位置为0和length-1
if (start<end){
int index = getIndex(a,start,end);
//找到选取数对应的位置后 分前后两组递归
ks(a,start,index-1); //前组0到索引处-1
ks(a,index+1,end);//后组索引处+1至最后
}
}
private static int getIndex(Double[] a,int start,int end) {
int i = start;//定义前半部分指向 起始是0 从前向后
int j = end;//定义后半部分指向 起始在最后 从后向前
double x = a[i];//选取的数 可任意选取 此处a[0]
while (i < j) { //前半指向和后半指向不一样
while (i < j && a[j] >= x) { //从后向前找比选取数小的数
j--;//j一直减,直到找到为止,此时j位置上就是比选取数小的数
}
if (i < j) { //还未找到选取数对应的索引
a[i] = a[j]; //交换数
i++; //下次从前向后找起始,新数下标
}
while (i < j && a[i] <= x) { //向前向后找比比新数大的数
i++; //i一直加,直到找到为止,此时j位置上就是比选取数大的数
}
if (i < j) {
a[j] = a[i]; //交换
j--; //下次从后向前找起始,新数下标
}
}
a[i]=x; //完成一次排序
return i;
//快速排序法 选取一个数经过左小又大的方式(升序)分左右两组,并确定其在数组中的位置;
//再将左右两组分别重复上述方法(递归)再确定两个选取数的位置;
//再次选取、分组、选取、分组......最终确定每个数的位置,完成排序;
//所以ks方法中,有寻找索引和左右分组(前组从前向后,后组从后向前)
//在寻找索引的时候,除了有选取的数,还需要两个数,一个从前向后找位置(前索引),一个从后向前找位置(后索引)
//在两个数不等的时候,先从后找数,直到找到小数后索引一直减,找到后将该数移动到选取数的位置
//再从前找,直到找到大数前索引一直加,找到后将该数移动到后索引的位置
//重复上述,直到前后两索引数相同,定位选取数位置,此时前索引=后索引=选取数的索引
}
}
六.归并排序
6.1概述
假设初始序列有N个数,可以看成是N个有序的子序列,每个子序列长度是1,然后两两归并,得到N/2个长度为2或1的有序子序列,然后两两归并,如此重复直到排序完成。
6.2代码实现
import java.util.Arrays;
import java.util.Scanner;
public class guibing {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[10000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count]=scanner.nextDouble();
count++;
}
//scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i] = input1[i];
}
cf(input2,0,input2.length-1);
System.out.println("排序后的数组为:"+ Arrays.toString(input2));
}
private static void cf(Double[] a, int start,int end) {
//先拆分后合并,拆分需要计算从哪分开,需要计算分开位置,所以需要起始和结束位置(start、end)
//计算中间索引
int centerIndex = (start+end)/2;
//开始拆分,分为两组
if (start<end){
cf(a,start,centerIndex); //前半组,起始到分界
cf(a,centerIndex+1,end);// 后半组,分界+1到最后
//gb(a,start,centerIndex,end);
}
gb(a,start,centerIndex,end); //合并并排序
//合并需要知道从哪合并,所以有centerIndex
//排序需要有两个‘移动索引’来比较两个位置的数的大小,所以有start和end
}
private static void gb(Double[] a,int start,int center,int end) {
Double[] temp = new Double[end-start+1];//定义一个数组存放比较后的数据(临时索引)
int i =start; //定义左边(前组)的起始索引
int j =center+1;//定义后边(后足)的起始索引
int index = 0;//临时数组的索引 临时索引
while (i<=center&&j<=end){ //当两个移动索引都在范围内时
if (a[i]<=a[j]){ //比较两个位置的数
temp[index]=a[i]; //提出较小数
i++; //移动索引移动------------------------(1)
}else { //同理
temp[index]=a[j];
j++;//-----------------------------------(2)
}
index++;
}
//处理剩余元素
while (i<=center){ //左边元素剩余(i仍然小于等于center) i在(1)处已经移动
temp[index]=a[i]; //赋值
i++; //移动索引移动
index++; //临时索引移动
}
while (j<=end){
temp[index]=a[j];
j++;
index++;
}
for (int k = 0; k < temp.length; k++) { //数组转移
a[k+start]=temp[k]; // 注意加start
}
}
}
//归并排序就是先拆后合
//先拆:找分界点拆 分界点需要有起始和结束位置计算 并且需要一直拆所以递归
//合并:前后小组索引移动比较数的大小 将第i个小数/大数存放在临时数组的第i个数上的位置
//最后 处理剩余
七.基数排序
7.1概述
基数排序法基于对不同位的分组(从个位开始)进行排序。
7.2代码实现
import java.util.Arrays;
import java.util.Scanner;
import static sun.management.MemoryUsageCompositeData.getMax;
public class jishu {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[10000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count]=scanner.nextDouble();
count++;
}
scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i] = input1[i];
}
Double[] output = new Double[count];
//output = cr(input2);
output = js(input2);
System.out.println("排序后的数组为:"+ Arrays.toString(output));
}
private static Double[] js(Double[] a) {
double max = getMaxNumber(a); //得到数组中的最大值
int len = String.valueOf(max).length(); //得到最大数一共几位,几位就大循环几次
double[][] temp = new double[10][a.length]; //根据需要创建
int[] c = new int[10]; //c用于计数 c[余数]=有几个某位相同的数 根据需要创建
for (int i = 0,n=1; i < len; i++,n=n*10) { //大循环 n为后加
for (int j = 0; j < a.length; j++) { //将a中的每个数转移出去
int ys = (int) ((a[j] / n) % 10); //丢失精度 该代码比较小数有问题 后加
temp[ys][c[ys]] = a[j]; //赋值 temp[余数][该余数下有多少个数]
c[ys]++;
}
//反转移数
int index=0; //创建反转回的索引
//for (int j = 0; j < a.length; j++) { //错误
for (int k = 0; k < c.length; k++) { //遍历余数(0-9)
if (c[k]!=0){
for (int l = 0; l < c[k]; l++) {//遍历c
a[index]=temp[k][l];
index++;
}}
c[k]=0;
}
//}
//Arrays.fill(c, 0);// 正确
}
return a;
}
private static double getMaxNumber(Double[] a) {
double max = a[0];
for (int i = 0; i < a.length; i++) {
if (a[i]>max){
max = a[i];
}
}
return max;
}
}
//基数排序法不需要两个索引去比较两个数的大小
//需要将数不断按...、十、个位分组排序
八.堆排序
8.1概述
堆排序利用堆这种数据结构进行偶爱徐,堆排序是一种选择性排序。
8.2代码实现
import java.util.Arrays;
import java.util.Scanner;
public class dui {
public static void main(String[] args) {
System.out.println("输入需要排序的数组");
Double[] input1 = new Double[10000000];
int count = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()){
input1[count]=scanner.nextDouble();
count++;
}
scanner.close();
Double[] input2 = new Double[count];
for (int i = 0; i < count; i++) {
input2[i] = input1[i];
}
Double[] output = new Double[count];
//output = cr(input2);
output=d(input2);
System.out.println("排序后的数组为:"+ Arrays.toString(output));
}
private static Double[] d(Double[] a) {
//定义开始调整的位置
int start = (a.length-1)/2; //从一半开始调 方便遍历数组全部元素
for (int i = start; i >=0 ; i--) {
toMaxHeap(a,a.length,i); // 将数组变成一个大顶堆
}
for (int i = a.length-1; i >=0 ; i--) { //最后一个数和第一个数交换位置并再次调整大顶堆
double t = a[0];
a[0] = a[i];
a[i] = t;
toMaxHeap(a,i,0);
}
return a;
}
private static void toMaxHeap(Double[] a, int size, int index) {
int left = index*2+1; //左节点
int right = index*2+2;//右节点
int maxIndex = index;
//获取最大节点对应的索引
if(left<size&&a[left]>a[maxIndex]){
maxIndex=left;
}
if (right<size&&a[right]>a[maxIndex]){
maxIndex=right;
}
//调换位置
if (maxIndex!=index){
double t = a[maxIndex];
a[maxIndex] = a[index];
a[index] = t;
//调换之后可能会影响到下面的子数,不是大顶堆,我们还需要再次调换
toMaxHeap(a,size,maxIndex);
}
}
}
九.总结归纳
时间复杂度和空间复杂度比较
排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 |
冒泡排序 | O(n^2) | O(1) | 很好 |
选择排序 | O(n^2) | O(1) | 很差 |
插入排序 | O(n^2) | O(1) | 很好 |
希尔排序 | 介于O(n)和O(n ^ 2)之间 | O(1) | 较差 |
快速排序 | O(nlogn) | O(logn) | 较差 |
并归排序 | O(nlogn) | O(n) | 较好 |
基数排序 | O(n+k) | O(n+k) | 很不好 |
堆排序 | O(nlogn) | O(1) | 较差 |
其中k是最大元素的值。
更多:8 种常见排序的总结