剑指offer经典题目(九)

目录

字符串相关

二叉树相关 


字符串相关

        题目1:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列  S ,请你把其循环左移 K 位后的序列输出。例如,字符序列 S = ”abcXYZdef” , 要求输出循环左移 3 位后的结果,即 “XYZdefabc” OJ地址

        图示如下。

        方法一:传统的方法,依次将字符串的每个字符左移。 

        题目解析: 写去实现一个单次左旋的函数,其实就是一个字符串前移的函数,但是要提前将第一个字符保存起来。

        注意:对于一个字符串而言,将这个字符串左旋字符串的长度次之后,其实这个字符串仍然是其本身,所以当左旋的次数大于字符串本身的长度时,就要对旋转的次数取模之后再进行左移。 

        编码如下。

class Solution {
  public:
    //字符串左旋一次
    static void _reverse(string& str) {
        char ch = str[0];
        int i = 0;
        while (i < str.size() - 1) {
            str[i] = str[i + 1];
            i++;
        }
        str[i] = ch;
    }


    string LeftRotateString(string str, int n) {
        if (str.size() == 0 || n == 0) {
            return str;
        }
        //对字符串的字符按照规则进行逆置
        //注意逆置前要先对这个逆置的次数取模,因为左移字符串本身的长度之后,这个字符串还是本身
        int num=n%str.size();
        while (num > 0) {
            _reverse(str);
            num--;
        }
        return str;
    }
};

        这个方法有一个弊端,就是每一次的左旋都伴随着整个字符串的移动,时间复杂度过高,所以我们因此产生了第二种方法。

        方法二:字符串逆置。

        题目解析:左旋 n 次,可以先将前 n 个字符逆置,然后再将剩余的字符逆置,然后再将整体字符进行逆置。 

        编码如下。

class Solution {
  public:
    //逆置字符串
    static void _reverse(string& str, int start, int end) {
        while (start < end) {
            char temp = str[start];
            str[start] = str[end];
            str[end] = temp;
            start++;
            end--;
        }
    }


    string LeftRotateString(string str, int n) {
        if (str.size() == 0 || n == 0) {
            return str;
        }
        //对字符串的字符按照规则进行逆置
        //注意逆置前要先对这个逆置的次数取模,因为左移字符串本身的长度之后,这个字符串还是本身
        int num = n % str.size();
        _reverse(str, 0, num - 1);
        _reverse(str, num, str.size() - 1);
        _reverse(str, 0, str.size() - 1);
        return str;
    }
};

        题目2:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? OJ地址

        图示如下。

        题目解析: 这个题目其实和上一个题目类似,也是一个字符串反转的问题。通过空格将字符串分为了多个子字符串,我们应该先将每个子字符串进行反转,最终将整个字符串进行反转,就会得到最终的结果,所以我们一定要先找 ' ' 空格这个字符。

        代码如下。

class Solution {
  public:
    static void _reverse(string& str, int start, int end) {
        while (start < end) {
            char temp = str[start];
            str[start] = str[end];
            str[end] = temp;
            start++;
            end--;
        }
    }
    string ReverseSentence(string str) {
        if (str.size() == 0) {
            return str;
        }
        int begin = 0;
        int end = begin;
        int i = 0;
        while (i < str.size()) {
            while (i < str.size() && str[i] == ' ') {
                i++;
            }
            begin = i;
            while (i < str.size() && str[i] != ' ') {
                i++;
            }
            end = i - 1 ;
            if (begin < str.size() && end < str.size()) {
                _reverse(str, begin, end);
            }

        }
        //最后对整体字符串进行逆置
        _reverse(str, 0, str.size() - 1);
        return str;
    }
};

二叉树相关 

         题目3:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三 行按照从左到右的顺序打印,其他行以此类推。OJ地址

        图示如下。

        题目解析:这道题目其实和二叉树的层序遍历息息相关,但是又区别于之前的层序遍历。因为之前的层序遍历我们只是将二叉树节点存储在了队列中,但是如果是之字形遍历,我们就不能单单的使用队列来存储节点,而是要使用栈和队列一起存储,队列起了辅助存储的作用,栈起到了之字形遍历的核心功能。 解决这个题目最重要的核心就是,当前层的遍历顺序,就是下一层的入队列和压栈的顺序。

        编码如下。

/**
 * struct TreeNode {
 *  int val;
 *  struct TreeNode *left;
 *  struct TreeNode *right;
 *  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
  public:

    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int> > result;
        if (pRoot == nullptr) {
            return result;
        }
        queue<TreeNode*> q;
        stack<TreeNode*> s;
        //mode为1就是从左往右遍历,否则就是从右往左遍历
        int mode = 1;
        vector<int>v;
        s.push(pRoot);
        //访问栈顶的元素
        while (!s.empty()) {
            while (!s.empty()) {
                TreeNode* node = s.top();
                v.push_back(node->val);
                s.pop();
                //定义从左往右遍历,还是从右往左遍历
                TreeNode* first = (mode == 1 ) ? node->left : node->right;
                TreeNode* second = (mode == 1 ) ? node->right : node->left;
                if (first) {
                    q.push(first);
                }
                if (second) {
                    q.push(second);
                }

            }
            result.push_back(v);
            v.clear();
            mode = (mode == 1) ? 2 : 1;
            //此时将队列中的元素入栈
            while (!q.empty()) {
                s.push(q.front());
                q.pop();
            }
        }
        return result;

    }
};

        题目4:给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。OJ地址

        图示如下。

        题目解析: 其实搜索二叉树是有一个特点的,就是搜索二叉树的中序遍历序列是一个升序序列,所以我们可以先求出搜索二叉树的中序遍历序列,然后根据搜索二叉树的中序遍历序列,求出第 k 小个节点。

         解决这个题目有两种方法,但是两种方法的区别其实仅仅就在于中序遍历方式一个是递归,一个是非递归。 

        方法一:递归法。

/**
 * struct TreeNode {
 *  int val;
 *  struct TreeNode *left;
 *  struct TreeNode *right;
 *  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
  public:
    static void LDR(TreeNode* proot, vector<int>& mid) {
        if (proot == nullptr) {
            return;
        }
        LDR(proot->left, mid);
        mid.push_back(proot->val);
        LDR(proot->right, mid);
    }
    int KthNode(TreeNode* proot, int k) {
        if (proot == nullptr || k == 0) {
            return -1;
        }
        vector<int> mid;
        LDR(proot, mid);
        if (mid.size() < k) {
            return -1;
        }
        return mid[k - 1];
    }
};

        方法二:非递归。

        编码如下。

/**
 * struct TreeNode {
 *  int val;
 *  struct TreeNode *left;
 *  struct TreeNode *right;
 *  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
  public:
    int KthNode(TreeNode* proot, int k) {

        if (proot == nullptr || k == 0) {
            return -1;
        }
        TreeNode* node = proot;
        stack<TreeNode*>s;
        while (!s.empty() || node != nullptr) {
            while (node) {
                s.push(node);
                node = node->left;
            }
            TreeNode* newnode = s.top();
            s.pop();
            k--;
            if (k == 0) {
                return newnode->val;
            }
            node = newnode->right;
        }
        return -1;
    }
};

        以上便是本期的所有内容。

        本期内容到此结束^_^ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

以棠~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值