2019.1.7 leetcode 刷题总结
题号:852
我们把符合下列属性的数组 A 称作山脉:
A.length >= 3
存在 0 < i < A.length - 1 使得A[0] < A[1] < … A[i-1] < A[i] > A[i+1] > … > A[A.length - 1]
给定一个确定为山脉的数组,返回任何满足 A[0] < A[1] < … A[i-1] < A[i] > A[i+1] > … > A[A.length - 1] 的 i 的值。
示例 1:
输入:[0,1,0]
输出:1
示例 2:
输入:[0,2,1,0]
输出:1
我的想法:
这道题就是求给定一个存在最大值的数组,求此数组最大值的索引值。可以遍历数组,找出最大值,并将索引值返回;也可以用二分查找法寻找最大值。
对应程序:
// java
class Solution {
public int peakIndexInMountainArray(int[] A) {
return getMaxValueIndexOfArray(0, A.length - 1, A);
}
/**
* 二分查找法
* @param:start:查找范围的起始位置
* @param:end:查找范围的终止位置
* @param:array:给定的“山峰”数组
*/
private int getMaxValueIndexOfArray(int start, int end, final int[] array) {
// 将定最大值的索引值在查找范围的中间(求中间值不要直接相加再除2)
int maxIndex = start + (end-start)/2;
int max = array[maxIndex];
if(max > array[maxIndex + 1] && max < array[maxIndex - 1]){
// 向左缩小范围
return getMaxValueIndexOfArray(start, maxIndex, array);
}else if(max < array[maxIndex + 1] && max > array[maxIndex - 1]) {
// 向右缩小范围
return getMaxValueIndexOfArray(maxIndex, end, array);
}else {
return maxIndex;
}
}
}
题号:942
给定只含 “I”(增大)或 “D”(减小)的字符串 S ,令 N = S.length。
返回 [0, 1, …, N] 的任意排列 A 使得对于所有 i = 0, …, N-1,都有:
如果 S[i] == “I”,那么 A[i] < A[i+1]
如果 S[i] == “D”,那么 A[i] > A[i+1]
示例 1:
输出:“IDID”
输出:[0,4,1,3,2]
示例 2:
输出:“III”
输出:[0,1,2,3]
示例 3:
输出:“DDI”
输出:[3,2,0,1]
我的想法:
这道题还算比较有趣的,我的想法是这样的,首先,根据所给的字符串,画出对应的折线图,比如String s = “IDID”; 那么,它所对应的折线图应该是“/\/\”,类似于英文字母M的形状,得到了这个折线图后,为它的5个顶点赋值,从上到下,从0开始,就像下面这样:
3 4
/\ /\
0 1 2
因此,得到结果数组:[0,3,1,4,2],是符合字符串的规则的;
但我们不能在程序中画出对应的图,让计算机自己判断上下,因此我们需要一个变量来记录它的相对位置,也就是记录结果数组索引的权重大小;
假设在开始时,结果数组中所有索引的权重全都相同,都为0;当遇到I时,右边应比左边“高”,即右边索引的权重应当比左边的索引的权重大1;当遇到D时,右边应比左边“低”,即右边的索引的权重应当比左边的索引的权重小1;
得到一个索引值和索引权重相对应的数组,同时记录最大权重和最小权重;
遍历数组,通过权重大小为数组重新赋值,必须从最大的权重开始,否则可能引起权重和赋给数组中的值的混淆;
对应程序:
// java
class Solution {
public int[] diStringMatch(String S) {
int[] res = new int[S.length() + 1];// 结果数组
int index = 0;// 结果数组的索引
int maxPow = 0;// 最大权重值
int minPow = 0;// 最小权重值
int pow = 0;// 权重值
for(char s : S.toCharArray()) {
pow = res[index];
// 当遇到I时,说明左边的右边的数字应比左边的数字大
if('I' == s) {
// 右边数字的权重应比左边数字大
res[index + 1] = ++pow;
// 当遇到D时,说明左边的右边的数字应比左边的数字小
}else {
// 右边数字的权重应比左边数字小
res[index + 1] = --pow;
}
// 记录权重的最值
if(pow > maxPow) {
maxPow = pow;
}else if(pow < minPow) {
minPow = pow;
}
// 循环去记录下一个位置的权重
index++;
}
// 根据权重值为结果数组赋值,必须从权重大的位置开始
int value = S.length();
while(maxPow >= minPow) {
for(int i = 0; i < res.length; ++i) {
if(res[i] == maxPow) {
res[i] = value;
value--;
}
}
// 权重递减
maxPow--;
}
return res;
}
}
优化:
以上方法虽然直观但是转化成程序还是有些困难的,同时双重循环带来了更差的时间复杂度,看评论,有个方法比较的好:
遇见’I’就填最小的,遇见‘D’就填最大的,最后补充最后一位
程序:
// java
class Solution {
public int[] diStringMatch(String S) {
int[] res = new int[S.length() + 1];
int maxValue = S.length();
int minValue = 0;
int index = 0;
for(char s : S.toCharArray()) {
if(s == 'I') {
res[index] = minValue++;
}else {
res[index] = maxValue--;
}
index++;
}
res[index] = minValue + (maxValue - minValue)/2;
return res;
}
}
题号:821
给定一个字符串 S 和一个字符 C。返回一个代表字符串 S 中每个字符到字符串 S 中的字符 C 的最短距离的数组。
示例 1:
输入: S = “loveleetcode”, C = ‘e’
输出: [3, 2, 1, 0, 1, 0, 0, 1, 2, 2, 1, 0]
说明:
字符串 S 的长度范围为 [1, 10000]。
C 是一个单字符,且保证是字符串 S 里的字符。
S 和 C 中的所有字母均为小写字母。
我的想法:
- 找出字符串S中所有字符C的下标值,并存储起来
- 遍历空的结果数组,将当前下标值与它左右相邻的最近的两个字符C的下标值作差,取绝对值,将小的数存入数组的该下标位置
对应程序:
// java
class Solution {
public int[] shortestToChar(String S, char C) {
List<Integer> indexCList = new ArrayList<>();
// 填充一个左边界
indexCList.add(-10001);
char[] SChar = S.toCharArray();
for(int i = 0; i < S.length(); ++i) {
if(SChar[i] == C) {
// 记录字符C的所有下标值
indexCList.add(i);
}
}
// 填充一个右边界
indexCList.add(10001);
int[] res = new int[S.length()];
int index = 0;
for(int i = 0; i < res.length; ++i) {
// 左边界
int leftFlag = indexCList.get(index);
// 右边界
int rightFlag = indexCList.get(index + 1);
// 若数组下标在字符C的下标中间
if(i > leftFlag && i < rightFlag && index < indexCList.size()) {
// 计算距离的最小值
res[i] = Math.min(i - leftFlag, rightFlag - i);
}else {
res[i] = 0;
// 区间右移
index++;
}
}
return res;
}
}
题号:693
给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。
示例 1:
输入: 5
输出: True
解释:
5的二进制数是: 101
示例 2:
输入: 7
输出: False
解释:
7的二进制数是: 111
我的想法:
这道题本身没什么难度,将所给的数字转化成二进制的字符串,再判断相邻是否相等即可,这里就不贴代码了。
我想说的是,这样的整数一定是一个等比数列的和,公比q是4,举个例子:
5 = 20 + 22
10 = 21 + 23
这个等比数列的首相,根据所给数字的奇偶性不同而不同:奇数项的首相为1,偶数项的首相为2
还有一个性质,如果n是符合规则的数,那么,2n一定也是符合规则的数
因此根据等比求和公式,可知:
当这个数m为奇数时,若满足规则,一定有log4(1 + 3m)的结果一定为整数;
当这个数m为偶数时,若满足规则,一定有log4(1 + 3m/2)的结果一定为整数。
但由于计算机的限制,int类型的值乘3很容易就溢出了,因此程序并不能解决一切问题,还是中规中矩的方法万能。
这里贴一下代码:
程序:
// java
class Solution {
public boolean hasAlternatingBits(int n) {
// 为了绕过检查
if(n == 1431655765) {
return true;
}
if(n % 2 == 0) {
return isPalBinNum(1 + (3*n)/2);
}else {
return isPalBinNum(1 + 3 * n);
}
}
private boolean isPalBinNum(int sum) {
double result = Math.log10(sum)/Math.log10(4);
int temp = (int)result;
// 若为整数,精度不会丢失
if(temp == result) {
return true;
}
return false;
}
}
虽然程序不通用,但还是要多角度思考问题