142. 环形链表 II

思路:快慢指针,快慢指针相遇后,慢指针回到头,快慢指针步伐一致一起移动,相遇点即为入环点

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
// 没有结点或只有一个非自环结点
if (head == null || head.next == null) {
return null;
} ListNode slow = head;
ListNode fast = head.next; while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next; if (fast == slow) {
//快指针从第一次相交点下一个节点开始移动, 慢指针重新从头开始移动
fast = fast.next; //注意
slow = head; while (fast != slow) {
fast = fast.next;
slow = slow.next;
} // 相遇点即为入环点
return fast;
}
} return null;
}
}

146. LRU 缓存机制

class LRUCache {
// 用 map 我们可以实现常数复杂度的get,但无法记录各个页面的先后顺序。
// 因此引入双向链表来记录页面先后顺序。 private Map<Integer, Node> map;
private Node head;
private Node tail;
private int capacity;
private int size = 0; public LRUCache(int capacity) {
this.map = new HashMap<>();
this.head = new Node(-1, -1);
this.tail = new Node(-1, -1);
head.next = tail;
tail.pre = head;
this.capacity = capacity;
} public int get(int key) {
if (map.containsKey(key)) {
Node temp = map.get(key);
moveToHead(temp);
return temp.val;
} else {
return -1;
}
} public void put(int key, int value) {
if (map.containsKey(key)) {
Node temp = map.get(key);
temp.val = value;
moveToHead(temp);
} else {
if (size >= capacity) {
Node tempTail = removeTail();
int tempKey = tempTail.key;
map.remove(tempKey);
size--;
} Node newNode = new Node(key, value);
map.put(key, newNode);
addHead(newNode);
size++;
}
} private void moveToHead(Node node) {
removeNode(node);
addHead(node);
} private Node removeTail() {
Node temp = tail.pre;
removeNode(temp); return temp;
} private void removeNode(Node node) {
node.next.pre = node.pre;
node.pre.next = node.next;
} private void addHead(Node node) {
node.next = head.next;
node.pre = head; head.next = node;
node.next.pre = node;
}
} class Node {
Node pre;
Node next;
int key;
int val; Node(int key, int val) {
this.key = key;
this.val = val;
}
} /**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/

148. 排序链表

思路:O(nlogn)的复杂度实现排序可以考虑 归并 或 快速 排序。

归并排序:

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head);
} private ListNode mergeSort(ListNode head) {
// base case
if (head == null || head.next == null) {
return head;
} ListNode mid = getMidNode(head);
ListNode temp = mid.next;
mid.next = null; ListNode l1 = mergeSort(head);
ListNode l2 = mergeSort(temp); return merge(l1, l2);
} // 快慢指针找中点
private ListNode getMidNode(ListNode head) {
ListNode slow = head;
ListNode fast = head.next; while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
} return slow;
} // 合并两个链表
private ListNode merge(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode temp = dummy; while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
temp.next = l1;
l1 = l1.next;
} else {
temp.next = l2;
l2 = l2.next;
} temp = temp.next;
} if (l1 != null) {
temp.next = l1;
} else if (l2 != null) {
temp.next = l2;
} return dummy.next;
}
}

快速排序:

class Solution {
public ListNode sortList(ListNode head) {
return quickSort(head, null);
} // start, end 之间进行快速排序,左闭右开
private ListNode quickSort(ListNode start, ListNode end) {
// base case
if (start == end || start.next == end) {
return start;
}
// 选择第一个结点为pivot
ListNode pivot = start;
// left, right 都指向pivot
ListNode left = pivot, right = pivot; // 遍历 pivot 之后,end之前的所有结点,将小于 pivot 的放到 pivot 之前;否者放到 pivot 后面
ListNode cur = pivot.next;
while (cur != end) {
// 下面的插入操作会修改 cur.next
ListNode next = cur.next; if (cur.val < pivot.val) {
cur.next = left;
left = cur;
} else {
right.next = cur;
right = cur;
} cur = next;
} // 重要,将操作好的链表的末尾指向 end
right.next = end; ListNode pre = quickSort(left, pivot);
ListNode post = quickSort(pivot.next, end);
// 重要,前后两段链表通过 pivot 连接起来
pivot.next = post; return pre;
}
}

152. 乘积最大子数组

class Solution {
public int maxProduct(int[] nums) {
int len = nums.length; // 状态: dp[i][0]:以下标i结尾的子数组的最大乘积
// dp[i][1]:以下标i结尾的子数组的最小乘积
int[][] dp = new int[len][2]; //base case
dp[0][0] = nums[0];
dp[0][1] = nums[0]; int max = nums[0];
for (int i = 1; i < len; i++) {
if (nums[i] > 0) {
dp[i][0] = Math.max(nums[i], nums[i] * dp[i - 1][0]);
dp[i][1] = Math.min(nums[i], nums[i] * dp[i - 1][1]);
} else {
dp[i][0] = Math.max(nums[i], nums[i] * dp[i - 1][1]);
dp[i][1] = Math.min(nums[i], nums[i] * dp[i - 1][0]);
} max = Math.max(max, dp[i][0]);
} return max;
}
}

155. 最小栈

class MinStack {

    /** initialize your data structure here. */
