Java【数据结构入门OJ题33道】——力扣刷题记录1

文章目录

  • 第一天
    • 存在重复元素
    • 最大子数组和
  • 第二天
    • 两数之和
    • 合并两个有序数组
  • 第三天
    • 两个数组的交集
    • 买卖股票最佳时机
  • 第四天
    • 重塑矩阵
    • 杨辉三角
  • 第五天
    • 有效的数独
    • 矩阵置零
  • 第六天
    • 字符串中第一个唯一字符
    • 救赎金
  • 第七天
    • 判断链表是否有环
    • 合并两个有序链表
    • 移除链表元素
  • 第八天
    • 反转链表
    • 删除重复元素
  • 第九天
    • 有效的括号匹配
    • 用栈实现队列
  • 第十天
    • 二叉树前序遍历(非递归)
    • 二叉树中序遍历(非递归)
    • 二叉树后序遍历(非递归)
  • 第十一天
    • 二叉树中序遍历
    • 二叉树最大深度
    • 对称二叉树
  • 第十二天
    • 反转二叉树
    • 路经总和
  • 第十三天
    • 二叉搜索树中的搜索
    • 二叉搜索树中的插入
  • 第十四天
    • 判断是否为二叉搜索树
    • 两数之和 IV
    • 最近公共祖先

各位读者好, 我是小陈, 这是我的个人主页
小陈还在持续努力学习编程, 努力通过博客输出所学知识
如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽
希望我的专栏能够帮助到你:
JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

记录力扣数据结构入门OJ题


提示:本人是正在努力进步的小菜鸟,不喜勿喷~,如有大佬发现某题有更妙的解法和思路欢迎提出讨论~

第一天

存在重复元素

OJ链接

利用 HashSet 可以去重的功能, new 一个hashSet , 遍历数组, 如果 hashSet 中不存在该数据, add; 如果存在, 返回 true;

class Solution {
    public boolean containsDuplicate(int[] nums) {
        Set<Integer> hashSet = new HashSet<>();
        for(int x : nums) {
            if(!hashSet.contains(x)) {
                hashSet.add(x);
            }else {
                return true;
            }
        }
        return false;
    }
}

时间复杂度: O(N) 需遍历数组长度
空间复杂度: O(N) 需创建数组长度为N的哈希表


最大子数组和

动态规划问题

class Solution {
    public int maxSubArray(int[] nums) {
        int ret = nums[0];// 取列表中最大值
        for(int i = 1; i < nums.length; i++) {
        // 前一个数据 > 0 时, 把前一个数据加到当前数据上, 返回列表中最大值
            if(nums[i - 1] > 0) {
                nums[i] += nums[i - 1];
            }
            ret = Math.max(ret, nums[i]);
        }
        return ret;
    }
}

时间复杂度: O(N) // 遍历数组
空间复杂度: O(1)



第二天

两数之和

OJ链接

哈希表

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] ret = new int[2];
        Map<Integer, Integer> hashMap = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
                hashMap.put(i,nums[i]); // (下标, 值)
        }
        Set<Map.Entry<Integer,Integer>> set = hashMap.entrySet();
        // 转化成Entry
        for(Map.Entry<Integer,Integer> entry1 : set) {
            for(Map.Entry<Integer,Integer> entry2 : set) {
               // 遍历set, 保证两个entry的key值不同时,且value值相加得target
                if(entry1.getValue() + entry2.getValue() == target && 
                                (entry1.getKey() != entry2.getKey())) {         
                    ret[0] = entry1.getKey();
                    ret[1] = entry2.getKey();
                    return ret;
                }
            }
        }
        return ret;
    }
}

这个方法时间效率太低了, 时间复杂度是O(N^2) 空间复杂度是O(N), 优化后如下

class Solution {
    public static int[] twoSum(int[] nums, int target) {
        int[] ret = new int[2];
        Map<Integer, Integer> treeMap = new TreeMap<>();
        for(int i = 0; i < nums.length; i++){
        	// target和当前数据的差
            int difference = target - nums[i];
            // 放入当前数据前查看map中是否存在插值, 存在就返回
            if (treeMap.containsKey(difference)) {
                // 找到了另一个数
                ret[0] = treeMap.get(difference);
                ret[1] = i;
                return ret;
            }
            // 把数据和下标放入map中
            treeMap.put(nums[i],i); // (值, 下标)
        }
        return ret;
    }
}

