Re0:Java编程系列

作者参加校招,在复习Java的同时,决定开一打系列博客。复习的同时,作者希望能留下材料,方便也服务一些新入门的小伙伴。

本系列文章从基础入手,由简单的功能函数开始,再扩展为项目结构等等。进一步延伸至Java编程的核心思想,封装继承多态

文章从面向功能编程方法开始过渡,手写代码的同时,逐渐向小伙伴揭示Java的编程思想和面向对象编程方法


提示:Java写代码前,需要Java环境和IDE集成环境,作者不赘述,希望小伙伴们提前准备好。

文章目录

  • Re0:Java编程系列
  • 前言
  • 一、数据结构?
  • 二、开始敲码!
    • 1.线性结构-数组
    • 2.强化训练-多数组
    • 3.树状结构-堆
    • 4.堆排序
  • 总结

前言

前些天我们简单地书写并管理了,排序算法的项目结构。
而现在,我们对数组的数据结构已经比较熟悉了。对于排序来说,自然是越快越好。排序思维的提升,与新的数据组织形式,对数据来进行更高效的管理,已经对我们更加重要。
想到这里,我合上漫画的书壳,没有沉浸在二次元的温柔乡,开始摸上自己的键盘。

温馨提示:本篇文章内容篇幅较长,可以找个安静的环境细细思考,可以提高食用质量。


一、数据结构?

引导:数据结构,数据的组织形式。

数据结构是一门相当重要的学科,它能涉及的内容很多,对许多领域都有很深远的影响。对个人来说,对数据结构的理解,能直接作用于逻辑思维与抽象分析的强化。例如,人际关系的分析。

现在,数据结构已经影响到编程的方方面面。好比,我们写的项目,包和类,从本质来讲,也是数据结构的一种。

我们将食物抽象化,举例面包和牛奶。面包,这个泛化概念,包括了许多许多种类的面包。分出菠萝包,法棍等等更具体的面包种类。

  • 菠萝包:味道甜,颜色橙黄,面包软硬适中。

它,菠萝包,具有一些属性。而我们将这些属性组织起来,建立一个类。类名就是菠萝包,而它的一些特点便是其中的成员变量。而这样的类,我们同样可以写出很多。

  • 法棍面包:味道难吃,颜色贼黄,面包巨硬。

简单地将两个类组织到同一个包下,将包名命名为面包,将许多和这个面包一样的包组织到一起,例如,米饭面包。这样,我们可以得到一个项目,它的名字可以叫食物

仔细想想,我们所接触到的绝大多数开源项目,都以这样的方式组织着。而数据结构,将这种,由一个点分支出多个点的结构,称为树状结构

数据结构逻辑上大体分为四种,集合结构线性结构树状结构图形结构

而且,经过前两次的Re0:Java编程系列,相信小伙伴们,已经对数组这种数据形式比较熟悉了。

接下来,我们将从实践中逐渐熟悉线性结构,对数组进行实际编程分析。

本次我们需要学习三个算法,快速排序,希尔排序和堆排序。通过三个算法的学习,我们开始对数据组织形式开始探索!

二、开始敲码!

1.线性结构-数组

线性结构中,最为典型的代表便是数组。

数组中,我们依靠下标对数组数据进行位置的判定,依靠数值对数组数据大小进行判定。

从简单上理解理解,下标对应着一个存储位置,而每一个存储位置中包含一个值。

不过,数组的下标与其所存储的值没有直接联系。因此,存储数据没有直接的逻辑联系,使得存储数据之间相互独立。

对结点的插入,删除运算时,需要遍历数组并且寻找数据,同时移动一系列的结点。

大概理解数组的结构后,接下来我们试着写一个难一点的排序。

快速排序:每一轮开始,选择一个中枢,并将其移动到合适的位置。将小于中枢的数值放在其左边位置,继续快排,将大于中枢的数值放在其右边位置,继续快排。直至,所有元素排序完成。

