Java基本排序总结(Sort)

news/2024/5/19 23:58:24 标签: 算法, 数据结构, 排序算法, java, 快速排序

这里写目录标题

  • 什么是排序
    • 排序的定义
    • 排序的相关术语
    • 排序的分类
    • 算法分析
  • 冒泡排序
  • 选择排序
  • 希尔排序
  • 归并排序
  • 直接插入排序
  • 快速排序(快排)
  • 堆排序
  • 海量数据的排序问题
  • 总结

什么是排序

排序的定义

对一序列对象根据某个关键字进行排序(通俗点来说就是吧一堆数据按照从小到大(升序),或者从大到小排列(降序)),我们下面都基于升序来讲解。

排序的相关术语

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
  • 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
  • 内排序:所有排序操作都在内存中完成
  • 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
  • 时间复杂度: 一个算法执行所耗费的时间
  • 空间复杂度:运行完一个程序所需内存的大小

排序的分类

在这里插入图片描述

算法分析

在这里插入图片描述

冒泡排序

冒泡排序bulleSort)这里就不在多说了,非常经典的一种排序,我们接触的第一种排序啦。

算法描述:

  • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  • 从头到尾两两元素进行比较交换 ,这时尾部元素为最大元素
  • 针对所有的元素重复以上的步骤,除了最后一个
  • 重复步骤1~3,直到排序完成

算法分析
时间复杂度:

  • 当数据有序时 最好 O(n)
  • 当数据无序时 最差 O(n2)

空间复杂度O(1)
不稳定

实现代码

java">    public static void bullSort(int[] array){
        for(int i=0;i<array.length;i++)
        {
            int flag=0;
            for(int j=0;j<array.length-1-i;j++)
            {
                if(array[j]>array[j+1])
                {
                    int tmp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=tmp;
                    flag=1;
                }
            }
            if(flag==0)
            {
                break;
            }
        }
    }

选择排序

选择排序(selectSort)是一种简单直观的排序算法。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
算法描述

  1. 先将第一个元素开始,将第一个元素与后面的每一个元素进行比较,如果比其小,则进行交换,直到数组遍历完,第一个元素便是最小的元素
  2. 接着从第二个元素开始,与第二个元素之后的元素进行比较
  3. 重复上述过程

算法分析
时间复杂度:

  • 当数据有序时 最好 O(n)
  • 当数据无序时 最差 O(n2)

空间复杂度O(1)
稳定
实现代码

java">    public static void selectSort(int[] array) {
         for(int i=0;i<array.length-1;i++)
         {
             for(int j=i+1;j<array.length;j++)
             {
                 if(array[j]<array[i])
                 {
                     int tmp=array[i];
                     array[i]=array[j];
                     array[j]=tmp;
                 }
             }
         }
    }

希尔排序

希尔排序shellSort)也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,它与插入排序的不同之处在于,它会优先比较距离较远的元素(将小的数据尽可能的放到前面)希尔排序是把数据按照增量数组中得值分成好多个组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的数据越来越多,当增量减至1时,整个数据恰被分成一组,其实此时也就是插入排序了。(增量数组当中的值为素数,但最后一个是1)
算法描述

  1. 选择一个增量序列drr[]
  2. 从增量序列中取出第一个k,对序列进行分组,并对进行每组进行插入排序
  3. 选择增量序列中的下一个,重复2步骤

算法分析
算法分析
时间复杂度:

  • 最好 O(n1.5)
  • 最坏 O(n2)

空间复杂度O(1)
不稳定

实现代码

java">    public static void shellSort(int[] array) {
        int[] drr = {5,3,1};//增量数组
        for (int i = 0; i < drr.length; i++) {
            shell(array,drr[i]);
        }
    }
    
    public static void shell(int[] array ,int gap) {
      int j,tmp;
      for(int i=gap;i<array.length;i++)
      {
          tmp=array[i];
          j=i-gap;
          for(;j>=0;j-=gap)
          {
              if(array[j]>tmp)
              {
                  array[j+gap]=array[j];
              }else{
                  break;
              }
          }
          array[j+gap]=tmp;
        }
    }
    
   

