热门标签 | HotTags
当前位置:  开发笔记 > 人工智能 > 正文

Java排序算法整合(冒泡,快速,希尔,拓扑,归并)

这篇文章主要介绍了Java排序算法整合(冒泡,快速,希尔,拓扑,归并),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

冒泡排序介绍

冒泡排序(Bubble Sort),又被称为气泡排序或泡沫排序。

它是一种较简单的排序算法。它会遍历若干次要排序的数列,每次遍历时,它都会从前往后依次的比较相邻两个数的大小;如果前者比后者大,则交换它们的位置。这样,一次遍历之后,最大的元素就在数列的末尾! 采用相同的方法再次遍历时,第二大的元素就被排列在最大元素之前。重复此操作,直到整个数列都有序为止!

冒泡排序图文说明

/*
	  *   a -- 待排序的数组
	  *   n -- 数组的长度
	  */
	  public static void bubbleSort(int[] a, int n) {
	    int i,j;
 
	    for (i=n-1; i>0; i--) {
	      // 将a[0...i]中最大的数据放在末尾
	      for (j=0; j a[j+1]) {
	          // 交换a[j]和a[j+1]
	          int tmp = a[j];
	          a[j] = a[j+1];
	          a[j+1] = tmp;
	        }
	      }
	    }
	   
	  }

运行:

int[] a = {20,40,30,10,60,50,70};
    String aa = "冒泡排序";
    bubbleSort(a,a.length);
 
 System.out.print(aa);
 for (int d : a) {
  System.out.print(d+",");
}

快速排序介绍

快速排序(Quick Sort)使用分治法策略。

它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快速排序流程:

  1. 从数列中挑出一个基准值。
  2. 将所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边);在这个分区退出之后,该基准就处于数列的中间位置。
  3. 递归地把"基准值前面的子数列"和"基准值后面的子数列"进行排序。
  4. 图文介绍

代码实现:

/**
	  *
	  * 参数说明:
	  *   a -- 待排序的数组
	  *   l -- 数组的左边界(例如,从起始位置开始排序,则l=0)
	  *   r -- 数组的右边界(例如,排序截至到数组末尾,则r=a.length-1)
	  */
	  public static void quickSort(int[] a, int l, int r) {
 
	    if (l  x)
	          j--; // 从右向左找第一个小于x的数
	        if(i 

运行:

      String aa = "快速排序";
	    quickSort(a,0,a.length-1);
	    
 
	    
	    System.out.print(aa);
	    for (int d : a) {
	  	  System.out.print(d+",");
		  }

直接插入排序介绍

直接插入排序(Straight Insertion Sort)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。

直接插入排序图文说明

代码实现:

 /**
	  * @param 
	  *   a -- 待排序的数组
	  *   n -- 数组的长度
	  */
	  public static void insertSort(int[] a, int n) {
    int i, j, k;
 
    for (i = 1; i = 0; j--)
        if (a[j]  j; k--)
          a[k + 1] = a[k];
        //将a[i]放到正确位置上
        a[k + 1] = temp;
      }
    }
  }

运行和冒泡一样。。。。。

希尔排序:

希尔(Shell)排序又称为缩小增量排序,它是一种插入排序。它是直接插入排序算法的一种威力加强版。该方法因DL.Shell于1959年提出而得名。

希尔排序的基本思想是:

把记录按步长 gap 分组,对每组记录采用直接插入排序方法进行排序。

随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,则完成排序。

我们来通过演示图,更深入的理解一下这个过程。

在上面这幅图中:

初始时,有一个大小为 10 的无序序列。

在第一趟排序中,我们不妨设 gap1 = N / 2 = 5,即相隔距离为 5 的元素组成一组,可以分为 5 组。接下来,按照直接插入排序的方法对每个组进行排序。

