算法题笔记
题源: 牛客网 🔗 www.nowcoder.com/activity/oj…
Q1: 重建二叉树
思路:
重建二叉树必须要有中序+前序或中序+后序才能重建,前序+后序是不能的。且一般是递归进行的。
根据前序+中序重建二叉树,首先需要确定根节点的值。由前序遍历定义可知,前序遍历结果的第一个值就是根节点的值pre[0] == root.val
。然后通过根节点的值在中序中的位置idx
,可以将中序遍历分成两部分,一部分是左子树的中序遍历结果(in[0]~in[idx-1]
),另一部分是右子树的中序遍历结果(in[idx+1]~in[len-1]
)。然后在左右子树递归此过程即可,Java代码实现参考如下:
import java.util.*;
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
int len = pre.length;
if(len == 0) {
return null;
}
int rootNodeVal = pre[0];
TreeNode res = new TreeNode(rootNodeVal);
int idx = 0;
for(;idx<len;idx++) {
if(in[idx] == rootNodeVal) {
break;
}
}
res.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,1+idx),
Arrays.copyOfRange(in,0,idx));
res.right = reConstructBinaryTree(Arrays.copyOfRange(pre,1+idx,len),
Arrays.copyOfRange(in,idx+1,len));
return res;
}
}
Q2:链表翻转
本题为链表翻转的进阶版,每K个一组进行翻转。同lc25提(https://leetcode-cn.com/problems/reverse-nodes-in-k-group/)
解题思路:
- 遍历链表得到长度,然后计算一共会分多少组
- 对每组的节点都存到一个
list
变量中,然后从后往前遍历,进行翻转操作。 - 然后织带前一组的最后一个节点的变量
pre
指向该list
的最后一个节点,然后pre
重新复制该list
的最后一个节点。 - 循环step2 直到所有组分完,返回即可 code1
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
public ListNode reverseKGroup (ListNode head, int k) {
// write code here
if (k == 1 || head == null || head.next == null) {
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
// 计算一共需要旋转多少组
int cnt = 0;
ListNode cur = head;
while (cur!=null) {
cur = cur.next;
cnt++;
}
cur = head;
int group = cnt/k;
//
while (group-->0) {
List<ListNode> l = new ArrayList<>();
for (int i=0;i<k;i++){
l.add(cur);
cur = cur.next;
}
for (int i=l.size()-1;i>0;i--){
l.get(i).next = l.get(i-1);
}
pre.next = l.get(l.size()-1);
l.get(0).next = cur;
pre = l.get(0);
}
return dummy.next;
}
}
code2 并不需要保存list,直接按位翻转即可
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0), prev = dummy, curr = head, next;
dummy.next = head;
int length = 0;
while(head != null) {
length++;
head = head.next;
}
head = dummy.next;
for(int i = 0; i < length / k; i++) {
for(int j = 0; j < k - 1; j++) {
next = curr.next;
curr.next = next.next;
next.next = prev.next;
prev.next = next;
}
prev = curr;
curr = prev.next;
}
return dummy.next;
}
}
Q3: 判断链表是否有环
解题思路:
典型的快慢指针问题
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null){
return false;
}
ListNode slow=head,quick=head.next;
while (quick != null) {
if (slow == quick) return true;
slow = slow.next;
quick = quick.next;
if (quick != null) {
quick = quick.next;
}else return false;
}
return false;
}
}
Q4 判断二叉树是否对称
解题思路:
典型的树🌲递归问题,完全对称的充要条件就是当前两节点值相同,且左右孩子也分别对称。
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
public boolean isSymmetric (TreeNode root) {
// write code here
if (root == null) {
return true;
}
return helper(root.left,root.right);
}
private boolean helper(TreeNode p1,TreeNode p2) {
if (p1 == null || p2 == null) {
return p1 == null && p2 == null;
}
if (p1.val == p2.val) {
return helper(p1.left,p2.right)&&helper(p1.right,p2.left);
}else {
return false;
}
}
}
Q5 青蛙🐸跳台阶问题
典型的dp
问题,当前位置只能从当前-1的台阶,或者-2两个台阶跳上来,因此可的状态转移方程:
dp[n] = dp[n-1]+dp[n-2]
进而代码如下:
public class Solution {
public int JumpFloor(int target) {
if (target <= 2) return target;
int[] dp = new int[target+1];
dp[1] = 1;
dp[2] = 2;
for (int i=3;i<=target;i++) {
dp[i] = dp[i-1]+dp[i-2];
}
return dp[target];
}
}
Q6 LRU cache
缓存作为帮助快速访问热点的数据的结构当然存越多的数据越好,但是缓存是在内存中,需要占据宝贵的存储资源。因此需要采用合理的缓存失效策略,LRU cache就是一种常用缓存失效的策略(Redis中就可以使用LRU作为缓存失效的策略)。
import java.util.*;
public class Solution {
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
public int[] LRU (int[][] operators, int k) {
// write code here
LRUADT lru = new LRUADT(k);
List<Integer> res = new ArrayList<>();
for (int[] op : operators) {
if (op[0] == 1) {
lru.set(op[1],op[2]);
}else {
res.add(lru.get(op[1]));
}
}
int[] r = new int[res.size()];
for (int i=0;i<r.length;i++) {
r[i] = res.get(i);
}
return r;
}
class LRUADT {
Map<Integer,Node> cache;
Node head,tail;
int capacity;
class Node {
int key;
int val;
Node next,prev;
Node(int key,int val) {
this.key = key;
this.val = val;
}
}
LRUADT(int capacity) {
cache = new HashMap<>();
head = new Node(-1,-1);
tail = new Node(-1,-1);
head.next = tail;
tail.prev = head;
this.capacity = capacity;
}
int get(int key) {
if (cache.containsKey(key)) {
// 设置为最早的
rotateFirst(key);
return cache.get(key).val;
}else {
return -1;
}
}
void set(int key,int val) {
if (cache.containsKey(key)){
Node n = cache.get(key);
delNode(n);
n.val = val;
addFirst(n);
}else if (cache.size() < capacity) {
Node n = new Node(key,val);
addFirst(n);
cache.put(key,n);
}else {
// 删去最后一个节点
Node del = tail.prev;
delNode(del);
cache.remove(del.key);
Node n = new Node(key,val);
addFirst(n);
cache.put(key,n);
}
}
void rotateFirst(int key) {
Node cur = cache.get(key);
if (cur.prev == head) {
return;
}
// 链表中删去 Node
delNode(cur);
// Node 加到最前面
addFirst(cur);
}
void addFirst(Node n){
Node first = head.next;
head.next = n;
n.prev = head;
n.next = first;
first.prev = n;
}
void delNode(Node n) {
Node prev = n.prev;
Node nxt = n.next;
n.prev = null;
n.next = null;
prev.next = nxt;
nxt.prev = prev;
}
}
}
Q7 合并有序链表
遍历两链表,每次将小的节点加入结果中即可。
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param l1 ListNode类
* @param l2 ListNode类
* @return ListNode类
*/
public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
// write code here
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
cur.next = l1;
cur = cur.next;
l1 = l1.next;
}else {
cur.next = l2;
cur = cur.next;
l2 = l2.next;
}
}
if (l1 != null) {
cur.next = l1;
}else if (l2 != null) {
cur.next = l2;
}
return dummy.next;
}
}
Q8 链表中环的入口节点
本题为lc经典算法题,目标是考察对floyd
判圈算法的认知。
floyd
判圈算法参考下文链接:
具体代码如下:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
/**
* floyd 判环算法
* @param head
* @return
*/
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) return null;
ListNode slow = head;
ListNode quick = head;
while (true) {
slow = slow.next;
quick = quick.next;
if (quick == null) return null;
quick = quick.next;
if (quick == null) return null;
if (slow == quick) break;
}
// 相遇
slow = head;
while (slow != quick) {
slow = slow.next;
quick = quick.next;
}
return slow;
}
}
Q9 大数加法
分析:
通过字符串进行超大数加减法,为经典算法题。只需通过按字符串从右往左依次进行相加即可,并保存进位值prev
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算两个数之和
* @param s string字符串 表示第一个整数
* @param t string字符串 表示第二个整数
* @return string字符串
*/
public String solve (String s, String t) {
// write code here
int len = Math.max(s.length(),t.length());
char[] res = new char[len+1];
int prev = 0;
for (int i=0;i<res.length;i++){
int a1=0,a2=0;
if (i < s.length()) {
a1 = s.charAt(s.length()-1-i)-'0';
}
if (i < t.length()) {
a2 = t.charAt(t.length()-1-i)-'0';
}
int v = a1+a2+prev;
if (v >= 10) {
res[res.length-1-i] = (char)((v%10)+'0');
prev = v/10;
}else {
res[res.length-1-i] = (char)(v+'0');
prev = 0;
}
}
if (res[0] == '0'){
return String.valueOf(Arrays.copyOfRange(res,1,res.length));
}else {
return String.valueOf(res);
}
}
Q10 合并K个已排序链表
该题与Q7 合并有序链表核心是相似的。本题只需遍历lists
中的每个ListNode
然后找到最小的续上答案里即可。
代码如下:
import java.util.*;
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode mergeKLists(ArrayList<ListNode> lists) {
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
lists.removeIf(Objects::isNull);
while (lists.size() > 1){
// find min
int idx = -1;
int min = Integer.MAX_VALUE;
for (int i=0;i<lists.size();i++) {
ListNode c = lists.get(i);
if (c.val < min) {
idx = i;
min = c.val;
}
}
// min next
cur.next = lists.get(idx);
cur = cur.next;
ListNode c = lists.get(idx);
if (c.next == null) {
lists.remove(idx);
}else {
lists.set(idx,c.next);
}
}
if (lists.size() == 1) {
cur.next = lists.get(0);
}
return dummy.next;
}
}
总结
以上为牛客在线算法题 Q1~Q10
,之后继续更新全部的题解~