Java实现排序算法

article/2025/10/24 6:43:14

一、常见排序算法:

     1、插入类排序:

           (1)直接插入排序

           (2)希尔排序

     2、选择类排序

           (1)简单选择排序

           (2)堆排序

     3、交换类排序

           (1)冒泡排序

           (2)快速排序

      4、归并排序

      5、基数排序

二、内部排序:只考虑数据量较小仅需要使用内存的排序算法

三、稳定与非稳定:如果一个排序算法能够保留数组中重复元素的相对位置则可以被称为是稳定的。反之,则是非稳定的。

四、直接插入排序

      1、基本思想

           通常人们整理扑克牌的方法是一张一张的来,将每一张牌插入到其他已经有序的牌中的适当位置。在计算机的实现中,为了要给插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。

      2、算法描述

       一般来说,插入排序都采用就地(in-place)在数组上实现。具体算法描述如下:

         (1)从第一个元素开始,该元素可以认为已经被排序

         (2)取出下一个元素,在已经排序的元素序列中从后向前扫描

         (3)如果该元素(已排序)大于新元素,将该元素移到下一位置

         (4)重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

         (5)将新元素插入到该位置后

         (6)重复步骤2~5

      3、Java实现

public class DirectSort {/** @desc: 通过交换进行插入排序,借鉴冒泡排序* @param a: 待排序数组* @return: void*/public void sort(int[] a){for (int i = 0; i < a.length-1; i++) {for (int j =i+1; j >0 ; j--) {if (a[j]<a[j-1]){int temp = a[j];a[j] = a[j-1];a[j-1] = temp;}}}}/** @desc: 通过将较大的元素都向右移动而不总是交换两个元素* @param a: 待排序数组* @return: void*/public void sort2(int[] a){for (int i = 0; i < a.length; i++) {int num = a[i];int j;for ( j = i; j>0 && num<a[j-1] ; j--) {a[j] = a[j-1];}a[j] = num;}}
}

        4、复杂度分析

平均时间复杂度

最好情况

最坏情况

空间复杂度

O(n²)

O(n²)

O(n²)

O(1)

        5、总结与思考

             插入排序所需的时间取决于输入元素的初始顺序。例如,对一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比随机顺序的数组或是逆序数组进行排序要快得多。

五、希尔排序

        也称 递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是 非稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一

       希尔排序是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

       1、基本思想

          将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次再将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。

           可以看到步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。一般来说最简单的步长取值是初次取数组长度的一半为增量,之后每次再减半,直到增量为1。

       2、算法描述

          (1)选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;

          (2)按增量序列个数 k,对序列进行 k 趟排序;

          (3)每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

       3、Java实现

/*
* @desc: 希尔排序( 递减增量排序算法)
* @param a: 待排序数组
* @return: void
*/
public void hillSort(int[] a){int length = a.length;int h = 1;while (h < length/3){h = 3 * h + 1;}for (;h>=1;h/=3){for (int i = 0; i < a.length - h; i+=h) {for (int j = i+h; j > 0 ; j -=h) {if (a[j]< a[j-h]){int temp = a[j];a[j] = a[j-h];a[j-h] = temp;}}}}
}

       4、复杂度分析

平均时间复杂度

最好情况

最坏情况

空间复杂度

O(nlog2 n)

O(nlog2 n)

O(nlog2 n)

