[leetcode]01

本文概述了多个编程问题,涉及链表反转、区间反转、合并排序链表、查找两数之和、移动链表中的0、相交链表分析、树的遍历、二叉树操作如深度、翻转和对称性判断,以及字符串处理和数组问题如字母异味词分组和最长连续序列。所有问题都集中在数据结构和算法上,难度从简单到困难不等。
摘要由CSDN通过智能技术生成

反转链表(简单)

描述:
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

  • 示例
    输入:{1,2,3},返回{3,2,1}
    输入:{1,2},返回{2,1}
    输入:{1},返回{1}
    输入:{},返回{}

  • 思路
    利用pre节点和next节点反转链表,最后returnpre节点
    在这里插入图片描述

import java.util.*;

public class Solution {
    public ListNode ReverseList (ListNode head) {
        ListNode pre = null;
        ListNode next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
}

链表内指定区间反转(中等)

描述:
给定一个单链表的头指针和一个指定区间[m,n],其中m、n均为整数且0<=m<=n<=链表长度。反转从位置m到位置n的链表,请返回反转后的链表的头指针。

  • 示例
    输入:{1,2,3,4,5},2,4,返回{1,4,3,2,5}

  • 思路

    1. 先找到第m个节点,并记录下来
    2. 然后从第m个节点开始,依次反转
    3. 最后返回反转后的链表的头指针
      在这里插入图片描述
import java.util.*;

public class Solution {
    public ListNode ReverseBetween (ListNode head, int m, int n) {
        if (head == null || head.next == null || m == n) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode preStart = dummy;
        ListNode start = head;
        for (int i = 1; i < m; i ++ ) {
            preStart = start;
            start = start.next;
        }
        for (int i = 0; i < n - m; i ++ ) {
            ListNode temp = start.next;
            start.next = temp.next;
            temp.next = preStart.next;
            preStart.next = temp;
        }
        return dummy.next;
    }
}

合并两个排序的链表(简单)

描述:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

  • 示例
    输入:{1,3,5},{2,4,6},返回{1,2,3,4,5,6}

  • 思路

    1. 比较两个链表的节点值,将较小的节点加入新链表
    2. 循环比较,直到其中一个链表为空
    3. 将另一个链表剩余的节点加入新链表
    4. 返回新链表
      在这里插入图片描述
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        } else if (l2 == null) {
            return l1;
        } else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

两数之和(简单)

描述:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

  • 示例
    输入:nums = [2, 7, 11, 15], target = 9
    输出:[0, 1]

  • 思路
    利用HashMap来进行便利,速度更快

class Solution {
public int[] twoSum(int[] nums, int target) {
    HashMap<Integer,Integer> map = new HashMap<>();
    for(int i=0;i<nums.length;i++)
    {
        if(map.containsKey(target-nums[i]))
        {
            return new int[] {map.get(target-nums[i]),i};
        }
        map.put(nums[i],i);
    }
    return null;
}
}

移动0(简单)

  • 思路: 一定要反过来想,不要只盯着0,可以设置一个指针,就是专业收集不是零的数 收集一遍后,后面的一定是0,就再将空出来的位置设置为0,就解决问题了
class Solution {
    public void moveZeroes(int[] nums) {
        int s=0;//定义收集不是0的数的指针
        //开始收集不是零的数
        for (int i = 0; i < nums.length ;i++) {
            if(nums[i]!=0){
                nums[s++] = nums[i];
            }
        }
        //收集完毕后,后面自然就都是0了
        for (int i = s; i < nums.length; i++) {
            nums[i]=0;
        }
    }
}

相交链表(简单)

  • 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

  • 思路:设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a a a ,「链表 headB」的节点数量为 b b b ,「两链表的公共尾部」的节点数量为 c c c

  • 考虑构建两个节点指针A,B分别指向两链表头节点 headA , headB ,做如下操作:

  • 指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:
    a + b − c a+b-c a+bc

  • 指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:
    a + b − c a+b-c a+bc

  • 若两链表 有 公共尾部 (即 c > 0 c>0 c>0) :指针 A , B 同时指向「第一个公共节点」node
    若两链表 无 公共尾部 (即 c = 0 c=0 c=0) :指针 A , B 同时指向null
    在这里插入图片描述

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA, B = headB;
        while (A != B) {
            A = A != null ? A.next : headB;
            B = B != null ? B.next : headA;
        }
        return A;
    }
}

树的中序遍历(简单)

  • 思路:树的中序遍历指的是对树的节点进行左-根-右的顺序遍历,因此可以考虑使用递归函数的方法进行编写。
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        inorder(root,res);
        return res;
    }
    public void inorder(TreeNode root,List<Integer> res){
            if(root == null)
            {
                return;
            }
            inorder(root.left,res);
            res.add(root.val);
            inorder(root.right,res);
    }
}

二叉树的最大深度(简单)

  • 判断二叉树的最大深度
  • 思路:二叉树的最大深度指的是二叉树的最大层数,因此可以考虑使用递归函数的方法进行编写。
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null)
        {
            return 0;
        }
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        return Math.max(left,right)+1;
    }
}