这是快排的一种经典写法,容易理解,也十分常见。现在,我们来分析,这种快速排序方法,需要哪些元素。

  • 存储一个中枢,记录下中枢的数值。
  • 排序过程时,我们需要左右的执行位置。
  • 注意函数的结构,一个获取中枢位置的函数,方便下一次快排。

仔细思考以上的问题,我们花上两三分钟,再开始编码。

代码如下(示例):

 package swap;//获取中枢函数public static int getqs1Mid(int[] array,int left,int right) {//初始记录中枢为数组第一个元素值int index = array[left];//判断下标,初始轮,右边寻找while(left < right) {//从右开始,第一个比中枢小的元素,若不存在,数组有序退出条件为left==rightwhile(array[right] >= index && left < right) {right--;}//交换,左边寻找开始array[left] = array[right];//从左开始,第一个比中枢大的元素,若不存在,数组有序退出条件为left==rightwhile(array[left] <= index && left < right) {left++;}//交换,次轮开始array[right] = array[left]; }//赋予中枢值到合适位置array[left] = index;//返回排好的中枢下标index = left;return index;}//快排,静态函数,递归调用public static int[] quickSort1(int[] array,int left,int right) {int index = 0;//判断左右下标if(left < right) {//获取中枢下标index = getqs1Mid(array, left, right);//中枢左边递归,直至left==rightquickSort1(array, left, index-1);//中枢右边递归,直至left==rightquickSort1(array, index+1, right);}return array;}

这种快速排序中,我们使用的存储方式为数组,从逻辑上来考虑,也是通过数组的下标进行排序。

记录数组首位中枢值,循环,从右边开始,找到小于中枢的第一个数,交换到左边位置,从左边开始,找到大于中枢的第一个数,交换到右边,一直持续到左右位置交替。

下标位置移动到合适位置时,index-1左边递归快排,index+1右边递归快排。

仔细想想,中枢位置移动式的快速排序,如何优化呢?

换一种方式,我们通过对左右两边元素进行交换。

代码如下(示例):

 package swap;//快排,静态递归,左右交换方式public static int[] quickSort2(int[] array,int left,int right) {//记录左移动下标int i = left;//记录右移动下标int j = right;int temp = 0;int pivot;//记录数组中间元素的值if((array[(left+right)/2]-array[left])*(array[left]-array[right])>=0) {pivot = array[left];} else if((array[left]-array[right])*(array[right]-array[(left+right)/2])>=0){pivot = array[right];} else {pivot = array[(left+right)/2];}//下标交替时,循环结束while(i<=j) {//寻找左下标,左边第一个大于pivot的元素,等于时保底,元素位置不会越过中值位置while(pivot > array[i]) {i++;}//寻找右下标,右边第一个小于pivot的元素,等于时保底,元素位置不会越过中值位置while(pivot < array[j]) {j--;}//交换一次,并使i,j记录到新的位置if(i<=j) {temp = array[i];array[i] = array[j];array[j] = temp;i++;j--;}//左快排if (right > i) {quickSort2(array, i, right);}//右快排if (left < j) {quickSort2(array, left, j);}}   return array;}

在这种快排模式中,我们不需要将中枢移动到合适位置。我们需要的是一个中间值,类似于中位数,通过中位数对左右两边进行判断排序。

记录移动左右两边的位置,我们发现,每一次排序完成后,i下标位置前的数必定大于中值,j下标位置后的书必定小于中值。

由此,我们多次递归,左快排下标由i->right,右快排下标由left->j。当两个下标ij交替时,这便意味着排序结束了。

通过对数组数据结构的解析,我们理解并掌握了一种不错的排序方法。

2.强化训练-多数组

快速排序函数编程完毕,我们发现,每一轮排序中,我们都是在一个数组结构中操作。

快排中,我们确认了函数的首下标与尾下标。在逻辑中,我们将一个数组一分为二,然后在分好的小数组中继续分,一直到排序完成。

这种思维模式,称为分治,是常用算法思维的一种。简单来说,将一个大问题,分解为许多独立平级的小问题,再将小问题解决,大问题便迎刃而解。

再次加深对数据结构的印象,我们来写个希尔排序。

