数据结构入门(力扣算法)

news2024/10/7 14:27:49

数据结构入门

  • 前面的题号为力扣的题号
  • 数组的
    • 217. 存在重复元素
    • 53. 最大子数组和
    • 1. 两数之和
    • 88. 合并两个有序数组
    • 350. 两个数组的交集 II
    • 121. 买卖股票的最佳时机
    • 566. 重塑矩阵
    • 118. 杨辉三角
    • 36. 有效的数独
    • 73. 矩阵置零
  • 字符串的
    • 387. 字符串中的第一个唯一字符
    • 383. 赎金信
    • 242. 有效的字母异位词
  • 链表
    • 141. 环形链表
    • 21. 合并两个有序链表
    • 203. 移除链表元素
    • 206. 反转链表
    • 83. 删除排序链表中的重复元素
  • 栈和队列
    • 20. 有效的括号
    • 232. 用栈实现队列
    • 144. 二叉树的前序遍历
    • 94. 二叉树的中序遍历
    • 145. 二叉树的后序遍历
    • 102. 二叉树的层序遍历
    • 104. 二叉树的最大深度
    • 101. 对称二叉树
    • 226. 翻转二叉树
    • 112. 路径总和
    • 700. 二叉搜索树中的搜索
    • 701. 二叉搜索树中的插入操作
    • 98. 验证二叉搜索树
    • 653. 两数之和 IV - 输入二叉搜索树
    • 235. 二叉搜索树的最近公共祖先

前面的题号为力扣的题号

数组的

217. 存在重复元素

问题
在这里插入图片描述

思路

  1. 排序,选用快速排序
  2. 两两相邻比较是否存在相同。

代码

class Solution {
    public boolean containsDuplicate(int[] nums) {
        // 先将数组排序
        kp(nums, 0, nums.length-1);

        // 局部两两比较不能相等
        for(int i=1; i<nums.length; i++) {
            if(nums[i-1]==nums[i]){
                return true;
            }
        }        

        return false;
    }

    // 有时间复杂度要求,这里调用快排
    static void  kp(int nums[], int low, int high){
        if(low<high) {
            int p =  kp_p(nums, low, high);
            // 左表快排
            kp(nums, low, p-1);
            // 右表快排
            kp(nums, p+1, high);
        }
    }

    static int kp_p(int nums[], int low, int high){
        int t = nums[low];
        while (low<high) {
            while(low<high && nums[high]>=t) high--;
            nums[low] = nums[high];
            while(low<high && nums[low]<=t) low++;
            nums[high] = nums[low];
        }

        nums[low] = t;
        return low;
    }

}

53. 最大子数组和

问题
在这里插入图片描述

思想

  1. 注意本题可以用动态规划的思路去解决
  2. 要求的为最大连续的和最大, 我们要找到那个有关联的子问题
  3. 子问题分析
    3.1 到第一个数位置结束, 最大连续数就是第一个位置的数
    3.2 到第二个位置的数结束, 最大连续和,若前一个位置最大连续为大于0,则最大连续要加上前面的, 如果前一个数的最大连续为负数, 则自己本身就是最大连续和
    3.3 所以每个最大连续和就和到前面位置的最大连续和有直接联系。 这就划分成求到每个位置最大连续和的子问题的处理

代码

class Solution {
    public int maxSubArray(int[] nums) {
        // 创建一个以i处元素为结尾的连续最大和数组 dp
        int []dp = new int[nums.length];

        dp[0] = nums[0];

        // dp[i] :  注意dp[1] 即 dp[0] 如果是大于0,  则 到dp[1] 最大连续为 dp[0] + nums[1] 
        //  如果dp[0] 小于0  , 则dp[1] 最大连续就是  nums[1] 本身

        for(int i=1; i<nums.length; i++) {
            if(dp[i-1]>0) {
                dp[i] = dp[i-1] + nums[i];
            }else {
                dp[i] = nums[i];
            }
        }

        // 此时dp数组中保存了以i位置为结束的最大的连续和, 我们只需找到以某个位置结束的最大值即可
        int max = dp[0];
        for(int i=1; i<dp.length; i++) {
            if(dp[i] > max) {
                max = dp[i];
            }
        }

        return max;
    }
}

1. 两数之和

问题

在这里插入图片描述
思想

  1. 只要求任意两元素加和为目标值, 并且要这两个数不为同一个位置的元素
  2. 题目要求 时间复杂度为 n方, 所以两层循环解决就可以

代码

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int indexs[] = new int[2];  // 用来保存和为目标值的两个下标
        for(int i=0; i<nums.length; i++) {
            for(int j=0; j<nums.length; j++) {
                if(nums[i] + nums[j] == target && i!=j) {
                    indexs[0]=i;
                    indexs[1]=j;
                    return indexs;
                }
            }
        }
        return indexs;
    }
}

88. 合并两个有序数组

题目
在这里插入图片描述

思想

  • 本质上就是完成一个归并操作
  • 特别注意归并结束后,一定检查两个数组的元素是否还有元素未被归并进去

代码

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        // 先创建一个临时的数组
        int numst[] = new int[n+m];

        int i,j,k;

        // 先用归并,归并到一个临时数组
        for(i=0,j=0,k=0; i<m&&j<n;) {
            if(nums1[i]<=nums2[j]) {
                numst[k++] = nums1[i++];
            }else {
                numst[k++] = nums2[j++];
            }
        }
        // 可能有一部分归并结束,另一个数组还有元素未归并
        while(i<m) {
            numst[k++] = nums1[i++];
        }
        while(j<n) {
            numst[k++] = nums2[j++];
        }

        // 再将临时数组复制到nums1
        for(i=0; i<m+n; i++) {
            nums1[i] = numst[i];
        }
    }
}

