剑指Offer优解

剑指Offer

不用加减乘除做加法(位运算)

class Solution{
    public int add(int a, int b){
        while(b!=0){
            int c = (a&b)<<1;//进行进位,向前进一个
            a ^= b;//异或相同为0,不同为1
            b = c;
        }
        return a;
    }
}

将无进位和、进位分开

{   n = a ⨁ b c = a & b < < 1 \begin{cases} \ n=a\bigoplus b\\ c=a\&b<<1 \end{cases} { n=abc=a&b<<1

翻转单词顺序

public class Solution{
    public String reverseWords(String s){
        StringBuilder sb = new StringBuilder();
        String[] str = s.trim().split(" ");
        for(int i=str.length-1;i>=0;i--){
            if(str[i].equals(" ")) continue;
            sb.append(str[i] + " ");
        }
        return sb.toString().trim();
    }
}

数组中数字出现的次数||,只有一个出现一次,其他出现了三次

出现了三次的数字二级制每一位之和是3的倍数。

class Solution{
    public int singleNumber(int[] nums){
        int[] counts = new int[32];
        for(int num:nums){
            for(int j=0;j<32;j++){
                counts[j] += num & 1;
                num >>>= 1;
            }
        }
        int res = 0, m = 3;
        for(int i=0;i<32;i++){
            res <<= 1;
            res |= counts[31 - i] % m;
        }
        return res;
    }
}

数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是 O ( n ) O(n) O(n),空间复杂度是 O ( 1 ) O(1) O(1)

class Solution{
    public int[] singleNumbers(int[] nums){
        int xorNumber = nums[0];
        for(int i=1;i<nums.length;i++){
            xorNumber ^= nums[i];
        }
        int onePosition = xorNumber&(-xorNumber);
        int ans1 = 0, ans2 = 0;
        for(int i=0;i<nums.length;i++){
            if((onePosition&nums[i]) == onePosition){
                ans1 ^= nums[i];
            }else{
                ans2 ^= nums[i];
            }
        }
        return new int[]{ans1^0, ans2^0};
    }
}

判断平衡二叉树

方法一:后序遍历+剪枝(从底至顶)

对二叉树进行后序遍历,从底至顶返回子树的深度,如判定某子树不是平衡树,则剪枝,直接向上返回。

class Solution{
    public boolean isBalanced(TreeNode root){
        return recur(root) != -1;
    }
    private int recur(TreeNode root){
        if(root == null) return 0;
        int left = recur(root.left);
        if(left == -1) return -1;
        int right = recur(root.right);
        if(right == -1) return -1;
        return Math.abs(left - right) < 2 ? Math.max(left,right) + 1:-1;
    }
}

二叉搜索树的第K大结点,中序遍历倒序为递减序列

class Solution{
    int res, k;
    public int kthLargest(TreeNode root, int k){
        this.k = k;
        dfs(root);
        return res;
    }
    
    void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.right);
        if(k == 0) return;
        if(--k == 0) res = root.val;
        dfs(root.left);
    }
}

0~n-1中缺失的数字

二分

public int missingNumber(int[] nums){
    int left = 0;
    int right = nums.length;
    while(left < right){
        int mid = (left + right) / 2;
        if(nums[mid] != mid) right = mid;
        else{
            left = mid + 1;
        }
    }
    return left;
}

在排序数组中查找数字|

统计一个数字在排序数组中出现的次数。

两个右边界相减

class Solution{
    public int search(int[] nums,int target){
        return helper(nums, target) - helper(nums, target - 1);
    }
    
    int helper(int[] nums, int target){
        int i = 0, j = nums.length - 1;
        while(i <= j){
            int m = (i + j) / 2;
            if(nums[m] <= target) i = m + 1;
            else j = m-1;
        }
        if(i >= right || nums[i]!= target) return -1;
        return i;
    }
}

数组中的逆序对

class Solution{
    public int reversePairs(int[] nums){
        int len = nums.length;
        if(len == 0) return 0;
        return mergeSort(nums,0,len-1);
    }
    
    public int mergeSort(int[] nums, int left, int right){
        if(left >= right) return 0;
        int mid = (right + left) >> 1;
        int l = mergeSort(nums,left,mid);
        int r = mergeSort(nums,mid+1,right);
        return (l+r+merge(nums,left,mid,right));
    }
    
    public int merge(int[] nums,int left, int mid, int right){
        int[] temp = new int[right-left+1];
        int ans = 0;
        int cur1 = mid;
        int cur2 = right;
        int cur3 = right - left;
        while(cur1 >= left&&cur2 >= mid+1){
            if(nums[cur1]<=nums[cur2]){
                temp[cur3--] = nums[cur2--];
            }else{
                temp[cur3--] = nums[cur1--];
                ans += cur2-mid;
            }
        }
        while(cur1 >= left){
            temp[cur3--] = nums[cur1--];
        }
        while(cur2 >= mid+1){
            temp[cur3--] = nums[cur2--];
        }
        int x = 0;
        while(left<=right){
            nums[left++]=temp[x++];
        }
        return ans;
    }
}

自己更能理解的

