#561 Array Partition I#
思路:题目要求数组中所有数字配对后,每一对中最小值加和sum,sum要尽可能大。我的第一反应就是暴力枚举。下标为0的数值可能匹配的下标有:1,2,3…n-1;接着计算下标为1的数值可能匹配的下标有哪些。不断匹配,直到数组中所有数字配对完成。求和,保存和最大的值。可以得出结果,时间复杂度O(n^n)。n的n次幂。因为有n步,每步有n种情况。
学习:先排序数组,然后相邻的做paire。原因是这样的。如果一个数组已经排序好,则有a0,a1,a2…a(2n-1)。所求和最大的应该是a0+a2+…a+(2n-2) ,这是因为(a0,a1),(a2,a3)…;最小的和是 a0+a1+a2+…+a(n-1),这是因为(a0,an),(a1,a(n+1))这样匹配。还有另外一种解释。要想让和最大,则需要每个数值尽可能大。观察:如果一次配对,当 (1,4) 与 (1,2)比较的时候就会发现(1,2)pair 要比(1,4)pair 要好。因为4这个数字可能与其他数字配对被保留下来。而(1,4)配对,那4一定被舍弃。所以,能够看出配对的两个数字要尽可能接近。这里的时间复杂度主要是排序的时间复杂度O(nlogn)。
学习2:上面的主要时间耗在排序,其实排序还有一种思路是桶排序。就是把相同的数字放在一个桶里面。在配对的时候还是把尽可能相同的数字放在一起配对。
参考:网页1;网页2;网页3
我学习到的就是做题目除了依赖直觉,还需要靠观察。从观察中总结规律,进而提高算法效率。
代码
#566 Reshape the Matrix
思路:这道题是一道真正的easy级别,按照题目描述直接写代码即可。
学习:这里学习到的一点是对下标的巧妙利用。当然,这不影响算法的时间复杂度。二维数组变一维数组 M[i][j]=M[i*n+j],n是列数;一维变二维 M[i] = M[i/n][i%n],n是列数;
public int[][] matrixReshape(int[][] nums, int r, int c) {
if(nums ==null) return nums;
int initRow = nums.length;
if(initRow==0) return nums;
int initCol = nums[0].length;
if(r*c!=initRow * initCol) return nums;
int[][] result = new int[r][c];
for(int i=0;i<r*c;i++){
result[i/c][i%c] = nums[i/initCol][i%initCol];
}
return result;
}
#775 Global and Local Inversions
思路:按照题目要求,分别计算全局反转次数与局部反转次数。这样会产生O(n^2)的时间复杂度。
学习:题目中提到了一个很重要的信息,We have some permutation A of [0, 1, …, N - 1], where N is the length of A。这说明正常的A=[0,1,2,3,…N-1],也就是说A[i] = i ,
0
<
=
i
<
N
0<=i<N
0<=i<N。
进一步分析得到:
1 每个local inversion 也是一个global inversion
2 如果想要 localCount = globalCount,也就是说只有local inversion,没有其余的global inversion
3 如果只有local inversion,那么当遇到inversion的时候,只要交换A[i]和A[i+1]的位置,那么数组从0到i+1就有序了。
例如A = [1,0,2]。当i=1的时候,
A
[
1
]
<
A
[
0
]
A[1]<A[0]
A[1]<A[0],那么交换A[1]和A[0]之后,A=[0,1,2],那么A[0]=0,A[1]=1,符合A[i] = i的要求。那这数组从0到1是有序的,这是一次local inversion。
例如A = [2,0,1]。当i=1的时候,
A
[
1
]
<
A
[
0
]
A[1]<A[0]
A[1]<A[0],那么交换A[1]和A[0]之后,A=[0,2,1],那么A[0]=0,A[1]=2,A[1]不符合A[1]=1的要求。那这样就可以立即返回false。
#495 Teemo Attacking
思路: 根据题目含义,这是要处理重复线段的问题。 例如[1,2], 2;第一次起始时间是1,结束时间是3(或者2s的末尾);第二次起始时间是2,结束时间是4(或者3s的末尾)。 用第一次的结束时间-第二次的起始时间,就是重复的时间。
代码
#62 Unique Paths
思路:在每个位置(i,j)有两种走向:向右走到达(i+1,j),向下走到达(i,j+1)。暴力搜索每种情况。处理边界条件。这样会超时。
思路二:动态规划思想:dp[i][j]表示到达(i,j)位置,有几种走法。初始条件是第一行、第一列的每个位置只有一种走法所以dp[0][] = 1,dp[][0]=1;递归条件 dp[i][j] = dp[i-1][j]+dp[i][j-1]。时间复杂度O(m*n)
学习:排列组合思想:表格是m行,n列,robot需要向下移动m-1次,向右移动n-1次。如果用D表示向下,R表示向右。例如在3x7的表格中,需要2次D,6次R。这样可以是任意组合例如D,D,R,R,R,R,R,R或者D,R,D,R,R,R,R,R。这样只需要计算(m-1)+(n-1)的组合数。
代码
#442 Find All Duplicates in an Array
思路:题目要求不用额外的空间,在O(n)时间内完成,这样就要非常注重一个前提1 ≤ a[i] ≤ n (n = size of array),数组内的元素是在长度范围内的。这样就可以将数组的下标与数组的值建立映射关系。这里建立映射的关系是把nums[i]放在nums[i]-1 的位置上。如果nums[nums[i]-1]已经等于nums[i]了,那说明是重复的。(nums[i] = -1 就说明是缺失的值)
学习:不用交换数值,只需要将对应位置修改为负数即可。
public List<Integer> findDuplicatesV2(int[] nums) {
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<nums.length;i++){
int val = Math.abs(nums[i]);
if(nums[val-1]<0){
list.add(val);
}else{
nums[val-1] = - nums[val-1];
}
}
return list;
}
#63 Unique Paths II
思路:与62类似,用动态规划解决。注意条件判断即可。
问题:没有找到更快的方法。
代码
287 Find the Duplicate Number
学习思路一:Floyd龟兔法
学习思路二:二分法
代码
15 3Sum
思路:三个数相加和为0,转为固定一个数nums[i],求两个数的和为0-nums[i]。将nums排序后,用两个指针从最前面和最末尾开始移动。
代码
18 4Sum
思路:可以基于3Sum,将一个数固定nums[i],再剩余数组中查找三个数和为target-nums[i]。三个数的和再转为2个数的和。
思路2:可以使用map,将两两数的和存起来放在map中,再循环nums,得到两个下标。这样做的最大难题是去重。参考discuss的代码
代码
#73 Set Matrix Zeroes
思路:看起来比较简单的一道题。难点是注意修改和判断不能相互影响。例如matrix[2][3]=0,则matrix[2][]=0,matrix[][3] = 0。那么在判断第三行的时候,只能依据修改前的matrix[3][3]来判断是为0,而不能依据修改后的。我的思路是先遍历一遍找,分别找到需要置0的行和列,放入list中。然后再遍历这些行,每一列设置为0;遍历这些列,每一行的该列设置为0。
学习1:遍历如果某一行包含元素0,则修改matrix[i][0] =0;如果某一列包含元素0,则修改matrix[0][j]=0;但是第0行和第0列会共享同一个空间matrix[0][0],则需要添加一个变量col0,col0=0表示第0列为0;matrix[0][0]=0则表示第0行为0。
代码