领扣网算法学习笔记 -- 283

领扣网算法学习笔记

本系列的算法题目来自领扣网

数组类算法第一天

题目:

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

解题过程:

思路一:

看到这题的第一个思路是:检测到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

自我整理:这部分代码精简,我看第一遍直接晕了,第二遍才反应过来,思路是这样的:

  1. 定义一个变量 j 用来循环赋值

  2. 循环数组

  3. 判断当前循环值是不是为0,不为0,就将当前值与循环变量位置的值进行交换,然后将循环变量往前移一位

  4. 如果是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
    

    自我整理:这个代码比我的要简洁,我擦,看样子我要优化我的代码了,这个思路很清晰:

    1. 定义一个变量用来记录非 0 的数量

    2. 将不是0的数据保存出来(这里只是读取不是0的变量,然后一直保存,不是像我之前那样后面的数据全部往前移)

    3. 将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

自我整理:这个算法思路也很清晰:

  1. 定义两个变量,一个记录0的个数,一个记录当前数据已经保存到那个位置了

  2. 然后循环数组,遇到0,记录 0 的个数就加一,不是0,就保存数据,记录当前保存位置的变量就+1

  3. 后面就再补齐0

自我总结:算法我觉得精简是一条优化的道路,但是精简的代码不代表执行效率就高,但是我目前很明显的缺点是:逻辑是怎样,代码就怎样写,导致代码过长,这一点反应了我的逻辑思维还不够好,还需要锻炼。

猜你喜欢

转载自blog.csdn.net/xxydzyr/article/details/83904669