举例数组为[12,5,9,16,6,8,27,58,80,0,7,4,33,55,77]
增量序列为[5,3,1]

  • 分五组

在这里插入图片描述

  • 每组进行插入排序

在这里插入图片描述

  • 在分三组,在进行插入排序

在这里插入图片描述

归并排序

归并排序mergeSort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。(原理-合并两个有序数组
在这里插入图片描述

算法描述
来分析一下吧,对于归并算法,有两个部分组成,分解和合并。首先讲讲分解,我们需要将待排序的序列不停地进行分解,通过两个索引变量控制,一个初始索引(low),一个结尾索引(high)。只有当两索引重合才结束分解。接下来是合并,合并操作也是最麻烦的,也是通过两个索引变量s1,s2。开始s1在左边序列的第一个位置,s2在右边序列的第一个位置,然后就是寻找左右两个序列中的最小值(合并两个数组),放到新序列中,这时可能会出现一边的元素都放置完毕了,而另外一边还存在元素,此时只需将剩余的元素按顺序放进新序列即可,因为这时左右两边的序列已经是有序的了,最后将新序列复制到旧序列。这里也特别需要注意,因为合并的过程是分步的,而并非一次合并完成,所以数组的索引是在不断变化的。(所以需要加strat保证位置的正确性)

算法分析
时间复杂度:

  • 最好:O(nlogn)
  • 最坏:O(nlogn)

空间复杂度 O(n)
稳定
实现代码

  • 递归实现
java">    public static void mergeSort(int[] array){
        mergeSortInternal(array,0,array.length-1);
    }
    
    public static void mergeSortInternal(int[] array,int low,int high){
        if(low>=high) return ;
        int mid=(low+high)/2;
        //分解
        mergeSortInternal(array,low,mid);
        mergeSortInternal(array,mid+1,high);
        //合并
        merge(array,low,mid,high);
    }
    
    //核心:两数组进行合并
    public static void merge(int[] array,int start,int mid,int end){
        int s1=start;
        int s2=mid+1;
        int[] tmp=new int[end-start+1];
        int k=0;
        while(s1<=mid&&s2<=end)
        {
            if(array[s1]<=array[s2])
            {
                tmp[k++]=array[s1++];
            }else{
                tmp[k++]=array[s2++];
            }
        }
        //有可能第一个段还有数据 有可能第2个段也有数据
        while(s1<=mid)
        {
            tmp[k++]=array[s1++];
        }
        while(s2<=end)
        {
            tmp[k++]=array[s2++];
        }
        for(int i=0;i<end-start+1;i++)
        {
            //一定要加上start
            array[i+start]=tmp[i];
        }
    }
 // 归并排序
    private static void merge_sort(int[] arr, int low, int high) {
        if (low>=high) return;
        int mid = low+high>>1;
        merge_sort(arr, low, mid);
        merge_sort(arr, mid + 1, high);

        int[] tmp = new int[high - low + 1]; 
        int i = low, j = mid + 1, k = 0;  
        // 进行归并
        while (i <= mid && j <= high) {
            if (arr[i] <= arr[j]) 
                tmp[k++] = arr[i++];
            else
                tmp[k++] = arr[j++];
        }
        while (i <= mid) tmp[k++] = arr[i++];
        while (j <= high) tmp[k++] = arr[j++];
        // 进行赋值
        for (i = low, j = 0; i <=high; i++, j++)
            arr[i] = tmp[j];
    }


  • 非递归实现
