首页 > 编程语言 >八大排序算法

八大排序算法

时间:2023-03-11 15:44:40浏览次数:38  
标签:arr 八大 int length value 算法 array 排序

概述

排序算法可以分为内部排序和外部排序。
内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。

image.png
image.png

一、冒泡排序

时间复杂度 O(n²)
步骤:
比较相邻的两个元素,如果第一个比第二个大,则交换位置。
重复步骤,当数组有array.length个元素时,会循环array.length-1次。

public static int[] bubbleSort(int[] array) {
        // 外层循环,最大排序 length-1 次
        for (int i = 0; i < array.length - 1; i++) {
            // 定义flag判断数组是否排序好,优化性能
            boolean flag = true;
            // 内层循环,如果第一个数大于第二个数,则交换位置。
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j + 1] < array[j]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag = false;
                }
            }
            // flag为true,则表明数组已排序好
            if (flag){
                break;
            }
        }
        return array;
    }

二、选择排序

时间复杂度O(n²)
步骤:
先在未排序序列中找到最小(大)的元素,存放到序列起始位置。
再从剩余序列中找最小(大)元素,排到已排序的序列末尾。循环往复。

public static int[] selectionSort(int[] array) {
    // 总共经过 N-1 次比较
    for (int i = 0; i < array.length - 1; i++) {
        int min = i;
        // 每次需要比较 N-1 次
        for (int j = i + 1; j < array.length; j++) {
            if (array[j] < array[min]) {
                // 找出本次循环中最小值元素的下标
                min = j;
            }
        }
        if (min != i) {
            int temp = array[i];
            array[i] = array[min];
            array[min] = temp;
        }
    }
    return array;
}

三、插入排序

时间复杂度O(n²)
步骤:
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

public static int[] insertionSort(int[] arr){
        for (int i = 0; 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;
            }
        }
        return arr;
    }

四、希尔排序

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
时间复杂度O(nlogn)
步骤:
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

public static void shellSort(int[] arr){
        int length = arr.length;
        int temp;
        for (int step = arr.length / 2; step > 0; step /= 2) {
            //对一个步长区间进行比较 [step,arr.length)
            for (int i = step; i < arr.length; i++) {
                // 记录要插入的数据
                int value = arr[i];
                int j;
                //对步长区间中具体的元素进行比较
                for (j = i - step; j >= 0 && arr[j] > value; j -= step) {
                    //j为左区间的取值,j+step为右区间与左区间的对应值。
                    arr[j + step] = arr[j];
                }
                //此时step为一个负数,[j + step]为左区间上的初始交换值
                arr[j + step] = value;
            }
        }
    }

五、归并排序

时间复杂度O(nlogn)
步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针达到序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。
public static void mergeSort(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }

        sort(array, 0, array.length - 1);
    }

    private static void sort(int[] array, int left, int right) {
        if (left == right) {
            return;
        }
        // 防止越界,不使用 (right + left) / 2
        int mid = left + ((right - left) >> 1);
        // 对左侧子序列进行递归排序
        sort(array, left, mid);
        // 对右侧子序列进行递归排序
        sort(array, mid + 1, right);
        // 合并
        merge(array, left, mid, right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        int[] temp = new int[right - left + 1];
        int i = 0;
        int p1 = left;
        int p2 = mid + 1;
        // 比较左右两部分的元素,哪个小,把那个元素填入temp中
        while (p1 <= mid && p2 <= right) {
            temp[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
        }
        // 上面的循环退出后,把剩余的元素依次填入到temp中
        // 以下两个while只有一个会执行
        while (p1 <= mid) {
            temp[i++] = array[p1++];
        }
        while (p2 <= right) {
            temp[i++] = array[p2++];
        }
        // 把最终的排序的结果复制给原数组
        for (i = 0; i < temp.length; i++) {
            array[left + i] = temp[i];
        }
    }

六、快速排序

时间复杂度O(nlogn)
步骤:

  1. 从数列中挑出一个元素,称为 "基准"(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
private static int[] quickSort(int[] arr, int left, int right) {
        if (left < right) {
            int partitionIndex = partition(arr, left, right);
            quickSort(arr, left, partitionIndex - 1);
            quickSort(arr, partitionIndex + 1, right);
        }
        return arr;
    }

    private static int partition(int[] arr, int left, int right) {
        // 设定基准值(pivot)
        int pivot = left;
        int index = pivot + 1;
        for (int i = index; i <= right; i++) {
            if (arr[i] < arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }
        swap(arr, pivot, index - 1);
        return index - 1;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

七、堆排序

时间复杂度为 Ο(nlogn)
步骤:

  1. 创建一个堆 H[0……n-1]
  2. 把堆首(最大值)和堆尾互换
  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置
  4. 重复步骤 2,直到堆的尺寸为 1

八、计数排序

时间复杂度O(n*k)
步骤:

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
private static int[] countingSort(int[] arr, int maxValue) {
        int bucketLen = maxValue + 1;
        int[] bucket = new int[bucketLen];

        for (int value : arr) {
            bucket[value]++;
        }

        int sortedIndex = 0;
        for (int j = 0; j < bucketLen; j++) {
            while (bucket[j] > 0) {
                arr[sortedIndex++] = j;
                bucket[j]--;
            }
        }
        return arr;
    }
// 获取最大值
private static int getMaxValue(int[] arr) {
    int maxValue = arr[0];
    for (int value : arr) {
        if (maxValue < value) {
            maxValue = value;
        }
    }
    return maxValue;
}

1、桶排序

时间复杂度O(n*k)
桶排序是计数排序的升级版。

  1. 在额外空间充足的情况下,尽量增大桶的数量
  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
  • 什么时候最快?
    • 当输入的数据可以均匀的分配到每一个桶中。
  • 什么时候最慢?
    • 当输入的数据被分配到了同一个桶中。
private static int[] bucketSort(int[] arr, int bucketSize) throws Exception {
        if (arr.length == 0) {
            return arr;
        }

        int minValue = arr[0];
        int maxValue = arr[0];
        for (int value : arr) {
            if (value < minValue) {
                minValue = value;
            } else if (value > maxValue) {
                maxValue = value;
            }
        }

        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) / bucketSize);
            buckets[index] = arrAppend(buckets[index], arr[i]);
        }

        int arrIndex = 0;
        for (int[] bucket : buckets) {
            if (bucket.length <= 0) {
                continue;
            }
            // 对每个桶进行排序,这里使用了插入排序
            bucket = insertSort.sort(bucket);
            for (int value : bucket) {
                arr[arrIndex++] = value;
            }
        }

        return arr;
    }
    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private static int[] arrAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }

2、基数排序

时间复杂度O(n*k)

基数排序 vs 计数排序 vs 桶排序

基数排序有两种方法:
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:

  • 基数排序:根据键值的每位数字来分配桶;
  • 计数排序:每个桶只存储单一键值;
  • 桶排序:每个桶存储一定范围的数值
/**
     * 获取最高位数
     */
    private int getMaxDigit(int[] arr) {
        int maxValue = getMaxValue(arr);
        return getNumLenght(maxValue);
    }

    private int getMaxValue(int[] arr) {
        int maxValue = arr[0];
        for (int value : arr) {
            if (maxValue < value) {
                maxValue = value;
            }
        }
        return maxValue;
    }

    protected int getNumLenght(long num) {
        if (num == 0) {
            return 1;
        }
        int lenght = 0;
        for (long temp = num; temp != 0; temp /= 10) {
            lenght++;
        }
        return lenght;
    }

    private int[] radixSort(int[] arr, int maxDigit) {
        int mod = 10;
        int dev = 1;

        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
            // 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)
            int[][] counter = new int[mod * 2][0];

            for (int j = 0; j < arr.length; j++) {
                int bucket = ((arr[j] % mod) / dev) + mod;
                counter[bucket] = arrayAppend(counter[bucket], arr[j]);
            }

            int pos = 0;
            for (int[] bucket : counter) {
                for (int value : bucket) {
                    arr[pos++] = value;
                }
            }
        }

        return arr;
    }

    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private int[] arrayAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;