O(1

       5、总结与思考

希尔排序更高效的原因是它权衡了子数组的规模和有序性。排序之初,各个子数组都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序。

六、简单选择排序

       1、基本思想

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

      选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对 n个元素的表进行排序总共进行至多 n-1 次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

      2、算法描述

         (1)从未排序序列中,找到关键字最小的元素

         (2)如果最小元素不是未排序序列的第一个元素,将其和未排序序列第一个元素互换

         (3)重复1、2步,直到排序结束。

      3、Java实现

/*
* @desc: 简单的选择排序
* @param a: 待排序数组
* @return: void
*/
public void selectSort(int[] a){for (int i = 0; i < a.length; i++) {int min = i;//选出之后待排序序列中值最小的位置for (int j = i+1; j < a.length; j++) {if (a[j]<a[min]){min = j;}}//最小值的位置不等于当前值的位置时进行交换if (min != i){int temp = a[i];a[i] = a[min];a[min] = temp;}}

        4、复杂度分析

平均时间复杂度

最好情况

最坏情况

空间复杂度

O(n²)

O(n²)

O(n²)

O(1)

        5、总结与思考

        选择排序的简单和直观名副其实,这也造就了它”出了名的慢性子”,无论是哪种情况,哪怕原数组已排序完成,它也将花费将近n²/2次遍历来确认一遍。即便是这样,它的排序结果也还是不稳定的。 唯一值得高兴的是,它并不耗费额外的内存空间。

七、堆排序

      1、定义:

            n个元素的序列{k1,k2,..,kn}

            当且仅当满足下关系时,称之为堆。

         把此序列对应的二维数组看成一个完全二叉树。那么堆的含义就是:完全二叉树中任何一个非叶子节点的值均不大于(或不小于)其左,右孩子节点的值。 由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。因此我们可使用大顶堆进行升序排序, 使用小顶堆进行降序排序。

        2、基本思想

     以大顶堆为例,堆排序的过程就是将待排序的序列构造成一个堆,选出堆中最大的移走,再把剩余的元素调整成堆,找出最大的再移走,重复直至有序。

        3、算法描述

          (1)先将初始序列K[1..n]建成一个大顶堆, 那么此时第一个元素K1最大, 此堆为初始的无序区.

          (2)再将关键字最大的记录K1 (即堆顶, 第一个元素)和无序区的最后一个记录 Kn 交换, 由此得到新的无序区K[1..n−1]和有序区K[n], 且满足K[1..n−1].keys⩽K[n].key

          (3)交换K1 和 Kn 后, 堆顶可能违反堆性质, 因此需将K[1..n−1]调整为堆. 然后重复步骤2, 直到无序区只有一个元素时停止。

堆排序需要两个过程

一是:建立堆

二是:堆顶与堆的最后一个元素交换位置

所以堆排序有两个函数组成

一是:建堆函数

二是:反复调用建堆函数以选择出剩余未排元素中最大的数来实现排序的函数。

         4、具体的操作:

          (1)大顶堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点

          (2)创建大顶堆(Build_Max_Heap):将堆所有数据重新排序

          (3)堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

        5、对于堆节点的访问:

          (1)父节点i的左子节点在位置:(2*i+1);

          (2)父节点i的右子节点在位置:(2*i+2);

          (3)子节点i的父节点在位置:floor((i-1)/2);

        6、Java实现

/*** @Description: 堆排序* @Version:*/public class HeapSort {/** @desc: 建堆函数---将数组堆化* @param a:待排序数组* @param n: 非叶子结点的索引* @return: void*/public void max_heapify(int[] a,int n){int child;for (int i = (n-1)/2; i >= 0 ; i--) {//左子结点的位置child = 2*i+1;//右子结点存在且大于左子结点,child变成右子结点if (child != n && a[child] < a[child + 1]){child++;}//交换父结点与左右子结点中的最大值if (a[i] < a[child]){int temp  = a[i];a[i] = a[child];a[child] = temp;}}}/** @desc: 堆排序* @param a: 待排序数组* @return: void*/public void heapSort(int[] a){for (int i = a.length - 1; i > 0 ; i--) {max_heapify(a,i);//堆顶元素(第一个元素)与Kn交换int temp = a[0];a[0] = a[i];a[i] = temp;}}
}

测试代码

import org.junit.Test;
import static org.junit.Assert.*;public class HeapSortTest {@Testpublic void fun(){HeapSort hs = new HeapSort();int[] a = {12,45,10,8,89,65,32,189,456,98,21,33,3,78,6};System.out.println("排序前的数组:");for (int i = 0; i < a.length; i++) {System.out.printf("%4d",a[i]);}System.out.println();long startTime = System.currentTimeMillis(); //获取开始时间hs.heapSort(a);long endTime = System.currentTimeMillis(); //获取结束时间System.out.println("排序耗时:"+(endTime-startTime)+"ns");System.out.println("排序后的数组:");for (int i = 0; i < a.length; i++) {System.out.printf("%4d",a[i]);}System.out.println();}
}

            7、复杂度分析

               (1)建立堆的过程, 从length/2 一直处理到0, 时间复杂度为O(n);

               (2)调整堆的过程是沿着堆的父子节点进行调整, 执行次数为堆的深度, 时间复杂度为O(lgn);

               (3)堆排序的过程由n次第2步完成, 时间复杂度为O(nlgn).

平均时间复杂度

最好情况

最坏情况

空间复杂度

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

           8、总结与思考

由于堆排序中初始化堆的过程比较次数较多, 因此它不太适用于小序列。 同时由于多次任意下标相互交换位置, 相同元素之间原本相对的顺序被破坏了, 因此, 它是不稳定的排序。

八、冒泡排序

       1、基本思想

            冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

      2、算法描述

        (1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。

        (2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

        (3)针对所有的元素重复以上的步骤,除了最后一个。

        (4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

     3、Java实现

/*** @Description: 冒泡排序* @Version:*/public class BubbleSort {public static  void sort(int[] a){//外循环控制比较的次数(趟数)for (int i = 0; i < a.length; i++) {//内循环控制到达的位置for (int j = 0; j < a.length-i-1; j++) {//前面的元素比后面大就交换if (a[j]>a[j+1]){int temp = a[j];a[j]  = a[j+1];a[j+1] = temp;}}}}
}

         4、复杂度分析

平均时间复杂度

最好情况

最坏情况

空间复杂度

O(n²)

O(n)

O(n²)

O(1)

          冒泡排序是最容易实现的排序, 最坏的情况是每次都需要交换, 共需遍历并交换将近n²/2次, 时间复杂度为O(n²). 最佳的情况是内循环遍历一次后发现排序是对的, 因此退出循环, 时间复杂度为O(n). 平均来讲, 时间复杂度为O(n²). 由于冒泡排序中只有缓存的temp变量需要内存空间, 因此空间复杂度为常量O(1).

         5、总结与思考

          由于冒泡排序只在相邻元素大小不符合要求时才调换他们的位置, 它并不改变相同元素之间的相对顺序, 因此它是稳定的排序算法。

九、快速排序

      1、基本思想

          快速排序的基本思想:挖坑填数+分治法。

          快速排序使用分治法策略来把一个序列(list)分为两个子序列(sub-lists)。

          快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

       2、算法描述

         (1)从数列中挑出一个元素,称为"基准"(pivot)。

         (2)重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

         (3)递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

      3、Java实现

public class MergerSort {/** @desc: * @param a: 待排序数组* @param low: 子序列的左边元素的下标* @param high: 子序列右边元素的下标* @return: void*/public static void sort(int[] a,int low,int high){//已经排完if (low >= high){return;}int left = low;int right = high;//保存基准值int pivot = a[left];while (left < right){ //从后向前找到比基准小的元素while (left < right && a[right] >= pivot){right--;}a[left] = a[right];while (left < right && a[left] <= pivot){ //从前往后找到比基准大的元素left++;}a[right] = a[left];}a[left] = pivot; //放置基准值,准备分治递归快排sort(a,low,left - 1);sort(a,left + 1,high);}
}

             4、复杂度分析

平均时间复杂度

最好情况

最坏情况

空间复杂度

O(nlog₂n)

O(nlog₂n)

O(n²)

O(1)

十、归并排序

      1、基本思想

           归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

      2、算法描述:

     归并排序可通过两种方式实现:

  • 自上而下的递归
  • 自下而上的迭代

     递归法(假设序列共有n个元素):

    (1)将序列每相邻两个数字进行归并操作,形成 floor(n/2)个序列,排序后每个序列包含两个元素;

     (2)将上述序列再次归并,形成 floor(n/4)个序列,每个序列包含四个元素;

     (3)重复步骤2,直到所有元素排序完毕。

迭代法

     (1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

     (2)设定两个指针,最初位置分别为两个已经排序序列的起始位置

     (3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

     (4)重复步骤3直到某一指针到达序列尾

     (5)将另一序列剩下的所有元素直接复制到合并序列尾

   3、Java实现

     归并排序其实要做两件事:

  • 分解:将序列每次折半拆分
  • 合并:将划分后的序列段两两排序合并

    因此,归并排序实际上就是两个操作,拆分+合并

public class Merger {//归并所需的辅助数组private static int[] aux;public static void sort(int[] a) {//一次性分配空间aux = new int[a.length];sort(a, 0, a.length - 1);}public static void sort(int[] a, int low, int high) {if (low >= high) {return;}int mid = (low + high) / 2;//将左半边排序sort(a, low, mid);//将右半边排序sort(a, mid + 1, high);merge(a, low, mid, high);}/*** 该方法先将所有元素复制到aux[]中,然后在归并会a[]中。方法咋归并时(第二个for循环)* 进行了4个条件判断:* - 左半边用尽(取右半边的元素)* - 右半边用尽(取左半边的元素)* - 右半边的当前元素小于左半边的当前元素(取右半边的元素)* - 右半边的当前元素大于等于左半边的当前元素(取左半边的元素)* @param a* @param low* @param mid* @param high*/public static void merge(int[] a, int low, int mid, int high) {//将a[low..mid]和a[mid+1..high]归并int i = low, j = mid + 1;for (int k = low; k <= high; k++) {aux[k] = a[k];}for (int k = low; k <= high; k++) {if (i > mid) {a[k] = aux[j++];} else if (j > high) {a[k] = aux[i++];} else if (aux[j] < aux[i]) {a[k] = aux[j++];} else {a[k] = aux[i++];}}}
}


http://chatgpt.dhexx.cn/article/kZTWtkXS.shtml

相关文章

Java排序算法——选择排序

Java排序算法——选择排序&#xff08;Selection sort&#xff09; 传送门 冒泡排序插入排序 简述 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放…

JAVA排序:快速排序算法

Java实现快速排序算法 快速排序算法体现了—分治思想&#xff1a;将大问题划分为多个相同独立的小问题&#xff0c;每个小问题的解决合在一起解决了大问题 实现快速排序的思想&#xff1a; {2,4,1,0,3,5}是目标数组 {0,1,2,3,4,5}是结果数组 选取中心轴pivot(中心轴的值用于比较…

Java排序算法——选择排序(Selection Sort)

上次总结了一下冒泡排序&#xff0c;这次来看看同样非常简单的选择排序 上期冒泡排序传送门&#xff1a; Java排序算法——冒泡排序&#xff08;Bubble Sort&#xff09;https://blog.csdn.net/babbfqb93/article/details/123005968?spm1001.2014.3001.5501 选择排序&#…

选择排序——Java排序算法

选择排序 选择排序&#xff08;SelectSort&#xff09;选择排序是所有排序中最简单的排序算法之一&#xff0c;同时也是要求我们必须掌握的排序算法之一。 时间复杂度 选择排序的时间复杂度为(n2) 算法步骤 1.在未排序的序列中找到最小或者最大的元素&#xff0c;存放到排…

java 排序api_JAVA排序算法API

上一个类 下一个类 框架 无框架 摘要&#xff1a; 嵌套 | 字段 | 构造函数 | 方法 详细信息&#xff1a; 字段 | 构造函数 | 方法 org.luosijin.test 类 Sort java.lang.Object org.luosijin.test.Sort public class Sort extends java.lang.Object Java实现几种常见排序方…

Java排序算法

7.1 排序算法的介绍 排序也称排序算法(Sort Algorithm)&#xff0c;排序是将一组数据&#xff0c;依指定的顺序进行排列的过程。 7.2 排序的分类 内部排序: 指将需要处理的所有数据都加载到内部存储器(内存)中进行排序。外部排序法&#xff1a; 数据量过大&#xff0c;无法全…

常见几种java排序算法

常见几种java排序算法 0. 总览时间复杂度和稳定性 1.插入排序2.分治排序法,快速排序法3.冒泡排序 low版4.冒泡排序 bigger版5.选择排序6. 归并排序8. 堆排序踩坑v1.0 巨慢不能用v2.0 太慢不能用v3.0 9. 其他排序7. 比较 0. 总览 时间复杂度和稳定性 平均最好最差稳定性冒泡n2…

Java常用实现八种排序算法与代码实现

一、交换排序 所谓交换&#xff0c;就是序列中任意两个元素进行比较&#xff0c;根据比较结果来交换各自在序列中的位置&#xff0c;以此达到排序的目的。 1. 冒泡排序 冒泡排序是一种简单的交换排序算法&#xff0c;以升序排序为例&#xff0c;其核心思想是&#xff1a; 从…

十大经典排序算法——java语言

文章目录 一、冒泡排序二、选择排序三、插入排序四、希尔排序五、归并排序六、快速排序七、堆排序八、计数排序九、桶排序十、基数排序 一、冒泡排序 概述&#xff1a; 冒泡排序是一种简单直观的排序算法。它重复的走访要排序的数列&#xff0c;一次比较两个元素&#xff0c;按…

Java常见排序算法

目录 1、归并排序 2、堆排序 3、基数排序 4、冒泡排序 5、希尔排序 6、快速排序 7、插入排序 8、选择排序 1、归并排序 1、基本思想 归并排序&#xff08;MERGE-SORT&#xff09;是利用归并的思想实现的排序方法&#xff0c;该算法采用经典的分治&#xff08;divide-a…

java实现七种经典排序算法

简单算法&#xff1a;冒泡&#xff0c;简单选择&#xff0c;直接插入 改进算法&#xff1a;希尔&#xff0c;堆&#xff0c;归并&#xff0c;快速 直接插入排序&#xff1a;将一个记录插入到已经拍好的有序列表中&#xff0c;从而得到一个新的、记录数增加1的有序表。 冒泡排…

java实现10种排序算法

1.冒泡排序(Bubble Sort) import java.util.Arrays; //冒泡排序 public class BubbleSort_01 {public static void main(String[] args) {int a[]{3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};//记录比较次数int count0;//i0,第一轮比较for (int i 0; i < a.length-1; i) {…

SQL sever数据库触发器设计

SQL sever数据库触发器设计 一、目的: 能够理解触发器调用的机制。能够使用SQL命令创建DML触发器。能够完成触发器的修改、删除等管理任务。 二、触发器: 定义&#xff1a;触发器&#xff08; T rigger &#xff09;是 SQL server 提供给程序员和数据分析员来保证数据完整性…

MySQL——超详细数据库触发器教程

目录 一、触发器的概念 二、创建触发器 三、查看触发器 四、删除触发器 总结 一、触发器的概念 在实际开发中往往会碰到这样的情况&#xff1a; 当我们对一个表进行数据操作时&#xff0c;需要同步对其它的表执行相应的操作&#xff0c;正常情况下&#xff0c;如果我们使用…

关于数据库触发器(trigger)的简单使用操作

最近在做一些东西&#xff0c;用到关于数据库触发器的简单使用。比如当我们在做用户模块的表设计的时候&#xff0c;我们建了联用户信息表&#xff08;t_user&#xff09;和账号表&#xff08;t_account&#xff09;&#xff0c;账号表&#xff08;t_account&#xff09;用来进…

MySQL数据库触发器

MySQL 数据库中触发器是一个特殊的存储过程&#xff0c;不同的是执行存储过程要使用 CALL 语句来调用&#xff0c;而触发器的 执行不需要使用 CALL 语句来调用&#xff0c;也不需要手工启动&#xff0c;只要一个预定义的事件发生就会被 MySQL自动调用 引发触发器执行的事件一般…

mysql数据库触发器失效,mysql 的数据库触发器解决方法

mysql 的数据库触发器 我要做一个数据库触发器&#xff0c;当删除数据库中的某一张表的时候触发这个一个事件&#xff0c;删除其他表中的某一些数据。 大家给个例子 ------解决方案-------------------- MYSQL官方免费手册中已经有现成的例子了。 CREATE TABLE test1(a1 INT); …

什么是数据库触发器?

目录 什么是数据库触发器&#xff1f; 事件 AFTER触发器 INSTEAD OF触发器 特殊数据库对象 定义 用于触发器 复杂的审计 执行业务规则 派生列值 触发器很棘手&#xff01; 什么是数据库触发器&#xff1f; 数据库触发器是在数据库中发生特定操作时运行的特殊存储过…

Oracle数据库 触发器

文章目录 一、触发器的定义二、触发器的分类三、触发器的功能四、触发器的语法五、触发器的使用案例案例1&#xff1a;向emp1表中插入一条数据后输出 欢迎加入 语句案例2&#xff1a;数据校验&#xff0c;在周四这一天不允许向emp1表中插入/更新数据案例3&#xff1a;创建触发器…

数据库之触发器详解

一、触发器的概念 触发器是与表有关的数据库对象&#xff0c;在满足定义条件时触发&#xff0c;并执行触发器中定义的语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性。 举个例子&#xff0c;比如你现在有两个表【用户表】和【日志表】&#xff0c;当一个…