剑指offer-专项突破版(26-50)

26.重排链表

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 面试题26:重排链表
 * 1. 对链表的做题中要注意反转链表这种解题思路
 * @Date: 下午 19:46 2021/11/3 0003
 **/
public class RearrangedList26 {
    public void reorderList(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        ListNode fast = dummy;
        ListNode slow = dummy;
        //找到中间节点
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next;
            if (fast.next != null)
                fast = fast.next;
        }

        ListNode temp = slow.next;
        slow.next = null;
        link(head, reverseList(temp),dummy);
    }

    //反转链表
    private ListNode reverseList(ListNode head) {
        ListNode cur = head;
        ListNode prev = null;
        ListNode next = null;
        while (cur != null) {
            next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        return prev;
    }

    //穿针引线
    private void link(ListNode node1, ListNode node2, ListNode head) {
        ListNode prev = head;
        while (node1 != null && node2 != null) {
            ListNode temp = node1.next;

            prev.next = node1;
            node1.next = node2;
            prev = node2;

            node1 = temp;
            node2 = node2.next;
        }

        if (node1 != null)
            prev.next = node1;
    }
}

27.回文链表

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 回文链表
 * @Date: 下午 20:58 2021/11/3 0003
 **/
public class PalindromeList27 {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null)
            return true;

        ListNode slow = head;
        ListNode fast = head.next;

        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }

        ListNode secondHalf = slow.next;
        if (fast.next != null)
            secondHalf = slow.next.next;

        //这句话一加,head所代表的链表就到slow后面的null了
        slow.next = null;
        return equals(secondHalf, reverseList(head));
    }

    private ListNode reverseList(ListNode head) {
        ListNode cur = head;
        ListNode prev = null;
        ListNode next = null;
        while (cur != null) {
            next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        return prev;
    }

    private boolean equals(ListNode head1, ListNode head2) {
        while (head1 != null && head2 != null) {
            if (head1.val != head2.val)
                return false;
            head1 = head1.next;
            head2 = head2.next;
        }

        return head1 == null && head2 == null;
    }
}

28.展平多级双向链表

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 展平多级双向链表
 * @Date: 下午 16:00 2021/11/4 0004
 **/


public class FlatteningDoublyLinkedLists28 {
    public Node flatten(Node head) {
        flattenGetTail(head);
        return head;
    }
    
    private Node flattenGetTail(Node head) {
        Node node = head;
        Node tail = null;
        while (node != null) {
            Node next = node.next;
            if (node.child != null) {
                Node child = node.child;
                Node childTail = flattenGetTail(node.child);

                node.child = null;
                node.next = child;
                child.prev = node;
                childTail.next = next;
                if (next != null) {
                    next.prev = childTail;
                }

                tail = childTail;
            }else
                tail = node;
            node = next;
        }
        return tail;
    }
}

29.排序的循环链表

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 面试题29:排序的循环链表
 * @Date: 下午 16:32 2021/11/4 0004
 **/
public class SortedCircularList29 {
    public Node insert(Node head, int insertVal) {
        Node node = new Node(insertVal);
        if (head == null) {
            head = node;
            head.next = head;
        }else if (head.next == head) {
            head.next = node;
            node.next = head;
        }else {
            insertCore(head, node);
        }

        return head;
    }

    private void insertCore(Node head, Node node) {
        Node cur = head;
        Node next = head.next;
        Node biggest = head;
        while (!(cur.val <= node.val && next.val >= node.val) && next != head) {
            cur = next;
            next = next.next;
            if (cur.val >= biggest.val)
                biggest = cur;
        }

        if (cur.val <= node.val && next.val >= node.val) {
            cur.next = node;
            node.next = next;
        }else {
            node.next = biggest.next;
            biggest.next = node;
        }
    }
}

30.插入、删除和随机访问都是O(1)的容器

package com.lxh.hash;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

/**
 * @Author: Tiger
 * @Description: 面试题30:插入、删除和随机访问都是O(1)的容器
 * 1. 思想很重要 同时学习取随机数函数
 * @Date: 下午 19:55 2021/11/4 0004
 **/

