LeetCode力扣刷题2

192 阅读3分钟

程序 = 数据结构 + 算法

力扣解题思路2

148.排序链表

  • 要求复杂度为nlogn则想到归并排序。
  • 通过快慢指针找到链表中点进行拆分,直到拆分到长度为1。
  • 然后不断合并。
public ListNode sortList(ListNode head) {
    return split(head,null);
}

public ListNode split(ListNode head,ListNode tail){
    if(head==tail||head.next==tail) return head;

    ListNode slow = head;
    ListNode fast = head.next;
    while(fast!=tail&&fast.next!=tail){
        fast = fast.next.next;
        slow = slow.next;
    }
    ListNode mid = slow.next;
    slow.next = null;
    ListNode left = split(head,null);
    ListNode right = split(mid,tail);
    return merge(left,right);
}

public ListNode merge(ListNode left,ListNode right){
    ListNode dummy = new ListNode(-1);
    ListNode p = dummy;
    while(left!=null&&right!=null){
        if(left.val<right.val){
            p.next = new ListNode(left.val);
            left = left.next;
        }else{
            p.next = new ListNode(right.val);
            right = right.next;
        }
        p = p.next;
    }
    p.next = left==null?right:left;
    return dummy.next;
}

151.翻转字符串里的单词

  • 模拟
public String reverseWords(String s) {
    StringBuilder sb = new StringBuilder();
    char[] cs = s.toCharArray();
    int n=s.length();
    boolean flag = true;
    int j = n-1,i=j;
    while(j>=0){
        if(cs[j]==' '){
            j--;
        }else{
            i = j-1;
            while(i>=0&&cs[i]!=' '){
                i--;
            }
            if(flag){
                flag = false;
            }else{
                sb.append(" ");
            }
            sb.append(s.substring(i+1,j+1));
            j = i;
        }
    }
    return sb.toString();
}

152.乘积最大子数组

  • 动态规划
  • 定义两个数组,max[i]记录算上当前位置数组中的最大乘积,min[i]记录最小乘积。
public int maxProduct(int[] nums) {
    int n = nums.length;
    int max[] = new int[n];
    int min[] = new int[n];

    int res = nums[0];

    max[0] = nums[0];
    min[0] = nums[0];

    for(int i=1;i<n;i++){
        max[i] = Math.max(nums[i],Math.max(nums[i]*max[i-1],nums[i]*min[i-1]));
        min[i] = Math.min(nums[i],Math.min(nums[i]*max[i-1],nums[i]*min[i-1]));

        res = Math.max(res,max[i]);
    }
    return res;
}

155.最小栈

  • 投机方法(两个栈是同步的):用两个栈,一个普通栈,一个最小栈。
Deque<Integer> stack;
Deque<Integer> min;
public MinStack() {
    stack = new ArrayDeque<>();
    min = new ArrayDeque<>();
}
public void push(int val) {
    stack.push(val);
    if(min.isEmpty()){
        min.push(val);
    }else{
        min.push(Math.min(val,min.peek()));
    }
}
public void pop() {
    stack.pop();
    min.pop();
}
public int top() {
    return stack.peek();
}
public int getMin() {
    return min.peek();
}
  • 单调栈方法(两个栈不同步):辅助栈只在加入的数比栈顶小入栈,普通栈出栈时如果等于单调栈,则同时出栈单调栈。
public void push(int val) {
    stack.push(val);
    int minPeek = min.isEmpty()?Integer.MAX_VALUE:min.peek();
    if(val<=minPeek){
        min.push(val);
    }
}

public void pop() {
    int val = stack.pop();
    if(min.peek()==val){
        min.pop();
    }
}

