【笔记】剑指offer

剑指offer&LeetCode

LeetCode All in One 题目讲解汇总(持续更新中…)【要是来不及直接刷题解吧hhh】
https://www.cnblogs.com/grandyang/p/4606334.html
Leetcode 分类顺序表第二版(增加重点250题)
https://cspiration.com/leetcodeClassification#103

反转问题

从头到尾打印链表

[Leetcode] Implement Stack using Queues 用队列实现栈

https://segmentfault.com/a/1190000003808785
复杂度
时间 O(N) 空间 O(N)

思路
和Implement Queue using Stack类似,我们也可以用两个队列来模拟栈的操作。当push时,我们将数字offer进非空的队列就行了。当pop时,因为要拿的是队列最后一个数,我们先将它前面的数offer进另一个队列,然后单独拿出最后一个数,并且不再offer进另一个队列,这样,另一个队列就少了最后一个数,而我们也拿到了最后一个数,而本来的队列就变成空了,等待下次的pop操作来填满。top操作和pop一样,区别在于我们拿到最后一个数后,还要再把它offer进另一个队列中。

class MyStack {
    
    Queue<Integer> queue1;
    Queue<Integer> queue2;
    int size;
    
    public MyStack(){
        this.queue1 = new LinkedList<Integer>();
        this.queue2 = new LinkedList<Integer>();
        this.size = 0;
    }
    
    // Push element x onto stack.
    public void push(int x) {
        if(queue1.isEmpty()){
            queue2.offer(x);
        } else {
            queue1.offer(x);
        }
        size++;
    }

    // Removes the element on top of the stack.
    public int pop() {
        if(size == 0){
            return -1;
        }
        int res = 0;
        // 将前面的数都offer进另一个队列,然后拿出最后一个数
        if(queue1.isEmpty()){
            for(int i = 0; i < size - 1; i++){
                queue1.offer(queue2.poll());
            }
            res = queue2.poll();
        } else {
            for(int i = 0; i < size - 1; i++){
                queue2.offer(queue1.poll());
            }
            res = queue1.poll();
        }
        size--;
        return res;
    }

    // Get the top element.
    public int top() {
        if(size == 0){
            return -1;
        }
        // 先执行pop
        int top = pop();
        // 然后将pop出来的数再塞回队列就行了
        if(queue1.isEmpty()){
            queue2.offer(top);
        } else {
            queue1.offer(top);
        }
        // 注意size也要加1
        size++;
        return top;
    }

    // Return whether the stack is empty.
    public boolean empty() {
        return size == 0;
    }
}

编辑距离及编辑距离算法

问题:找出字符串的编辑距离,即把一个字符串s1最少经过多少步操作变成编程字符串s2,操作有三种,添加一个字符,删除一个字符,修改一个字符
http://www.cnblogs.com/biyeymyhjob/archive/2012/09/28/2707343.html
显然可以有如下动态规划公式:

if i == 0 且 j == 0,edit(i, j) = 0
if i == 0 且 j > 0,edit(i, j) = j
if i > 0 且j == 0,edit(i, j) = i
if i ≥ 1  且 j ≥ 1 ,edit(i, j) == min{ edit(i-1, j) + 1, edit(i, j-1) + 1, edit(i-1, j-1) + f(i, j) },
当第一个字符串的第i个字符不等于第二个字符串的第j个字符时,f(i, j) = 1;否则,f(i, j) = 0。

在这里插入图片描述
在这里插入图片描述

LeetCode 222. 完全二叉树的节点个数

https://blog.youkuaiyun.com/sinat_15723179/article/details/80937378

题目描述:

给出一个完全二叉树,求出该树的节点个数。

说明:

完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例:

输入:

    1
   / \
  2   3
 / \  /
4  5 6
输出: 6

思路:这个解题方法是来自于leetCode上一个解法。主要思想是计算出树的高度,然后统计出叶子节点的数目,然后利用公式 nodenums=2^(height-1)-1+叶子节点数目,就可以计算出来了。这里统计叶子节点数目的时候,利用了二分思想,每次都判断当前节点的左子树中的最右子节点是否存在,如果是叶子节点,说明以这个左子树是满二叉树,可以利用公式把这个满二叉树的叶子节点计算出来。如果当前的叶子节点不存在的话,就继续在这个节点的左子树中找,因为既然这个左子树都已经无法构成满二叉树了,和这个子树处同一层的右子树也不可能是满二叉树(完全二叉树的性质)。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int getHeight(TreeNode *root)
    {
        int height = 1;
        while(root->left != NULL)
        { 
            height++; 
            root = root->left;}
        return height;
    }
  
    int countNodes(TreeNode* root) {
        if(root == NULL) 
            return 0;
        
        int height = getHeight(root);
        
        //利用变形的二分法来计算叶子节点的个数
        int leafCnt=0;
        int level=height-2;
        TreeNode *cur=root;
        while(level>=0)
        {
            TreeNode *temp=cur->left;
            for(int i=0; i<level;++i)
                temp=temp->right;//找到最后一个right
            if(temp){//判断是否存在,如果存在这一层前半部分的叶子节点肯定是存在的,然后就可以从当前cur的右边看右边是否存在叶子节点
                leafCnt+=1<<level;
                cur=cur->right;
            } 
            else
                cur=cur->left;
            
            level--;
        }
        
        if(cur)//是否存在最后一个单独的节点
            leafCnt++;
        
        return (1<<height-1)-1+leafCnt;//左移和右移运算符优先级很低,注意加括号
    }
 
};
--------------------- 
作者:一个假程序媛 
来源:优快云 
原文:https://blog.csdn.net/sinat_15723179/article/details/80937378 
版权声明:本文为博主原创文章,转载请附上博文链接!