class RandomContainer30 {
    HashMap<Integer, Integer> numToLocation;
    ArrayList<Integer> nums;

    //预处理
    public RandomContainer30() {
        numToLocation = new HashMap<>();
        nums = new ArrayList<>();
    }

    //如果数据集中不包含一个数值,则把它添加到数据集中
    public boolean insert(int val) {
        if (numToLocation.containsKey(val))
            return false;
        //哈希表中存储当前值和在数组中的位置(数组下标)
        numToLocation.put(val, nums.size());
        nums.add(val);
        return true;
    }

    //如果数据集中包含一个数值,则把它删除
    public boolean remove(int val) {
        if (!numToLocation.containsKey(val))
            return false;
        int location = numToLocation.get(val);

        //注意数组长度和数组下标之间的关系
        numToLocation.put(nums.get(nums.size() - 1), location);
        numToLocation.remove(val);
        nums.set(location, nums.get(nums.size() - 1));
        nums.remove(nums.size() - 1);
        return true;
    }

    //随机访问,随机返回数据集中的一个数值,要求数据集中每个数字都被返回的概率都相同
    //取随机数函数
    public int getRandom() {
        Random random = new Random();
        int r = random.nextInt(nums.size());
        return nums.get(r);
    }

}

31.最近最少使用缓存 (again)

package com.lxh.hash;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: Tiger
 * @Description: 面试题31:最近最少使用缓存 (again)
 * @Date: 下午 22:04 2021/11/6 0006
 **/

class ListNode {
    public int key;
    public int value;
    public ListNode next;
    public ListNode prev;

    public ListNode(int k, int v) {
        key = k;
        value = v;
    }
}

class LeastRecentlyUsedCache31 {
    private ListNode head;
    private ListNode tail;
    private Map<Integer, ListNode> map;
    int capacity;

    public LeastRecentlyUsedCache31(int cap) {
        map = new HashMap<>();

        //创建一头一尾两个哨兵节点,put所添加的节点位于这两个节点之间
        head = new ListNode(-1, -1);
        tail = new ListNode(-1, -1);
        head.next = tail;
        tail.prev = head;

        capacity = cap;
    }

    public int get(int key) {
        ListNode node = map.get(key);
        if (node == null) {
            return -1;
        }

        moveToTail(node, node.value);

        return node.value;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            moveToTail(map.get(key), value);
        }else {
            if (map.size() == capacity) {
                ListNode toBeDeleted = head.next;
                deleteNode(toBeDeleted);

                map.remove(toBeDeleted.key);
            }

            ListNode node = new ListNode(key, value);
            insertToTail(node);

            map.put(key, node);
        }
    }

    //把双向链表中的一个节点移到链表的尾部
    private void moveToTail(ListNode node, int newValue) {
        deleteNode(node);

        node.value = newValue;
        insertToTail(node);
    }

    private void deleteNode(ListNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void insertToTail(ListNode node) {
        tail.prev.next = node;
        node.prev = tail.prev;
        node.next = tail;
        tail.prev = node;
    }
}

32.有效的变位词

package com.lxh.hash;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: Tiger
 * @Description: 面试题32:有效的变位词
 * @Date: 下午 17:43 2021/11/7 0007
 **/
/*如果只考虑英文字母,则用数组来模拟哈希表
public class ValidConjugation32 {
    public boolean isAnagram(String str1, String str2) {
        if (str1.length() != str2.length())
            return false;
        int[] counts = new int[26];
        for (char ch : str1.toCharArray()) {
            counts[ch - 'a']++;
        }

        for (char ch : str2.toCharArray()) {
            if (counts[ch - 'a'] == 0)
                return false;
            counts[ch - 'a']--;
        }
        return true;
    }
}*/

//考虑非英文字母,则用真正的哈希表HashMap
public class ValidConjugation32 {
    public boolean isAnagram(String str1, String str2) {
        if (str1.length() != str2.length())
            return false;
        Map<Character, Integer> counts = new HashMap<>();
        for (char ch : str1.toCharArray()) {
            counts.put(ch, counts.getOrDefault(ch, 0) + 1);
        }

        for (char ch : str2.toCharArray()) {
            if (!counts.containsKey(ch) || counts.get(ch) == 0) {
                return false;
            }
            counts.put(ch, counts.get(ch) - 1);
        }
        return true;
    }



}

