数据结构之常见排序算法

2023-11-06

1.排序概念

排序:就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假设一组序列中,有两个相同的元素,在排序之后,两个相同元素的前后顺序颠倒了,说明这个排序算法是不稳定的,反之。

在这里插入图片描述

2.10种排序比较

在这里插入图片描述
在这里插入图片描述

3.排序算法

3.1直接插入排序(元素越有序,越高效)

思路:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到
一个新的有序序列 。

在这里插入图片描述

 /*
    * 时间复杂度:最坏:o(N^2)
    *            最好:O(N)   数据都有序,j不会往前走,直接break
    *               当数据趋于有序的时候,排序速度非常快
    *               一般场景就是数据基本有序,建议使用直接插入排序
    * 空间复杂度:O(1)
    * 稳定性:稳定
    *    如果一个排序是稳定的,那么就可以实现为不稳定的,
    *     但是如果一个排序本身就是不稳定的,那么不能变成稳定的
    *
    * */
 
    //插入排序
    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int temp = array[i];
            int j = i - 1;
            for (; j >= 0; j--) {
                if (array[j] > temp) {
                    array[j + 1] = array[j];
                } else {
                    break;
                }
            }
            array[j + 1] = temp;
        }

    }

3.2希尔排序序( 缩小增量排序 )

基本思想:希尔排序是选的一个增量值分组,然后对每组使用直接插入排序算法排序,随着增量值减小,每组包含的值也越来越多,当增量值减为1时,整个序列在一组内排序

在这里插入图片描述

上代码:

 //希尔排序   每组进行插入排序
   /*时间复杂度:O(N^1.3) - O(N ^ 1.5)
    *空间复杂度:o(1)
    * 稳定性:不稳定的排序
    **/
   public static void shellSort(int[] array) {
       int gap = array.length;
       while (gap > 1) {
           gap /= 2;
           shell(array, gap);
       }
   }

   //插入排序 ------->gap
   public static void shell(int[] array, int gap) {
       for (int i = gap; i < array.length; i++) {
           int temp = array[i];
           int j = i - gap;
           for (; j >= 0; j -= gap) {
               if (array[j] > temp) {
                   array[j + gap] = array[j];
               } else {
                   break;
               }
           }
           array[j + gap] = temp;
       }
   }

3.3直接选择排序

基本思想:在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
在这里插入图片描述

(1)第一种

/*选择排序
     *时间复杂度:O(N^2)
     * 空间复杂度:o(1)
     * 稳定性:不稳定
     *
     * */
    public static void selectSort(int[] arrays) {
        for (int i = 0; i < arrays.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arrays.length; j++) {
                if (arrays[j] < arrays[minIndex]) {
                    minIndex = j;
                }
            }
            swap(arrays, i, minIndex);
        }
    }
     private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

(2)前面是用了一个minIndex去找最小值然后交换,最后排序

那么如果用两个一个minIndex去找最小值,一个maxIndex去找最大值,然后用left指向前,right指向后,比如说是升序那就minIndex和left交换,maxIndex和right交换,这样效率应该是比第一种方法要快

在这里插入图片描述

3.4堆排序

基本思想:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

在这里插入图片描述

/*堆排序
     *时间复杂度:O(Nlogn)
     * 空间复杂度:O(1)
     * 稳定性:不稳定
     * */
    public static void heapSort(int[] array) {
        createBigHeap(array);
        int end = array.length - 1;
        while (end > 0) {
            swap(array, 0, end);
            shiftDown(array, 0, end);
            end--;
        }
    }

    private static void createBigHeap(int[] array) {
        for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
            shiftDown(array, parent, array.length);
        }
    }

    private static void shiftDown(int[] array, int parent, int len) {
        int child = parent * 2 + 1;
        //至少有左孩子
        while (child < len) {
            if (child + 1 < len && array[child] < array[child + 1]) {
                //有右孩子,且右孩子最大
                child++;
            }
            if (array[child] > array[parent]) {
                swap(array, child, parent);
                parent = child;
                child = 2 * parent + 1;

            } else {
                //child比parent小,不需要调整
                break;
            }
        }

    }

3.5冒泡排序