public int reversePairs(int[] nums) {
     return merge(nums, 0, nums.length - 1);
}

int merge(int[] arr, int start, int end) {
    if (start == end) return 0;
     int mid = (start + end) / 2;
     int count = merge(arr, start, mid) + merge(arr, mid + 1, end);

     int[] temp = new int[end - start + 1];
     int i = start, j = mid + 1, k = 0;
     while (i <= mid && j <= end) {
     count += arr[i] <= arr[j] ? j - (mid + 1) : 0;
        temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
     }
     while (i <= mid) {
        count += j - (mid + 1);
        temp[k++] = arr[i++];
     }
     while (j <= end)
         temp[k++] = arr[j++];
     System.arraycopy(temp, 0, arr, start, end - start + 1);
     return count;
}

礼物的最大价值

**思路:**礼物的最大价值,

class Solution{
    public int maxValue(int[][] grid){
        int m = grid.length, n = grid[0].length;
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                if(i == 0 && j == 0) continue;
                if(i == 0) grid[i][j] += grid[i][j-1];
                else if(j == 0) grid[i][j] += grid[i-1][j];
                else grid[i][j] += Math.max(grid[i-1][j], grid[i][j-1]);
                
            }
        }
        return grid[m-1][n-1];
    }
}
class Solution{
    public int maxValue(int[][] grid){
        int m = grid.length, n = grid[0].length;
        for(int i=1;i<m;i++){
            grid[i][0] += grid[i-1][0];
        }
        for(int j = 1;j < n;i++){
            grid[0][j] += grid[0][j-1];
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                grid[i][j] += Math.max(grid[i-1][j], grid[i][j-1]);
            }
        }
        return grid[m-1][n-1];
    }
}
//多开一行一列能使代码更加简洁
class Solution{
    public int maxValue(int[][] grid){
        int row = grid.length;
        int column = grid[0].length;
        
        int[][] dp = new int[row+1][column+1];
        for(int i=1;i<= row;i++){
            for(int j=1;j<=column;j++){
                dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]) + grid[i-1][j-1];
            }
        }
        return dp[row][column];
    }
}

把数字翻译成字符串

动态规划:字符串遍历

class Solution{
    public int translateNum(int num){
        String s = String.valueOf(num);
        int a = 1, b = 1;
        for(int i=2;i<=s.length();i++){
            String tmp = s.substring(i - 2,i);
            int c = tmp.compareTo("10") >= 0 && tmp.compareTo("25") <=0? a+b:b;
            a = b;
            b = c;
        }
        return b;
    }
}

更直观的方法:

class Solution{
    public int translateNum(int num){
        String s = String.valueOf(num);
        int[] dp = new int[s.length() + 1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i=2;i<=s.length();i++){
            String temp = s.substring(i-2,i);
            if(temp.compareTo("10") >= 0 && temp.compateTo("25") <= 0){
                dp[i] = dp[i-1]+dp[i-2];
            }else{
                dp[i] = dp[i-1];
            }
        }
        return dp[s.length()];
    }
}

把数组排成最小的数

本质上是一个排序问题

示列

输入: [10,2]
输出: "102"
class Solution{
    public String minNumber(int[] nums){
        String[] strs = new String[nums.length];
        for(int i=0;i<nums.length;i++){
            strs[i] = String.valueOf(nums[i]);
        }
        fastSort(strs, 0, strs.length - 1);
        StringBuilder res = new StringBuilder();
        for(String s: strs){
            res.append(s);
        }
        return res.toString();
    }
    private void fastSort(String[] strs, int l, int r){
        if(l >= r) return;
        int i=l, j = r;
        String tmp = strs[i];
        while(i < j){
            while((strs[j] + strs[l]).compareTo((strs[l] + strs[j]))>=0 && i < j) j--;
            while((strs[i] + strs[l]).compareTo((strs[l] + strs[i])) <= 0 && i < j) i++;
            tmp = strs[i];
            strs[i] = strs[j];
            strs[j] = tmp;
        }
        strs[i] = strs[l];
        strs[l] = tmp; 
        fastSort(strs, l, i-1);
        fastSort(strs, i+1, r);
    }
}


class Solution {
    public String minNumber(int[] nums) {
        String[] strs = new String[nums.length];
        for(int i = 0; i < nums.length; i++)
            strs[i] = String.valueOf(nums[i]);
        fastSort(strs, 0, strs.length - 1);
        StringBuilder res = new StringBuilder();
        for(String s : strs)
            res.append(s);
        return res.toString();
    }
    void fastSort(String[] strs, int l, int r) {
        if(l >= r) return;
        int i = l, j = r;
        String tmp = strs[i];
        while(i < j) {
            while((strs[j] + strs[l]).compareTo(strs[l] + strs[j]) >= 0 && i < j) j--;
            while((strs[i] + strs[l]).compareTo(strs[l] + strs[i]) <= 0 && i < j) i++;
            tmp = strs[i];
            strs[i] = strs[j];
            strs[j] = tmp;
        }
        strs[i] = strs[l];
        strs[l] = tmp;
        fastSort(strs, l, i - 1);
        fastSort(strs, i + 1, r);
    }
}

