一、冒泡排序
package SortAlgorithm;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {-1, 20, -10, 10, 5};
System.out.println("排序前的数组:");
System.out.println(Arrays.toString(arr));
Date date1 = new Date();
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String datestr1 = simpleDateFormat1.format(date1);
System.out.println(datestr1);
BubbleSort(arr);
System.out.println("排序后的数组:");
System.out.println(Arrays.toString(arr));
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String datestr = simpleDateFormat.format(date);
System.out.println(datestr);
}
public static void BubbleSort(int[] arr) {
int temp = 0; //临时变量,用于数组中相邻两个元素比较后交换位置
boolean flag = false; //判断数组中是否有元素交换,用于冒泡排序优化,可提前结束循环
for (int j = 0; j < arr.length - 1; j++) { //每次放置好一个数字后,就减少一轮比较
for (int i = 0; i < arr.length - 1 - j; i++) { //每次放置好一个大的数,从最大的数开始
if (arr[i] > arr[i+1]) {
flag = true; //一旦有元素交换,说明还未完成排序
temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
// System.out.println("第一次排序" + Arrays.toString(arr));
}
}
if (flag == false) { //一次交换也没进行
break;
}else {
flag = false; //重置flag,进行下次判断
}
// System.out.println("第一次排序" + Arrays.toString(arr));
}
}
}
二、选择排序
速度比冒泡排序快。
package SortAlgorithm;
import java.util.Arrays;
public class SelectSort {
public static void main(String[] args) {
int[] arr = {101, 34, 119, 1};
SelectSort(arr);
}
public static void SelectSort(int[] arr) {
for (int j = 0; j < arr.length - 1; j++) {
int minIndex = j; //用于指向找到的最小的元素的下标,方便后面元素交换,先假设当前的元素就是最小的
int min = arr[j]; //存放最小的元素
//从当前元素的后一个开始循环比较,直到找到最小的,然后置换
for (int i = j+1; i < arr.length; i++) {
if (min > arr[i]) {
minIndex = i; //重置minIndex
min = arr[i]; //重置min
}
}
//将最小值和arr[j]置换
if (minIndex != j) { //当最小元素不是当前元素时才互换
arr[minIndex] = arr[j];
arr[j] = min;
}
System.out.println("第一轮后:" + Arrays.toString(arr));
}
}
}
三、插入排序
package SortAlgorithm;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {101, 34, 119, 1};
insertSort(arr);
}
public static void insertSort(int[] arr) {
//定义待插入的数
for (int i = 1; i < arr.length; i++) {
int insertVal = arr[i];
int insertIndex = i-1; //即arr[1]前面的一个数的下标
//insertIndex >= 0保证不越界
//insertVal < arr[insertIndex],当insertVal比前面的数小时,将前面的数后移一位
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex+1] = arr[insertIndex];
insertIndex--; //将insertIndex向前移动一位,再将insertVal与之比较,直到找到其位置
}
//当退出while循环时,说明insertVal位置找到,即insertIndex+1的位置
arr[insertIndex+1] = insertVal;
System.out.println(Arrays.toString(arr));
}
}
}
四、希尔排序
package SortAlgorithm;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0}; //数组共有十个数,第一次分成五组进行插入排序,第二次分成2组,第三次分成一组
int[] arr1 = new int[80000];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = (int)(Math.random()*80000); //生成一个[0,80000)的数组
}
//测试速度
//排序前
Date date1 = new Date();
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String strdate1 = simpleDateFormat1.format(date1);
System.out.println(strdate1);
//交换法
shellSort1(arr);
//移动法
shellSort2(arr);
//排序后
Date date2 = new Date();
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
String strdate2 = simpleDateFormat2.format(date2);
System.out.println(strdate2);
}
//交换法希尔排序
public static void shellSort1(int[] arr) {
int temp = 0;
for (int gap = arr.length/2; gap > 0; gap = gap/2) { //gap是步长每次循环排序后,gap/2重新进行分组排序,直到gap=1
for (int i = gap; i < arr.length; i++) { //i=5的时候是数组中第6个数3,分成5组的时候3要和8进行插入排序。
//这个循环是让后面的5个数和前面的5个进行比较,从后往前,那么嵌套循环中的j就要从i=0开始从数组中的第一个数往后
for (int j = i - gap; j >= 0; j -= gap) {
//j>=0和j-=5的目的是因为是从后往前比较的,比如i=2的时候,第一次是arr[0]和arr[2]比较,当i增加到4,j=2,先是arr[2]先和arr[4]比较,较小的数放在arr[2]中
//然后j-=2,j=0,arr[0]需要再次和arr[2]比较,因为arr[4]有可能换到了arr[2]中。j>=0是为了保证从后往前比较的时候不越界。
while (arr[j] > arr[j+gap]) {
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
/*
* 第一次分组示例
int gap = arr.length/2; //第一次的步长
int temp = 0;
for (int i = 5; i < arr.length; i++) { //i=5的时候是数组中第6个数3,分成5组的时候3要和8进行插入排序。
//这个循环是让后面的5个数和前面的5个进行比较,从后往前,那么嵌套循环中的j就要从i=0开始从数组中的第一个数往后
for (int j = i - 5; j >= 0; j -= 5) {
//j>=0和j-=5的目的是因为是从后往前比较的,比如i=2的时候,第一次是arr[0]和arr[2]比较,当i增加到4,j=2,先是arr[2]先和arr[4]比较,较小的数放在arr[2]中
//然后j-=2,j=0,arr[0]需要再次和arr[2]比较,因为arr[4]有可能换到了arr[2]中。j>=0是为了保证从后往前比较的时候不越界。
while (arr[j] > arr[j+5]) {
temp = arr[j];
arr[j] = arr[j+5];
arr[j+5] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
*/
}
//移动法希尔排序
public static void shellSort2(int[] arr){
for (int gap = arr.length/2; gap > 0; gap = gap/2) { //gap是步长每次循环排序后,gap/2重新进行分组排序,直到gap=1
for (int i = gap; i < arr.length; i++) {
int insertIndex = i;
int temp = arr[insertIndex];
while (insertIndex-gap >= 0 && temp < arr[insertIndex - gap]) { //参考看插入排序
arr[insertIndex] = arr[insertIndex - gap];
insertIndex = insertIndex - gap;
}
arr[insertIndex] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
五、快速排序
package SortAlgorithm;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {9,0,78,23,-567,70};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
//快速排序法需要传进去三个参数,数组arr,数组的最左边的元素下标left,数组最右边的元素下标right
public static void quickSort(int[] arr, int left, int right) {
int l = left; //左指针
int r = right; //右指针
int temp = 0; //临时变量,用于交换
int pivot = arr[(left+right)/2];
//首先让数组分别从两头开始查找,比中间值大的放在左边,比中间值小的放在右边,进行左右交换
while (l < r) {
while (arr[l] < pivot) { //在左边寻找比中间值大的值
l++;
}
while (arr[r] > pivot) { //在右边寻找比中间值小的值
r--;
}
if (l == r) { //如果左右指针在中间值处相遇,说明左右交换完成,跳出循环,开始进入递归,对左右两边的值分别进行快排
break;
}
temp = arr[l]; //交换左右两边的值
arr[l] = arr[r];
arr[r] = temp;
//下面这两个条件的作用是发生在中间值被交换到其他位置,发生移位
//为什么中间值要被移位?为了保证中间值左边的数一定比其小,右边的数一定比它大
if (arr[l] == pivot) { //如果中间值被交换到左边,就让右边的指针向中间值靠拢,直到左右指针在中间值处相遇
r--;
}
if (arr[r] == pivot) {
l++;
}
}
if (l == r) { //跳出循环时,r和l是相等的,这个时候需要将r和l分别向前移动,形成两个分组
l++;
r--;
}
//对这两个分组使用递归分别进行快速排序
if (left < r) {
quickSort(arr, left, r);
}
if (right > l) {
quickSort(arr, l, right);
}
}
}
六、归并排序
package SortAlgorithm;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] arr = {8,4,5,7,1,3,6,2};
int[] temp = new int[arr.length];
mergeSort(arr, 0, arr.length-1, temp);
}
//分解数组和合并数组的总的方法
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);
System.out.println("归并排序后分解的数组" + Arrays.toString(arr));
//合并
merge(arr, left, right, mid, temp);
System.out.println("归并排序后合并的数组" + Arrays.toString(arr));
System.out.println();
}
}
//合并的方法
/**
*
* @param arr 需要排序的数组
* @param left 数组最左边的下标
* @param right 数组最左边的下标
* @param mid 数组分成两份后,左边那份最右边的值的下标
* @param temp 临时数组,用于存放合并后的数
*
*/
public static void merge(int[] arr, int left, int right, int mid, int[] temp) {
int i = left; //左指针,用于指向左边那份数值的下标
int j = mid + 1; //右指针,用于指向右边那份数值的下标
int t = 0; //临时数组的指针,指向临时数组当前值的下标
//1、按照规则把两份分好的数排好顺序放入临时数组中
while (i <= mid && j <= right) { //当i <= mid && j <= right时,说明还没有循环一遍
if (arr[i] <= arr[j]) { //如果左边的数比右边的数小,就把左边的数放入临时数组中
temp[t] = arr[i];
i += 1; //将指针右移继续对比下一个数
t += 1;
}else {
temp[t] = arr[j];
j += 1; //将指针右移继续对比下一个数
t += 1;
}
}
//2、当一边的数全部放进数组后,另一边的数就直接按顺序依次放入临时数组中,因为他已经排好顺序
while (i <= mid) { //当i<=mid时,说明左边的数还有剩余
temp[t] = arr[i];
i += 1; //将指针右移继续对比下一个数
t += 1;
}
while (j <= right) { //当j <= right时,说明右边的数还有剩余
temp[t] = arr[j];
j += 1; //将指针右移继续对比下一个数
t += 1;
}
//3、把排好顺序的临时数组temp中的数重新放回原数组arr中
//由于前面分组之后也要排序,存放到临时数组中,然后排好后放进原数组,
t = 0;
int tempLeft = left;
System.out.println("tempLeft和right" + "," + tempLeft + "," + right);
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
}
七、基数排序
package SortAlgorithm;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = { 53, 3, 542, 748, 14, 214 };
radixSort(arr);
}
public static void radixSort(int[] arr) {
// 为了计算出数组中的个十百位数,需要对其进行除法和取模运算,因此就需要找到数组中的最大数
int max = arr[0]; // 先假定数组中第一个数最大,然后让其与数组中其它数比较,直到找到最大数
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
// 求最大数是几位数
int maxLength = (max + "").length(); // 先将max转换成字符串,再计算其长度
// 先定义一个用于排序的桶,即一个二维数组,里面有10个一维数组,分别存放各个位是0-9的数
// 为了保证存入每个桶中的数不溢出,就用空间换时间,多给每个一维数组一些空间
int[][] bucket = new int[10][arr.length];
// 然后定义一个计数的一维数组,用来保存每个桶中放了几个数据。
// 因为有十个桶,所以一维数组需要统计这十个桶中每个分别存放了多少数据,比如count[0] =
// 5,说明第一个桶bucket[0][]中存放了5个数据
int[] count = new int[10];
for (int n = 0, l = 1; n < maxLength; n++, l *= 10) {
// 下面将数组中的每个数据按照个位数值放入对应的桶中,并计数,统计每个桶中放了多少
for (int i = 0; i < arr.length; i++) {
// 计算每个数值的个位数
int digitOfElement = arr[i] / l % 10;
// 按照个位数,将其放入到对应的桶中
// 放到digitOfElement桶中的第count[digitOfElement]个位置,第一个位置是0,第二个是1
bucket[digitOfElement][count[digitOfElement]] = arr[i];
// count计数加一,计数以及改变元素放入上面桶中的位置
count[digitOfElement] += 1;
}
// 从桶中依次取出数据放入原数组
// j表示桶,从第一个桶到最后一个桶,共十个
int index = 0;
for (int j = 0; j < count.length; j++) {
// 先判断桶中是否有数据
if (count[j] != 0) { // 说明桶中有count[j]个数
// 循环该桶(一维数组),取出数据放入原数组
for (int k = 0; k < count[j]; k++) {
arr[index] = bucket[j][k];
index++;
}
}
// 第一轮结束后将count[j]中的数取出后需要将其置零,否则会影响第二轮的统计
count[j] = 0;
}
System.out.println(Arrays.toString(arr));
}
}
}
八、堆排序
package SortAlgorithm;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
//要求对数组进行升序排列
int[] arr = { 4, 6, 8, 5, 9 };
int[] arr1 = { 100, 22, 15, 46, -12, 15, 14, 4, 6, 8, 5, 9 };
heapSort(arr1);
}
//编写一个堆排序的方法
public static void heapSort(int[] arr) {
int temp = 0;
// adjustHeap(arr, 1, arr.length);
// System.out.println(Arrays.toString(arr)); //4 9 8 5 6
// adjustHeap(arr, 0, arr.length);
// System.out.println(Arrays.toString(arr)); //9 6 8 5 4
// //这一步可省略,因为直接对i输入0,可以一次性将大顶堆构建完成
// //1.完成将数组转换成大顶堆
// //i从第一个非子叶节点开始,直到最后一个
// for (int i = arr.length/2 - 1; i >= 0; i--) {
// adjustHeap(arr, i, arr.length);
// }
// System.out.println(Arrays.toString(arr)); //9 6 8 5 4
//2.将对顶元素与末尾元素进行交换,此时,末尾元素就是最大元素
//3.重新调整结构,用剩下的元素重构大顶堆,继续第2步操作
//j就是末尾元素
for (int j = arr.length - 1; j > 0; j--) {
//交换
temp = arr[j]; //队尾元素
arr[j] = arr[0];
arr[0] = temp;
//必须从第0个元素开始构建大顶堆,这样可以一次性将大顶堆构建完成
//如果从第一个非叶子节点开始,还要传入的i的值要一直改变,需要多次传入才能最终完成,而从0开始,一次性就可以完成
adjustHeap(arr, 0, j);
}
System.out.println(Arrays.toString(arr));
}
//将一个数组(二叉树),调整成一个大顶堆的方法
/**
* 功能:完成将以i为非叶子节点的树调整成大顶堆
* 举例:{ 4, 6, 8, 5, 9 },第一次i=1,调整后变成{ 4, 9, 8, 5, 6 }
* 第二次i=0,调整成{ 9, 6, 8, 5, 4 }
* @param arr 待调整的数组
* @param i 表示非叶子结点在数组中的索引
* @param length 表示对多少个元素进行调整,因为调整一次后,就会少一个元素
*
*
*/
public static void adjustHeap(int[] arr,int i, int length) {
//先用一个临时变量保存传进来的非叶子节点i中的值,为了方便后面的交换
int temp = arr[i];
//然后比较非叶子节点i中的值和其左右子节点中的值得大小
//先比较左右子节点中的值的大小,其中k表示左子节点下标k+1表示右子节点下标,要保证左右子节点的下标不越界
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
//k = 2 * k + 1是比较完当前的三个节点后,进入到和传进来的节点交换的那个节点的子节点进行同样的比较
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
} //结束这句后,k就指向左右节点中较大的一个
//然后比较非子叶节点中的值和k指向的值得大小
if (temp < arr[k]) { //第二次比较时,temp中的数相当于交换后的k中的数,但是实际上还没有交换,因为还要进行下一轮比较
arr[i] = arr[k];
i = k; //为了结束循环后将原来i中的值放入到k指向的位置,如果有多轮交换,为了第二轮交换时将k中的值和其左右节点交换
}else {
break;
}
}
arr[i] = temp; //将i中的值放入k中,此时的i指向k的位置
}
}