leetcode刷题记录(852、942、821、693)

本文是2019年1月7日的LeetCode刷题总结,涉及题目包括:852-山脉数组的最大值索引、942-字符串的增减排列、821-最短距离数组、693-交替位二进制数。文章详细介绍了每道题目的解题思路、程序实现及优化方法。
摘要由CSDN通过智能技术生成

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 中的所有字母均为小写字母。

我的想法:

  1. 找出字符串S中所有字符C的下标值,并存储起来
  2. 遍历空的结果数组,将当前下标值与它左右相邻的最近的两个字符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;
    }
}

虽然程序不通用,但还是要多角度思考问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值