课时29:数组排序案例分析

简介: 本次分享的主题是数组排序案例分析。主要分为一个部分:数组排序分析

课时29:数组排序案例分析

摘要:本次分享的主题是数组排序案例分析。主要分为一个部分:数组排序分析 

 

接下来看一个经典的案例,数组排序。数组排序是指将一个杂乱的数组按照特定的顺序进行排列。虽然数组排序听起来简单,但实际上它总是通过一个基础的模型来完成的。例如在本次讲解中先来实现一个升序排序的方式。仔细观察一下排序的处理过程。


那么这个代码应该怎么写?可以这样写。比如现在来分析一下数组排序的代码。现在先对数组排序进行简短的分析,然后再继续往下讲。首先说到数组随意创建一个的数组跟上一个 Int Date ,并为其赋予一些值,比如跟上 New Int 无序,先初始化一个无序的数组,随意填写一些元素: 7 , 10 , 8 , 9 , 0, 1 , 2 , 3 , 5 , 10 , 10 , 9 ,最后再添加一个 7 

image.png

public class ArrayDemo {
public static void main(String args[]){
Io
 }
}

接下来在代码中还需要添加一个数字 4 到这个数组里,并且为了更好地展示排序过程会多添加几个数字作为例子,也会尽量举一个极端的例子来说明。之后会在数组末尾添加数字 6 ,然后在接近末尾的位置将 0001 放到一个相对靠后的位置。这样就得到了一个待排序的数组。那么这个数组排序后的结果应该是从 0 开始,依次是 1 , 2 , 3 等数字顺序排列。


现在来进行详细的分析。首先要对这个数组进行分析,第一点先确定一下原始数组的内容。原始数组中的值依次列出8 , 9 , 0 ,然后在代码中再添加 2  3 ,紧接着是 5 ,还有其他值,比如 10 。现在已经把这些值都列完了。接下来再加上 7 ,然后是 6 ,最后还有一个 1 


接下来看一下最为传统的一种排序处理形式。这种处理形式的基本思路是在每次迭代中,将当前的最大值移动到数组的末尾。那么这是什么意思?接下来通过具体的过程来了解一下。

image.png

比如现在想进行第一次排序。第一次排序是这样进行的:先比较 8和 9 ,显然 9  8 大,所以 9 保持不动。接下来 9  0 比较,9 大于 0 ,因此 9  0 需要换位置,即向后移动。然后 9  2比较, 9 大于 2 ,所以 9 继续往后挪动。接着是 3  5 ,依次进行比较和可能的换位。但到这里还需要继续换吗?不需要了。


那么现在 7 该与谁换? 7  10 换,然后 10  6 换, 6 再与1 换,最后再回到 10 进行换位。这就是第一次排序的过程。所以第一次排序结果之后会发现已经将一个较大的数据放到了最后。接下来继续进行下一步,稍微做一些标注。这些标注代表的是第一次排序的执行结果。为了方便理解用大红色来进行标示。


那么现在进行第二次排序, 8  0  2  3  5 的比较显然不需要多说,因为它们都比 8 小。而 9 在这个位置不需要动,因为9 已经比它前面的数都大了。接下来比较 9  7  7  9 小,然后是 6 ,这里不需要详细说每个比较过程,但结果是 9 会“跳过”这些较小的数,保持在较后的位置。所以第二步移动的是 9 ,确保它处于更靠后的正确位置。到现在为止排序已经有了一些改变


接下来进入第三次排序, 0  2  3  5  8 这些数之间的相对位置可能不需要变动,因为它们已经处于或接近最终位置。但需要关注 8  7 的比较,这里 7 需要向后移动。同时 6  1 也会发生变动,最终这些变动会“回归”到 8 的考虑中,但这里的重点是理解每次排序都在逐步将较大的数向后“拽”。这是第三次排序的概述。


接下来继续进行排序,观察每次操作是否都在将当前未排序部分的最大值向后移动?

image.png

然后再来看第四次排序,对于 0  2  3  5  7  6 这一系列数字,显然 0  2  3  5  7 的位置相对固定,主要关注的是 1  6 。这次应该移动 1  6 ,让 6 向前挪动到它应该在的位置。这样数据的一部分排序就完成了。这是第四次排序的结果。接下来观察前几次排序基本上都是在将较大的数值向前“顶”或移动到它们应该在的位置。