翻转二叉树(简单)

  • 将给定的二叉树进行翻转
  • 思路:我们在做二叉树题目时候,第一想到的应该是用 递归 来解决。
    仔细看下题目的 输入 和 输出,输出的左右子树的位置跟输入正好是相反的,于是我们可以递归的交换左右子树来完成这道题。
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null)
        {
            return null;
        }
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);
        root.left = right;
        root.right = left;
        return root;
    }
}

对称二叉树(简单)

  • 判断给定的二叉树是否为对称的
  • 思路:我们可以考虑使用递归的方法进行编写。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null)
        {
            return true;
        }
        return isSymmetric(root.left,root.right);
    }
    public boolean isSymmetric(TreeNode left,TreeNode right)
    {
        if(left == null && right == null)
        {
            return true;
        }
        if(left == null || right == null)
        {
            return false;
        }
        if(left.val != right.val)
        {
            return false;
        }
        return isSymmetric(left.left,right.right) && isSymmetric(left.right,right.left);
    }
}

二叉树的直径(简单)

  • 计算给定二叉树的最大直径
  • 思路:二叉树的直径指的是任意两个节点路径长度中的最大值。
class Solution {
    private int ans;
    public int diameterOfBinaryTree(TreeNode root) {
        ans = 1;
        depth(root);
        return ans - 1;
    }
    public int depth(TreeNode root)
    {
        if(root == null)
        {
            return 0;
        }
        int left = depth(root.left);
        int right = depth(root.right);
        ans = Math.max(ans,left + right + 1);
        return Math.max(left,right) + 1;
    }
}

字母异味词分组(中等)

  • 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

  • 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

  • 示例 1:

      输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
      输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
    
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> m = new HashMap<>();
        for(String str : strs){
            char[] s = str.toCharArray();
            Arrays.sort(s);
            String key = new String(s);
            // 方法首先检查map中是否存在给定的key
            // getOrDefault(key,defaultValue):如果key存在于map中,方法返回与该key关联的值。反之,如果key未找到,方法返回提供的defaultValue
            List<String> list = m.getOrDefault(key,new ArrayList<String>());
            //无论找没找到,最后得到了一个list集合,添加str进入其中
            list.add(str);
            m.put(key,list);
        }
        return new ArrayList<>(m.values());
    }
}
  • 本题需要注意的是,在new之后新建的List必须是一个对象,由于List本身只是一个接口,所以不能实例化对象,因此后面写的是ArrayList

最长连续序列(中等)

  • 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

  • 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

  • 思路:

  • 1.初始化一个 HashSet<Integer> 变量 num_set
    使用增强型 for 循环遍历数组 nums,将每个元素添加到 num_set 中。由于 HashSet自动去重,最终 num_set 中存储的是 nums 中的所有不重复整数。

  • 2.定义变量 longestStreak 用于记录最长连续整数序列的长度,初始化为 0。

  • 3.使用增强型 for 循环再次遍历 num_set 中的所有整数(即不重复的 nums 元素)。对于当前遍历到的整数 num,检查 num_set 是否包含 num - 1。如果不包含,说明 num 可能是某个连续整数序列的起始点

  • 4.初始化当前整数 currentNumnum,当前连续序列长度 currentStreak 为 1。使用 while 循环,只要 num_set 包含 currentNum + 1,就将 currentNum 加 1,currentStreak 加 1,继续扩展连续序列。
    currentNum 无法继续扩展(即 num_set 不包含 currentNum + 1)时,退出 while 循环。

class Solution {
    public int longestConsecutive(int[] nums) {
   Set<Integer> num_set = new HashSet<Integer>();
        for (int num : nums) {
            num_set.add(num);
        }

        int longestStreak = 0;

        for (int num : num_set) {
            if (!num_set.contains(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;

                while (num_set.contains(currentNum + 1)) {
                    currentNum += 1;
                    currentStreak += 1;
                }

                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }

        return longestStreak;
    }
}

接雨水(困难)

  • 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
    在这里插入图片描述

  • 思路:

  • 1.初始化两个指针 leftright 分别指向数组的第一个元素和最后一个元素。

  • 2.初始化两个变量 leftMaxrightMax 分别表示 leftright 指针指向的柱子的高度最大值。

  • 3.使用 while 循环,当 leftright 指针没有相遇时,继续执行以下操作:

  • 4.如果 height[left] 小于等于 height[right],将 left 指针向右移动,并更新 leftMaxmax(leftMax,height[left])。然后计算当前柱子能接的雨水量,并更新结果 ans

  • 5.如果 height[left] 大于 height[right],将 right 指针向左移动,并更新 rightMaxmax(rightMax,height[right])。然后计算当前柱子能接的雨水量,并更新结果 ans

  • 6.重复步骤 3 和 4,直到 leftright 指针相遇。

class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
}
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
  • 12
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值