CodeTop-LeetCode-6-10
53. 最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
思路:
- 动态规划,dp数组记录每个子数组的的和
- 状态转移:前面子数组的和加上下一个元素(若卡面子数组和为负数则不加)就是这个子数组的和
代码:
class Solution {
/**
动态规划
f[i]是以nums[i]结尾的最大子数组和
两种情况:
1. nums[i]单独成一个数组,f[i]就是他
2. nums[i]和前面的子数组加起来作为最大子数组,f[i]就是f[i]=f[i−1]+nums[i]
这两种情况最大的结果就是最大和
如果如果 nums[i] 左边的子数组元素和是负的,就不用相加了
所以直接通过Math.max(f[i - 1], 0) + nums[i],一个公式计算两种情况
*/
public int maxSubArrayOld(int[] nums) {
int n = nums.length;
int[] f = new int[n];
f[0] = nums[0];
int ans = f[0];
for(int i = 1; i < n; i++){
f[i] = Math.max(f[i - 1], 0) + nums[i];
ans = Math.max(ans, f[i]);
}
return ans;
}
//空间优化
//由于转移方程里f[i]只和f[i−1]一个数有关,只需要记录一个滚动变量就可以
public int maxSubArray(int[] nums) {
int f = 0;
int ans = Integer.MIN_VALUE;
for(int num : nums){
f = Math.max(f, 0) + num;
ans = Math.max(ans, f);
}
return ans;
}
}
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
思路:
- 新建一个结果链表就行,不用在原来的链表上改
- 哨兵节点记录结果链表用于返回,cur指针进行连接和遍历操作
- 对于两个参数链表,直接用他们传入的头指针遍历
代码:
class Solution {
/**
创建一个哨兵节点,作为合并后的新链表头节点的前一个节点。
这样可以避免单独处理头节点,也无需特判链表为空的情况
*/
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode();
ListNode cur = dummy; //当前指向的节点,合并后指向末尾节点
while(list1 != null && list2 != null){
if(list1.val < list2.val){
cur.next = list1;
list1 = list1.next;
} else {
cur.next = list2;
list2 = list2.next;
}
//记得改变当前节点
cur = cur.next;
}
//如果最后还有没拼接的节点, 把链表剩余节点拼到后面
cur.next = list1 != null ? list1 : list2;
return dummy.next;
}
}
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的 回文 子串。
思路:
- 二维dp数组记录每两个子串左右边界组成的子串是否为回文串
- 状态转移:子串是回文串,子串两端如果加上两个相同的字母,仍然是回文串
代码:
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if(len < 2){
//特殊情况,只有一个字符
return s;
}
//记录最长回文串的长度
int maxLen = 1;
//记录开始位置
int begin = 0;
//dp[i][j] 表示 s[i, j] 是否是回文串
//定义一个存储所有可能的结果的二维数组
boolean[][] dp = new boolean[len][len];
char[] charArray = s.toCharArray();
//i==j情况
for(int i = 0; i < len; i++){
//对角线元素
//表示每个单个字母是一个回文子串
dp[i][i] = true;
}
//开始填充每一种子串的情况
//由于i和j是有左右大小关系的
//这里只需要遍历对角线上方,即i<j
for(int j = 1; j < len; j++){
for(int i = 0; i < j; i++){
if(charArray[i] != charArray[j]){
//如果有一对左右字符不相等,这个子串不是回文串
dp[i][j] = false;
} else {
//两种其他情况
if(j - i < 3){
//如果这个子串长度不超过3,肯定回文
//则不需要再看内部子串的情况
dp[i][j] = true;
} else {
//这个子串是否回文,需要看其内部子串是否回文
dp[i][j] = dp[i + 1][j - 1];
}
}
//只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文
//此时记录回文的最大长度和起始位置
if(dp[i][j] && j - i + 1 > maxLen){
//如果是回文,并且长度比之前记录的更长,则更新
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
102. 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
思路:
- 迭代,BFS:用队列或者两个数组记录遍历到的当前层元素和下一层子节点的元素
- 递归,DFS:深度遍历节点,遍历时传入当前层数,根据节点层数将节点值添加到结果数组
代码:
//迭代
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null){
return List.of();
}
//答案返回
List<List<Integer>> ans = new ArrayList<>();
//队列存放每一层的节点
Queue<TreeNode> queue = new ArrayDeque<>();
//根先入队
queue.add(root);
//反复遍历队列,知道没有节点入队
while(!queue.isEmpty()){
int n = queue.size();
//定义列表存放这一层的节点值
List<Integer> vals = new ArrayList<>(n);
while(n-- > 0){
//遍历队列中的节点,将子节点入队
TreeNode node = queue.poll();
vals.add(node.val);
if(node.left != null)
queue.add(node.left);
if(node.right != null)
queue.add(node.right);
}
//这一层的节点添加到结果列表
ans.add(vals);
}
return ans;
}
}
//递归
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null){
return List.of();
}
//答案返回
List<List<Integer>> ans = new ArrayList<>();
//dfs,带一个层数参数
//遍历到哪一层的节点,就把他加到对应层的ans子数组中
dfs(1, root, ans);
return ans;
}
private void dfs(int index, TreeNode root, List<List<Integer>> ans){
//如果ans中数组的个数比当前层数小,则需要添加一个数组来存放这一层的节点
if(ans.size() < index){
ans.add(new ArrayList<Integer>());
}
//将当前节点的值加入到ans中,index代表当前层数
ans.get(index - 1).add(root.val);
//递归处理子树,同时层数+1
if(root.left!=null) {
dfs(index+1, root.left, ans);
}
if(root.right!=null) {
dfs(index+1, root.right, ans);
}
}
}
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
思路:
- 见注释
代码:
class Solution {
/**
这个题和两数之和Ⅱ不一样,这里是无序数组返回下标
不能通过双指针优化搜索过程?
正难则反
可以枚举一个数,在剩下的数中找到符合要求的另一个数
可以借助一个哈希表,它适合O(1)的找到一个数
在遍历的同时,把遍历到的数放入map中
这样,在遍历到一个数时,map中是它之前的所有数,数组剩下的是他之后的数
如果map中没有目标元素,将其放到map中,继续遍历,看后面你是否有能和map中匹配的元素
*/
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
int[] res = new int[2];
for(int j = 0; j < nums.length; j++){
if(map.containsKey(target - nums[j])){
res[0] = map.get(target - nums[j]);
res[1] = j;
return res;
} else {
map.put(nums[j], j);
}
}
return res;
}
}

浙公网安备 33010602011771号