时间复杂度: O(N)
空间复杂度: O(N)


合并两个有序数组

OJ链接

逆向双指针
从后往前放入nums1中(nums1后半部分是空的, 所以从后放不会覆盖前面的数据)
谁大谁放
结束条件为 j 把nums2遍历完
由于 i j 遍历的过程中一直在比较, 所以能够保证nums1中越靠后的数据越大

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m-1;// nums1的指针
        int j = n-1;// nums2的指针
        int k = m+n-1;
        while(j >= 0){
            // 从后往前放
            if(i >= 0 && nums1[i] > nums2[j] ) {
                nums1[k] = nums1[i];
                i--;
                k--;
            }else{
                nums1[k] = nums2[j];
                j--;
                k--;
            }
          
        }
    }
}

时间复杂度:O(M + N) // 双指针遍历数组
空间复杂度:O(1)


第三天

两个数组的交集

OJ链接

哈希表

public class Intersect {
    public int[] intersect(int[] nums1, int[] nums2) {
        int[] ret = null;
        Map<Integer, Integer> hashMap = new HashMap<>();
        ArrayList<Integer> list = new ArrayList<>();
        for(int i = 0; i < nums1.length ; i++) {
            if(hashMap.containsKey(nums1[i]) ) {
                // 如果已经存在这个值, 让它的value值+1
                hashMap.put(nums1[i], hashMap.get(nums1[i]) + 1);
            }else {
                // 如果不存在, 放入这个值
                hashMap.put(nums1[i], 1);
            }
        }
        for(int i = 0; i < nums2.length; i++) {
            if(hashMap.containsKey(nums2[i]) && hashMap.get(nums2[i]) > 0) {
                // 如果nums1中存在当前值(value值 > 0视为存在)
                hashMap.put(nums2[i], hashMap.remove(nums2[i]) - 1);
                list.add(nums2[i]);
            }
        }
        // 转化成数组
        ret = new int[list.size()];
        for(int i = 0; i < ret.length; i++) {
            ret[i] = list.get(i);
        }
        return ret;
    }
}

买卖股票最佳时机

OJ链接

动态规划

public class MaxProfit {
    public int maxProfit(int[] prices) {
        int minPrices = Integer.MAX_VALUE;
        int difference = 0;
        for(int i = 0; i < prices.length; i++) {
            if(prices[i] < minPrices) {
                minPrices = prices[i];
            }else {
                // 找到差值最大的情况
                difference = Math.max(prices[i] - minPrices, difference);
            }
        }
        return difference;
    }
}


第四天

重塑矩阵

OJ链接

核心思想: 遍历, 把数据的一维数组下标, 映射到二维数组中

题解如图:
Java【数据结构入门OJ题33道】——力扣刷题记录1_第1张图片

class Solution {
    public int[][] matrixReshape(int[][] mat, int r, int c) {
        int[][] ret = new int[r][c];
        int row = mat.length; // 行数
        int column = mat[0].length; // 列数
        if(mat == null || row * column != r * c) {
            return mat;
        }
        // 二维数组用一维表示
        for(int i = 0; i < row * column; i++) {
            ret[i/c][i%c] = mat[i/column][i%column];
        }
        return ret;
    }
}

时间复杂度 : O(M*N)
空间复杂度 : O(1)


杨辉三角

OJ链接

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> ret = new ArrayList<>();
        List<Integer> firstColumn = new ArrayList<>();
        // 第一行
        firstColumn.add(1);
        ret.add(firstColumn);
        int i = 1;
        while(i < numRows) {
            // 第一个1
            List<Integer> prev = ret.get(i - 1);
            List<Integer> column = new ArrayList<>();
            column.add(1);
            // 中间数
            int j = 1;
            while(j < i) {
                column.add(prev.get(j -1) + prev.get(j));
                j++;
            }
            // 最后一个1
            column.add(1);
            ret.add(column);
            i++;
        }
        return ret;
    }
}


第五天

有效的数独

