1.输入字符串检查括号是否匹配
import java.util.Stack;
public class Main{
public static viod main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
String s = sc.next();
System.out.println(Match(s));
}
}
private static boolean Match(String s){
Stack<Character> stack = new Stack<>();
boolean flag = true;
char[] temp = s.toCharArray();
for(char ch : temp){
if(ch == '(' || ch == '{' || ch == '['){
stack.push(ch);
}else{
if(!stack.isEmpty()) {
Character c = stack.peep();
if(isMatch(c,ch)){
stack.pop();
}else{
flag = false;
break;
}
}else{
flag = false;
break;
}
}
}
}
if(stack.size() != 0){
flag = false;
break;
}
private static boolean isMatch(char c ,char ch){
if(ch == '(' && c == ')'){
} return true;
if(ch == '{' && c == '}'){
return true;
}
if(ch == '[' && c == ']'){
return true;
}
return false;
}
2.单例:
懒汉式:
2.饿汉式
3.双检锁
4.静态内部类
5.无序数组中最长的递增子序列
class Solution {
public int findLengthOfLCIS(int[] nums) {
int ans = 0, anchor = 0;
for (int i = 0; i < nums.length; ++i) {
if (i > 0 && nums[i-1] >= nums[i]) anchor = i;
ans = Math.max(ans, i - anchor + 1);
}
return ans;
}
6.判断字符是否是回文串
思路其实是从中间外一直延伸,当左右两个边界相等的时候,就进行记录当前的字符串的长度。
class Solution {
public String longestPalindrome(String s) {
if(s.length() == 0)return s;
int max = 1;
int left = 0;//记录字符串左边
int right = 0;//记录字符串右边
for(int i = 0;i < s.length();i ++) {
int l = i - 1;//奇数情况
int r = i + 1;
while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r) ) {
int len = (r - l + 1);
if(len > max) {
max = len;
left = l;
right = r;
}
l --;
r ++;
}
//偶数情况
l = i;
r = i + 1;
while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r) ) {
int len = (r - l + 1);
if(len > max) {
max = len;
left = l;
right = r;
}
l --;
r ++;
}
}
return s.substring(left,right + 1);
}
}
7.无重复子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> map = new HashMap<>();
int ans = 0;
for(int i = 0,j = 0;j < s.length(); j ++){
if(map.containsKey(s.charAt(j))){
i = Math.max(map.get(s.charAt(j)),i);
}
ans = Math.max(ans,j - i + 1);
map.put(s.charAt(j),j + 1);
}
return ans;
}
}
8.一个二维字符矩阵
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
思路:使用深度优先遍历算法,把二维数组,字符串数组,下表,k是字符数组下标,而i,j是二维数组下标。
标记当前矩阵元素: 将 board[i][j] 值暂存于变量 tmp ,并修改为字符 ‘/’ ,代表此元素已访问过,防止之后搜索时重复访问。
搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 (代表只需一条可行路径) ,并记录结果至 res 。
还原当前矩阵元素: 将 tmp 暂存值还原至 board[i][j] 元素。
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 || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
if(k == word.length - 1) return true;
char tmp = board[i][j];
board[i][j] = '/';
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] = tmp;
return res;
}
}
M,N 分别为矩阵行列大小, KK 为字符串 word 长度。
时间复杂度:
设字符串长度为 KK ,搜索中每个字符有上、下、左、右四个方向可以选择,舍弃回头(上个字符)的方向,剩下 33 种选择,因此方案数的复杂度为 O(3^K)
空间复杂度:O(K)
9.驼峰字符串
(1)将驼峰命名转为小写下划线命名
public static String underscoreName(String camelCaseName) {
StringBuilder result = new StringBuilder();
if (camelCaseName != null && camelCaseName.length() > 0) {
result.append(camelCaseName.substring(0, 1).toLowerCase());
for (int i = 1; i < camelCaseName.length(); i++) {
char ch = camelCaseName.charAt(i);
if (Character.isUpperCase(ch)) {
result.append("_");
result.append(Character.toLowerCase(ch));
} else {
result.append(ch);
}
}
}
return result.toString();
}
下划线转驼峰 user_name
public static String camelCaseName(String underscoreName) {
StringBuilder result = new StringBuilder();
if (underscoreName != null && underscoreName.length() > 0) {
boolean flag = false;
for (int i = 0; i < underscoreName.length(); i++) {
char ch = underscoreName.charAt(i);
if ("_".charAt(0) == ch) {
flag = true;
} else {
if (flag) {
result.append(Character.toUpperCase(ch));
flag = false;
} else {
result.append(ch);
}
}
}
}
return result.toString();
}
(2)驼峰字符串匹配
时间复杂度:O(M+N*K)
设quriesquries数组长度为N,字符串总长度为M,pattrenpattren的长度为K
空间复杂度:O(1)O(1) 不包含返回值数组所占用空间
思路:
开始分别用两个指针idx1和idx2指向query和pattrn的头部。
判断两个指针所指向的字符是否相等,如果相等则同时后移
若不相等,则idx1的指针不断后移,直到走到query字符串末尾或和idx2指向的字符相等。注意根据题意我们只能插入小写字母,故idx1后移的过程中若遇到不匹配大写字母,则立即返回false。
当指针idx1或idx2走到字符串的末尾时退出循环,这时我们要判断idx2是否走到了pattrn字符串的末尾,如果不是,说明我们还有剩余字符未成功匹配,返回false。同时我也要判断query中剩余的字符中是否全是小写字母,若不是返回false。
class Solution {
public List<Boolean> camelMatch(String[] queries, String pattern) {
List<Boolean> ans = new ArrayList<>();
for (String query : queries)
ans.add(isMatch(query, pattern));
return ans;
}
private boolean isMatch(String query, String pattern) {
int idx1 = 0, idx2 = 0, n1 = query.length(), n2 = pattern.length();
while (idx1 < n1 && idx2 < n2){
char ch1 = query.charAt(idx1), ch2 = pattern.charAt(idx2);
if (ch1 == ch2) {
idx1++;idx2++;
} else {
if (ch1 >= 'A' && ch1 <= 'Z') return false;
idx1++;
}
}
if (idx2 != n2) return false;
while (idx1 < n1) {
char ch1 = query.charAt(idx1++);
if (ch1 >= 'A' && ch1 <= 'Z') return false;
}
return true;
}
}
10.找出数组中出现次数长度超过一半的那个数
class Solution {
public int majorityElement(int[] nums) {
ArrayList<Integer> list = new ArrayList<>();
int count = 0;
int len = nums.length;
int i;
Arrays.sort(nums);
for ( i = 0; i < nums.length - 1; i++) {
if (!list.contains(nums[i]))
list.add(nums[i]);
count ++;
if (nums[i] != nums[i + 1]) {
if (count > len / 2) {
break;
} else {
list.clear();
}
}
}
return nums[i];
}
}
解题思路:
1.哈希表统计法: 遍历数组 nums ,用 HashMap 统计各数字的数量,最终超过数组长度一半的数字则为众数。此方法时间和空间复杂度均为 O(N)。
2.数组排序法: 将数组 nums 排序,由于众数的数量超过数组长度一半,因此 数组中点的元素 一定为众数。此方法时间复杂度 O(N log_2 N)。
3.摩尔投票法: 核心理念为 “正负抵消” ;时间和空间复杂度分别为 O(N) 和 O(1) ;是本题的最佳解法。
假设第一个数字是众数,把第一过数字赋值给x,如果现在这个数与默认众数相等,则votes进行++,否则votes进行–。
class Solution {
public int majorityElement(int[] nums) {
int x = 0, votes = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
// 验证 x 是否为众数
for(int num : nums)
if(num == x) count++;
return count > nums.length / 2 ? x : 0; // 当无众数时返回 0
}
return x;
}
}
11.删除数组重复元素
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
12.两个栈实现一个队列
思路:先将数据存到第一个栈里,再将第一个栈里的元素全部出栈到第二个栈,第二个栈出栈,即可达到先进先出。
import java.util.Stack;
public class TwoStacksQueue {
public Stack<Integer> stackPush;
public Stack<Integer> stackPop;
public TwoStacksQueue() {
stackPush = new Stack<Integer>();
stackPop = new Stack<Integer>();
}
//元素进栈
public void add(int pushInt) {
stackPush.push(pushInt);
}
public int poll() {
if (stackPop.empty() && stackPush.empty()) {
throw new RuntimeException("Queue is empty!");
} else if (stackPop.empty()) {
while (!stackPush.empty()) {
stackPop.push(stackPush.pop());
}
}
return stackPop.pop();
}
//和poll效果一样
// public int peek() {
// if (stackPop.empty() && stackPush.empty()) {
// throw new RuntimeException("Queue is empty!");
// } else if (stackPop.empty()) {
// while (!stackPush.empty()) {
// stackPop.push(stackPush.pop());
// }
// }
// return stackPop.peek();
// }
public static void main(String[] args) {
TwoStacksQueue queue = new TwoStacksQueue();
queue.add(2);
queue.add(4);
queue.add(7);
queue.add(13);
System.out.println(queue.poll());
System.out.println(queue.poll());
}
}
13.两个队列实现一个栈
数据的插入原则:保持一个队列为空,一个队列不为空,往不为空的队列中插入元素
import java.util.LinkedList;
import java.util.Queue;
/**
* 两个队列实现一个栈
*
* 一个队列加入元素,弹出元素时,需要把队列中的 元素放到另外一个队列中,删除最后一个元素
* 两个队列始终保持只有一个队列是有数据的
*
*/
public class StackByQueue<T> {
private Queue<T> queue1 = new LinkedList<>();
private Queue<T> queue2 = new LinkedList<>();
/**
* 压栈
*
* 入栈非空的队列
*/
public boolean push(T t) {
if (!queue1.isEmpty()) {
return queue1.offer(t);//插入数据
} else {
return queue2.offer(t);
}
}
/**
* 弹出并删除元素
*/
public T pop() {
if (queue1.isEmpty() && queue2.isEmpty()) {
throw new RuntimeException("queue is empty");
}
if (!queue1.isEmpty() && queue2.isEmpty()) {
while (queue1.size() > 1) {
queue2.offer(queue1.poll());
}
return queue1.poll();//先入的后出
}
if (queue1.isEmpty() && !queue2.isEmpty()) {
while (queue2.size() > 1) {
queue1.offer(queue2.poll());
}
return queue2.poll();
}
return null;
}
@Override
public String toString() {
return this.queue1.toString() + ", " +this.queue2.toString();
}
public static void main(String[] args) {
StackByQueue<Integer> s = new StackByQueue<>();
s.push(1);
s.push(2);
s.push(3);
s.pop();
System.out.println(s);
s.push(4);
s.push(5);
s.pop();
System.out.println(s);
}
}
14.输出一个集合的所有子集合
找出一个集合的所有子集合,用排列组合的方式来解答此问题的话,假设集合里面有n个元素,那个子集合的数目为2^n.
具体思路为:对于集合里面的任何一个元素,有两种可能,一种是在子集合里,另一种是不在子集合里。假如我们已经得到n-1个元素的子集合,那么n个元素的子集合是:n-1个元素的子集合 + n-1个元素的子集合中的所有集合都添加进额外的那个元素。
static ArrayList<ArrayList<Integer>> getSubsets(ArrayList<Integer> set, int index){
ArrayList<ArrayList<Integer>> allsubsets;
if(set.size() == index){
allsubsets = new ArrayList<ArrayList<Integer>>();
allsubsets.add(new ArrayList<Integer>()); //empty set
}else{
allsubsets = getSubsets(set, index+1);
int item = set.get(index);
ArrayList<ArrayList<Integer>> moresubsets = new ArrayList<ArrayList<Integer>>();
for(ArrayList<Integer> s: allsubsets){
ArrayList<Integer> newSubset = new ArrayList<Integer>();
newSubset.addAll(s);
newSubset.add(item);
moresubsets.add(newSubset);
}
allsubsets.addAll(moresubsets);
}
return allsubsets;
}
public static void main(String[] args){
ArrayList<Integer> s = new ArrayList<Integer>();
s.add(1);
s.add(2);
s.add(3);
ArrayList<ArrayList<Integer>> allsubsets = getSubsets(s, 0);
for(ArrayList<Integer> set : allsubsets){
System.out.println(set);
}
15.扑克牌中的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
简单来说就是要是5个数字,最大和最小差值在5以内,并且没有重复数值。用一个set来填充数据,0不要放进去。set的大小加上0的个数必须为5个。此外set中数值差值在5以内。
import java.util.TreeSet;
public class Solution {
public boolean isContinuous(int [] n) {
if (n.length < 5 || n.length > 5) {
return false;
}
int num = 0;
TreeSet<Integer> set = new TreeSet<> ();
for (int i=0; i<n.length;i++) {
if (n[i]==0) {
num ++;
} else {
set.add(n[i]);
}
}
if ((num + set.size()) != 5) {
return false;
}
if ((set.last() - set.first()) < 5) {
return true;
}
return false;
}
}
16.链表逆序:
17.输出比给定数大的下一个数字
可以使用字典排序
第一步先找到num[i +1] < num[i]
第二步确定反转的起点i,并且又从后开始往前遍历,找到,nums[j] < nums[i]
第三步就是把第一步与第二步确定位置的数字进行反转
public class Solution {
public void nextPermutation(int[] nums) {
int i = nums.length - 2;
while (i >= 0 && nums[i + 1] <= nums[i]) {
i--;
}
if (i >= 0) {
int j = nums.length - 1;
while (j >= 0 && nums[j] <= nums[i]) {
j--;
}
swap(nums, i, j);
}
reverse(nums, i + 1);
}
private void reverse(int[] nums, int start) {
int i = start, j = nums.length - 1;
while (i < j) {
swap(nums, i, j);
i++;
j--;
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
18.两个长字符串数字求和
思路:我们直接计算是不行的,需要每一位字符一个一个相加,相加之后在一位一位添加到一个新字符串中,但是如果说两个大于5的数值相加,就会溢出一位,相应的上一位就应该加1,所以说我们需要将两个字符串反转,从小到大一个一个的加,溢出之后,在进位上加1。
StringBuffer s1 = new StringBuffer(str1).reverse();
StringBuffer s2 = new StringBuffer(str2).reverse();
StringBuffer res = new StringBuffer();
int len1 = s1.length();
int len2 = s2.length();
int len;
if (len1 < len2) {
len = len2;
int count = len2 - len1;
while (count-- > 0)
s1.append('0');
} else {
len = len1;
int count = len1 - len2;
while (count-- > 0)
s2.append('0');
}
int overflow = 0;
int num;
for (int i = 0; i < len; i++) {
num = s1.charAt(i) - '0' + s2.charAt(i) - '0' + overflow;
if (num >= 10) {
overflow = 1;
num -= 10;
} else {
overflow = 0;
}
res.append(String.valueOf(num));
}
if (overflow == 1)
res.append(1);
return res.reverse().toString();
19.求两个正整数的最小公倍数