一、冒泡排序
1、原理:将较大元素向后调
(1)从前往后依次比较相邻的元素,若前者大于后者,则交换两者位置。
(2)最后一对元素比较完成,则一趟排序结束,最后元素为本趟最大的数。
(3)每趟除去最后一个元素,从(1)进行下一趟排序,直到只剩下一个元素或某趟排序后标志位没有改变为止。
2、性质:
(1)稳定性:两个元素相等时,不交换两者位置,则冒泡排序是一种稳定排序算法。
(2)时间复杂度:
最好时间复杂度:O(n)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
(3)空间复杂度:O(1)
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {4, 3, 2, 1};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 冒泡排序
* @param arr 待排序数组
*/
public static void bubbleSort(int[] arr) {
//设置排序趟数
for (int i = 0; i < arr.length - 1; i++) {
//定义元素交换标志
boolean flag = true;
//每趟排序,除去最后一个元素
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
flag = false;
}
}
//一趟排序结束后,若标志位没有改变,说明冒泡排序完成
if (flag) {
break;
}
}
}
/**
* 交换数组元素位置
* @param arr 目标数组
* @param i 元素索引
* @param j 元素索引
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
二、快速排序
1、原理:
(1)定义第一个元素为基准,从左向右定位比基准元素大的值的索引(左索引),从右向左定位比基准元素小的值的索引(右索引),交换两者位置,直到左索引不小于右索引为止。
(2)交换基准元素与右索引对应的元素,此时左边元素小于或等于基准值,右边元素大于基准值。
(3)重复(1)、(2),递归左边数组,递归右边数组(递归终止条件:数组左索引大于或等于右索引)。
2、性质:
(1)稳定性:左边某个元素可能与基准元素相等,交换基准元素与右索引对应的元素后,左边某个元素与基准元素相对位置会发生变化,则快速排序是一种不稳定排序算法。
(2)时间复杂度:
最好时间复杂度:O(nlog(n))
最坏时间复杂度:O(n^2)
平均时间复杂度:O(nlog(n))
(3)空间复杂度:O(1)
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {4, 2, 6, 8, 7, 2, 9};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
/**
* 递归方式实现快速排序
* @param arr 待排序的数组
* @param start 起始索引
* @param end 终止索引
*/
public static void quickSort(int[] arr, int start, int end) {
if (start < end) {
//保存基准值
int base = arr[start];
//定义左索引
int left = start;
//定义右索引
int right = end + 1;
while (true) {
//从左向右定位比基准元素大的值的索引
while (left < end && arr[++left] <= base) ;
//从右向左定位比基准元素小的值的索引
while (right > start && arr[--right] >= base) ;
if (left < right) {
//交换两者位置
swap(arr, left, right);
} else {
break;
}
}
//交换基准元素与右索引对应的元素,使左边元素都小于或等于基准值,右边元素都大于基准值
swap(arr, start, right);
//递归左边数组
quickSort(arr, start, right - 1);
//递归右边数组
quickSort(arr, right + 1, end);
}
}
/**
* 交换数组元素位置
* @param arr 目标数组
* @param i 元素索引
* @param j 元素索引
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
三、堆排序
1、原理:
(1)根据末尾元素,找到末尾元素的父节点,从后向前遍历,找出其左右节点较大的值,若较大子节点大于其父节点值,交换两者位置。
(2)从末尾元素的父节点开始,由后向前遍历,重复(1),直到头元素为止。
(3)建立一次大顶堆后,将数组头元素与末尾元素交换,重复(1)、(2)。
2、性质:
(1)稳定性:建立大顶堆的过程中,两个大小相等的元素相对位置可能会发生变化,则堆排序是一种不稳定排序算法。
(2)时间复杂度:
最好时间复杂度:O(nlog(n))
最坏时间复杂度:O(nlog(n))
平均时间复杂度:O(nlog(n))
(3)空间复杂度:O(1)
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {1, 4, 7, 2, 5, 8, 6};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 堆排序
* @param arr 待排序数组
*/
public static void heapSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
//建立一次大顶堆,数组首个元素最大
buildMaxBuild(arr, arr.length - 1 - i);
//每次将首元素交换到数组末尾
swap(arr, 0, arr.length - 1 -i);
}
}
/**
* 建立大顶堆:建一次大顶堆,数组首个元素最大
* @param arr 待排序数组
* @param lastIndex 末尾索引
*/
public static void buildMaxBuild(int[] arr, int lastIndex) {
//根据末尾元素,找到末尾元素的父节点,进行从后往前遍历
for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
int currentIndex = i;
//判断当前索引是否存在子节点
while (2 * currentIndex + 1 <= lastIndex) {
//存在子节点,定义左子节点为较大值对应的索引
int biggerIndex = 2 * currentIndex + 1;
//判断是否存在右子节点
if (biggerIndex < lastIndex) {
//比较左右子节点大小,找到较大值索引
if (arr[biggerIndex + 1] > arr[biggerIndex]) {
biggerIndex++;
}
}
//比较子节点与父节点的值
if (arr[biggerIndex] > arr[currentIndex]) {
//子节点大于父节点值,交换对应位置
swap(arr, biggerIndex, currentIndex);
//交换位置后,较大索引处的值发生变化,再次循环,判断是否存在子节点
currentIndex = biggerIndex;
} else {
//没有交换,直接结束本次循环
break;
}
}
}
}
/**
* 交换数组元素位置
* @param arr 目标数组
* @param i 元素索引
* @param j 元素索引
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
四、归并排序
1、原理:
(1)申请空间,使其大小为两个已经排好序的数组长度之和,该空间用来存放合并后的数组。
(2)定义两个指针,最初位置分别为两个已经排好序数组的起始位置。
(3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置,重复(3),直到其中一个指针超出数组。
(4)将另一数组剩下的所有元素直接复制到合并数组的尾部
2、性质:
(1)稳定性:数组元素合并时,若两数相等,相对位置较前者先放到辅助数组中,则归并排序是一种稳定排序算法。
(2)时间复杂度:
最好时间复杂度:O(nlog(n))
最坏时间复杂度:O(nlog(n))
平均时间复杂度:O(nlog(n))
(3)空间复杂度:O(n)
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] arr = {4, 5, -1, 2, 8, 6};
mergeSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
/**
* 使用递归方式实现归并排序
* @param arr 待排序数组
* @param left 起始索引
* @param right 终止索引
*/
public static void mergeSort(int[] arr, int left, int right) {
//递归遍历到只有两个元素
if (left < right) {
//定义数组中间索引
int center = (left + right) / 2;
//递归左数组
mergeSort(arr, left, center);
//递归右数组
mergeSort(arr, center + 1, right);
//左右数组元素合并
merge(arr, left, center, right);
}
}
/**
* 数组元素比较合并
* @param arr 待合并数组
* @param left 起始索引
* @param center 中间索引
* @param right 终止索引
*/
public static void merge(int[] arr, int left, int center, int right) {
//开辟临时数组
int[] tempArr = new int[arr.length];
//定义临时数组索引
int tempIndex = left;
//定义右数组起始索引
int middle = center + 1;
//定义起始索引
int tempLeft = left;
//左右两个数组元素比较大小,较小者保存到临时数组
while (left <= center && middle <= right) {
if (arr[left] <= arr[middle]) {
tempArr[tempIndex++] = arr[left++];
} else {
tempArr[tempIndex++] = arr[middle++];
}
}
//左数组有剩余
while (left <= center) {
tempArr[tempIndex++] = arr[left++];
}
//右数组有剩余
while (middle <= right) {
tempArr[tempIndex++] = arr[middle++];
}
//将排好序的临时数组复制到原数组
while (tempLeft <= right) {
arr[tempLeft] = tempArr[tempLeft++];
}
}
}