求职Leetcode题目(8)

1.字符串相乘 

43626da0a22a4fa8b450abaafe5f7fbb.png

第一种方法,直接暴力手撕算法: 

976388810ed24a69b38b7775dfcaff1c.png

class Solution {
    /**
    * 计算形式
    *    num1
    *  x num2
    *  ------
    *  result
    */
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        // 保存计算结果
        String res = "0";
        
        // num2 逐位与 num1 相乘
        for (int i = num2.length() - 1; i >= 0; i--) {
            int carry = 0;
            // 保存 num2 第i位数字与 num1 相乘的结果
            StringBuilder temp = new StringBuilder();
            // 补 0 
            for (int j = 0; j < num2.length() - 1 - i; j++) {
                temp.append(0);
            }
            int n2 = num2.charAt(i) - '0';
            
            // num2 的第 i 位数字 n2 与 num1 相乘
            for (int j = num1.length() - 1; j >= 0 || carry != 0; j--) {
                int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
                int product = (n1 * n2 + carry) % 10;
                temp.append(product);
                carry = (n1 * n2 + carry) / 10;
            }
            // 将当前结果与新计算的结果求和作为新的结果
            res = addStrings(res, temp.reverse().toString());
        }
        return res;
    }

    /**
     * 对两个字符串数字进行相加,返回字符串形式的和
     */
    public String addStrings(String num1, String num2) {
        StringBuilder builder = new StringBuilder();
        int carry = 0;
        for (int i = num1.length() - 1, j = num2.length() - 1;
             i >= 0 || j >= 0 || carry != 0;
             i--, j--) {
            int x = i < 0 ? 0 : num1.charAt(i) - '0';
            int y = j < 0 ? 0 : num2.charAt(j) - '0';
            int sum = (x + y + carry) % 10;
            builder.append(sum);
            carry = (x + y + carry) / 10;
        }
        return builder.reverse().toString();
    }
}

 解法2:

该算法是通过两数相乘时,乘数某位与被乘数某位相乘,与产生结果的位置的规律来完成。具体规律如下:

乘数 num1 位数为 M,被乘数 num2 位数为 N, num1 x num2 结果 res 最大总位数为 M+N
num1[i] x num2[j] 的结果为 tmp(位数为两位,"0x", "xy" 的形式),其第一位位于 res[i+j],第二位位于 res[i+j+1]。

bc240d24439f4f23bebd1a7547f88501.png

75b49ac0c34d49208235d775bf139916.png

3cdeb213aabb4ce593336065e0bf6682.png

class Solution {
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        int m = num1.length(), n = num2.length();
        int[] ansArr = new int[m + n];
        for (int i = m - 1; i >= 0; i--) {
            int x = num1.charAt(i) - '0';
            for (int j = n - 1; j >= 0; j--) {
                int y = num2.charAt(j) - '0';
                ansArr[i + j + 1] += x * y;
            }
        }
        for (int i = m + n - 1; i > 0; i--) {
            ansArr[i - 1] += ansArr[i] / 10;
            ansArr[i] %= 10;
        }
        int index = ansArr[0] == 0 ? 1 : 0;
        StringBuffer ans = new StringBuffer();
        while (index < m + n) {
            ans.append(ansArr[index]);
            index++;
        }
        return ans.toString();
    }
}

 2.最小覆盖子串10a46782312c446085911457ff2388f7.png

c4f596eb397743fd9155ad2d28726734.png

4、对于新加入的字符s[i],如果hs[s[i]] <= ht[s[i]],说明当前新加入的字符s[i]是必需的,且还未到达字符串t所要求的数量。我们还需要事先定义一个cnt变量, cnt维护的是s字符串[j,i]区间中满足t字符串的元素的个数,记录相对应字符的总数。新加入的字符s[i]必需,则cnt++。

5、我们向右扩展滑动窗口的同时也不能忘记收缩滑动窗口。因此当hs[s[j]] > ht[s[j]时,说明hs哈希表中s[j]的数量多于ht哈希表中s[j]的数量,此时我们就需要向右收缩滑动窗口,j++并使hs[s[j]]--,即hs[s[j ++ ]] --。

6、当cnt == t.size时,说明此时滑动窗口包含符串 t 的全部字符。我们重复上述过程找到最小窗口即为答案。 

2d14455775804b989ccc0eaf0617848e.png

class Solution {
    public String minWindow(String s, String t) {
        //维护s串中滑动窗口中各个字符出现次数
        Map<Character, Integer> hs = new HashMap<>();
        //维护t串中各个字符出现次数
        Map<Character, Integer> ht = new HashMap<>();
        for (int i = 0; i < t.length(); i++) {
            ht.put(t.charAt(i), ht.getOrDefault(t.charAt(i), 0)+1);
        }
        String ans="";
        //cnt维护s串[left,right]中满足t串的元素的个数,记录相对应字符的总数
        int len=Integer.MAX_VALUE,cnt=0;
        //区间[left,right]表示当前滑动窗口
        for (int left=0,right = 0; right < s.length(); right++) {
            hs.put(s.charAt(right), hs.getOrDefault(s.charAt(right), 0)+1);
            //如果ht表中也包含当前字符
            if (ht.containsKey(s.charAt(right))) {
                //并且hs表中的字符个数<=ht表中的字符个数,说明该字符是必须的,并且还未到达字符串t所要求的数量
                if (hs.get(s.charAt(right))<=ht.get(s.charAt(right))) {
                    cnt++;
                }
            }
            //收缩滑动窗口
            //如果左边界的值不在ht表中 或者 它在hs表中的出现次数多于ht表中的出现次数
            while(left < right && (!ht.containsKey(s.charAt(left)) || hs.get(s.charAt(left)) > ht.get(s.charAt(left)))){
                    hs.put(s.charAt(left),hs.get(s.charAt(left)) - 1);
                    left++;
            }
            //此时滑动窗口包含符串 t 的全部字符
            if (cnt==t.length()&&right-left+1<len) {
                len=right-left+1;
                ans=s.substring(left,right+1);
            }
        }
        return ans;
    }
}

3.分隔链表ea8ac04d05ed43d28812d30a90250b9c.png

根据题意,考虑通过「新建两个链表」实现原链表分割,算法流程为:

  1. 新建两个链表 sml_dummy , big_dummy ,分别用于添加所有「节点值 <x 」、「节点值 ≥x 」的节点。
  2. 遍历链表 head 并依次比较各节点值 head.val 和 x 的大小:
  3. 若 head.val < x ,则将节点 head 添加至链表 sml_dummy 最后面;
  4. 若 head.val >= x ,则将节点 head 添加至链表 big_dummy 最后面;
  5. 遍历完成后,拼接 sml_dummy 和 big_dummy 链表。
  6. 最终返回头节点 sml_dummy.next 即可。 

cb5504589e6e4fc88b805037fc8e40bf.png

class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode smlDummy = new ListNode(0), bigDummy = new ListNode(0);
        ListNode sml = smlDummy, big = bigDummy;
        while (head != null) {
            if (head.val < x) {
                sml.next = head;
                sml = sml.next;
            } else {
                big.next = head;
                big = big.next;
            }
            head = head.next;
        }
        sml.next = bigDummy.next;
        big.next = null;
        return smlDummy.next;
    }
}

猜你喜欢

转载自blog.csdn.net/2302_79993788/article/details/141290007