基本思想:冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。重复的进行上面的步骤,直到没有再需要交换的
在这里插入图片描述

 /*
     * 冒泡排序
     * 时间复杂度:不考虑优化 O(N^2),优化后,o(N)
     *空间复杂度o(1)
     * 稳定性:稳定
     * */
    public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            //考虑优化如果某一轮中没有发生交换,则证明已经有序,不必进行后面轮数
            boolean flg = false;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    swap(array, j, j + 1);
                    flg = true;
                }
            }
            if (flg == false) {
                return;
            }
        }
    }

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

3.6快速排序 递归实现(无序使用最好)

基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,
左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,
然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
在写递归的过程中可联想 二叉树的前序遍历 找基准值 进行区间划分

 //快速排序
    /*
     * 时间复杂度:N*logN
     *            最好情况:N*logN
     *            最坏情况:N^2   有序   倒序   (只有左树或者只有右树)
     * 空间复杂度:
     *            最好情况:logN
     *            最坏情况:N
     * 稳定性:不稳定
     *
     * */
    //1.递归实现快速排序
    public static void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }

    private static void quick(int[] array, int start, int end) {
        //递归结束条件
        //为什么取大于号  :1,2,3,4   越界
        if (start >= end) {
            return;
        }
        //基准
        int pivot = partition(array, start, end);//划分
        quick(array, start, pivot - 1);
        quick(array, pivot + 1, end);
    }
     //插入排序
    public static void insertSort2(int[] array, int start, int end) {
        for (int i = start + 1; i <= end; i++) {
            int temp = array[i];
            int j = i - 1;
            for (; j >= 0; j--) {
                if (array[j] > temp) {
                    array[j + 1] = array[j];
                } else {
                    break;
                }
            }
            array[j + 1] = temp;
        }

    }
 

3.6.1挖坑法 (建议用这个 找基准)

必须先走右边,再走左边,这样就不会出现将大的放在前面的情况
在这里插入图片描述

//基准  挖坑法
    private static int partition(int[] array, int left, int right) {
        int tmp = array[left];
        while (left < right) {
            //left<right   不然会越界
            //“ = ”必须取,不然会发生死循环:6  23 1  3 6
            while (left < right && array[right] >= tmp) {
                right--;
            }
            array[left] = array[right];
            while (left < right && array[left] <= tmp) {
                left++;
            }
            array[right] = array[left];
        }
        array[left] = tmp;
        return left;
    }

3.6.2Hoare法

在这里插入图片描述

//Hoare法
    private static int partition2(int[] array, int left, int right) {
        int tmp = array[left];
        int i = left;
        while (left < right) {
            //为什么要先从右边走,因为左边先停下来就会比tmp大,与i位置交换就会把比tmp大的换到前面
            while (left < right && array[right] >= tmp) {
                right--;
            }
            while (left < right && array[left] <= tmp) {
                left++;
            }
            swap(array, left, right);
        }
        swap(array, left, i);
        return left;
    }

3.6.3三数取中法 优化排序算法

在这里插入图片描述

在这里插入图片描述

 //1.递归实现快速排序
    public static void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }

    private static void quick(int[] array, int start, int end) {
        //递归结束条件
        //为什么取大于号  :1,2,3,4   越界
        if (start >= end) {
            return;
        }
        //优化2:解决减少递归的次数
        if (end - start + 1 <= 14) {
            //假设有十万条数据,还剩十几条,那么数据基本有序,用插入排序更高效
            insertSort2(array, start, end);
            return;
        }

        // 三数取中法优化1:不用每一次都用start,而是从start ,mid  ,end中取一个中间数来作为基准然后将它与start位置的数交换位置即可,
        // 后续不用改变
        int i = minThree(array, start, end);
        swap(array, i, start);
        //基准
        int pivot = partition2(array, start, end);//划分
        quick(array, start, pivot - 1);
        quick(array, pivot + 1, end);
    }
     //三数取中法
    private static int minThree(int[] array, int start, int end) {
        int mid = (start + end) / 2;
        if (start < end) {
            if (mid < start) {
                return start;
            } else if (mid > end) {
                return end;
            } else {
                return mid;
            }
        } else {
            if (mid < end) {
                return end;
            } else if (mid > start) {
                return start;
            } else {
                return mid;
            }
        }
    }

3.7 快速排序 非递归实现

