冒泡排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 冒泡排序规则:
* 1.排序的次数是数组长度-1
* 2.每次都将本趟最大或最小的那个数放到倒数位置
* 3.(优化)当发现一趟排序没有进行交换的时候说明当前的数组已经是有序的,不要再往下进行比较了
*/
public class BubbleSort {
public static void main(String[] args) {
// int[] arr = {3, 9, -1, 10, 20};
//测是大量数据时冒泡排序的耗时
int[] arr = new int[80000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random() * 80000);
}
System.out.println("排序中...");
long start = System.currentTimeMillis();
bubbleSort(arr);
long end = System.currentTimeMillis();
System.out.println("冒泡排序耗时:" + (end - start) + "ms");
//
// int temp = 0;//交换时使用的临时变量
// boolean flag = false;//设置一个哨兵用于优化冒泡排序
//为了方便理解写出冒泡排序的原始解法
// //第一趟将第一大的数排到倒数第一的位置
// for (int j = 0; j < arr.length - 1; j++){
// if (arr[j] > arr[j + 1]){
// temp = arr[j + 1];
// arr[j + 1] = arr[j];
// arr[j] = temp;
// }
// }
// System.out.println("第一趟排序完之后的数组:" + Arrays.toString(arr));
//
// //第二趟将第二大的数排到倒数第二的位置
// for (int j = 0; j < arr.length - 2; j++){
// if (arr[j] > arr[j + 1]){
// temp = arr[j + 1];
// arr[j + 1] = arr[j];
// arr[j] = temp;
// }
// }
// System.out.println("第二趟排序完之后的数组:" + Arrays.toString(arr));
//
// //第三趟将第三大的数排到倒数第三的位置
// for (int j = 0; j < arr.length - 3; j++){
// if (arr[j] > arr[j + 1]){
// temp = arr[j + 1];
// arr[j + 1] = arr[j];
// arr[j] = temp;
// }
// }
// System.out.println("第三趟排序完之后的数组:" + Arrays.toString(arr));
//
// //第四趟将第四大的数排到倒数第四的位置
// for (int j = 0; j < arr.length - 4; j++){
// if (arr[j] > arr[j + 1]){
// temp = arr[j + 1];
// arr[j + 1] = arr[j];
// arr[j] = temp;
// }
// }
// System.out.println("第四趟排序完之后的数组:" + Arrays.toString(arr));
//使用循环嵌套就能解决上面的多次循环
// 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]){
// flag = true;//标志本趟有经过交换操作
// temp = arr[j + 1];
// arr[j + 1] = arr[j];
// arr[j] = temp;
// }
// }
// if(!flag){//flag依然是false说明本趟没有交换操作,可以退出循环
// break;
// }else{//本趟有交换操作为了下次标识要将flag还原
// flag = false;
// }
// System.out.printf("第%d趟排序完之后的数组:%s\n", (i+1), Arrays.toString(arr));
// }
}
//将上面的冒牌排序封装为一个方法
public static void bubbleSort(int[] arr){
int temp = 0;//交换时使用的临时变量
boolean flag = false;//设置一个哨兵用于优化冒泡排序
//使用循环嵌套就能解决上面的多次循环
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]){
flag = true;//标志本趟有经过交换操作
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
if(!flag){//flag依然是false说明本趟没有交换操作,可以退出循环
break;
}else{//本趟有交换操作为了下次标识要将flag还原
flag = false;
}
// System.out.printf("第%d趟排序完之后的数组:%s\n", (i+1), Arrays.toString(arr));
}
}
}
选择排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 选择排序思想:
* 1.一共进行n-1轮排序,n为数组arr的长度
* 2.第i轮将第i小/大的数放到arr[i-1]的位置
* 3.(优化)如果第i小/大的数就是本身就不用交换
*/
public class SelectSort {
public static void main(String[] args) {
// int[] arr = {110, 30, 119, 1};
// System.out.println("排序前:" + Arrays.toString(arr));
// selectSort(arr);
//测试选择排序的耗时
int[] arr = new int[80000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random() * 80000);
}
System.out.println("排序中...");
long start = System.currentTimeMillis();
selectSort(arr);
long end = System.currentTimeMillis();
System.out.println("选择排序耗时测试用时:" + (end - start) + "ms");//选择排序耗时测试用时:3541ms
}
public static void selectSort(int[] arr){
//从下面的详细分析可以找到规律
int minIndex = 0;//假设最小值的下标
int min = arr[0];//假设最小值
boolean flag = false;//标识本轮要不要交换
for (int i = 0; i < arr.length - 1; i++) {
//遍历数组从minIndex+1下标开始,找到最小值和最小值的下标
for (int j = i + 1; j < arr.length; j++){
if(min > arr[j]) {
flag = true;
minIndex = j;
min = arr[j];
}
}
if(flag) {//找到新的最小值才要交换
//将当前的数值和最小值交换
arr[minIndex] = arr[i];
arr[i] = min;
flag = false;//重置flag
}
//显示本轮经过交换的情况
// System.out.println("第"+ (i + 1) +"轮交换后的结果:" + Arrays.toString(arr));
}
/*
//以案例:使用选择排序将{110, 30, 119, 1}这个数组从小到大排序
//为了理解选择排序,将步骤拆分为以下 3 轮排序
//第一轮:{110, 30, 119, 1}->{1, 30, 119, 110}
int minIndex = 0;//假设最小值的下标
int min = arr[0];//假设最小值
boolean flag = false;//标识本轮要不要交换
//遍历数组从minIndex+1下标开始,找到最小值和最小值的下标
for (int j = 0 + 1; j < arr.length; j++){
if(min > arr[j]) {
flag = true;
minIndex = j;
min = arr[j];
}
}
if(flag) {//找到新的最小值才要交换
//将当前的数值和最小值交换
arr[minIndex] = arr[0];
arr[0] = min;
flag = false;//重置flag
}
//显示本轮经过交换的情况
System.out.println("第1轮交换后的结果:" + Arrays.toString(arr));
//第二轮:{1, 30, 119, 110}->{1, 30, 119, 110}
minIndex = 1;//假设最小值的下标
min = arr[1];//假设最小值
flag = false;//标识本轮要不要交换
//遍历数组从minIndex+1下标开始,找到最小值和最小值的下标
for (int j = 1 + 1; j < arr.length; j++){
if(min > arr[j]) {
flag = true;
minIndex = j;
min = arr[j];
}
}
if(flag) {//找到新的最小值才要交换
//将当前的数值和最小值交换
arr[minIndex] = arr[1];
arr[1] = min;
flag = false;//重置flag
}
//显示本轮经过交换的情况
System.out.println("第2轮交换后的结果:" + Arrays.toString(arr));
//第三轮:{1, 30, 119, 110}->{1, 30, 110, 119}
minIndex = 2;//假设最小值的下标
min = arr[2];//假设最小值
flag = false;//标识本轮要不要交换
//遍历数组从minIndex+1下标开始,找到最小值和最小值的下标
for (int j = 2 + 1; j < arr.length; j++){
if(min > arr[j]) {
flag = true;
minIndex = j;
min = arr[j];
}
}
if(flag) {//找到新的最小值才要交换
//将当前的数值和最小值交换
arr[minIndex] = arr[2];
arr[2] = min;
flag = false;//重置flag
}
//显示本轮经过交换的情况
System.out.println("第3轮交换后的结果:" + Arrays.toString(arr));
*/
}
}
插入排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 插入排序算法规则:
* 1.排序数组分为两部分:有序表和无序表(默认数组的第一个数为有序表,剩余的元素都是无序表)
* 2.每次去除无序表的第一个元素a和有序表的最后一个元素b进行比较(我们这里是按照从小到大排序),
* 如果发现a<b证明还没有找到插入位置,需要将b后移一个位置,a再和b的前一个数(重新赋值给b)
* 进行比较当发现a>b时证明已经发现插入的位置,将a插入该位置就行
* 3.从上面的分析可以知道 插入排序 要得到有序的数组,要经过 数组的长度-1 轮的排序操作
*/
public class InsertSort {
public static void main(String[] args) {
// int[] arr = {110, 30, 119, 1};
// System.out.println("排序前的数组:" + Arrays.toString(arr));
// insertSort(arr);
//测试插入排序的耗时
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int)(Math.random() * 80000);
}
System.out.println("排序中...");
long start = System.currentTimeMillis();
insertSort(arr);
long end = System.currentTimeMillis();
System.out.println("插入排序的耗时:" + (end - start) + "ms");//插入排序的耗时:1099ms
}
public static void insertSort(int[] arr){
//从下面的分步分析可以得到规律
int insertIndex = 0;//假设从无需表中选出的数据将要插到数组的位置
int insertValue = 0;//从无序表中选出的数据
for (int i = 0; i < arr.length - 1; i++) {
insertIndex = i;//假设从无需表中选出的数据将要插到数组的位置
insertValue = arr[insertIndex + 1];//从无序表中选出的数据
//查找选出的数据要插入的位置
//insertIndex >= 0保证不发生数组越界
//insertValue < arr[insertIndex] 说明还没找到要插入的位置,如果向从大到小可以将<改为>
while(insertIndex >= 0 && insertValue < arr[insertIndex]){
//将arr[insertIndex]的数据后移一位
arr[insertIndex + 1] = arr[insertIndex];
//将insertIndex向前移动
insertIndex--;
}
//当退出循环说明已经找到了要插入的位置下标就是 insertIndex + 1
arr[insertIndex + 1] = insertValue;
//打印本轮排序后的数组
// System.out.printf("第%d轮排序后的数组:%s\n", (i + 1), Arrays.toString(arr));
}
/*
//案例:将arr = {110, 30, 119, 1}用插入排序算法将arr从小到大排序
//为了分析,拆分多轮排序
//第1轮:{110, 30, 119, 1}->{30, 110, 119, 1}
int insertIndex = 0;//假设从无需表中选出的数据将要插到数组的位置
int insertValue = arr[insertIndex + 1];//从无序表中选出的数据
//查找选出的数据要插入的位置
//insertIndex >= 0保证不发生数组越界
//insertValue < arr[insertIndex] 说明还没找到要插入的位置
while(insertIndex >= 0 && insertValue < arr[insertIndex]){
//将arr[insertIndex]的数据后移一位
arr[insertIndex + 1] = arr[insertIndex];
//将insertIndex向前移动
insertIndex--;
}
//当退出循环说明已经找到了要插入的位置下标就是 insertIndex + 1
arr[insertIndex + 1] = insertValue;
//打印本轮排序后的数组
System.out.printf("第%d轮排序后的数组:%s\n", 1, Arrays.toString(arr));
//第2轮:{30, 110, 119, 1}->{30, 110, 119, 1}
insertIndex = 1;//假设从无需表中选出的数据将要插到数组的位置
insertValue = arr[insertIndex + 1];//从无序表中选出的数据
//查找选出的数据要插入的位置
//insertIndex >= 0保证不发生数组越界
//insertValue < arr[insertIndex] 说明还没找到要插入的位置
while(insertIndex >= 0 && insertValue < arr[insertIndex]){
//将arr[insertIndex]的数据后移一位
arr[insertIndex + 1] = arr[insertIndex];
//将insertIndex向前移动
insertIndex--;
}
//当退出循环说明已经找到了要插入的位置下标就是 insertIndex + 1
arr[insertIndex + 1] = insertValue;
//打印本轮排序后的数组
System.out.printf("第%d轮排序后的数组:%s\n", 2, Arrays.toString(arr));
//第2轮:{30, 110, 119, 1}->{1, 30, 110, 119}
insertIndex = 2;//假设从无需表中选出的数据将要插到数组的位置
insertValue = arr[insertIndex + 1];//从无序表中选出的数据
//查找选出的数据要插入的位置
//insertIndex >= 0保证不发生数组越界
//insertValue < arr[insertIndex] 说明还没找到要插入的位置
while(insertIndex >= 0 && insertValue < arr[insertIndex]){
//将arr[insertIndex]的数据后移一位
arr[insertIndex + 1] = arr[insertIndex];
//将insertIndex向前移动
insertIndex--;
}
//当退出循环说明已经找到了要插入的位置下标就是 insertIndex + 1
arr[insertIndex + 1] = insertValue;
//打印本轮排序后的数组
System.out.printf("第%d轮排序后的数组:%s\n", 3, Arrays.toString(arr));
*/
}
}
希尔排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 希尔排序算法:(实现方法分为交换式和移位式)
*
* 引出希尔排序:看看插入算法存在的问题,当一个有较小的数排在后面而前
* 面的数都是比较有序的数组需要排序时,比如{2, 3, 4, 5, 6, 1}即使
* 前五个数都是有序的,但是还是要经过 5 轮的排序:
* {2, 3, 4, 5, 6, 1}->{2, 3, 4, 5, 6, 1}->{2, 3, 4, 5, 6, 1}->{2, 3, 4, 5, 6, 1}
* ->{2, 3, 4, 5, 6, 1}->{1, 2, 3, 4, 5, 6}, 所以插入排序在某些排序中的效率不高
*
* 希尔排序是插入排序的升级版,又称缩小增量排序
* 希尔排序算法思想:
* 假设需要排序的数组为:arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0}
* 第1轮排序,将arr分成(arr.length/2) = 5组:就是步长为5的 5组数据:
* (8, 3), (9, 5), (1, 4), (7, 6), (2, 0)
* 在5组数据内进行各自排序,所以第1轮排序后的数组为{3, 5, 1, 6, 0, 8, 9, 4, 7, 2}
*
* 第2轮排序,将arr分成(arr.length/2/2) = 2组:就是步长为2的 2组数据:
* (3, 1, 0, 9, 7), (5, 6, 8, 4, 2)
* 两组数据内进行各自排序,所以第二轮排序后的数组为{0, 2, 1, 4, 3, 5, 7, 6, 9, 8}
*
* 第3轮排序,将arr分为(arr.length/2/2/2) = 1组,就是所有数据为一组:
* (0, 2, 1, 4, 3, 5, 7, 6, 9, 8)
* 这一组数据内进行插入排序,所以第3轮排序后的数组为{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
*
* 从上面的分析来看,希尔排序就是将较小(前提是从小到大排序)的数据方法数组较前的位置,
* 避免插入排序的弊端问题
*/
public class ShellSort {
public static void main(String[] args) {
// int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
// System.out.println("排序前:" + Arrays.toString(arr));
// shellSort2(arr);
//测试希尔排序的耗时(交换式)
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int)(Math.random() * 80000);
}
// System.out.println("排序中...");
// long start = System.currentTimeMillis();
// shellSort(arr);
// long end = System.currentTimeMillis();
// System.out.println("希尔排序(交换式)的耗时:" + (end - start) + "ms");//希尔排序(交换式)的耗时:9863ms
System.out.println("排序中...");
long start = System.currentTimeMillis();
shellSort2(arr);
long end = System.currentTimeMillis();
System.out.println("希尔排序(移位式)的耗时:" + (end - start) + "ms");//希尔排序(移位式)的耗时:31ms
}
//交换式实现希尔排序(效率较低)
public static void shellSort(int[] arr){
//根据下面的分析可以总结规律
int temp = 0;
int count = 0;//统计轮数
for (int gap = (int)arr.length / 2; gap >= 1; gap = (int)(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];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
// System.out.printf("第%d轮排序后:%s\n", ++count, Arrays.toString(arr));
}
/*
//为类更清楚分析希尔排序,将分步骤实现希尔排序
//第1轮排序
int temp = 0;
for (int i = 5; i < arr.length; i++) {
for (int j = i - 5; j >= 0; j-=5) {
if(arr[j] > arr[j + 5]){//如果这一组内的数前者比后者大的就进行交换
temp = arr[j];
arr[j] = arr[j + 5];
arr[j + 5] = temp;
}
}
}
System.out.printf("第%d轮排序后:%s\n", 1, Arrays.toString(arr));
//第2轮排序
temp = 0;
for (int i = 2; i < arr.length; i++) {
for (int j = i - 2; j >= 0; j-=2) {
if(arr[j] > arr[j + 2]){//如果这一组内的数前者比后者大的就进行交换
temp = arr[j];
arr[j] = arr[j + 2];
arr[j + 2] = temp;
}
}
}
System.out.printf("第%d轮排序后:%s\n", 2, Arrays.toString(arr));
//第3轮排序
temp = 0;
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j-=1) {
if(arr[j] > arr[j + 1]){//如果这一组内的数前者比后者大的就进行交换
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.printf("第%d轮排序后:%s\n", 3, Arrays.toString(arr));
*/
}
//移位式实现希尔排序(就是结合插入排序)
public static void shellSort2(int[] arr){
int index = 0;//表示将要插入的位置
int value = 0;//表示将要
int count = 0;//统计轮数
for(int gag = arr.length / 2; gag > 0; gag /= 2){
for (int i = gag; i < arr.length; i++) {
index = i - gag;
value = arr[i];
//查找要插入的位置
while(index >= 0 && value < arr[index]){
//将当前index的值赋值给index+gag也就是i下标的位置
arr[index + gag] = arr[index];
//将index向前移动gap个位置
index -= gag;
}
//退出循环证明已经找到插入的位置了为index+gap
arr[index + gag] = value;
}
// System.out.printf("第%d轮排序后:%s\n", ++count, Arrays.toString(arr));
}
}
}
快速排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 快速排序思想:以将数组arr = {7, 4, 1, 9, 5, 3, 2, 6, 0, 8}从小到大排序为例讲解
* 1.确定基准点本例的基准点为int mid=(right+left)/2=4, left=0, right=arr.length-1=9
* 基准点将数组分为左右两边
* 2.定义变量的记录基准点的值midVal = arr[mid] = 5, 定义两个游标r = 9, l = 0方便遍历数组
* 左右两边,将比midVal小的数放到midVal的左边,将比midVal大的数放到midVal的右边
* 3.比较两个游标所指元素和midVal的大小,在右边while(arr[r]>midVal){r-=1}找到arr[r]<midVal
* 的数,在左边while(arr[l]<midVal){l+=1}找到arr[l]>midVal的数,将arr[r]和arr[l]交换,
* 4.继续第3步的操作,直到r==l时证明midVal左边的数都小于midVal,右边的数都大于midVal,结束本次循环,
* 还有如果数组中的有相同元素时,当arr[r]==arr[l]也应该终止循环,否则会无限循环
* 5.然后使用递归将midVal左右两边的进行2,3,4的操作
*/
public class QuickSort {
public static void main(String[] args) {
// int[] arr = {7, 4, 1, -1, -340, 70, -3, 5, 80, 9, 5, 3, 2, 6, 0, 8};
// System.out.println("排序前:" + Arrays.toString(arr));
// quickSort(arr, 0, arr.length - 1);
// System.out.println("排序后:" + Arrays.toString(arr));
//测试快速排序的效率
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int)(Math.random() * 80000);
}
System.out.println("排序中...");
long start = System.currentTimeMillis();
quickSort(arr, 0, arr.length - 1);
long end = System.currentTimeMillis();
System.out.println("快速排序的耗时:" + (end - start) + "ms");//快速排序的耗时:68ms
}
public static void quickSort(int[] arr, int left, int right){
//定义两个游标并初始化
int l = left;
int r = right;
//定义基准点,并初始化
int mid = (l + r) / 2;
//定义变量记录基准点的值
int midVal = arr[mid];
//临时变量用于交换
int temp = 0;
while(l < r){//直到r==l时证明midVal左边的数都小于midVal,右边的数都大于midVal,结束本次循环
//在左边找到一个大于midVal的元素,到退出循环时说明已经找到
while(arr[l] < midVal){
l += 1;
}
//在右边找到一个小于midVal的元素,到退出循环时说明已经找到
while(arr[r] > midVal){
r -= 1;
}
//当遇到arr[r]与arr[l]相等时也许时r,l重叠,也许是数组中有相同元素,
// 此时应该终止循环,否则就会无限循环
if(arr[r] == arr[l]){
break;
}
if(!(r == l)) {//当r和l相等的时候不用进行交换操作
//将左右两边找到的数交换
temp = arr[r];
arr[r] = arr[l];
arr[l] = temp;
}
}
//将r, l重新赋值
r -= 1;//将r移动到midVal的前一个位置,作为下一次左递归的右起点
l += 1;//将l移动到midVal的后一个位置,作为下一次右递归的左起点
//左边递归
if(r > left){
quickSort(arr, left, r);
}
//左边递归
if(l < right){
quickSort(arr, l, right);
}
}
}
归并排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 归并排序算法:以将数组arr = {7, 4, 1, 9, 5, 3, 2, 6, 0, 8}从小到大排序为例讲解
* 归并排序的思想是分治算法,先分后治,分:将arr拆分为单独的元素,治:将拆分的元素按顺序归并
* 分的步骤:
* 1.定义左右标志并初始化int left = 0, int right = arr.length - 1;
* 2.定义中间标志int mid = (left + right) / 2,将数组从mid成两部分分别是:
* {arr[left]-arr[mid]}和{arr[mid+1]-arr[right]}
* 3.使用递归算法将数组分为单独的元素
*
* 治的步骤:就是合并被分开的每个元素(合并的次数: arr.length - 1)
* 1.传入的参数是:int[] arr, int left, int right, int[] temp;temp数组用于暂存排序后的元素
* 2.定义两个游标并初始化:int r = left, int l = (left + right)/2 + 1,r,l分别指向需要合并的
* 两个分组的最开始位置,定义并初始化int t = 0,指向temp数组kai开始位置
* 3.开始合并的第一步:比较arr[r]与arr[l],
* if(arr[l]>arr[r]){temp[t] = arr[r]; r+=1; t+=1}
* if(arr[l]<arr[r]){temp[t] = arr[l]; l+=1; t+=1}
* 4.合并的第二步:将左或右其中还没加入temp数组中的元素继续加入temp中
* while(r<(left+right)/2){temp[t]=arr[r]; r+=1; t+=1}
* 或
* while(l<(left+right)/2){temp[t]=arr[l]; l+=1; t+=1}
* 5.合并的第三步:将temp中的值赋值给arr,并不是每次合并的后都要将temp中的所有元素都赋值给arr
* 只需要将新加入的值赋值给arr的对应位置就可以,从arr[left]到arr[right]
*
* {7, 4, 1, 9, 5, 3, 2, 6, 0, 8}
* {7, 4, 1} {9, 5} {3, 2, 6} {0, 8}
* {7, 4} {1} {9} {5} {3, 2} {6} {0} {8}
* {7} {4} {3} {2}
* {4, 7} {2, 3}
* {1, 4, 7} {5, 9} {2, 3, 6} {0, 8}
* {1, 4, 5, 7, 9} {0, 2, 3, 6, 8}
* {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
*/
public class MergeSort {
public static void main(String[] args) {
// int[] arr = {7, 4, 1, 9, 5, 3, 2, 6, 0, 8};
// int[] temp = new int[arr.length];//归并排序需要符出更大的空间代价,以空间换时间
// System.out.println("排序前:" + Arrays.toString(arr));
// mergeSort(arr, 0, arr.length-1, temp);
// System.out.println("排序后:" + Arrays.toString(arr));
//测试归并排序的效率
int[] arr = new int[80000];
int[] temp = new int[arr.length];//归并排序需要符出更大的空间代价,以空间换时间
for (int i = 0; i < 80000; i++) {
arr[i] = (int)(Math.random() * 80000);
}
System.out.println("排序中...");
long start = System.currentTimeMillis();
mergeSort(arr, 0, arr.length - 1, temp);
long end = System.currentTimeMillis();
System.out.println("归并排序的耗时:" + (end - start) + "ms");//归并排序的耗时:21ms
}
//归并排序
public static void mergeSort(int[] arr, int left, int right, int[] temp){
//定义变量记录中间下标
int mid = (left + right) / 2;
//循环递归分割
if (left < right){
mergeSort(arr, left, mid, temp);
mergeSort(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 r = left, int l = (left + right) + 1,r,l
// 分别指向需要合并的两个分组的最开始位置
int l = left;
int r = mid + 1;
// 定义并初始化int t = 0,指向temp数组开始位置
int t = 0;
//开始合并的第一步:比较arr[r]与arr[l],
// * if(arr[l]>arr[r]){temp[t] = arr[r]; r+=1; t+=1}
// * if(arr[l]<arr[r]){temp[t] = arr[l]; l+=1; t+=1}
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数组中的元素继续加入temp中
// * while(r<(left+right)/2){temp[t]=arr[r]; r+=1; t+=1}
// * 或
// * while(l<(left+right)/2){temp[t]=arr[l]; l+=1; t+=1}
//当左边没有遍历完,需要继续将左边的元素加入到temp中
while(l <= mid){
temp[t] = arr[l];
t += 1;
l += 1;
}
//当右边没有遍历完,需要继续将左边的元素加入到temp中
while(r <= right){
temp[t] = arr[r];
t += 1;
r += 1;
}
//合并的第三步:将temp中的值赋值给arr,并不是每次合并的后都要将temp中的所有元素都赋值给arr
// * 只需要将新加入的值赋值给arr的对应位置就可以,从arr[left]到arr[right]
int cur = left;
//将t重新指向temp数组的起始位置
t = 0;
while(cur <= right){
arr[cur] = temp[t];
cur += 1;
t += 1;
}
}
}
基数排序
package com.weeks.sort;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
*
* 基数排序:以将arr = {543, 68, 8, 52, 73, 46, 9, 775} 从小到大排序为例
*
* 桶排序是以空间换时间的算法,如果要排序大量的数据,基数排序不适用,桶排序需要消耗极大的内存空间
* 还有如果数组中有负数要注意以下算法需要适当调整
*/
public class BucketSort {
public static void main(String[] args) {
// int[] arr = {543, 68, 8, 52, 73, 46, 9, 775};
// bucketSort(arr);
//测试快速排序的效率
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int)(Math.random() * 80000);
}
System.out.println("排序中...");
long start = System.currentTimeMillis();
bucketSort(arr);
long end = System.currentTimeMillis();
System.out.println("桶排序的耗时:" + (end - start) + "ms");//桶排序的耗时:35ms
}
public static void bucketSort(int[] arr){
//从下面详细分析可找到规律
//首先要获得数组中最大数的位数
//查找最大数
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if(max < arr[i]){
max = arr[i];
}
}
//获得最大数的长度(位数个数)
int maxLen = (max + "").length();
//初始化一个二维数组,二维数组的行数是10,代表十个桶分别装0-9这10个数
//每一行就是一个一维数组,一维数组的大小是arr.length,防止溢出(比如每个数的个位都是1,
// 那么就只能将所有元素都装在1这个桶里)
int[][] buckets = new int[10][arr.length];
//初始化一个一位数组,表示每个桶中装了几个有效元素
int[] elements = new int[10];
for (int l = 0, n = 1; l < maxLen; l++, n *= 10){
//将每个元素按照 对应位 的大小分别放入对应的桶中
for (int i = 0; i < arr.length; i++) {
//获得 个位 的数字
int data = arr[i] / n % 10;
//将当前元素按照放入data桶中
buckets[data][elements[data]++] = arr[i];
}
//将通过的数据,按照桶的编号0-9,依次取出有效数据放入arr中
int index = 0;//索引arr数组
//遍历所有的桶
for (int i = 0; i < buckets.length; i++) {
//遍历每个桶中的有效数据
for (int j = 0; j < elements[i]; j++) {
//将桶中数据放回arr中
arr[index++] = buckets[i][j];
}
}
//取出所有数据后需要将桶中的有效数据清零
for (int i = 0; i < elements.length; i++) {
elements[i] = 0;
}
// System.out.println("第" + (l + 1) + "轮排序后:" + Arrays.toString(arr));
}
/*
//分部讲解基数排序排序的过程
//初始化一个二维数组,二维数组的行数是10,代表十个桶分别装0-9这10个数
//每一行就是一个一维数组,一维数组的大小是arr.length,防止溢出(比如每个数的个位都是1,
// 那么就只能将所有元素都装在1这个桶里)
int[][] buckets = new int[10][arr.length];
//初始化一个一位数组,表示每个桶中装了几个有效元素
int[] elements = new int[10];
//第一轮排序
//将每个元素按照 个位 的大小分别放入对应的桶中
for (int i = 0; i < arr.length; i++) {
//获得 个位 的数字
int data = arr[i] % 10;
//将当前元素按照放入data桶中
buckets[data][elements[data]++] = arr[i];
}
//将通过的数据,按照桶的编号0-9,依次取出有效数据放入arr中
int index = 0;//索引arr数组
//遍历所有的桶
for (int i = 0; i < buckets.length; i++) {
//遍历每个桶中的有效数据
for (int j = 0; j < elements[i]; j++) {
//将桶中数据放回arr中
arr[index++] = buckets[i][j];
}
}
//取出所有数据后需要将桶中的有效数据清零
for (int i = 0; i < elements.length; i++) {
elements[i] = 0;
}
System.out.println("第1轮排序后:" + Arrays.toString(arr));
//第二轮排序
//将每个元素按照 个位 的大小分别放入对应的桶中
for (int i = 0; i < arr.length; i++) {
//获得 十位 的数字
int data = arr[i] / 10 % 10;
//将当前元素按照放入data桶中
buckets[data][elements[data]++] = arr[i];
}
//将通过的数据,按照桶的编号0-9,依次取出有效数据放入arr中
index = 0;//索引arr数组
//遍历所有的桶
for (int i = 0; i < buckets.length; i++) {
//遍历每个桶中的有效数据
for (int j = 0; j < elements[i]; j++) {
//将桶中数据放回arr中
arr[index++] = buckets[i][j];
}
}
//取出所有数据后需要将桶中的有效数据清零
for (int i = 0; i < elements.length; i++) {
elements[i] = 0;
}
System.out.println("第2轮排序后:" + Arrays.toString(arr));
//第三轮排序
//将每个元素按照 个位 的大小分别放入对应的桶中
for (int i = 0; i < arr.length; i++) {
//获得 十位 的数字
int data = arr[i] / 100 % 10;
//将当前元素按照放入data桶中
buckets[data][elements[data]++] = arr[i];
}
//将通过的数据,按照桶的编号0-9,依次取出有效数据放入arr中
index = 0;//索引arr数组
//遍历所有的桶
for (int i = 0; i < buckets.length; i++) {
//遍历每个桶中的有效数据
for (int j = 0; j < elements[i]; j++) {
//将桶中数据放回arr中
arr[index++] = buckets[i][j];
}
}
//取出所有数据后需要将桶中的有效数据清零
for (int i = 0; i < elements.length; i++) {
elements[i] = 0;
}
System.out.println("第3轮排序后:" + Arrays.toString(arr));
*/
}
}
堆排序(要运用到二叉树的知识)
package com.weeks.tree;
import java.util.Arrays;
/**
* @author 达少
* @version 1.0
* 堆排序:
* 1.将一个需要排序的数组以大顶堆的方式排列(升序大顶堆,降序小顶堆)
* 2.从左往右,从下至上调整每个非叶子节点为大顶堆
* 3.完成步骤2时,将根节点与末尾结点交换,最大值就在末尾了
* 4.将数组长度减一,就是不带上末尾元素,重复2和3
*
* 大顶堆:每个非叶子结点的数值大于或等于左右结点的数值,就是
* arr[i] >= arr[2 * i + 1] && arr[i] >= arr[2 * i + 2]
* 小顶堆:每个非叶子结点的数值小于或等于左右结点的数值,就是
* arr[i] <= arr[2 * i + 1] && arr[i] <= arr[2 * i + 2]
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
System.out.println("排序前:" + Arrays.toString(arr));
headSort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
public static void headSort(int[] arr){
//首先将无序的数组,构建大顶堆
for (int i = arr.length / 2 -1; i >= 0; i--) {
adjust(arr, i, arr.length);
}
int temp = 0;//辅助交换
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 adjust(int[] arr, int i, int length){
//先取出下标为i的值,保存在临时变量中
int temp = arr[i];
//开始调整
for (int k = 2 * i + 1; k < length; k = 2 * k + 1){
if((k + 1) < length && arr[k] < arr[k + 1]){//左右结点的值比较大小
k++;//如果右结点的数大于左结点的数,就将k++,让k指向右结点
}
if(arr[k] > temp){//比较arr[k]与temp的大小
//如果条件成立
arr[i] = arr[k];
i = k;//i指向k,让下次循环看看是否本次的调整影响到下面的顺序
}else{
//这里能直接break,是因为堆排序调整是从左到右,从下至上开始调整
//如果当没有发现当前值比下面的结点小的时候,证明下面的已经调整好了
break;
}
}
//将temp的值放在最后的调整的位置
arr[i] = temp;
}
}