大家好我是苏麟 , 今天带来番外篇 .
十大排序算法 , 你值得拥有 .
1.冒泡排序 BubbleSort
最基本的排序算法,最常用的排序算法 .
我们以关键字序列{26,53,48,11,13,48,32,15}看一下排序过程:
动画演示 :
代码如下 : (基础版)
class Solution {
public int[] sortArray(int[] nums) {
for(int i = 0;i < nums.length - 1;i++){
for(int j = 0;j < nums.length - i - 1;j++){
if(nums[j] > nums[j + 1]){
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
return nums;
}
}
优化 :
class Solution {
public int[] sortArray(int[] nums) {
int flag = 1;
for(int i = 0;flag && i < nums.length - 1;i++){
flag = 0;
for(int j = 0;j < nums.length - i - 1;j++){
if(nums[j] > nums[j + 1]){
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = 1;
}
}
}
return nums;
}
}
空间复杂度 仅仅使用一个辅助单元 ,因此空间复杂度为O(1)。
时间复杂度 假设待排序的元素个数为n,则总共需要进行n-1趟排序,对 j 个元素的子序列进行一趟排序需要进行j-1次关键字比较,因此总的比较次数为n(n-1)/2,因此时间复杂度为O(n^2)。
稳定性 冒泡排序的特点是稳定性好,因为排序过程中始终只交换相邻元素,比较对象大小相等时不交换,相对位置不变,故稳定。
2.选择排序 SelectSort
选择排序是默认前面都是已经排序好的,然后从后面 选择最小的放在前面排序好的的后面,首先第一轮循环的时候默认的排序好的为空,然后从后面选择最小的放到数组的第一个位置,第二轮循环的时候默认第个元素是已经 排序好的,然后从剩下的找出最小的放到数组的第二个位置,第三轮循环的时候默认前 两个都是已经排序好的,然后再从剩下的选择一个最小的放到数组的第三个位置,以此 类推。还是上面的序列,我们看一下选择排序是怎么做的:
动画演示 :
代码 :
public static void SelectSort(int[] nums){
int min;
for (int i = 0;i < nums.length;i++){
min = i;
for (int j = i + 1;j < nums.length;j++){
if (nums[i] > nums[j]){
min = j;
}
}
if (i != min){
int temp = nums[i];
nums[i] = nums[min];
nums[min] = temp;
}
}
}
空间复杂度:仅仅使用一个辅助单元 ,因此空间复杂度为O(1)
平均时间复杂度: 在待排序序列已经有序的情况下,简单选择排序不用移动元素。最坏情况下,也就是序列正好是逆序的,则要进行n(n-1)/2次比较,因此最坏时间复杂度为O(n^2).
稳定性:选择排序是不稳定算法
3.插入排序 InsertionSort
插入排序的原理是默认前面的元素都是已经排序好的,然后从后面逐个读取插入到前面排序好的合适的位置,就相当于打扑克的时候每获取一张牌的时候就插入到合适的位置一样。
以{26,53,48,11,13,48,32,15)为例,插入排序的过程如下所示:
动态演示 :
代码 :
public static void main(String[] args) {
int[] arr = {10, 78, 65, 32, 21, 89, 13, 54, 7, 3};
insertSort(arr);
}
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int j = i;
int temp = arr[i];
for (; j > 0; j--){
if (arr[j - 1] > temp){
arr[j] = arr[j - 1];
}else {
break;
}
}
arr[j] = temp;
}
//打印
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
时间复杂度 : O(n^2).
4.希尔排序 ShellSort
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
- 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
以{26,53,48,11,13,48,32,15)为例,执行过程!
分析 :
这里给个视频 : 基础算法-206-排序算法-希尔排序_哔哩哔哩_bilibili
案例代码 :
public static void main(String[] args) {
int[] arr = {10, 78, 65, 32, 21, 89, 13, 54, 7, 3};
shellSort(arr);
}
public static void shellSort(int[] arr) {
for (int k = arr.length >> 1; k >= 1; k = k >> 1) {
for (int i = k; i < arr.length; i++) {
int temp = arr[i];
int change = i - k;
while (change >= 0 && temp < arr[change]) {
arr[change + k] = arr[change];
change -= k;
}
if (change != i - k) {
arr[change + k] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
代码 :
public static void shellSort(int[] arr) {
for (int k = arr.length >> 1; k >= 1; k = k >> 1) {
for (int i = k; i < arr.length; i++) {
int temp = arr[i];
int change = i - k;
while (change >= 0 && temp < arr[change]) {
arr[change + k] = arr[change];
change -= k;
}
if (change != i - k) {
arr[change + k] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
稳定性 : 非常稳定
7.堆排序 Heapsort
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
- 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
- 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
堆排序的平均时间复杂度为 Ο(nlogn)。
动态演示 :
分析 :
这里给个视频理解堆排序 :选择排序_堆排序_1什么是堆排序_哔哩哔哩_bilibili
案例代码 :
public static void main(String[] args) {
int[] arr = {10, 78, 65, 32, 21, 89, 13, 54, 7, 3};
HeapSort(arr);
}
//堆排序
public static void HeapSort(int[] nums) {
int[] arr = Arrays.copyOf(nums, nums.length);
int len = arr.length;
//构建堆
buildMaxHeap(arr, len);
for (int i = len - 1; i > 0; i--) {
swap(arr,0,i);
len--;
heapify(arr,0,len);
}
System.out.println(Arrays.toString(arr));
}
//构建堆
public static void buildMaxHeap(int[] arr, int len) {
for (int i = len >> 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 largest = i;
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest, len);
}
}
//交换
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
代码 :
//堆排序
public static void HeapSort(int[] nums) {
int[] arr = Arrays.copyOf(nums, nums.length);
int len = arr.length;
//构建堆
buildMaxHeap(arr, len);
for (int i = len - 1; i > 0; i--) {
swap(arr,0,i);
len--;
heapify(arr,0,len);
}
System.out.println(Arrays.toString(arr));
}
//构建堆
public static void buildMaxHeap(int[] arr, int len) {
for (int i = len >> 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 largest = i;
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest, len);
}
}
//交换
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
8.计数排序
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
动画演示 :
分析 :
这里也有视频 : 基础算法-215-排序算法-计数排序-改进_哔哩哔哩_bilibili
案例代码 : (此代码有些瑕疵 , 不能处理负数)
public static void main(String[] args) {
//int[] arr = {10, 78, 65, 32, 21, 89, 13, 54, 7, 3};
//sort(arr);
int[] brr = {0, 1,1, 7, 8, 9, 10};
//Tsort(brr, 3);
CountingSort(brr);
}
public static void CountingSort(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max)
max = arr[i];
}
int[] count = new int[max + 1];
for (int i = 0; i < arr.length; i++) {
count[arr[i]]++;
}
//合并
int n = 0;
for (int i = 0; i < max; i++) {
while (count[i] != 0) {
arr[n++] = i;
count[i]--;
}
}
//打印
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
代码 :
public static void CountingSorts(int[] arr) {
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max)
max = arr[i];
else if (arr[i] < min)
min = arr[i];
}
int[] count = new int[max - min + 1];
for (int i = 0; i < arr.length; i++) {
count[arr[i] - min]++;
}
//合并
int n = 0;
for (int i = 0; i < count.length; i++) {
while (count[i] != 0) {
arr[n++] = min + i;
count[i]--;
}
}
//打印
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
时间复杂度 : 2N + k
题目 :
LeetCode 912. 排序数组
此题用计数排序可达到最优解 , 大家可以试试 .
9.桶排序 BucketSort
桶排序是将数组分散到有限的桶中,然后每个桶再分别排序,而每个桶的排序又可以使用其他排序方式进行排序,可以是桶排序也可以是其他排序。一句话就是: 划分多个范围相同的区间,每个子区间自排序最后合并。
桶的大小可以随便定,其实我们完全可以把桶固定在一 个数量,根据数组的大小来确定,也可以自己定,比如3个或者5个7个等,桶的大小确 定之后,下步就需要把数组中的值一一存放到桶里,小的值就会放到前面的桶里,大 的值就会放到后面的桶里,中间的值就会放到中间的桶里,然后再分别对每个桶进行单 独排序,最后再把所有桶的数据都合并到一起就会得到排序好的数组。
例子 : 一个数组{29,25,21,3,9,49,37,43}
元素分布在桶中:
元素在每个桶中排序:
视频解析 :
这里给个视频 : 基础算法-217-排序算法-桶排序-改进_哔哩哔哩_bilibili
案例代码 :
public static void main(String[] args) {
//分10个桶
int[] arr = {10, 78, 65, 32, 21, 89, 13, 54, 7, 3};
sort(arr);
}
public static void sort(int[] arr) {
List<List<Integer>> list = new ArrayList<>(10);
//创建每个桶
for (int i = 0; i < 10; i++) {
list.add(new ArrayList<>());
}
//往桶里添加元素
for (int i = 0; i < arr.length; i++) {
list.get(arr[i] / 10).add(arr[i]);
}
int n = 0;
for (int i = 0; i < 10; i++) {
//排序
List<Integer> integers = list.get(i).stream().sorted().collect(Collectors.toList());
for (Integer integer : integers) {
arr[n++] = integer;
}
}
//打印
for (int i= 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
}
代码 :
public static void main(String[] args) {
//int[] arr = {10, 78, 65, 32, 21, 89, 13, 54, 7, 3};
//sort(arr);
int[] brr = {0, 1, 7, 8, 9, 10};
Tsort(brr,3);
}
//range桶中存放的元素个数
public static void Tsort(int[] arr, int range) {
//最大值 最小值
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max)
max = arr[i];
else if (arr[i] < min)
min = arr[i];
}
//创建桶的个数
int count = (max - min) / range + 1;
List<List<Integer>> list = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
list.add(new ArrayList<>());
}
//存放数据
for (int i = 0; i < arr.length; i++) {
list.get((arr[i] - min) / range).add(arr[i]);
}
//存到数组中
int n = 0;
for (int i = 0; i < count; i++) {
List<Integer> integers = list.get(i).stream().sorted().collect(Collectors.toList());
for (Integer integer : integers) {
arr[n++] = integer;
}
}
//打印
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
10.基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数 .
动态演示 :
分析 :
这里给个视频 : 基础算法-219-排序算法-基数排序2_哔哩哔哩_bilibili
案例代码 :
public static void main(String[] args) {
String[] s = new String[]{"123", "125", "151", "167", "142", "152"};
//字符串数组 , 字符串长度
RadixSort(s, 3);
}
public static void RadixSort(String[] arr, int length) {
List<List<String>> list = new ArrayList<>(10);
//创建桶
for (int i = 0; i < 10; i++) {
list.add(new ArrayList<>());
}
for (int j = length - 1; j >= 0; j--) {
//根据位添加到桶里
for (String s : arr) {
list.get(s.charAt(j) - '0').add(s);
}
//重新排序
int n = 0;
for (List<String> strings : list) {
for (String string : strings) {
arr[n++] = string;
}
strings.clear();
}
System.out.println(Arrays.toString(arr));
}
}
代码 : (处理数字)
public static void RadixSort(String[] arr, int length) {
List<List<String>> list = new ArrayList<>(10);
//创建桶
for (int i = 0; i < 10; i++) {
list.add(new ArrayList<>());
}
for (int j = length - 1; j >= 0; j--) {
//根据位添加到桶里
for (String s : arr) {
list.get(s.charAt(j) - '0').add(s);
}
//重新排序
int n = 0;
for (List<String> strings : list) {
for (String string : strings) {
arr[n++] = string;
}
strings.clear();
}
}
代码 : (处理多种字符)
public static void RadixSort(String[] arr, int length) {
List<List<String>> list = new ArrayList<>(128);
//创建桶
for (int i = 0; i < 128; i++) {
list.add(new ArrayList<>());
}
for (int j = length - 1; j >= 0; j--) {
//根据位添加到桶里
for (String s : arr) {
list.get(s.charAt(j)).add(s);
}
//重新排序
int n = 0;
for (List<String> strings : list) {
for (String string : strings) {
arr[n++] = string;
}
strings.clear();
}
}
这期就到这里 , 下期见!