前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >剑指Offer-2

剑指Offer-2

作者头像
晚上没宵夜
发布2020-12-16 15:22:43
4260
发布2020-12-16 15:22:43
举报

只能说受益匪浅

1

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

代码语言:javascript
复制
// 思路:用辅助栈来模拟出入栈
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

从上往下层级遍历二叉树

代码语言:javascript
复制
// 思路:用一个 队列 模拟层次
// 用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;
    }
}
代码语言:javascript
复制
// 用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

判断是否后序遍历

代码语言:javascript
复制
// 现在开始自己规定,凡是自己传进去的数组长度这些参数,都是实际长度,就是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

二叉树和为某值的路径

代码语言:javascript
复制
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

复杂链表的复制

代码语言:javascript
复制
/*
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

二叉搜索树转变双向链表

代码语言:javascript
复制
/**
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 + 交换 / 回溯)

代码语言:javascript
复制
// 思路:根据字符串的排列的特点,选择深度优先搜索,可通过字符交换实现,重复字符用剪枝
// 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

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

代码语言:javascript
复制
// 思路1:遍历多次,保存每个元素出现的次数
// 思路2:排序后,众数肯定出现在中间
代码语言:javascript
复制
// 最优解
// 思路:摩尔投票法,查找超过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问题(快排,堆排)

代码语言:javascript
复制
// 无脑解法
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;
    }
}
代码语言:javascript
复制
// 快排,我叫为哨兵排
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

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

代码语言:javascript
复制
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出现的次数

代码语言:javascript
复制
// 暴力转成字符串判断
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;
    }
}
代码语言:javascript
复制
// 区分位数: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;
    }
}
代码语言:javascript
复制
// 最优解,上面的优化
// 当百位 = 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

把数组排成最小

代码语言:javascript
复制
// 本质是排序问题,一般用快排
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;
    }
}
代码语言:javascript
复制
// 思路二
// 数字m、n拼接成 mn 和 nm
// 若mn>nm,则m大于n
// 若mn<nm,则m小于n
// 若mn=nm,则m等于n

13

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

代码语言:javascript
复制
// 思路:一个丑数一定由另一个丑数乘以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

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

代码语言:javascript
复制
// 哈希表,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;
    }
}
代码语言:javascript
复制
// 变形体,返回第一个只出现一次的字符
// 哈希表,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

数组的逆序对

代码语言:javascript
复制
// 暴力破解法,双层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;
    }
}
代码语言:javascript
复制
// 最优解
// 归并排序的利用,分治过程中前后数字可对比,是统计的最佳时机
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

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

代码语言:javascript
复制
// 先走链表二者长度差,然后同步走到相同节点
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;
    }
}
代码语言:javascript
复制
// 思路二,两条y状的链表,从尾遍历到头,第一个不相同的就是交点,使用栈/递归实现
代码语言:javascript
复制
// 思路三:最优解,双指针
// 两个指针同步走,哪个到了链表尾,就设置为对方的头节点继续遍历,最后会相遇
// 长度相同有公共结点,第一次就遍历到;没有公共结点,走到尾部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

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

代码语言:javascript
复制
// 思路:傻子做法
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;
    }
}
代码语言:javascript
复制
// 思路:首先二分法,找到之后向前向后找
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;
    }
}
代码语言:javascript
复制
// 思路三:最优,二分左右边界,相减即可
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

求树深

代码语言:javascript
复制
// 递归
public class Solution {
    public int TreeDepth(TreeNode root) {
        // 递归出口
        if(root == null) return 0;
        
        // 递归条件
        return Math.max( TreeDepth(root.left)+1 , TreeDepth(root.right)+1 );
        
    }
}
代码语言:javascript
复制
// 层次遍历解决,此层次和之前的不同
// 每层次遍历一遍,即深度+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题

代码语言:javascript
复制
// 递归
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

数组中只出现一次的数字

代码语言:javascript
复制
// 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];
    }
}
代码语言:javascript
复制
// 最优解
// 使用数组的异或运算,相同为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;
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-12-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档