课时30:数组转置案例分析

简介: 本次分享的主题是数组转置案例分析。主要分为二个部分:1.数组反转的具体操作2.内存分析

课时30:数组转置案例分析

摘要:本次分享的主题是数组转置案例分析。主要分为二个部分:

1.数组反转的具体操作

2.内存分析

 

01.数组反转的具体操作

 

接下来看一下数组反转的操作。什么叫数组反转?数组反转操作指的是将数组中的元素进行前后转置处理,也就是说将数组的首尾元素进行交换。假设现在有一个数组,其内容如下: 123456789 。那么要求交换后的内容是什么?是 987654321 


那么数组反转如何进行?对于数组的前后交换有两种方法,先分别看一下它们各自的特点,比如现在来看第一种方法,定义一个新的数组,然后按照逆序的方式保存原数组的元素。那么按照逆序方式保存是什么意思?简单来说就是从原数组的最后一个元素开始,依次将元素放入新数组中。

比如现在要讨论的是数组转置。


这个过程是这样的:先有一个原始数组,内容依次是 1  2  3  4  5  6  7  8  9 。注意现在没有数字 10 。那么现在要求的是这个数组的转置结果。转置的过程就是根据现有的数组元素,重新排列它们的位置。那么这里的角标是从几开始计算的?


那么按照常规的开发过程,角标的顺序应该是怎样的?应该是升序排列。通常这种升序结构在排序时会从 0 开始一直排列到某个特定的数字。不过这里可能有些混淆,正确的说法应该是从 0 排列到8 升序排列。

image.png

那么如果按照这样的规则来排列,整个过程中的角标会告诉我们什么?首先从 0 开始设置第一个数据的位置,这是获取数据时的情况。但如果是在设置数据时?假设在这里预先设定了一个值,对应角标为 8 ,这个值暂时不变。接下来依次有 1  2  3  4  5  6 ,然后再加上 7 ,接着是 8 ,最后是 9 


那么从角标 8开始应该按照什么样的顺序进行后续操作?按照这一边的顺序进行,也就是说采用的是降序排列即从 8 开始,然后是 7  6  5 ……。这样,从 0  8 的序列在降序排列下就变成了 8  7  6 ……的顺序。这是现在所说的方法,即生成一个新的数组,并通过逆序的模式来完成数组的转置。那么按照这样的方法来实现。接下来编写相应的代码。


在这个过程中先简化一下程序,去掉不必要的部分,只保留输出方法,以便清晰地展示结果。、现在有一个数组,里面存放的是 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 这些数字,接下来编写代码。首先定义一个名为 Temp 的第二个数组,其长度应该与第一个数组相同。然后定义一个变量 Int Fot ,并将其初始化为 Temp.length - 1 那么这里描述的 Fot 是第二个数组的索引下标


image.png

System.out.print (temp[x]+"、");
    }
System.out.println() ;
  }
}
public class ArrayDemo {
public static void main(String args[]) {
int data [] = new int[]{1,2,3,4,5, 6, 7,8,9} ;
 
ArrayUtil.printArray (data);
 }
}

接下来继续完善代码并解释其含义。首先编写一个 For 循环,初始化 Int x = 0 ,条件是 X < Date.length ,每次循环 X++ 。在循环体内为 Temp 数组赋值, Temp[date.length - 1 - x] = Date[x] 。这里 Date 是原始数组,通过逆序的方式将 Date 数组的元素赋值给 Temp 数组。


保存完上述代码后实现了 Temp 数组是Date 数组的逆序版本。但目标通常是得到一个可以直接使用的新数组,所以接下来将 Temp 数组的值赋给一个新的数组。现在来看一下最终的结果,通过编译和执行代码能否得到逆序的数组。现在已经通过这段代码实现了所需的功能。但是还需要进一步分析内存的使用情况。


image.png

System.out.print(temp[x] +"、") ;}
    }