希尔排序,通过将一个数组划分为多个小数组,对每个小组进行插入排序。多轮分组后,最后一次相当于对整个数组进行排序。不过,因为前面的多次分组排序,数组已经相当有序了,最后一次排序会非常迅速。

代码如下(示例):

 package insert;public int total;//小组的间隔量public int gap;public int[] shellSort1(int[] array) {total = 0;int temp = 0;//距离量控制for (gap = array.length/2; gap > 0; gap = gap/2) {//排序从分好的第一组开始,第一组下标0,gapfor (int i = gap; i < array.length; i++) {//一次插入排序int j = i;temp = array[j];//判断小数组是否处于数组首while(j-gap > 0) {//同一轮中,小数组下标间隔为gapif(array[j-gap] > temp) {array[j] = array[j-gap];j = j - gap;} else {break;}}total++;array[j] = temp;}}return array;}

对于希尔排序来说,我们将一个整体数组,分出许多小数组,再依次对小数组进行插入排序。

3.树状结构-堆

经过对数组的详细分析与强化训练,相信对于数组这个数据结构,我们已经比较清楚了。

而现在,我们新增一种树状结构,堆。

堆是一种特殊的树状结构,满足两点。

  • 堆是一个完全二叉树。
  • 堆中结点的值总是不大于或不小于其父结点的值。

简单来讲,常用堆有两种,最大堆最小堆,当然其它的叫法也存在。

最大堆:根结点为整个堆中的最大元素。

最小堆:根结点为整个堆中的最小元素。

不过,说到堆,我们需要了解一下完全二叉树。这里,稍稍提一下。

二叉树:每个结点至多拥有两棵子树,并且,二叉树的子树有左右之分,其次序不能任意颠倒。

完全二叉树:二叉树的一种,最后一层可以不完全填充,其叶子结点都靠左对齐。

  • 任务:查寻以下内容,并弄清楚概念。
  • Complete Binary Tree-完全二叉数
  • Perfect Binary Tree-完美二叉树
  • Full Binary Tree-完满二叉树

接下来,将以最大堆为例,对数据结构堆进行详细分析。

首先,进行堆排序,我们需要思考一个内容,我们需要什么样的存储结构?

之前的算法分析,相信大家多少有一点注意到了。

在程序设计中,数据逻辑结构和数据存储结构是可以不一致的。

堆排序中,我们存储数据的思想是,将数据按照最大堆的方式进行存储。叶子结点的值,将不大于根结点。

但实际上,我们实现它,依旧可以使用数组结构。

数组下标从0开始,那么根结点为0下标时,它的左右孩子结点分别为1下标2下标

如此推导,设一个结点下标为index,它的左孩子为index*2+1,它的右孩子为index*2+2。而无论它是父结点的左孩子还是右孩子,父结点始终为(index-1)/2

这取决于,完全二叉树的性质。

4.堆排序

之前,我们已经分析好,如何在数组中存储,堆结构。接下来,我们要通过最大堆这一数据结构,进行排序。

最大堆中,我们能知道,根结点始终是堆结构中的最大元素。那么,当数组已经排好最大堆结构时,我们将根结点与最后一个叶子结点置换,再将数组的长度置为length-1,再次将新的数组置为最大堆。

如此,我们便可以重复操作,一直到数组有序。

而堆排序中,我们首要目的,便是将一群数组中的无序元素,组织成最大堆的结构。

堆中,有两种常用的操作方法。

  • 插入函数,将一个元素插入到堆中合适位置。
  • 下沉函数,将一个元素下沉到堆中合适位置。

在堆排序中,我们可以使用这两种函数,对堆初始化,依次排序,通过下标在结构中新增元素和减少元素。

代码如下(示例):