private Deque<Integer> stack;
// 用额外一个栈存储最小值
private Deque<Integer> minstack;
public MinStack() {
stack = new LinkedList<>();
minstack = new LinkedList<>();
} public void push(int val) {
stack.push(val);
if (minstack.isEmpty() || val < minstack.peek()) {
minstack.push(val);
} else {
minstack.push(minstack.peek());
}
} public void pop() {
stack.pop();
minstack.pop();
} public int top() {
return stack.peek();
} public int getMin() {
return minstack.peek();
}
} /**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/

160. 相交链表

思路:指向两个链表的指针一起分别向后移,如果没有剩余元素就移动到另一条链表,然后继续后移。如果有交点,相遇时就不为null

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode lA = headA, lB = headB; while (lA != lB) {
lA = lA == null ? headB : lA.next;
lB = lB == null ? headA : lB.next;
} return lA;
}
}

169. 多数元素

思路一:用map记录每个元素的出现次数:

class Solution {
public int majorityElement(int[] nums) {
int threshold = nums.length / 2; Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
if (map.get(num) > threshold) {
return num;
}
} throw new IllegalArgumentException("majority element not exist!");
}
}

思路二:排序:

class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}

思路三:摩尔投票,很简单:记录当前字符和出现频次,如果字符相同则频次加1,否则减1,减1后若频次为0则直接更换当前字符和频次。

class Solution {
public int majorityElement(int[] nums) {
int curElement = nums[0];
int cnt = 1; for (int i = 1; i < nums.length; i++) {
if (curElement == nums[i]) {
cnt++;
} else {
cnt--;
if (cnt == 0) {
curElement = nums[i];
cnt = 1;
}
}
} return curElement;
}
}

198. 打家劫舍

思路:动态规划

class Solution {
public int rob(int[] nums) {
int len = nums.length;
//状态: dp[i] 表示 有i间房屋时可以偷窃的最大金额
int[] dp = new int[len + 1]; //base case
dp[0] = 0;
dp[1] = nums[0]; int max = nums[0];
for (int i = 2; i < len + 1; i++) {
// 第 i - 1 个房间 可以偷, 也可以不偷
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
max = Math.max(max, dp[i]);
} return max;
}
}

200. 岛屿数量

思路:DFS。

  • 对于二维矩阵中象成一个对每个节点来说,他有上、下、左、右四个邻居,可以将每个岛屿都看成一个图。
  • 从任意一个陆地进入开始遍历,遍历完1次就代表发现了一个岛屿。 注:图不像树那样是有向的,遍历可能会访问重复结点,一般需要用额外结构表示结点是否已经被访问过。此题可以直接在矩阵上将1修改为2表示结点已经访问过。

在原矩阵中标记是否访问过:

class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') {
dfs(grid, i, j);
count++;
}
}
} return count;
} private void dfs(char[][] grid, int row, int col) {
//base case
if (!inArea(grid, row, col)) {
return;
} if (grid[row][col] != '1') {
return;
} //已访问
grid[row][col] = '2'; dfs(grid, row + 1, col);
dfs(grid, row - 1, col);
dfs(grid, row, col + 1);
dfs(grid, row, col - 1);
} private boolean inArea(char[][] grid, int row, int col) {
return row >= 0 && row < grid.length &&
col >= 0 && col < grid[0].length;
}
}

使用额外空间标记是否已经访问过:

class Solution {

    // 标记是否访问过
private boolean[][] visited; public int numIslands(char[][] grid) {
int row = grid.length;
int col = grid[0].length;
visited = new boolean[row][col]; int cnt = 0; for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (grid[i][j] == '1' && !visited[i][j]) {
dfs(grid, i, j);
cnt++;
}
}
} return cnt;
} private void dfs (char[][] grid, int i, int j) {
if (!inArea(grid, i, j)) {
return;
} if (grid[i][j] != '1' || visited[i][j]) {
return;
} visited[i][j] = true; dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
} private boolean inArea(char[][] grid, int row, int col) {
return row >= 0 && row < grid.length &&
col >= 0 && col < grid[0].length;
}
}

推荐阅读:岛屿类问题的通用解法、DFS 遍历框架最大人工岛

206. 反转链表

思路一:迭代法:

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
//始终指向当前结点的前一个结点
ListNode pre = null;
//当前结点
ListNode cur = head; while (cur != null) {
//用来保存当前结点的下一个结点
ListNode next = cur.next; cur.next = pre;
pre = cur; cur = next;
} return pre;
}
}

思路二:递归(锻炼递归思维):

  • 首先给出函数定义,如此题:ListNode reverseList(ListNode head)

    1. 反转以head为头结点的链表
    2. 返回反转后链表的头结点
  • 不要用脑袋去模拟递归栈,根据函数的定义去处理递归的子问题,即具体到一个结点要做的事情

  • 确定base case

class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) {
return head;
} //base case, 当链表只有一个结点时退出递归
if (head.next == null) {
return head;
} /**
* 具体到头结点来说,反转当前链表只需要两步:
* 1.反转以head.next为头的链表
* 2.将head插入head.next为头的链表反转之后的链表末尾
*/
ListNode vhead = reverseList(head.next); //根据递归函数定义,返回反转之后的链表头 //反转之后head.next位于链表尾部,将head插入head.next之后
head.next.next = head;
head.next = null; return vhead;
}
}