350. 两个数组的交集 II

问题
在这里插入图片描述

思想

  1. 首先将两个数组进行排序
  2. 两个有序的数组,相同就输出。不同的话,就小的下标递增
  3. 当有一个下标走到尽头,就结束了。

代码

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        // 采用排序 + 双指针的方式
        // 先将nums1与nums2进行排序
        kp(nums1, 0, nums1.length-1);
        kp(nums2, 0, nums2.length-1);

        // 定义两个指针,分别指向两个数组的起始位置
        int p1=0,p2=0,k=0;
        int numst[] = new int[nums1.length + nums2.length];
        while(p1<nums1.length && p2<nums2.length) {
            if(nums1[p1] == nums2[p2]) {
                //相等的时候保存
                numst[k++] = nums1[p1];
                p1++;
                p2++;
            }else if(nums1[p1] > nums2[p2]) {
                // 小的数后移
                p2++;
            }else {
                p1++;
            }
        }
        return Arrays.copyOfRange(numst, 0, k);

    }

    // 快排
    void kp(int d[], int low, int high){
       if(low<high) {
            // 一次划分,确定元素的位置
            int p = kp_hf(d, low, high);

            // 将上面的划分左右侧的子表 分别再递归划分
            kp(d, low, p-1);
            kp(d, p+1, high);
       }
    }

    // 一次快排划分
    int kp_hf(int d[], int low, int high) {
        int p = d[low];
        while(low<high) {
            while(low<high && d[high] >= p) high--;
            d[low] = d[high];
            while(low<high && d[low] <= p) low++;
            d[high] = d[low];   
        }
        d[low] = p;
        return low;
    }
}

121. 买卖股票的最佳时机

问题
在这里插入图片描述

思想

  1. 用动归的思想,在代码有详细注释
  2. dp[i] 动态记录截至第i天的最低价格(用于购入)
  3. max 用于动态记录 当天股价卖出, 与最低点购入的利润是否为最大

代码

class Solution {
    public int maxProfit(int[] prices) {

        // 根据动态规划的思想去解决
        // 1. 截至第i是否为最低点, 与截至其前一天是不是最低点有关联,第i天最低,则截至第i天,保留第i天,第i天不是最低点,则保留截至第i-1天最低点数
        // 2. 今天的股价卖出(今天的股价减去 截至今天的最低价的最高值)

        int max=0;
        int dp[] = new int[prices.length];

        dp[0] = prices[0];  // dp保存截至第i天买入的价格的最低价 

        for(int i=1; i<prices.length; i++) {
            // 不断判断截至第i天的最低购入价格

            // 今天是最低价,则截至今天最低价是今天的价, 否则为前i-1天的最低价
            dp[i] = dp[i-1] < prices[i] ? dp[i-1] : prices[i]; 

            // 判断今天的价格减去截至今天的最低价dp[i]是否为最大利润
            max = max>(prices[i] - dp[i]) ? max : (prices[i] - dp[i]);
        }

        return max;

    }
}

566. 重塑矩阵

问题

在这里插入图片描述

思想

  1. 注意重塑的时候,原矩阵和重塑后的矩阵是否是元素数相同的矩阵.
  2. 重塑的时候遍历原矩阵, 新的矩阵的列标到指定数的时候做行标和列标的调整即可。

代码

class Solution {
    public int[][] matrixReshape(int[][] mat, int r, int c) {
        int [][] jz = new int[r][c]; // 重塑后矩阵的维度
        int m=0,n=0; // 用于记录新矩阵每行一共多少元素

        // 新矩阵的元素数应该与原矩阵元素数一致
        if(mat.length * mat[0].length != r * c) {
            // 不一致时返回原矩阵
            return mat;
        }

        // 遍历原矩阵
        for(int i=0; i<mat.length; i++) {
            for(int j=0; j<mat[0].length; j++) {
                if(n==c) {
                    // 新矩阵要换行了
                    m++; // 行号自增
                    n=0; // 列号变0
                }
                jz[m][n++] = mat[i][j]; 
            }
        }

        return jz;
    }
}

118. 杨辉三角

问题
在这里插入图片描述

思想

  1. 注意用list实现的时候

代码

class Solution {
    public List<List<Integer>> generate(int numRows) {
        // 创建一个二维的列表结构
        List<List<Integer>> list = new ArrayList<>();

        for(int i=0; i<numRows; i++) {
            // 创建每行的列表
            ArrayList<Integer> row = new ArrayList<>();

            for(int j=0; j<=i; j++) {
                if(j==0 || j==i) { // 两斜边位置为1
                    row.add(1);
                } else {
                    row.add(list.get(i-1).get(j-1) +  list.get(i-1).get(j));
                }
            }

            list.add(row); // 将本行添加进去
        }

        return list;
    }
}

36. 有效的数独

问题
在这里插入图片描述

思想

  1. 用两个二维数组分别记录, 某数字在所在行、所在列出现的次数
  2. 用三维数组表示 该数字 所在小区域内出现的 次数。

在这里插入图片描述

代码