162.寻找峰值

  • 因为复杂度要求logn,所以要用二分查找。
  • nums[m]<nums[m+1]时m+1右侧必定包含峰值。
  • nums[m】>nums[m+1]时,m左侧必定包含峰值。
public int findPeakElement(int[] nums) {
    int l = 0,r = nums.length-1;
    while(l<r){
        int m = (l+r)/2;
        if(nums[m]<nums[m+1]){
            l = m+1;
        }else{
            r = m;
        }
    }
    return l;
}

165.比较版本号

169.多数元素

    1. 排序后去中值,复杂度为nlogn不符合。
    1. 定义一个变量res和个数t,遍历所有元素x,如果x与res相等,则t++,如果x与res不想等,则t--。如果t减少为0,则替换x。最后留下的x就是出现次数大于n/2的元素。复杂度为n
public int majorityElement(int[] nums) {
    int res = nums[0],t = 1;
    for(int i=1;i<nums.length;i++){
        if(nums[i]==res){
            t++;
        }else{
            t--;
        }
        if(t==0) {
            res = nums[i];
            t = 1;
        }
    }
    return res;
}

198.打家劫舍

  • 动态规划
  • dp[i]定义为当前能偷窃到的最高金额。
  • 不偷为dp[i-1]
  • 偷为dp[i-2]+nums[i]
  • 取上面两者最大值
public int rob(int[] nums) {
    int n = nums.length;
    int[] dp = new int[n];
    dp[0] = nums[0];
    for(int i=1;i<n;i++){
        int x1 = dp[i-1];
        int x2 = (i-2>=0?dp[i-2]:0)+nums[i];
        dp[i] = Math.max(x1,x2);
    }
    return dp[n-1];
}
  • 由于dp[i]只与i-1和i-2有关,可以用三个变量来代替dp数组。
public int rob(int[] nums) {
    int n = nums.length;
    if(n==1) return nums[0];
    int a = nums[0];
    int b = Math.max(nums[0],nums[1]);
    for(int i=2;i<n;i++){
        int c = Math.max(a+nums[i],b);
        a = b;
        b = c;
    }
    return b;
}

207.课程表

  • 拓扑
  • 广度优先算法:需要计算每个节点的入度,当入度为0就插入队列,然后队列出队后在重新更新相关节点的入度
  • 用List<List<>>存储边,用数组存储入度个数。
public boolean canFinish(int numCourses, int[][] prerequisites) {
    List<List<Integer>> edges = new ArrayList<>();
    int[] in = new int[numCourses];
    int res = 0;
    for(int i=0;i<numCourses;i++){            
        edges.add(new ArrayList<>());
    }

    //计算每个节点的入度,并构建有向图
    for(int[] info:prerequisites){
        edges.get(info[1]).add(info[0]);
        in[info[0]]++;
    }

    Queue<Integer> queue = new LinkedList<>();
    for(int i=0;i<numCourses;i++){
        if(in[i]==0){
            queue.add(i);
        }
    }
    while(!queue.isEmpty()){
        int x = queue.poll();
        res ++ ;

        for(int edge:edges.get(x)){
            in[edge]--;
            if(in[edge]==0){
                queue.add(edge);
            }
        }
    }
    return res==numCourses;
}
  • 深度优先算法:需要记录每个节点的状态,0为未搜索,1为正在搜索,2为已搜索。
  • 如果在dfs的过程中遇到正在搜索则说明存在有环。
  • 由于深度优先算法的特性,他并不需要记录每个节点的入度,会自动找到没有出度的叶子结点,找到叶子结点定义为已搜索,然后不断回退继续深度优先搜索。直到遇到环或者搜索结束。
List<List<Integer>> edges;
boolean hasLoop ;
int[] state;

public boolean canFinish(int numCourses, int[][] prerequisites) {
    edges = new ArrayList<>();
    for(int i=0;i<numCourses;i++){
        edges.add(new ArrayList<>());
    }
    hasLoop = false;
    state = new int[numCourses];

    for(int[] info:prerequisites){
        edges.get(info[1]).add(info[0]);
    }

    for(int i=0;i<numCourses&&!hasLoop;i++){
        if(state[i]==0){
            dfs(i);
        }
    }
    return !hasLoop;
}

public void dfs(int i){
    state[i] = 1;
    for(int t:edges.get(i)){
        if(state[t]==0){
            dfs(t);
        }else if (state[t]==1){
            hasLoop = true;
            return;
        }
    }
    state[i] = 2;
}

208.实现Trie(前缀树)

  • 由于是树形结构,每个节点Trie应该持有一组子节点Trie[],并且判断当前节点是否为叶子结点的isLeaf。
  • 对于初始化,每个节点的子节点应该对应26个字母。
  • 对于插入,即遍历字符串的每一位,从树的根节点开始不断向下新建节点。
  • 根节点不存储数据,从子节点开始插入和检索。
  • 注意只有小写字母,c-'a'而不是c-'0'
class Trie {
    Trie[] child;
    boolean isLeaf;

    public Trie() {
        child = new Trie[26];
        isLeaf = false;
    }
    
    public void insert(String word) {
        Trie node = this;
        for(int i=0;i<word.length();i++){
            char c = word.charAt(i);
            int index = c-'a';
            if(node.child[index]==null){
                node.child[index] = new Trie();
            }
            node = node.child[index];
        }
        node.isLeaf = true;
    }
    
    public boolean search(String word) {
        Trie node = this;
        for(int i=0;i<word.length();i++){
            char c = word.charAt(i);
            int index = c-'a';
            if(node.child[index]==null) return false;
            node = node.child[index];
        }
        return node.isLeaf;
    }
    
    public boolean startsWith(String prefix) {
        Trie node = this;
        for(int i=0;i<prefix.length();i++){
            char c = prefix.charAt(i);
            int index = c-'a';
            if(node.child[index]==null) return false;
            node = node.child[index];
        }
        return true;
    }
}

209.长度最小的子数组

  • 滑动窗口
  • 定义窗口为左闭右开。
  • i和j都设置为0
public int minSubArrayLen(int target, int[] nums) {
    int n = nums.length;
    if(n==0) return 0;
    int i=0,j=0;
    int sum = 0;
    int res = Integer.MAX_VALUE;
    while(j<n){
        sum+=nums[j];
        while(sum>=target){
            res = Math.min(res,j-i+1);
            sum-=nums[i];
            i++;
        }
        j++;
    }
    return res==Integer.MAX_VALUE?0:res;
}

210.课程表2

  • 相比较1增加输出数组 public int[] findOrder(int numCourses, int[][] prerequisites) { //广度优先遍历 List<List> edges = new ArrayList<>(); int in[] = new int[numCourses]; int res[] = new int[numCourses]; Queue queue = new LinkedList<>(); int size = 0; for(int i=0;i<numCourses;i++){ edges.add(new ArrayList<>()); } for(int[] info:prerequisites){ edges.get(info[1]).add(info[0]); in[info[0]]++; } for(int i=0;i<numCourses;i++){ if(in[i]==0){ queue.add(i); } } while(!queue.isEmpty()){ Integer x = queue.poll(); res[size++] = x; for(int edge:edges.get(x)){ in[edge]--; if(in[edge]==0){ queue.add(edge); } } } if(size==numCourses) return res; return new int[0]; }

数组中的第k个最大元素

  • 快排
  • 用随机函数选定下标p,p的左边全是大于nums[p],p的右边全是小于nums[p]
  • 通过p和k的比较进行左右两边的选择,也就是剪纸
public int findKthLargest(int[] nums, int k) {
    return quickSort(nums,0,nums.length-1,k-1);
}

public int quickSort(int[] nums,int l,int r,int k){
        int p = partation(nums,l,r);
    if(p==k) {
        return nums[p];
    }
    return p<k?quickSort(nums,p+1,r,k):quickSort(nums,l,p-1,k);
}

public int partation(int[] nums,int l,int r){
    int rand = new Random().nextInt(r-l+1)+l;
    swap(nums,rand,l);
    int k = nums[l];

    while(l<r){
        while(l<r&&nums[r]<=k) r--;
        if(l<r){
            nums[l] = nums[r];
            l++;
        }
        
        while(l<r&&nums[l]>=k) l++;
        if(l<r){
            nums[r] = nums[l];
            r--;
        }
    }
    nums[l] = k;
    return l;
}

public void swap(int[] nums,int i,int j){
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
}

221.最大正方形

  • dp算法
  • 上,左,左上的最小值+1
public int maximalSquare(char[][] matrix) {
    int n = matrix.length;
    int m = matrix[0].length;
    int[][] dp = new int[n][m];
    int res = 0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            int c = matrix[i][j] - '0';
            if(i==0||j==0){
                dp[i][j] = c;
            }else if(c==0){
                dp[i][j] = 0;
            }else{
                dp[i][j] = Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i][j-1]))+1;
            } 
            res = Math.max(res,dp[i][j]); 
        }
    }
    return res*res;
}