数字序列中某一位的数字

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

思路:

  • 确定 n所在 数字 的 位数 ,记为 digit
  • 确定 n所在的 数字 ,记为 num
  • 确定nnum中的哪一数位,并返回结果。
class Solution{
    //n是位数
    public int findNthDigit(int n){ //n称为数位
        int digit = 1; //位数,比如10表示两位数
        long start = 1;//每digit位数的起始数字
        long count = 9;
        while(n > count){
            n -= count;
            digit += 1;
            start *= 10;
            count = digit * start * 9;
        }
        long num = start + (n - 1) / digit;
        return Long.toString(num).charAt((n-1) % digit) - '0';
    }
}

1~n整数中1出现的次数

怎样去理解:生活中常见的密码锁,就是那种几个滚轮的密码锁,固定其中的一位密码,拨动其他位置的密码

class Solution{
    public int countDigitOne(int n){
        int digit = 1, res = 0;
        int high = n/10, cur = n%10, low = 0;
        while(high != 0 || cur != 0){
            if(cur == 0) res += high * digit;
            else if(cur == 1) res += high * digit + low + 1;
            else res += (high + 1) * digit;
            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        return res;
    }
}

连续子数组的最大和

思路:动态规划

class Solution{
    public int maxSubArray(int[] nums){
        int res = nums[0];
        for(int i=1;i<nums.length;i++){
            nums[i] += Math.max(nums[i-1],0);
            res = Math.max(res, nums[i]);
        }
        return res;
    }
}

数据流中的中位数

建立一个小顶堆和一个大顶堆

class MedianFinder{
    Queue<Integer> A,B;
    public MediaFinder(){
        A = new PriorityQueue<>();
        B = new PriorityQueue<>((x,y)->(y-x));
    }
    public void addNum(int num){
        if(A.size() != B.size()){
            A.add(num);
            B.add(A.poll());
        }else{
            B.add(num);
            A.add(B.poll());
        }
    }
    public double findMedian(){
        return A.size() != B.size() ? A.peek():(A.peek() + B.peek()) / 2.0;
    }
}

数组中出现次数超过一半的数字(摩尔投票法)

class Solution{
    public int majorityElement(int[] nums){
        int x = 0, votes = 0;
        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }
        return x;
    }
}

字符串的排列

class Solution{
    List<String> res = new LinkedList<>();
    char[] c;
    public String[] permutation(String s){
        c = s.toCharArray();
        dfs(0);
        return res.toArray(new String[res.size()]);
    }
    
    void dfs(int x){
        if(x == c.length - 1){
            res.add(String.valueOf(c));
            return;
        }
        HashSet<Character> set = new HashSet<>();
        for(int i = x;i < c.length;i++){
            if(set.contains(c[i])) continue;
            set.add(c[i]);
            swap(i,x);
            dfs(x + 1);
            swap(i,x);
        }
    }
    void swap(int a, int b){
        char tmp = c[a];
        c[a] = c[b];
        c[b] = tmp;
    }
}

序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null) return new String("[]");
        StringBuilder res = new StringBuilder("[");
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(node != null){
                res.append(node.val + ",");
                queue.add(node.left);
                queue.add(node.right);
            }else{
                res.append("null,");
            }
        }
        res.deleteCharAt(res.length() - 1);
        res.append("]");
        return res.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]")) return null;
        String[] vals = data.substring(1,data.length() - 1).split(",");
        TreeNode root = new TreeNode(Integer.valueOf(vals[0]));
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int i = 1;
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(!vals[i].equals("null")){
                node.left = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.left);
            }
            i++;
            if(!vals[i].equals("null")){
                node.right = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.right);
            }
            i++;
        }
        return root;
    }
}

二叉搜索树与双向链表

输入一颗二叉搜索树,将该二叉树转换为一个排序的循环双向链表。要求不能创建任何新的结点,只能调整树中接地那指针的指向。

/*
class Node{
	public int val;
	public Node left;
	public Node right;
	
	public Node(){
		
	}
	public Node(int _val){
		val = _val;
	}
	public Node(int _val,Node _left,Node _right){
		val = _val;
		left = _left;
		right = _right;
	}
}
*/
class Solution{
    Node pre,head;
    public Node treeToDoublyList(Node root){
        if(root == null) return null;
        dfs(root);
        head.left = pre;
        pre.right = head;
        return head;
    }
    void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.left);
        if(pre != null) pre.right = root;
        else head = root;
        root.left = pre;
        pre = root;
        dfs(root.right);
    } 
}

复杂链表的复制

HashMap

class Solution{
    public Node copyRandomList(Node head){
        HashMap<Node, Node> map = new HashMap<>();
        Node cur = head;
        while(cur != null){
            map.put(cur,new Node(cur.val));
            cur = cur.next;
        }
        cur = head;
        while(cur != null){
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(head);
    }
}
class Solution {
    LinkedList<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>(); 
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        recur(root, sum);
        return res;
    }
    void recur(TreeNode root, int tar) {
        if(root == null) return;
        path.add(root.val);
        tar -= root.val;
        if(tar == 0 && root.left == null && root.right == null)
            res.add(new LinkedList(path));
        recur(root.left, tar);
        recur(root.right, tar);
        path.removeLast();
    }
}