System.out,println() ;
  }
}
public class ArrayDemo {
public static void main(String args[]) {
int data [] = new int []{1,2,3,4, 5, 6,7,8,9};
int temp [] = new int [data.length] ; //第二个数组 
int foot = temp.length - 1; //第二个数组的脚标 
for (int x =0 ; x<data.length ; x ++){
temp[foot --] = data[x] ;
}
data = temp ;
ArrayUtil.printArray (data);}
 }
}

 

02.内存分析

 

这个程序该如何操作?关于数组转置处理方式一由于数据量较大不会像刚才那样展示全部数据,只象征性地展示几个数字就可以了。首先这段代码的第一步是定义一个新的 Date 数组,这一点大家应该都已经知道了,需要用它来保存相应的内容。


这里说明一下不会全部保存,只是象征性地保存几个数字,比如 1  2  3 。紧接着在这段代码中,第二步是声明一个名为 Int Temp 的变量,并为其分配一个新的整型空间


image.png

这句话的含义无需赘述,接下来打算创建一个新数组。当然如果空间不够就需要删除一些内容,新数组的长度将与原始数组相同,但其内容将全部设置为其对应数据类型的默认值。这里有一个变量叫做 Temper ,现在将在这一步骤中对其进行处理。


当然最关键的问题是现在要进行的是重新赋值操作,也就是为 Temper 赋值。而且这个过程是否会逆序处理 Date 数组?不论是否逆序处理只关注一点:如果这段代码真正执行完毕后, Temper 的值应该变为321 

image.png

而最终的关键性问题在于此: Date 等于 Temper 。这句话究竟意味着什么?无需进一步详细描述或图示,因为已经可以明确这是关于数组的操作。关于数组的具体内容甚至无需绘制得过于详尽。这是数组,其内容如何不必过分深究。


在程序设计中一个最令人头疼但也是至关重要的问题是:希望整个代码中,原本说 Data 有数组内容, Temple 也有数组内容。但由于这样一句话的存在整个代码的结构就会出现一个特别不理想的状态。这个不理想的状态就是它会告诉你现在的 Date 不再需要了,要断开与它的连接。原本由 Data 指向的内容会转移到哪里?它会指向新的操作对象。

image.png

现在可以看到的确实现了一个数组逆序的操作,但这个逆序是以什么为代价?是以产生垃圾数据为代价的。那么这样的逆序显然是不理想的。因此第一种做法会产生垃圾。回顾第一种做法会发现它会导致一些无用的内存空间被占用。每一个内存空间都应该被极致利用,所以现在来尝试第二种做法。


提出一个可以在一个数组上进行转置操作。当然有些同学可能会担心,在数字数组上进行转置是否会遇到奇偶数的问题?为了说明问题这里不打算使用太多的数据,因为数据过多会使得描述变得复杂。接下来将采用两个具有代表性的数据组来进行说明。首先这是第一组数据,为了简化会稍微减少一些数据。那么这组数据的个数是奇数,现在正在准备一组奇数数据。


第二个数称之为 6 ,它是一个偶数,现在如果要对奇数个数进行转置,最佳的方法是怎样的?第一次转置是谁跟谁交换?是 5 和1 交换,然后再进行第二次转置,是 4  2 交换。转置完成后整个流程已经实现了转置的处理操作。那如果是以它为主进行转置,现在来看偶数个数的情况。


首先进行第一次转置,是 1  6 进行交换。接着进行第二次转置,是 5  2 进行交换。然后再进行第三次转置,是 4  3 进行交换。通过这一系列的比较和交换之后大概就能知道哪些数需要交换,哪些数不需要交换了。

image.png

对于这些换与不换的操作需要进行一番细致的讨论。先把这个大碗放在这里,之后用一条虚线来表示,并给它一个稍微浅一点的颜色以示区分。