33.变位词组

package com.lxh.hash;

import java.util.*;

/**
 * @Author: Tiger
 * @Description: 面试题33:变位词组
 * @Date: 下午 18:08 2021/11/7 0007
 **/

/*将单词映射到数字 该算法的时间复杂度为O(mn)但该解法有个问题 可能会造成溢出
public class ConjugationPhrase33 {
    public List<List<String>> groupAnagrams(String[] strs) {
        int hash[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101};

        Map<Long, List<String>> groups = new HashMap<>();

        for (String str : strs) {
            long wordHash = 1;
            for (int i = 0; i < str.length(); ++i) {
                wordHash *= hash[str.charAt(i) - 'a'];
            }

            //使用putIfAbsent方法添加键值对,如果map集合中没有该key对应的值,则直接添加,并返回null,如果已经存在对应的值,则依旧为原来的值。
            //此处当数字乘积没有出现过则创建一个新的以key为首的链表,当存在时则保持原来的链表
            groups.putIfAbsent(wordHash, new LinkedList<String>());
            groups.get(wordHash).add(str);
        }

        //groups.value()本身就是一个链表了
        return new LinkedList<>(groups.values());
    }
}*/

//
public class ConjugationPhrase33 {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> groups = new HashMap<>();
        for (String str : strs) {
            //将字符串转换成字符数组
            char[] charArray = str.toCharArray();
            Arrays.sort(charArray);
            //将排完序的字符数组再转回字符串
            String sorted = new String(charArray);

            groups.putIfAbsent(sorted, new LinkedList<String>());
            //因为获得的值为一个链表 所以可以直接利用链表的add进行添加
            groups.get(sorted).add(str);
        }
        //哈希表有keys和values可以直接调用
        return new LinkedList<>(groups.values());
    }
}

34.外星语言是否排序

package com.lxh.hash;

/**
 * @Author: Tiger
 * @Description: 面试题34:外星语言是否排序
 * 1. 时间复杂度为O(mn) 空间复杂度为O(1)
 * @Date: 下午 18:52 2021/11/7 0007
 **/
public class AreAlienLanguagesSorted34 {
    public boolean isAlienSorted(String[] words, String order) {
        int[] orderArray = new int[order.length()];

        //先出现的字符的值越小
        for (int i = 0; i < order.length() - 1; ++i) {
            orderArray[order.charAt(i) - 'a'] = i;
        }

        for (int i = 0; i < words.length - 1; ++i) {
            if (!isSorted(words[i], words[i + 1], orderArray))
                return false;
        }

        return true;
    }

    private boolean isSorted(String word1, String word2, int[] order) {
        int i = 0;
        for (; i < word1.length() && i < word2.length(); ++i) {
            char ch1 = word1.charAt(i);
            char ch2 = word2.charAt(i);
            if (order[ch1 - 'a'] < order[ch2 - 'a']) {
                return true;
            }

            if (order[ch1 - 'a'] > order[ch2 - 'a']) {
                return false;
            }
        }
        //当不一样长时,说明word2字符串长度更短,应该排在word1前面
        return i == word1.length();
    }
}

35.最小时间差

package com.lxh.hash;

import java.util.List;
import java.util.Map;

/**
 * @Author: Tiger
 * @Description: 面试题35:最小时间差
 * @Date: 下午 19:20 2021/11/7 0007
 **/
public class MinimumTimeDifference35 {
    public int findMinDifference(List<String> timePoints) {
        if (timePoints.size() > 1440) {
            return 0;
        }

        boolean minuteFlags[] = new boolean[1440];

        for (String time : timePoints) {
            //spilt函数将字符串按照某一个指定符号进行分割开并形成字符串数组
            String t[] = time.split(":");
            //Integer.parseInt(“”)返回的是一个基本类型的int,将字符串直接转换为数字
            //将时间变成以分钟来表示的
            int min = Integer.parseInt(t[0]) * 60 + Integer.parseInt(t[1]);
            //说明此时已经存在同时间点 故最小时间差为0
            if (minuteFlags[min])
                return 0;

            minuteFlags[min] = true;
        }
        return helper(minuteFlags);
    }

