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. SCP,SSH应用

  2. jenkins pipeline构建后发送邮件通知

    jenkins pipeline构建后发送邮件通知 mail配置 进入系统配置 找到最下边的邮件通知 创建任务 Pipeline片段 post { always { bat "" ...

  3. Docker_CICD笔记

    1 环境说明 1.1 机器配置 主机名称 IP地址 系统版本/内存/cpu核数/硬盘 安装软件 controlnode 172.16.1.70/24 centos7.4/4/2/60 docker.d ...

  4. 4、git和gitlab的配置(2)

    4.10.使用 gitlib 模拟开发流程: 1.项目经理指定开发计划: project01 需求 开发者 完成日期 v1.0 首页 dev01 4.28 支付 dev02 4.30 2.rpm(项目 ...

  5. 2、oracle用户和权限

    权限主要可以分成三类:系统权限.角色.对象权限,角色是一类系统权限的分组, Oracle 的角色存放在表 dba_roles 中,某角色包含的系统权限存放在 dba_sys_privs 中, 包含的对 ...

  6. 6、基本数据类型(list)

    6.1.列表: 1.li = [1, 12, 9, "age", ["孙子涵", ["19", 10], "张涵予"], ...

  7. webpack(3)基础的打包过程

    没有配置文件的打包 如果我们没有使用配置文件webpack.config.js,那么我们就需要通过命令来打包 案例 我们首先创建一个webpackTest文件夹,然后在文件夹中再创建2个子文件夹dis ...

  8. AcWing 1282. 搜索关键词

    给定n个长度不超过50的由小写英文字母组成的单词,以及一篇长为m的文章. 请问,有多少个单词在文章中出现了. #include<bits/stdc++.h> using namespace ...

  9. 【重学Java】多线程进阶(线程池、原子性、并发工具类)

    线程池 线程状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.线程对象在不同的时期有不同的状态.那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定 ...

  10. 浅淡fhq_Treap

    浅淡 \(fhq\_Treap\) 前言 fhq_Treap \(yyds\)! \(sto\ FHQ\ orz\) 机房大佬们都打的 \(Splay\) 只有蒟蒻打的 \(fhq\) (防火墙)(范 ...