1.数组
1.栈
1.有效的括号-20
指定一个由{}、[]、()三种组成的括号字符串,来校验是否是有效匹配的字符串。
/**思路:1、初始校验、判断是否为空、是否为奇数(这两种情况直接返回结束)2、转换为字节数组、将对立的另一半压入栈中;3、遍历压入另一半元素时,如果元素不相等、或者栈为空则认为不匹配*/public class Solution{public boolean solution(String str){// 初始校验if(str.isEmpty()){return false;}// 转换为字符数组、循环压入另一半入栈Stack<Character> stack = new Stack<Character>();for(char s : str.toCharArray()){if(s == '('){stack.push(')');}else if(s == '['){stack.push(']');}else if(s == '{'){stack.push('}');}else if(stack.isEmpty() || s != stack.pop()){// 为空,则表示为奇数个、不相等则表示不匹配return false;}return stack.isEmpty();}}}
2.链表
1.环形链表
给定一个链表、其中pos指示的是表尾部指向形成环状结点的位置,
/**思路:定义双指针、初始均为指向头结点1、preNode指针每次移动两个元素2、currNode指针每次移动一个元素3、当两个指针相等时,则会追上,表示存在环形链表*/public class Solution{public boolean solution(ListNode head){ListNode preNode, currNode = head;while(preNode != null && preNode.next != null){preNode = preNode.next.next;currNote = currNode.next;if(currNode == preNode) return true;}return false;}}
2.反转链表
给定一个链表,返回反转后的链表
public class Solution{public ListNode reverserNode(ListNode head){// 定义当前指针指向头结点ListNode curr = head;// 定义前指针、临时指针ListNode pre, temp = null;while(curr != null){// 将临时指针指向第一个结点的下一个结点temp = curr.next;// 将当前结点的next指向precurr.next = pre;// 分别移动pre、currpre = curr;curr = temp;}return pre;}}
2.Map容器
1.两数相加
给定一个不重复元素的数组、指定一个目标值、求出数组中 num1 + num2 = target元素的下标数组
/**思路:1、利用Map键值对的特性、存储元素值key、对应的索引下标i2、循环遍历数组,target - nums[0]后的值加入Map中;3、遍历第二个元素,当target -nums[1]后的值等于第一次加入Map中元素的值,则返回数组4、如果仍然不相等,则再次加入当前值。*/public class Solution{public int[] twoSum(int[] nums, int target){// Map存储元素HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();for(int i = 0; i < nums.lenght; i++){if(map.containsKey(target - nums[i])){// target - 当前值后的元素存在之前加入的map容器中return new int[]{map.get(target - nums[i]), i};}// 不存在,则加入当前元素map.put(nums[i], i);}// 未找到则返回空return null;}}
2.只出现一次的数字
给定一个整数数组、只包含一个出现一次的数字、其余都是两次、找出出现一次的数字
/**思路:使用hash表存在,则value+1,不存在,则value=1*/public class Solution{public int singletonValue(int[] nums){Map<Integer, Integer> map = new HashMap<>();// 遍历元素放入hash表中for(Integer i : nums){Integer count = map.get(i);count = count == null ? 1 : ++count;map.put(i. count);}// 找出value为1的值for(Integer i : map.keySet()){if(map.get(i) == 1){return i;}}return -1;}}
3.滑动窗口-不重复的最长字符串长度

/**思路:1、使用Map容器记录遍历后的元素、key=元素值-value=索引值2、当出现重复元素时、则改变起始不重复元素的索引指针3、通过right - left + 1计算最大长度*/public class Solution{public int maxLength(String s){// 获取长度int length = s.length();if(length == 0){return 0;}// Hash结构,存储元素HashMap<Character, Integer> map = new HashMap<>();// 循环遍历元素int max = 0;int left = 0;for(int i = 0; i < length; i++){if(map.containsKey(s.charAt(i))){// 包含重复元素。则改变起始位置left = Math.max(left, map.get(s.charAt(i)) + 1);}// 不重复则加入元素map.put(s.charAt(i), i);// 计算最大长度max = Math.max(max, i - left + 1);}return max;}}
3.递归
1.合并两个有序链表-21
指定两个递增的有序链表、将其从小到大进行合并,返回合并后的递增有序链表
/**思路:1、递归思路求解2、第一次比较、list1中的1与list2中的2比较、如果list1中的1结点 < 小于list2中的1结点,则比较list1中的2结点与list2中所有结点。大于,则相反思想3、当list1、list2中有一个为空则进行返回*//*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/public class Solution{public ListNode mergeListNode(ListNode list1, ListNode list2){if(list1 == null){return list2;}if(list2 == null){return list1;}// 递归调用合并if(list1.val < list2.val){// 递归比较list1的下一个结点与list2中的所有结点list1.next = mergeListNode(list1.next, list2);return list1;}else{// 反过来进行递归list2.next = mergeListNode(list2.next, list1);return list2;}}}
4.动态规划
1.最大子数组和
给定一个数组、求出最大值连续子数组,返回最大值
/**思路:关键词:连续、最大和、具有最大和的连续子数组动态规划:大问题化为若干子问题,求最优解1、如何划分子问题第一种:我们无法确定最大和连续子数组中到底包含哪一个,但是可以知道经过每一个元素的最大和,然后在所有的最大和数组中找出最大值即可(最优解)1.子问题1:经过-2的连续子数组最大和?2.子问题2:经过1的连续子数组最大和?......9.子问题9:经过4的连续子数组最大和?第二种:上述分析中,每个子问题中又包含不确定性,无法确定子问题中到底有多少中情况。因此、我们还要进行其他方式划分1.子问题1:以-2结尾的连续子数组最大和?2.子问题2:以1结尾的连续子数组最大和?......9.子问题9:以4结尾的连续子数组最大和?2、如何确定状态转换上述问题中,可能存在两种类型、全部大于0,与不全部大于0。这两种状态中,以nums[i]结尾的元素一定会被包含到,则只需要考虑剩余nums[i - 1]的状态。1、如果全部或部分 > 0(且dp[i - 1] > 0),则dp[i] = dp[i - 1] + nums[s];2、如果剩余部分 <= 0(且dp[i - 1] <= 0),则dp[i] = nums[i]故状态转义方程如下:dp[i - 1] + nums[i], dp[i - 1] > 0dp[i] = {nums[i], dp[i - 1] <= 0*/public class Solution{public int maxSubArray(int[] nums){// 定义状态数组int len = nums.length;int[] dp = new int[len];// 初始化初值dp[0] = nums[0];// 根据状态转移方程计算子问题for(int i = 1; i < len; i++){if(dp[i - 1] > 0){// dp[i - 1] > 0,直接相加dp[i] = dp[i - 1] + nums[i];}else{// dp[i - 1] <= 0dp[i] = nums[i];}}// 从dp数组中寻找最大值int result = dp[0];for(int i = 1; i < len; i++){result = Math.max(result, dp[i]);}return result;}}
2.爬楼梯
假设存在n阶楼梯、每次只能爬1或2个台阶。求出有多少中走法;
/**思路:关键词:共n阶、每次只能爬1或2阶。求走法?动态规划,划分子问题、求子问题中的最优解1、划分子问题1.总共n阶楼梯2.第n阶只有一种走法、则第n-1阶有多少种走法、第n-2阶有多少种走法2、定义状态方程0 1 2 3 5 走法0 1 2 3 4 阶数规律:从2开始,每一阶走法都是n-1 + n-2阶之和dp[n] = dp[n - 1] + dp[n - 2]*/class Solution {public int climbStairs(int n) {if(n <= 2){return n;}// 定义状态数组int[] dp = new int[n + 1];// 初始化初值dp[0] = 0;dp[1] = 1;dp[2] = 2;// 循环处理3阶以上问题for(int i = 3; i <= n; i++){dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];}}
3.买卖股票最佳时机
给定一个股票数组、只能单次的买卖、当前买入、在未来的某一天卖出股票、求最大利润。
注意:不能在买入前进行卖出、不能在同一天进行买卖
/**思路:动态规划1、状态划分:dp[i][0]:表示在[0 - i]区间范围内,第i天结束之后不持有股票此时利润为0dp[i][1]:表示在[0 - i]区间范围内,第i天结束之后持有股票此时利润为第i天的负数 -precies[i]2、状态转义方程第一天:dp[0][0]:不持股、则最大利润为0dp[0][1]:持股、则最大利润为这一天的负数-prices[i]第二天:dp[1][0]:未持股、或者持股卖出(两者中间取最大值)则最大利润为dp[i - 1][0], dp[i - 1][i] + prices[i];dp[1][1]:持股则和之前的进行比较、取一个较大值则最大利润为dp[i - 1][1], -prices[i]*/public class Solution{public int maxPrice(int[] prices){// 排除基本情况int len = prices.length;if(len < 2){return 0;}// 定义状态方程int[][] dp = new dp[lne][2];// 初始化第一天的值dp[0][0] = 0;dp[0][1] = -prices[0];// 从第二天开始进行遍历for(int i = 1; i < len; i++){// 不持股、或者持股卖出dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);}// 返回最后一次最大值即可return dp[len - 1][0];}}
4.最长回文子串
给定一个字符串、找出最长的回文字符串
/**思路:动态规划求解最优解(最大值)1、定义状态方程组初始化一个二维数组、用来记录动态规划中的变化值2、初始化化方程组对角线元素中的自身都是回文字符串、可以初始化为true3、状态转义方程1、从第一个字符开始、逐渐扩宽字符区间、然后在从每个字符区间进行缩小2、外层循环控制扩宽区间、内层循环控制缩减每个区间false, chars[i] != chars[j]dp[i][j] =j-i < 3||dp[i+1][j-1]*/public class Solution{public String maxString(String s){// 获取字符串长度int len = s.length();if(len == 1){return s;}// 定义状态方程boolean[][] dp = new boolean[len][len];// 获取字符数组char[] chars = s.toCharArray();// 初始化初始值for(int i = 0; i < len; i++){dp[i][i] = true;}// 定义最大长度、开始位置int maxLen = 1;int begin = 0;// 循环开始遍历元素for(int j = 1; j < len; j++){for(int i = 0; i < j; i++){// 判断当前区间中的两端是否相同if(chars[i] != chars[j]){dp[i][j] = false;}else{// 相同,则判断剩余个数是否小于3if(j - i < 3){dp[i][j] = true;}else{dp[i][j] = dp[i + 1][j - 1];}}}// 改变长度、开始位置if(dp[i][j] && j - i + 1 > maxLen){maxLen = j - i + 1;begin = i;}}// 返回最大回文子串return s.substring(begin, begin + maxLen);}}