在第二趟排序中,我们把上次的 gap 缩小一半,即 gap2 = gap1 / 2 = 2 (取整数)。这样每相隔距离为 2 的元素组成一组,可以分为 2 组。按照直接插入排序的方法对每个组进行排序。

在第三趟排序中,再次把 gap 缩小一半,即gap3 = gap2 / 2 = 1。 这样相隔距离为 1 的元素组成一组,即只有一组。按照直接插入排序的方法对每个组进行排序。此时,排序已经结束。

需要注意一下的是,图中有两个相等数值的元素 5 和 5 。我们可以清楚的看到,在排序过程中,两个元素位置交换了。

所以,希尔排序是不稳定的算法。

代码实现:

/**
	  	 * 希尔排序
	  	 * @param list
	  	 */
	  public static void shellSort(int[] a) {
	    int gap = a.length / 2;
 
	    while (1 <= gap) {
	      // 把距离为 gap 的元素编为一个组,扫描所有组
	      for (int i = gap; i = 0 && temp 

运行参考冒泡、、、、、

拓扑排序介绍

拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。

这样说,可能理解起来比较抽象。下面通过简单的例子进行说明!

例如,一个项目包括A、B、C、D四个子部分来完成,并且A依赖于B和D,C依赖于D。现在要制定一个计划,写出A、B、C、D的执行顺序。这时,就可以利用到拓扑排序,它就是用来确定事物发生的顺序的。

在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B出现在A的后面。

拓扑排序的算法图解

拓扑排序算法的基本步骤:

1. 构造一个队列Q(queue) 和 拓扑排序的结果队列T(topological);

2. 把所有没有依赖顶点的节点放入Q;

3. 当Q还有顶点的时候,执行下面步骤:

3.1 从Q中取出一个顶点n(将n从Q中删掉),并放入T(将n加入到结果集中);

3.2 对n每一个邻接点m(n是起点,m是终点);

3.2.1 去掉边;

3.2.2 如果m没有依赖顶点,则把m放入Q;

注:顶点A没有依赖顶点,是指不存在以A为终点的边。

以上图为例,来对拓扑排序进行演示。

第1步:将B和C加入到排序结果中。

顶点B和顶点C都是没有依赖顶点,因此将C和C加入到结果集T中。假设ABCDEFG按顺序存储,因此先访问B,再访问C。访问B之后,去掉边,并将A和D加入到队列Q中。同样的,去掉边,并将F和G加入到Q中。

将B加入到排序结果中,然后去掉边;此时,由于A和D没有依赖顶点,因此并将A和D加入到队列Q中。

将C加入到排序结果中,然后去掉边;此时,由于F有依赖顶点D,G有依赖顶点A,因此不对F和G进行处理。

第2步:将A,D依次加入到排序结果中。

第1步访问之后,A,D都是没有依赖顶点的,根据存储顺序,先访问A,然后访问D。访问之后,删除顶点A和顶点D的出边。

第3步:将E,F,G依次加入到排序结果中。

因此访问顺序是:B -> C -> A -> D -> E -> F -> G

拓扑排序的代码说明

拓扑排序是对有向无向图的排序。下面以邻接表实现的有向图来对拓扑排序进行说明。

1. 基本定义

public class ListDG {
  // 邻接表中表对应的链表的顶点
  private class ENode {
    int ivex;    
    // 该边所指向的顶点的位置
    ENode nextEdge; 
    // 指向下一条弧的指针
  }
 
  // 邻接表中表的顶点
  private class VNode {
    char data;     
    // 顶点信息
    ENode firstEdge;  
    // 指向第一条依附该顶点的弧
  };
 
  private VNode[] mVexs; 
  // 顶点数组
 
  ...
}
  1. ListDG是邻接表对应的结构体。 mVexs则是保存顶点信息的一维数组。
  2. VNode是邻接表顶点对应的结构体。 data是顶点所包含的数据,而firstEdge是该顶点所包含链表的表头指针。
  3. ENode是邻接表顶点所包含的链表的节点对应的结构体。 ivex是该节点所对应的顶点在vexs中的索引,而nextEdge是指向下一个节点的。

2. 拓扑排序

/*
* 拓扑排序
*
* 返回值:
*   -1 -- 失败(由于内存不足等原因导致)
*   0 -- 成功排序,并输入结果
*   1 -- 失败(该有向图是有环的)
*/
public int topologicalSort() {
  int index = 0;
  int num = mVexs.size();
  int[] ins;        
  // 入度数组
  char[] tops;       
  // 拓扑排序结果数组,记录每个节点的排序后的序号。
  Queue queue;  
  // 辅组队列
 
  ins  = new int[num];
  tops = new char[num];
  queue = new LinkedList();
 
  // 统计每个顶点的入度数
  for(int i = 0; i 

说明:

  1. queue的作用就是用来存储没有依赖顶点的顶点。它与前面所说的Q相对应。
  2. tops的作用就是用来存储排序结果。它与前面所说的T相对应。

归并排序

基本思想

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

分而治之

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

合并相邻有序子序列

再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

代码实现

package sortdemo;
 
import java.util.Arrays;
 
/**
* Created by chengxiao on 2016/12/8.
*/
public class MergeSort {
 public static void main(String []args){
   int []arr = {9,8,7,6,5,4,3,2,1};
   sort(arr);
   System.out.println(Arrays.toString(arr));
 }
 public static void sort(int []arr){
   int []temp = new int[arr.length];
   //在排序前,先建好一个长度等于原数组长度的临时数组,
   //避免递归中频繁开辟空间
   sort(arr,0,arr.length-1,temp);
 }
 private static void sort(int[] arr,int left,int right,int []temp){
   if(left

最后

归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。java中Arrays.sort()采用了一种名为TimSort的排序算法,就是归并排序的优化版本。从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文介绍如何利用动态规划算法解决经典的0-1背包问题。通过具体实例和代码实现,详细解释了在给定容量的背包中选择若干物品以最大化总价值的过程。 ... [详细]
  • 本文详细探讨了Java中的24种设计模式及其应用,并介绍了七大面向对象设计原则。通过创建型、结构型和行为型模式的分类,帮助开发者更好地理解和应用这些模式,提升代码质量和可维护性。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • 深入理解C++中的KMP算法:高效字符串匹配的利器
    本文详细介绍C++中实现KMP算法的方法,探讨其在字符串匹配问题上的优势。通过对比暴力匹配(BF)算法,展示KMP算法如何利用前缀表优化匹配过程,显著提升效率。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文探讨如何设计一个安全的加密和验证算法,确保生成的密码具有高随机性和低重复率,并提供相应的验证机制。 ... [详细]
  • 深入解析:手把手教你构建决策树算法
    本文详细介绍了机器学习中广泛应用的决策树算法,通过天气数据集的实例演示了ID3和CART算法的手动推导过程。文章长度约2000字,建议阅读时间5分钟。 ... [详细]
  • 在金融和会计领域,准确无误地填写票据和结算凭证至关重要。这些文件不仅是支付结算和现金收付的重要依据,还直接关系到交易的安全性和准确性。本文介绍了一种使用C语言实现小写金额转换为大写金额的方法,确保数据的标准化和规范化。 ... [详细]
  • 在给定的数组中,除了一个数字外,其他所有数字都是相同的。任务是找到这个唯一的不同数字。例如,findUniq([1, 1, 1, 2, 1, 1]) 返回 2,findUniq([0, 0, 0.55, 0, 0]) 返回 0.55。 ... [详细]
  • 本文探讨了卷积神经网络(CNN)中感受野的概念及其与锚框(anchor box)的关系。感受野定义了特征图上每个像素点对应的输入图像区域大小,而锚框则是在每个像素中心生成的多个不同尺寸和宽高比的边界框。两者在目标检测任务中起到关键作用。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
author-avatar
xinlang138438
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有