1. 冒泡排序
核心提炼:相邻元素,两两比较,按序交换,逐趟进行,给人以水泡逐渐上浮的感觉
时间复杂度:每一趟比较的次数依次为:n-1次、n-2次…1次,所以累加求和得到复杂度为O(n^2)
空间复杂度:只需要一个中间变量temp用于元素交换,故复杂度为O(1)
实现代码:
/**
* 冒泡排序(两两比较--交换)
* @param arr
*/
public static void bubbleSort(int[] arr){
if(arr == null || arr.length <= 0){
return;
}
int length = arr.length;
//如果有一趟比较的过程中没有进行任何元素的交换,说明已经有序,不再进行后续的比较交换工作
boolean stop = true;
for(int i = 0 ; i < length - 1 ; i++){
for(int j = 0 ; j < length - i - 1 ; j++){
if(arr[j] > arr[j + 1]){
swap(arr,j,j+1);
stop = false;
}
}
if(stop == true){
break;
}
}
}
/**
* 公用交换方法
* @param arr
* @param left
* @param right
*/
private static void swap(int[] arr , int left , int right){
if(arr == null || arr.length <= 0 || left < 0 || right >= arr.length){
return;
}
int temp = arr[left] + arr[right];
arr[left] = temp - arr[left];
arr[right] = temp - arr[left];
}
2. 快速排序
核心提炼:选择分界点元素,将待排序元素按照大小分为两部分,小于分界点元素的以及大于分界点元素的,对这两部分递归进行上述操作,直至所有的元素有序
背后的思想:分治法,分而治之
时间复杂度:O(nlog2(n))
空间复杂度:O(1)
注意:快速排序对于基本有序或有序的待排序数组来说,会退化为冒泡排序,可以对中轴位置的获取进行优化,可以避免这种极端情况
实现代码:
/**
* 递归调用快排核心方法进行局部数组的快排
* @param arr
* @param begin
* @param end
*/
public static void quickSort(int[] arr,int begin,int end){
if(arr == null || arr.length <= 0 || begin < 0 || end > arr.length - 1){
return;
}
if(begin < end){
int pivot = quickSortCore(arr , begin , end);
quickSort(arr,begin,pivot-1);
quickSort(arr,pivot+1,end);
}
}
/**
* 快排核心方法(获取中轴元素位置)
* @param arr
*/
public static int quickSortCore(int[] arr,int begin,int end){
if(arr == null || arr.length <= 0 || begin < 0 || end > arr.length - 1){
return -1;
}
int low = begin;
int high = end;
int pivot = arr[begin];
while(low < high){
//切记这里需要判断前后位置,并且比较大小时需要考虑两者相等的情况,否则会出现死循环
while(high > low && pivot <= arr[high]){
high--;
}
arr[low] = arr[high];
while(high > low && pivot >= arr[low]){
low++;
}
arr[high] = arr[low];
}
//一趟快排之后得到的分界点位置
arr[low] = pivot;
return low;
}
3. 简单选择排序
核心提炼:逐趟选择剩余待排序元素中最小的元素,放到有序部分的尾部
时间复杂度:O(n^2)
空间复杂度:O(1)
实现代码:
/**
* 简单选择排序(找下标--交换)
* @param arr
*/
public static void selectSort(int[] arr){
if(arr == null || arr.length <= 0){
return;
}
int length = arr.length;
for(int i = 0 ; i < length - 1 ; i++){
int min = i;
for(int j = i + 1 ; j < length ; j++){
if(arr[j] < arr[min]){
min = j;
}
}
if(min != i){
swap(arr,i,min);
}
}
}
4. 堆排序
核心提炼:完全二叉树为背景,引出大顶堆和小顶堆,关键部分在于初次建堆和后续堆的调整
实现代码:
/**
* 堆排序主方法
* @param arr
*/
public static void heapSort(int[] arr){
if(arr == null || arr.length <= 0){
return;
}
int len = arr.length;
//第一次将给定数组调整为大顶堆,将数组转换为完全二叉树即可得出编码的逻辑,顺序是从右向左
for(int i = len/2 - 1 ; i >= 0 ; i--){
adjustMaxHeap(arr,i,len);
}
//交换堆顶元素至数组尾部,并对交换后的数组进行大顶堆的调整
for(int i = len - 1 ; i > 0 ; i--){
swap(arr,0,i);
adjustMaxHeap(arr,0,i-1);
}
}
/**
* 堆排序-调整堆的方法
* @param arr
* @param root
* @param len
*/
public static void adjustMaxHeap(int[] arr, int root, int len){
//子树的根节点
int temp = arr[root];
//循环处理从当前节点的子树,使其变为大顶堆,顺序是从上往下
for(int i = 2*root; i < len; i++){
//判断左右子节点中的较大元素,并将下标更新
if(i < len-1 && arr[i] < arr[i+1]){
i++;
}
//如果根节点已大于左右子节点,则堆调整完毕
if(temp >= arr[i]){
break;
}
//将较大的子节点替换为父节点
arr[root] = arr[i];
//更新最后待插入的节点下标
root = i;
}
//更新最后的子节点
arr[root] = temp;
}
5. 简单插入排序
核心提炼:将待排序元素分为两部分,左边为有序部分(初始时只有第一个元素),右边为待排序部分,依次取出待排序元素与有序部分进行比较,寻找合适的插入位置进行插入
实现代码:
/**
* 简单插入排序(无序变有序--找位置插入)
* @param arr
*/
public static void insertSort(int[] arr){
if(arr == null || arr.length <= 0){
return;
}
int length = arr.length;
for(int i = 1 ; i < length ; i++){
int temp = arr[i];
int j = i - 1;
for( ; j >= 0 ; j--){
if(arr[j] > temp){
arr[j + 1] = arr[j];
}else{
break;
}
}
arr[j+1] = temp;
}
}
6. 希尔排序
核心提炼:简单插入排序的改进版,采用缩小增量的插入排序,将待排序元素分段进行排序,直至最终的完全有序
代码实现:
7. 归并排序
核心提炼:完全二叉树为背景,分治思想
实现代码:
public static void mergeSort(int[] arr){
if(arr == null || arr.length <= 0){
return;
}
int[] temp = new int[arr.length];
mergeSortCore(arr,temp,0,arr.length-1);
}
private static void mergeSortCore(int[] arr, int[] temp, int low, int high) {
int mid = (low + high) / 2;
if(low == high){
temp[low] = arr[low];
for(int i = low; i <= high ; i++){
arr[i] = temp[i];
}
}else{
mergeSortCore(arr,temp,low,mid);
mergeSortCore(arr,temp,mid+1,high);
mergeCore(arr,temp,low,mid,high);
//注意,一定要将已排序部分更新到原数组中
for(int i = low; i <= high ; i++){
arr[i] = temp[i];
}
}
}
private static void mergeCore(int[] arr, int[] temp, int low, int mid, int high) {
int begin = mid + 1;
int index = low;
while(low <= mid && begin <= high){
if(arr[low] < arr[begin]){
temp[index] = arr[low];
low++;
}else{
temp[index] = arr[begin];
begin++;
}
index++;
}
while(low <= mid){
temp[index] = arr[low];
index++;
low++;
}
while(begin <= high){
temp[index] = arr[begin];
index++;
begin++;
}
}
8. 基数排序
9. 桶排序