OJ链接

	public boolean isValidSudoku(char[][] board) {
        int[][] row = new int[9][9];
        int[][] column = new int[9][9];
        int[][] Box = new int[9][9];
        for(int i = 0; i < 9; i++) {
            for(int j = 0; j < 9; j++) {
                if(board[i][j] != '.') {
                    int val = board[i][j] - '0';
                    if(row[i][val - 1] != 0 || column[val - 1][j] != 0) {
                        // 说明这一行或这一列已经有过此数据,返回false
                        return false;
                    }
                    if(Box[(i/3) * 3 + j/3][val - 1] != 0) {
                        // 说明九宫格内已经有过此数据, 返回false
                        return false;
                    }
                    row[i][val - 1] = val;
                    column[val - 1][j] = val;
                    Box[(i/3) * 3 + j/3][val - 1] = val;
                }
            }
        }
        return true;
    }

矩阵置零

OJ链接

    public void setZeroes(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(matrix[i][j] == 0) {
                    queue.offer(i * n + j);
                }
            }
        }
        while(!queue.isEmpty()) {
            int num = queue.poll();
            for(int i = 0; i < n; i++) {
                matrix[num / n][i] = 0;
            }
            for(int j = 0; j < m; j++) {
                matrix[j][num % n] = 0;
            }
        }
    }


第六天

字符串中第一个唯一字符

OJ链接

	public int firstUniqChar1(String s) {
        int len = s.length();
        Map<Character, Integer> hashMap = new HashMap<>();
        for(int i = 0; i < len; i++) {
            char ch = s.charAt(i);
            if(hashMap.containsKey(ch)) {
                int num =  hashMap.remove(ch);
                hashMap.put(ch, num + 1);
            }else {
                hashMap.put(ch,1);
            }
        }
        for(int j = 0; j < len; j++) {
            if(hashMap.get(s.charAt(j)) == 1) {
                return j;
            }
        }
        return -1;
    }
	public int firstUniqChar2(String s) {
        int len = s.length();
        for(int i = 0; i < len; i++) {
            if(s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i))) {
                return i;
            }
        }
        return -1;
    }

救赎金

OJ链接

	public boolean canConstruct(String ransomNote, String magazine) {
        Map<Character, Integer> hashMap = new HashMap<>();
        for(int i = 0; i < ransomNote.length(); i++) {
            char ch = ransomNote.charAt(i);
            hashMap.put(ch, hashMap.getOrDefault(ch, 0) + 1);
        }

        for(int j = 0; j < magazine.length(); j++) {
            char ch = magazine.charAt(j);
            if( hashMap.containsKey(ch) &&  hashMap.get(ch) > 0) {
                hashMap.put(ch, hashMap.get(ch) - 1);
                if(hashMap.get(ch) ==0 ) {
                    hashMap.remove(ch);
                }
                if(hashMap.size() == 0) {
                    return true;
                }
            }
        }
        return false;
    }

代码优化1:

        for (int i = 0; i < t.length(); i++) {
            char ch = t.charAt(i);
            table.put(ch, table.getOrDefault(ch, 0) - 1);
            if (table.get(ch) < 0) {
                return false;
            }
        }

代码优化2:

    public boolean isAnagram3(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        Arrays.sort(str1);
        Arrays.sort(str2);
        return Arrays.equals(str1, str2);
    }


第七天

判断链表是否有环

哈希或者双指针

OJ链接

    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }

合并两个有序链表

OJ链接

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode pHead = new ListNode(-1);
        ListNode prev = pHead;
        while(list1 != null && list2 != null) {
            if(list1.val <= list2.val) {
                prev.next = list1;
                list1 = list1.next;
                prev = prev.next;
            }else {
                prev.next = list2;
                list2 = list2.next;
                prev = prev.next;
            }
        }
        prev.next = list1 == null ? list2 : list1;
        return pHead.next;
    }

移除链表元素

OJ链接

    public ListNode removeElements(ListNode head, int val) {
        if(head == null) {
            return null;
        }
        ListNode newHead = head;
        ListNode cur = head;
        ListNode curPrev = null;
        while(cur != null) {
            if(cur.val == val) {
                if(cur == newHead) {
                    newHead = newHead.next;
                }else {
                    curPrev.next = cur.next;
                    cur = curPrev.next;
                    continue;
                }
            }
            curPrev = cur;
            cur = cur.next;
        }
        return newHead;
    }