    //
    private int helper(boolean minuteFlags[]) {
        int minDiff = minuteFlags.length - 1;
        int prev = -1;
        int first = minuteFlags.length - 1;
        int last = -1;
        for (int i = 0; i < minuteFlags.length; ++i) {
            if (minuteFlags[i]) {
                if (prev >= 0 )
                    minDiff = Math.min(i - prev, minDiff);

                prev = i;
                //first用来记录最小的时间位置
                first = Math.min(i, first);
                //last用来记录最大的时间位置
                last = Math.max(i, last);
            }
        }
        //要将最小的时间加上一天1440分钟作为第二天时间与最长的时间位置进行比较
        minDiff = Math.min(first + minuteFlags.length - last, minDiff);
        return minDiff;
    }
}

36.后缀表达式

package com.lxh.stack;

import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 面试题36:后缀表达式
 * 1. 时间复杂度O(n) 空间复杂度O(n)
 * @Date: 下午 21:02 2021/11/8 0008
 **/
public class PostfixExpression36 {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<Integer>();
        for (String token : tokens) {
            switch (token) {
                case "+":
                case "-":
                case "*":
                case "/":
                    int num1 = stack.pop();
                    int num2 = stack.pop();
                    //先出来的为右操作数
                    stack.push(calculate(num2, num1, token));
                    break;
                default:
                    //将字符串转换为整数
                    stack.push(Integer.parseInt(token));
            }
        }
        return stack.pop();
    }

    private int calculate(int num1, int num2, String operator) {
        switch (operator) {
            case "+":
                return num1 + num2;
            case "-":
                return num1 - num2;
            case "*":
                return num1 * num2;
            case "/":
                return num1 / num2;
            default:
                return 0;
        }
    }
}

37.小行星碰撞

package com.lxh.stack;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 面试题37:小行星碰撞
 * 1. 时间复杂度O(n) 空间复杂度O(n)
 * 2. stack.stream().mapToInt(i->i).toArray();
 * @Date: 上午 11:13 2021/11/9 0009
 **/
public class AsteroidCollision37 {
    public int[] asteroidCollision(int[] asteroids) {
        Stack<Integer> stack = new Stack<>();
        for (int as : asteroids) {
            while (!stack.empty() && stack.peek() > 0 && stack.peek() < -as) {
                stack.pop();
            }

            if (!stack.empty() && as < 0 && stack.peek() == -as) {
                stack.pop();
            }else if (as > 0 || stack.empty() || stack.peek() < 0) {
                stack.push(as);
            }
        }
        //mapToInt(i->i)它将每个 Integer 拆箱为一个 int
        //将栈内数据转换为数组的方法
        return stack.stream().mapToInt(i->i).toArray();
    }

    public static void main(String[] args) {
        AsteroidCollision37 ac37 = new AsteroidCollision37();
        int[] nums = {4, 5, -6, 4, 8, -5};
        // nums = ac37.asteroidCollision(nums);
        System.out.println(Arrays.toString(ac37.asteroidCollision(nums)));
    }
}

38.每日温度

package com.lxh.stack;

import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 面试题38:每日温度
 * @Date: 下午 14:12 2021/11/9 0009
 **/
public class DailyTemperature38 {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] result = new int[temperatures.length];
        //用栈保存温度的数组下标
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < temperatures.length; i++) {
            while (!stack.empty() && temperatures[i] > temperatures[stack.peek()]) {
                int prev = stack.pop();
                result[prev] = i - prev;
            }
            stack.push(i);
        }
        return result;
    }
}

39.直方图最大矩形面积

package com.lxh.stack;

import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 面试题39:直方图最大矩形面积
 * @Date: 下午 14:37 2021/11/9 0009
 **/
