1.两数之和
import java.util.HashMap;
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
return new int[]{map.get(target - nums[i]), i};
} else {
map.put(nums[i], i);
}
}
return null;
}
}
3.无重复字符的最长子串
import java.util.HashMap;
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> map = new HashMap<>();
int begin = 0;
int maxLength = 0;
for (int end = 0; end < s.length(); end++) {
char c = s.charAt(end);
if (map.containsKey(c)) {
begin = Math.max(begin,map.get(c)+1);
map.put(c, end);
} else {
map.put(c, end);
}
maxLength = Math.max(maxLength, s.substring(begin, end + 1).length());
}
return maxLength;
}
}
要点:
1.用begin和end表示子串开始和结束位置
2.用hash表检查重复字符
3.从左向右查看每个字符,如果:
没遇到重复字符,调整end
遇到重复的字符,调整begin
将当前字符放入hdsh表
4.end-begin+1是当前子串长度
20.有效的括号
class Solution {
char[] array = new char[10000];
int top = 0;
public boolean push(char value) {
if (isFull()) {
return false;
}
array[top] = value;
top++;
return true;
}
public char pop() {
char value = array[top - 1]; // 注意 pop 和 peek 时,top 要-1
top--;
return value;
}
public char peek() {
char value = array[top - 1];
return value;
}
public boolean isEmpty() {
return top == 0;
}
public boolean isFull() {
return top == array.length;
}
public boolean isValid(String s) {
Solution stack = new Solution();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(')
stack.push(')');
else if (c == '[')
stack.push(']');
else if (c == '{')
stack.push('}');
else {
if (!stack.isEmpty() && c == stack.peek()) { // 注意 if 判断栈是否空
stack.pop();
} else
return false;
}
}
return stack.isEmpty(); // 最终栈空才是有效的括号,非空证明有多余的括号
}
}
用栈处理,遇到左括号就把对应的右括号入栈
遇到右括号就 peek 栈顶元素,看看他们是否相同,如果相同,则证明这对括号有效,pop 弹出
如果不相同,就说明这对括号无效,有一对括号无效直接返回 false
21.合并两个有序链表
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode mergeList = new ListNode(-1, null);
ListNode p1 = list1;
ListNode p2 = list2;
ListNode p = mergeList;
while (p1 != null && p2 != null) {
if (p1.val <= p2.val) {
p.next = new ListNode(p1.val, p.next);
p1 = p1.next;
} else {
p.next = new ListNode(p2.val, p.next);
p2 = p2.next;
}
p = p.next;
}
if (p1 == null) {
p.next = p2;
} else {
p.next = p1;
}
mergeList = mergeList.next;
return mergeList;
}
}
创建一个新链表 mergeList ,双指针,list1 list2 一个链表一个指针,小的元素放入新链表
来自哪个链表哪个指针向前走一步,当有一个指针为空,退出循环,把另一个表整体给新链表接上
最后链表要排掉最开始设置的哨兵 -1
83.删除排序链表中的重复元素
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode sentinel = new ListNode(Integer.MIN_VALUE, head);
ListNode p1 = sentinel;
ListNode p2 = head;
while (p2 != null) {
if (p1.val == p2.val) {
p2 = p2.next;
p1.next = p2;
continue;
}
p2 = p2.next;
p1 = p1.next;
}
return sentinel.next;
}
}
带哨兵的单链表,双指针
136.只出现一次的数字
import java.util.HashSet;
class Solution {
public int singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if (!set.add(nums[i])) {
set.remove(nums[i]);
}
}
return (int) set.toArray()[0];
}
}
Q:为什么不用 HashMap ?
A:因为最后需要输出一个 int 整型,而 HashMap 最后转数组再转 int 很麻烦,而且占用空间也 大,用 HashSet 则很方便,而且 HashSet 不允许有重复的元素,非常适合
141.环形链表
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode p1 = head;
ListNode p2 = head;
while (p2 != null && p2.next != null) {
// if(p1 == p2)
// return true;
// p1 = p1.next;
// p2 = p2.next.next;
p1 = p1.next;
p2 = p2.next.next;
if (p1 == p2)
return true;
}
return false;
}
}
【快慢指针】
如果快指针 p2 走完全程,说明 head 链表不是环形链表
如果两个快慢指针相遇,说明 head 链表是环形链表
注意要先走后判断,否则初始状态就是相遇状态
150.逆波兰表达式求值
【逆波兰表达式】(Reverse Polish Notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)
class Solution {
private int[] array = new int[10000];
private int top = 0; // 栈顶指针
public boolean push(int value) {
if (isFull()) {
return false;
}
array[top] = value;
top++;
return true;
}
public int pop() {
int value = array[top - 1]; // 注意 pop 和 peek 时,top 要-1
top--;
return value;
}
public int peek() {
int value = array[top - 1];
return value;
}
public boolean isEmpty() {
return top == 0;
}
public boolean isFull() {
return top == array.length;
}
public int evalRPN(String[] tokens) {
char[] array = new char[10000]; // 自带的数组没用上
int top = 0;
Solution stack = new Solution();
for (String t : tokens) {
switch (t) {
case "+" -> {
int a = stack.pop();
stack.push(stack.pop() + a);
break;
}
case "-" -> {
int a = stack.pop();
stack.push(stack.pop() - a);
break;
}
case "*" -> {
int a = stack.pop();
stack.push(stack.pop() * a);
break;
}
case "/" -> {
int a = stack.pop();
stack.push(stack.pop() / a);
break;
}
default -> {
stack.push(Integer.parseInt(t));
break;
}
}
}
return stack.pop();
}
}
遇到数字就 Integer.parseInt 转换成 int 入栈
遇到运算符就出栈两个元素,再把运算结果入栈
203.移除链表元素
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode sentinel = new ListNode(-1, head); // 哨兵
ListNode prev = sentinel;
ListNode curr = head;
while (curr != null) {
if (curr.val == val) {
curr = curr.next;
prev.next = curr;
continue;
}
curr = curr.next;
prev = prev.next;
}
return sentinel.next;
// return head;
}
}
利用带哨兵的单链表和双指针(prev 和 curr),依次遍历,当 curr 当前结点的值和 val 相等,则令 curr 指向下一个结点,并令 prev 指向 curr,把中间的结点跳过
但是如果最终 return 的是 head ,则不能通过 head = [7,7,7,7],val=7 用例(待解决)
206.反转链表
class Solution {
public ListNode reverseList(ListNode head) {
ListNode list = null;
ListNode curr = head;
while (curr != null) {
list = new ListNode(curr.val, list);
curr = curr.next;
}
return list;
}
}
利用“头插法”( addFirst 方法)
217.存在重复元素
import java.util.HashMap;
class Solution {
public boolean containsDuplicate(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
return true;
} else {
map.put(nums[i], i);
}
}
return false;
}
}
不停把元素放进哈希表,如果重复就 return true,否则 false
819.最常见的单词
(p.s. 代码先放这里,实在是看不懂)
1.将 paragraph 截取为一个个单词
用的是 split(string regex) 方法
2.将单词加入 map 集合,单词本身作为 key ,出现次数作为 valve ,避免禁用词加入
3.在map集合中找到 valve 最大的,返回它对应的 key 即可
234.回文链表
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode p1 = head;
ListNode p2 = head;
while (p2 != null && p2.next != null) {
p1 = p1.next;
p2 = p2.next.next;
}
// ListNode headReverse = new ListNode(-1,null);
ListNode headReverse = null;
while (p1 != null) {
// headReverse = new ListNode(p1.val, headReverse.next);
headReverse = new ListNode(p1.val, headReverse);
p1 = p1.next;
}
p1 = head;
p2 = headReverse;
while (p1 != null && p2 != null) {
if (p1.val != p2.val) {
return false;
}
p1 = p1.next;
p2 = p2.next;
}
return true;
}
}
注释掉的代码为原来写的代码,不能通过用例[1,1,2,1],原因可能是 headReverse 链表中,初始了一个 val=-1 的结点,在与 head 链表比较的时候,会参与比较,从而输出 true
所以创建一个新链表的时候,直接初始为 null,后续循环指向自己,而不是 next
876.链表的中间结点
class Solution {
public ListNode middleNode(ListNode head) {
ListNode p1 = head;
ListNode p2 = head;
while (p2 != null && p2.next != null) {
p1 = p1.next;
p2 = p2.next.next;
}
return p1;
}
}
【快慢指针】
p1 慢指针 一次走一步
p2 快指针 一次走两步
这样 p2 为 null 的时候,p1 正好在中间
注意 while 循环判断的条件