class Solution {
    public boolean isValidSudoku(char[][] board) {
        // 创建一个二维数组, 记录当前行的数据各有多少个
        // 行标表示第几行, 列标表示的是具体的数值  row[i][j]表示第i行值为j的的数的个数
        int row[][] = new int[9][9];

        // 创建一个用于记录每列的元素个数统计的二维数组
        int col[][] = new int[9][9];

        // 创建一个三维数组,统计 9个3*3的小矩形内的元素个数  i/3 , j/3  正好划分为九个
        int juzhen[][][] = new int [3][3][9];
        // 遍历这个9*9的二维数组, 并记录board[i][j] 这个元素值,在所在行、所在列、所在小矩形区域内的个数
        for(int i=0; i<9; i++) {
            for(int j=0; j<9; j++) {
                if(board[i][j] != '.') {
                    //有元素时,不为. 
                    int index = board[i][j] - '1';
                    // 记录该元素  在所在行的出现次数
                    row[i][index]++;
                    // 记录该元素  在所在列出现的次数
                    col[j][index]++;
                    // 记录该元素在所在矩阵内出现的次数
                    juzhen[i/3][j/3][index]++;
                    // 判断在所在行、列、矩阵是否超过1次,超过就不是有效数独  
                    if(row[i][index]>1 || col[j][index]>1 || juzhen[i/3][j/3][index]>1) {
                        return false;
                    }
                }
            }
        }

        // 有效返回true
        return true;
    }
}

73. 矩阵置零

问题

在这里插入图片描述

思想

在这里插入图片描述

代码

class Solution {
    public void setZeroes(int[][] matrix) {
        boolean col0falg = false, row0falg=false;  // 标记第一行/列 原数组是否有零

        int rowLen = matrix.length, colLen = matrix[0].length;

        for(int i=0; i<rowLen; i++) {
            // 判断第一列原本是否有0
            if(matrix[i][0] == 0) {
                col0falg = true;
            }
        }

        for(int j=0; j<colLen; j++) {
            // 判断第一行原本是否有0
            if(matrix[0][j] == 0) {
                row0falg = true;
            }
        }

        // 判断除了第一行、列的元素是否为0, 为0时标记所在行列的第一行列为0
        for(int i=1; i<rowLen; i++) {
            for(int j=1; j<colLen; j++) {
                if(matrix[i][j] == 0) {  // 当前元素为0则↓
                    // 设置第一行/列的所在位置为0
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
        }

        // 将第一行/列 为0的所在行列设为0
        for(int i=1; i<rowLen; i++) {
            for(int j=1; j<colLen; j++) {
                if(matrix[i][0] == 0 || matrix[0][j] == 0) { // 根据当前行列有0设置该元素为0
                    matrix[i][j] = 0;
                }
            }
        }

        // 根据原本第一行列是否有0, 设置第一行列为0
        if(col0falg) {
            // 第一列有0
            for(int i=0; i<rowLen; i++) {
                matrix[i][0] = 0;
            }
        }
        if(row0falg) {
            // 第一行有0
            for(int i=0; i<colLen; i++) {
                matrix[0][i] = 0;
            }
        }
    }
}

字符串的

387. 字符串中的第一个唯一字符

问题

在这里插入图片描述
思路

// 两次遍历
// 第一次遍历用哈希表纪录出现的字母频次
// 第二次遍历判断一次频次的字母,返回其下标

代码

class Solution {
    public int firstUniqChar(String s) {
        // 两次遍历 
        // 第一次遍历用哈希表纪录出现的字母频次
        // 第二次遍历判断一次频次的字母,返回其下标

        HashMap<Character, Integer> map = new HashMap<>();

        // 第一次遍历,统计频次
        for(int i=0; i<s.length(); i++) {
            char el = s.charAt(i);
            map.put(el, map.getOrDefault(el, 0)+1);
        }

        // 第二次遍历, 判断
        for(int i=0; i<s.length(); i++) {
            char el = s.charAt(i);
            if(map.get(el) == 1) {
                return i;
            }
        }

        // 如果没有出现频次为1的则说明全有重复
        return -1;
    }
}

383. 赎金信

问题
在这里插入图片描述
·思路

统计 两个字符串的字符出现的次数, 再比较字符串1出现的字符次数是否小于字符串2出现的次数
全部小于则,字符串1可由字符串2组成

代码


class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
       /**
            统计 两个字符串的字符出现的次数, 再比较字符串1出现的字符次数是否小于字符串2出现的次数
            全部小于则,字符串1可由字符串2组成
        */

        HashMap<Character, Integer> map1 = new HashMap<>();
        HashMap<Character, Integer> map2 = new HashMap<>();

        // 统计字符串1的字符个数
        for(int i=0; i<ransomNote.length(); i++) {
            char el = ransomNote.charAt(i);
            map1.put(el, map1.getOrDefault(el, 0) + 1);
        }

        // 统计字符串2的字符个数
        for(int i=0; i<magazine.length(); i++) {
            char el = magazine.charAt(i);
            map2.put(el, map2.getOrDefault(el, 0) + 1);
        }

        // 比较第一个字符串出现的字符是否都小于第二个字符串的字符数
        for(char key: map1.keySet()) {
            if(map1.get(key) > map2.getOrDefault(key, 0)) {
                return false;
            }
        }

        // 全部满足时会到这里
        return true;
    }
}

242. 有效的字母异位词

问题

在这里插入图片描述
思想

用map分别统计两个字符串出现的字符的次数
第一个字符串出现时频率加1
第二个字符串出现时频率减1
最后看频率是否全为0