标签:arr,八大,int,length,value,算法,array,排序
From: https://www.cnblogs.com/SunIcecream/p/17206180.html

相关文章

  • 卡尔曼滤波算法综述(KF、EKF、UKF和IMM)
    本篇博文是对之前学习的书籍《卡尔曼滤波原理及应用--------MATLAB仿真》里面的卡尔曼滤波知识做一个回顾,里面不会包含具体的公式推导,只是对里面的几种算法做一个综述,......
  • 冒泡排序
    原理第一个元素如果大于第二个元素比较,则他们位置调换。假设有6个元素,需要经过6*6=36次循环。 代码/***升序**@paramnumArr*@ret......
  • 最短路径算法
    原理1.算出目前数据中,起点到终点的最短路径2.路径从短到长获取目前最短的路径,设置标识,有标识的不参与下一步循环 packagecom.jason.base.arithmetic;importlom......
  • 【质因数分解算法详解】C/Java/Go/Python/JS/Dart/Swift/Rust等不同语言实现
    关于质因数分解算法的不同语言实现,通过实例来看不同语言的差异什么是质因数算法?即任意一个合数可以分解为多个质数相乘。例如:20=2*2*545=3*3*5210=2*......
  • 《渗透测试》算法分析&传输加密&数据格式&密文存储&代码混淆&逆向保护 2023 day8
           1数据在传输的时候为什么要进行编码   安全测试的时候往往会对url等地方进行修改、构造数据。数据传输的时候被编码的话,如果不按照对应编码......
  • 【LeetCode回溯算法#07】子集问题I+II,巩固解题模板并详解回溯算法中的去重问题
    子集力扣题目链接给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可以按任意顺序返回解集。示例1:输......
  • 机器学习算法之有监督学习和无监督学习的区别
    如今机器学习和人工智能是大家耳熟能详的两个词汇,在我们日常生活中也是被高频的提到。其实机器学习只是人工智能的一部分,是人工智能的一个子集,它往往是通过示例和经验模型......
  • itil运维八大流程
    ITIL(ITInfrastructureLibrary)是CCTA(英国国家计算机和电信局)于20世纪80年代末开发的一套IT服务管理标准库,它把英国各个行业在IT管理方面的最佳实践归纳起来变成规范,旨......
  • HJ14 字符串排序
    描述给定n个字符串,请对n个字符串按照字典序排列。 数据范围: 1\len\le1000\1≤n≤1000  ,字符串长度满足 1\lelen\le100\1≤len≤100 输入描述:......
  • 快速排序——C语言描述
    快速排序——C语言描述目录快速排序——C语言描述0测试用例框架1定义2代码4测试用例0测试用例框架https://blog.csdn.net/m0_59469991/article/details/127137119?......