第一个只出现一次的字符(有序哈希表)

class Solution{
    public char firstUniqChar(String s){
        Map<Character, Boolean> map = new LinkedHashMap<>();
        char[] sc = s.toCharArray();
        for(char c:sc){
            map.put(c, !dic.containsKey(c));
        }
        for(Map.Entry<Character,Boolean> d:dic.entrySet()){
            if(d.getValue()) return d.getKey();
        }
        return ' ';
    }
}

丑数

class Solution{
    public int nthUglyNumber(int n){
        int a = 0, b = 0, c = 0;
        int[] dp = new int[n];
        dp[0] = 1;
        for(int i=1;i<n;i++){
            int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
            dp[i] = Math.min(Math.min(n2,n3),n5);
            if(dp[i] == n2) a++;
            if(dp[i] == n3) b++;
            if(dp[i] == n5) c++;
        }
        return dp[n-1];
    }
}

验证搜索树的后序遍历序列(递归分治)

二叉树的后序遍历序列

class Solution{
    public boolean verifyPostorder(int[] postorder){
        return recur(postorder,0,postorder.length - 1);
    }
    boolean recur(int[] postorder,int i, int j){
        if(i >= j) return true;
        int p = i;
        while(postorder[p] < postorder[j]) p++;
        int m = p;
        while(postorder[p] > postorder[j]) p++;
        return p == j && recur(postorder,i,m-1) && recur(postorder,m,j-1);
    }
}

从上到下打印二叉树|||(BFS/双端队列)