第八天

反转链表

遍历一次链表, 依次头插即可

    public ListNode reverseList(ListNode head) {
        if(head == null) {
            return null;
        }
        ListNode cur = head;
        ListNode newHead = cur;
        while(cur.next != null)  {
            ListNode curNext = cur.next;
            cur.next = curNext.next;
            curNext.next = newHead;
            newHead = curNext;
        }
        return newHead;
    }

删除重复元素

注意: 重复数据连续出现的次数可能不止有两次, 所以删除重复数据操作需要while循环

    public ListNode deleteDuplicates(ListNode head) {
        if(head == null) {
            return null;
        }
        ListNode cur = head;
        while(cur != null && cur.next != null ) {
            ListNode curNext = cur.next;
            while (curNext != null && cur.val == curNext.val){
                cur.next = curNext.next;
                curNext = cur.next;
            }
            cur = cur.next;
        }
        return head;
    }


第九天

有效的括号匹配

OJ链接

 // 有效的括号匹配
    public boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        // 遍历字符串
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            // 如果是左括号就压栈
            if (ch == '(' || ch == '[' || ch == '{') {
                stack.push(ch);
            }
            //如果是右括号
            if (ch == ')' || ch == ']' || ch == '}') {
                // 如果栈为空,说明没有对应的左括号,直接返回false
                if (stack.isEmpty()) {
                    return false;
                }
                // 栈不为空的情况
                char ch2 = stack.peek();
                if ((ch == ')' && ch2 == '(') || (ch == ']' && ch2 == '[')
                        || (ch == '}' && ch2 == '{')) {
                    stack.pop();
                }else {
                    // 不匹配,返回false
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

用栈实现队列

OJ链接

    // 用栈实现队列
    Deque<Integer> stack1 ;
    Deque<Integer> stack2;
    public MyQueue() {
        stack1 = new ArrayDeque<>();
        stack2 = new ArrayDeque<>();
    }

    public void push(int x) {
        stack1.push(x);
    }

    public int pop() {
        // 必须保证第二个栈为空时才能入栈
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

    public int peek() {
        // 必须保证第二个栈为空时才能入栈
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }

    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }


第十天

二叉树前序遍历(非递归)

OJ链接

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root == null) {
            return ret;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                ret.add(cur.val);
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
        return ret;
    }

二叉树中序遍历(非递归)

OJ链接

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root == null) {
            return ret;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            ret.add(cur.val);
            cur = cur.right;
        }
        return ret;
    }

二叉树后序遍历(非递归)

OJ链接

	public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root == null) {
            return ret;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode cur = root;
        TreeNode tmp = null;
        while (cur != null || !stack.isEmpty()) {
            while(cur!=null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null || top.right == tmp) {
                ret.add(top.val);
                tmp = stack.pop();
            }else {
                cur = top.right;
            }
        }
        return ret;
    }


第十一天

二叉树中序遍历

OJ链接

    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            List<Integer> inList = new ArrayList<>();
            int size = queue.size();
            while (size > 0) {
                TreeNode node = queue.poll();
                inList.add(node.val);
                size--;
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            list.add(inList);
        }
        return list;
    }

二叉树最大深度

OJ链接

    public int maxDepth(TreeNode root) {
        if(root == null) {
            return 0;
        }
        if(root.left == null && root.right == null) {
            return 1;
        }
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
    }

对称二叉树

OJ链接

    public boolean isSymmetric(TreeNode root) {
        if (root == null ) {
            return true;
        }
        return isSymmetricChild(root.left, root.right);
    }
    public boolean isSymmetricChild(TreeNode leftRoot, TreeNode rightRoot) {
        if(leftRoot == null && rightRoot != null ||
               leftRoot!=null && rightRoot == null) {
            return false;
        }
        if(leftRoot == null && rightRoot == null) {
            return true;
        }
        if(leftRoot.val != rightRoot.val) {
            return false;
        }
        return isSymmetricChild(leftRoot.left, rightRoot.right)&&
                isSymmetricChild(leftRoot.right, rightRoot.left);
    }


第十二天

反转二叉树

