算法<java>——排序(冒泡、插入、选择、归并、快速、计数、堆、桶、基数)
*时间复杂度和空间复杂度
一、冒泡排序
1.工作原理:
(1)比较相邻的两个元素,如果前面的元素大于后面的元素,则交换他们的位置。
(2)对每一对相邻的元素进行同样的操作,从前两个元素到最后两个元素,一趟比较后,最大的元素跑到了最后一位。
(3)针对所有的元素重复以上的步骤,除了最后一个。
2.代码:
public class BubbleSort {public static void main(String[] args) {int[] arr = {5, 2, 9, 1, 5, 6};bubbleSort(arr);System.out.println("\n排序后的数组:");for (int num : arr) {System.out.print(num + " ");}}public static void bubbleSort(int[] arr) {int n = arr.length;boolean swapped;for (int i = 0; i < n - 1; i++) {swapped = false;for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {// 交换 arr[j] 和 arr[j + 1]int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = true;}}// 如果没有发生交换,说明数组已经有序if (!swapped) {break;}}}
}
二、插入排序
1.工作原理:
(1)从第一个元素开始,该元素可以被认为是已经排序的元素。
(2)取出下一个元素,在已经排序的元素序列中从后向前扫描
(3)如果索引元素大于新的元素,将索引元素移至前一个位置。
(4)重复步骤3,直到找到已排序地元素小于或者等于新元素的位置,将新元素插入到该位置。
(5)重复2-5步骤,遍历n-1次
2.代码:
public class InsertionSort {// 插入排序算法实现public static void insertionSort(int[] array) {int n = array.length;// 从第二个元素开始遍历数组for (int i = 1; i < n; i++) {int key = array[i]; // 当前要插入的值int j = i - 1; // 已排序部分的最后一个元素的下标// 在已排序部分中寻找合适的位置插入keywhile (j >= 0 && array[j] > key) {array[j + 1] = array[j]; // 向后移动元素j--;}array[j + 1] = key; // 将key插入合适的位置}}public static void main(String[] args) {int[] arr = {12, 11, 13, 5, 6};insertionSort(arr);System.out.print("Sorted array: ");for (int i : arr) {System.out.print(i + " ");}}
}
三、选择排序
1.工作原理:
(1)首先,从未排序的元素中找到最小(或者最大)的元素。
(2)将其放到已排序序列的末尾(或者放到另一个数组的开头)
(3)然后,再从剩余的未排序元素中,找到最小(最大)的元素,放到已排序序列的末尾。
(4)重复(2)(3)步骤,直至所有元素都已排序
2.代码:
function selectionSort(array)n = array.lengthfor i from 0 to n-1minIndex = ifor j from i+1 to n-1if array[j] < array[minIndex]minIndex = jswap array[i] and array[minIndex]end for
end function
//外层循环遍历数组,
//内层循环找到未排序部分中的最小元素,并将其与当前外层循环位置的元素交换,
//这样就不断地将最小元素放到已排序部分的末尾,直到整个数组都排序完成
四、归并排序
1.工作原理:
(1)分解:将待排序的数组不断地二分,直到每个子序列只有一个元素为止。
(2)排序:对每个元素进行排序,可以采用递归的方式来实现。
(3)合并:将排序好的子序列再合并为一个整体有序的数组。
2.代码:
public class MergeSort {// 归并排序的主函数,对数组array进行归并排序public static void mergeSort(int[] array, int left, int right) {if (left < right) {// 计算中间位置int middle = (left + right) / 2;// 递归地对左半部分进行归并排序mergeSort(array, left, middle);// 递归地对右半部分进行归并排序mergeSort(array, middle + 1, right);// 合并左右两部分merge(array, left, middle, right);}}// 将两个已经排序的数组段 array[left...middle] 和 array[middle+1...right] 合并成一个有序数组public static void merge(int[] array, int left, int middle, int right) {// 计算左右两部分数组段的长度int n1 = middle - left + 1;int n2 = right - middle;// 创建临时数组存储左右两部分的数据int[] leftArray = new int[n1];int[] rightArray = new int[n2];// 将数据拷贝到临时数组for (int i = 0; i < n1; i++) {leftArray[i] = array[left + i];}for (int i = 0; i < n2; i++) {rightArray[i] = array[middle + i + 1];}// 合并左右两部分数组段int i = 0, j = 0;int k = left;while (i < n1 && j < n2) {if (leftArray[i] <= rightArray[j]) {array[k] = leftArray[i];i++;} else {array[k] = rightArray[j];j++;}k++;}// 将剩余的元素拷贝到数组中while (i < n1) {array[k] = leftArray[i];i++;k++;}while (j < n2) {array[k] = rightArray[j];j++;k++;}}
}
五、快速排序
1.工作原理:
选取一个基准元素(通常为第一个元素),将序列分为两个子序列;
将小于基准元素的放在基准元素的左边,大于的放在基准元素的右边;
对左右子序列分别进行递归快速排序;
合并左右子序列。
2.代码:
public class QuickSort {public static void main(String[] args) {int[] arr = {5, 2, 9, 3, 7, 6, 1, 8, 4};quickSort(arr, 0, arr.length - 1); // 调用快速排序算法for (int i : arr) {System.out.print(i + " "); // 输出排序后的数组}}public static void quickSort(int[] arr, int low, int high) {if (arr == null || arr.length == 0 || low >= high) {return; // 如果数组为空或者low大于等于high,直接返回}int middle = low + (high - low) / 2; // 计算中间值的索引int pivot = arr[middle]; // 取得中间值作为基准值int i = low, j = high; // 初始化i和j作为左右指针while (i <= j) { // 当i小于等于j时循环while (arr[i] < pivot) { // 在左半部分找到第一个大于等于基准值的元素i++;}while (arr[j] > pivot) { // 在右半部分找到第一个小于等于基准值的元素j--;}if (i <= j) { // 如果左指针小于等于右指针int temp = arr[i]; // 交换arr[i]和arr[j]arr[i] = arr[j];arr[j] = temp;i++;j--;}}if (low < j) { // 递归处理左半部分quickSort(arr, low, j);}if (high > i) { // 递归处理右半部分quickSort(arr, i, high);}}
}
六、堆排序
1.工作原理:
构建堆:首先将待排序的数据构建成一个二叉堆,可以是最大堆或最小堆。这个步骤可以使用从下往上的循环方式,从最后一个非叶子节点开始,逐个向前调整节点使得以当前节点为根的子树满足堆的性质。
排序:将堆顶元素(最大值或最小值)与堆的最后一个元素交换位置,并将堆的大小减一。接着调整交换后的堆,使其满足堆的性质。重复以上步骤,直到整个数组排序完成。
2.代码:
public class HeapSort {// 堆排序的主要入口方法,用于对整个数组进行排序public static void sort(int[] arr) {// 构建最大堆buildMaxHeap(arr);// 从最后一个非叶子节点开始,依次将根节点与末尾元素交换,并重新调整堆for (int i = arr.length - 1; i > 0; i--) {swap(arr, 0, i); // 将根节点与末尾元素交换adjustHeap(arr, 0, i); // 重新调整堆}}// 构建最大堆的方法private static void buildMaxHeap(int[] arr) {int lastIndex = arr.length - 1;int parentIndex = (lastIndex - 1) / 2; // 最后一个非叶子节点的下标for (int i = parentIndex; i >= 0; i--) {adjustHeap(arr, i, arr.length);}}// 调整堆的方法,使其满足最大堆的性质private static void adjustHeap(int[] arr, int index, int length) {int temp = arr[index]; // 当前父节点for (int k = 2 * index + 1; k < length; k = 2 * k + 1) { // 从index结点的左子结点开始,也就是2*index+1处开始if (k + 1 < length && arr[k] < arr[k + 1]) { // 如果左子结点小于右子结点,k指向右子结点k++;}if (arr[k] > temp) { // 如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)arr[index] = arr[k];index = k;} else {break;}}arr[index] = temp; // 将temp值放到最终的位置}// 交换数组中两个元素的方法private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}// 测试方法public static void main(String[] args) {int[] arr = {4, 6, 8, 5, 9, 1, 2, 3, 7};sort(arr);for (int num : arr) {System.out.print(num + " ");}}
}
七、计数排序
1.工作原理:
(1)统计每个元素出现的次数,遍历待排序的数组,统计每个元素出现的次数,可以使用额外的辅助数组来保存结果。
(2)根据元素的计数信息,确定元素在排序后的位置;根据元素值和它在计数数组中的计数位置,确定元素在排序后的位置。
(3)将元素放置到正确的位置;遍历待排序的数组,根据元素值在计数数组中的位置,将元素放置到正确的位置上。
(4)输出排序后的结果;将排序后的结果一次输出,即得到排序好的数组、
2.代码:
public class CountingSortExample {public static void countingSort(int[] array, int max) {int[] count = new int[max + 1]; // 创建计数数组,并初始化为 0for (int num : array) {count[num]++; // 统计每个元素的个数}int k = 0;for (int i = 0; i < count.length; i++) {while (count[i] > 0) {array[k] = i; // 将计数数组中的元素按顺序填充到原始数组中k++;count[i]--;}}}public static void main(String[] args) {int[] array = {4, 2, 2, 8, 3, 3, 1};int max = 8; // 数组中的最大值System.out.println("Before sorting: " + Arrays.toString(array));countingSort(array, max);System.out.println("After sorting: " + Arrays.toString(array));}
}
八、桶排序
1.工作原理:
遍历待排序的数组,根据预设的规则将元素分配到对应的桶中;
对每个桶内的元素进行排序,可以使用插入排序、快速排序等方法;
将各个桶内的元素按顺序进行合并,得到最终的有序数组
2.代码:
import java.util.ArrayList;
import java.util.Collections;public class BucketSort {public static void bucketSort(int[] nums) {int bucketSize = 5; // 设置桶的大小int maxValue = Integer.MIN_VALUE;int minValue = Integer.MAX_VALUE;for (int num : nums) {maxValue = Math.max(maxValue, num); // 找到待排序数组的最大值minValue = Math.min(minValue, num); // 找到待排序数组的最小值}int bucketCount = (maxValue - minValue) / bucketSize + 1; // 计算桶的个数ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketCount);for (int i = 0; i < bucketCount; i++) {buckets.add(new ArrayList<>()); // 初始化桶}for (int num : nums) {int bucketIndex = (num - minValue) / bucketSize; // 计算元素应该放入的桶的索引buckets.get(bucketIndex).add(num); // 将元素放入对应的桶中}int index = 0;for (ArrayList<Integer> bucket : buckets) {Collections.sort(bucket); // 对每个桶内的元素进行排序for (int num : bucket) {nums[index++] = num; // 将排序好的元素放回原数组}}}public static void main(String[] args) {int[] arr = {29, 25, 3, 49, 9, 37, 21, 43};bucketSort(arr);for (int num : arr) {System.out.print(num + " ");}}
}
九、基数排序
1.工作原理:
首先,将待排序的整数按照个位数的数值,进行分配到“桶”中、
其次,按照个位数的顺序将桶中的数字依次取出,组成一个新的序列、
然后,再以十位数的数值进行分配到对应的桶中,并按照顺序取出,形成新的顺序、
最后,以此类推,直到所有位数都分配完毕,得到有序的序列。
2.代码:
import java.util.ArrayList;public class RadixSort {// 获取数字的指定位数的数字public static int getDigit(int number, int digitPlace) {return (number / digitPlace) % 10;}// 基数排序函数public static void radixSort(int[] arr) {// 找出数组中的最大值,以确定需要多少位数int max = arr[0];for (int num : arr) {if (num > max) {max = num;}}// 计算最大值的位数int numDigits = 1;while (max > 9) {max /= 10;numDigits++;}// 创建 10 个桶ArrayList<ArrayList<Integer>> buckets = new ArrayList<>();for (int i = 0; i < 10; i++) {buckets.add(new ArrayList<>());}// 进行排序for (int digit = 1; digit <= Math.pow(10, numDigits - 1); digit *= 10) {// 将数字分配到对应的桶中for (int num : arr) {int digitVal = getDigit(num, digit);buckets.get(digitVal).add(num);}// 从桶中取出数字,并按顺序放回原数组int index = 0;for (int i = 0; i < 10; i++) {for (int num : buckets.get(i)) {arr[index++] = num;}buckets.get(i).clear(); // 清空桶,以便下一轮排序使用}}}// 测试public static void main(String[] args) {int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};radixSort(arr);System.out.print("排序后的数组:");for (int num : arr) {System.out.print(num + " ");}}
}
十、希尔排序
1.工作原理:
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所 有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后 取, 重复上述分组和排序的工 作。当到达=1时,所有记录在统一组内排好序。
2.代码:
void ShellSort(int* a, int n)
{//n:待排序数字的个数;n=sizeof(a)/sizeof(int);// 多次预排序(gap > 1) +直接插入 (gap == 1)int gap = n;while (gap > 1){//gap = gap / 2;gap = gap / 3 + 1;// 多组并排for (int i = 0; i < n - gap; ++i){int end = i;int x = a[end + gap];while (end >= 0){if (a[end] > x){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = x;}}
}
###案例
1023:【编程入门】选择排序:
题目描述
用选择法对10个整数从小到大排序。
输入格式
输入10个无序的数字
输出格式
排序好的10个整数
样例输入
4 85 3 234 45 345 345 122 30 12
样例输出
3
4
12
30
45
85
122
234
345
345
import java.util.*;
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int[] array=new int[10];for(int i=0;i<10;i++) {array[i]=scanner.nextInt();}selectionSort(array);}public static void selectionSort(int[] array) {for(int i=0;i<array.length;i++) {int minIndex=i;for(int j=i+1;j<array.length;j++) {//从未排序的元素中找到最小的元素if(array[j]<array[minIndex]) {minIndex=j;//更新最小元素的下标}}//将其放到已排序序列的末尾(0到i为已排序,j到n-1为未排序)int temp=array[i];array[i]=array[minIndex];array[minIndex]=temp;}for(int i=0;i<10;i++) {System.out.println(array[i]);}}
}
package day2;import java.util.Scanner;public class SelectionSort {public static void main(String[] args) {Scanner scanner= new Scanner(System.in);int[] arr=new int[10];for (int i = 0; i < 10; i++) {arr[i]=scanner.nextInt();}for(int i=0;i<arr.length-1;i++){for(int j=i+1;j<arr.length;j++){if(arr[i]>arr[j]){int temp=arr[i];arr[i]=arr[j];arr[j]=temp;}}}printArr(arr);}private static void printArr(int[] arr){for(int i=0;i<arr.length;i++){System.out.println(arr[i]);}System.out.println();}
}