class Solution{
    public List<List<Integer>> levelOrder(TreeNode root){
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> res = new LinkedList<>();
       	if(root != null) queue.add(root);
        while(!queue.isEmpty()){
            LinkedList<Integer> tmp = new LinkedList<>();
            for(int i = queue.size();i > 0;i--){
                TreeNode node = queue.poll();
                if(res.size() % 2 == 0) tmp.addLast(node.val);
                else{
                    tmp.addFirst(node.val);
                }
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            res.add(tmp);
        }
        return res;
    }
}

Shell排序

class Solution{
    public shellSort(int[] arr){
        for(int gap = arr.length / 2;gap >=0;gap /= 2){
            for(int i = gap; i<arr.length;i++){
                int j = i;
                int temp = arr[j];
                if(arr[j] < arr[j - gap]){
                    while(j-gap >= 0 && temp < arr[j - gap]){
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    arr[j] = temp;
                }
            }
        }
    }
}

计数排序

public static int[] countSort(int[] nums){
    int max = Integer.MIN_VALUE;
    for(int num : nums){
        if(num > max){
            max = num;
        }
    }
    int[] count = new int[max + 1];
    for(int num : nums){
        count[num]++;
    }
    int[] result = new int[nums.length];
    int index = 0;
    for(int i=0;i<max;i++){
        while(count[i] > 0){
            result[index++] = i;
            count[i]--;
        }
    }
    return result;
}

生产者和消费者

利用synchronized实现

import java.util.LinkedList;

public class Main {
    //常量
    private static int MAX_VALUE = 100;
    public static void main(String[] args) {
        Concurrentcomm con = new Concurrentcomm();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i= 0;i < MAX_VALUE;i++){
                        Thread.sleep(0);
                        con.product();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
        //消费者
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    for (int i=0;i<MAX_VALUE;i++){
                        con.customer();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

class Concurrentcomm{
    //常量
    private static int MAX_VALUE = 10;
    //缓存
    LinkedList<String> linkedList = new LinkedList<>();
//    Object object = new Object();

    /**
     * 生产者方法
     * @throws Exception
     */
    public void product() throws Exception{
        synchronized (linkedList){
            while (MAX_VALUE == linkedList.size()){
                System.out.println("仓库已满,【生产者】:暂时不能执行生成任务!");
                linkedList.wait();
            }
            linkedList.push("李四");
            System.out.println("【生产者】:生产了一个产品\t【现仓储量为】:" + linkedList.size());
            linkedList.notify();
        }
    }

    public void customer() throws Exception{
        /**
         * 根据jdk的void notifyAll()的描述,“解除那些在该对象上调用wait()方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。
         * 如果当前线程不是对象所得持有者,
         * 该方法抛出一个java.lang.IllegalMonitorStateException异常
         * 因此,我们使用同一把锁
         */
        synchronized (linkedList){
            //多线程判断中使用while不要使用if否则会出现虚假唤醒问题
            while(linkedList.size() == 0){
                System.out.println("仓库无货,【消费者】:暂时不能执行消费任务!");
                linkedList.wait();
            }
            linkedList.poll();
            System.out.println("【消费者】:消费了一个产品\t【现仓储量为】:" + linkedList.size());
            linkedList.notify();
        }
    }
}

利用阻塞队列实现

//生产者
class Producer implements Runnable{
    //共享阻塞队列
    private BlockingDeque<Data> queue;
    //是否还在运行
    private volatile boolean isRunning = true;
    //id生成器
    private static AtomicInteger count = new AtomicInteger();
    //生成随机数
    private static Random random = new Random();

    public Producer(BlockingDeque<Data> queue){
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while(isRunning){
                //模拟注水耗时
                Thread.sleep(random.nextInt(1000));
                int num = count.incrementAndGet();
                Data data = new Data(num, num);
                System.out.println("当前>>注水管:"+Thread.currentThread().getName()+"注水容量(L):"+num);
                if(!queue.offer(data,2, TimeUnit.SECONDS)){
                    System.out.println("注水失败...");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void stop(){
        isRunning = false;
    }
}

//消费者
class Consumer implements Runnable{

    private BlockingDeque<Data> queue ;

    private static Random random = new Random();

    public Consumer(BlockingDeque<Data> queue){
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            try {
                Data data = queue.take();
                //模拟抽水耗时
                Thread.sleep(random.nextInt(1000));
                if(data != null){
                    System.out.println("当前<<抽水管:"+Thread.currentThread().getName()+",抽取水容量(L):"+data.getNum());
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }
}

//生产的数据
/**
 * 建立一个模拟数据,水
 */
class Data {
    private int id;
    private int num;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Data(int id, int num) {
        this.id = id;
        this.num = num;
    }

    public Data() {

    }
}

import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) throws InterruptedException {

        BlockingDeque<Data> queue = new LinkedBlockingDeque<>(10);

        Producer producer1 = new Producer(queue);
        Producer producer2 = new Producer(queue);
        Producer producer3 = new Producer(queue);

        Consumer consumer1 = new Consumer(queue);
        Consumer consumer2 = new Consumer(queue);
        Consumer consumer3 = new Consumer(queue);

        ExecutorService service = Executors.newCachedThreadPool();
        ExecutorService service1 = Executors.newFixedThreadPool(2);
//        service.execute(producer1);
//        service.execute(producer2);
//        service.execute(producer3);
        service1.execute(producer1);
        service1.execute(consumer1);


//        service.execute(consumer1);
//        service.execute(consumer2);
//        service.execute(consumer3);

        Thread.sleep(3000);
        producer1.stop();
//        producer2.stop();
//        producer3.stop();

        Thread.sleep(1000);
        service.shutdown();
    }
}

验证栈的压入、弹出序列

class Solution{
    public boolean validateStackSequences(int[] pushed, int[] popped){
        LinkedList<Integer> stack = new LinkedList<>();
        int i = 0;
        for(int num : pushed){
            stack.push(num);
            while(!stack.isEmpty() && stack.peek() == popped[i]){
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

包含min函数的栈(辅助栈,清晰图解)

class MinStack{
    Stack<Integer> A,B;
    public MinStack(){
        A = new Stack<>();
        B = new Stack<>();
    }
    public void push(int x){
        A.add(x);
        if(B.empty() || B.peek() >= x){
            B.add(x);
        }
    }
    public void pop(){
        if(A.pop().equals(B.peek())){
            B.pop();
        }
    }
    publit int top(){
        return A.peek();
    }
    public int min(){
        return B.peek();
    }
}

顺时针打印矩阵

class Solution{
    public int[] spiralOrder(int[][] matrix){
        if(matrix == null) return new int[0];
        int l= 0, r = matrix[0].length - 1,t=0,b = matrix.length - 1,x = 0;
        int[] res = new int[(r + 1)*(b+1)];
        while(true){
            for(int i=l;i<=r;i++) res[x++] = matrix[t][i];
            if(++t > b) break;
            for(int i=t;i<=b;i++) res[x++] = matrix[i][r];
            if(l > --r) break;
            for(int i=r;i>=l;i--) res[x++] = matrix[b][i];
            if(t > --b) break;
            for(int i=b;i>=t;i--) res[x++] = matrix[i][l];
            if(++l > r) break;
        }
        return res;
    }
}

对称二叉树(递归)

class Solution{
    public boolean isSymmetric(TreeNode root){
        return isMirror(root,root);
    }
    public boolean isMirror(TreeNode A,TreeNode B){
        if(A == null && B == null) return true;
        if(A == null || B == null) return false;
        return A.val == B.val && isMirror(A.left,B.right) && isMirror(A.right, B.left);
    }
}

二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。左右结点交换。

class Solution{
    public TreeNode mirrorTree(TreeNode root){
        if(root == null) return root;
        TreeNode Node = mirrorTree(root.left);
        root.left = mirrorTree(root.right);
        root.right = Node;
        return root;
    }
}

是否是树的子结构

class Solution{
    public boolean isSubStructure(TreeNode A,TreeNode B){
        return (A!=null && B!=null) && (recur(A,B) || isSubstructure(A.left,B) || isSubstructure(A.right,B));
    }
    
    private boolean recur(TreeNode A,TreeNode B){
        if(B == null) return true;
        if(A == null || A.val != B.val)  return false;
        return recur(A.left,B.left)&&recur(A.right,B.right);
    }
}

合并两个排序的链表

//定义一个哑结点
while中是&&

翻转链表

public ListNode reverseList(ListNode head){
    if(head == null || head.next == null){
        return head;
    }
    ListNode newList = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return newList;
}

调整数组顺序使奇数位于偶数前面(双指针)

class Solution{
    public int[] exchange(int[] nums){
        int i = 0, j = nums.length - 1, tmp;
        while(i < j){
        	while(i < j && nums[i] % 2 != 0){
            	i++;
        	}
        	while(i < j && nums[j]%2 == 0){
            	j--;
        	}
        	tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
        return nums;
    }
}

表示数值的字符串(有限状态自动机)-判断字符串是否表示数值

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100""5e2"、"-123"、"3.1416"、"0123"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"、"-1E-16"及"12e+5.4"都不是。

class Solution{
    public boolean isNumber(String s){
        Map[] states = {
            new HashMap<>() {{put(' ',0);put('s',1);put('d',2);put('.',4);}},      //0
            new HashMap<>() {{put('d',2);put('.',4);}},                            //1
            new HashMap<>() {{put('d',2);put('.',3);put('e',5);put(' ',8);}},       //2
            new HashMap<>() {{put('d',3);put('e',5);put(' ',8);}},                 //3
            new HashMap<>() {{put('d',3);}},
            new HashMap<>() {{ put('s', 6); put('d', 7); }},                        // 5.
            new HashMap<>() {{ put('d', 7); }},                                     // 6.
            new HashMap<>() {{ put('d', 7); put(' ', 8); }},                        // 7.
            new HashMap<>() {{ put(' ', 8); }}                                      // 8.
        };
        int p = 0;
        char t;
        for(char c : s.toCharArray()){
            if(c >= '0' && c <= '9') t = 'd';
            else if(c == '+' || c == '-') t = 's';
            else if(c == '.' || c == 'e' || c == 'E' || c == ' ') t = c;
            else t = '?';
            if(!states[p].containsKey(t)) return false;
            p = (int)states[p].get(t);
        }
        return p == 2 || p == 3 || p == 7 || p == 8;
    }
}

乘积最大子序列

状态转移方程: i m a x = m a x ( i m a x ∗ n u m s [ i ] , n u m s [ i ] ) imax=max(imax*nums[i],nums[i]) imax=max(imaxnums[i],nums[i])

i m i n = m i n ( i m i n ∗ n u m s [ i ] , n u m s [ i ] ) imin=min(imin*nums[i],nums[i]) imin=min(iminnums[i],nums[i])

class Solution{
    public int maxProduct(int[] nums){
        int max = Integer.MIN_VALUE,imax = 1, imin = 1;
        for(int i=0;i<nums.length;i++){
            if(nums[i] < 0){
                int tmp = imax;
                imax = imin;
                imin = tmp;
            }
            imax = Math.max(imax * nums[i], nums[i]);
            imin = Math.min(imin * nums[i], nums[i]);
            
            max = Math.max(max,imax);
        }
        return max;
    }
}

两个字符串正则化匹配

class Solution{
    public boolean isMatch(String A,String B){
        int n = A.length();
        int m = B.length();
        boolean[][] f = new boolean[n+1][m+1];
        
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                if(j == 0){
                    f[i][j] = i == 0;
                }else{
                    if(B.charAt(j-1)!='*'){
                        if(i > 0 && (A.charAt(i-1) == B.charAt(j-1) || B.charAt(j-1) == '.')){
                            f[i][j] = f[i-1][j-1];
                        }
                    }else{
                        if(j >= 2){
                            f[i][j] |= f[i][j-2];
                        }
                        if(i >= 1 && j >= 2 && (A.charAt(i-1) == B.charAt(j-2)||B.charAt(j-2) == '.')){
                            f[i][j] |= f[i-1][j];
                        }
                    }
                }
            }
        }
        return f[n][m];
    }
}

删除链表的结点(利用辅助指针删除)

class Solution{
    public ListNode deleteNode(ListNode head, int val){
        if(head.val == val) return head.next;
        ListNode pre = head, cur = head.next;
        while(cur != null && cur.val != val){
            pre = cur;
            cur = cur.next;
        }
        pre.next = cur.next;
        return head;
    }
}

打印从1到最大的n位数(分治算法 / 全排列)

当是很大的数的时候???

  • 固定高位,进行递归
	static StringBuilder res;
    static int count = 0, n1, nine = 0, start;
    static char[] num, loop = {'0','1','2','3','4','5','6','7','8','9'};
    public static String printNumbers(int n){
        n1 = n;
        res = new StringBuilder();
        num = new char[n1];
        start = n - 1;
        dfs(0);
        res.deleteCharAt(res.length() - 1);
        return res.toString();
    }

    private static void dfs(int x){
        if( x == n1){
            String s = String.valueOf(num).substring(start);
            if (!s.equals("0"))
                res.append(s + ",");
            if (n1 - start == nine) start--;
            return;
        }
        for (char i : loop){
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }

数值的整数次方(快速幂)

class Solution{
    public double myPow(double x, int n){
        if(x == 0) return 0;
        long b = n;
        double res = 1.0;
        if(b < 0){
            x = 1 / x;
            b = -b;
        }
        while(b > 0){
            if((b & 1) == 1) res *= x;
            x *= x;
            b >>= 1;
        }
        return res;
    }
}
5
1 2 +
3 4 -
1000000000 1000000000 *
2 3 ^
2 1000000000 ^

二分法角度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5zKh3QUF-1603511882220)(C:\Users\xwj\AppData\Roaming\Typora\typora-user-images\image-20200807212506210.png)]

答案和上面是一样的

剪绳子||(数学推导/贪心思想+快速幂求导)

推论:1.当所有绳子长度相等时,乘积最大

2.最优的绳子长度为3

大数求余发

解决方案: 循环求余 、 快速幂求余 ,其中后者的时间复杂度更低,两种方法均基于以下求余运算规则推出:

( x y ) ⊙ p = [ ( x ⊙ p ) ( y ⊙ p ) ] ⊙ p (xy) \odot p = [(x \odot p)(y \odot p)] \odot p (xy)p=[(xp)(yp)]p

本题中:二分求余法

class Solution{
    public int cuttingRope(int n){
        if(n <= 3) return n-1;
        int b = n % 3, p = 1000000007;
        long rem = 1, x = 3;
        for(int a = a / 3-1;)
    }
}

快速幂求余

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n - 1;
        int b = n % 3, p = 1000000007;
        long rem = 1, x = 3;
        for(int a = n / 3 - 1; a > 0; a /= 2) {
            if(a % 2 == 1) rem = (rem * x) % p;
            x = (x * x) % p;
        }
        if(b == 0) return (int)(rem * 3 % p);
        if(b == 1) return (int)(rem * 4 % p);
        return (int)(rem * 6 % p);
    }
}

循环求余法(贪心算法)

class Solution{
    if(n <= 3) return n - 1;
    long res = 1L;
    int p = (int)le9+7;
    while(n > 4){
        res = res*3%p;
        n -= 3;
    }
    //出来循环只有三种情况,分别是n=2、3、4
    return (int)(res*n%p);
}

机器人的运动路径

地上有一个m行n列的方格,从坐标[0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

public class Solution {
    private int[][] direct = {{1, 0}, {0, 1},{-1,0},{0,-1}};
    private boolean[][] visited;
    private int res = 0;
    private int M;
    private int N;

    public int movingCount(int m, int n, int k) {
        this.visited = new boolean[m][n];
        this.M = m;
        this.N = n;
        dfs(0, 0, k);
        return res;
    }

    private void dfs(int x, int y, int k) {
        if (!inArea(x, y) || visited[x][y]) {
            return;
        }
        if (x / 10 + x % 10 + y / 10 + y % 10 <= k) {
            res++;
            visited[x][y] = true;
            for (int i = 0; i < 4; i++) {
                int newX = x + direct[i][0];
                int newY = y + direct[i][1];
                dfs(newX, newY, k);
            }
        }
    }

    private boolean inArea(int x, int y) {
        return x >= 0 && y >= 0 && x < M && y < N;
    }
}

滑动窗口的最大值

利用一个队列来维护出口最大值,即滑动窗口的最大值

class Solution{
    public int[] maxSlidingWindow(int[] nums, int k){
        if(nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for(int j=0,i<1-k;j<nums.length;i++,j++){
            if(i > 0 && deque.peekFirst() == nums[i-1]){
                deque.removeFirst();
            }
            while(!deque.isEmpty() && deque.peekLast() < nums[j]){
                deque.removeLast();
            }
            deque.addLast(nums[j]);
            if(i >= 0){
                res[i] = deque.peekFirst();
            }
            return res;
        }
    }
}

矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

class Solution{
    public boolean exist(char[][] board, String word){
        char[] words = word.toCharArray();
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                if(dfs(board,words,i,j,0)) return true;
            }
        }
        return false;
    }
    boolean dfs(char[][] board, char[] word,int i, int j,int k){
        if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
        if(k == word.length - 1) return true;
        char tmp = board[i][j];
        board[i][j] = '/';
        boolean res = dfs(board,word,i+1,j,k+1) ||dfs(board,word,i-1,j,k+1) ||dfs(board,word,i,j+1,k+1) ||dfs(board,word,i,j-1,k+1);
        board[i][j] = tmp;
        return res;
    }
}

旋转数组的最小数字

class Solution{
    public int minArray(int[] numbers){
        int i=0, j = numbers.length - 1;
        while(i < j){
            int m = (i + j) / 2;
            if(numbers[m] > numbers[j]) i = m+1;
            else if(numbers[m] < numbers[j]) j = m;
            else j--;
        }
    }
}

数组中重复的数字

思路:原地置换

如果没有重复数字,那么正常排序后,数字i应该在下标i的位置,所以思路就是重头扫描数组,遇到下标为i的数字如果不是i的话,nums[i]nums[nums[i]]进行交换。

class Solution{
    public int findRepeatNumber(int[] nums){
        int temp;
        for(int i=0;i< nums.length;i++){
            while(nums[i]!=i){
                if(nums[i] == nums[nums[i]]){
                    return nums[i];
                }
                temp = nums[i];
                nums[i] = nums[temp];
                nums[temp] = temp;
            }
        }
        return -1;
    }
}

缺失的第一个整数

class Solution{
    public int firstMissingPositive(int[] nums){
        int len = nums.length;
        for(int i=0;i<len;i++){
            while(nums[i]>0&nums[i] <=len&&nums[nums[i]-1]!=nums[i]){
                swap(nums,nums[i]-1,i);
            }
        }
        for(int i=0;i<len;i++){
            if(nums[i] != i+1){
                return i+1;
            }
        }
        return len + 1;
    }
    private void swap(int[] nums,int index1,int index2){
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
}

跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

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

斐波那契数列

思路:初始化三个整形遍历sumab,利用辅助变量sum使得ab两个数字交替前进即可,这样可以节约O(n)的空间。

循环求余法,规则:sum = (a + b) ⨀ \bigodot p

class Solution{
    public int fib(int n){
        int a = 0, b = 1;
        for(int i=0;i<n;i++){
            int sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
}

使用两个栈实现队列

class CQueue{
    LinkedList<Integer> A,B;
    public CQueue(){
        A = new LinkedList<>();
        B = new LinkedList<>();
    }
    public void appendTail(int value){
        A.push(value);
    }
    public int deleteHead(){
        if(!B.isEmpty()){
            return B.pop();
        }
        if(A.isEmpty()) return -1;
        while(!A.isEmpty()){
            B.push(A.pop());
        }
        return B.pop();
    }
}

重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

class Solution{
    public TreeNode buildTree(int[] preorder,int[] inorder){
        if(preorder.length == 0||inorder.length == 0) return null;
        return dfs(preorder,inorder,0,0,inorder.length-1);
    }
    public TreeNode dfs(int[] preorder,int[] inorder,int preStart,int inStart,int inEnd){
        if(inStart > inEnd) return null;
        int currentVal = preorder[preStart];
        TreeNode root = new TreeNode(currentVal);
        int index = 0;
        for(int i=0;i<inorder.length;i++){
            if(currentVal == inorder[i]){
                index = i;
            }
        }
        root.left = dfs(preorder,inorder,preStart+1,inStart,index-1);
        root.right = dfs(preorder,inorder,preStart+index-inStart+1,index+1,inEnd);
        return root;
    }
}

**二 :**上面那种方法需要遍历整个中序序列,所有可以用map来规划

class Solution{
    HashMap<Integer,Integer> dict = new HashMap<>();
    int[] po;
    public TreeNode buildTree(int[] preorder,int[] inorder){
        po = preorder;
        for(int i=0;i<inorder.length;i++){
            dic.put(inorder[i],i);
        }
        return recur(0,0,inorder.length-1);
    }
    public TreeNode recur(int left_root, int in_left,int in_right){
        if(in_left > in_right) return null;
        TreeNode root = new TreeNode(po[left_root]);
        int i = map.get(po[left_root]);
        root.left = recur(left_root + 1,in_left,i-1);
        root.right =recur(left_root+i-in_left+1,i+1,in_right);
        return root;
    }
}

该方法利用一个全局的数组存储前序遍历,利用HashMap对中序遍历的值和下标建立key-value映射。所以在递归遍历的时候不需要再传入前序和中序的数组,直接是下标即可。

前序+中序是:left_root + (i-in_left) + 1

后序+中序是:right_root - (in_right-i)-1

重头到尾打印链表

输入一个链表的头结点,从尾到头反过来返回每个节点的值(用数组返回)。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    List<Integer> list = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        dfs(head);
        int[] arr = new int[list.size()];
        for(int i=0;i<list.size();i++){
            arr[i] = list.get(i);
        }
        return arr;
    }
    public void dfs(ListNode head){
        if(head == null) return;
        dfs(head.next);
        list.add(head.val);
    }
}

替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

class Solution {
    public String replaceSpace(String s) {
        StringBuilder res = new StringBuilder();
        for(char c:s.toCharArray()){
            if(c == ' '){
                res.append("%20");
            }
            else{
                res.append(c);
            }
        }
        return res.toString();
    }
}

二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        int i = matrix.length - 1, j = 0;
        while(i >= 0 && j < matrix[0].length)
        {
            if(matrix[i][j] > target) i--;
            else if(matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
}

数组中重复的数字

方法一:利用Set

class Solution{
    public int findRepeatNumber(int[] nums){
        HashSet<Integer> set = new HashSet<>();
        for(int num : nums){
            if(set.contains(num)) return num;
            set.add(num);
        }
        return -1;
    }
}

方法二:原地交换

class Solution{
    public int findRepeatNumber(int[] nums){
        int i = 0;
        while(i < nums.length){
            if(nums[i] == i){
                i++;
                continue;
            }
            if(nums[nums[i]] == nums[i]) return nums[i];
            int tmp = nums[i];
            nums[i] = nums[tmp];
            nums[tmp] = tmp;
        }
        return -1;
    }
}

同名微信公众号

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值