• 剑指Offer-2




    只能说受益匪浅


    1

    判定入栈,出栈序列是否匹配

    // 思路:用辅助栈来模拟出入栈
    import java.util.Stack;
    public class Solution {
        public boolean IsPopOrder(int [] pushA,int [] popA) {
            int cnt = 0;								// 记录出栈个数或下标   
            Stack<Integer> stack = new Stack<>();					  // 辅助栈
            
            for(int i = 0; i < pushA.length; i++){
                stack.push(pushA[i]);						// 模拟入栈
                while(!stack.isEmpty() && stack.peek() == popA[cnt]){ // while循环模拟出栈
                    stack.pop();
                    cnt++;
                }
            }
            return stack.isEmpty();							// 判断辅助栈是否为空
        }
    }
    


    2

    从上往下层级遍历二叉树

    // 思路:用一个 队列 模拟层次
    // 用LinkedList模拟队列
    // 栈是addFirst,removeFirst
    // 队列addLast,removeFirst
    // 这里的层次遍历,不是一层层来的,是一层里面分左右子树分开来的
    import java.util.ArrayList;
    import java.util.LinkedList;
    public class Solution {
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
            ArrayList<Integer> list = new ArrayList();      // 存取层次遍历序列
            LinkedList<TreeNode> queue = new LinkedList();  // 存储节点模拟层次的
            
            if(root == null) return list;
            queue.addLast(root);
            
            while(!queue.isEmpty()){
                TreeNode temp = queue.removeFirst();  // 出队
                list.add(temp.val);
                
                if(temp.left != null){
                    queue.addLast(temp.left);
                }
                if(temp.right != null){
                    queue.addLast(temp.right);
                }
            }
            return list;
        }
    }
    
    // 用ArrayList模拟队列
    public class Solution {
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
           ArrayList<Integer> list = new ArrayList();
           ArrayList<TreeNode> queue = new ArrayList();
            
           if(root == null) return list;
           queue.add(root);
            
           while(queue.size() != 0){
               TreeNode temp = queue.remove(0);
               list.add(temp.val);
               
               if(temp.left != null){
                   queue.add(temp.left);
               }
               if(temp.right != null){
                   queue.add(temp.right);
               }
           }
            return list;
        }
    }
    


    3

    判断是否后序遍历

    // 现在开始自己规定,凡是自己传进去的数组长度这些参数,都是实际长度,就是length-1这种
    // 思路:后序中最后一个是根,去除最后一个可以分成两段。前段小于根,后段大于根,以此类推递归
    public class Solution {
        public boolean VerifySquenceOfBST(int [] sequence) {
            if(sequence == null || sequence.length == 0) return false;
            return search(sequence,0,sequence.length-1);
        }
        private boolean search(int[] arr,int left,int right){
            
            if(left >= right) return true;  // 递归出口,这里最重要
            
            int mid = left;  // 从左遍历找分界(对比根),小心越界
            while(arr[mid] < arr[right] && mid < right){
                mid++;
            }
            for(int i = mid; i < right; i++){  // 判断右端是否符合
                if(arr[i] < arr[right]){
                    return false;
                }
            }
            // 左去界(遍历的时候比根大了才停止的),右去根
            return search(arr,left,mid-1) && search(arr,mid,right-1);
        }
    }
    


    4

    二叉树和为某值的路径

    import java.util.ArrayList;
    public class Solution {
        
        // 一个保存当前遍历的路径,一个保存符合的全部路径
        ArrayList<ArrayList<Integer>> list = new ArrayList<>();
        ArrayList<Integer> path = new ArrayList<>();
        
        public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
            search(root,target);
            return list;
        }
        
        // 遍历用前序或者DFS
        private void search(TreeNode root, int target){
            if(root == null) return ;
            
            target -= root.val;
            path.add(root.val);
            
            if(root.left == null && root.right == null && target == 0){
                list.add(new ArrayList<Integer>(path));
            }
            
            search(root.left,target);
            search(root.right,target);
            
            // 回溯时不用加回target,因为是个副本
            path.remove(path.size()-1);
        }
    }
    


    5

    复杂链表的复制

    /*
    public class RandomListNode {
        int label;
        RandomListNode next = null;
        RandomListNode random = null;
    
        RandomListNode(int label) {
            this.label = label;
        }
    }
    */
    // 思路:
    // 1. 复制每个节点(暂不处理随机指向),将新复制的节点插入原节点后面:A->A1
    // 2. 处理随机指向
    // 3. 复制链表和原链表分离
    public class Solution {
        public RandomListNode Clone(RandomListNode pHead){
            
            if(pHead == null) return null;
            
            // 1. 复制链表,复制节点插入到原节点后面
            RandomListNode node = pHead;
            while(node != null){
                RandomListNode next = node.next;
                RandomListNode cloneNode = new RandomListNode(node.label);
                node.next = cloneNode;  // 链表插入过程
                cloneNode.next = next;
                node = next;  // 节点插入后,当前节点记得跳转到next
            }
            
            // 2. 遍历处理随机指向
            node = pHead;
            while(node != null){
                if(node.random != null){
                    // 重点:指向随机的下一个(因复制时插入到后一个去了)
                    node.next.random = node.random.next;
                }
                node = node.next.next;  // 复制插入要跳多一个
            }
            
            // 3. 分离节点,奇偶分离
            RandomListNode oldNode = pHead;
            RandomListNode newHead = pHead.next;  // 新表头
            while(oldNode != null){
                RandomListNode newNode = oldNode.next;
                oldNode.next = newNode.next;
                if(newNode.next != null){
                    newNode.next = newNode.next.next;
                }
                oldNode = oldNode.next; // 上面已经更新了旧节点指向,已经跳过一个节点了
            }
            return newHead;
        }
    }
    
    // 3. 分离节点,奇偶分离
    // RandomListNode oldNode = pHead;
    // RandomListNode newNode = pHead.next;  // 因为有复制,所以后一个节点一定不为空
    // RandomListNode newHead = newNode;  // 新表头
    // while(newNode.next != null){
    //     oldNode.next = newNode.next;
    //     oldNode = oldNode.next;
    //     newNode.next = oldNode.next;
    //     newNode = newNode.next;
    // }
    


    6

    二叉搜索树转变双向链表

    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
    
        }
    
    }
    */
    /**
     * 递归中序遍历:左 根 右
     * 下面if、else中的意思
     *    4
         / 
     *  3   5
     * 第一步if:head与temp赋值3节点;
     * 第二步else:改动temp节点互相指向,最后head赋值4节点:3 <--> 4
     * 第三步else:改动temp节点互相指向,最后head赋值5节点:4 <--> 5
     * 综上:3 <--> 4 <--> 5,链表完成
     */
    public class Solution {
        TreeNode temp = null;          // 临时节点,帮助形成双向链表
        TreeNode head = null;          // 表头,用于返回
        public TreeNode Convert(TreeNode pRootOfTree) {
            
            if(pRootOfTree == null) return null;  // 递归出口
            
            Convert(pRootOfTree.left); // 左子树遍历
            
            if (head == null) {        // 首次要处理根节点
                head = pRootOfTree;    // 第一次访问,记录头节点,用于访问返回
                temp = pRootOfTree;
            } else {
                temp.right = pRootOfTree;  // 按中序遍历顺序连成链表,详情看上面图
                pRootOfTree.left = temp;   // 中序就是有序,只需将当前temp指向下一个即可
                temp = temp.right;        // 然后移动当前节点到下一个
            }
            
            Convert(pRootOfTree.right); // 右子树递归
            return head;
        }
    }
    


    7

    字符串的排列(标准的DFS + 交换 / 回溯)

    // 思路:根据字符串的排列的特点,选择深度优先搜索,可通过字符交换实现,重复字符用剪枝
    // 1. 分成两部分,首个字符、后面的全部字符
    // 2. 每个字符都可在首位,即首字符和后面的进行交换
    // 3. 固定第一个字符,求后面的排列,即递归进行2,3步,出口为到了数组长度
    import java.util.HashSet;
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class Solution {
    
        // 保存所有排列
        ArrayList<String> res = new ArrayList();
        
        public ArrayList<String> Permutation(String str) {
            char[] arr = str.toCharArray();  // 转成数组容易遍历
            dfs(arr,0);                      // 实现排列
            Collections.sort(res);           // 排序
            return (ArrayList) res;
        }
        
        private void dfs(char[] arr,int index){
            if(index == arr.length - 1){     // 到叶子节点认为一个排列,递归出口
                res.add(String.valueOf(arr));
                return ;
            }
            HashSet<Character> set = new HashSet<>(); // 用来做剪枝的,防止重复
            for(int i = index; i < arr.length; i++){  // 遍历交换:使得每个元素都在首位
                if(set.contains(arr[i])){
                    continue; // 重复,因此剪枝(假设每个元素不重复)
                }
                set.add(arr[i]);
                swap(arr,index,i);  //  1. 首位和后面的全部逐个交换,即每个元素都有在首位的可能
                dfs(arr,index + 1); //  2. 固定首位,排列后面的字符
                swap(arr,index,i);  //  3. 回溯,不影响后面的每个元素都排在首位
            }
        }
        
        // 交换
        private void swap(char[] arr,int i,int j){
            char temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    


    8

    找出数组中的一个出现的次数超过数组长度的一半的数

    // 思路1:遍历多次,保存每个元素出现的次数
    // 思路2:排序后,众数肯定出现在中间
    
    
    // 最优解
    // 思路:摩尔投票法,查找超过1/2的数,肯定只有一个
    // 流程:依次从序列中选择两个数字,若不同则抵消(票数-1),相同当前数值的票数+1,最后剩下的数字就是所找
    // 步骤:1.对抗两两抵消、2.计算结果是否有效
    public class Solution {
        public int MoreThanHalfNum_Solution(int [] array) {
            
            int major = 0;
            int count = 0;	// 当前major的票数
            
            for(int i = 0; i < array.length; i++){  // 从头到尾遍历
                if(count == 0){
                    major = array[i];
                }
                if(major == array[i]){
                    count++;
                }else{
                    count--;
                }
            }
            
            int countRs = 0;
            for(int num : array){
                if(major == num){
                    countRs++;
                }
            }
            return (countRs > array.length/2) ? major : 0;
        }
    }
    
    


    9

    找出其中最小的K个数,TopK问题(快排,堆排)

    // 无脑解法
    public class Solution {
        public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
            Arrays.sort(input);
            ArrayList<Integer> list = new ArrayList<>();
            for(int i = 0; i < k; i++){
                list.add(input[i]);
            }
            return list;
        }
    }
    
    
    // 快排,我叫为哨兵排
    import java.util.ArrayList;
    public class Solution {
        
        public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
            if(input == null || k > input.length) return list;
            
            quickSort(input,0,input.length-1);
            
            ArrayList<Integer> list = new ArrayList();
            for(int i = 0; i < k; i++){
                list.add(input[i]);
            }
            return list;
        }
        
        private void quickSort(int[] arr, int left,int right){
            
            if(left > right) return ;
            int base = arr[left];
            int i = left,j = right;
            
            while(i < j){  // 选基准交换
                while(i < j && base <= arr[j]){
                    j--;
                }
                while(i < j && base >= arr[i]){
                    i++;
                }
                if(i < j){
                    int temp = arr[i];
    				arr[i] = arr[j];
    				arr[j] = temp;
                }
            }
            
            arr[left] = arr[i];  // 基准归位
            arr[i] = base;
            
            quickSort(arr,left,i-1);  // 二分治
            quickSort(arr,i+1,right);
        }
    }
    
    


    10

    计算连续子向量的最大和,有负数

    public class Solution {
        public int FindGreatestSumOfSubArray(int[] array) {
            
            if(array == null) return 0;
    
            // 注意:题目指的连续,不一定从下标0开始,可以窗口滑动的
            // maxSum不初始化为0,存在全负数情况,所以初始值array[0]
            // maxSum存储最大和
            int curSum,maxSum;
            curSum = maxSum = array[0];
            
            for(int i = 1; i < array.length; i++){
                
                // 一旦遇到和为负数,证明前面的正数效果作废了
                // 当前和小于0,抛弃前面的和,重新从现在加起
                if(curSum < 0){
                    curSum = array[i];
                }else if(curSum > 0){
                    curSum += array[i];
                }
                
                // 更新最大和
                if(curSum > maxSum){
                    maxSum = curSum;
                }
            }
            return maxSum;
        }
    }
    
    


    11

    计算整数中1出现的次数

    // 暴力转成字符串判断
    public class Solution {
        public int NumberOf1Between1AndN_Solution(int n) {
            StringBuffer str = new StringBuffer();
            for(int i = 1;i <= n; i++){
                str.append(i);
            }
            
            int count = 0;
            String s = str.toString();
            
            for(int i = 0;i < s.length(); i++){
                if(s.charAt(i) == '1'){
                    count++;
                }
            }
            return count;
        }
    }
    
    
    // 区分位数:i表示当前位,其余表示为高位和低位
    // 每次循环取当前位,即高位模10(high % 10),分别有三种情况。如下:
    public class Solution {
        public int NumberOf1Between1AndN_Solution(int n) {
            int count = 0;
            for(int i = 1; i <= n; i *= 10){ // 只需循环位数次
                
                int high = n / i;
                int low  = n % i;
                
                if(high % 10 == 0){
                    count += high / 10 * i;
                }else if (high % 10 == 1){
                    count += (high / 10 * i) + (low + 1);
                }else {
                    count += (high / 10 + 1) * i;
                }
            }
            return count;
        }
    }
    
    
    // 最优解,上面的优化
    // 当百位 = 0,则high / 10 == (high + 8) / 10
    // 当百位 > 1,取8就进位,效果等于(high / 10 + 1)
    public class Solution {
        public int NumberOf1Between1AndN_Solution(int n) {
            int count = 0;
            for(int i = 1; i <= n; i *=10){
                
                int high = n / i;
                int low  = n % i;
                
                if(high % 10 == 1){
                    count += low + 1;
                }
                
                count += (high + 8) / 10 * i;
            }
            return count;
        }
    }
    
    


    12

    把数组排成最小

    // 本质是排序问题,一般用快排
    public class Solution {
        public String PrintMinNumber(int [] numbers) {
            
            for(int i = 0; i < numbers.length-1; i++)  // 冒泡排序
                for(int j = 0; j < numbers.length-i-1; j++){
                    String str1 = numbers[j] + "" + numbers[j+1];
                    String str2 = numbers[j+1] + "" + numbers[j];
                    if(str1.compareTo(str2) > 0){  // 排到最后的是最大
                        int temp = numbers[j];
                        numbers[j] = numbers[j+1];
                        numbers[j+1] = temp;
                    }
                }
            
            String str = "";
            for(int i = 0; i < numbers.length; i++){
                str += numbers[i];
            }
            return str;
        }
    }
    
    
    // 思路二
    // 数字m、n拼接成 mn 和 nm
    // 若mn>nm,则m大于n
    // 若mn<nm,则m小于n
    // 若mn=nm,则m等于n
    
    


    13

    丑数:把只包含质因子2、3和5的数

    // 思路:一个丑数一定由另一个丑数乘以2或3或5得到
    // 这就是动态规划??
    import java.util.ArrayList;
    public class Solution {
        public int GetUglyNumber_Solution(int index) {
            
            if(index <= 0) return 0;
            ArrayList<Integer> list = new ArrayList();
            list.add(1);  // 默认第一个丑数为1
            
            // 用三个下标来模拟三个队列的尾部,加入list证明已经排好序
            int i2 = 0,i3 = 0,i5 = 0;
            while(list.size() < index){  // 从各自的队列取出
                int m2 = list.get(i2)*2;
                int m3 = list.get(i3)*3;
                int m5 = list.get(i5)*5;
                int min = Math.min(m2,Math.min(m3,m5));
                list.add(min);
                if(min == m2) i2++;
                if(min == m3) i3++;
                if(min == m5) i5++;
            }
            return list.get(list.size()-1);
        }
    }
    
    


    14

    第一个只出现一次的字符位置

    // 哈希表,VALUE存放次数
    import java.util.HashMap;
    public class Solution {
        public int FirstNotRepeatingChar(String str) {
            
            if(str.length() == 0) return -1;
            
            HashMap<Character,Integer> map = new HashMap();
            char[] arr = str.toCharArray();
            
            
            for(int i = 0; i < str.length(); i++){
                if( map.containsKey(str.charAt(i)) ){
                    int num = map.get(str.charAt(i));
                    map.put(str.charAt(i),num+1);
                }else{
                    map.put(str.charAt(i),1);
                }
            }
            for(int i = 0; i < str.length(); i++){
                if( map.get(str.charAt(i)) == 1 ){
                    return i;
                }
            }
            return 0;
        }
    }
    
    
    // 变形体,返回第一个只出现一次的字符
    // 哈希表,VALUE存放次数,这里一次的话可以存放TRUE/FALSE
    import java.util.HashMap;
    public class Solution {
        public int FirstNotRepeatingChar(String str) {
    
            char[] arr = str.toCharArray();
            HashMap<Character,Boolean> hashMap = new HashMap<>();
            
            for(char c : arr){
                hashMap.put(c,!hashMap.containsKey(c));
            }
            
            for(char c : arr){
                if(hashMap.get(c)){
                    return c;
                }
            }
            return "";
        }
    }
    
    


    15

    数组的逆序对

    // 暴力破解法,双层for循环,内层以i+1开头(因为当前元素的前面才能构成逆序)
    public class Solution {
        public int reversePairs(int[] nums) {
            int cnt = 0;
            for (int i = 0; i < nums.length - 1; i++) {
                for (int j = i + 1; j < nums.length; j++) {
                    if (nums[i] > nums[j]) {
                        cnt++;
                    }
                }
            }
            return cnt;
        }
    }
    
    
    // 最优解
    // 归并排序的利用,分治过程中前后数字可对比,是统计的最佳时机
    public class Solution {
        
        int count = 0;  // 统计逆序对
        
        public int InversePairs(int [] array) {
            if(array == null || array.length == 0) return 0;
            mergeSort(array,0,array.length-1);
            return count;
        }
        
        private void mergeSort(int[] arr,int start,int end){
            if(start < end){  // 拆分分治的过程,递归出口,长度为1默认排好序
                int mid = start + (end - start) / 2;
                mergeSort(arr,start,mid);
                mergeSort(arr,mid+1,end);
                merge(arr,start,mid,end);  // 最后合并
            }
        }
        
        private void merge(int[] arr,int start,int mid,int end){
            int[] temp = new int[end - start + 1];	// 辅助数组,最后赋值回原数组
            
            int i = start,j = mid + 1;
            int index = 0;
            while(i <= mid && j <= end){
                if(arr[i] > arr[j]){
                    temp[index++] = arr[j++];
                    
                    // 与归并排序就多了下面这两句
                    // 合并数组时,array[i]大于后面array[j]时
                    // 则array[i]~array[mid]都是大于array[j]的,所以count += mid + 1 - i
                    count += mid - i + 1;
                    count = count > 1000000007 ? count % 1000000007 : count;
                }else{
                    temp[index++] = arr[i++];
                }
            }
            
            while(i <= mid)
                temp[index++] = arr[i++];
            while(j <= end)
                temp[index++] = arr[j++];
            
            for (int k = 0;k < temp.length;k++)
                arr[start+k] = temp[k];
        }
    }
    
    


    16

    两个链表的第一个公共结点

    // 先走链表二者长度差,然后同步走到相同节点
    public class Solution {
        public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
            
            // 0. 移动节点要记得复位,这里卡了好久,不然NPE
            ListNode temp1 = pHead1;
            ListNode temp2 = pHead2;
            
            // 1. 记录二者的长度
            int p1 = 0, p2 = 0;
            while(pHead1 != null){
                p1++;
                pHead1 = pHead1.next;
            }
            while(pHead2 != null){
                p2++;
                pHead2 = pHead2.next;
            }
            
            if(pHead1 != pHead2) return null;  // 尾节点都不相交,下面也无需遍历了,简化操作可忽略
            
            // 2. 上面移动指针要复位
            //    移动长链表,移动距离为二者长度差
            pHead1 = temp1;
            pHead2 = temp2;
            if(p1 > p2){
                int temp = p1 - p2;
                while(temp > 0){
                    pHead1 = pHead1.next;
                    temp--;
                }
            }else{
                int temp = p2 - p1;
                while(temp > 0){
                    pHead2 = pHead2.next;
                    temp--;
                }
            }
            
            // 3. 二者并行找相同节点
            while(pHead1 != null || pHead2 != null){
                if(pHead1 == pHead2){
                    return pHead1;
                }
                pHead1 = pHead1.next;
                pHead2 = pHead2.next;
            }
            
            // 4. 没有公共节点
            return null;
        }
    }
    
    
    // 思路二,两条y状的链表,从尾遍历到头,第一个不相同的就是交点,使用栈/递归实现
    
    
    // 思路三:最优解,双指针
    // 两个指针同步走,哪个到了链表尾,就设置为对方的头节点继续遍历,最后会相遇
    // 长度相同有公共结点,第一次就遍历到;没有公共结点,走到尾部NULL相遇,返回NULL
    // 长度不同有公共结点,第一遍差值就出来了,第二遍一起到公共结点;没有公共,一起到结尾NULL
    public class Solution {
        public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
            
            ListNode p1 = pHead1;
            ListNode p2 = pHead2;
            
            while(p1 != p2){
                p1 = (p1 == null ? pHead2 : p1.next);
                p2 = (p2 == null ? pHead1 : p2.next);
            }
            return p1;
        }
    }
    
    


    17

    统计一个数字在排序数组中出现的次数(排序就二分)

    // 思路:傻子做法
    public class Solution {
        public int GetNumberOfK(int [] array , int k) {
            int cnt = 0;
            for(int i = 0; i < array.length; i++){
                if(k == array[i]){
                    cnt++;
                }
            }
            return cnt;
        }
    }
    
    
    // 思路:首先二分法,找到之后向前向后找
    public class Solution {
        public int GetNumberOfK(int [] array , int k) {
            
            int cnt = 0;
            int left = 0;
            int right = array.length - 1;
            int mid = -1;
            
            while(left <= right){
                mid = left + (right-left) / 2;
                if(array[mid] == k){
                    cnt++;
                    break;
                }else if(array[mid] < k){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
            
            if(mid == -1) return cnt;  // 没找到相同的,先退出了
            
            for(int i = mid+1; i < array.length; i++){
                if(array[i] == k) cnt++;
                else break;
            }
            for(int i = mid-1; i >= 0; i--){
                if(array[i] == k) cnt++;
                else break;
            }
            return cnt;
        }
    }
    
    
    // 思路三:最优,二分左右边界,相减即可
    public class Solution {
        public int GetNumberOfK(int [] array , int k) {
            if(array == null || array.length == 0) return 0;
            
            int first = getFirstK(array,k);
            int last = getLastK(array,k);
            
            if(first == -1 || last == -1) return 0;
            else return last - first + 1;
        }
        private int getFirstK(int [] array, int k){
            int low = 0;
            int high = array.length - 1;
            while(low <= high){
                int mid = low + (high-low) / 2;
                if(array[mid] == k){
                    high = mid - 1;
                }else if(array[mid] > k){
                    high = mid - 1;
                }else{
                    low = mid + 1;
                }
            }
            if(low == array.length) return -1;  // 这里最重要
            return array[low] == k ? low : -1;
        }
        private int getLastK(int [] array, int k){
            int low = 0;
            int high = array.length - 1;
            while(low <= high){
                int mid = low + (high - low) / 2;
                if(array[mid] == k){
                    low = mid + 1;
                }else if(array[mid] > k){
                    high = mid - 1;
                }else{
                    low = mid + 1;
                }
            }
            if(high == -1) return -1;
            return array[high] == k ? high : -1;
        }
    }
    
    


    18

    求树深

    // 递归
    public class Solution {
        public int TreeDepth(TreeNode root) {
            // 递归出口
            if(root == null) return 0;
            
            // 递归条件
            return Math.max( TreeDepth(root.left)+1 , TreeDepth(root.right)+1 );
            
        }
    }
    
    
    // 层次遍历解决,此层次和之前的不同
    // 每层次遍历一遍,即深度+1
    // 遍历完就深度也出来了
    // 注意,遍历一次就要一层全部处理完,否则就不是树深+1了
    import java.util.LinkedList;
    public class Solution {
        public int TreeDepth(TreeNode root) {
            
            int cnt = 0;                                    // 层数
            LinkedList<TreeNode> queue = new LinkedList();  // 存储节点模拟层次的
            
            if(root == null) return cnt;
            queue.addLast(root);
            
            while(!queue.isEmpty()){
                int size = queue.size();
                for(int i = 0; i < size; i++){              // for将当前层处理完
                    TreeNode temp = queue.removeFirst();    // 出队
                    if(temp.left != null) queue.addLast(temp.left);
                    if(temp.right != null) queue.addLast(temp.right);
                }
                cnt++;
            }
            return cnt;
        }
    }
    
    


    19

    验证平衡二叉树平衡(任何结点的两个子树的高度差小于等于1),可以结合17题

    // 递归
    public class Solution {
        public boolean IsBalanced_Solution(TreeNode root) {
            // 空也是一个平衡树
            if(root == null) return true;
            return getDepth(root) != -1;
        }
        
        // 后序遍历算深度,每个节点只用算一次
        private int getDepth(TreeNode node){
            
            if(node == null) return 0;
            
            // 左树的深度,+1动作放到最后,因为下面要判断-1
            int left = getDepth(node.left);
            if(left == -1) return -1;
            
            // 右树的深度
            int right = getDepth(node.right);
            if(right == -1) return -1;
            
            // 左右树深度比较,也就是递归
            if(Math.abs(left - right) > 1) return -1;
            
            // 当前
            return Math.max(left,right) + 1;
        }
    }
    
    


    20

    数组中只出现一次的数字

    // num1,num2分别为长度为1的数组。传出参数,将num1[0],num2[0]设置为返回结果即可,C语言题目垃圾
    // 用HashSet去重特性
    import java.util.HashSet;
    public class Solution {
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            HashSet<Integer> set = new HashSet();
            
            // set.add,如果存在返回true,如为false则插入元素
            for(int i = 0; i < array.length; i++){
                if(set.contains(array[i])){
                    set.remove(array[i]);
                }else{
                    set.add(array[i]);
                }
            }
            Object[] temp = set.toArray();
            num1[0] = (int) temp[0];
            num2[0] = (int) temp[1];
        }
    }
    
    
    // 最优解
    // 使用数组的异或运算,相同为0,不同为1,任何数与0异或为本身
    // 如果一个数字只出现一次,其余两次,那么全体异或过程中两两相同的就会抵消变为0,剩下的数和0异或得出本身
    // eg:
    int res = 0;
    int[] nums = {1,1,2,3,4,5,3,4,5};
    for(int value : nums){
        res ^= value;
    }
    System.out.println(res); // 2
    
    // ——————————————————————————————————————————————————————————————————————————————————————
    
    // 如果出现了两次,那么就要进行分组异或 
    // 假如两个不同的数为 a、b,那么所有数字异或结果就是 a^b 的结果,记为x
    // x转成二进制,其中的0和1表示a、b二进制中相同和不同的部分
    // 若选二进制x中,不为0的位,按照该位分组,默认选不为0的最低位
    // 流程:
    // 1.对所有数字异或,得出x
    // 2.在x中找不为0的最低位
    // 3.根据这位对所有的数字进行分组
    // 4.在每个组内进行异或操作,得到两个数字
    //num1,num2分别为长度为1的数组。传出参数
    //将num1[0],num2[0]设置为返回结果
    public class Solution {
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            
            int ret = 0;
            for(int num : array){
                ret ^= num;
            }
            
            int div = 1;
            while((div & ret) == 0){
                div <<= 1;		// div两倍关系,才能在二进制上逐步变成1,所以分组也只能是2,4,6来分
            }
            
            int a = 0;
            int b = 0;
            for(int num : array){
                if ((div & num) != 0) {
                    a ^= num;
                } else {
                    b ^= num;
                }
            }
    
            num1[0] = a;
            num2[0] = b;
        }
    }
    
    


  • 相关阅读:
    图片灰度化,并且resize图片
    C语言学习笔记
    路飞学城14天集训营作业2—三级菜单
    路飞学城14天集训营作业4—员工信息表
    路飞学城14天集训营作业3—购物车
    路飞学城14天集训营作业1—登陆认证
    js钩子函数
    APP2.0后台控件API
    KindEditor 插件API使用说明
    TreeView插件 API
  • 原文地址:https://www.cnblogs.com/Howlet/p/14132494.html
Copyright © 2020-2023  润新知