蓝桥杯-八大经典排序算法


表格总览

排序方法

时间复杂度(平均)

时间复杂度(最坏)

时间复杂度(最好)

空间复杂度

稳定性

复杂性

直接插入排序

O(n2)

O(n2)

O(n)

O(1)

稳定

简单

希尔排序

O(nlog2n)

O(n2)

O(n)

O(1)

不稳定

较复杂

直接选择排序

O(n2)

O(n2)

O(n2)

O(1)

不稳定

简单

堆排序

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

不稳定

较复杂

冒泡排序

O(n2)

O(n2)

O(n)

O(1)

稳定

简单

快速排序

O(nlog2n)

O(n2)

O(nlog2n)

O(nlog2n)

不稳定

较复杂

归并排序

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(n)

稳定

较复杂

基数排序

O(d(n+r))

O(d(n+r))

O(d(n+r))

O(n+r)

稳定

较复杂

插入排序

插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动 。

直接插入排序

从左往右,每次都以i+1为右边界,然后i+1的元素需要插入已经排好序的[0,i]的数组里,从后面一直比较到前面,遍历从i到0,只要有元素大于i+1那个元素就交换,最后插入成功后[0,i+1]就是排好序的数组了。

public static void insertSort(int[] arr) {
    int temp;
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = i+1; j > 0; j--) {
            // 前者 > 后者,交换;
            if(arr[j-1] > arr[j]) {
                temp = arr[j-1];
                arr[j-1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    System.out.println(change(arr));
}

希尔排序

缩小增量排序,对直接插入排序的一种改进,分组插入方法,每次都以rightStart=k为右边界的开始遍历,然后以比较0+k与k+k的方式遍历数组,前者大于后者就替换,直到k=0。

public static void shellSort(int[] arr) {
    int temp;
    int k = arr.length/2;
    while (k != 0) {
        for (int rightStart = k; rightStart < arr.length; rightStart++) {
            int left = rightStart - k;
            int right = rightStart;
            // 每次都是左右俩边对比
            while (right < arr.length) {
                if(arr[left] > arr[right]) {
                    temp = arr[left];
                    arr[left] = arr[right];
                    arr[right] = temp;
                }
                left += k;
                right += k;
            }
        }
        k /= 2;
    }
    System.out.println(change(arr));
}

选择排序

选择排序是一种简单直观的排序算法,它的工作原理是每一次从待排序的数据元素中选出最小(最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法。

直接选择排序

从左往右遍历,基于左边界的值进行比较,找到最小的元素进行替换,保证左边界的值是最小的。


public static void selectSort(int[] arr) {
    int temp;
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = i+1; j < arr.length; j++) {
            // 参照物 > 目标,交换
            if(arr[i] > arr[j]) {
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    System.out.println(change(arr));
}

堆排序

将待排序序列构成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其于末尾元素进行交换,此时末尾就为最大值,然后将剩余n-1个元素重新构成一个堆,这样就会得到n个元素的次小值,如此反复执行就能得到一个有序序列。

public class HeapSort {
 
	public static void heapSort(int[] arr) {
		if (arr == null || arr.length == 0) {
			return;
		}
		int len = arr.length;
		// 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
		buildMaxHeap(arr, len);
 
		// 交换堆顶和当前末尾的节点,重置大顶堆
		for (int i = len - 1; i > 0; i--) {
			swap(arr, 0, i);
			len--;
			heapify(arr, 0, len);
		}
	}
 
	private static void buildMaxHeap(int[] arr, int len) {
		// 从最后一个非叶节点开始向前遍历,调整节点性质,使之成为大顶堆
		for (int i = (int)Math.floor(len / 2) - 1; i >= 0; i--) {
			heapify(arr, i, len);
		}
	}
 
	private static void heapify(int[] arr, int i, int len) {
		// 先根据堆性质,找出它左右节点的索引
		int left = 2 * i + 1;
		int right = 2 * i + 2;
		// 默认当前节点(父节点)是最大值。
		int largestIndex = i;
		if (left < len && arr[left] > arr[largestIndex]) {
			// 如果有左节点,并且左节点的值更大,更新最大值的索引
			largestIndex = left;
		}
		if (right < len && arr[right] > arr[largestIndex]) {
			// 如果有右节点,并且右节点的值更大,更新最大值的索引
			largestIndex = right;
		}
 
		if (largestIndex != i) {
			// 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
			swap(arr, i, largestIndex);
			// 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
			heapify(arr, largestIndex, len);
		}
	}
 
	private static void swap (int[] arr, int i, int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
}

交换排序

对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。

冒泡排序

每次遍历进行i与i+1的值进行比较,把大的值都往数组后面丢,保证了数组末尾的元素为最大。

public static int[] bubbleSort(int[] arr) {
    int temp;
    for(int i=arr.length-1;i>0;i--) {
        for(int j=0;j<i;j++) {
            // 前者如果小于后者,就调换
            if (arr[j] > arr[j+1]) {
                temp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    System.out.println(change(arr));
    return arr;
}

快速排序

思想就是设定一个基准值,然后把小于该值的丢左右,大于的丢右边。

快速排序的操作是这样的:

  1. 设定最左边的也就是l下标的为基准值
  2. 从数列的右边开始往左边找,我们设这个下标为 r,也就是进行右减减操作(r--),找到第 1 个比基准数小的值,赋值给下标l,也就是数组[l] = 数组[r],然后 l++
  3. 从l开始往右边找,也就是执行左加加操作(l++),找到第 1 个比基准数大的值,赋值给下标r,也就是数组[r] = 数组[l],然后 r--
  4. 重复2跟3,直到 l 与 r 相遇时结束,最后把基准值赋给下标r,这时候 r 左边的值均比 r 上的值小,而 r 右边的值都比 r 上的值大。
  5. 基于 r 为划分线 的 左右边数组(不包括r了),让它们继续按照这个步骤处理,最终就能获取到排序后的值。
public class QuickSort {
    private int[] array;
    public QuickSort(int[] array) {
        this.array = array;
    }
    public void sort() {
        quickSort(array, 0, array.length - 1);
    }
    public void print() {
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
   
    /**
     * 快速排序
     * @param src
     * @param begin
     * @param end
     */
    private void quickSort(int[] src, int begin, int end) {
        if (begin < end) {
            int k = src[begin];
            int l = begin;
            int r = end;
            while (l < r) {
                while (l < r && src[r] > k) {
                    r--;
                }
                if (l < r) {
                    src[l] = src[r];
                    l++;
                }
                while (l < r && src[l] < k) {
                    l++;
                }
                if (l < r) {
                    src[r] = src[l];
                    r--;
                }
            }
            src[l] = k;
            quickSort(src, begin, l - 1);
            quickSort(src, l + 1, end);	
        }
    }
}

归并排序

把大数组拆分成小数组不断进行比较排序得到最终结果。比如{1,2,3,4} => {1,2},{3.4} => {1}, {2},{3},{4}。

每次比较都需要新建一个临时一样大小的数组用来转比较结果。


public static void MergeSort(int[] arr) {
    split(arr, 0, arr.length - 1);
    System.out.println(change(arr));
}
// 拆分
public static void split(int[] arr, int left, int right) {
    if(left == right) {
        return;
    }
    int mid = (right + left) / 2;
    split(arr, left, mid);
    split(arr, mid + 1, right);
    // 拆分到最细后,进行排序
    sort(arr, left, mid, right);
}

// 排序
public static void sort(int[] arr, int left, int mid, int right) {
    // 比较2个数组的元素大小,设置到left 到 right里
    int leftIndex = left;
    int rightIndex = mid + 1;
    // 插入的临时排好序的数组;
    int[] tempArr = new int[right - left + 1];
    int tempIndex = 0;
    // 左边的界限为 left ---- mid,右边为 mid + 1  ----  right
    while(leftIndex <= mid && rightIndex <= right) {
        // 谁小,就设置谁
        if(arr[leftIndex] < arr[rightIndex]) {
            tempArr[tempIndex++] = arr[leftIndex++];
        } else {
            tempArr[tempIndex++] = arr[rightIndex++];
        }
    }

    // 遍历完,肯定只有一边的数组剩余,没加到临时数组里,加上
    while (leftIndex <= mid) {
        tempArr[tempIndex++] = arr[leftIndex++];
    }
    while (rightIndex <= right) {
        tempArr[tempIndex++] = arr[rightIndex++];
    }
    // 最后遍历设置到原数组里
    tempIndex = 0;
    for (int i = left;i<=right;i++) {
        arr[i] = tempArr[tempIndex++];
    }
}

基数排序

将整数按位数切割成不同的数字,然后按每个位数分别比较。
具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值