1、两数之和
经典两数之和,可以通过暴力枚举解决(时间复杂度O(n^2)),当然也可以通过哈希表,hashtable.containsKey(),来选择判断x和target-x
class Solution {
public int[] twoSum(int[] nums, int target) {
int m=nums.length;
int[] re=new int [2];
for(int i=0;i<m;i++){
for(int j=i+1;j<m;j++){
if(nums[i]+nums[j]==target){
re[0]=i;
re[1]=j;
}
}
}
return re;
}
}
2、两数相加
遍历两个链表,逐位计算它们的和
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode root = new ListNode(0);
ListNode curNode = root;
int carry = 0;
while (l1 != null || l2 != null || carry != 0) {
int l1val = l1 != null ? l1.val : 0;
int l2val = l2 != null ? l2.val : 0;
int sumval = l1val + l2val + carry;
carry = sumval / 10;
ListNode sumNode = new ListNode(sumval % 10);
curNode.next = sumNode;
curNode = sumNode;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
return root.next;
}
}
3、无重复字符的最长子串
通过滑动窗口来做,start是当前字符的前一个相同字符的位置加一,然后res存储最大的长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
//字符上一次出现的位置
int[] last = new int[128];
for(int i=0;i<128;i++){
last[i] = -1;
}
int n = s.length();
int res = 0;
//滑动窗口开始的位置
int start = 0;
for(int i=0;i<n;i++){
int index = s.charAt(i);
start = Math.max(start,last[index] + 1);
res = Math.max(res,i-start+1);
last[index] = i;
}
return res;
}
}
5、最长回文子串
找到字符串s中的最长回文子串。通过动态规划法来考虑P(i,j)=P(i+1,j−1),所以外层for循环枚举自子串的长度2-n,内层for循环枚举左边界,然后通过判断dp[i,j]的true来选择最长的子串。
class Solution {
public String longestPalindrome(String s) {
int size = s.length();
boolean[][] dp = new boolean[size][size];
int max = 1;
int left = 0;
int right = 0;
if(size<2){
return s;
}
for(int i=0;i<size;i++){
dp[i][i] = true;
}
char[] str = s.toCharArray();
for(int L=2;L<=size;L++){
for(int i=0;i<size;i++){
int j = i+L-1;
if(j>=size){
break;
}
if(str[i]!=str[j]){
dp[i][j] = false;
}else{
if(j-i<3){
dp[i][j] = true;
}else{
dp[i][j] = dp[i+1][j-1];
}
}
if(dp[i][j] && j-i+1>max){
max = j-i+1;
left = i;
right = j;
}
}
}
return s.substring(left,right+1);
}
}
10、正则表达式匹配
'.‘匹配单个字符,’*'匹配零个或多个前一位字符。动态规划来解题,dp[i,j]来表示s的前i个字符和p的前j个字符是否能够匹配,其中只有星号有特殊情况。通过判断星号代表的零个或多个字符来决定动态规划的公式。匹配0次有 dp[i,j]=dp[i,j-2],匹配多次有dp[i,j]=dp[i-1,j],让s中有星号包含的字符都得到校验。
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] f = new boolean[m + 1][n + 1];
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p.charAt(j - 1) == '*') {
f[i][j] = f[i][j - 2];
if (matches(s, p, i, j - 1)) {
f[i][j] = f[i][j] || f[i - 1][j];
}
} else {
if (matches(s, p, i, j)) {
f[i][j] = f[i - 1][j - 1];
}
}
}
}
return f[m][n];
}
public boolean matches(String s, String p, int i, int j) {
if (i == 0) {
return false;
}
if (p.charAt(j - 1) == '.') {
return true;
}
return s.charAt(i - 1) == p.charAt(j - 1);
}
}
11、盛最多水的容器
双指针法,左右指针移动,最后得到最大的max,时间复杂度O(n)
class Solution {
public int maxArea(int[] height) {
int l = height.length-1;
int i=0,j=l;
int res = 0;
while(i<j){
int hgt = Math.min(height[i],height[j]);
res = Math.max(res,hgt*(j-i));
if(height[i]<height[j]){
i++;
}else{
j--;
}
}
return res;
}
}
15、三数之和
在nums数组中选择所有的a,b,c组合,使得和为0。先对nums进行sort排序,先在第一重for循环中定义first,和前一个数重复则跳过;再定义third=n-1;在第二个for循环中定义second,起始值为first+1,和前一个数重复则跳过;三数之和大于0,third–,等于0,加入list中,直到second==third,break。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
int n = nums.length;
Arrays.sort(nums);
for(int first=0;first<n;first++){
if(first>0&&nums[first]==nums[first-1]){
continue;
}
int third = n-1;
for(int second=first+1;second<n;second++){
if(second>first+1&&nums[second-1]==nums[second]){
continue;
}
while(second<third && nums[first]+nums[third]+nums[second]>0){
third--;
}
if(second==third){
break;
}
if(nums[second]+nums[first]+nums[third]==0){
List<Integer> ans = new ArrayList<>();
ans.add(nums[first]);
ans.add(nums[second]);
ans.add(nums[third]);
list.add(ans);
}
}
}
return list;
}
}
17、电话号码的字母组合
经典深度优先搜索。新建hashmap,插入key和value。在dfs中,有if判断终止条件,for循环中有res.append()和dfs()和res.deleteCharAt()。
class Solution {
List<String> list = new ArrayList<>();
Map<Character,String> hsp = new HashMap<>(){{
put('2',"abc");
put('3', "def");
put('4', "ghi");
put('5', "jkl");
put('6', "mno");
put('7', "pqrs");
put('8', "tuv");
put('9', "wxyz");
}};
public List<String> letterCombinations(String digits) {
if(digits.length()==0){
return list;
}
dfs(digits,0,new StringBuffer());
return list;
}
void dfs(String digits,int index,StringBuffer res){
if(index == digits.length()){
list.add(res.toString());
return;
}
char ans = digits.charAt(index);
String lis = hsp.get(ans);
int size1 = lis.length();
for(int i=0;i<size1;i++){
res.append(lis.charAt(i));
dfs(digits,index+1,res);
res.deleteCharAt(index);
}
}
}
19、删除链表的倒数第n个结点
先求出链表的总长度,然后判断是从头开始的第几个结点,之后再做处理。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode res = head;
int m = 0;
while(res != null){
res = res.next;
m++;
}
if(m==1) return res;
int mi = m-n-1;
res = head;
int i=0;
if(mi<0){
return res.next;
}
while(i!=mi){
res = res.next;
i++;
}
res.next = res.next.next;
return head;
}
}
20、有效的括号
通过入栈和出栈的顺序来判断括号是否有效,s是String类型,s.toCharArray()可以转化为char数组类型。类如char == ‘(’,入栈 ‘)’ ,在判断出栈的字符即可。
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for(char c:s.toCharArray()){
if(c=='(') stack.push(')');
else if(c=='{') stack.push('}');
else if(c=='[') stack.push(']');
else{
if(stack.isEmpty() || stack.pop()!=c) return false;
}
}
return stack.isEmpty();
}
}
21、合并两个有序链表
新建一个链表,当两个链表都不为空时,选择小的插入,之后选择空的链表插入。
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode listt = new ListNode();
ListNode list = listt;
while(list1 != null || list2 != null){
if(list1 == null){
list.next = list2;
list = list.next;
list2 = list2.next;
}
else if(list2 == null){
list.next = list1;
list = list.next;
list1 = list1.next;
}
else{
if(list1.val<list2.val){
list.next = list1;
list = list.next;
list1 = list1.next;
}else{
list.next = list2;
list = list.next;
list2 = list2.next;
}
}
}
return listt.next;
}
}
22、括号生成
dfs深度优先搜索,生成每一组括号时,得先生成左边的括号,再生成右边的括号。n为括号的组数,即左右括号各为n个。dfs中,if左右两边的括号都是0时,为终止条件,list.add() 。left==right时,str+“(”,left减一;left<right时,可以有两种情况。
class Solution {
List<String> list = new ArrayList<>();
public List<String> generateParenthesis(int n) {
dfs("",n,n);
return list;
}
void dfs(String str,int left,int right){
if(left==0 && right==0){
list.add(str);
return;
}
if(left == right){
dfs(str+"(",left-1,right);
}else if(left<right){
if(left>0){
dfs(str+"(",left-1,right);
}
dfs(str+")",left,right-1);
}
}
}
23、合并K个升序链表
思路:新建一个总的链表res,再将其他的链表分别与这个链表合并。
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode res = null;
for(ListNode list : lists){
res = rest(res,list);
}
return res;
}
private ListNode rest(ListNode res,ListNode list){
ListNode ans = new ListNode();
ListNode l = ans;
while(res!=null && list!=null){
if(res.val < list.val){
ans.next = res;
res = res.next;
} else{
ans.next = list;
list = list.next;
}
ans = ans.next;
}
if(res!=null){
ans.next = res;
ans = ans.next;
}
if(list!=null){
ans.next = list;
ans = ans.next;
}
return l.next;
}
}
31、下一个排列
寻找数组中下一个最大的排列数组,先处理完最大和最小的两种特殊情况。从右往左选择比右边的数小的数的位置n,然后在从右到n的范围内,选择第一个比n位置的数大的数的位置res。然后将n与res互换位置,再通过Arrays.sort(nums,n,size)排列。
class Solution {
public void nextPermutation(int[] nums) {
int size = nums.length;
if(size == 1){
return;
}
int n = size - 1;
while(n!=0){
if(nums[n] > nums[n-1]){
break;
} else{
n--;
}
}
if(n==0){
Arrays.sort(nums);
} else if(n==size-1){
swap(nums,n-1,n);
} else{
int res = size-1;
while(nums[res]<=nums[n-1]){
res--;
}
swap(nums,n-1,res);
Arrays.sort(nums,n,size);
}
}
void swap(int[] nums,int a,int b){
int temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
}
32、最长有效括号
通过栈来解决问题,如果前面的 ‘)’ 符号太多,删除旧边界添加新边界,保持stack的长度为1。如果是 ‘(’ ,则入栈数组中的位置, ‘)’ 出栈,并通过 i - stack.peek() 判断最大长度。
class Solution {
public int longestValidParentheses(String s) {
Stack<Integer> stack = new Stack<>();
stack.push(-1);
char[] ch = s.toCharArray();
int size = ch.length;
int max = 0;
for(int i=0;i<size;i++){
if(ch[i] == '('){
stack.push(i);
} else{
if(stack.size()==1){
stack.pop();
stack.push(i); //这里始终是删除旧边界,添加新边界
} else{
stack.pop();
max = Math.max(max,i-stack.peek());
}
}
}
return max;
}
}
33、搜索旋转排序数组
二分法。一个有序,另一个可能有序可能无序。
class Solution {
public int search(int[] nums, int target) {
int i=0,j=nums.length-1;
if(j==0){
if(nums[i]==target){
return 0;
}
else{
return -1;
}
}
while(i<=j){
int mid = (i+j)/2;
if(nums[mid]==target){
return mid;
}
if(nums[mid]>=nums[i]){
if(target>=nums[i] && target<nums[mid]){
j=mid-1;
}else{
i=mid+1;
}
}
else{
if(target>nums[mid] && target<=nums[j]){
i=mid+1;
}else{
j=mid-1;
}
}
}
return -1;
}
}
34、在排序数组中查找元素的第一个和最后一个位置
二分法,时间复杂度 O(log n) 。判断左右边界值,找到target的一个位置,在通过while左右来判断。
class Solution {
int m = -1,n = -1;
public int[] searchRange(int[] nums, int target) {
int i = 0;
int j = nums.length-1;
int zh = (i+j)/2;
if(j==-1){
return new int[]{-1,-1};
}
rus(nums,i,j,target);
return new int[]{n,m};
}
void rus(int[] nums,int i,int j,int target){
int zh = (i+j)/2;
if(nums[zh] < target && zh+1<=j){
rus(nums,zh+1,j,target);
}
else if(nums[zh]>target && zh-1>=i){
rus(nums,i,zh-1,target);
}
else if(nums[zh]==target){
m = zh;
n = zh;
while(m<j && nums[m+1]==target){
m++;
}
while(n>i && nums[n-1]==target){
n--;
}
}
else{
n=-1;
m=-1;
}
}
}
39、组合总和
candidates = [2,3,5], target = 8;[[2,2,2,2],[2,3,3],[3,5]];
dfs解法:target最终被减为0时,终止一次的dfs,小于0时,return;for循环中添加数组中的每个元素,ans.add(),dfs(),ans.remove()。
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> ans = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates,target,0);
return list;
}
void dfs(int[] candidates,int target,int start){
if(target==0){
list.add(new ArrayList<>(ans));
return;
}
if(target<candidates[0]){
return;
}
for(int i=start;i<candidates.length;i++){
ans.add(candidates[i]);
dfs(candidates,target-candidates[i],i);
ans.remove(ans.size()-1);
}
}
}
42、接雨水
先判断左右两边的最大高度,然后最后是左右两边的最大高度的最小值-本高度的和。
class Solution {
public int trap(int[] height) {
int size = height.length;
if(size == 1){
return 0;
}
int[] leftheight = new int[size];
int[] rightheight = new int[size];
leftheight[0] = height[0];
rightheight[size-1] = height[size-1];
for(int i=1;i<size;i++){
leftheight[i] = Math.max(leftheight[i-1],height[i]);
}
for(int i=size-2;i>=0;i--){
rightheight[i] = Math.max(rightheight[i+1],height[i]);
}
int sum = 0;
for(int i=0;i<size;i++){
sum += Math.min(leftheight[i],rightheight[i]) - height[i];
}
return sum;
}
}
46、全排列
返回不重复数组的所有可能的全排列。dfs加回溯来解决,先建立一个list存入数组数据,再建立一个list1存list。
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> res = new ArrayList<>();
for(int num : nums){
res.add(num);
}
int n = nums.length;
dfs(n,0,list,res);
return list;
}
void dfs(int n,int first,List<List<Integer>> list,List<Integer> res){
if(first == n){
list.add(new ArrayList<>(res));
return;
}
for(int i=first;i<n;i++){
Collections.swap(res,first,i);
dfs(n,first+1,list,res);
Collections.swap(res,first,i);
}
}
}
48、旋转图像
不要想着一次性的旋转好,先在矩阵上下掉转,然后在斜对角线掉转。
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
for(int i=0;i<n/2;i++){
for(int j=0;j<n;j++){
swap(matrix,i,j,n-i-1,j);
}
}
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
swap(matrix,i,j,j,i);
}
}
}
void swap(int[][] matrix,int i,int j,int x,int y){
int temp = matrix[i][j];
matrix[i][j] = matrix[x][y];
matrix[x][y] = temp;
}
}
49、字母异位词分组
把string数组中的每个str转化为char数组,对char数组进行排序后作为key,str作为value。依次返回每个key的value。其中用到的 String key = String.valueOf(ch),将char数组转化为String类型;map.get(key).add(str);new ArrayList(map.values())。
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String,ArrayList<String>> map = new HashMap<>();
for(String str : strs){
char[] ch = str.toCharArray();
Arrays.sort(ch);
String key = String.valueOf(ch);
if(!map.containsKey(key)){
map.put(key,new ArrayList<>());
}
map.get(key).add(str);
}
return new ArrayList(map.values());
}
}
53、最大子数组和
动态规划,类似滚动数组思想。
class Solution {
public int maxSubArray(int[] nums) {
int max = nums[0];
int sum = 0;
for(int num : nums){
sum = Math.max(sum+num,num);
max = Math.max(max,sum);
}
return max;
}
}
55、跳跃游戏
maxplace为当前位置所能跳到的最远距离的位置,将他和每个点到的最远位置对比。直到最远位置大于等于终点即可返回true,否则false。
class Solution {
public boolean canJump(int[] nums) {
int m = nums.length;
int maxplace = 0;
for(int i=0;i<m;i++){
if(i<=maxplace){
maxplace=Math.max(maxplace,i+nums[i]);
if(maxplace>=m-1){
return true;
}
}
}
return false;
}
}
56、合并区间
先排序,再处理,最后合并。
class Solution {
public int[][] merge(int[][] intervals) {
int size = intervals.length;
if(size == 1){
return intervals;
}
// 重新排序
Arrays.sort(intervals,new Comparator<int[]>(){
public int compare(int[] interval1,int[] interval2){
return interval1[0] - interval2[0];
}
});
int count = 0;
for(int i=1;i<size;i++){
// [1,5] [3,7] ==> [1,5] [5,7]
if(intervals[i][0] <= intervals[i-1][1] && intervals[i-1][1] < intervals[i][1]){
intervals[i][0] = intervals[i-1][1];
count++;
}
// [1,5] [3,4] ==> [1,5] [5,5]
else if(intervals[i][0] <= intervals[i-1][1] && intervals[i-1][1] >= intervals[i][1]){
intervals[i][0] = intervals[i-1][1];
intervals[i][1] = intervals[i-1][1];
count++;
}
}
int[] ans = new int[size*2];
int z = 0;
// 将所有数存入ans中
for(int i=0;i<size;i++){
ans[z++] = intervals[i][0];
ans[z++] = intervals[i][1];
}
int[][] in = new int[(size-count)][2];
int m = 0;
for(int i=0;i<size-count;i++){
in[i][0] = ans[m];
m++;
// 重复的跳转
while(m<ans.length-1 && ans[m]==ans[m+1]){
m = m+2;
}
in[i][1] = ans[m];
m++;
}
return in;
}
}
62、不同路径
动态规划,先给上左两边赋初始值1,到某个点的路径总数是上面点的路径总数+左边点的路径总数。
class Solution {
public int uniquePaths(int m, int n) {
int[][] ans = new int[m][n];
for(int i=0;i<m;i++){
ans[i][0] = 1;
}
for(int i=0;i<n;i++){
ans[0][i] = 1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
ans[i][j] = ans[i-1][j] + ans[i][j-1];
}
}
return ans[m-1][n-1];
}
}
64、最小路径和
动态规划,先给上左付初始值,到每个点的最小距离都是这个点上左的最小距离的最小值+这个点的距离。
class Solution {
public int minPathSum(int[][] grid) {
int high = grid.length;
int width = grid[0].length;
for(int i=1;i<high;i++){
grid[i][0] += grid[i-1][0];
}
for(int j=1;j<width;j++){
grid[0][j] += grid[0][j-1];
}
for(int i=1;i<high;i++){
for(int j=1;j<width;j++){
grid[i][j] += Math.min(grid[i-1][j],grid[i][j-1]);
}
}
return grid[high-1][width-1];
}
}
70、爬楼梯
class Solution {
public int climbStairs(int n) {
int[] dp = new int[100000];
dp[0] = 1;
dp[1] = 2;
for(int i=2;i<n;i++){
dp[i] = dp[i-2] + dp[i-1];
}
return dp[n-1];
}
}
72、编辑距离
word1 = “horse”, word2 = “ros”,返回将 word1 转换成 word2 所使用的最少操作数。
dp数组表示word1的前i个字母转化为word2的前j个字母所使用的最少操作。
如果当前字母相同,则dp[i,j] = dp[i-1,j-1];否则是增、删、替三个操作的最小值+1。
其中增加为dp[i,j] = dp[i,j-1] + 1;就相当于原来是i-1,j-1,现在word1增加了一个,所以比较i,j-1。
class Solution {
public int minDistance(String word1, String word2) {
int m1 = word1.length();
int m2 = word2.length();
int[][] dp = new int[m1+1][m2+1];
for(int i=1;i<=m1;i++){
dp[i][0] = dp[i-1][0] + 1;
}
for(int j=1;j<=m2;j++){
dp[0][j] = dp[0][j-1] + 1;
}
for(int i=1;i<=m1;i++){
for(int j=1;j<=m2;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1];
}
else{
dp[i][j] = Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j])) + 1;
}
}
}
return dp[m1][m2];
}
}
75、颜色分类
随便的一个排序算法,这里是冒泡排序。
class Solution {
public void sortColors(int[] nums) {
for(int i=0;i<nums.length-1;i++){
for(int j=0;j<nums.length-i-1;j++){
if(nums[j]>nums[j+1]){
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
}
}
76、最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。
先把t中的所有字符都在need数组中计数,然后滑动窗口的思想来计算最小值。
class Solution {
public String minWindow(String s, String t) {
int[] need = new int[128];
for(int i=0;i<t.length();i++){
need[t.charAt(i)]++;
}
int l=0, r=0, size=Integer.MAX_VALUE, count=t.length(), start=0;
while(r<s.length()){
char c = s.charAt(r);
if(need[c]>0){
count--;
}
need[c]--;
if(count==0){
//移掉左边没用的
while(l<r && need[s.charAt(l)]<0){
need[s.charAt(l)] ++;
l++;
}
//替换最大值
if(r-l+1 < size){
size = r-l+1;
start = l;
}
//记录完这个size后,l+1
need[s.charAt(l)]++;
l++;
count++;
}
r++;
}
return size == Integer.MAX_VALUE ? "" : s.substring(start,start+size);
}
}
78、子集
dfs加回溯。
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> ans = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
dfs(nums,0);
return list;
}
void dfs(int[] nums,int start){
list.add(new ArrayList<>(ans));
if(start>=nums.length){
return;
}
for(int i=start;i<nums.length;i++){
ans.add(nums[i]);
dfs(nums,i+1);
ans.remove(ans.size()-1);
}
}
}
79、单词搜索
dfs加回溯。
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(dfs(board,words,i,j,0)) return true;
}
}
return false;
}
boolean dfs(char[][] board,char[] word,int i, int j, int k){
if(i>=board.length || j>=board[0].length || i<0 || j<0 || board[i][j] != word[k]){
return false;
}
if(k==word.length-1) return true;
board[i][j]='\0';
boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j]=word[k];
return res;
}
}
84、柱状图中最大的矩形
栈中下一个柱体就是左边第一个小于自身的柱体。
area始终算的是i之前的最大面积。
// 在一维数组中,找第一个比自己小的元素的位置。
class Solution {
public int largestRectangleArea(int[] heights) {
// 这里为了代码简便,在柱体数组的头和尾加了两个高度为 0 的柱体。
int[] tmp = new int[heights.length + 2];
System.arraycopy(heights, 0, tmp, 1, heights.length);
Deque<Integer> stack = new ArrayDeque<>();
int area = 0;
for (int i = 0; i < tmp.length; i++) {
// 对栈中柱体来说,栈中的下一个柱体就是其「左边第一个小于自身的柱体」;
// 若当前柱体 i 的高度小于栈顶柱体的高度,说明 i 是栈顶柱体的「右边第一个小于栈顶柱体的柱体」。
// 因此以栈顶柱体为高的矩形的左右宽度边界就确定了,可以计算面积
while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
int h = tmp[stack.pop()];
area = Math.max(area, (i - stack.peek() - 1) * h);
}
stack.push(i);
}
return area;
}
}
85、最大矩形
和上题类似,只不过要统计出每列1的个数。
class Solution {
public int maximalRectangle(char[][] matrix) {
if (matrix.length == 0 || matrix[0].length == 0) {
return 0;
}
int sx = matrix.length;
int sy = matrix[0].length;
int[] heights = new int[sy];
int ans = 0;
for(int i=0;i<sx;i++){
for(int j=0;j<sy;j++){
if(matrix[i][j] == '1'){
heights[j]++;
} else{
heights[j] = 0;
}
}
ans = Math.max(ans,largestRectangleArea(heights));
}
return ans;
}
public int largestRectangleArea(int[] heights) {
int[] tmp = new int[heights.length + 2];
System.arraycopy(heights, 0, tmp, 1, heights.length);
Deque<Integer> stack = new ArrayDeque<>();
int area = 0;
for (int i = 0; i < tmp.length; i++) {
while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
int h = tmp[stack.pop()];
area = Math.max(area, (i - stack.peek() - 1) * h);
}
stack.push(i);
}
return area;
}
}
94、二叉树的中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
mid(root,list);
return list;
}
void mid(TreeNode root,List<Integer> list){
if(root == null){
return;
}
mid(root.left,list);
list.add(root.val);
mid(root.right,list);
}
}
96、不同的二叉搜索树
动态规划,可以分解为左子树和右子树。
class Solution {
public int numTrees(int n) {
int[] res = new int[n+1];
res[0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
res[i] += res[j-1] * res[i-j];
}
}
return res[n];
}
}
98、验证二叉搜索树
通过二叉搜索树的特点来解答,节点的左子树只包含小于当前节点的数,节点的右子树只包含大于当前节点的数。
class Solution {
public boolean isValidBST(TreeNode root) {
return todo(root,Long.MIN_VALUE,Long.MAX_VALUE);
}
boolean todo(TreeNode root,long low,long high){
if(root == null){
return true;
}
if(root.val<=low || root.val>=high){
return false;
}
else{
return todo(root.left,low,root.val) && todo(root.right,root.val,high);
}
}
}
101、对称二叉树
递归
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left,root.right);
}
boolean compare(TreeNode left,TreeNode right){
if((left!=null&&right!=null)&&left.val==right.val){
return compare(left.left,right.right) && compare(left.right,right.left);
}
else if(left==null && right==null){
return true;
}
else{
return false;
}
}
}
102、二叉树的层序遍历
广度优先搜索,每个节点都入队和出队一次,所以时间复杂度为O(n)。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list1= new ArrayList<List<Integer>>();
if(root == null){
return list1;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while(!queue.isEmpty()){
List<Integer> list2= new ArrayList<Integer>();
int size = queue.size();
for(int i=1;i<=size;i++){
TreeNode node = queue.poll();
list2.add(node.val);
if(node.left != null){
queue.add(node.left);
}
if(node.right != null){
queue.add(node.right);
}
}
list1.add(list2);
}
return list1;
}
}
104、二叉树的最大深度
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
else{
int leftmax = maxDepth(root.left);
int rightmax = maxDepth(root.right);
return Math.max(leftmax,rightmax)+1;
}
}
}
105、从前序与中序遍历序列构造二叉树
先判断每个根节点的位置,然后再分割左右子树,递归来做。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length==0 || inorder.length==0){
return null;
}
TreeNode root = new TreeNode(preorder[0]);
for(int i=0;i<preorder.length;i++){
if(preorder[0] == inorder[i]){
// 前序遍历:根左右
// 中序遍历:左根右
root.left = buildTree(Arrays.copyOfRange(preorder,1,i+1),
Arrays.copyOfRange(inorder,0,i));
root.right = buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),
Arrays.copyOfRange(inorder,i+1,inorder.length));
break;
}
}
return root;
}
}
114、二叉树展开为链表
将二叉树转化为前序遍历的list之后,在通过list顺序转化为链表顺序。
class Solution {
List<TreeNode> list = new ArrayList<>();
public void flatten(TreeNode root) {
res(root);
for(int i=1;i<list.size();i++){
TreeNode prev = list.get(i-1);
TreeNode ans = list.get(i);
prev.left = null;
prev.right = ans;
}
}
void res(TreeNode root){
if(root == null){
return;
}
list.add(root);
res(root.left);
res(root.right);
}
}
121、买卖股票的最佳时机
买卖股票:最大值:当天的值减去最小值 和 历史最大值 的比较
最小值:当天的值和历史最小值的比较。
class Solution {
public int maxProfit(int[] prices) {
//动态规划,dp思想
int l1 = prices.length;
int max =0;
int min = prices[0];
for(int i=0;i<l1;i++){
//最大值为 i-1天的最大值 与 i天减之前最小值 的比较
max=Math.max(max,prices[i]-min);
//最小值为 i天及以前的最小值比较
min=Math.min(min,prices[i]);
}
return max;
}
}
124、二叉树中的最大路径和
其中的递归函数中,返回值是:本节点值nums 加上 左右节点的递归函数中的最大值。
在递归函数中,用max记录最大路径和。
class Solution {
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
int a = count(root);
return max;
}
int count(TreeNode root){
if(root == null){
return 0;
}
int num = root.val;
int left = Math.max(count(root.left),0);
int right = Math.max(count(root.right),0);
max = Math.max(max,num+left+right);
return num + Math.max(left,right);
}
}
128、最长连续序列
class Solution {
public int longestConsecutive(int[] nums) {
if(nums.length == 0){
return 0;
}
Arrays.sort(nums);
int count = 1;
int max = 1;
for(int i=1;i<nums.length;i++){
if(nums[i-1] == nums[i]){
continue;
}
if(nums[i-1]+1 == nums[i]){
count++;
max = Math.max(count,max);
} else{
count = 1;
}
}
return max;
}
}
136、只出现一次的数字
异或运算
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int num : nums){
res ^= num;
}
return res;
}
}
139、单词拆分
动态规划。先通过 new HashSet(wordDict) 把字典中的单词写入hashset中,然后建立dp数组,如果dp[i] = true,说明第
i个以及之前的字符都能够得到匹配。
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet = new HashSet(wordDict);
boolean[] dp = new boolean[s.length()+1];
dp[0] = true;
for(int i=1;i<=s.length();i++){
for(int j=0;j<i;j++){
if(dp[j] && wordDictSet.contains(s.substring(j,i))){
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
141、环形链表
这里通过快慢指针来做,快指针走两步,慢指针走一步,如果快慢指针相同,则返回true。
本题还可以通过hashset来做。
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null){
return false;
}
ListNode fast = head.next;
ListNode slow = head;
while(fast != slow){
if(fast == null || fast.next == null){
return false;
}
fast = fast.next.next;
slow = slow.next;
}
return true;
}
}
142、环形链表 II
和上题类似,还要判断入环点的位置。
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode fast = head.next;
ListNode slow = head;
Set<ListNode> hst = new HashSet<>();
while(fast != slow){
if(fast == null || fast.next == null){
return null;
}
hst.add(slow);
fast = fast.next.next;
slow = slow.next;
}
while(!hst.contains(slow)){
hst.add(slow);
slow = slow.next;
}
return slow;
}
}