public class MaximumRectangleAreaHistogram39 {

    /*方案一:蛮力法 时间O(n*n) 空间O(1)
    public int largestRectangleArea(int[] heights) {
        int maxArea = 0;
        for (int i = 0; i < heights.length; i++) {
            int min = heights[i];
            for (int j = 0; j < heights.length; j++) {
                min = Math.min(min, heights[j]);
                int area = min * (j - i + 1);
                maxArea = Math.max(maxArea, area);
            }
        }
        return maxArea;
    }*/

    //方案二:分治法 时间 平均O(nlogn) 最坏O(n*n) 空间 平均O(logn) 最坏O(n)
    /*public int largestRectangleArea(int[] heights) {
        return helper(heights, 0, heights.length);
    }

    private int helper(int[] heights, int start, int end) {
        if (start == end) {
            return 0;
        }
        if (start + 1 == end) {
            return heights[start];
        }

        int minIndex = start;
        for (int i = start + 1; i < end; i++) {
            if (heights[i] < heights[minIndex]) {
                minIndex = i;
            }
        }

        int area = (end - start) * heights[minIndex];
        int left = helper(heights, start, minIndex);
        int right = helper(heights, minIndex + 1, end);

        area = Math.max(area, left);
        return Math.max(area, right);
    }*/

    //方案三:单调栈法 时间O(n) 空间O(n)
    public int largestRectangleArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);

        int maxArea = 0;
        for (int i = 0; i < heights.length; i++) {
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i]) {
                int height = heights[stack.pop()];
                int width = i - stack.peek() - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }

        while (stack.peek() != -1) {
            int height = heights[stack.pop()];
            int width = heights.length - stack.peek() - 1;
            maxArea = Math.max(maxArea, height * width);
        }
        return maxArea;
    }
}

40.矩阵中的最大矩形

package com.lxh.stack;

import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 面试题40:矩阵中的最大矩形
 * 1. 时间O(mn) 空间O(n)
 * @Date: 下午 15:37 2021/11/9 0009
 **/
public class LargestRectangleInMatrix40 {
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0)
            return 0;

        int[] heights = new int[matrix[0].length];
        int maxArea = 0;
        for (char[] row : matrix) {
            for (int i = 0; i < row.length; i++) {
                if (row[i] == '0') {
                    heights[i] = 0;
                }else {
                    heights[i]++;
                }
            }

            maxArea = Math.max(maxArea, largestRectangleArea(heights));
        }

        return maxArea;
    }

    private int largestRectangleArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);

        int maxArea = 0;
        for (int i = 0; i < heights.length; i++) {
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i]) {
                int height = heights[stack.pop()];
                int width = i - stack.peek() - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }

        while (stack.peek() != -1) {
            int height = heights[stack.pop()];
            int width = heights.length - stack.peek() - 1;
            maxArea = Math.max(maxArea, height * width);
        }
        return maxArea;
    }

}

41.滑动窗口的平均值

package com.lxh.queue;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @Author: Tiger
 * @Description: 面试题41:滑动窗口的平均值
 * @Date: 下午 20:05 2021/11/12 0012
 **/
class MovingAverage {
    private Queue<Integer> nums;
    private int capacity;
    private int sum;

    public MovingAverage(int size) {
        nums = new LinkedList<>();
        capacity = size;
    }

    public double next(int val) {
        //
        nums.offer(val);
        sum += val;
        if (nums.size() > capacity) {
            sum -= nums.poll();
        }

        return (double) sum / nums.size();
    }

}

42.最近请求次数

package com.lxh.queue;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @Author: Tiger
 * @Description: 面试题42:最近请求次数
 * @Date: 下午 20:14 2021/11/12 0012
 **/
class RecentCounter {
    private Queue<Integer> times;
    private int windowSize;

    public RecentCounter() {
        times = new LinkedList<>();
        windowSize = 3000;
    }

    public int ping(int t) {
        times.offer(t);
        while (times.peek() + windowSize < t) {
            times.poll();
        }

        return times.size();
    }
}

43.在完全二叉树中添加节点

package com.lxh.queue;

