leetcode算法练习——剑指Offer58-II.左旋转字符串 KMP算法学习 232.用栈实现队列 225. 用队列实现栈

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算法:先匹配而后发现不匹配后跳至之前匹配的地方再继续往后匹配
    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

注意点:

  1. 利用两个栈:一个保存输入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
  2. 如果队列1为空,则放队列1,把队列2的全推到队列1
  3. 如果队列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();
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值