151.反转字符串的单词
该题目有点类似于字符串的反转,但是该题目需要保证的是字符串内的单词反转后仍然是原先单词,并且格式以“单词1 单词2 单词3”的格式(即需要去除多余空格)
针对于这么频繁的字符串操作我们自然就想到使用的是StringBuilder类
刚好他自带了一个reverse()方法用于字符串反转,剩下的部分我们只需要对空格进行处理和内部单词的顺序进行处理。
1.空格部分我是用的计数法,对多次重复出现的空格采取删除。
2.内部单词顺序我采用正常的字符串反转的方法,对其反转,则需要精准截取字符串即可。
//字符串中的单词替换
public String reverseWords(String s) {
//去除头部空格
s = s.trim();
//去除尾部空格(从尾部开始查找)
int end = s.length()-1;
for(;end>=0; end--) {
if(s.charAt(end) != ' ') {
break;
}
}
s = s.substring(0,end + 1);
//反转整个字符串
StringBuilder sb = new StringBuilder(s).reverse();
//针对每个单词在进行反转(此时的单词内部顺序也被反转)
reverseWord(sb);
return sb.toString();
}
//用于反转单词
public void reverseWord(StringBuilder sb) {
int start = 0;
int end = 0;
//记录空格数
int times = 0;
while(end < sb.length()) {
if(sb.charAt(end) == ' ') {
//第一次出现空格
if(times < 1) {
//单词反转(此时end为空格)
reverseWord(sb,start,end - 1);
times++;
//指针移动
end++;
start = end;
}else { //删除多余空格
sb.deleteCharAt(end);
}
}else {
//空格清零
times = 0;
//单词右边界移动
end++;
}
}
if(end > start) {
//此时end到达右边界,所以需要 - 1
reverseWord(sb,start,end - 1);
}
}
//反转单个字符
private void reverseWord(StringBuilder sb, int left, int right) {
while(left < right) {
char temp = sb.charAt(left);
sb.setCharAt(left,sb.charAt(right));
sb.setCharAt(right,temp);
left++;
right--;
}
}
152.乘积最大的子数组
该题目特点就在于他是求乘积最大。
众所周知,使乘积增大有很多中情况,比如正数 * 正数,负数 * 负数。
我们无法的值究竟是哪一种才是最大值,所以我们采用两个dp数组,分别保存最大值和最小值,用于判断。
//乘积最大子数组(动态规划)
public int maxProduct(int[] nums) {
int ans = nums[0];
//维护一个最大值和最小值的dp数组
int[] max = new int[nums.length];
int[] min = new int[nums.length];
max[0] = nums[0];
min[0] = nums[0];
for(int i=1; i<nums.length; i++) {
max[i] = Math.max(nums[i],Math.max(max[i - 1] * nums[i], min[i - 1] * nums[i]));
min[i] = Math.min(nums[i],Math.min(max[i - 1] * nums[i], min[i - 1] * nums[i]));
ans = Math.max(max[i],ans);
}
return ans;
}
153.寻找旋转排序数组的最小值
因为原数组是升序排序数组,经过一定旋转后就改变了模样了。
我们可以采用二分法,对这个旋转数组进行二分查找。
既然选择了二分法那么就需要考虑需要将数组二分。
重点来了:一开始需要想清楚,
1.如果数组整体有序,那么最小值一定在最左边。
2.如果有旋转部分,则最小值一定出现在被旋转的半边。
并且无序半边的话,一定是这种模式 [大1,大2,最小值,小1,小2,小3],所以我们可以将有序半边直接舍去,并且舍弃最左边的高一位
//寻找旋转数组的最小值(二分法)
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right) {
int mid = left + (right - left) / 2;
//左半边有序
if(nums[left] <= nums[mid]) {
if(nums[left] < nums[right]) { //说明是整体有序
return nums[left];
}else { //说明右半部分存在反转,则此时left不为最小值
left = mid + 1;
}
}else { //左半边无序(最小值一定存在于无序中)
//舍去最左边的高一位
left++;
//舍去有序半边
right = mid;
}
}
return nums[left];
}
155.最小栈
题目是常数实现返回栈内最小值。
该题目就是一个对比的思想,用两个栈实现,一个栈用于保存数组,一个栈用于保存栈内最小值(getMin()时直接peek栈顶即可)。
思路是我们在push入栈时,和min栈进行对比,如果比min更小则入min栈,如果比min大则仍旧再入一次min的栈顶元素。
/*
最小栈,要求在时间内检索栈内最小值
*/
class MinStack {
//记录最小值
private Stack<Integer> min = new Stack<>();
private Stack<Integer> stack = new Stack<>();
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
//更新最小值
if(min.isEmpty()) {
min.push(x);
}else {
if(x < min.peek()) {
min.push(x);
}else { //无需更新
min.push(min.peek());
}
}
stack.push(x);
}
public void pop() {
stack.pop();
min.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return min.peek();
}
}
160.相交链表
使用hashSet对一条链表进行保存,访问另一条链表时,如果出现第一次重复则直接返回。
//求链表交点
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> hashSet = new HashSet<>();
while(headA != null) {
hashSet.add(headA);
headA = headA.next;
}
while(headB != null) {
if (hashSet.contains(headB)) {
return headB;
}
headB = headB.next;
}
return null;
}