[LeetCode] Implement Trie (Prefix Tree) 实现字典树(前缀树)

http://www.cnblogs.com/grandyang/p/4491665.html【强推点一点!这个博主好腻害!有很多】

题目描述

Implement a trie with insert, search, and startsWith methods.
Note:
You may assume that all inputs are consist of lowercase letters a-z.

解析:

这道题让我们实现一个重要但又有些复杂的数据结构-字典树, 又称前缀树或单词查找树,详细介绍可以参见网友董的博客,例如,一个保存了8个键的trie结构,“A”, “to”, “tea”, “ted”, “ten”, “i”, “in”, and “inn”.如下图所示:

在这里插入图片描述

字典树主要有如下三点性质:

  1. 根节点不包含字符,除根节点意外每个节点只包含一个字符。

  2. 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。

  3. 每个节点的所有子节点包含的字符串不相同。

字母树的插入(Insert)、删除( Delete)和查找(Find)都非常简单,用一个一重循环即可,即第i 次循环找到前i 个字母所对应的子树,然后进行相应的操作。实现这棵字母树,我们用最常见的数组保存(静态开辟内存)即可,当然也可以开动态的指针类型(动态开辟内存)。至于结点对儿子的指向,一般有三种方法:

1、对每个结点开一个字母集大小的数组,对应的下标是儿子所表示的字母,内容则是这个儿子对应在大数组上的位置,即标号;

2、对每个结点挂一个链表,按一定顺序记录每个儿子是谁;

3、使用左儿子右兄弟表示法记录这棵树。

三种方法,各有特点。第一种易实现,但实际的空间要求较大;第二种,较易实现,空间要求相对较小,但比较费时;第三种,空间要求最小,但相对费时且不易写。

class TrieNode {
public:
    // Initialize your data structure here.
    TrieNode *child[26];
    bool isWord;
    TrieNode() : isWord(false){
        for (auto &a : child) a = NULL;
    }
};

class Trie {
public:
    Trie() {
        root = new TrieNode();
    }

    // Inserts a word into the trie.
    void insert(string s) {
        TrieNode *p = root;
        for (auto &a : s) {
            int i = a - 'a';
            if (!p->child[i]) p->child[i] = new TrieNode();
            p = p->child[i];
        }
        p->isWord = true;
    }

    // Returns if the word is in the trie.
    bool search(string key) {
        TrieNode *p = root;
        for (auto &a : key) {
            int i = a - 'a';
            if (!p->child[i]) return false;
            p = p->child[i];
        }
        return p->isWord;
    }

    // Returns if there is any word in the trie
    // that starts with the given prefix.
    bool startsWith(string prefix) {
        TrieNode *p = root;
        for (auto &a : prefix) {
            int i = a - 'a';
            if (!p->child[i]) return false;
            p = p->child[i];
        }
        return true;
    }

private:
    TrieNode* root;
};

code jam

2017

Problem A. Oversized Pancake Flipper

https://code.google.com/codejam/contest/dashboard?c=3264486#s=p0&a=0
反转煎饼,每次能且只能反转K块
在这里插入图片描述
题解:
https://code.google.com/codejam/contest/dashboard?c=3264486#s=a&a=0
Scoreboard:
https://code.google.com/codejam/contest/scoreboard?c=3264486#vf=1
在这里插入图片描述
异或的意思是 按位比较,位相同(0,0或1,1)时,结果为0,位不同时(0,1或1,0)结果为1
所以,0异或任何数A 结果还是A
A异或1时,A的最后一位是1,则会变为0,如果是0,则会变为1,因此,结果一定是非A

#include <bits/stdc++.h>

using namespace std;

int N, K;
char S[10000];

void _main(int TEST)
{
    scanf("%s%d", S, &K);
    N=strlen(S);
    int ans=0;
    for(int i=0; i+K-1<N; i++) if(S[i]=='-')
    {
        for(int j=0; j<K; j++)
            S[i+j]^='-'^'+';  // change to '+';
        ans++;
    }
    for(int i=0; i<N; i++) if(S[i]=='-')//if still remain '-', failed.
        ans=-1;
    if(ans==-1)
        printf("IMPOSSIBLE\n");
    else
        printf("%d\n", ans);
}

int main()
{
    freopen("A-small.in", "r", stdin);
    freopen("A-small.out", "w", stdout);
   	// freopen("A-large.in", "r", stdin);
    // freopen("A-large.out", "w", stdout);
    int TEST;
    scanf("%d", &TEST);
    for(int i=1; i<=TEST; i++)
    {
        //cerr << i << endl;
        printf("Case #%d: ", i);
        _main(i);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值