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. 拦截导弹(CDQ分治,DP)

    很好的题,值得细细说,(果然又是个假期望)....... 首先我们提取信息,显然这是个三维偏序问题 用简单的DP式子表示需要满足 f[i]=max(f[1--j]+1)(v[j]<v[i],h[ ...

  2. Jenkins+Github+Nginx实现前端项目自动部署

    前言 最近在搭建一个自己的网站,网站框架搭好了要把项目放到服务器运行,但是每次更新网站内容就要手动部署一次,实在很麻烦,于是就想搭建一套自动化部署的服务.看了一些案例最后选用现在比较主流的Jenkin ...

  3. Linux中使用QT读取并显示温度传感器数值

    环境: Ubuntu 16.04 64 bit QT5.13.1/QT5.12 原理 对于Linux来说一切都是文件,温度传感器也是个文件,如果要获取某个温度传感器的数值,可以读取相应的文件,Linu ...

  4. ceph-csi源码分析(3)-rbd driver-服务入口分析

    更多ceph-csi其他源码分析,请查看下面这篇博文:kubernetes ceph-csi分析目录导航 ceph-csi源码分析(3)-rbd driver-服务入口分析 当ceph-csi组件启动 ...

  5. 中文NER的那些事儿3. SoftLexicon等词汇增强详解&代码实现

    前两章我们分别介绍了NER的基线模型Bert-Bilstm-crf, 以及多任务和对抗学习在解决词边界和跨领域迁移的解决方案.这一章我们就词汇增强这个中文NER的核心问题之一来看看都有哪些解决方案.以 ...

  6. Windows10 上Docker 安装运行Consul

    背景简介 Consul是一种服务网格解决方案,提供具有服务发现,配置和分段功能的全功能控制平面. 这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全服务网格. Consul需要数据平面并 ...

  7. Spring Cloud 专题之四:Zuul网关

    书接上回: SpringCloud专题之一:Eureka Spring Cloud专题之二:OpenFeign Spring Cloud专题之三:Hystrix 经过前面三章对Spring Cloud ...

  8. API安全综述

    API安全综述 译自:An Overview on API Security. 本文概括了API防护有关的方方面面,从上层视角介绍了API防护中主要注意的点,并给出了相应的建议.本文可以作为一个API ...

  9. AcWing 829. 模拟队列

    实现一个队列,队列初始为空,支持四种操作: (1) "push x" – 向队尾插入一个数x: (2) "pop" – 从队头弹出一个数: (3) " ...

  10. [网络流24题]最长k可重线段集[题解]

    最长 \(k\) 可重线段集 题目大意 给定平面 \(x-O-y\) 上 \(n\) 个开线段组成的集合 \(I\) ,和一个正整数 \(k\) .试设计一个算法,从开线段集合 \(I\) 中选取开线 ...