leetcode算法练习
剑指Offer58-II.左旋转字符串
题目链接
思路:
例如:abcdefg n=2
- 反转区间为前n的子串 ba cdefg
- 然后反转区间为n到末尾的子串 ba gfedc
- 最后反转整个字符串 cdefgab
class Solution {
public String reverseLeftWords(String s, int n) {
int len=s.length();
StringBuilder sb=new StringBuilder(s);
// 反转区间为前n的子串
reverseString(sb,0,n-1);
// 反转区间为n到末尾的子串
reverseString(sb,n,len-1);
// 反转整个字符串 reverse库函数
return sb.reverse().toString();
}
// 反转字符串 从start到end
public void reverseString(StringBuilder sb, int start, int end) {
while (start < end) {
char temp = sb.charAt(start);
// start处设置为end
sb.setCharAt(start, sb.charAt(end));
// end处设置为temp
sb.setCharAt(end, temp);
start++;
end--;
}
}
}
KMP算法学习
KMP主要就是用来解决字符串匹配问题
经典问题:
-
给出文本串:aabaabaaf 模式串:aabaaf 求文本串中是否出现过模式串
-
正常两层for循环遍历 挨个匹配
-
KMP算法:先匹配而后发现不匹配后跳至之前匹配的地方再继续往后匹配
-
为什么跳转至b处——前缀表 帮助我们找到之前已经匹配过的内容!
利用前缀表:f前面的内容都匹配 即aabaa字符串 那么就找该字符串的最长相等前后缀 很明显是aa 为2 即跳到下标为2的地方 即b -
什么是前缀?什么是后缀?
假设aabaaf
前缀:只包含首字母 不包含尾字母的所有字串——即a aa aab aaba aabaa
后缀:只包含尾字母 不包含首字母的所有字串——即f af aaf baaf abaaf
最长相等前后缀:
a 因为不包含首尾字母 所以严格来说 最长相等前后缀是0
aa 是1 因为都是a
aab 0 没有相等前后缀
aaba 1 都是a
aabaa 2 都是aa
aabaaf 0 没有相等前后缀——由此得到前缀表:0 1 0 1 2 0 -
得到前缀表0 1 0 1 2 0如何利用前缀表进行匹配?
-
前缀表内代码中会使用next或prefix数组来表示——在遇到冲突时告诉我们要回退到哪里
-
有些实现会将前缀表0 1 0 1 2 0整体右移 第一位初始为-1或者全部减一
a a b a a f
0 1 0 1 2 0(下面之间利用这种)
-1 0 1 0 1 2 右移
-1 0 -1 0 1 -1 减一
代码实现前缀表:
void getNext(int[] next, String s){
// 初始化 i指向后缀末尾位置 j指向前缀末尾位置以及i之前(包括i)子串的最长相等前后缀长度
// 末尾表示 首个位置 不是语序上的末尾
int j = 0;
next[0] = 0;
// 因为i表示后缀末尾 所以从1开始
for (int i = 1; i<s.length(); i++){
// 处理前后缀不相同的情况 j要找前一位对应的下标
while(j>0 && s.charAt(i) != s.charAt(j)){
// 向前回退 不断回退 直至
j=next[j-1];
}
// 处理前后缀相同的情况 j还表示子串的最长相等前后缀长度 所以j++
if(s.charAt(i)==s.charAt(j)){
j++;
}
// 更新next
next[i] = j;
}
}
232.用栈实现队列
void push(int x)——将元素 x 推到队列的末尾
int pop()——从队列的开头移除并返回元素
int peek()——返回队列开头的元素
boolean empty()——如果队列为空 返回 true 否则返回 false
注意点:
- 利用两个栈:一个保存输入in 一个来输出out 每次有弹出行为的时候 就要把所有输入的元素弹出
// 创建两个栈
Stack<Integer> stackIn = new Stack<>();
Stack<Integer> stackOut = new Stack<>();
// 入队列 将元素x推到队列的末尾
void push(int x){
// 就是 入栈 将元素输入stackIn
stackIn.push(x);
}
// 出队列 并返回元素值 从队列的开头移除并返回元素
int pop(){
// 就是 出栈 要先判断stackOut是否为空
// 如果出栈为空 就所有的stackIn的元素放入stackOut中
// 如果不为空 那就从stackOut里直接拿
if(stackOut.isEmpty()){
while(!stackIn.isEmpty()){
stackOut.push(stackIn.top());
stackIn.pop()
}
}
// 获取元素
result = stackOut.top();
// 弹出元素
stackOut.pop();
return result;
}
// 返回队列开头的元素
// 进行代码的复用 把第一个数值弹出来了 也获取到了值 因为只需要值 所以把该值再加入进去
int peek(){
result = this.pop();
stackOut.push(result);
return result;
}
225. 用队列实现栈
add——增加一个元素——如果队列已满 则抛出一个IIIegaISlabEepeplian异常
remove——移除并返回队列头部的元素——如果队列为空 则抛出一个NoSuchElementException异常
element——返回队列头部的元素——如果队列为空 则抛出一个NoSuchElementException异常
offer——添加一个元素并返回true——如果队列已满 则返回false
poll——移除并返回队列头部的元素——如果队列为空 则返回null
peek——返回队列头部的元素——如果队列为空 则返回null
put——添加一个元素——如果队列满 则阻塞
take——移除并返回队列头部的元素——如果队列为空 则阻塞
drainTo(list)——一次性取出队列所有元素
利用两个队列来实现:
注意点:
- 如果两个都为空那就放队列1
- 如果队列1为空,则放队列1,把队列2的全推到队列1
- 如果队列2为空,则放队列2,把队列1的全推到队列2
class MyStack {
Queue<Integer> queueIn = new LinkedList<>();
Queue<Integer> queueOut = new LinkedList<>();
public MyStack() {
}
public void push(int x) {
if(queueIn.isEmpty() && queueOut.isEmpty()){
queueIn.add(x);
}else{
if(queueIn.isEmpty()){
queueIn.add(x);
while(!queueOut.isEmpty()){
queueIn.add(queueOut.poll());
}
}else{
queueOut.add(x);
while(!queueIn.isEmpty()){
queueOut.add(queueIn.poll());
}
}
}
}
public int pop() {
if(!queueOut.isEmpty()){
return queueOut.poll();
}
return queueIn.poll();
}
public int top() {
if(!queueOut.isEmpty()){
return queueOut.peek();
}
return queueIn.peek();
}
public boolean empty() {
return queueIn.isEmpty() && queueOut.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/