224.基本计算器

  • 一个栈存储数字nums,一个栈存储运算符ops
  • 定义一个计算函数,取nums两位,取ops一位,根据ops来进行计算,计算结果再插入nums中
  • 将s中的" "全部置换成""
  • 循环遍历s,
    1. 如果为'('则入ops栈。
    1. 如果为')'则不断计算,直到遇到(将其弹出。
    1. 如果为数字,则计算当前数字之后的所有连续数字,组成一个整数入nums栈。
    1. 如果为运算符,则先计算能计算的(不断计算直到遇到优先级比当前运算符还要低的运算符),再入ops栈。
  • 运算符的优先级存储在map中
public int calculate(String s) {

    Map<Character,Integer> map = new HashMap<>(){{
        put('+',1);
        put('-',1);
        put('*',2);
        put('/',2);
        put('%',2);
        put('^',3);
    }};

    Deque<Integer> nums = new ArrayDeque<>();
    Deque<Character> ops = new ArrayDeque<>();
    s = s.replaceAll(" ","");

    char[] cs = s.toCharArray();

    nums.addLast(0);

    for(int i=0;i<cs.length;i++){
        if(cs[i]=='('){
            ops.addLast(cs[i]);
        }else if(cs[i]==')'){
            while(!ops.isEmpty()){
                Character c = ops.peekLast();
                if(c=='('){
                    ops.pollLast();
                    break;
                }else{
                    cal(nums,ops);
                }
            }
        }else{
            if(Character.isDigit(cs[i])){
                int u = 0;
                int j = i;
                while(j<cs.length&&Character.isDigit(cs[j])){
                    u = u * 10 + cs[j] - '0';
                    j++;
                }
                nums.addLast(u);
                i = j-1;
            }else{
                if(i>0&&(cs[i-1]=='(')){
                    nums.addLast(0);
                }
                while(!ops.isEmpty()&&ops.peekLast()!='('){
                    if(map.get(ops.peekLast())>=map.get(cs[i])){
                        cal(nums,ops);
                    }else break;
                }
                ops.addLast(cs[i]);
            }
        }
        
    }
    while(!ops.isEmpty()) cal(nums,ops);
    return nums.peekLast();

}


void cal(Deque<Integer> nums,Deque<Character> ops){
    if(nums.isEmpty()||nums.size()<2) return ;
    if(ops.isEmpty()) return ;

    int num1 = nums.pollLast();
    int num2 = nums.pollLast();
    char c = ops.pollLast();
    int tmp = 0;
    if(c=='+'){
        tmp = num1 + num2;
    }else if(c=='-'){
        tmp = num2 - num1;
    }else if(c=='*'){
        tmp = num1 * num2;
    }else if(c=='/'){
        tmp = num2 / num1;
    }else if(c=='^'){
        tmp = (int)Math.pow(num2,num1);
    }
    nums.addLast(tmp);
}

230.二叉搜索树中第k小的元素

  • 可能面临的情况:
    1. 无:递归或非递归的中序遍历。
    1. 这个树经常要被访问:将子树的结点个数存储在hash表中(复杂度为H树的高度)或者放到List中(复杂度为1)
    1. 这个树经常要被修改
  • 中序搜索(递归方法),找到第k个返回。
int res = -1;
int num = 0;
public int kthSmallest(TreeNode root, int k) {
    dfs(root,k);
    return res;
}

public void dfs(TreeNode root,int k){
    if(root==null) return ;

    dfs(root.left,k);
    num++;
    if(num==k){
        res = root.val;
        return ;
    }
    dfs(root.right,k);
}
  • 中序遍历(非递归方法)
public int kthSmallest(TreeNode root, int k) {
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode p = root;
    while(p!=null||!stack.isEmpty()){
        while(p!=null){
            stack.addLast(p);
            p = p.left;
        }
        if(!stack.isEmpty()){
            p = stack.pollLast();
            k--;
            if(k==0){
                return p.val;
            }
            p = p.right;
        }
    } 
    return 0;
}

234.回文链表

  • 快慢指针,慢指针将前半部分倒置。
public boolean isPalindrome(ListNode head) {
    if(head==null||head.next==null) return true;
    ListNode pre = null;
    ListNode slow = head;
    ListNode fast = head;
    ListNode next = null;
    while(fast!=null&&fast.next!=null){
        fast = fast.next.next;
        
        next = slow.next;
        slow.next = pre;
        pre = slow;
        slow = next;  
    }
    if(fast!=null) slow = slow.next;
    while(pre!=null&&slow!=null){
        if(pre.val!=slow.val) return false;
        pre = pre.next;
        slow = slow.next;
    }
    return true;
}

236.二叉树的最近公共祖先

  • 重点在于是二叉树,不是搜索二叉树
  • 利用后序遍历,定义三个boolean值,分别为当前根节点,左子树,右子树是否有目标结点。
TreeNode res = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    dfs(root,p,q);
    return res;
}