代码

class Solution {
    public boolean isAnagram(String s, String t) {
        /**
            用map分别统计两个字符串出现的字符的次数
            第一个字符串出现时频率加1
            第二个字符串出现时频率减1
            最后看频率是否全为0
         */


        // 创建两个map,用于统计字符出现的频次
         HashMap<Character, Integer> map = new HashMap<>();

        // 统计字符串1的字符出现频率(出现加一)
        for(int i=0; i<s.length(); i++) {
            char el = s.charAt(i);
            map.put(el, map.getOrDefault(el, 0) + 1);
        }

        // 统计字符串2的字符出现频率(出现减一)
        for(int i=0; i<t.length(); i++) {
            char el = t.charAt(i);
            map.put(el, map.getOrDefault(el, 0) - 1);
        }
        
        // 比较是否相等
        for(char key:map.keySet()) {
            if(map.get(key) != 0) {
                // 存在一个不相等,就不是异位词
                return false;
            }
        }

        // 字符个数全相等才是异位词
        return true;
    }
}

链表

141. 环形链表

问题
在这里插入图片描述
思想

  1. 方法一:就是用快慢指针, 无环的话快指针会先到尾部,有环的话快指针会追上慢指针
  2. 方法二: 通过一个set集合结构, 如果指针到尾部就无环,如果指针被重复添加到set则有环(add方法添加重复时返回fasle)

快慢指针法

 public boolean hasCycle(ListNode head) {
        if(head==null || head.next==null) {
            // 快慢指针的起始位置为空,一定无环
            return false;
        }

        // 判断链表是否有环,可通过快慢指针
        ListNode high,low = new ListNode();
        low = head;
        high = head.next;

        while(low != high) {
            // 如果无环,快指针能率先到达尾部
            if(high == null || high.next==null) {
                // 此时为无环
                return false;
            }

            // 快指针一次后移两位,慢指针一次后移一位
            low = low.next;
            high = high.next.next;
        }

        // 快慢指针相等的时候跳出了循环代表有环了
        return true;
       
    }

使用set判断是否有重复结点的方法

 public boolean hasCycle(ListNode head) {
        Set<ListNode> set = new HashSet<>();

        while(head!=null) { //head 等于 null 时表示到尾部无环
            if(!set.add(head)) {
                // 有重复时添加返回false,表示有环, 取反后进入该方法体
                return true;
            }
            head = head.next;
        }

        // 无环
        return false;
    }

21. 合并两个有序链表

问题

在这里插入图片描述
思想

比较两个链表的值,将较小的结点拿到新的链表,以尾插法的方式进行插入
注意从两个链表拿出结点时的断链操作

代码

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        /**
            比较两个链表的值,将较小的结点拿到新的链表,以尾插法的方式进行插入
            注意从两个链表拿出结点时的断链操作
         */
        // list 为最终链表表头, l1,l2用于暂存list1/2, r为新链表的尾指针
        ListNode l1,l2,r = new ListNode();
        l1 = list1;
        l2 = list2;
        ListNode list = new ListNode();
        r = list;

        while(l1!=null && l2 != null) {
            if(l1.val < l2.val) {
                r.next = l1;  // 将l1链接到新链表尾部 
                l1 = l1.next;  // l1后移
             
            }else{
                r.next = l2;  // 将l2链接到新链表尾部 
                l2 = l2.next;  // l2后移
            }
            r = r.next;  // 新链表尾指针后移
            r.next = null;  // 断链
        }
        // 检查还没有遍历完的链表
        while(l1!=null) {
            r.next = l1;
            l1=l1.next;
            r = r.next;
            r.next=null;
        }
        while(l2!=null) {
            r.next = l2;
            l2=l2.next;
            r = r.next;
            r.next=null;
        }

        // 返回新链表
        list = list.next;
        return list;
    }

}

203. 移除链表元素

问题
在这里插入图片描述
思想

通过一个游标遍历链表,当遇到指定删除的值的时候,进行删除操作, 注意一些细节,在代码中有标注

代码

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        /**
            通过一个游标遍历链表,当遇到指定删除的值的时候,进行删除操作
         */

         ListNode t = head;

        // 头结点为空直接返回
        if(head == null) {
            return head;
        }
        
        while(t.next != null) {
            if(t.next.val == val) {
                // t.next 为要删除的时 直接删除
                t.next = t.next.next; 
            }else{
                // 不为删除时后移
                t = t.next;
            }
        }
        
        // 判断表头是否为要删除
        if(head.val == val && head!=null) {
            head = head.next;
        }

        return head;
    }
}

206. 反转链表

问题

在这里插入图片描述

思想

遍历一个遍历,拿到的结点以头插法插入到另一个链表

代码

class Solution {
    public ListNode reverseList(ListNode head) {
        /**
        
            遍历一个遍历,拿到的结点以头插法插入到另一个链表
         */

        // 如果本来就是空
        if(head==null) {
            return head;
        }

        // 创建新的链表
        ListNode L = head;   // 用于保存反转后的链表
        ListNode T;          // 用于遍历原链表
        
        head = head.next;    // 防断链,第一个结点为头结点(本质考察不带头结点的链表反转)
        L.next = null;       // 断链
        while(head!=null) {  // 下一个结点不空时
            T = head;        // 拿到下一个结点
            head = head.next;  // 防止断链

            T.next = L;       // 头插法插入新表
            L = T;
        } 

        return L;


    }
}