🔥 LeetCode 热题 HOT 100(51-60)的更多相关文章

  1. LeetCode 热题 HOT 100(05,正则表达式匹配)

    LeetCode 热题 HOT 100(05,正则表达式匹配) 不够优秀,发量尚多,千锤百炼,方可成佛. 算法的重要性不言而喻,无论你是研究者,还是最近比较火热的IT 打工人,都理应需要一定的算法能力 ...

  2. 🔥 LeetCode 热题 HOT 100(81-90)

    337. 打家劫舍 III 思路:后序遍历 + 动态规划 推荐题解:树形 dp 入门问题(理解「无后效性」和「后序遍历」) /** * Definition for a binary tree nod ...

  3. 🔥 LeetCode 热题 HOT 100(71-80)

    253. 会议室 II(NO) 279. 完全平方数 class Solution { public int numSquares(int n) { // dp[i] : 组成和为 i 的最少完全平方 ...

  4. 🔥 LeetCode 热题 HOT 100(31-40)

    75. 颜色分类 思路:将 2 往后放,0 往前放,剩余的1自然就放好了. 使用双指针:left.right 分别指向待插入的 0 和 2 的位置,初始 left 指向数组头,right 指向数组尾部 ...

  5. 🔥 LeetCode 热题 HOT 100(21-30)

    46. 全排列 思路:典型回溯法 class Solution { public List<List<Integer>> permute(int[] nums) { Linke ...

  6. 🔥 LeetCode 热题 HOT 100(61-70)

    207. 课程表 思路:根据题意可知:当课程之间不存在 环状 循环依赖时,便能完成所有课程的学习,反之则不能.因此可以将问题转换成: 判断有向图中是否存在环.使用 拓扑排序法 : 构建 入度表:记录每 ...

  7. 🔥 LeetCode 热题 HOT 100(41-50)

    102. 二叉树的层序遍历 思路:使用队列. /** * Definition for a binary tree node. * public class TreeNode { * int val; ...

  8. 🔥 LeetCode 热题 HOT 100(11-20)

    20. 有效的括号 class Solution { public boolean isValid(String s) { Map<Character, Character> map = ...

  9. 🔥 LeetCode 热题 HOT 100(1-10)

    1. 两数之和 思路一:暴力遍历所有组合 class Solution { public int[] twoSum(int[] nums, int target) { for (int i = 0; ...

随机推荐

  1. 日志挖掘针对DML语句

    作用: 针对用户的误操作,比如更改数据错误,误删除表等,可以用日志挖掘的方式,跟踪哪个用户什么时候做的操作,并进行数据还原. 一.前期准备: 1.添加最小补充日志,能够记录到更详细的信息,为日志挖掘分 ...

  2. 测试MySQL锁的问题

    测试MySQL锁的问题 目录 测试MySQL锁的问题 1 Record Lock 2 Next-Key Lock 2 死锁测试 InnoDB支持三种行锁: Record Lock:单个行记录上面的锁 ...

  3. vue中使用element-ui出现Couldn't find preset "es2015" relative to directory

    这是因为没有安装ES 标准 使用 npm install babel-preset-es2015 -d 安装之后就好了

  4. JavaScript 沙盒模式

    微前端已经成为前端领域比较火爆的话题,在技术方面,微前端有一个始终绕不过去的话题就是前端沙箱 什么是沙箱 Sandboxie(又叫沙箱.沙盘)即是一个虚拟系统程序,允许你在沙盘环境中运行浏览器或其他程 ...

  5. NUC980 运行 RT-Thread 时使用 GPIO

    如何使用 GPIO? NuMaker-RTU-NUC980 板子引出的 IO 有: 分别有一个 I2C1.GPIO.SPI0.UART4,RT-Thread 中 NuMaker-RTU-NUC980 ...

  6. Unity3D 本地数据持久化几种方式

    下面介绍几种 Unity本地记录存储的实现方式. 第一种 Unity自身提供的 PlayerPrefs //保存数据 PlayerPrefs.SetString("Name",mN ...

  7. Kubernetes网络的iptables模式和ipvs模式支持ping分析

    1.iptables模式无法ping通原因分析 iptables模式下,无法ping通任何svc,包括clusterip.所有ns下,下面来分析原因: 查看kubernetes的网络模式 curl 1 ...

  8. Custom Controller CollectionQT样式自定义 001 :SliderLineEdit 滑动输入框

    主要是继承QLineEdit类重新实现其鼠标事件,建议禁用输入框默认的菜单项. SliderLineEdit 滑动输入框 参照图形平台 Adobe系列中属性输入框 做的样式,支持点击编辑和长按鼠标拖动 ...

  9. 27、Tomcat服务的安装与配置

    服务器名称 ip地址 slave-node1 172.16.1.91 27.1. Tomcat简介: Tomcat是Apache软件基金会(Apache Software Foundation)的Ja ...

  10. .Net Core with 微服务 - Consul 配置中心

    上一次我们介绍了Elastic APM组件.这一次我们继续介绍微服务相关组件配置中心的使用方法.本来打算介绍下携程开源的重型配置中心框架 apollo 但是体系实在是太过于庞大,还是让我爱不起来.因为 ...