public boolean dfs(TreeNode root,TreeNode p,TreeNode q){
    if(root==null) return false;

    boolean incurrent = root.val==p.val||root.val==q.val;
    boolean inleft = dfs(root.left,p,q);
    boolean inright = dfs(root.right,p,q);

    if((inleft&&inright)||((inright||inleft)&&incurrent)){
        res = root;
    }
    return incurrent||inleft||inright;
}

239.滑动窗口最大值

  • 滑动窗口
  • 维持一个双端队列并且是单调递减队列。
  • 每次按照单调递减入队列,取队列头为最大值。
  • 双端队列中存储的是元素下标,用于判断是否还在滑动窗口内。
public int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length;
    int[] res = new int[n-k+1];
    int idx = 0;

    Deque<Integer> deque = new ArrayDeque<>();
    for(int i=0;i<k;i++){
        while(!deque.isEmpty()&&nums[deque.peekLast()]<nums[i]){
            deque.pollLast();
        }
        deque.offerLast(i);
    }

    res[idx++] = nums[deque.peekFirst()];
    int j = k;
    while(j<n){
        while(!deque.isEmpty()&&nums[deque.peekLast()]<nums[j]){
            deque.pollLast();
        }
        deque.offerLast(j);
        while(!deque.isEmpty()&&deque.peekFirst()<=j-k){
            deque.pollFirst();
        }
        res[idx++] = nums[deque.peekFirst()];
        j++;
    }
    return res;
}