83. 删除排序链表中的重复元素

问题
在这里插入图片描述

思想

当链表只有一个元素或者空的时候, 直接返回
当链表多余一个元素的时候,比较当前结点与下一个结点是否相同,相同时删除下个结点

代码

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        /**
            当链表只有一个元素或者空的时候, 直接返回
            当链表多余一个元素的时候,比较当前结点与下一个结点是否相同,相同时删除下个结点
         */

         if(head==null || head.next==null) {
             return head;
         }

         ListNode L = head;
         while(L.next!=null) {
            
            if(L.next.val == L.val) {
                // 下一个结点和当前结点相同时
                L.next = L.next.next;
            }else {
                // 不相同时后移
                L = L.next;
            }
         }

         return head;
    }
}

栈和队列

20. 有效的括号

问题
在这里插入图片描述

思想

通过栈这个数据结构实现括号匹配,当遇到左括号入栈,遇到右括号出战比对是否对应
当字符串遍历结束时,栈空则匹配成功,栈不空则匹配失败。

代码

class Solution {
    public boolean isValid(String s) {
        /**
            通过栈这个数据结构实现括号匹配,当遇到左括号入栈,遇到右括号出战比对是否对应
            当字符串遍历结束时,栈空则匹配成功,栈不空则匹配失败。
         */
        
        // 声明栈
        Stack<Character> stack = new Stack<>();

        // 遍历字符串
        for(int i=0; i<s.length(); i++) {
            char el = s.charAt(i);

            if(el=='(' || el=='[' || el=='{') {
                // 左括号就入栈
                stack.push(el);
            }else{
                if(stack.empty()) {
                    // 上来就不是左括号,一定不对
                    return false;
                }
                char t = stack.pop();  // 栈顶出栈
                // 如果是右括号时
                if(el==')' && t!='(' )   {
                    return false;
                }
                
                if(el==']' && t!='[') {
                    return false;
                }

                if(el=='}' && t!='{') {
                    return false;
                }
            }  
        }
        return stack.empty()?true:false;
         
    }
}

232. 用栈实现队列

问题

在这里插入图片描述

思想

// 两个栈都是无限容量
入队: 往栈1进
队空: 栈1,栈2都空
出队: 栈2有元素,直接出栈2, 无元素时,先把栈1出到栈2,再从栈2出

代码

class MyQueue {
    /**
        // 两个栈都是无限容量
        入队: 往栈1进
        队空: 栈1,栈2都空
        出队: 栈2有元素,直接出栈2, 无元素时,先把栈1出到栈2,再从栈2出
     */

    // 声明两个栈
    Stack<Integer> s1, s2;
    
    public MyQueue() {
        // 构造函数中初始化
        s1 = new Stack<>();
        s2 = new Stack<>();
    }
    
    public void push(int x) { // 入队操作
        // 栈1入队
        s1.push(x);
    }
    
    public int pop() {   // 出队
        if(s2.empty()) {
            // 栈2空
            //先把栈1入到栈2
            while(!s1.empty()) {
                s2.push(s1.pop());
            }
        }

        // 从栈2出栈(因为栈1出栈  再入栈2 后 就是队列了)
        return s2.pop();
    }
    
    public int peek() { // 获取对头元素
        if(s2.empty()) {
            // 栈2空
            //先把栈1入到栈2
            while(!s1.empty()) {
                s2.push(s1.pop());
            }
        }

        // 从栈2出栈(因为栈1出栈  再入栈2 后 就是队列了)
        return s2.peek();
    }
    
    public boolean empty() {  // 判断队空
        if(s1.empty() && s2.empty()) {
            // 两栈都空则空
            return true;
        }else {
            // 有一栈不空,则不空
            return false;
        }
    }
}

144. 二叉树的前序遍历

题目
在这里插入图片描述

思想

通过递归实现 先序

代码

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        // 创建List
        List<Integer> list = new ArrayList<>();
        // 调用先序遍历
        diGui(root, list);

        return list;
    }


    // 先序
    public void diGui(TreeNode T, List<Integer> list) {
        // 用于实现递归实现遍历二叉树
        if(T==null) {
            // 传进结点为空时
            return;
        }

        // 结点不为空
        list.add(T.val);
        // 遍历左子树
        diGui(T.left, list);
        // 遍历右子树
        diGui(T.right, list);
    }
}

94. 二叉树的中序遍历

题目
在这里插入图片描述

思想

递归实现 中序遍历

代码

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
    // 创建List
        List<Integer> list = new ArrayList<>();
        // 调用中序遍历
        diGui(root, list);

        return list;
    }


    // 先序
    public void diGui(TreeNode T, List<Integer> list) {
        // 用于实现递归实现遍历二叉树
        if(T==null) {
            // 传进结点为空时
            return;
        }

       
        // 遍历左子树
        diGui(T.left, list);

         // 结点不为空
        list.add(T.val);

        // 遍历右子树
        diGui(T.right, list);
    }
}

145. 二叉树的后序遍历

问题
在这里插入图片描述

思想

用递归实现 后序

代码

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
     // 创建List
        List<Integer> list = new ArrayList<>();
        // 调用中序遍历
        diGui(root, list);

        return list;
    }
    // 先序
    public void diGui(TreeNode T, List<Integer> list) {
        // 用于实现递归实现遍历二叉树
        if(T==null) {
            // 传进结点为空时
            return;
        }
        // 遍历左子树
        diGui(T.left, list);
        // 遍历右子树
        diGui(T.right, list);
         // 结点不为空
        list.add(T.val);
    }
}