package select;//最大堆,将index位置的数值向上上浮数组合适位置public static void insertHeap(int[] array,int index) {//记录index下标的数值,子结点的值int temp = array[index];//引索父结点位置int i = (index-1)/2;//循环条件,判断父子结点到达最大堆顶端后while(i >= 0 && index > 0) {//判断,父结点大于子结点if(array[i] >= temp){break;} else {array[index] = array[i];//更新父子结点index = i;i = (index-1)/2;}//子结点值移动位置array[index] = temp; }}//最大堆,将index位置的数值向下下沉到合适位置,循环写法public static void sinkHeap(int[] array,int index,int length) {//引索左叶子结点位置int i = index*2+1;//记录index下标的数值,父结点的值int temp = array[index];//判断,存在左孩子while(i < length) {//判断右孩子,选择左右孩子中最大的if(i+1 < length && array[i+1] > array[i]) {i++;}//判断,孩子结点值小于父结点,退出if(array[i] <= temp) {break;}//父结点的值更新array[index] = array[i];//父结点位置下移index = i;//新增左孩子结点位置i = index*2+1;}//赋值初始父结点值array[index] = temp;}//最大堆,将index位置的数值向下下沉到合适位置,递归写法public static void submergeHeap(int[] array,int index,int length) {if(index < length) {//设最大值下标int max = index;//左右孩子下标位置int left = index*2+1;int right = index*2+2;//三结点,索引最大值下标if(left < length) {if(array[max] < array[left]) {max = left;}}if(right < length) {if(array[max] < array[right]) {max = right;}}//判断,交换父子结点值,父结点下标下移if(max != index) {int temp = array[index];array[index] = array[max];array[max] = temp;//下沉至合适位置submergeHeap(array, max, length);}}}

而这只是两种种功能函数,一次只能排好一个元素。想要对堆整体进行排序,便需要思考调用函数的方式。

我们花一点时间思考一下,如何通过调用函数来写成堆初始化呢?

  1. 插入函数,达成堆初始化,数组元素从什么位置开始?
  2. 下沉函数,达成堆初始化,数组元素从什么位置开始?

代码如下(示例):

 //最大堆初始化public void setHeap(int[] array) {//从后往前,第一个父结点开始for (int i = (array.length-1)/2; i >= 0; i--) {sinkHeap(array, i, array.length);}}//堆排1,插入函数控制public int[] heapSort1(int[] array) {int temp = 0;//堆排序,从堆的大小判定for (int i = array.length-1; i > 0; i--) {//堆初始化for (int j = 0;j <= i;j++) {insertHeap(array, j);}//置换元素值temp = array[0];array[0] = array[i];array[i] = temp;}return array;}//堆排2,下沉函数控制public int[] heapSort2(int[] array) {int temp = 0;//堆初始化setHeap(array);//堆排序,判断堆的大小for (int i = array.length-1; i >= 0; i--) {temp = array[0];array[0] = array[i];array[i] = temp;//每轮首元素下沉合适位置,重新置为最大堆sinkHeap(array, 0, i);}return array;}

细细思考一下,这三个函数都有一些十分细节的内容。这便当做,本次博客的小作业了。

  • 编程细节
  • 函数中第一次循环的位置,思考为何?
  • heapSort1函数中使用插入方式进行初始化,和heapSort2函数使用下沉方式的区别在哪里?

我们在test包中,写好的sortTest类中,将新增的排序类导入。写好测试用例,主函数新增调用。

鼠标右键,敲击Run,我们来跑程序。


总结

到现在,6种不同的排序方式和思维,我都将其写在了脑壳里。
仔细想想,这些排序还真有些相通之处。将大问题分解成小问题,再依次将小问题解决,分治的方法深入我心。
也许,程序的真谛,叫做信息处理。

