前言
学习算法有一段时间了,但是总感觉并没有真的掌握,今天开始把自己前一段时间在leetcode上写过的题目,重新拿出来看看,并最大程度还原自己当时的做题过程以及思路。做一个简单的总结。
第一章:数组与字符串
1.1数组
题目724:寻找数组的中心索引
给定一个整数类型的数组 nums,请编写一个能够返回数组 “中心索引” 的方法。
我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。
做算法题,再简单也要先从读题开始。通过读题我们可以知道这样一个信息:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。
我们将其转化为数学语言(或者数学表达式)就可以表示为:
left_sum = right_sum;
以此为判断条件进行编码,我们可以编写如下代码(直接计算left_sum 与 right_sum并进行比较):
class Solution724 {
public int pivotIndex(int[] nums) {
int len = nums.length;
for(int i = 0; i < len; i ++)
{
int left_sum = 0;
int right_sum = 0;
for(int j = 0; j < i; j ++)
left_sum += nums[j];
for(int k = i + 1; k < len; k ++)
right_sum += nums[k];
if(left_sum == right_sum)
return i;
}
return -1;
}
}
但是这个代码的时间复杂度相当高,因为在外层循环中嵌套了内层循环。对于本题来说,这就是近乎无脑的暴力解法,并不是说暴力法不可取。但在学习的初期,需要去思考如何改进代码让代码的时间复杂度尽可能的降低。
接着我们继续分析:
想降低时间复杂度,对于本题来讲就是减少循环的使用次数。
我们来看,依旧是“数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和”这句话,依旧是left_sum = right_sum。但是如果我们将数学关系(或者说判断条件)改变一下,变成:
sum - num[i] = 2 * left_sum;
由此我们重新设计代码:
class Solution724 {
public int pivotIndex(int[] nums) {
int len = nums.length;
int sum = 0;
int leftSum = 0;
for(int i = 0; i < len; i ++)
sum += nums[i];
for(int i = 0; i < len; i ++){
if(nums[i] == sum - 2 * leftSum)
return i;
leftSum += nums[i];
}
return -1;
}
}
先计算出所有元素的总和sum,再去遍历数组判断是否找到中心索引,这样时间复杂度就得到了改进。
在代码中也涉及到一些小的细节,如:
-
为什么leftSum赋初值为0不为nums[0]?
因为nums数组可能为空,如果写上nums[0]必然会发生数组下标越界的错误 -
为啥要先判断nums[i] == sum - 2 * leftSum,再执行leftSum += nums[i]嘞?
因为如果nums数组只有一个数,那他的中心索引就是它本身了。虽然我感觉这么出没啥意思但为了保险还是这样写了…
724小题总结:
作为我学习算法写的第一个题,虽然他很简单,甚至于直接暴力揍他就行了,但是想第二个解法的过程着实让一个刚刚接触算法的小傻子有点吃力。9月22号写完的题,11月13号才写总结多少是有点晚了。但也是由于最近学起来总感觉不在状态才萌发了这个念头,希望自己能继续坚持下去,不管是java,算法还是即将到来的web考试以及课设。很多东西学的半斤八两,希望借此机会好好沉淀一下知识,让自己的水平有所提高。
2020年11月13日16:48