🚀 LeetCode刷题笔记
169. 多数元素
题目描述:
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/majority-element
思路分析:
-
可以直接对整个数组进行排序,由于相同的数字是大于数组的一半的,那么就可以直接将中间的数字返回。中间的数字就是存在最多的数
-
摩尔投票法
该方法的思路是:开始的时候指定一个cand_num,初始化为第一个数,同时定义一个count用来记录这个数出现的个数。然后遍历数组,如果下一个数字和这个数字相同就count++,如果不相同的话就将count–,如果减去之后count为0了那么就将cand_num的值设置为当前这个数。同时将count的值设置为1.
class Solution {
public int majorityElement(int[] nums) {
int cand_num = nums[0]; //初始化为第一个数
int count = 1; //记录出现的次数 初始化为1
for(int i=0;i<nums.length;i++){
if(nums[i]==cand_num) count++; //相同的话就加1
else { //不同的话就先减一在判断
count--;
if(count==0){ //相减之后为0的话就将cand_num重新赋值 并将count赋值为1
cand_num = nums[i];
count = 1;
}
}
}
return cand_num;
}
}
338. 比特位计数
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
思路:
- 写一个函数用来计算每个数字的1的个数,然后对所有的数字遍历 然后将每个数字的结果放入到结果数组中
class Solution {
public int[] countBits(int n) {
int[] res = new int[n+1];
res[0] = 0;
for(int i = 1; i <= n; i++){
res[i] = getNum(i);
}
return res;
}
//计算1的个数
public int getNum(int x){
int count = 0;
while(x!=0){
if((x&1) ==1) {
count++;
}
x = x>>1;
}
return count;
}
}
-
奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
-
偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的
class Solution { public int[] countBits(int n) { int[] res = new int[n+1]; res[0] = 0; for(int i = 1; i <= n; i++){ if(i%2==0) res[i] = res[i/2]; else res[i] = res[i-1]+1; } return res; } }
中等
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
滑动窗口算法解决:这里可以使用HashMap来表示窗口,Key为字符,Value为字符的出现次数 如果里面的字符个数大于1 说明在子段中出现了重复的元素 就应该滑动窗口,只有当里面没有重复的元素的时候才会计算子串的长度 即right-left
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> window = new HashMap<>(); //定义窗口记录每个字符的个数
int left=0,right=0,res=0;
while(right<s.length()){
char temp = s.charAt(right); //先取出最最右边的元素
right++; //后移
if(window.containsKey(temp)) window.put(temp,window.get(temp)+1); //如果当前的窗口中已经包含了该元素 就将该元素的Value+1
else window.put(temp,1); //第一次出现的话就设置为1
while(window.get(temp)>1){ //如果当前这个元素的值大于1 说明存在重复的元素 就应该将窗口向右滑动 也就是将left++
char t = s.charAt(left); //滑动的时候应该注意的是将当前元素的value减一 同时left++
left++;
window.put(t,window.get(t)-1);
}
res = Math.max(res,right-left); //取最大值
}
return res;
}
}
22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
有效括号组合需满足:左括号必须以正确的顺序闭合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
采用回溯法的思想 主要是遍历一颗树 对树进行深度优先遍历 然后将其中的某写不符合条件的结果删除 就可以得到最后的结果
class Solution {
private List<String> res = new ArrayList<>();
public List<String> generateParenthesis(int n) {
if(n<=0) return null;
dfs("",0,0,n);
return res;
}
public void dfs(String path,int left,int right,int n){
// 判断不合法的情况 右括号的数量大于左括号 左括号的数量超过一半的数量
if(right>left || left>n) return;
//终止条件 当路径长度达到了2*n 说明已经遍历完成
if(path.length() == 2*n) {
res.add(path); //添加到结果中
return; //返回到上层继续下一次遍历
}
dfs(path+'(',left+1,right,n); //继续下一层的遍历 如果是加左括号的话就将left++ 右边就是将右边的right++
dfs(path+')',left,right+1,n);
}
}
46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
if(nums.length==0) return null;
boolean[] used = new boolean[nums.length];
Arrays.fill(used,false);
dfs(result,nums,used,new ArrayList<>());
return result;
}
public void dfs(List<List<Integer>> res,int[] nums,boolean[] used,ArrayList<Integer> tmp){
//结束条件
if(tmp.size()==nums.length) {
res.add(new ArrayList(tmp)); //这里不能直接加tmp 而是要new ArrayList 因为tmp引用的那个对象一直在不断的变化
return;
}
//选择条件
for(int i=0; i<nums.length;i++){
if(used[i]==true) continue;
//做选择
used[i]=true;
tmp.add(nums[i]);
dfs(res,nums,used,tmp);
//撤销选择
tmp.remove(tmp.size()-1);
used[i] = false;
}
}
}
31. 下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
输入:nums = [1,2,3]
输出:[1,3,2]
我们希望下一个数比当前数大,这样才满足“下一个排列”的定义。因此只需要将后面的「大数」与前面的「小数」交换,就能得到一个更大的数。比如 123456,将 5 和 6 交换就能得到一个更大的数 123465。
我们还希望下一个数增加的幅度尽可能的小,这样才满足“下一个排列与当前排列紧邻“的要求。为了满足这个要求,我们需要:
在尽可能靠右的低位进行交换,需要从后向前查找
将一个 尽可能小的「大数」 与前面的「小数」交换。比如 123465,下一个排列应该把 5 和 4 交换而不是把 6 和 4 交换
将「大数」换到前面后,需要将「大数」后面的所有数重置为升序,升序排列就是最小的排列。以 123465 为例:首先按照上一步,交换 5 和 4,得到 123564;然后需要将 5 之后的数重置为升序,得到 123546。显然 123546 比 123564 更小,123546 就是 123465 的下一个排列
class Solution {
public void nextPermutation(int[] nums) {
int len = nums.length;
for(int i = len-1;i>=1;i--){ //从后向前找到第一个升序的一对数字
if(nums[i]>nums[i-1]){
Arrays.sort(nums,i,len); //将较大数字到数组最后进行排序
for(int j = i;j < len;j++){
if(nums[j]>nums[i-1]){ //在后面的序列中找到第一个比i-1位置大的数字进行交换
int temp = nums[j];
nums[j] = nums[i-1];
nums[i-1] = temp;
return;
}
}
}
}
Arrays.sort(nums);//走到这里说明数组是降序的 所以之间到第一个最小的数 对数组进行排序就行
return;
}
}
48. 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
class Solution {
public void rotate(int[][] matrix) {
//先沿着对角线进行交换位置
for(int i =0;i<matrix.length;i++){
for(int j=0;j<i;j++){ //这里的循环是小于i 因为只需要对矩阵的上下三角进行交换位置
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
int n = matrix.length;
int n1= matrix[0].length;
//垂直方向进行翻转
for(int i=0;i<n;i++){
if(n1%2==0){ //如果长度是偶数
for(int j =0;j<n1/2;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[i][n1-j-1];
matrix[i][n1-j-1] = temp;
}
}
else if(n1%2==1){ //奇数
for(int j =0;j<=n1/2;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[i][n1-j-1];
matrix[i][n1-j-1] = temp;
}
}
}
}
}
49. 字母异位词分组
示例 1:
输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
//判断是否为空字符串数组
if(strs == null || strs.length == 0){
return new ArrayList();
}
//1.创建一个哈希表 这里哈希表的思想很重要 因为同一个List中的元素的字母是一样的 所以可以考虑使用排序后的字符串作为key ,value就是List
Map<String,List> map = new HashMap<String, List>();
for (String s: strs) {
//将字符串转化为字符数组
char[] chars = s.toCharArray();
//对字符数组按照字母顺序排序
Arrays.sort(chars);
//将排序后的字符串作为哈希表中的key值
//这里需要提前将这个key存储 可以使用new String(chars)
//不能每次使用key的时候都new String() 会报错
String key = String.valueOf(chars);
//2.判读哈希表中是否有该key值
if (!map.containsKey(key)){
//若不存在,则为新的异位词语,在map中创建新的键值对
map.put(key,new ArrayList());
}
//3.将该字符串放在对应key的list中
map.get(key).add(s);
}
//返回map中所有键值对象构成的list
return new ArrayList(map.values());
}
}
64. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
class Solution {
public int minPathSum(int[][] grid) {
int len = grid.length;
int len0 = grid[0].length;
int dp[][] = new int[len][len0];
dp[0][0] = grid[0][0];
//先初始化第0行 只有往右边走 依次相加就行
for(int i = 1;i < len0; i++){
dp[0][i] = grid[0][i]+dp[0][i-1];
}
//初始化第0列元素 和上面类似
for(int i = 1;i < len; i++){
dp[i][0] = dp[i-1][0]+grid[i][0];
}
//dp数组赋值
for(int i = 1;i<len;i++){
for(int j = 1;j<len0;j++){
dp[i][j] = Math.min(dp[i][j-1],dp[i-1][j])+grid[i][j]; //注意一下这里的比较最小值的两个数的索引下标
}
}
return dp[len-1][len0-1];
}
}
24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null || head.next ==null) return head;
ListNode next = head.next;
ListNode newNode = swapPairs(next.next);
head.next = newNode;
next.next = head;
return next;
}
}
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。**进阶:**你能尝试使用一趟扫描实现吗?
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head == null) return null;
ListNode fast = new ListNode();
ListNode slow = new ListNode();
slow = head;
fast = slow;
for(int i= 0; i < n; i++){
fast = fast.next;
}
//如果是null则说明往后面移动了链表长度这么多 则说明倒数第n个刚好就是头结点 就是说删除头结点
if(fast==null) return head.next;// 说明删除的是头结点
while(fast.next!=null){ //这里一定要考虑fast.next是否为空 防止产生空指针异常
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return head;
}
}
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
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;
}
}
}
78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
if(nums.length==0) {
res.add(new ArrayList<>());
return res;
}
dfs(nums,0);
return res;
}
public void dfs(int[] nums,int index){
res.add(new ArrayList<>(path));
if(index>=nums.length) return;
//因为集合是无序的 所以开始的下标是从index开始而不是0 这样就可以保证 1可以遍历2 3 但是2 只能遍历 3 最后的3就只有自己
for(int i = index; i<nums.length;i++){
path.add(nums[i]);
dfs(nums,i+1);
path.removeLast();
}
}
}
2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
小技巧:对于链表问题,返回结果为头结点时,通常需要先初始化一个预先指针 pre,该指针的下一个节点指向真正的头结点head。使用预先指针的目的在于链表初始化时无可用节点值,而且链表构造过程需要指针移动,进而会导致头指针丢失,无法返回结果。
代码作者:guanpengchn
链接:https://leetcode-cn.com/problems/add-two-numbers/solution/hua-jie-suan-fa-2-liang-shu-xiang-jia-by-guanpengc/
![img](https://gitee.com/yan256992/cloudimages/raw/master/img/addtwonumber1.jpg)
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode cur = pre; //工作指针
int carry = 0;
while(l1!=null || l2!=null){
int x = l1==null?0:l1.val;
int y = l2==null?0:l2.val;
int sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
cur.next = new ListNode(sum);
cur = cur.next;
if(l1!=null) l1=l1.next;
if(l2!=null) l2=l2.next;
}
//如果有进位的话就在后面加上一个节点
if(carry==1) cur.next = new ListNode(carry);
return pre.next;
}
}
96. 不同的二叉搜索树
给你一个整数
n
,求恰由n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
class Solution {
public int numTrees(int n) {
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2;i <= n;i++)
for(int j=0;j<i;j++)
dp[i]+=dp[i-j-1]*dp[j];
return dp[n];
}
}
102. 二叉树的层序遍历
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
if(root==null) return new ArrayList<>();
List<List<Integer>> res= new ArrayList<>();
q.offer(root);
while(!q.isEmpty()){
List<Integer> tmp = new ArrayList<>();
int n = q.size();
for(int i = 0;i<n;i++){
TreeNode node = q.poll();
tmp.add(node.val);
if(node.left!=null) q.offer(node.left);
if(node.right!=null) q.offer(node.right);
}
res.add(tmp);
}
return res;
}
}
17. 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
示例 1:
输入:digits = “23”
输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]
示例 2:输入:digits = “”
输出:[]
![img](https://gitee.com/yan256992/cloudimages/raw/master/img/17_telephone_keypad.png)
class Solution {
List<String> res = new ArrayList<>();
HashMap<Integer, char[]> map = new HashMap() {{ //存储为数组更好操作
put(2, new char[]{'a','b','c'});
put(3, new char[]{'d', 'e', 'f'});
put(4, new char[]{'g', 'h', 'i'});
put(5, new char[]{'j', 'k', 'l'});
put(6, new char[]{'m', 'n', 'o'});
put(7, new char[]{'p', 'q', 'r', 's'});
put(8, new char[]{'t', 'u', 'v'});
put(9, new char[]{'w', 'x', 'y', 'z'});
}};
public List<String> letterCombinations(String digits) {
//特殊情况判断
if(digits.length()==0) return new ArrayList<>();
//保存结果
List<Character> path = new LinkedList<>();
// 获取当前字符串的字符的数值
int index = digits.charAt(0)-'0';
backtrack(digits,path,index,map,0);
return res;
}
private void backtrack(String digits, List<Character> path, int index, HashMap<Integer, char[]> map,int pos) {
// 结束条件 如果path的长度和源字符串的长度一致 或者说当前遍历字符串的位置已经到达了源字符串的最后一个位置了
if(path.size()==digits.length() || pos== digits.length()){
StringBuilder sb = new StringBuilder();
for (Character character : path) {
sb.append(character);
}
res.add(sb.toString());
return;
}
index = digits.charAt(pos)-'0';
for (int i = 0; i < map.get(index).length; i++) {
//做选择
char[] tmp = map.get(index);
path.add(tmp[i]);
//这里就需要遍历源字符串的下一个位置 但是这里不能使用pos++ 因为如果是pos++的话第一次是执行pos这个值 然后第二次才是pos+1后的值
backtrack(digits,path,index,map,pos+1);
//撤销选择
path.remove(path.size()-1);
}
}
}
动态规划的方法的思想其实就是用空间换时间,将某些结果存储下来,减少计算的次数,这种思想和redis相同,用空间换时间
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return "";
}
int left = 0;
int right =0;
int MaxLeft=0;
int max = 0;
int len = 1;
int strLen = s.length();
for(int i = 0;i<s.length();i++){
left = i-1;
right= i+1;
while(left>=0 && s.charAt(left)==s.charAt(i)){
len++;
left--;
}
while(right < strLen && s.charAt(right)==s.charAt(i)){
right++;
len++;
}
while(left >= 0 && right<strLen && s.charAt(left)==s.charAt(right)){
len = len+2;
right++;
left--;
}
if(len>max){
max = len;
MaxLeft = left;
}
len=1;
}
return s.substring(MaxLeft+1,MaxLeft+1+max);
}
}
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums.length<3) return new ArrayList<>();
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for(int i = 0 ;i < nums.length; i++){
int cur = nums[i]; //记录当前的这个数
if(cur>0) return res; //如果大于0的话就直接返回结果
if(i>0 && nums[i]==nums[i-1]) continue; //去重
int L = i+1;
int R = nums.length-1; //定义L R 这里要定义在循环内部 如果在外部的话 每次都会导致LR不一样 导致最后的结果出问题
while(L<R){
int sum = cur + nums[L]+nums[R];
if(sum==0) {
List<Integer> tmp = new ArrayList<>();
tmp.add(cur);
tmp.add(nums[L]);
tmp.add(nums[R]);
res.add(tmp);
while(L<R && nums[L+1]==nums[L])L++; //这里有可能下一个元素还是这个值 所以这里要判断一下 如果满足的话就继续下一个
while(L<R && nums[R-1]==nums[R])R--; //同上
L++;
R--;
}
else if(sum<0) L++;
else R--;
}
}
return res;
}
}
33. 搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
首先,要在数组中查找元素 首先考虑的就是二分查找,但是二分查找针对的是有序数组 但是这里这个数组也可以近似看成有序的,首先找到中间的那个元素,然后看这个元素与left这个的大小比较 ,通过这个找到一个有序的部分,然后查找。在有序部分找到最普通的部分进行查找
class Solution {
public int search(int[] nums, int target) {
if(nums.length==0 || nums==null) return -1;
int left = 0 ;
int right = nums.length-1;
while(left<=right){
int mid = left+(right-left)/2;
if(nums[mid]==target) return mid;
if(nums[left]<=nums[mid]){ //前半部分有序 在前面找
if(nums[left]<=target && nums[mid]>target) right = mid-1; //最普通的情况 大于左边 小于右边
else left = mid+1; //其他情况
}
else{ //后半部分有序
if(nums[right]>=target && nums[mid]<target) left = mid+1; //普通情况 左边小 右边大
else right = mid-1;
}
}
return -1;
}
}
79. 单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
class Solution {
boolean[][] visited;
public boolean exist(char[][] board, String word) {
int m = board.length; //行数
int n = board[0].length; //列数
if(m*n < word.length()) return false;
visited = new boolean[m][n];
for(int i =0;i < m; i++){
for(int j =0; j< n;j++){
//先找到搜索的入口
if(board[i][j]==word.charAt(0)){
if(dfs(i,j,0,word,board)) return true; //开始搜索
}
}
}
return false;
}
public boolean dfs(int row, int col,int index,String word, char[][] target){
// 边界条件
if(row<0 || row>=target.length || col >= target[0].length || col < 0 ) return false;
// 如果没有被访问 并且对应的字符相等 并且未被访问
if(target[row][col] == word.charAt(index) && visited[row][col]==false){
visited[row][col] = true; //将这个位置置访问
if(index == word.length()-1) return true; //!!!!! 判断当前这个index和字符串的长度
boolean flag1 = dfs(row+1,col,index+1,word,target); //搜索四个方向
boolean flag2 = dfs(row-1,col,index+1,word,target);
boolean flag3 = dfs(row,col-1,index+1,word,target);
boolean flag4 = dfs(row,col+1,index+1,word,target);
visited[row][col] = false; // 将位置置位false
return flag1 || flag2 || flag3 || flag4;
}
return false;
}
}
105. 从前序与中序遍历序列构造二叉树
给定一棵树的前序遍历
preorder
与中序遍历inorder
。请构造二叉树并返回其根节点。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return help(preorder,0,preorder.length,inorder,0,inorder.length);
}
public TreeNode help(int[] preorder,int pStart,int pEnd,int[] inorder,int iStart,int iEnd){
if(pStart == pEnd) return null;
int rootVal = preorder[pStart]; //获取根节点的值
int NumIndex = 0;
for(int i = iStart;i<iEnd;i++){
if(inorder[i] == rootVal) NumIndex = i; //找到中序遍历中值相等的那个元素的索引
}
int num1 = NumIndex - iStart; //找到这个位置的元素的左边有多少元素
TreeNode root = new TreeNode(rootVal);
root.left = help(preorder,pStart+1,pStart+1+num1,inorder,iStart,NumIndex);//对划分后的数组进行遍历
root.right = help(preorder,pStart+1+num1,pEnd, inorder,NumIndex+1,iEnd);
return root;
}
}
128. 最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> s = new HashSet<>();
for(int num: nums){ //去重
s.add(num);
}
int Res = 0; //保留最长的子序列
for(int num: nums){ //遍历集合 //下面也可以换成num+1 但是后面也需要改变 保证是按照一个方向搜索的 从大到小或者从小到大
if(!s.contains(num-1)){ //如果不包含比他小一的那个元素 那么就设置最长的序列长度为1 并且更新curNum
int tempRes = 1;
int curNum = num;
while(s.contains(curNum+1)){ //寻找比它大的元素的个数有多少
tempRes++; //更新以这个元素为起点的序列长度
curNum++; //更新元素
}
Res = Math.max(Res,tempRes); //比较获得最大值
}
}
return Res;
}
}
152. 乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution {
/**
首先由于这里是涉及到乘法的 如果单纯的一个数组来记录最大值的话是不够的
因为有的最大的元素也有可能两个负数进行相乘最后得到最大的值
所以要维护两个数组 一个数组用于记录最大 一个记录最小
然后当前元素与这两个进行比较 取大的那一个和当前元素比较 获得当前这个位置的最大值
同时 最小的元素也是类似 只不过比较的时候是取最小的那一个
*/
public int maxProduct(int[] nums) {
if(nums.length==1)return nums[0];
int[] maxDp = new int[nums.length];
int[] minDp = new int[nums.length];
int ans = nums[0]; //这里初始化要设置为第一个元素 而不是0 否则第一个元素就是最大的话就会报错
maxDp[0] = minDp[0] = nums[0];
for(int i = 1; i<nums.length; i++){
//dp数组的值 是当前这个元素 当前元素*最大元素 当前元素*最小元素 中的最大值
maxDp[i] = Math.max(nums[i],Math.max(nums[i]*maxDp[i-1],nums[i]*minDp[i-1]));
minDp[i] = Math.min(nums[i],Math.min(nums[i]*maxDp[i-1],nums[i]*minDp[i-1]));
ans = Math.max(ans,maxDp[i]);
}
return ans;
}
}