现在轮到 6  1 进行比较和可能的移动了。因为其他数字在这个步骤中没有机会移动。继续进行到第五个排序过程。在这个过程中数字序列又发生了一些改变。改变之后关注 5  1 。这两个数字将进行再一次的比较和可能的交换位置,这是第六次排序。

image.png

现在把注意力放在这几个值上,让它们进行必要的调整。在 5 6排序完之后发现 1  3 又需要进行更换。大家应该能理解这个更换的过程,这是第七次排序。第七次排序后又有一些值被确定了下来。每次排序都在逐步确定所需的数据位置。


接下来进行第八次排序,这次应该是 1 2 进行交换。到这里整个的排序流程就已经全部完成了。那么这种排序就是最简单的数据排序过程。接下来要实现这种排序,比如按照刚才所描述的数据依次排序的过程,实际上是应该一个一个进行的。现在给出了几个数据作为示例。下面先来编写代码实现一次排序,按照刚才所描述的方法。


为了让大家看得更清楚会先把打印数组的方法加进来。现在正在编写这段代码。这个程序可以这样写:使用 For 循环,从 Y 等于 0开 始,直到 Y 小于 Date.length ,每次循环 Y++ 后。在完成这段代码后程序需要实现一个比较和交换的流程。假设要比较数组中的元素,那么比较的流程应该是这样的:从第 0 开始, 0  1 比较,然后 1与 2 比较,以此类推。


所以可以这样写代码:如果 Date[Y] 大于Date[Y + 1] ,那么就表示需要交换这两个元素。交换数据的流程就是将 Date[Y] 的值赋给一个临时变量 Temp ,然后将 Date[Y + 1] 的值赋给 Date[Y] ,最后将 Temp 的值赋给 Date[Y + 1] 。这样数据交换的流程就完成了。之后 Y 继续递增进行下一次循环,直到遍历完整个数组。

image.png

public class ArrayDemo {
public static void main(String args[]){ 
int data [] = new int [] {8,9,0,2,3,5,10,7,6,1}; 
for (int y = 0 ; y< data,length ; y ++) { 
 
   }
 }
 public static void printArray(int temp []){ 
for (intx=0;x< temp.length ;x++){ 
System.out.println (temp[x]) ;
     }
  }
}

在创建变量之后来看一下结果如何。现在 Date 变量放在这里,并在这个位置对它进行输出。现在在这个代码块里再加一个输出,并且加上一个换行符,之后跟上 Out.println 。现在代码编写完成后一起来观察这个结果。通过编译并执行代码为什么会出现“越界为十”的情况。


为什么索引会是十?这是因为已经到达了数组的最后一个元素,即9,而当尝试加一时,就超出了数组的范围,也就是发生了越界。所以这里应该怎么办?这里就需要减一。修改完代码后再次进行编译和执行。

image.png

for (int y=0 ;y<data.length ; y ++){
if (data[y]> data[y+11){//交换数据
int temp = data[y] ;
data[y] = data[y+ 1];
data[y + 1] = temp ;
   }
}
printArray (data);
}
public static void printArray(inttemp []){
for (int x=0;x<temp.length;x++) {
system.out.print(temp[x] + "、") ;
 }
System.out.println() ;
 }
}

这里将之前分析的结果直接展示给大家。这是刚才对问题的分析结果。现在利用这个分析结果来观察刚才的结果, 80235976 和 10 ,但需要注意的是这只是单次排序的结果。然而在实际应用中可能需要多次排序。那么最多可以排序多少次?数组的长度是完全可以满足多次排序需求的。因此为了进行简单的实验验证可以在代码中设置一个外部循环,让排序过程多次执行。


可以这样写:定义一个整型变量 X 并初始化为 0 ,当 X 小于数组 Date 的长度时,执行排序操作,并在每次循环结束时进行 X++ 。这样设置后外部循环将确保排序过程能够完整地执行多次。

image.png

D:\mldnjava>javac ArrayDemo.java
 
D:\mldnjava>java ArrayDemo
Exception in thread "main" java. lang. ArrayIndexOut0fBoundsException: 10
at ArrayDemo.main(ArrayDemo.java:5)
 