Re0:Java编程系列-3 进阶排序思维分析与对比相关推荐

  1. 【Java编程系列】java用POI、Itext生成并下载PPT、PDF文件

    热门系列: [Java编程系列]WebService的使用 [Java编程系列]在Spring MVC中使用工具类调用Service层时,Service类为null如何解决 [Java编程系列]Spr ...

  2. 【Java编程系列】Minio实现文件上传下载

    热门系列: [Java编程系列]Amazon S3实现文件上传下载 目录 热门系列: 1.前言 2.Minio实战代码 2.1 Minio环境部署 2.2 Minio的Sdk对接实现 2.2.1 Mi ...

  3. 【Java编程系列】Java判断世界各时区的夏令时、冬令时

    热门系列: [Java编程系列]java用POI.Itext生成并下载PPT.PDF文件 [Java编程系列]二进制如何表示小数?0.3+0.6为什么不等于0.9?纳尼!!! 程序人生,精彩抢先看 目 ...

  4. 【Java编程系列】Java自定义标签-Tag

    热门系列: [Java编程系列]WebService的使用 [Java编程系列]在Spring MVC中使用工具类调用Service层时,Service类为null如何解决 [Java编程系列]Spr ...

  5. 【Java编程系列】log4j配置日志按级别分别生成日志文件

    热门系列: [Java编程系列]WebService的使用 [Java编程系列]在Spring MVC中使用工具类调用Service层时,Service类为null如何解决 [Java编程系列]Spr ...

  6. 【Java编程系列】gateway限流实践时发生的问题和解决方案

    前期回顾: [Java编程系列]Springcloud-gateway自带限流方案实践篇 1.实践中发生的问题 主要有以下几个问题: 1.限流返回的响应数据无法自定义 (LogFormatUtils. ...

  7. 【Java编程系列】使用Java进行串口SerialPort通讯

    热门系列: [Java编程系列]WebService的使用 [Java编程系列]在Spring MVC中使用工具类调用Service层时,Service类为null如何解决 [Java编程系列]Spr ...

  8. JAVA 编程中的汉字处理(1)---java 编程技术中汉子问题的分析与解决

    Java 编程技术中汉字问题的分析及解决 段明辉, 自由撰稿人 简介: 在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题.一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够 ...

  9. 明翰Java教学系列之进阶面向对象篇

    复习 1.Java有多少种数据类型,数据类型的分类?数据类型的大小排序?默认类型?默认值? 2.Java的工作机制? 3.Java中有多少种初始化数组的方式? 4.什么是变量,如何定义变量?Java中 ...

最新文章

  1. C++关键字union
  2. 记一次lnmp经历 nginx 多个php版本支持配置
  3. 一文看懂BCH减半时间计算及减半影响
  4. 漫画:什么是删库跑路?
  5. java.lang.IllegalArgumentException: An invalid character [10] was present in the Cookie value
  6. 新建带自定义模板的office文件
  7. 关于手机天气应用中的城市搜索的联想查找方式优化
  8. 如何使用Node.js来制作电子音乐-编写我们的旋律
  9. 什么是signal(SIGCHLD, SIG_IGN)函数
  10. SQL注入--利用cookie进行注入
  11. 测试中常用的批处理命令
  12. 利用Java SE基本知识是开发【学生信息管理系统】中
  13. CSS之弹性盒子模型(Flex布局)
  14. Qt编写可视化大屏电子看板系统25-模块3设备监控
  15. logging level级别
  16. lscpu与cat /proc/cpuinfo获取的CPU信息释义
  17. 机器学习笔试面试系列算法集锦
  18. CMake中option和cmake_dependent_option的使用
  19. ffmpeg 录制屏幕
  20. (一)arcpy开发利用arcpy在arcgis中批量裁剪影像

热门文章

  1. Android 蓝牙 A2DP基础概念、A2DP音频流的建立及传输流程、A2DP播放暂停音乐命令交互过程分析 - 史上最全分析
  2. 计算机主机的装机用途,计算机攒机作业分析.doc
  3. 一个与实验楼类似的平台
  4. VBScript语言 by.肖洪福
  5. 2020这些数码产品离我们远去
  6. 暴雪正在等待另一项_win10更新魔兽世界提示“正在等待另一项安装或更新”如何解决...
  7. 微信魂斗罗归来服务器共通吗,魂斗罗归来安卓微信444区-独当一面开服时间表_魂斗罗归来新区开服预告_第一手游网手游开服表...
  8. C语言排序算法——快速排序
  9. asp毕业设计——基于asp+access的搜索引擎设计与实现(毕业论文+程序源码)——搜索引擎
  10. Linux 无血缘关系进程通信