102. 二叉树的层序遍历

题目
在这里插入图片描述

层次遍历实现代码

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        /**
            二叉树的层次遍历,通过队列辅助实现
         */

         // 创建一个辅助队列
        Queue<TreeNode> queue = new ArrayDeque<>();
        
        // 用来保存每层的元素
        List<List<Integer>> list = new ArrayList<>();

         // 先让根元素入队
         if(root==null){ // 根空直接返回
            return list;
         } else { // 根不空,入队
            queue.add(root);
         }

         while(!queue.isEmpty()) {
             // 队列不空时

             // 创建每层的List
             List<Integer> level = new ArrayList<>();

             int n = queue.size();  // 记录当前队列有多少元素,也就是本层多少元素
             for(int i=0; i<n; i++) {
                 TreeNode node = queue.poll();  // 出队

                 // 将该节点添加到二维列表
                 level.add(node.val);

                 // 判断左右指针域是否为空,不空则将结点入队
                 if(node.left !=null) {
                     queue.add(node.left);
                 }
                 if(node.right != null) {
                     queue.add(node.right);
                 }
             }
            // 将本层入到列表
            list.add(level);
        }

        return list;
    }
}

104. 二叉树的最大深度

问题
在这里插入图片描述
思想

通过递归去做, 当遇到空时返回 0, 非空时 为 左右遍历加1的最大值

代码

class Solution {
    public int maxDepth(TreeNode root) {
        // 通过递归去做, 当遇到空时返回  0, 非空时 为 左右遍历加1的最大值
        if(root == null) {
            return 0;
        }else {
            return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
        }

    }

}

101. 对称二叉树

题目
在这里插入图片描述

深度优先遍历思想实现代码

class Solution {
    public boolean isSymmetric(TreeNode root) {
        /**
            局部判断, 左有右无/左无右有为不对称
         */

        if(root == null) {  // 空树为对称
            return true;
        }
        
        // 如果不是空树,则左右局部递归判断
        return dfs(root.left, root.right);
         
    }

    public boolean dfs(TreeNode left, TreeNode right) {
        if(left==null && right==null) {  // 结点的左右都空对称
            return true;
        }

        if(left==null || right==null){  // 存在一面不空,一面空, 不对称
            return false;
        }

        if(left.val != right.val) {    // 两边都不空,但是  val不等也不行
            return false;
        }
        // 左右结点都有且 val相同     
        // 再向下比较, 注意左子树的左孩子要和右子树的右孩子比较
        //                左子树的右子孩子要和右子树的左孩子比较
        return dfs(left.left,right.right) && dfs(left.right, right.left);
    }
}

226. 翻转二叉树

问题
在这里插入图片描述
思想

可以通过先序遍历二叉树,对于每个遍历的结点,局部交换左右子树,每个结点都遍历过来的时候,整个树就交换了。

代码

class Solution {
    public TreeNode invertTree(TreeNode root) {
        diGui(root);

        return root;
    }

    // 遍历交换每个结点的左右子树  (先序)

    public void diGui(TreeNode root) {

        if(root!=null) {

            // 交换左右子树
            TreeNode T = root.left;
            root.left = root.right;
            root.right = T;


            // 遍历左子树
            diGui(root.left);
            // 遍历右子树
            diGui(root.right);
        }
    }
}

112. 路径总和

问题
在这里插入图片描述

思想

用广度优先遍历, 遍历到每个结点时记录到每个结点的路径长度
通过队列广度优先时,一定是父节点遍历出子节点,所以可以统计到
父节点到字节的的路径

代码

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        /**
            思路1:  用广度优先遍历, 遍历到每个结点时记录到每个结点的路径长度
                     通过队列广度优先时,一定是父节点遍历出子节点,所以可以统计到
                    父节点到字节的的路径
         */

         // 先判断是不是空树
         if(root==null) {
             return false;
         }

         // 创建两个辅助队列, 一个记录结点,一个记录元素
         Queue<TreeNode> queueNode = new LinkedList<>();
         Queue<Integer> queueInt = new LinkedList<>();

         

         // 根结点入队
         queueNode.offer(root);
         queueInt.offer(root.val);
        
         while(!queueNode.isEmpty()) {
             TreeNode now = queueNode.poll();
             int temp = queueInt.poll();

             // 假如该出队结点为叶子,判断到该出队的结点的路径和是不是等于目标值
             if(now.left==null && now.right==null) {
                 if(temp == targetSum) {
                     // 到叶子结点等于模板值
                     return true;
                 }
                 // 否则就跳过此循环
                 continue;
             }

             // 假如不是叶子结点,则将存在的孩子结点入队

             if(now.left!=null) { // 左孩子不空
                 queueNode.offer(now.left);
                 queueInt.offer(now.left.val + temp);  //父节点值加左孩子结点值 
             }

             if(now.right != null) {  // 右孩子不空
                queueNode.offer(now.right);
                queueInt.offer(now.right.val + temp); // 父节点加左节点的值
             }
         }

         return false;


    }
}

700. 二叉搜索树中的搜索

问题

在这里插入图片描述
思想

// 通过先序遍历,遍历二叉树,然后访问结点的时候看是否等于val,等于则返回该结点,
// 不等于则看结点值大于还是小于根结点,大于根结点则在右子树,小于则在左子树