import sun.reflect.generics.tree.Tree;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @Author: Tiger
 * @Description: 面试题43:在完全二叉树中添加节点
 * 1. 类型CBTInserter的构造函数 时间复杂度是O(n) 函数insert的时间复杂度是O(1)
 * @Date: 上午 11:38 2021/11/13 0013
 **/
class InsertNodeIntoBinaryTree43 {
    private Queue<TreeNode> queue;
    private TreeNode root;

    public InsertNodeIntoBinaryTree43(TreeNode root) {
        this.root = root;//把参数值赋给成员变量,成员变量的值改变
        queue = new LinkedList<>();
        queue.offer(root);
        while (queue.peek().left != null && queue.peek().right != null) {
            TreeNode node = queue.poll();
            queue.offer(node.left);
            queue.offer(node.right);
        }
    }

    public int insert(int v) {
        TreeNode parent = queue.peek();
        TreeNode node = new TreeNode(v);

        if (parent.left == null) {
            parent.left = node;
        }else {
            //如果节点右孩子也有了 所以此时对于这个节点它的左右孩子都有了 就不可能是被插入的对象了
            //就可以把它给删除了 同时把他的左右孩子放进队列 继续遍历找到第一个没有节点左右孩子不满的
            parent.right = node;

            queue.poll();
            queue.offer(parent.left);
            queue.offer(parent.right);
        }

        return parent.val;
    }

    public TreeNode get_root() {
        return this.root;
    }
}

44.二叉树中每层的最大值

package com.lxh.queue;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * @Author: Tiger
 * @Description: 面试题44:二叉树中每层的最大值
 * @Date: 下午 15:17 2021/11/13 0013
 **/
public class MaximumValueOfEachLevel44 {
    /* 方案一:用一个队列实现二叉树的广度优先搜索
    public List<Integer> largestValues(TreeNode root) {
        int current = 0;
        int next = 0;
        Queue<TreeNode> queue = new LinkedList<>();
        if (root != null) {
            queue.offer(root);
            current = 1;
        }

        List<Integer> result = new LinkedList<>();
        int max = Integer.MIN_VALUE;
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            current--;
            max = Math.max(max, node.val);

            if (node.left != null) {
                queue.offer(node.left);
                next++;
            }

            if (node.right != null) {
                queue.offer(node.right);
                next++;
            }

            if (current == 0) {
                result.add(max);
                max = Integer.MIN_VALUE;
                current = next;
                next = 0;
            }
        }

        return result;
    }*/
    //方案二 用两个队列实现二叉树的广度优先搜索
    public List<Integer> largestValues(TreeNode root) {
        Queue<TreeNode> queue1 = new LinkedList<>();
        Queue<TreeNode> queue2 = new LinkedList<>();
        if (root != null) {
            queue1.offer(root);
        }

        List<Integer> result = new LinkedList<>();
        int max = Integer.MIN_VALUE;
        while (!queue1.isEmpty()) {
            TreeNode node = queue1.poll();
            max = Math.max(max, node.val);

            if (node.left != null) {
                queue2.offer(node.left);
            }

            if (node.right != null) {
                queue2.offer(node.right);
            }

            if (queue1.isEmpty()) {
                result.add(max);
                max = Integer.MIN_VALUE;

                queue1 = queue2;
                //将queue2重新初始化为空
                queue2 = new LinkedList<>();
            }
        }

        return result;
    }
}

45.二叉树最底层最左边的值

package com.lxh.queue;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @Author: Tiger
 * @Description: 面试题45:二叉树最底层最左边的值
 * @Date: 下午 15:35 2021/11/13 0013
 **/
public class LeftmostValueOfTheLowestLevel45 {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue1 = new LinkedList<>();
        Queue<TreeNode> queue2 = new LinkedList<>();
        queue1.offer(root);
        int bottomLeft = root.val;
        while (!queue1.isEmpty()) {
            TreeNode node = queue1.poll();
            if (node.left != null) {
                queue2.offer(node.left);
            }

            if (node.right != null) {
                queue2.offer(node.right);
            }

            if (queue1.isEmpty()) {
                queue1 = queue2;
                queue2 = new LinkedList<>();
                if (!queue1.isEmpty()) {
                    bottomLeft = queue1.peek().val;
                }
            }
        }
        return bottomLeft;
    }
}

