文章目录
反转链表(简单)
描述:
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
-
示例
输入:{1,2,3},返回{3,2,1}
输入:{1,2},返回{2,1}
输入:{1},返回{1}
输入:{},返回{} -
思路
利用pre节点和next节点反转链表,最后return
pre节点
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} -
思路
- 先找到第m个节点,并记录下来
- 然后从第m个节点开始,依次反转
- 最后返回反转后的链表的头指针
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} -
思路
- 比较两个链表的节点值,将较小的节点加入新链表
- 循环比较,直到其中一个链表为空
- 将另一个链表剩余的节点加入新链表
- 返回新链表
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+b−c -
指针
B
先遍历完链表headB
,再开始遍历链表headA
,当走到node
时,共走步数为:
a + b − c a+b-c a+b−c -
若两链表 有 公共尾部 (即 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.初始化当前整数
currentNum
为num
,当前连续序列长度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.初始化两个指针
left
和right
分别指向数组的第一个元素和最后一个元素。 -
2.初始化两个变量
leftMax
和rightMax
分别表示left
和right
指针指向的柱子的高度最大值。 -
3.使用 while 循环,当
left
和right
指针没有相遇时,继续执行以下操作: -
4.如果
height[left]
小于等于height[right]
,将left
指针向右移动,并更新leftMax
为max(leftMax,height[left])
。然后计算当前柱子能接的雨水量,并更新结果ans
。 -
5.如果
height[left]
大于height[right]
,将right
指针向左移动,并更新rightMax
为max(rightMax,height[right])
。然后计算当前柱子能接的雨水量,并更新结果ans
。 -
6.重复步骤 3 和 4,直到
left
和right
指针相遇。
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)。