冒泡排序
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
/**
* 冒泡排序
* @ClassName: BubbleSort
* @author: IT_小白
* @date 2023-05-30
* @Description:
*/
public class BubbleSort {
public static void bubbleSort(int[] arr) {
if(arr == null || arr.length <= 0)return ;
for (int i = 0; i < arr.length; i++) {
for (int j = 1; j < arr.length - i; j++) {
if(arr[j] < arr[j-1]) {
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
}
选择排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。重复第二步,直到所有元素均排序完毕。
/**
* 选择排序
* @ClassName: SelectionSort
* @author: IT_小白
* @date 2023-05-31
* @Description:
*/
public class SelectionSort {
public static void selectionSort(int[] arr) {
if(arr == null && arr.length <= 0) return ;
for (int i = 0; i < arr.length; i++) {
//获取此次循环中最小值
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if(arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if(minIndex != i) {
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
selectionSort(arr);
System.out.println(Arrays.toString(arr));
}
}
插入排序
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
/**
* 插入算法
* @ClassName: InsertSort
* @author: IT_小白
* @date 2023-05-31
* @Description:
*/
public class InsertSort {
public static void insertSort(int[] arr) {
if(arr == null && arr.length <= 0)return;
//以索引0位置的数作为有序序列
for (int i = 1; i < arr.length; i++) {
//用来记录要插入的值
int temp = arr[i];
int j = i;
while(j > 0 && temp < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
//说明插入到前面了
if(j != i) {
arr[j] = temp;
}
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
}
希尔排序
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1; 按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1
时,整个序列作为一个表来处理,表长度即为整个序列的长度。
/**
* 希尔排序
* @ClassName: ShellSort
* @author: IT_小白
* @date 2023-05-31
* @Description:
*/
public class ShellSort {
public static void shellSort(int[] arr) {
int length = arr.length,temp;
for(int step = length / 2;step >= 1;step /= 2) {
for(int i = step;i < length;i++) {
temp = arr[i];
int j = i - step;
while(j >= 0 && arr[j] > temp) {
arr[step + j] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
}
归并算法
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列; 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置; 重复步骤 3 直到某一指针达到序列尾;
将另一序列剩下的所有元素直接复制到合并序列尾。
/**
* 归并算法
* @ClassName: MergeSort
* @author: IT_小白
* @date 2023-05-31
* @Description:
*/
public class MergeSort {
/**
*
* @param arr 原数组
* @param left 左索引
* @param right 右索引
* @param 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);
//拆分后合并
merge(arr,left,right,mid,temp);
}
}
/**
* 由于不断拆分重组,所以每半边的数据都是有序的,
* 所以在新的一轮重组过程就需要检查左边元素是否都小于右边元素,并将正确的顺序放到临时数组中,最终替换对应位置的原数组。
* @param arr 原数组
* @param left 左索引
* @param right 右索引
* @param mid 中间索引
* @param temp 临时数组
*/
private static void merge(int[] arr,int left,int right,int mid,int[] temp) {
int i = left,j = mid + 1,index = 0;
//用左边序列跟右边序列进行比较
while(i <= mid && j <= right ) {
if(arr[i] < arr[j]) {
temp[index++] = arr[i++];
} else {
temp[index++] = arr[j++];
}
}
//说明左边序列还有未排序的有序数据
while(i <= mid) {
temp[index++] = arr[i++];
}
//说明右边序列还有未排序的有序数据
while(j <= right) {
temp[index++] = arr[j++];
}
int resultIndex = left;
index = 0;
//将临时数组中的数据填充回原数组
while(resultIndex <= right) {
arr[resultIndex++] = temp[index++];
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
mergeSort(arr, 0, arr.length-1, new int[arr.length] );
System.out.println(Arrays.toString(arr));
}
}
快速排序
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
/**
* 快速排序
* @ClassName: QuickSort
* @author: IT_小白
* @date 2023-06-01
* @Description:
*/
public class QuickSort {
public static void quickSort(int[] arr,int left,int right) {
if(arr == null || arr.length <= 0 || left > right)return;
int start = left,end = right,key = arr[left];
while(start < end) {
//从后往前比较
while(start < end && arr[end] >= key) {
end--;
}
//再从前往后比较
while(start < end && arr[start] <= key) {
start++;
}
//交换值
if(start < end) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
}
arr[left] = arr[start];
arr[start] = key;
//向左递归
quickSort(arr, left, end - 1);
//向右递归
quickSort(arr, end + 1, right);
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}
堆排序
创建一个堆 H[0……n-1];
把堆首(最大值)和堆尾互换;
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
重复步骤 2,直到堆的尺寸为 1。
/**
* 堆排序
* @ClassName: HeapSort
* @author: IT_小白
* @date 2023-06-01
* @Description:
*/
public class HeapSort {
private static void heapSort(int[] arr) {
if(arr == null && arr.length <= 0)return;
int length = arr.length;
//构建堆
buildHeap(arr,length);
for (int i = length - 1; i >= 0; i--) {
//将堆顶元素与末尾元素调换
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
//长度-1隐藏队尾元素
length--;
//将堆顶元素下沉,使目前最大的元素浮到堆顶
sink(arr, 0, length);
}
}
private static void buildHeap(int[] arr, int length) {
//由于使用的是一维数组模拟堆结构,所以构建时从中间开始
for(int i = (int)Math.floor(length / 2) ; i >= 0;i--) {
//下沉元素
sink(arr, i, length);
}
}
private static void sink(int[] arr,int index,int length) {
//左右节点 和 当前节点
int leftChild = (index * 2) + 1;
int rightChild = (index * 2) + 2;
int present = index;
if(leftChild < length && arr[leftChild] > arr[present]) {
//下标变动
present = leftChild;
}
if(rightChild < length && arr[rightChild] > arr[present]) {
//下标变动
present = rightChild;
}
if(index != present) {
int temp = arr[present];
arr[present] = arr[index];
arr[index] = temp;
//继续下沉
sink(arr, present, length);
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}
计数排序
(1)找出待排序的数组中最大和最小的元素
(2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项
(3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
(4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
/**
* 计数排序
* @ClassName: CountSort
* @author: IT_小白
* @date 2023-06-01
* @Description:
*/
public class CountSort {
public static void countSort(int[] arr) {
if(arr == null && arr.length <= 0)return ;
//获取最大值
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if(maxValue < arr[i]) {
maxValue = arr[i];
}
}
//设置桶
int[] bucket = new int[maxValue + 1];
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
//从桶中取值
int index = 0;
for (int i = 0; i < bucket.length; i++) {
while(bucket[i] > 0) {
arr[index++] = i;
bucket[i]--;
}
}
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
countSort(arr);
System.out.println(Arrays.toString(arr));
}
}
桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
1、在额外空间充足的情况下,尽量增大桶的数量
2、使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
/**
* 桶排序
* @ClassName: BucketSort
* @author: IT_小白
* @date 2023-06-01
* @Description:
*/
public class BucketSort {
/**
* 桶排序
* @param arr 原数组
* @param bucketSize 设置桶的系数
*/
public static void bucketSort(int[] arr,int bucketSize) {
if(arr == null && arr.length <= 0)return ;
//获取最大值和最小值
int maxValue = arr[0],minValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if(maxValue < arr[i]) {
maxValue = arr[i];
}
if(minValue > arr[i]) {
minValue = arr[i];
}
}
//设置桶
int bucketCount = (int)Math.floor((maxValue - minValue) / bucketSize) + 1;
int[][] buckets = new int[bucketCount][0];
for (int i = 0; i < arr.length; i++) {
int index = (int)Math.floor((arr[i] - minValue) / bucketCount);
buckets[index] = arrAppend(buckets[index], arr[i]);
}
int arrIndex = 0;
for(int[] bucket : buckets) {
if(bucket.length <= 0) {
continue;
}
//对桶中元素进行排序,这里调用的快排算法
QuickSort.quickSort(bucket, 0, bucket.length - 1);
for(int value : bucket) {
arr[arrIndex++] = value;
}
}
}
/**
* 自动扩容并填值
* @param arr
* @param value
* @return
*/
private static int[] arrAppend(int[] arr,int value) {
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = value;
return arr;
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,9,1,3,14};
bucketSort(arr,5);
System.out.println(Arrays.toString(arr));
}
}
基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
/**
* 基数排序
* @ClassName: RadixSort
* @author: IT_小白
* @date 2023-06-01
* @Description:
*/
public class RadixSort {
public static void radixSort(int[] arr) {
if(arr == null && arr.length <= 0) return ;
//获取最大值的位数
int maxNumLength = getMaxNumLength(arr);
//帮助数组元素获取对应位数数值
int mid = 10,div = 1;
for (int i = 0; i < maxNumLength; i++ ,mid *= 10,div *= 10) {
//创建临时数组
int[][] temps = new int[mid * 2][0];
for (int j = 0; j < arr.length; j++) {
//此设计可用于比较负数
int index = ((arr[j] % mid) / div) + mid;
temps[index] = arrAppend(temps[index], arr[j]);
}
//将临时数组中的数据按顺序倒入原数组中
int index = 0;
for(int[] temp : temps) {
for(int value : temp) {
arr[index++] = value;
}
}
}
}
/**
* 获取数组中最大值的位数
* @param arr
* @return
*/
private static int getMaxNumLength(int[] arr) {
int maxValue = arr[0],maxNumLength = 0;
for (int i = 1; i < arr.length; i++) {
if(maxValue < arr[i]) {
maxValue = arr[i];
}
}
for(int i = maxValue ; i > 0; i /= 10) {
maxNumLength++;
}
return maxNumLength;
}
/**
* 自动扩容并填值
* @param arr
* @param value
* @return
*/
private static int[] arrAppend(int[] arr,int value) {
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = value;
return arr;
}
public static void main(String[] args) {
int[] arr = new int[] {23,3,5,12,-10,9,1,3,14,-4};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
}