46.二叉树的右侧视图

package com.lxh.queue;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * @Author: Tiger
 * @Description: 面试题46:二叉树的右侧视图
 * @Date: 下午 15:43 2021/11/13 0013
 **/
public class RightSideViewOfBinaryTree46 {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> view = new LinkedList<>();
        if (root == null)
            return view;

        Queue<TreeNode> queue1 = new LinkedList<>();
        Queue<TreeNode> queue2 = new LinkedList<>();
        queue1.offer(root);

        while (!queue1.isEmpty()) {
            TreeNode node = queue1.poll();
            if (node.left != null) {
                queue2.offer(node.left);
            }

            if (node.right != null) {
                queue2.offer(node.right);
            }

            if (queue1.isEmpty()) {
                view.add(node.val);
                queue1 = queue2;
                queue2 = new LinkedList<>();
            }
        }
        return view;
    }
}

47.二叉树剪枝(基于后续遍历)

package com.lxh.tree;

/**
 * @Author: Tiger
 * @Description: 面试题47:二叉树剪枝(基于后续遍历)
 * @Date: 下午 17:09 2021/11/15 0015
 **/
public class BinaryTreePruning47 {
    public TreeNode pruneTree(TreeNode root) {
        if (root == null) {
            return root;
        }

        root.left = pruneTree(root.left);
        root.right = pruneTree(root.right);
        if (root.left == null && root.right == null && root.val == 0) {
            return null;
        }

        return root;
    }
}

48.序列化和反序列化二叉树

package com.lxh.tree;

/**
 * @Author: Tiger
 * @Description: 面试题48:序列化和反序列化二叉树
 * 1. 序列化 是将二叉树序列化成一个字符串 反序列化是指 还原成原来二叉树的算法
 * 2. String.valueOf(root.val)将数字转化成字符串 Integer.valueOf(str)将字符串转化成数字
 * @Date: 上午 10:57 2021/11/16 0016
 **/
public class SerializationAndDeserialization48 {
    //序列化树
    public String serialize(TreeNode root) {
        if (root == null) {
            return "#";
        }

        String leftStr = serialize(root.left);
        String rightStr = serialize(root.right);
        return String.valueOf(root.val) + "," + leftStr + "," + rightStr;
    }

    //反序列化二叉树
    /* 关于此处我们需要一个下标去扫描字符串数组nodeStrs中的每个字符串。通常用一个整数值来表示数组的下标,
    * 但在下述代码中却定义了一个长度为1的整数数组i。这是因为递归函数dfs每反序列化一个节点时下标就会增加1,
    * 并且函数的调用者需要知道下标增加了。如果函数dfs的第二个参数i是整数类型,那么即使在函数体内修改i的值,
    * 修改之后的值也不能传递给它的调用者。但把i定义为整数数组之后,可以修改整数数组中的数字,修改之后的数值
    * 就能传给他的调用者了。
    *
    * ?:为什么当定义一个整数类型时 在函数体内修改i的值 也不能传递给它的调用者。
    * */
    public TreeNode deserialize(String data) {
        String[] nodeStrs = data.split(",");
        int[] i = {0};
        return dfs(nodeStrs, i);
    }

    private TreeNode dfs(String[] strs, int[] i) {
        String str = strs[i[0]];
        i[0]++;

        if (str.equals("#")) {
            return null;
        }

        TreeNode node = new TreeNode(Integer.valueOf(str));
        node.left = dfs(strs, i);
        node.right = dfs(strs, i);
        return node;
    }
}

49.从根节点到叶节点的路径数字之和

package com.lxh.tree;

/**
 * @Author: Tiger
 * @Description: 面试题49:从根节点到叶节点的路径数字之和
 * @Date: 下午 14:45 2021/11/16 0016
 **/
public class SumOfThePathNumbers49 {
    public int sumNumbers(TreeNode root) {
        return dfs(root, 0);
    }

