leetcode 题解

1. 关于旋转数组

旋转数组求最小值,最大值,以及任意值:
https://leetcode.windliang.cc/leetCode-33-Search-in-Rotated-Sorted-Array.html

2. 198 House Robber

求数组前n个数字相乘的最大值, 与第198题 House Robber解决方法类似;动态规划时需要记住两对状态(显著特点:原题为一维数组的搜索问题,但DP数组的构建实际上是二维数组);

3. 264 Ugly number

题意:求第n个丑数,丑数指该数字的因子只能是2,3,5的组合,1为例外.
思路:假设前n个丑数的数组为ugly[n],在这个数组中,肯定有这样三个数,c1,c2,c3,这三个数分别乘以2,3,5就会比ugly数组中的所有值都大,因此,下一个数为min(c1*2,c2*3,c3*5)。只要从开始就记住c1,c2,c3的位置,每次新加入数字后,就更新相应的c1,c2,c3的位置(例如上一个ugly为c1*2,则c1需要更新为c1=c1+1,更新不要想的太复杂);

class Solution {
    public int nthUglyNumber(int n) {
        int[] c = new int[3];
        int[] ugly = new int[n];
        ugly[0] = 1;
        for(int i=1;i

4. 3 求最长无重复字符串

考虑简单的一种情形,假设字符串为"abacdb",我们使用start表示上一个不重复的位置(start初始值为0),解释如下图所示(s即为start)。


寻找上一个b出现的位置,没找到, 则s=0此时,可计算出不重复的长度为i-s+1=2

然后i=2时,继续先向前寻找出上一个a出现的位置,然后确定s的位置,此时i-s+1=2

继续i向右移动,此时,向前寻找上一个c出现的位置,没有找到,那么此时的s=0,但是如果按照0来计算,则明显错误了因为虽然c没有重复出现,但a重复出现了

因此,这道题,理解第三幅图是重点,此时,s的更新需要考虑上一步的情况,即在本次找到的s和上一次的s之前取最大值即可。简单来说,s只能向右移动,不能回退。
将向前查找使用hashmap来完成,则代码如下:

class Solution {
    public int lengthOfLongestSubstring(String s) {
     Map map = new HashMap();
     int start=0,maxlength=0;
     for(int i=0;imaxlength?length:maxlength;
        map.put(ch,i);
     }
        return maxlength;
    }
}

5 leetcode 第19题 删除链表的倒数第n个节点

题目示意

解法一: 使用两遍循环来解,第一遍求链表总长度,然后使用总长度计算出正向的位置,第二遍循环删除该点;

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        int length = 1;
        ListNode temp = head;
        while(temp.next!=null){
            length++;
            temp = temp.next;
        }
        int N = length-n;
        temp = head;
        for(int i=0;i

解法二:双指针解法。即使用两个指针,这两个指针之间相差n个节点,然后向后移动,当最前面的指针移动到末尾时,后面的指针即是倒数第n个节点。这是解决链表问题常用的解法,例如在判断链表是否有环时也使用快慢指针;注意,链表题中为了便于处理,经常增加一个头结点

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {       
//只有一个节点时,删除自己会有异常,因此在前面多加一个节点
        ListNode headNode = new ListNode(9527);
        headNode.next = head;
        ListNode fast=headNode,slow =headNode;
        int i=0;
        while(fast.next!=null){
            if(n<=0){
                slow = slow.next;
            }
            fast = fast.next;
            n--;
        }
        if(slow.next!=null)
            slow.next = slow.next.next;
        return headNode.next ;
    }
}

4 24 两两交换链表节点;48旋转图像;

注意:交换节点时不要形成环;矩阵的转置操作(其实遍历的是上三角),矩阵的换行操作(其实遍历的是一半的列);

5 49 字符串分组

注意:考察hashmap时,经常需要进行聚合key,但有时key稍微有些差异(例如这道题,字母相同,顺序不同的,也算作相同的key),所以需要对key寻找一个映射函数来区分,或者对key进行简单的变形;

    public List> groupAnagrams(String[] strs) {
        Map> map = new HashMap();
        for(String s: strs){
            char[] ch = s.toCharArray();
            Arrays.sort(ch);
            String key = String.valueOf(ch);
            if(!map.containsKey(key)) map.put(key,new LinkedList());
            map.get(key).add(s);  //注意这里的操作,新元素也要增添进去;      
        }
        return new ArrayList(map.values());     
    }

50. Pow(x, n)

求x的n次方,使用正常的循环解法时间太长,使用递归来解;
对于递归类的问题,首先写其递归方程:

然后分析能不能使用动态规划,这道题明显不需要使用;

class Solution {
    public double myPow(double x, int n) {
      if(n==0) return 1;
      int p = n>0?n:-n;
      double out = help(x,p);
      if(n<0) return 1/out;
      else return out;
    }
    public double help(double x,int n){
       if(n==0) return 1.0; 
       double d = help(x,n/2);
       return d*d*pow0(x,n%2);
    }
    public double pow0(double x,int n){
        if(n==0) return 1.0;
        else return x;
    }
}

51. 69 开方

思路:使用牛顿迭代法开平方;

  • 求迭代方程;
  • 定义cur,pre;
  • 更新cur,pre;
  • 当abs(cur-pre)>0.0001时,结束
class Solution {
    public int mySqrt(int x) {
        double pre=1,cur=1;
        do{
            pre = cur;
            cur = pre/2.0+x/(2*pre);
        }while(Math.abs(cur-pre)>0.1);
        return (int)cur;
    }
}

70 跳台阶

刚开始使用DFS来解,但n太大时会超时;

class Solution {
    public int climbStairs(int n) {
        return help(n,0);
    }
    //------n>44时会超时----------
    public int help(int n,int sum){
        if(sum==n){
            return 1;
        }
        if(sum>n){
            return 0;
        }
        int c1=0,c2=0;
        return help(n,sum+1)+help(n,sum+2);            
    }    
}

使用动态规划来做:
注意,看当前状态是由前那几个状态到达的,这很重要,比如树型结构
分清是状态汇聚(多变1),还是状态分裂(1变多),这两种情况对应的DP的顺序定义不一样

class Solution {
    public int climbStairs(int n) {
        if(n==1) return 1;
        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        for(int i=3;i<=n;i++){
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

数组长度的一半的数

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
用栈结构来模拟:两数字不同时从数组中去掉,最后剩下的就是该数字;
https://bestswifter.com/arrayoccurmorethanhalf/
用次数的方法:该数字出现次数大于其他数字出现的数字之和;
https://zhuanlan.zhihu.com/p/39538880

数据流中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

import java.util.*;

public class Solution {
    PriorityQueue minHeap = new PriorityQueue();
    PriorityQueue maxHeap = new PriorityQueue(11, new Comparator(){
        public int compare(Integer o1,Integer o2){
            return o2.compareTo(o1);
        }
    });
    int count = 0;
    public void Insert(Integer num) {
       count++;
       if(count%2==0){
           if(!maxHeap.isEmpty()&&numminHeap.peek()){
                minHeap.offer(num);
                num = minHeap.poll();
            }
            maxHeap.offer(num);
        }
    }

    public Double GetMedian() {
        double result = 0;
        if(count%2==0){
            result =  (minHeap.peek()+maxHeap.peek())/2.0;
        }
        else{
            result =  maxHeap.peek();
        }
        return result;
        
    }


}

你可能感兴趣的:(leetcode 题解)