现在把这个虚线放在中间位置。当明确了整个转置过程之后这个交换的次数是如何计算出来的?然而中间的部分其实并没有进行太多的交换,因此自然而然地认为现在要实现这种转置,最关键的就是要确定数组转换的次数

image.png

那么次数的计算应该是基于什么?是基于数组长度除以 2 。但这个时候是否需要考虑数组是奇数个元素还是偶数个元素?实际上并不需要去考虑数组是奇数个元素还是偶数个元素。


接下来以此为例来回答问题。 5 除以 2 等于 2.5 ,但由于整数不保留小数位,所以实际进行 2 次交换。那么 6 除以 2 等于 3 进行 3 次交换,正好满足要求。所以现在经清楚了这个代码应该怎么写。


并且之前的写法不好,应该做出改变。那么代码可以这样写:首先定义一个变量 Center ,它等于 Date.length 除以 2的结果注意这里应取整数结果,即向下取整,以确定转换的次数

image.png

System.out.print(temp[x] +"、");
      }
System.out.println() ;
}
}
public class ArrayDemo {
public static void main(String args[]){
int data [] = new int [] {1,2,3, 4,5, 6, 7,8,9} ;
 
ArrayUtil.printArray (data);
}
}

完成上述步骤后现在处于下方,并定义了一个名为 Int 的变量,注意这里 Int 有两个下标,一个表示递增,一个表示递减。另外还定义了一个名为 Head 的变量,其初始值为 0 ,这个变量用于标记操作的下标。同时还定义了一个名为 Tell 的变量,其值等于Data.lse  1 ,这也是一个用于操作的下标。


接下来进入了一个 For 循环,其中 Int x = 0 ,循环条件是 X < Center ,每次循环 X++ 。在这个循环内部执行了一个数据交换操作:首先定义了一个临时变量 Temper ,并将其赋值为 Data 。然后找到 Head 所指向的元素,将 Data  Head 位置的值与 Data中 Tell 位置的值进行交换。


具体来说就是将 Data[head] 赋值为Data[tell] ,再将 Data[tell] 赋值为 Temper 的值,从而完成了交换。那么在交换完成后 Head 是否需要递增?是的,这通常是拣选操作的一部分。

image.png

}
}
public class ArrayDemo{
public static void main(String args[]) {
int data [] = new int[] {1,2,3,4,5,6,7,8,9}; 
int center = data.length /2 ; // 确定转换的次数 
int head =0; // 操作脚标
int tail =data.length-1;//操作脚标 
for (intx=0;x<center ;x++){
int temp = data [head]; 
data [head]
    }
ArrayUtil.printArray (data);
  }
}

再次审视处理完成后的结果以确认其是否满足要求。之后再次进行编译并执行程序,特别关注转置操作的结果。这里的转置是在原地实现的,也就是说它不需要额外的存储空间来存储转置后的矩阵,而是在原矩阵上进行操作。


就程序而言,如果对比两个程序版本,可以发现第一个程序的循环次数要多于第二个程序。当对两种实现方式进行比较时,可以发现第一种处理方式的循环次数较多,并且可能会导致额外的内存开销。而第二种实现方式循环次数有所降低


然而第二种实现中引入了 If 判断语句,这在一定程度上增加了时间复杂度。但也需要考虑到第二种实现方式可以减少无用对象的产生,这对于提升程序性能是有帮助的。因此综合考量两种方式,第二种实现方式在整体上来看会更为合适。

}
ArrayUtil.printArray (data);
  }
}

当然为了确保代码能够正常编写还需要完成一个重要的步骤应该将转换功能封装为一个受保护的方法。因为主方法中不应该包含过多的转换流程。所以可以在这里添加一个名为 Reverse 的方法在未来的代码中,当需要进行转换时可以直接在 Reverse 方法中完成。这样在主方法中只需要调用 Obj.reverse() 就可以实现转置处理。这样的封装不仅提高了代码的可读性,还使得转换流程更加清晰和模块化。