第一个分析:
在这里插入图片描述
第二个分析:
在这里插入图片描述
上代码:

//2.非递归实现快速排序
    /*
     * 时间复杂度:O(Nlog n)
     * 空间复杂度:O(log n)
     * 稳定性:不稳定
     * */
    public static void quickSort2(int[] array) {
        Deque<Integer> stack = new LinkedList<>();
        int left = 0;
        int right = array.length - 1;
        int pivot = partition(array, left, right);
        //保证左边与右边都大于两个元素,才可以排序,只有一个元素就不用排序了
        if (pivot > left + 1) {
            stack.push(left);
            stack.push(pivot - 1);
        }
        if (pivot < right - 1) {
            stack.push(pivot + 1);
            stack.push(right);
        }
        while (!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            pivot = partition(array, left, right);
            //保证左边与右边都大于两个元素,才可以排序,只有一个元素就不用排序了
            if (pivot > left + 1) {
                stack.push(left);
                stack.push(pivot - 1);
            }
            if (pivot < right - 1) {
                stack.push(pivot + 1);
                stack.push(right);
            }
        }
    }

3.8归并排序

基本思想:归并排序是建立在归并操作上的一种有效的排序算法,将已有序的子序列合并,得到完全有序的序列;也就是先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

递归实现:
在这里插入图片描述


 //归并排序
    public static void mergeSort(int[] array) {
        mergeSortFunc(array, 0, array.length - 1);

    }

    private static void mergeSortFunc(int[] array, int start, int end) {
        //和快速排序原因一样
        if (start >= end) {
            return;
        }
        int mid = (end + start) / 2;
        //分
        mergeSortFunc(array, start, mid);
        mergeSortFunc(array, mid + 1, end);
        //并
        merge(array, start, end, mid);
    }

    private static void merge(int[] array, int start, int end, int mid) {
        int s1 = start;
        int s2 = mid + 1;
        //tmp数组下标
        int k = 0;
        //合并成一个临时数组
        int[] tmp = new int[end - start + 1];
        while (s1 <= mid && s2 <= end) {
            if (array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            } else {
                tmp[k++] = array[s2++];
            }
        }
        //s2数组为空时,s1还有数据
        while (s1 <= mid) {
            tmp[k++] = array[s1++];
        }
        //同样的
        while (s2 <= end) {
            tmp[k++] = array[s2++];
        }
        //将tmp临时数组数据给原数组
        for (int i = 0; i < tmp.length; i++) {
            //这里start+i意思是,不一定start都是0下标开始的
            array[i + start] = tmp[i];
        }
    }

非递归实现归并排序:

  //非递归实现归并排序
    public static void mergeSort2(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            //i += gap * 2,当前组拍序好后,i往后移动,排序下一组
            for (int i = 0; i < array.length; i += gap * 2) {
                int left = i;
                int mid = i + gap - 1;
                if (mid >= array.length) {避免越界异常
                    mid = array.length - 1;
                }
                int right = mid + gap;//
                if (right >= array.length) {//避免越界异常
                    right = array.length - 1;
                }
                merge(array, left, right, mid);
            }
            //间隙为1的每组排序好后,改为间隙为2的每组,重复上述步骤,接着排序下一组,直到gap
            //超过array.lengh,就排序好了
            gap *= 2;
        }

    }

3.9计数排序

在这里插入图片描述

 //计数排序
    /*
    * 时间复杂度:O(N+范围)
    * 空间复杂度:
    *
    * */
    public static void countSort(int[] array) {
        //1遍历数组,找到数组最大值和最小值
        int min = array[0];
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
            }
            if (array[i] > max) {
                max = array[i];
            }
        }
        //2确定计数数组的长度
        int len = max - min + 1;
        int[] count = new int[len];
        //3遍历array数组,在计数数组中记录数字出现的个数
        for (int i = 0; i < array.length; i++) {
            count[array[i] - min]++;
        }
        //根据计数数组,将数字按照数组顺序重新返回array数组
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                //这里i+min:在array中加上min,反映真实数据
                array[index] = i + min;
                index++;
                count[i]--;
            }
        }
    }

4.0基数排序

