本系列的算法题目来自领扣网
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
看到这题的第一个思路是:检测到0后,将数组后面的数往前移,往数组的末尾补0,这样就确保在原数组上操作,且不用拷贝额外的数组。
class Solution {
public void moveZeroes(int[] nums) {
// 思路,从头开始判断是否是0,是的话,就将数组后面的元素往前移一位,然后将0放置在最后。
int arraylength = nums.length; // 获得数组长度
int arrlength = arraylength; // 获得循环次数
for(int i=0;i<arrlength;i++){
if(nums[i] == 0){ // 判断当前值是否为0;
for(int j=i;j<arraylength-1;j++){ //将剩余部分的数据往前移一位
nums[j]=nums[j+1];
}
nums[arraylength-1] = 0; // 将末位置0
i--;
arrlength--;
}
}
}
}
该代码在领扣上用时37ms
这样做的话,数组是只用遍历一次,但是数组没出现一个0,移动数组里的元素的代价(次数)就很大,于是就思考能不能不移动,
当出现0时我不移动,只是继续往后读,当往后读遇到不是0的时候,将数据移到是0的位置,
于是我定义一个变量用来记录0所在的位置,但是又遇到一个新问题,这个变量怎么赋初值(就是起始0在哪),
这个问题其实很多余,遍历的数据不同,起始的0的位置也不同,没办法固定,所以我直接遍历,当出现第一个0 的时候就设定这个值。然后继续往下遍历,不是0就与当前位置交换,当数组遍历完后,在之前的数组长度与目前记录0的位置的值的差值就是要补的0的个数,于是再补0就好。
class Solution {
public void moveZeroes(int[] nums) {
// 定义一个变量记录当前为0的位置,然后查询下一个数据是否为0,当数据不为0时,与当前数据互换位置
int i = 0; // 获取一个循环变量,记录当前循环到哪了
int locationnum = 0; // 暂时定义0位为第一个为0的位置
int arraylength = nums.length; // 获得数组长度
for(;i<arraylength;i++){ // 先读取第一个0的位置
if(nums[i]==0){
locationnum = i;
break;
}
}
for(i++;i<arraylength;i++){ // 判断接下来的数是否为0
if(nums[i]!=0){ // 如果不为0,则与当前的0的位置交换
nums[locationnum] = nums[i]; // 然后记录0的位置往前移一位
locationnum++;
}
}
if(locationnum != 0){
for(;locationnum<arraylength;locationnum++){ // 将不是0的数全部往前挪了,剩下的就全是0
nums[locationnum] = 0;
}
}
}
}
// 用时2ms
class Solution {
public void moveZeroes(int[] nums) {
int j = 0 ;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
int temp = nums[i];
nums[i] = nums[j];
nums[j++] = temp;
}
}
}
}
// 用时1ms
自我整理:这部分代码精简,我看第一遍直接晕了,第二遍才反应过来,思路是这样的:
定义一个变量 j 用来循环赋值
循环数组
判断当前循环值是不是为0,不为0,就将当前值与循环变量位置的值进行交换,然后将循环变量往前移一位
如果是0,则跳过
这代码比我的优在不用去找第一个0 在哪,直接循环就OK了,
但是我觉得,如果一个较大的数组,里面0的数量较少,那么这个算法我感觉就会时间变长,不能算是最优的,因为没一个不为0 的数,它都要执行额外的三条语句。毕竟刚入门算法,它的复杂度我暂时还不会算,所以只是个人猜想。
class Solution {
public void moveZeroes(int[] nums) {
int idx = 0;
for (int num : nums) {
if (num != 0) {
nums[idx++] = num;
}
}
while (idx < nums.length) {
nums[idx++] = 0;
}
}
}
// 用时2ms
自我整理:这个代码比我的要简洁,我擦,看样子我要优化我的代码了,这个思路很清晰:
定义一个变量用来记录非 0 的数量
将不是0的数据保存出来(这里只是读取不是0的变量,然后一直保存,不是像我之前那样后面的数据全部往前移)
将0补齐
class Solution {
public void moveZeroes(int[] nums) {
int count = 0;
int nonZeroIndex = 0;
for(int i = 0;i<nums.length;i++)
{
if(nums[i] == 0)
{
count++;
}
else{
nums[nonZeroIndex++] = nums[i];
}
}
for(int i = nums.length-count;i<nums.length;i++)
{
nums[i] = 0;
}
}
}
// 用时3ms
自我整理:这个算法思路也很清晰:
定义两个变量,一个记录0的个数,一个记录当前数据已经保存到那个位置了
然后循环数组,遇到0,记录 0 的个数就加一,不是0,就保存数据,记录当前保存位置的变量就+1
后面就再补齐0
自我总结:算法我觉得精简是一条优化的道路,但是精简的代码不代表执行效率就高,但是我目前很明显的缺点是:逻辑是怎样,代码就怎样写,导致代码过长,这一点反应了我的逻辑思维还不够好,还需要锻炼。