java">    //非递归版本
    public static void mergeSort2(int[] array) {
        for (int i = 1; i < array.length; i*=2) {
            merge(array,i);
        }
    }
    
    public static void merge(int[] array,int gap) {
        int s1 = 0;
        int e1 = s1+gap-1;
        int s2 = e1+1;
        int e2 = s2+gap-1 < array.length ? s2+gap-1 : array.length-1;
        int[] tmp = new int[array.length];
        int k = 0;//下标
        //当有两个归并段的时候
        while (s2 < array.length) {
            //当当有两个归并段 且 这两个段内都要数据
            while (s1 <= e1 && s2<= e2) {
                if(array[s1] <= array[s2]) {
                    tmp[k++] = array[s1++];
                }else{
                    tmp[k++] = array[s2++];
                }
            }
            while (s1 <= e1) {
                tmp[k++] = array[s1++];
            }
            while (s2 <= e2){
                tmp[k++] = array[s2++];
            }
            //从这里开始的时候,每个下标都可能越界
            s1 = e2+1;
            e1 = s1+gap-1;
            s2 = e1+1;
            e2 = s2+gap-1 < array.length ? s2+gap-1 : array.length-1;
        }
        //退出上面循环后,
        // 那么把s1段内的数据给拷贝下来,因为 有可能e1已经越界了
        while (s1 < array.length) {
            tmp[k++] = array[s1++];
        }

        //拷贝tmp到原数组当中
        for (int i = 0; i < tmp.length; i++) {
            array[i] = tmp[i];
        }
    }

直接插入排序

直接插入排序insertSort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
生活中打扑克牌就是插入排序的体现
在这里插入图片描述

算法描述

  • 1.从第一个元素开始,该元素可以认为已经被排序;
  • 2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 3.如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置,然后将新元素插入到该位置后;
  • 5.重复步骤2~4。

算法分析
时间复杂度:

  • 当数据有序时 最好:O(n)
  • 数据是无序的 最坏:O(n2)

空间复杂度 O(1)
稳定

实现代码

java"> public class insertSort {
    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int j = i-1;
            for (j; j >= 0 ; j--) {
                //如果这里是一个大于等于号 此时这个排序就不稳定了
                if(array[j] > tmp) {
                    array[j+1] = array[j];
                }else {
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

直接插入排序数据越有序,排序越快。当数据大部分有序时,使用直接插入排序最合适,直接插入排序也会用在其他一些排序的优化上。

快速排序(快排)

快速排序quickSort)的基本思想:通过一趟排序将待排记录分成两部分,其中一部分数据比某一个值均大,另一部分比这个值均小,(称这个值为基准pivot)然后分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述

  1. 从数列中挑出一个元素,称为 “基准”(pivot)
  2. 在找的过程中,重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)
  3. 递归地(quick)把小于基准值元素和大于基准值元素在进行排列即可

算法分析
时间复杂度:

  • 最好:O(nlogn)
  • 数据是有序的 最坏:O(n2)

空间复杂度 O(nlogn)
不稳定
实现代码

  • 递归实现
