2018.12.24 leetcode 刷题总结
题号:961
在大小为 2N 的数组 A 中有 N+1 个不同的元素,其中有一个元素重复了 N 次。
返回重复了 N 次的那个元素。
示例 1:
输入:[1,2,3,3]
输出:3
示例 2:
输入:[2,1,2,5,3,2]
输出:2
我的想法:
- 先对数组排序,排序后的数组中出现N次的数字一定位置相邻
- 从头遍历数组,用临时变量与数组中的元素值相比较,若相同则返回该值;若不相同,将数组元素的值赋给临时变量
- 遍历时不必遍历整个数组,由于排序的原因,只遍历数组长度的一半加一个元素即可
对应程序:
// java
class Solution {
public int repeatedNTimes(int[] A) {
// 排序
Arrays.sort(A);
// 临时变量,初始化为数组第一个元素的值
int temp = A[0];
// 遍历数组,注意下标范围[1, A.length/2]
for(int i = 1; i < A.length/2 + 1 ; ++i) {
if(temp == A[i]) {
return temp;
}else {
temp = A[i];
}
}
// 若前面的N项都不相等,则后面的N项一定都是相等的
return A[A.length/2];
}
}
优化:
由于是新题,没有图表可以参考,看过评论后发现一种不用循环比较的办法:
程序:
// java
class Solution {
public int repeatedNTimes(int[] A) {
Arrays.sort(A);
return A[A.length/2] == A[A.length - 1]?A[A.length/2]:A[A.length/2 - 1];
}
}
由于出现N次的数字在经过数组排序后是相邻的,而且他们的长度是数组的一半,因此,若A[A.length - 1] 和 A[A.length/2] 这个两个值是相同的,说明整个数组的后半段都是相同的数字,前半段都是不同的;若不相同时,说明这个数是出现在 [0, A.length - 2] 的范围中的,这个范围的长度是A.length - 1,由于出现N次的数字长度是 A.length/2,因此可以保证,无论这组相同的数字的出现的范围是什么,在数组中下标为 (A.length - 2)/2的数字一定是出现N次的这个数字
如图:
因此,还有一种和A[0]比较的方法,想法和上面相同
程序:
// java
class Solution {
public int repeatedNTimes(int[] A) {
Arrays.sort(A);
return A[0] == A[A.length/2 - 1]?A[0]:A[A.length/2];
}
}
题号:682
你现在是棒球比赛记录员。
给定一个字符串列表,每个字符串可以是以下四种类型之一:
1.整数(一轮的得分):直接表示您在本轮中获得的积分数。
2."+"(一轮的得分):表示本轮获得的得分是前两轮有效 回合得分的总和。
3.“D”(一轮的得分):表示本轮获得的得分是前一轮有效 回合得分的两倍。
4.“C”(一个操作,这不是一个回合的分数):表示您获得的最后一个有效 回合的分数是无效的,应该被移除。
每一轮的操作都是永久性的,可能会对前一轮和后一轮产生影响。
你需要返回你在所有回合中得分的总和。
输入: [“5”,"-2",“4”,“C”,“D”,“9”,"+","+"]
输出: 27
解释:
第1轮:你可以得到5分。总和是:5。
第2轮:你可以得到-2分。总数是:3。
第3轮:你可以得到4分。总和是:7。
操作1:第3轮的数据无效。总数是:3。
第4轮:你可以得到-4分(第三轮的数据已被删除)。总和是:-1。
第5轮:你可以得到9分。总数是:8。
第6轮:你可以得到-4 + 9 = 5分。总数是13。
第7轮:你可以得到9 + 5 = 14分。总数是27。
我的想法:
- 首先想到的数据结构就是栈,题目中定义的操作无非是对栈顶元素操作的扩展
- 遍历string数组,对应不同的元素进行不同的操作,将原始值和中间的结果统统入栈
- 遍历栈,得到结果
对应程序:
// java
class Solution {
public int calPoints(String[] ops) {
// LinkedList可充当栈结构
LinkedList<Integer> stack = new LinkedList<>();
for(String op : ops) {
// 匹配数字,入栈
if(op.matches("^-?[0-9]+$")) {
stack.addFirst(Integer.valueOf(op));
}
// 移除栈顶元素
if(op.equals("C")) {
stack.removeFirst();
}
// 栈顶元素乘2的结果入栈
if(op.equals("D")) {
Integer top = stack.getFirst();
// 可能存在结果溢出
stack.addFirst(top * 2);
}
// 计算位于栈顶的前两个元素,将结果入栈
if(op.equals("+")) {
Integer temp1 = stack.removeFirst();
Integer temp2 = stack.getFirst();
Integer temp3 = temp1 + temp2;
stack.addFirst(temp1);
stack.addFirst(temp3);
}
}
int result = 0;
// 遍历栈
for(Integer num : stack) {
result += num;
}
return result;
}
}
优化
LinkedList底层的数据结构是基于双向循环链表的,因此遍历它会非常的费时,我们考虑用数组来实现,并且将if…else的判断换成switch…case的方式
程序:
// java
class Solution {
public int calPoints(String[] ops) {
int index = -1;
int[] stack = new int[ops.length];
for(String str : ops) {
switch(str) {
case "C" :
// 相当于删除元素
stack[index] = 0;
// 指针前移
index--;
break;
case "D" :
// 计算要填充到数组中的值
int mul = stack[index] * 2;
// 指针后移
index++;
// 写入数组
stack[index] = mul;
break;
case "+" :
// 计算下标最大的两个元素的和
int sum = stack[index] + stack[index-1];
// 指针右移
index++;
// 写入数组
stack[index] = sum;
break;
// 其他情况,即为数字时
default :
// 一定是将数字添加到数组中的操作,先右移指针
index++;
// 写入数组
stack[index] = Integer.parseInt(str);
break;
}
}
int result = 0;
// 遍历数组计算结果
for(int num : stack) {
result += num;
}
return result;
}
}
说明:以上程序是指针始终指向数组最后一个元素,因此在将新的元素写入数组时,先进行指针右移的操纵,再写入值,这也是为什么下标的初始值是-1