基本思想:基数排序是利用分配和收集两种基本操作。
基数排序是一种按记录关键字的各位值逐步进行排序的方法。
此种排序一般适用于记录的关键字为整数类型的情况。所有对于字符串和文字排序不适合。

在这里插入图片描述

  private static void radixSort(int[] arr) {
        //待排序列最大值
        int max = arr[0];
        int exp;//指数
        //计算最大值
        for (int anArr : arr) {
            if (anArr > max) {
                max = anArr;
            }
        }
        //从个位开始,对数组进行排序
        for (exp = 1; max / exp > 0; exp *= 10) {
            //存储待排元素的临时数组
            int[] temp = new int[arr.length];
            //分桶个数
            int[] buckets = new int[10];
 
            //将数据出现的次数存储在buckets中
            for (int value : arr) {
                //(value / exp) % 10 :value的最底位(个位)
                buckets[(value / exp) % 10]++;
            }
            //更改buckets[i],
            for (int i = 1; i < 10; i++) {
                buckets[i] += buckets[i - 1];
            }
            //将数据存储到临时数组temp中
            for (int i = arr.length - 1; i >= 0; i--) {
                temp[buckets[(arr[i] / exp) % 10] - 1] = arr[i];
                buckets[(arr[i] / exp) % 10]--;
            }
            //将有序元素temp赋给arr
            System.arraycopy(temp, 0, arr, 0, arr.length);
        }
 
    }

4.1桶排序

基本思想:将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了

在这里插入图片描述

public static void bucketSort(int[] arr){
    // 计算最大值与最小值
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }
    // 计算桶的数量
    int bucketNum = (max - min) / arr.length + 1;
    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
    for(int i = 0; i < bucketNum; i++){
        bucketArr.add(new ArrayList<Integer>());
    }
    // 将每个元素放入桶
    for(int i = 0; i < arr.length; i++){
        int num = (arr[i] - min) / (arr.length);
        bucketArr.get(num).add(arr[i]);
    }
    // 对每个桶进行排序
    for(int i = 0; i < bucketArr.size(); i++){
        Collections.sort(bucketArr.get(i));
    }
    // 将桶中的元素赋值到原序列
	int index = 0;
	for(int i = 0; i < bucketArr.size(); i++){
		for(int j = 0; j < bucketArr.get(i).size(); j++){
			arr[index++] = bucketArr.get(i).get(j);
		}
	}  
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构之常见排序算法 的相关文章