代码

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
            // 通过先序遍历,遍历二叉树,然后访问结点的时候看是否等于val,等于则返回该结点,
            //  不等于则看结点值大于还是小于根结点,大于根结点则在右子树,小于则在左子树

            if(root == null) {
                return null;
            }else if(root.val == val){ // 找到结点
                return root;
            }else if(root.val < val){
                // 根小,向右寻找
                return searchBST(root.right, val);
            }else {
                // 根大,向左寻找
                return searchBST(root.left, val);
            }

    }


}

701. 二叉搜索树中的插入操作

问题
在这里插入图片描述
思想

通过二叉搜索树的思想,找到叶子结点判断插入到叶子结点的左孩子或者右孩子上

代码

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        // 通过二叉搜索数的方式
        
        if(root == null) {
            return new TreeNode(val);
        }else {
            // 如果没有到null,就按二叉搜索树去寻找
            if(root.val > val) {
                // 根大,在左侧
                root.left = insertIntoBST(root.left, val);
            }else {
                // 传入规则没有相等值,则为根小在右侧
                root.right = insertIntoBST(root.right, val);
            }
        }
        return root;
    }
}

98. 验证二叉搜索树

问题
在这里插入图片描述

思路

/**
方案1: 采用递归判断的方式
1. 首先我们知道bsf每个结点的左子树全小于它,右子树全大于它,每个结点都是如此
2. 我们用一个low,high记录到当前结点的值,当前结点作为父节点时
其左子树皆小于它,所以该结点的值作为向左遍历的上限值
其右子树皆大于它,所以该结点的值作为向右遍历的下限值
3. 所以当遍历到一个结点时会判断该结点的值
是不是小于最小值(因为最小值向右遍历确定的,右子树皆大于父节点,所以有小于最小值的不为二叉搜索树)
或是否大于最大值(同理更新最大值是向左遍历时确定的,所以当前结点大于最大值时,不为bsf)
*/

·代码

class Solution {
    public boolean isValidBST(TreeNode root) {
        

        return bsf(root, Long.MIN_VALUE, Long.MAX_VALUE);

    }

    public boolean bsf(TreeNode root, long low, long high) {
        if(root==null) {
            return true;
        }else if(root.val >=high || root.val<=low) {   // 在子树中有超过父节点的值或者低于
            return false;
        }else {   // 向左遍历时更新上限,向右遍历时更新下限
            return bsf(root.left, low, root.val) && bsf(root.right,root.val, high);
        }
    }
}

653. 两数之和 IV - 输入二叉搜索树

问题

在这里插入图片描述
思路·

/**
通过集合的判断有没有符合 k-当前结点值 得值在集合中
每次都将遍历到的结点值加入到集合当中,当bsf中存在两个结点的值等于目标值的时候
当遍历到第二个加和的值的时候在集合中一定有 k-当前结点的值
*/

代码

class Solution {
    
    Set<Integer> set = new HashSet<Integer>();


    public boolean findTarget(TreeNode root, int k) {
        /**
            通过集合的判断有没有符合  k-当前结点值 得值在集合中
            每次都将遍历到的结点值加入到集合当中,当bsf中存在两个结点的值等于目标值的时候
            当遍历到第二个加和的值的时候在集合中一定有   k-当前结点的值
         */

        if(root == null) {
            return false;
        }

        if(set.contains(k - root.val)) {
            // 集合中存在  一个值为  目标值减去当前结点值  ==  有两个值加起来等于目标结点值
            return true; 
        }

        set.add(root.val);

        return findTarget(root.left, k) || findTarget(root.right, k);


    }
}

235. 二叉搜索树的最近公共祖先

问题

在这里插入图片描述

代码

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) {
            return root;
        }   

        // 开始递归
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        // 如果left为空,说明两个结点都在右子树
        if(left == null) {
            return right;
        }

        // 如果right 为空 说明两个结点都在左子树
        if(right == null) {
            return left;
        }

        // 如果left和right都不为空,说明这两个结点一个在左子树一个在右子树
        return root;
 
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode 437. 路径总和 III

LeetCode 437. 路径总和 III 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff…

JUC面试(十一)——LockSupport

可重入锁 可重入锁又名递归锁 是指在同一个线程在外层方法获取锁的时候&#xff0c;再进入该线程的内层方法会自动获取锁(前提&#xff0c;锁对象得是同一个对象)&#xff0c;不会因为之前已经获取过的锁还没释放而阻塞。 Java中ReentrantLock和synchronized都是可重入锁&am…

第一章 概述

第一章 概述 1.1 计算机网络在信息时代中的作用 21世纪的一些重要特征 数字化&#xff0c;网络化和信息化 以网络为核心的信息时代 互联网的两个重要基本特点 连通性共享&#xff08;资源共享&#xff09; 1.2 互联网概述 计算机网络由若干个结点货连接这些结点的链路组成…

【唐诗学习】四、边塞诗派代表

四、边塞诗派代表 边塞诗派起源 盛唐是中国历史上一个空前的盛世&#xff0c;国库丰盈&#xff0c;社会十分安定&#xff0c;百姓的幸福指数高。 盛唐是中国历史上一个空前的盛世&#xff0c;国库丰盈&#xff0c;社会十分安定&#xff0c;百姓的幸福指数高。唐太宗以后的几个…

Citadel——Dusk网络的Zero-Knowledge KYC解决方案

