随机名言

剑指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;
    }
}



posted @ 2020-12-14 11:59  Howlet  阅读(75)  评论(0编辑  收藏  举报

Copyright © By Howl