2019.1.23 leetcode 刷题总结
题号:137
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
我的想法:
- 将所给数组排序,索引index从0开始,比较index和index+1位置上的数是否相同,若相同,index = index + 3,跳到下一组去校验;若不同,返回nums[index];
- 上面index的范围在[0,nums.length - 1],若上面没返回,说明在结果在排序后的数组的最后一位,返回nums[nums.length - 1]。
对应程序:
// java
class Solution {
public int singleNumber(int[] nums) {
Arrays.sort(nums);
for(int i = 0; i < nums.length - 1; i += 3) {
if(nums[i] != nums[i + 1]) {
return nums[i];
}
}
return nums[nums.length - 1];
}
}
题号:303
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
示例:
给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
说明:
你可以假设数组不可变。
会多次调用 sumRange 方法。
我的想法:
- 把构造函数中传进来的数组传递给该类的一个变量,在sumRange()方法中相加返回,程序代码就不贴了
优化:
- 肯定不会是上面这么简单的想法,题目中说明会多次调用sumRange()方法,因此,我们可以在构造函数中,将所有的前i项(i >= 0 && i <= nums.length - 1)的和缓存(这里用数组sum)起来,在调用sumRange()方法时,直接返回sum[j] - sum[i - 1]就可以
程序:
// java
class NumArray {
private int[] sum;
public NumArray(int[] nums) {
sum = new int[nums.length];
for(int i = 0; i < sum.length; ++i) {
if(i == 0) {
sum[i] = nums[i];
}else {
sum[i] = sum[i - 1] + nums[i];
}
}
}
public int sumRange(int i, int j) {
if(i == 0) {
return sum[j];
}else {
return sum[j] - sum[i - 1];
}
}
}
题号:950
牌组中的每张卡牌都对应有一个唯一的整数。你可以按你想要的顺序对这套卡片进行排序。
最初,这些卡牌在牌组里是正面朝下的(即,未显示状态)。
现在,重复执行以下步骤,直到显示所有卡牌为止:
从牌组顶部抽一张牌,显示它,然后将其从牌组中移出。
如果牌组中仍有牌,则将下一张处于牌组顶部的牌放在牌组的底部。
如果仍有未显示的牌,那么返回步骤 1。否则,停止行动。
返回能以递增顺序显示卡牌的牌组顺序。
答案中的第一张牌被认为处于牌堆顶部
示例:
输入:[17,13,11,2,3,5,7]
输出:[2,13,3,11,5,17,7]
解释:
我们得到的牌组顺序为 [17,13,11,2,3,5,7](这个顺序不重要),然后将其重新排序。
重新排序后,牌组以 [2,13,3,11,5,17,7] 开始,其中 2 位于牌组的顶部。
我们显示 2,然后将 13 移到底部。牌组现在是 [3,11,5,17,7,13]。
我们显示 3,并将 11 移到底部。牌组现在是 [5,17,7,13,11]。
我们显示 5,然后将 17 移到底部。牌组现在是 [7,13,11,17]。
我们显示 7,并将 13 移到底部。牌组现在是 [11,17,13]。
我们显示 11,然后将 17 移到底部。牌组现在是 [13,17]。
我们展示 13,然后将 17 移到底部。牌组现在是 [17]。
我们显示 17。
由于所有卡片都是按递增顺序排列显示的,所以答案是正确的。
我的想法:
- 开始想从结果数组找规律,只发现偶数索引上的数值是从小到大,奇数索引的规律有点乱,放弃。
- 看了评论,有一种数据结构是Deque,双向队列,在队列的两端都可以进行入队和出队
- 从结果往回推,以本题为例:
- 图片中左边是正在递增的数组,右边是正在减少的结果数组,从上到下,就是题目中给出的示例的过程,我们要做的就是从下到上,将结果数组还原回去
- 从下到上观察右侧数组,它的元素是从一个递增的数组的末尾元素添加得到的,当右边数组元素个数小于1时,新增加的元素事直接插入到数组前面的,随着数组中元素个数的增加,在新元素添加到右边数组的头部时,要先将现有数组中的最后一个元素移至头部,再将新元素插入
- 当然,存在这么多移动元素,插入元素的操作,肯定不要用数组,用的数据结构就是上面提到的Deque
对应程序:
// java
class Solution {
public int[] deckRevealedIncreasing(int[] deck) {
// 将原数组排序
Arrays.sort(deck);
// 双端队列Deque
Deque<Integer> deque = new LinkedList<>();
for(int i = deck.length - 1; i >= 0; --i) {
if(deque.size() < 2) {
// offerFirst:将元素添加在首部
deque.offerFirst(deck[i]);
}else {
// 先将原队列中的最后一个元素放到队列的首部
// pollLast:将最后一个元素弹出弹出队列
deque.offerFirst(deque.pollLast());
// 再将新元素插入到队列的首部
deque.offerFirst(deck[i]);
}
}
int[] res = new int[deck.length];
for(int i = 0; i < res.length; ++i) {
res[i] = deque.pollFirst();
}
return res;
}
}
题号:125
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
示例 2:
输入: “race a car”
输出: false
我的想法:
- 将字符串中除小写字母,大写字母,数字之外的字符全部去掉,得到一个新的字符串;
- 将新的字符串翻转,与翻转前相比较(忽略大小写),若相同返回true;否则返回false;
- 或者可以不反转字符串,利用双指针:一个指向头,一个指向尾,比较对应位置上的字符是否相同(忽略大小写),若有不同,返回false;否则指针向中间移动;
对应程序:
// java
class Solution {
// 解法1,对应我的想法2
public boolean isPalindrome(String s) {
StringBuilder res = new StringBuilder();
for(char c :s.toCharArray()) {
if(c < 48 || (c > 57 && c < 65) || (c > 90 && c < 97) || c > 122) {
continue;
}
res.append(c);
}
return res.toString().equalsIgnoreCase(res.reverse().toString());
}
// 解法2:对应我的想法3
public boolean isPalindrome1(String s) {
StringBuilder temp = new StringBuilder();
for(char c : s.toCharArray()) {
if(c < 48 || (c > 57 && c < 65) || (c > 90 && c < 97) || c > 122) {
continue;
}
temp.append(Character.toLowerCase(c));
}
int start = 0;
int end = temp.length() - 1;
while(start < end) {
if(temp.charAt(start) != temp.charAt(end)) {
return false;
}
start++;
end--;
}
return true;
}
}