1. 引言 近期&#xff0c;Dusk网络宣布其已支持名为Citadel的Zero-Knowledge KYC解决方案&#xff0c;使得用户和机构可控制其权限以及个人信息分享。该架构可用于all claim-based KYC requests&#xff0c;并让用户完全控制他们共享的信息以及与谁共享信息&#xff0c;同时完…

详解Java中的BIO、NIO、AIO

1、 详解Java中的BIO、NIO、AIO 1.1、引言 IO流是Java中比较难理解的一个知识点&#xff0c;但是IO流在实际的开发场景中经常会使用到&#xff0c;比如Dubbo底层就是NIO进行通讯。本文将介绍Java发展过程中出现的三种IO&#xff1a;BIO、NIO以及AIO&#xff0c;重点介绍NIO。…

【c语言进阶】常见的静态通讯录

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…

2.H3CNE-网络参考模型

OSI参考模型产生背景各大IT设备厂商只支持自己的私有协议&#xff0c;跨厂商设备兼容性差用户购买和维护成本高不利于网络技术发展概念定义了网络中设备所遵守的层次结构优点开放的标准化接口&#xff0c;协议不再封闭多厂商设备兼容易于理解、学习和更新协议标准实现模块化工程…

【Leetcode刷题】141、环形链表

原题链接&#xff1a;https://leetcode.cn/problems/linked-list-cycle/?favorite2cktkvj给你一个链表的头节点 head &#xff0c;判断链表中是否有环。如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的…

Python数据可视化(二)使用统计函数绘制简单图形

该文会讲解一些大家比较熟悉却又经常混淆的统计图形&#xff0c;掌握这些统计图形可以对数据可视化有一个深入理解&#xff0c;并正确使用。2.1 函数 bar()——用于绘制柱状图函数功能&#xff1a;在 x 轴上绘制定性数据的分布特征。调用签名&#xff1a;plt.bar(x,y)。参数说明…

day21|216.组合总和III、17.电话号码的字母组合

216.组合总和III 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3, n 7 输出: …

说说配置中心

什么是配置中心在微服务的环境下,将项目需要的配置信息保存在配置中心,需要读取时直接从配置中心读取,方便配置管理的微服务工具可以将部分yml文件的内容保存在配置中心一个微服务项目有很多子模块,这些子模块可能在不同的服务器上,如果有一些统一的修改,需要逐一修改这些子模块…

python数据可视化开发:Matplotlib库基础知识

文章目录前言01.工具栏组件02.图表数据03.设置字体字典&#xff08;1&#xff09;全局字体样式&#xff08;2&#xff09;常用中文字体对应名称&#xff08;3&#xff09;查询当前系统所有字体04.图像配置实例05.图表标题06.文本组件07.坐标轴标签组件08.网格组件09.绘制折线10…

【头歌】双向链表的基本操作

双向链表的基本操作第1关&#xff1a;双向链表的插入操作任务描述本关任务&#xff1a;编写双向链表的插入操作函数。相关知识双链表中用两个指针表示结点间的逻辑关系&#xff1a;指向其前驱结点的指针域prior&#xff0c;指向其后继结点的指针域next。双向链表的结点结构如图…

MySQL数据库面试题[万字汇总]

1) MySQL数据库相关错题本1、存储引擎相关1、MySql的存储引擎的不同MySQL存储引擎主要有InnoDB, MyISAM, Memory, 这三个区别在于:Memory是内存数据引擎, 会断电重启(在双M或者主从架构下会产生较多异常), 且不支持行级锁. 默认索引是数组索引, 支持B索引InnoDB和MyISAM的区别:…

流批一体计算引擎-5-[Flink]的Python Table API和SQL程序

参考Flink从入门到入土&#xff08;详细教程&#xff09; 参考flink的默认窗口触发机制 参考彻底搞清Flink中的Window 参考官方Python API文档 1 IDEA中运行Flink 从Flink 1.11版本开始, PyFlink 作业支持在 Windows 系统上运行&#xff0c;因此您也可以在 Windows 上开发和…

【数据结构】极致详解:树与二叉树(上)——结构与概念

目录 &#x1f6eb;前言&#x1f6eb;&#xff1a; &#x1f680;一、树&#x1f680;&#xff1a; 1.树的概念&#xff1a; 2.树的相关概念&#xff1a; 3.树的表示&#xff1a; 4.树的实际使用场景&#xff1a; &#x1f6f0;️二、二叉树&#x1f6f0;️&#xff1a;…

acwing-Diango项目 (后半)

acwing-Django项目 文章目录acwing-Django项目前言5. 创建账号系统5.1用户名密码登录写登录界面写注册界面写动作 实现三个函数 register login logout5.2 Web端acapp一键登录在django中集成redis(准备工作)首先 pip install django_redis配置一下缓存启动redis-serverredis在d…

特征工程——文本特征

文本特征 expansion编码 consolidation编码 文本长度特征 标点符号特征 词汇属性特征 特殊词汇特征 词频特征 TF-IDF特征 LDA特征 下面的文章主要是梯度提升树模型展开的&#xff0c;抽取的特征主要为帮助梯度提升树模型挖掘其挖掘不到的信息&#xff0c;本文介绍的所…

NodeJS Web 框架 Express 之中间件

NodeJS Web 框架 Express 之中间件参考描述中间件next()一个简单的中间件函数使用全局中间件局部中间件共享注意事项位置next()分类错误级中间件内置中间件express.urlencoded()express.json()第三方中间件参考 项目描述哔哩哔哩黑马程序员搜索引擎Bing 描述 项目描述Edge109…