264.丑数

  • dp[i]表示第i个丑数,
  • 分别定义三个下标变量x2,x3,x5。表示当前下标需要乘上相应的数(如x2下标对应的值需要乘上2)。
  • dp[i]就是三者的最小值
  • dp[i]=min(dp[x2]*2,dp[x3]*3,dp[x5]*5)
  • 然后比较dp[i]和三者,如果相等则说明该下标以及有了对应的丑数了,需要后移。
public int nthUglyNumber(int n) {
    int[] dp = new int[n+1];
    dp[1] = 1;

    int x2 = 1,x3 = 1,x5 = 1;
    for(int i=2;i<=n;i++){
        dp[i] = Math.min(dp[x2]*2,Math.min(dp[x3]*3,dp[x5]*5));
        if(dp[i]==dp[x2]*2){
            x2++;
        }
        if(dp[i]==dp[x3]*3){
            x3++;
        }
        if(dp[i]==dp[x5]*5){
            x5++;
        }
    }
    return dp[n];
}

279.完全平方数

  • dp[i]表示构成整数i需要完全平方数的最少数量。
  • dp[1] = 1;
  • 从后往前遍历取最小值
public int numSquares(int n) {
    int[]dp = new int[n+1];
    dp[1] = 1;
    for(int i=2;i<=n;i++){
        dp[i] = i;
        for(int j=1;i-j*j>=0;j++){
            dp[i] = Math.min(dp[i],dp[i-j*j]+1);
        }
    }
    return dp[n];
}