接下来观察一下结果。先编译一下,然后再执行一次。会发现它们的效果是一样的。所以到这个阶段代码才算是一个较为合理的程序这就是关于矩阵转置的过程。在处理数组时,由于可以通过索引来控制元素,因此循环逻辑的使用会比较频繁。无论是排序还是转置,往往都需要在循环中实现。这个过程实际上是科班出身所必须掌握的基本功。

相关文章
|
传感器 人工智能 自动驾驶
构建未来:利用AI技术优化城市交通系统
【4月更文挑战第27天】 随着城市化的快速发展,交通拥堵已成为全球城市的普遍问题。本文探讨了人工智能(AI)技术在缓解城市交通压力、提升交通效率方面的应用前景。通过分析智能信号控制、交通流量预测、自动驾驶车辆以及公共交通优化等关键技术的实际案例,评估了AI在现代交通系统中的潜力与挑战。本研究不仅展示了AI技术在改善城市交通中的有效性,同时也指出了实施过程中需要克服的技术和社会障碍。
|
7月前
|
存储 监控 API
1688平台API接口实战:Python实现店铺全量商品数据抓取
本文介绍如何使用Python通过1688开放平台的API接口自动化抓取店铺所有商品数据。首先,开发者需在1688开放平台完成注册并获取App Key和App Secret,申请“商品信息查询”权限。接着,利用`alibaba.trade.product.search4trade`接口,构建请求参数、生成MD5签名,并通过分页机制获取全量商品数据。文中详细解析了响应结构、存储优化及常见问题处理方法,还提供了竞品监控、库存预警等应用场景示例和完整代码。
|
11月前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
221 9
|
JSON 自然语言处理 数据格式
【自定义插件系列】用自定义插件在阿里云百炼上生成一篇图文并茂的文章
本文介绍了如何在阿里云百炼平台上利用自定义插件生成图文并茂的文章。通过大模型生成小红书风格的文章,提取关键元素生成图像提示词,结合文生图插件生成图片,并最终整合文本与图像输出给用户。整个流程包括多个步骤:从创建对话型工作流开始,经过多次大模型处理、脚本转换和自定义插件操作,到最后完成图文混排的输出。
747 0
|
机器学习/深度学习 算法 数据挖掘
6种有效的时间序列数据特征工程技术(使用Python)
在本文中,我们将探讨使用日期时间列提取有用信息的各种特征工程技术。
373 1
|
11月前
|
Linux 虚拟化 数据安全/隐私保护
AlmaLinux 9.5 正式版发布 - RHEL 二进制兼容免费发行版
AlmaLinux 9.5 正式版发布 - RHEL 二进制兼容免费发行版
331 11
AlmaLinux 9.5 正式版发布 - RHEL 二进制兼容免费发行版
|
10月前
|
数据采集 前端开发 JavaScript
Next14 Server Components 和 Client Components 的区别
Next14 Server Components 和 Client Components 的区别
188 8
|
Serverless 计算机视觉
【基础回顾&DIYIoU】尝试撸一份属于自己的IoU函数
【基础回顾&DIYIoU】尝试撸一份属于自己的IoU函数
336 1
【基础回顾&DIYIoU】尝试撸一份属于自己的IoU函数
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
这篇文章详细解释了工厂模式,包括简单工厂、工厂方法和抽象工厂三种类型。每种模式都通过代码示例展示了其应用场景和实现方法,并比较了它们之间的差异。简单工厂模式通过一个工厂类来创建各种产品;工厂方法模式通过定义一个创建对象的接口,由子类决定实例化哪个类;抽象工厂模式提供一个创建相关或依赖对象家族的接口,而不需要明确指定具体类。
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
|
12月前
|
SQL 测试技术 数据库
如何设计一个交互式网站?
【10月更文挑战第7天】如何设计一个交互式网站?
276 4