142 环形链表 II
- 双指针,之前做过,先判断是否存在环(141 环形链表),然后判断环的入口
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
ListNode p1 = slow;
ListNode p2 = head;
while(true){
if(p1 == p2) return p1;
p1 = p1.next;
p2 = p2.next;
}
}
}
return null;
}
}
*146 LRU 缓存
- 哈希表+双向链表,哈希表存储key及其在双向链表中的位置,双向链表按照被使用的顺序存储键值对(头部最近使用,尾部最久未使用)。get方法若key存在,将双向链表中对应节点移到头部;put方法若key不存在,在链表头部、哈希表添加节点,然后判断双向链表节点数量是否超过容量,超过了就删除尾部节点,同时删除哈希表中对应节点。注意:可以直接使用LinkedHashMap实现,但是最好手动实现一个双向链表。修改时使用虚拟头节点和虚拟尾节点,虚拟头节点head.next才是真正的头节点,虚拟尾节点tail.prev才是真正的尾节点;真头节点prev为head,真尾节点next为tail,是双向链表而不是双向循环链表!
- 时间复杂度O(1),空间复杂度O(capacity)
private Map<Integer, MyDLinkedNode> map;
private int size;
private int cap;
private MyDLinkedNode head, tail;
public LRUCache(int capacity) {
map = new HashMap<Integer, MyDLinkedNode>();
size = 0;
cap = capacity;
head = new MyDLinkedNode();
tail = new MyDLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if(!map.containsKey(key)){
return -1;
}else{
MyDLinkedNode node = map.get(key);
moveHead(node);
return node.value;
}
}
public void put(int key, int value) {
if(map.containsKey(key)){
MyDLinkedNode node = map.get(key);
node.value = value;
moveHead(node);
}else{
MyDLinkedNode node = new MyDLinkedNode(key, value);
addHead(node);
map.put(key, node);
size += 1;
if(size > cap){
int delKey = tail.prev.key;
delNode(tail.prev);
map.remove(delKey);
size -= 1;
}
}
}
class MyDLinkedNode{
int key;
int value;
MyDLinkedNode prev;
MyDLinkedNode next;
public MyDLinkedNode(){}
public MyDLinkedNode(int k, int v){
key = k;
value = v;
}
}
private void addHead(MyDLinkedNode node){
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
private void delNode(MyDLinkedNode node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveHead(MyDLinkedNode node){
delNode(node);
addHead(node);
}
}
148 排序链表
- 归并排序,自顶向下递归空间复杂度为O(n),从底至顶直接合并空间复杂度为O(1),合并时同JZ25 合并两个排序的链表。
- 注意:使用快慢指针寻找链表中点,快指针每次移动两步,慢指针每次移动一步,快指针到达链表尾部时慢指针即为中点。
- 自顶向下递归,分割区间为左闭右开,前半部分为[head, slow),后半部分为[slow, tail),直到每部分只有一个节点再进行合并(两个有序链表合并)。
- 从底至顶直接合并,相当于从一个节点开始,两两合并,合并后的链表(两个节点)再两两合并,直到全部合并。定义当前合并的单个链表长度,每轮乘以2,若大于链表总长,则说明全部合并。使用虚拟头节点,定义
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head, null);
}
public ListNode mergeSort(ListNode head, ListNode tail){
if(head == null){
return null;
}
if(head.next == tail){
head.next = null;
return head;
}
ListNode slow = head;
ListNode fast = head;
while(fast != tail && fast.next != tail){
fast = fast.next.next;
slow = slow.next;
}
ListNode mid = slow;
ListNode left = mergeSort(head, mid);
ListNode right = mergeSort(mid, tail);
return mergeTwoLists(left, right);
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode virtualHead = new ListNode(0);
ListNode cur = virtualHead;
while(l1 != null && l2 != null){
if(l1.val > l2.val){
cur.next = l2;
l2 = l2.next;
}else{
cur.next = l1;
l1 = l1.next;
}
cur = cur.next;
}
cur.next = (l1 == null ? l2 : l1);
return virtualHead.next;
}
public ListNode sortList2(ListNode head) {
if(head == null){
return null;
}
int leng = 0;
ListNode node = head;
while(node != null){
leng++;
node = node.next;
}
ListNode virtualHead = new ListNode(0, head);
for(int i = 1; i < leng; i <<= 1){
ListNode pre = virtualHead;
ListNode cur = virtualHead.next;
while(cur != null){
ListNode head1 = cur;
for(int j = 1; j < i && cur.next != null; j++){
cur = cur.next;
}
ListNode head2 = cur.next;
cur.next = null;
cur = head2;
for(int j = 1; j < i && cur != null && cur.next != null; j++){
cur = cur.next;
}
ListNode next = null;
if(cur != null){
next = cur.next;
cur.next = null;
}
ListNode m = mergeTwoLists(head1, head2);
pre.next = m;
while(pre.next != null){
pre = pre.next;
}
cur = next;
}
}
return virtualHead.next;
}
}
152 乘积最大子数组
- 动态规划,注意:这里需要根据当前位置的正负性进行分类讨论,应该维护两个dp数组,一个保存大于0的最大积,另一个保存小于0的最小积,当前位置为负数时,求(最小积乘当前值,最大积乘当前值,当前值)的最大最小值。优化:可使用滚动数组。
- 时间复杂度O(n),空间复杂度O(n),优化为O(1)
class Solution {
public int maxProduct(int[] nums) {
int[] dpMax = new int[nums.length];
int[] dpMin = new int[nums.length];
int max = nums[0];
dpMax[0] = nums[0];
dpMin[0] = nums[0];
for(int i = 1; i < nums.length; i++){
dpMax[i] = Math.max(Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i]), nums[i]);
dpMin[i] = Math.min(Math.min(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i]), nums[i]);
max = Math.max(max, dpMax[i]);
}
return max;
}
}
class Solution {
public int maxProduct(int[] nums) {
int dpMax = nums[0];
int dpMin = nums[0];
int max = nums[0];
for(int i = 1; i < nums.length; i++){
int m1 = dpMax;
int m2 = dpMin;
dpMax = Math.max(Math.max(m1 * nums[i], m2 * nums[i]), nums[i]);
dpMin = Math.min(Math.min(m1 * nums[i], m2 * nums[i]), nums[i]);
max = Math.max(max, dpMax);
}
return max;
}
}
155 最小栈
class MinStack {
Deque<Integer> mainStack;
Deque<Integer> auxStack;
public MinStack() {
mainStack = new LinkedList<>();
auxStack = new LinkedList<>();
}
public void push(int val) {
mainStack.push(val);
if(auxStack.isEmpty()){
auxStack.push(val);
}else{
if(val <= auxStack.peek()){
auxStack.push(val);
}
}
}
public void pop() {
int cur = mainStack.pop();
if(cur == auxStack.peek()){
auxStack.pop();
}
}
public int top() {
return mainStack.peek();
}
public int getMin() {
return auxStack.peek();
}
}
160 相交链表
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lengA = 0;
int lengB = 0;
ListNode pA = headA;
ListNode pB = headB;
while(pA != null){
lengA++;
pA = pA.next;
}
while(pB != null){
lengB++;
pB = pB.next;
}
if(lengA < lengB){
ListNode temp = headB;
headB = headA;
headA = temp;
int leng = lengB;
lengB = lengA;
lengA = leng;
}
pA = headA;
pB = headB;
while(lengA > lengB){
pA = pA.next;
lengA--;
}
while(pA != null && pB != null){
if(pA == pB){
return pA;
}
pA = pA.next;
pB = pB.next;
}
return null;
}
}
169 多数元素
class Solution {
public int majorityElement(int[] nums) {
int mode = nums[0];
int sum = 1;
for(int i = 1; i < nums.length; i++){
if(sum == 0){
mode = nums[i];
}
if(nums[i] == mode){
sum++;
}else{
sum--;
}
}
return mode;
}
}
198 打家劫舍
- 动态规划,之前做过,优化:保存前两个值即可。
- 时间复杂度O(n),空间复杂度O(n),优化后O(1)
class Solution {
public int rob(int[] nums) {
if(nums.length == 1) return nums[0];
if(nums.length == 2) return Math.max(nums[0], nums[1]);
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++){
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[nums.length - 1];
}
}
class Solution {
public int rob(int[] nums) {
if(nums.length == 1) return nums[0];
if(nums.length == 2) return Math.max(nums[0], nums[1]);
int dp0 = nums[0];
int dp1 = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++){
int dp2 = Math.max(dp1, dp0 + nums[i]);
dp0 = dp1;
dp1 = dp2;
}
return dp1;
}
}
*200 岛屿数量
- DFS,题目意思是水平或垂直相连的1看作一个岛屿,为了避免重复,需要修改遍历过的格子数(1改为0,复杂的情况可以改为新状态2)。计算递归的次数为岛屿数量。
- BFS,使用队列,遍历格子,若当前节点为1且在各自内,则入队,入队后将该格子修改为0,然后将其上下左右格子加入队列,直到队列为空。计算入队的次数为岛屿数量。
- 时间复杂度O(mn),空间复杂度O(mn),BFS为O(min(m,n))
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'){
bfs(grid, i, j);
count++;
}
}
}
return count;
}
private void dfs(char[][] grid, int i, int j){
if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length){
return;
}
if(grid[i][j] != '1'){
return;
}
grid[i][j] = 0;
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
}
private void bfs(char[][] grid, int i, int j){
if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length){
return;
}
if(grid[i][j] != '1'){
return;
}
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{i, j});
grid[i][j] = 0;
bfs(grid, i, j - 1);
bfs(grid, i, j + 1);
bfs(grid, i - 1, j);
bfs(grid, i + 1, j);
}
}
206 反转链表
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while(cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
|