随机推荐

  • C#读取硬盘物理序列号-非管理员权限

    using System using System Collections Generic using System Text using System Runtime InteropServices namespace SCBLL Com
  • 服务器(Linux系统)指定目录安装Anaconda教程

    1 下载 通过weg命令下载 Xshell终端输入命令 wget c https repo anaconda com archive Anaconda3 2020 11 Linux x86 64 sh 输入后开始下载 我这里用的pychar
  • VC++如何计算一段代码的执行时间

    单位为毫秒 在程序调试的过程中 VS2010包含
  • java/php/net/python会员健身系统管理设计

    本系统带文档lw万字以上 答辩PPT 查重 如果这个题目不合适 可以去我上传的资源里面找题目 找不到的话 评论留下题目 或者站内私信我 有时间看到机会给您发 本课题要求实现一套会员健身系统管理 系统功能包括会员 个人资料管理 教练信息管理
  • 使用 VS2022 配置 QT 开发环境的步骤

    使用 VS2022 配置 QT 开发环境的步骤 QT 是一个跨平台的 C GUI 库 可以在 Windows Mac Linux 等操作系统上运行 在 Visual Studio 2022 中配置 QT 的开发环境 可以让开发者在 Wind
  • Label Assignment

    前言 今天在研究四点模型的时候 了解到一个新概念 Label Assignment 记录一下 Label assignment 参考文档 目标检测中的Label Assignment Label assignment 主要是指检测算法在训练
  • 文件翻转教学python

    目录 第1关 读文件全部内容到一个字符串 第2关 读文件前n个字符 第3关 逐行读取并输出文件内容 第4关 读取文件到列表中 第5关 读取文件中的数据到二维列表 第6关 将唐诗写入到文件中 第1关 读文件全部内容到一个字符串 任务描述 本关
  • OpenGL学习例程精析(3d纹理)

    OpenGL学习例程精析 3d纹理 代码分析 glPixelStorei 完整代码 最终效果 代码分析 3d纹理的配置要比2d纹理复杂一些 glPixelStorei glPixelStorei GL UNPACK ALIGNMENT 1
  • eclipse的安装和汉化

    eclipse是一个可扩展的开发平台 受到开发人员的欢迎与好评 其安装和汉化的步骤如下 在本文中涉及的网址都是官方网址 确保下载软件的安全 纯净 1 下载jdk1 8 0并安装 网址 http www oracle com technetw
  • 响应式数据大屏构造

    数据大屏构建 需求 UI 实现响应式数据大屏 适配各种屏幕 不允许出现滚动条 方案 rem 实现原理 根据屏幕宽度 计算1rem的宽度 配置根元素的font size 所有的像素单位按照rem计算 优点 实现响应式 根据设计稿和VW的宽度实
  • 海量图片曝光百度新家“搜索框”大厦

    今天陪朋友到百度办事 有幸参观了百度的新办公大楼 搜索框大厦 大厦特别漂亮 内部设计特别炫 功能更是酷啊 海量图片第一时间与大家分享一下 刚到上地环岛 远远就看到气势宏伟的大厦 非常醒目 波浪形的玻璃外墙 相当气派 无论从正面 侧面还是背面
  • fetch使用

    fetch基本使用方法 1 fetch与ajax作用相同 发送请求 2 ajax是使用XMLHttpRequest对象来请求数据 因此需要先new XMLHttpRequest 然后连接发送接收 3 fetch是一个方法 fetch 地址
  • vue中点击按钮关闭当前页面踩坑记录

    vue中关闭当前页面踩坑记录 当前页面直接使用window close不行 必须是新窗口才能使用window close 所以要router跳转时打开新窗口才能关闭 直接使用 不行 window close 先使用下面跳转对应页面 let
  • windows下两种方法通过cmd进入指定目录

    方法一 通过cmd cd命令进入 相同盘符下的目录可直接使用cd 但是windows下不同于linux 不能直接跨盘符cd进入目录 例如 从C盘进入E盘下面的目录 需要两行命令 跨盘符 跨盘符目录 先后顺序都可以 先输入跨盘符目录 再输入跨
  • C语言求班级平均分案例讲解

    我们先看例题 统计3个班成绩情况 每个班有5个同学 求出所有班级的平均分以及各个班级的平均分 从键盘输入成绩 思路分析 1 我们定义一个3行5列的二维数组用来存放学生的成绩 1行表示1个班的学生成绩 总共3行 可以存放3个班的成绩 每行有5
  • 菜鸟视角的openwrt(一) 初识openwrt

    作为一只菜鸟 为了熟悉openwrt系统 看了很多前辈的文章 因为写作的角度或者说目标人群不同 侧重点也不同 学到的知识零零碎碎 等积累的知识多了 回头再来看 才发现 原来如此 原来作者已经帮我们总结好了 这篇文章对老鸟来说 可以直接忽略
  • 二分模板——数的范围

    789 数的范围 先用二分求出x的左边界 a mid gt x mid在x的右边 所以右边界变为mid 即 if a mid gt x r mid else l mid 1 根据模板得出mid mid l r gt gt 1 若得出的左边界
  • stm32+DS1302+TM1638驱动程序

    TM1638数码管显示驱动程序 参考 1 TM1638与STM32连接 1 1 硬件连接 Vcc 电源 GND 电源地 STB PA0 CLK PA1 DIO PA2 1 2 驱动程序 TM1638 c文件 Program Assignme
  • MySQL基础入门语法

    一 数据库基础概念 1 1 数据库定义 数据库 存储数据的软件 长期存储在计算机内 有组织的数据集合 表 数据库存储数据的基本单位 数据按照分类存储到不同的表中 能够高效的的查询其中数据 对于测试工作 如果项目页面没有实现 需要校验数据 则
  • 数据结构之常见排序算法

    文章目录 1 排序概念 2 10种排序比较 3 排序算法 3 1直接插入排序 元素越有序 越高效 3 2希尔排序序 缩小增量排序 3 3直接选择排序 3 4堆排序 3 5冒泡排序 3 6快速排序 递归实现 无序使用最好 3 6 1挖坑法 建