OJ链接

    public TreeNode invertTree(TreeNode root) {
        if(root == null ) {
            return null;
        }
        TreeNode tmp = root.right;
        root.right = root.left;
        root.left = tmp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

路经总和

OJ链接

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null) {
            return false;
        }
        targetSum -= root.val;
        // 走到头的情况, 判断sum是否等于0
        if(root.left == null && root.right == null) {
            if(targetSum == 0) {
                return true;
            }else {
                return false;
            }
        }
        boolean hasLeft = hasPathSum(root.left, targetSum);
        boolean hasRight = hasPathSum(root.right, targetSum);
        return hasLeft || hasRight;
    }


第十三天

二叉搜索树中的搜索

OJ链接

    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null) {
            return null;
        }
        TreeNode cur = root;
        while(cur != null) {
            if(val > cur .val) {
                cur = cur.right;
            }else if(val == cur.val) {
                return cur;
            }else {
                cur = cur.left;
            }
        }
        return null;
    }

二叉搜索树中的插入

OJ链接

   public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root == null) {
            return new TreeNode(val);
        }
        TreeNode cur = root;
        TreeNode curParent = null;
        while(cur != null) {
            curParent = cur;
            if(val > cur.val) {
                cur = curParent.right;
            }else {
                cur = curParent.left;
            }
        }
        if(val > curParent.val) {
            curParent.right = new TreeNode(val);
        }
        if(val < curParent.val){
            curParent.left = new TreeNode(val);
        }
        return root;
    }


第十四天

判断是否为二叉搜索树

OJ链接

中序遍历每一个结点, 放入list中, 然后再遍历list检查是否有序

    public boolean isValidBST(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        preOrder(root, list);
        return check(list);
    }
    public void preOrder(TreeNode root, List<Integer> list) {
        if(root == null) {
            return;
        }
        preOrder(root.left, list);
        list.add(root.val);
        preOrder(root.right, list);
    }
    public boolean check(List<Integer> list) {
        for(int i = 1; i < list.size(); i++) {
            if(list.get(i-1) >= list.get(i)) {
                return false;
            }
        }
        return true;
    }

方法2, 利用LinkedList(底层是双向链表), 递归进行中序遍历, 每遍历到一个结点之前判断链表尾结点的val是否比当前结点的val值小

    public boolean isValidBST(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();
        return inOrder(root, list);
    }
    public boolean inOrder(TreeNode root, LinkedList<Integer> list) {
        if(root == null) {
            return true;
        }
        boolean bLeft = inOrder(root.left, list);
        // 两种情况需要尾插
        // 1,第一次插入  2,到目前为止中序遍历序列有序
        // 否则直接返回false
        if(list.isEmpty() || (bLeft && list.getLast() < root.val)) {
            list.add(root.val);
        }else {
            return false;
        }
        boolean bRight = inOrder(root.right, list);
        return bLeft && bRight;
    }

两数之和 IV

OJ链接

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        Set<Integer> hashSet = new HashSet<>();
        return inOrder(root, k, hashSet);
    }
    public boolean inOrder(TreeNode root, int k, Set<Integer> hashSet) {
        if(root == null) {
            return false;
        }
        boolean bLeft = inOrder(root.left, k, hashSet);
        int ret = k - root.val;
        if(hashSet.contains(ret) ) {
            return true;
        }
        hashSet.add(root.val);
        boolean bRight = inOrder(root.right, k, hashSet);
        return bLeft || bRight;
    }

最近公共祖先

OJ链接

左边找到返回该结点, 去右边找

三种情况 :
1, 左右只有一边不为空, 哪边不为空返回哪边
2, 左右都不为空, 返回当前结点
3, 左右都为空, 返回null

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) {
            return null;
        }
        if(root.val == p.val || root.val == q.val) {
            return root;
        }
        TreeNode leftRet = lowestCommonAncestor(root.left, p,q);
        TreeNode rightRet = lowestCommonAncestor(root.right,p,q);

        if(leftRet != null && rightRet == null) {
            return leftRet;
        }else if(rightRet != null && leftRet == null) {
            return rightRet;
        }else if(leftRet != null && rightRet != null) {
            return root;
        }
        return null;
    }

你可能感兴趣的:(OJ题,leetcode,数据结构,java)