D:\mldnjava>javac ArrayDemo.java
 
D:\mldnjava>java ArrayDemo
8、0、2、3、5、9、7、6、1、10、
D:\mldnjava>

 

image.png

现在来编译并执行代码,再次观察结果可以看到虽然代码运行了,但实际上并没有实现预期的排序效果。进一步分析后发现每次循环中 Y 的比较次数是否可以有所减少。现在的 Y 还在重复判断后续的元素,这个过程其实可以优化。可以通过 X  1 的方式,随着X 的增长,判断次数也会相应减少。


接下来具体看一下这个过程。比如编译代码后再次执行。在整个排序过程中可以发现如果第一次需要全比对,那么第二次就会少比对一次,第三次又会少比对两次,以此类推。因此可以通过控制循环变量来优化代码。也就是说如果想要实现一个数组的排序功能,代码基础框架应该像这样构建


image.png

public class ArrayDemo {
public static void main(String args[]) {
int data [] = new int[] {8,9,0,2,3,5,10,7,6,1} ; 
for (int x =0;x<data.length ; x ++) {
for (int y = 0 ; y < data,length -1 ; y ++) {
if (data[y]> data[y + 1]) {//交换数据
int temp = data[y] ; 
data[y] = data[y + 1] ; 
data[y + 1] = temp ;
}
     }
   }
printArray(data);
}
public static void printArray(int temp[]) {
for (int x=0; x< temp.length ;x ++){
System.out.print (temp[x] +"、");
 }

以上程序代码都是通过主方法完成的。这并不符合面向对象的设计结构。那么最好的做法是将排序处理的操作交给哪一个方法来完成?可以交给未完成。这个方法应该被命名为未进行而这个叫做处理完成


这个位怎么处理?比如来看整个代码过程。这里请注意这边的代码过程。一个是排序,即给定数字就进行排序,然后第二个过程的话应该在这里有一个输出功能。那么可以这样写:首先找到一个类,这个类叫做 RVUT 将其放在这里。接着在这个类中写一个静态方法,命名为 Salt ,并接收一个整型参数 Date 


那么这句话所描述的是什么含义?为何要添加一个 Static 关键字?以及在这里添加 Static 的目的是什么?我们主要进行的是对数组的排序处理,但是如果不加 Static 关键字,那么就需要先创建一个类的实例化对象来调用这个方法。而如果添加了 Static 关键字,那么就可以直接通过类名来调用这个方法,无需实例化对象。


image.png

class ArrayUtil {
public static void sort(intdata[]){ 
for (intx=0 ;x<data.length;x++){
for (inty=0 iy< data.length-x-1;y++){
if (data[y] > data[y+11){//交换数据
int temp = dataly]; 
data[y]= data[y+1]; 
data[y + 1] = temp ;
}
        }
}
}
}
 
public class ArrayDemo {
public static void main (String args[]){
int data [] = new int[]{8,9,0,2,3,5,10,7,6,1};
 
printArray (data) ;

于是这个问题就产生了。为什么现在这个地方不使用实例化对象去调用方法,而是要通过类名直接调用?根据原则类中的方法如果不需要访问类的实例属性,那么这个方法就可以被声明为 Static 。既然 Static 方法不依赖于类的实例属性,那么创建一个实例化对象来调用这样的方法有必要吗?没有必要


当在未来进行软件设计的时候,如果发现某个类中不存在需要实例化的属性,那么定义的方法就没有必要使用普通方法了。因为普通方法需要在创建了类的实例化对象之后才能被调用。

现在我们并不需要实例化对象。所以在当前的代码中直接进行方法调用即可,就像对日期进行排序那样。


既然我可以进行输出操作,那么将这段代码放在这里是否可以继续在此添加一个输出功能?比如添加一个名为 Rvut.PrintArray 的方法。那么现在在这里是否可以正常完成这些方法的调用?由于类中没有定义属性,所以在这里没有提及普通方法的意义。这就是为什么在这里没有实现普通方法的主要原因。此时已经实现了对数组的排序。

image.png

D:\mldnjava>java ArrayDemo
8、0、2、3、5、9、7、6、1、10、
 
D:\mldnjava>javac ArrayDemo.java
 
D:\mldnjava>java ArrayDemo
0、1、2、3、5、6、7、8、9、10、
 
D:\mldnjava>javac ArrayDemo.java
 
D:\mldnjava>javaArrayDemo
0、1、2、3、5、6、7、8、9、10、
 
D:\mldnjava>javac ArrayDemo.java

在整个过程中主方法代码是不是极其简洁?并且所有的功能都交给了类去进行封装和处理。所以这样的结构看起来就非常合理了。

相关文章
|
Android开发
autojs下拉刷新
牙叔教程 简单易懂
1181 0
|
前端开发 测试技术 API
GraphQL 中的分页与排序:一分钟浅谈
本文深入介绍了 GraphQL 中的分页与排序功能,解释了为何这些功能在处理大量数据时至关重要,并详细说明了如何通过 `first` 和 `after` 参数实现分页,以及如何使用 `orderBy` 参数进行排序。同时,文章还探讨了常见问题及解决方法,帮助开发者避免陷阱,提升查询性能和用户体验。
302 70
|
11月前
|
机器学习/深度学习 前端开发 算法
基于STP文件的智能比对系统技术介绍
基于STP文件的智能比对系统通过集成多项先进技术,实现设计图纸与实物的自动化、高精度比对。系统采用分布式架构,包含前端Web界面、后端处理服务器、图像数据库和深度学习模型模块,支持STP文件解析、3D模型可视化、多视角图片生成及实物照片智能匹配。该系统显著提升机械制造和质量控制领域的效率与准确性,减少人工操作误差,广泛应用于设计验证、质量检测等场景。
764 3
|
安全 UED 开发者
鸿蒙开发:沉浸式效果实现
沉浸式效果实现后,一定要注意安全区域的内容避让,防止内容延伸后被导航条或者状态栏遮挡,具体是选择安全区域或者窗口管理方式,按照需求进行处理,如果仅仅是某个页面,直接安全区域即可。
388 0
鸿蒙开发:沉浸式效果实现
|
SQL 存储 数据安全/隐私保护
MyBatis-Plus演绎:数据权限控制,优雅至极!
项目使用mybaits-plus,所以在mybaits-plus的基础上增加数据权限的过滤 mybaits-plus自带数据权限支持,但由于系统数据权限相对复杂,通过查看文档发现好像并不适用,且原项目版本低,所以最终还是通过自己的方式实现
1885 1
MyBatis-Plus演绎:数据权限控制,优雅至极!
|
设计模式 负载均衡 Kubernetes
解密微服务架构:从理论到实践
在这篇文章中,我们将深入探讨微服务架构的核心概念,并通过一个实际案例来展示如何在现实世界中构建和部署一个微服务系统。文章将从微服务的定义开始,逐步介绍其优势、挑战、设计模式、以及如何使用现代技术栈来实现微服务架构。
|
消息中间件 NoSQL Java
Flink-06 Flink Java 3分钟上手 滚动窗口 时间驱动 Kafka TumblingWindow TimeWindowFunction TumblingProcessing
Flink-06 Flink Java 3分钟上手 滚动窗口 时间驱动 Kafka TumblingWindow TimeWindowFunction TumblingProcessing
246 0
|
消息中间件 存储 Cloud Native
深度剖析 RocketMQ 5.0,IoT 消息:物联网需要什么样的消息技术?
本文来学习一个典型的物联网技术架构,以及在这个技术架构里面,消息队列所发挥的作用。在物联网的场景里面,对消息技术的要求和面向服务端应用的消息技术有什么区别?学习 RocketMQ 5.0 的子产品 MQTT,是如何解决这些物联网技术难题的。
91543 4
|
Java 程序员 C#
C# 介绍、应用领域、入门、语法、输出和注释详解
C#(发音为“C-Sharp”)是一种由 Microsoft 创建的面向对象的编程语言,运行在 .NET Framework 上。源于 C 家族,与流行的语言如 C++ 和 Java 相近。首个版本发布于 2002 年,而最新版本,C# 12,于 2023 年 11 月发布
398 0
|
机器学习/深度学习 人工智能 算法
【AI大模型应用开发】【补充知识】文本向量化与向量相似度(含Python代码)
【AI大模型应用开发】【补充知识】文本向量化与向量相似度(含Python代码)
484 0

热门文章

最新文章