leetcode
Posted 我们村里的小花儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode相关的知识,希望对你有一定的参考价值。
1、最长有效括号
给定一个只包含 \'(\' 和 \')\' 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
方法一:动态规划
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
int dp[] = new int[s.length()];
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == \')\') {
if (s.charAt(i - 1) == \'(\') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == \'(\') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = Math.max(maxans, dp[i]);
}
}
return maxans;
}
}
思路和算法
撇开方法一提及的动态规划方法,相信大多数人对于这题的第一直觉是找到每个可能的子串后判断它的有效性,但这样的时间复杂度会达到 O(n3)O(n^3)O(n3),无法通过所有测试用例。但是通过栈,我们可以在遍历给定字符串的过程中去判断到目前为止扫描的子串的有效性,同时能得到最长有效括号的长度。
具体做法是我们始终保持栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」,这样的做法主要是考虑了边界条件的处理,栈里其他元素维护左括号的下标:
对于遇到的每个 ‘(’ ,我们将它的下标放入栈中
对于遇到的每个 ‘)’ ,我们先弹出栈顶元素表示匹配了当前右括号:
如果栈为空,说明当前的右括号为没有被匹配的右括号,我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
如果栈不为空,当前右括号的下标减去栈顶元素即为「以该右括号为结尾的最长有效括号的长度」
我们从前往后遍历字符串并更新答案即可。
需要注意的是,如果一开始栈为空,第一个字符为左括号的时候我们会将其放入栈中,这样就不满足提及的「最后一个没有被匹配的右括号的下标」,为了保持统一,我们在一开始的时候往栈中放入一个值为 −1-1−1 的元素。
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
Stack<Integer> stack = new Stack<>();
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == \'(\') {
stack.push(i);
} else {
stack.pop();
if (stack.empty()) {
stack.push(i);
} else {
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;
}
}
2、查询字符串中的最长重复子串
package test1;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class T {
public static void main(String[] args) {
String str = "2018新款夏无袖雪纺女连衣裙连衣裙女装女装中长款休闲显瘦连衣裙修身裙子";
HashMap<String, Integer> map = new HashMap<>();
List<String> dupStr = new ArrayList<>();
int e = str.length();// 比较的
for (int i = e - 1; i > 1; i--) {
for (int s = 0; s < i - 1; s++) {
String sourceStr = str.substring(s, i);
String replaceAll = str.replaceAll(sourceStr, "");
int length = replaceAll.length() + sourceStr.length();
if (e == length) {
// 只存在一个
continue;
} else {
// 重复了,记录下来
if (checkExist(dupStr, sourceStr)) {
map.put(sourceStr, (e - length) / sourceStr.length());
dupStr.add(sourceStr);
}
}
}
}
System.out.println(map + " ");
}
private static boolean checkExist(List<String> dupStr, String sourceStr) {
boolean needRecord = true;
for (String str : dupStr) {
if (str.contains(sourceStr)) {
return false;
}
}
return needRecord;
}
}
运行结果
{连衣裙=2, 女装=1}
3、两个字符串的最大公共子串
package test1;
public class commonString {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s=getMaxSubString("abcdef","defg");
System.out.println(s);
}
// 如,传递的参数为 "abcdef" 和"defg"
public static String getMaxSubString(String maxString,String minString){
String max=null; //并不知道哪个字符串长,哪个字符串短。
String min=null;
//1。 先找到最大的字符串和最小的字符串。 根据长度进行比较
max=maxString.length()>minString.length()? maxString:minString;
min=maxString.equals(max)?minString:maxString;
//2. 求出最小的那个的长度。 根据这个长度,进行相应的循环。
int minLength=min.length();
//3.如果整个包含的话,那个就不用循环判断了。
if(max.contains(min)){
return min;
}
//3.开始进行相关的循环操作了。
for (int i = 0; i <minLength; i++) { //从最小的开始循环。
// 从开头处开始,最后的位置是minLength; 因为subString 截取时不到后面的那个参数,所以这里是<=minLength;
for(int start=0,end=minLength-i;end<=minLength;start++,end++){
/**
* 第一次循环时, 先看整个是否进行了包含,去掉0位,即defg 是否在abcdef 里面。
* 第二次循环时,要把defg 去掉一位,看是否在abcdef 里面。 截取时,有def 和efg 两种。
* 第三次循环时,把defg 去掉两位。 截取有: de ef fg 三种方式。
* 第四次循环时, 把defg 去掉三位,有 d e f g 四种方式。 如果还不存在,则说明没有相同的子串。
* 外层循环 为最小的字符串的长度。 0~ length_1
* 内层循环为: 从0 开始,结束位置为 length-i, 判断是否在,如果不再,则进行整体移动, 为1 ~length-i+1,2~ length-i+2
* 直到 end 结束位置为 length , 因为substring 时不会取出最后的索引值。
*
*/
String temp=min.substring(start,end); //截取的那个。
if(max.contains(temp)){
return temp;
}
}
}
return null;
}
}
4、
非递归实现二叉树先序、中序和后序遍历
用递归方式实现二叉树先序、中序和后序遍历很简单。
用递归方法解决的问题都能用非递归的方法实现。递归就是利用函数栈来保存信息,如果用自己申请的数据结构来代替函数栈,也可以实现相同的功能。
用非递归的方式实现二叉树的先序遍历(LeetCode144):
1、申请一个栈stack,然后将头节点压入stack中。
2、从stack中弹出栈顶节点,打印,再将其右孩子节点(不为空的话)先压入stack中,最后将其左孩子节点(不为空的话)压入stack中。
3、不断重复步骤2,直到stack为空,全部过程结束。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
import java.util.*;
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
Stack<TreeNode> stack=new Stack<TreeNode>();
if (root!=null) {
stack.push(root);
while(!stack.empty()) {
TreeNode tr=stack.pop();
list.add(tr.val);
if(tr.right!=null) {
stack.push(tr.right);
}
if(tr.left!=null) {
stack.push(tr.left);
}
}
}
return list;
}
}
用非递归的方式实现二叉树的中序遍历(LeetCode94):
1、申请一个栈stack,初始时令cur=head
2、先把cur压入栈中,依次把左边界压入栈中,即不停的令cur=cur.left,重复步骤2
3、不断重复2,直到为null,从stack中弹出一个节点,记为node,打印node的值,并令cur=node.right,重复步骤2
4、当stack为空且cur为空时,整个过程停止。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode head) {
List<Integer> list=new ArrayList<Integer>();
Stack<TreeNode> stack=new Stack<TreeNode>();
if (head!=null) {
while(head!=null||!stack.empty()) {
if(head!=null) {
stack.push(head);
head=head.left;
}else {
head=stack.pop();
list.add(head.val);
head=head.right;
}
}
}
return list;
}
}
用非递归的方式实现二叉树的后序遍历(LeetCode145):
用非递归的方式实现后序遍历有点麻烦。
1、申请一个栈s1,然后将头节点压入栈s1中。
2、从s1中弹出的节点记为cur,然后依次将cur的左孩子节点和右孩子节点压入s1中。
3、在整个过程中,每一个从s1中弹出的节点都放进s2中。
4、不断重复步骤2和步骤3,直到s1为空,过程停止。
5、从s2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序。
以上是关于leetcode的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段