IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 浅析八大排序算法 -> 正文阅读

[数据结构与算法]浅析八大排序算法

一.希尔排序

  • 主要思想通过在每一轮设置索引的增量间隔(随轮数增加不断有规律地缩小)来对每一组间隔的两个元素进行比较排序,直到增量间隔为1。
  • 步骤概述:
    1.设置增量间隔gap的for循环;
    2.设置处理每一组gap间隔的元素的for循环;
    3.通过移位的方式比较排序这两个gap间隔的元素。
  • 模板代码
public static void shellSort(int arr[]){
        for(int gap = arr.length / 2;gap > 0;gap /= 2){
            for(int i = gap; i < arr.length;i++){
                int j = i;
                int temp = arr[j];
                if(arr[j] < arr[j - gap]){
                    while (j - gap >= 0 && arr[j] < arr[j - gap]){
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    arr[j] = temp;
                }
            }
        }
    }
  • 示例理解
    在这里插入图片描述

二.插入排序(从小到大为例)

  • 主要思想把一组元素看成两组,左边为已经有序的,右边为待排序的,然后通过依次让右边的每一个元素与左边从右到左的有序元素作比较,直到找到那个比这个右边的元素大的元素的位置,即为插入位置。

  • 步骤说明
    1.把0索引元素作为左边的有序组,从1索引开始遍历右边待排序组的for循环;
    2.for循环中注意暂存当前的那个右边组的元素值;
    3.while循环条件,如果右边的元素要插入左边,先从左边组的最后一个元素开始比较,由于是从小到大排序,而且左边组已经是有序的,即这个元素在左边组最大。既然想插入到左边组,要么比这个左边元素小,一直找,直到找到插入位置;要么比这个左边元素大,不动。
    4.由于while的递减操作会最终多减去1,则要在该循环外的插入位置索引加1再插入暂存的元素值。

  • 示例图解
    在这里插入图片描述

  • 代码模板

 public static void insertSort(int arr[]){
        for(int i = 1;i < arr.length;i++){
            int inserVal = arr[i];
            int inserIndex = i - 1;
            while(inserIndex >= 0 && arr[inserIndex] > inserVal){
                arr[inserIndex + 1] = arr[inserIndex];
                inserIndex--;
            }
            arr[inserIndex + 1] = inserVal;
        }
    }

三.堆排序(以大顶堆为例)

  • 基础知识回顾
    1.堆:即完全二叉树,可由数组按编号存储。左子节点对应2i+1节点,右子节点为2i+2,父节点为(i-1)/2。例如数组[5, 1, 7, 2, 8, 6, 3, 9, 4],其堆图如下:
    在这里插入图片描述
    2.大顶堆:指每个根节点都比其子节点大,即第一个根节点就是最大的完全二叉树(每一层从左往右编号不中断的二叉树)。大顶堆图示如下:
    在这里插入图片描述
  • 主要思想
    1.大顶堆调节方法:先暂存指定的非叶子节点,然后让该节点与其子节点进行比较并排序,使其构成大顶堆结构。当左右的任一个子节点被与这个父节点交换,则以这个被交换下来的父节点为新的父节点,继续变成大顶堆的操作,最终形成以最初的暂存非叶子节点位置为父节点的局部大顶堆结构。
    2.堆排序方法:基于大顶堆的结构,知道根节点是最大值,那就可以不断缩小遍历范围,让每次缩小范围内的最后一个节点与当前根节点进行交换,即让每次缩小范围的最后一个节点都变成最大值,并对每次缩小范围内的所有节点,从根节点开始,再次重构成大顶堆结构。
  • 代码模板
    1.调节大顶堆:
public static void adjustHeap(int[] arr,int i,int length){
        int temp = arr[i];
        for(int index = i * 2 + 1;index < length;index = index * 2 + 1){
            if(index + 1 < length && arr[index] < arr[index + 1]){
                index++;
            }
            if(arr[index] > temp){
                arr[i] = arr[index];
                i = index;
            }
        }
        arr[i] = temp;
    }

2.堆排序:

public static void heapSort(int[] array){
        int temp;
        for(int i = array.length/2 - 1;i >= 0;i--){
            adjustHeap(array,i,array.length);
        }
        for(int j = array.length - 1;j > 0;j--){//每次减一说明最大值已在最后排好序,接下来不需动它,只对剩余元素进行操作。
            temp = array[j];
            array[j] = array[0];
            array[0] = temp;
            adjustHeap(array,0,j);
        }//可以发现j既表示了每次缩小范围的尾节点索引,也表示了缩小到范围区间。
    }

四.基数排序

  • 主要思想:准备10个桶,每个桶是一个一维数组。首先比较待排序数组的每一个元素的个位数与这10个桶中的哪一个桶的索引对应相等,然后将这个元素放入该桶中,遍历完所有元素后,又将放入了桶中的元素按桶索引依次放回原来的数组。之后依次每一轮比较的是元素的十位,百位…,重复以上操作。(对于位数不够的补0,例如数组[4,10],当比较十位数时,4就看成04,即其十位数为0)。
  • 图解示例第一轮(数组[53,3,542,748,14,214]):
    在这里插入图片描述
  • 代码模板:
    public static void radisSort(int[] arr){
        int max = arr[0];
        for(int i = 0;i < arr.length;i++){
            if(arr[i] > max){
                max = arr[i];
            }
        }//得到最大值max
        //得到最大值位数
        int maxLength = (max + "").length();
        //初始化10个桶,考虑到全部元素可能会放入一个桶的极端情况,故每个桶长度为全部元素个数,即数组长度。
        int[][] bucket = new int[10][arr.length];
        //初始化一个数组来对应记录每个桶中的元素个数。
        int[] bucketElementCounts = new int[10];

        for(int i = 0, n = 1;i < maxLength;i++, n *= 10){//这第一层for是表示从个位开始依次轮每一位数
            for(int j = 0;j < arr.length;j++){
            //这第二层for表示放入桶中
                int digitOfElement = arr[j] / n % 10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            int index = 0;
            for(int k = 0;k < bucketElementCounts.length;k++){
            //这第三层for表示放回数组
                if(bucketElementCounts[k] != 0){
                    for(int l = 0;l < bucketElementCounts[k];l++){
                        arr[index++] = bucket[k][l];
                    }
                }
                //此处表示放回数组后,对应桶中的元素个数要清空
                bucketElementCounts[k] = 0;
            }
        }
    }

五.选择排序(升序为例)

  • 主要思想:首先将数组中一个元素自定义为最小的元素(一般从左到右选,故第一次选的就是首元素),然后让该元素与剩余元素进行比较,遇到比该元素小的就更换一开始选定的最小元素为这个较小的元素,一轮比较完所有元素后,就将这个最小元素与首元素交换。之后,依次选定剩余的元素为最小元素,每一轮进行同样的比较操作并与选定首元素进行交换。
  • 图解示例及说明:
    在这里插入图片描述
    1.第一趟排序:选定第一个元素7为最小元素,将其与剩余元素进行比较完后,知剩余元素中最小元素为-2,则交换第一个元素7(主要思想提到的选定的首元素)与最小元素-2。
    2.第二趟排序:选定第二个元素4为最小元素,将其与剩余元素进行比较完后,知剩余元素中最小元素为4,此时的最小元素就是选定的元素,则不进行交换操作。
    余下每趟排序分析同理,不赘述。
  • 代码模板:
public static void chooseSort(int[] arr){
        for(int i = 0;i < arr.length;i++){//依次选定每一个元素作为首元素
            int minIndex = i;
            int min = arr[i];
            for(int j = i + 1;j < arr.length;j++) {//让选定首元素与后面的元素比较,从而确定真正的最小元素
                if (min > arr[j]) {
                    min = arr[j];
                    minIndex = j;
                }
            }
            if(minIndex != i){//交换选定首元素和最小元素
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

六.归并排序(以升序为例)

  • 主要思想:分而治之
    1.分:将所有元素不断按对半拆分成多组,直到分成每一个单元素为一组。
    2.治:分的逆操作,即合并。同时,在不断合并每一组元素时,每合并一次都对合并的元素进行排序操作。
  • 图解示例及说明:
    在这里插入图片描述
    在这里插入图片描述
    图二分析如下(最后一次合并):
    1.合并之后,分成左右两半看待,由于左边的4大于右边的1,则将较小的1放入暂存数组,然后右边索引右移。(可以理解为那个被放到暂存数组中的元素,被从原数组中取出,则要考虑下一个元素,即移动的地方是被放入暂存数组中的元素的位置)。
    2.第一步是右边移动的情况,这第二步是左边移动的情况。由于左边的4小于右边的6,故分析同第一步,较小的4放入暂存数组,左边索引右移。
    3.考虑到可能左边或右边部分会出现剩余未比较的元素,则直接将全部按顺序放入暂存数组。
    注意点:左半部分和右半部分都是已经有序的,因为合并时,从两个单元素开始合并,它们各自本身就是一个有序数组,接着按同样的规律操作而形成的更长的左右半数组也就是有序的。
  • 代码模板:
public static void merge(int[] arr,int left,int right,int mid,int[] temp){
        int t = 0;//暂存数组的索引
        int i = left;//左半部的索引
        int j = mid + 1;//右半部的索引
        while (i <= mid && j <= right){//左右半比较并存放到暂存数组
            if(arr[i] < arr[j]){
                temp[t] = arr[i];
                t++;
                i++;
            }else{
                temp[t] = arr[j];
                t++;
                j++;
            }
        }
        while(i <= mid){//将左半部剩余元素放到暂存数组
            temp[t] = arr[i];
            t++;
            i++;
        }
        while(j <= right){//将右半部剩余元素放到暂存数组
            temp[t] = arr[j];
            t++;
            j++;
        }

        t = 0;
        int tempLeft = left;
        while (tempLeft <= right){//将暂存数组元素拷贝到原数组
            arr[tempLeft] = temp[t];
            tempLeft++;
            t++;
        }
    }

    public static void mergeSort(int[] arr,int left,int right,int[] temp){
        if(left < right){
            int mid = (left + right) / 2;
            mergeSort(arr,left,mid,temp);//对左半部拆分
            mergeSort(arr,mid + 1,right,temp);//对右半部拆分
            merge(arr,left,right,mid,temp);//合并
        }
    }

七.快速排序(以升序为例)

  • 主要思想:首先选定一个基准元素a,然后将这个元素前面的元素依次与它进行比较,直到找到比它大的元素b。再将这个元素后面的元素依次与它进行比较,直到找到比它小的元素c。此时,将b与c交换位置。
  • 图解示例及说明:
    在这里插入图片描述
    以首元素6为基准值,从尾元素开始向左遍历并比较。
    在这里插入图片描述
    j向左遍历到比6小的5就停住,i向右遍历到比6大的7就停住,5和7交换位置。此时序列为6 1 2 5 9 3 4 7 10 8。
    同理,之后要交换左边的9和右边的4,然后i和j遍历到相同位置3处,此时第一轮遍历完成,将基准值6与3交换位置,即6已经到达正确位置。
    在这里插入图片描述
    然后在6的左边序列和右边序列分别选定基准值,再进行同样遍历比较操作。
    在这里插入图片描述
  • 代码模板:
public static void quickSort(int[] arr,int left,int right){
        int l = left;
        int r = right;
        int temp;
        int pivot = arr[(left + right)/2];//基准值
        while(l < r){
            while (arr[l] < pivot){//找基准值左边比它大的元素
                l++;
            }
            while(arr[r] > pivot){//找基准值右边比它小的元素
                r--;
            }
            if(l >= r){
                break;
            }
            temp = arr[l];//交换基准值左右两边的元素
            arr[l] = arr[r];
            arr[r] = temp;

            if(arr[l] == pivot){//保证pivot处用[l,r]这个闭区间内,防止陷入死循环
                r--;
            }
            if(arr[r] == pivot){//保证pivot处用[l,r]这个闭区间内,防止陷入死循环
                l++;
            }
        }

        if(r == l){
            r--;
            l++;
        }
        if(r > left){
            quickSort(arr,left,r);
        }
        if(l < right){
            quickSort(arr,l,right);
        }

    }

理解死循环加一和减一:基准值是要始终保持在[r,l]这个闭区间的,这一点应该能理解,因为交换的都是它左右两边的值,r和l最多也就刚好处在基准值的位置上。如果arr[r]等于pivot,然后通过r++这样移动来突破死循环,会使pivot处用[r,l]之外,同理,在arr[l]等于pivot,l–也一样,所以这样不对,应该是代码模板中的样子。

八.冒泡排序(以升序为例)

  • 主要思想:按下标顺序依次让待排序序列的每一个元素与其相邻元素(下一个)比较,若发现逆序则交换。
  • 图解示例
    在这里插入图片描述
  • 代码模板:
public static void bubbleSort(int[] arr){
        int temp;
        boolean flag = false;
        for(int i = 0;i < arr.length - 1;i++){
            for(int j = 0;j < arr.length - 1 - i;j++{//多间了一个i,说明后面的较大值已经排好,不需要再排了。
                if(arr[j] > arr[j + 1]){
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            if(!flag){//一趟排序中没有发生过一次交换
                break;
            }else{
                flag = true;//为下次判断重置flag
            }
        }
    }

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-07 21:48:54  更:2021-08-07 21:49:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 2:26:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计