92. 反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode handle = dummy;
ListNode preLeftNode = null;
ListNode preRightNode = null;
ListNode rightNode = null;
// step1:找到节点
int index = 0;
while (dummy.next != null) {
if (index == left - 1) {
preLeftNode = dummy;
}
if (index == right) {
rightNode = dummy;
}
dummy = dummy.next;
index++;
}
// step2:切断链条
ListNode mid = preLeftNode.next;
preLeftNode.next = null;
ListNode last = rightNode.next;
rightNode.next = null;
// step3:revert
ListNode re = revertNode(mid);
// step4:拼接链条
preLeftNode.next = re;
mid.next = last;
return handle.next;
}
private ListNode revertNode(ListNode node) {
ListNode pre = null;
ListNode cur = node;
while (cur != null) {
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
\
树的遍历
前中后序遍历
public List<Integer> inorderTraversal(TreeNode root) {
final ArrayList<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
result.addAll(inorderTraversal(root.left));
result.add(root.val);
result.addAll(inorderTraversal(root.right));
return result;
}
层次遍历
1.递归
每一层放入List,当前层加到结果集,左右节点加入下一层List,递归调用
private void getLevel(List<TreeNode> nodes) {
if(nodes.isEmpty()) {
return;
}
List<Integer> currentLevel = new ArrayList<>();
List<TreeNode> nextLevel = new ArrayList<>();
for (TreeNode treeNode : nodes) {
if (treeNode !=null) {
currentLevel.add(treeNode.val);
nextLevel.add(treeNode.left);
nextLevel.add(treeNode.right);
}
}
if (!currentLevel.isEmpty()) {
result.add(currentLevel);
}
getLevel(nextLevel);
}
2.BFS
void bfs(TreeNode root) {
Queue<TreeNode> nodes = new ArrayDeque<>();
nodes.add(root);
while (!nodes.isEmpty()) {
// 这一层有多少个!!!
int n = nodes.size();
List<Integer> current = new ArrayList<>();
for (int i = 0; i < n; i++) {
final TreeNode node = nodes.poll();
if (node != null) {
current.add(node.val);
if (node.left != null) {
nodes.add(node.left);
}
if (node.right != null) {
nodes.add(node.right);
}
}
}
result.add(current);
}
}
DFS模板
void dfs(TreeNode root) {
if (root == null) {
return;
}
dfs(root.left);
dfs(root.right);
}
BFS模板
void bfs(TreeNode root) {
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll(); // Java 的 pop 写作 poll()
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
广搜万能模板
前中后序都可以
public static List<Integer> inOrder(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<Object> stack = new Stack<>();
stack.push(root);
while (!stack.empty()) {
Object o = stack.pop();
if (o instanceof TreeNode) {
TreeNode node=(TreeNode) o;
//因为中序遍历是左节点--根节点--右节点
//即出栈顺序为左节点--根节点--右节点,入栈顺序相反
if (node.right != null) {
stack.push(node.right);
}
stack.push(node.val);
if (node.left != null) {
stack.push(node.left);
}
}
else {
res.add((int)o);
}
}
return res;
}
11. 盛最多水的容器
双指针法,否则会超时,谁小往前走一步
34. 在排序数组中查找元素的第一个和最后一个位置
二分法查找
33. 搜索旋转排序数组
找到旋转点,分别进行二分
15. 三数之和
三数之和等于0,一个主节点,遍历,加两个指针
面试题 02.07. 链表相交
正常方法,遍历链表,放到List中,判断第二个链表是否包含,注意包含指相等而不是值相等
高级方法:双指针分别遍历两个链表,有相同元素就是相交了
合法二叉搜索树
不是当前节点是合法的,且左右节点都合法就可以了,要考虑每个节点允许值的范围
public boolean isValidBST(TreeNode root, long minVal, long maxVal) {
if (root == null)
return true;
//每个节点如果超过这个范围,直接返回false
if (root.val >= maxVal || root.val <= minVal)
return false;
//这里再分别以左右两个子节点分别判断,
//左子树范围的最小值是minVal,最大值是当前节点的值,也就是root的值,因为左子树的值要比当前节点小
//右子数范围的最大值是maxVal,最小值是当前节点的值,也就是root的值,因为右子树的值要比当前节点大
return isValidBST(root.left, minVal, root.val) && isValidBST(root.right, root.val, maxVal);
}
547. 省份数量
这是一道并查集的题,通过不断归并父亲完成计算。
并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
- 合并(Union):把两个不相交的集合合并为一个集合。
- 查询(Find):查询两个元素是否在同一个集合中。
当然,这样的定义未免太过学术化,看完后恐怕不太能理解它具体有什么用。所以我们先来看看并查集最直接的一个应用场景:亲戚问题。
200. 岛屿数量
并查集可以做
使用dfs更加便捷,遍历过的地方变成0
private void dfs(char[][] grid, int i, int j){
if(i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
}
463. 岛屿的周长
对于一个岛屿,看他的上下左右4条边,如果不是岛屿,总条数加1
public int islandPerimeter(int[][] grid) {
int res = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
if (i == 0 || grid[i - 1][j] == 0) res++;
if (i == grid.length - 1 || grid[i + 1][j] == 0) res++;
if (j == 0 || grid[i][j - 1] == 0) res++;
if (j == grid[0].length - 1 || grid[i][j + 1] == 0) res++;
}
}
}
return res;
}
1143. 最长公共子序列
动态规划
if (text1.charAt(i-1) == text2.charAt(j-1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
82. 删除排序链表中的重复元素 II
思路一:直接遍历删除元素
public ListNode deleteDuplicates(ListNode head) {
// write code here
if (head == null) {
return head;
}
ListNode current = new ListNode(-1);
current.next = head;
ListNode handle = current;
while (current.next.next != null && current.next != null) {
if (current.next.val == current.next.next.val) {
int target = current.next.val;
while (current.next != null && target == current.next.val) {
current.next = current.next.next;
}
} else {
current = current.next;
}
}
return handle.next;
}
思路二:递归删除
对于A-A-B类型的节点
如果当前节点和下一个节点一样,当前节点指向到下一个不一样的
A-B-B
如果当前节点和下一个不一样,当前节点肯定不需要删除,A.next = deleteDup(a.next)
146. LRU 缓存
自定义双向链表+hashmap实现
hashmap负责判断当前key是否存在
注意点
1.删除元素同时删除map中的
2.增删除元素要维护size数值
class Node {
int key;
int value;
Node pre;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
public Node() {
}
}
int size;
int cap;
HashMap<Integer, Node> map = new HashMap<>();
Node head;
Node tail;
public LRUCache(int capacity) {
cap = capacity;
size = 0;
head = new Node();
tail = new Node();
head.next = tail;
tail.pre = head;
}
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
}
Node node = map.get(key);
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
Node node = map.get(key);
node.value = value;
moveToHead(node);
} else {
if (cap <= size) {
Node removeNode = removeTail();
map.remove(removeNode.key);
size--;
}
final Node node = new Node(key, value);
map.put(key, node);
addToHead(node);
size++;
}
}
private void moveToHead(Node node) {
remove(node);
addToHead(node);
}
private void addToHead(Node node) {
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
}
private void remove(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private Node removeTail() {
final Node res = tail.pre;
remove(res);
return res;
}
使用自带数据结构实现
class LRUCache {
int capacity;
Map<Integer, Integer> map;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new LinkedHashMap<>();
}
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
}
// 先删除旧的位置,再放入新位置
Integer value = map.remove(key);
map.put(key, value);
return value;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
map.remove(key);
map.put(key, value);
return;
}
map.put(key, value);
// 超出capacity,删除最久没用的,利用迭代器删除第一个
if (map.size() > capacity) {
map.remove(map.entrySet().iterator().next().getKey());
}
}
}
剑指 Offer II 095. 最长公共子序列
dp
public int longestCommonSubsequence(String text1, String text2) {
if (text1 == null || text2 == null || text1.length() == 0 || text2.length() == 0) {
return 0;
}
int len = text1.length();
int width = text2.length();
// 多方一个元素 让第一个元素具有普遍性,不需要特殊处理
int[][] dp = new int[len + 1][width + 1];
for (int i = 1; i <= len; i++) {
char c1 = text1.charAt(i - 1);
for (int j = 1; j <= width; j++) {
char c2 = text2.charAt(j - 1);
if (c2 == c1) {
// 非常重要! 获取左上角的
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[len][width];
}
322. 零钱兑换
public int coinChange(int[] coins, int amount) {
// 给一个不可能的值做初值,区分找不到
int max = amount+1;
// 表示当前金额需要的最少硬币个数
int[] dp = new int[amount + 1];
// 赋初值
Arrays.fill(dp, max);
dp[0] = 0;
// 遍历金额 一定要允许 = amount
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i - coins[j]] + 1, dp[i]);
}
}
}
// 返回前确认返回值是否有效
return dp[amount] > amount ? -1 : dp[amount];
}