287.寻找重复数

  • 将数组看成链表
  • 快慢指针找到环的入口
public int findDuplicate(int[] nums) {
    int slow = nums[0],fast = nums[0];
    while(true){
        slow = nums[slow];
        fast = nums[nums[fast]];
        if(slow == fast) break;
    }

    fast = nums[0];
    while(fast!=slow){
        fast = nums[fast];
        slow = nums[slow];
    }
    return slow;
}

300.最长递增子序列

  • 动态规划
  • dp[i]存储着当前的递归子序列,如果要加入的数比dp[i]还大则加入到最后,并且maxlen+1,如果比dp[i]小则通过二分查找的方式找到该数应该在的地方。
public int lengthOfLIS(int[] nums) {
    int n = nums.length;
    int[] dp = new int[n];
    int maxLen = 0;
    for(int num:nums){
        int l = 0,r = maxLen;
        while(l<r){
            int m = (l+r)/2;
            if(dp[m]<num){
                l = m+1;
            }else {
                r = m;
            }
        }
        dp[l] = num;
        if(l==maxLen) maxLen++;
    }
    return maxLen;
}

309.最佳买卖股票时机含冷冻期

  • 动态规划,由于每个dp都只与前两位有关,可改成状态。
  • 定义三个状态,has_stock,no_stock,freeze
public int maxProfit(int[] prices) {
    int n = prices.length;
    if(n<=1) return 0;

    int hasStock = -prices[0];
    int noStockNotFreeze = 0;
    int noStockButFreeze = 0;
    for(int i=1;i<prices.length;i++){
        int x1 = Math.max(hasStock,noStockNotFreeze-prices[i]);
        int x2 = Math.max(noStockNotFreeze,noStockButFreeze);
        int x3 = hasStock + prices[i];

        hasStock = x1;
        noStockNotFreeze = x2;
        noStockButFreeze = x3;
    }
    return Math.max(noStockButFreeze,noStockNotFreeze);
}

322.零钱兑换

  • 动态规划,dp[i][j]表示前i个硬币中最少构成总金额为j的硬币个数。
  • 定义dp[n+1][m+1],第一列全部为0,表示构成总金额为0需要0个硬币,第一行全为最大值,表示0硬币构成任何金额都需要无穷大个硬币。
  • 如果nums[i]>=j表示可以选择是否参与组成,如果不参与则为dp[i-1][j],如果参与则为dp[i][j-nums[i]],取两者最小值作为dp[i][j]。
public int coinChange(int[] coins, int amount) {
    if(amount==0) return 0;
    
    int n = coins.length;

    int[][] dp = new int[n+1][amount+1];
    for(int i=0;i<=amount;i++){
        dp[0][i] = amount+1;
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=amount;j++){
            dp[i][j] = dp[i-1][j];
            if(coins[i-1]<=j){
                dp[i][j] = Math.min(dp[i-1][j],dp[i][j-coins[i-1]]+1);
            }
        }
    }
    return dp[n][amount]==amount+1?-1:dp[n][amount];
}
  • 优化成一阶,因为dp只与当前行和上一行有关。