java">    //递归写法及其优化
    public static void quickSort1(int[] array){
        quick(array,0,array.length-1);
    }
    //找基准
    public static int pivot(int[] array,int low,int high) {
        int tmp = array[low];
        while (low < high) {
            while (array[high]>=tmp&&low<high) {
                high--;
            }
            //把high数据赋值给low
            array[low]=array[high];
            while (array[low]<=tmp&&low<high) {
                low++;
            }
            //把low下标的值给high
            array[high]=array[low];
        }
        array[low] = tmp;
        return low;
    }
    public static void insertSort(int[] array,int low,int high)
    {
        int tmp,j;
        for(int i=low;i<=high;i++)
        {
            tmp=array[i];
            j=i-1;
            for(;j>=low;j--)
            {
                if(array[j]>tmp)
                {
                    array[j+1]=array[j];
                }else{
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }
    public static void quick(int[] array,int low,int high){
            if(low>=high) return ;
            if(high-low + 1 <= 50) {
                //使用插入排序
                insertSort(array,low,high);
                return;//记着这里一定要return  这里说明 这个区别范围有序了 直接结束
            }
            //优化1
            medianOfThree(array,low,high);
            int piv=pivot(array,low,high);
            quick(array,low,piv-1);
            quick(array,piv+1,high);
        }

//三数取中法优化(找基准)
    public static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

    public static void medianOfThree(int[] array,int low,int high) {
        int mid = (low+high)/2;
        //array[mid] <= array[low] <= array[high]
        if(array[low] < array[mid]) {
            swap(array,low,mid);
        }//array[mid] <= array[low]
        if(array[low] > array[high]) {
            swap(array,low,high);
        }//array[low] <= array[high]
        if(array[mid] > array[high]) {
            swap(array,mid,high);
        }//array[mid] <= array[high]
    }

//2 递归实现
    private static void  quickSort(int[] arr,  int low, int high) {
        if(low>=high) return ;
        int i=low,j=high;
        while (i< j) {
            while (i < j && arr[j] >= arr[low]) j--;
            while (i < j && arr[i] <= arr[low]) i++;
            swap(arr, i, j);
        }
        swap(arr, i, low);
        quickSort(arr,  low, i - 1);
        quickSort(arr,  i + 1, high);
    }
    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
  • 非递归代码
java">//非递归写法
    //找基准
    public static int pivot(int[] array,int low,int high) {
        int tmp = array[low];
        while (low < high) {
            while (array[high]>=tmp&&low<high) {
                high--;
            }
            //把high数据赋值给low
            array[low]=array[high];
            while (array[low]<=tmp&&low<high) {
                low++;
            }
            //把low下标的值给high
            array[high]=array[low];
        }
        array[low] = tmp;
        return low;
    }
    public static void quickSort2(int[] array) {
        Stack<Integer> stack = new Stack<>();
        int low = 0;
        int high = array.length-1;
        int piv = pivot(array,low,high);//
        if(piv > low + 1) {
            stack.push(low);
            stack.push(piv-1);
        }
        if(piv < high-1) {
            stack.push(piv+1);
            stack.push(high);
        }
        while (!stack.empty()) {
            high = stack.pop();
            low = stack.pop();
            piv = pivot(array,low,high);
            if(piv > low + 1) {
                stack.push(low);
                stack.push(piv-1);
            }
            if(piv < high-1) {
                stack.push(piv+1);
                stack.push(high);
            }
        }
    }

堆排序

堆排序heapSort)在学堆排序之前我们需要了解大根堆、小根堆、如何构造堆等相关的一些知识(不了解的话,可以看看博主之前写的堆的文章)。
算法描述
我们默认是升序从小到大,我们分为三个步骤

  • 我们需要建一个大堆(降序的话建小堆)
  • 然后将第一个元素(最大的元素)与最后一个元素进行交换,然后再对第一个元素进行向下调整(保证剩余元素为大根堆,第一个元素为剩下元素的最大值)
  • 然后再将倒数第二个元素与第一个元素进行2过程,重复进行,知道走到第一个元素结束

算法分析
时间复杂度:

  • 最好的最坏都是O(n*log2n)

空间复杂度O(1)
稳定的

实现代码
代码

java">   //向下调整
    public static void adjustDown(int[] array,int parent,int len) {
        int child = 2*parent+1;
        while (child < len) {
            if(child+1 < len && array[child] < array[child+1]) {
                child++;//
            }
            if(array[child] > array[parent]) {
                int tmp = array[child];
                array[child] = array[parent];
                array[parent] = tmp;
                parent = child;
                child = 2*parent+1;
            }else {
                break;
            }
        }
    }
    //建大堆
    public static void crateBigHeap(int[] array) {
        for(int i = (array.length-1-1) /2; i>= 0;i--) {
            adjustDown(array,i,array.length);
        }
    }
    //堆排序
    public static void heapSort(int[] array) {
        crateBigHeap(array);
        int end=array.length-1;
        while(end>0)
        {
            int tmp=array[0];
            array[0]=array[end];
            array[end]=tmp;
            adjustDown(array,0,end);
            end--;
        }
    }

海量数据的排序问题

外部排序:排序过程需要在磁盘等外部存储进行的排序前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序

  1. 先把文件切分成 200 份,每个 512 M
  2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
  3. 进行 200 路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

总结

  1. 只有冒泡插入归并是稳定的,其余是不稳定的
  2. 归并、快排和堆比较难,一定要理解,他们三个也挺像的,时间复杂度都是O(nlogn),如果数据无序的话快排最快(不然为啥叫快排哈哈哈!)但空间的复杂度不同,归并最大是O(n)(因为开辟了数组),其次是快排为O(logn)(因为递归中开辟了栈帧),最小是堆排为O(1)。大家要理解。
  3. 只有归并是外排序,其余都是内排序

上面这些排序都是基于比较然后进行交换的思想,但也有不需要交换的方法,那便是计数排序、桶排序等等,大家感兴趣的话可以下去看看,这里就不做过多描述了。

好了,如果有错误和不懂的地方的话留言评论即可。


http://www.niftyadmin.cn/n/1322722.html

相关文章

最简单DIY的51蓝牙遥控小车设计方案

51单片机物联网智能小车系列文章目录 第一篇&#xff1a;最简单DIY的51蓝牙遥控小车设计方案 文章目录51单片机物联网智能小车系列文章目录前言一、最简单DIY的51蓝牙遥控小车设计方案是什么&#xff1f;二、制作步骤1.购买现成的小车配件2.下载代码3.根据软件和硬件完成硬件连…

Java--二叉搜索树

二叉搜索树概念重要操作查找插入删除&#xff08;重点&#xff09;性能分析总结概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为…

最简单DIY串口蓝牙硬件实现方案

51单片机物联网智能小车系列文章目录 第一篇&#xff1a;最简单DIY的51蓝牙遥控小车设计方案 第二篇&#xff1a;最简单DIY串口蓝牙硬件实现方案 文章目录51单片机物联网智能小车系列文章目录前言一、最简单DIY串口蓝牙硬件实现方案是什么&#xff1f;二、制作步骤1.搭建ESP32开…

最简单DIY蓝牙PS2遥控器控制蓝牙智能小车

51单片机物联网智能小车系列文章目录 第一篇&#xff1a;最简单DIY的51蓝牙遥控小车设计方案 第二篇&#xff1a;最简单DIY串口蓝牙硬件实现方案 第三篇&#xff1a;最简单DIY蓝牙PS2遥控器控制蓝牙智能小车 文章目录51单片机物联网智能小车系列文章目录前言一、最简单DIY蓝牙P…

剑指Offer16数值的整数次方

数值的整数次方问题描述分析AC代码不写不知道&#xff0c;一写啥也不会。原来还有个快速幂这个玩意&#xff0c;真的是学无止境。问题描述 问题链接&#xff1a;https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/ 分析 问题很简单&#xff0c;就是自己实…

最简单DIY基于51单片机的舵机控制器

51单片机物联网智能小车系列文章目录 第一篇&#xff1a;最简单DIY的51蓝牙遥控小车设计方案 第二篇&#xff1a;最简单DIY串口蓝牙硬件实现方案 第三篇&#xff1a;最简单DIY蓝牙PS2遥控器控制蓝牙智能小车 第四篇&#xff1a;最简单DIY基于51单片机的舵机控制器 文章目录51单…

java--哈希表(hash)

hash哈希的引入哈希冲突避免冲突设计合适的哈希函数负载因子的调节&#xff08;重点&#xff09;解决冲突闭散列&#xff08;开放地址法&#xff09;开散列&#xff08;链地址法&#xff09;重点hash和java类集的关系海量数据处理的问题位图&#xff08;缩小存储空间&#xff0…

Java--枚举

枚举枚举的背景和定义枚举的使用switch常用的方法枚举和反射相关面试题写一个单例模式用静态内部类实现一个单例模式用枚举实现一个单例模式枚举的优缺点枚举的背景和定义 枚举是在JDK1.5以后引入的。主要用途是&#xff1a;将一组常量组织起来&#xff0c;在这之前表示一组常…