    private int dfs(TreeNode root, int path) {
        if (root == null) {
            return 0;
        }

        path = path * 10 + root.val;
        if (root.left == null && root.right == null) {
            return path;
        }

        return dfs(root.left, path) + dfs(root.right, path);
    }
}

50.向下的路径节点值之和

package com.lxh.tree;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: Tiger
 * @Description: 面试题50:向下的路径节点值之和
 * @Date: 下午 15:03 2021/11/16 0016
 **/
public class SumOfTheNodeValues50 {
    public int pathSum(TreeNode root, int sum) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);

        return dfs(root, sum, map, 0);
    }

    private int dfs(TreeNode root, int sum, Map<Integer, Integer> map, int path) {
        if (root == null) {
            return 0;
        }

        path += root.val;
        int count = map.getOrDefault(path - sum, 0);
        map.put(path, map.getOrDefault(path, 0) + 1);

        count += dfs(root.left, sum, map, path);
        count += dfs(root.right, sum, map, path);

        map.put(path, map.get(path) - 1);

        return count;
    }

    public static void main(String[] args) {
        SumOfTheNodeValues50 sotnv50 = new SumOfTheNodeValues50();
        TreeNode node1 = new TreeNode(5);
        TreeNode node2 = new TreeNode(2);
        TreeNode node3 = new TreeNode(4);
        TreeNode node4 = new TreeNode(1);
        TreeNode node5 = new TreeNode(6);
        TreeNode node6 = new TreeNode(3);
        TreeNode node7 = new TreeNode(7);
        node1.left = node2;
        node1.right = node3;
        node2.left = node4;
        node2.right = node5;
        node3.left = node6;
        node3.right = node7;
        int i = sotnv50.pathSum(node1, 8);
        System.out.println(i);

    }
}

50.二叉树的深度优先搜索

package com.lxh.tree;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 深度优先搜索 - 前序遍历
 * @Date: 下午 15:15 2021/11/15 0015
 **/
public class PreorderTraversal {
    /* 递归形式
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> nodes = new LinkedList<>();
        dfs(root, nodes);
        return nodes;
    }

    private void dfs(TreeNode root, List<Integer> nodes) {
        if (root != null) {
            nodes.add(root.val);
            dfs(root.left, nodes);
            dfs(root.right, nodes);
        }
    }*/

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> nodes = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                nodes.add(cur.val);
                stack.push(cur);
                cur = cur.left;
            }

            cur = stack.pop();
            cur = cur.right;
        }

        return nodes;
    }
}

package com.lxh.tree;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 深度优先搜索 - 中序遍历
 * @Date: 下午 14:51 2021/11/15 0015
 **/
public class InorderTraversal {
    /* 递归的形式
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> nodes = new LinkedList<>();
        dfs(root, nodes);
        return nodes;
    }

    private void dfs(TreeNode root, List<Integer> nodes) {
        if (root != null) {
            dfs(root.left, nodes);
            nodes.add(root.val);
            dfs(root.right, nodes);
        }
    }*/

    //非递归的形式 即用到了栈
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> nodes = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }

            cur = stack.pop();
            nodes.add(cur.val);
            cur = cur.right;
        }

        return nodes;
    }
}

package com.lxh.tree;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/**
 * @Author: Tiger
 * @Description: 深度优先搜索 - 后续遍历
 * @Date: 下午 15:26 2021/11/15 0015
 **/
public class PostorderTraversal {
    /* 递归的形式
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> nodes = new LinkedList<>();
        dfs(root, nodes);
        return nodes;
    }

    private void dfs(TreeNode root, List<Integer> nodes) {
        if (root != null) {
            dfs(root.left, nodes);
            dfs(root.right, nodes);
            nodes.add(root.val);
        }
    }*/

    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> nodes = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }

            cur = stack.peek();
            if (cur.right != null && cur.right != prev) {
                cur = cur.right;
            }else {
                stack.pop();
                nodes.add(cur.val);
                prev = cur;
                cur = null;
            }
        }

        return nodes;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值