public int coinChange(int[] coins, int amount) {
    if(amount==0) return 0;

    int n = coins.length;

    int[] dp = new int[amount+1];
    for(int i=0;i<=amount;i++){
        dp[i] = amount+1;
    }

    dp[0] = 0;

    for(int i=1;i<=n;i++){
        for(int j=coins[i-1];j<=amount;j++){
            dp[j] = Math.min(dp[j],dp[j-coins[i-1]]+1);
        }
    }
    return dp[amount]==amount+1?-1:dp[amount];
}

337.打家劫舍3

  • 二叉树类型
  • dfs,每个节点获取左右子节点的能够盗取的最高金额(分为偷和不偷)。

public int rob(TreeNode root) {
    int[] res = dfs(root);
    return Math.max(res[0],res[1]);
}
public int[] dfs(TreeNode root){
    if(root==null) return new int[]{0,0};
    //定义0为偷,1为不偷
    int[] left = dfs(root.left);
    int[] right = dfs(root.right);
    int[] res = new int[2];

    res[0] = root.val + left[1] + right[1];
    res[1] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
    return res;
}

338.比特位记数

  • dp数组
  • 如果当前数是偶数的话,dp[i] = dp[i/2],否则dp[i-1]+1
  • 判断奇偶性可以采用位运算。
public int[] countBits(int n) {
    if(n==0)return new int[]{0};
    if(n==1)return new int[]{0,1};
    int[] dp = new int[n+1];
    dp[0] = 0;
    dp[1] = 1; 
    for(int i=2;i<=n;i++){
        if(i%2==0){
            dp[i] = dp[i/2];
        }else {
            dp[i] = dp[i-1]+1;
        }
    }
    return dp;
}

前k个高频元素

  • 利用堆排序
  • 先利用map存储每个元素的频数,再利用小顶堆(优先队列)遍历存放进去,然后取k个到res数组中。
  • 注意要从小到大排序,因为要比较peek当前队列中最小的那个。
public int[] topKFrequent(int[] nums, int k) {
    Map<Integer,Integer> map = new HashMap<>();

    for(int num:nums){
        map.put(num,map.getOrDefault(num,0)+1);
    }
    // System.out.println(map);
    PriorityQueue<Map.Entry<Integer,Integer>> queue = new PriorityQueue<>((e1,e2)-> e1.getValue()-e2.getValue());

    for(Map.Entry<Integer,Integer> entry:map.entrySet()){
        if(queue.size()==k){
            if(queue.peek().getValue()<entry.getValue()){
                queue.poll();
                queue.offer(entry);
            }
        }else{
            queue.offer(entry);
        }
    }
    int[] res = new int[k];
    for(int i=0;i<k;i++){
        res[i] = queue.poll().getKey();
    }
    return res;
}

394.字符串解码

  • 定义当前字符串,当前数字,栈中字符串,栈中数字。
  • 当遇到左括号,将当前数字和当前字符串分别入栈。
  • 当遇到右括号时,取出数字栈顶num1,取出字符串栈顶str1,,str1+遍历累加num1次的cur,并赋值给res。
public String decodeString(String s) {
    StringBuilder res = new StringBuilder();
    int num = 0;
    Deque<StringBuilder> resStack = new ArrayDeque<>();
    Deque<Integer> numStack = new ArrayDeque<>();

    char[] cs = s.toCharArray();

    for(char c:cs){
        if(Character.isDigit(c)){
            num = num * 10 + c - '0';
        }else if(c=='['){
            resStack.offerFirst(res);
            numStack.offerFirst(num);
            num = 0;
            res = new StringBuilder();
        }else if(c==']'){
            StringBuilder tempStr = resStack.pollFirst();
            int tempNum = numStack.pollFirst();
            for(int i=0;i<tempNum;i++){
                tempStr.append(res);
            }
            res = tempStr;
        }else{
            res.append(c);
        }
    }
    return res.toString();
}

除法求值