[剑指Offer] 第2章课后题详解

[剑指Offer] 第2章课后题详解

目录

  • 剑指Offer 第2章课后题详解
    • 目录
    • 有序数组的插入
      • 分析
      • 正常解法
      • 非主流解法
    • 两个队列实现栈
      • 分析
      • 解法
    • 2的整数次方
      • 分析
      • 解法
    • 不同位数
      • 分析
      • 解法

有序数组的插入

本题为《剑指Offer》“面试题4:替换空格”一节中的“相关题目”。

有两个排序的数组A1和A2,内存在A1的末尾有足够多的空余空间容纳A2。请实现一个函数,把A2中的所有数字插入到A1中并且所有数字是排序的。

分析

其实这道题就是实现一个归并排序,只是在数组中,数据是顺序存储的,如果在数组头部进行归并排序,每一次操作都会移动后面所有的数据,开销很大。所以应该从尾部进行操作。

正常解法

//两个数组的初始实际长度是固定,而数组a2也不需要进行改动,所以都可以声明为常量,进行保护。题目中已经说明空间足够,不然还需要再声明一个参数length,表示a1的维度,与a1,a2的初始长度之和进行比较,以防越界。
auto insertArray(int *a1, const int length1, const int *a2, const int length2) -> void{

    //i,j为a1,a2数组实际末尾下标,k为a1数组中进行归并位置的下标
    int i = length1 - 1, j = length2 - 1, k = length1 + length2 - 1;
    while(i >= 0 && j >= 0){
        //如果a1数组中未处理末尾的数大于或等于a2数组中未处理末尾的数,则将a1的该位置的数插入到a1数组末尾,否则插入a2该位置的数
        if(a1[i] >= a2[j]){
            a1[k--] = a1[i--];
        }
        else{
            a1[k--] = a2[j--];
        }
    }
    //如果数组a2中还有元素没有插入到数组a1中,则全部依次插入到数组a1前面位置
    while(j >= 0){
        a1[k--] = a2[j--];
    }
}

非主流解法

使用标准库中的vector(可变大小数组)和泛型算法会让代码变得相当简单,但相应地算法的时间复杂度会受到影响(因为会进行一次排序)。

//传入两个vector的引用
auto inserVector(vector<int> &a1, vector<int> &a2) -> void{
    //将a2的全部元素插入到a1的尾部
    a1.insert(a1.end(), a2.begin(), a2.end());
    //用泛型算法sort函数对a1进行排序
    sort(a1.begin(),a1.end());
}

两个队列实现栈

本题为《剑指Offer》“面试题7:用两个栈实现队列”一节中的“相关题目”。

栈声明如下:

template <typename T> class CStack{
public:
    void push(const T& node);
    T pop();
private:
    queue<T> queue1;
    queue<T> queue2;
    //标志目前元素存储在哪个队列中,1表示queue1,0表示queue2;
    bool flag = 1;
};

分析

根据队列的先进先出原则和栈的后进先出原则,可以进行如下设计:先将入栈元素依次加入到队列1中,出栈时,将队列1除队尾元素外依次加入到队列2中,再弹出队列1中的最后一个元素(队尾元素)。再有元素入栈时,就加入到队列2中,出栈时,将队列2除队尾元素外依次加入到队列1中,再弹出队列2中的最后一个元素(队尾元素)。之后元素入栈,又加入到队列1中,依此类推。

解法

template <typename T> void CStack<T>::push(const T& node){
    if (flag) {
        queue1.push(node);
    }
    else{
        queue2.push(node);
    }
}

template <typename T> T CStack<T>::pop(){
    if (flag) {
        //标志为1,队列1为空时,栈为空
        if (queue1.empty()) {
            throw new runtime_error("stack is empty");
        }
        //将队列1除队尾元素外依次加入到队列2中
        while (queue1.size() > 1) {
            T& data = queue1.front();
            queue1.pop();
            queue2.push(data);
        }
        //修改标志
        flag = 0;
        //弹出并返回队尾元素
        T head = queue1.front();
        queue1.pop();
        return head;
    }
    else{
        //标志为0,队列2为空时,栈为空
        if (queue2.empty()) {
            throw new runtime_error("stack is empty");
        }
        while (queue2.size() > 1) {
            T& data = queue2.front();
            queue2.pop();
            queue1.push(data);
        }
        flag = 1;

        T head = queue2.front();
        queue2.pop();

        return head;
    }
}

2的整数次方

本题为《剑指Offer》“面试题10:二进制中1的个数”一节中的“相关题目”。

用一条语句判断一个整数是不是2的整数次方。

分析

一个整数如果是2的整数次方,那么它的二进制表示中有且只有一位是1,而其他所有位都是0。把这个数减去1之后,原本为1的那一位会变成0,而在这一位之后的所有0都会变成1。把这个整数与它减一后的数做与运算则结果为0。

解法

auto isCubeOf2(const int n) -> bool{
    return !(n & (n - 1));
}

不同位数

本题为《剑指Offer》“面试题10:二进制中1的个数”一节中的“相关题目”。

输入两个整数m和n,计算需要改变m的二进制中多少位才能得到n。

分析

问题等价于m和n中有多少位不同,所以将m和n进行异或运算再计算结果二进制中的1的个数就完成求解。

解法

auto differentNumber(const int m, const int n) -> int{
    //tmp为m和n的异或结果
    int tmp = m ^ n;
    int count = 0;
    unsigned flag = 1;

    //依次检查tmp每一位是否为1
    while(flag){
        if(flag & tmp){
            ++count;
        }
        flag = flag << 1;
    }
    return count;
}
时间: 2024-12-27 10:37:15

[剑指Offer] 第2章课后题详解的相关文章

[剑指Offer] 第4章课后题详解

[剑指Offer] 第4章课后题详解 目录 剑指Offer 第4章课后题详解 目录 二叉树的镜像 分析 解法 拓展 判断前序遍历 分析 解法 拓展 字符的组合 分析 解答 正方体的顶点 分析 解法 8个皇后 分析 解法 二叉树的镜像 本题为<剑指Offer>"面试题19:二叉树的镜像"一节中的"本题拓展". 请完成一个函数,输入一个二叉树,该函数输出它的镜像,要求使用循环的方法,不能用递归.二叉树节点定义如下: struct BinaryTreeNode

[剑指Offer] 第5章课后题详解

[剑指Offer] 第5章课后题详解 目录 剑指Offer 第5章课后题详解 目录 删除指定字符 分析 解法 优化 删除重复元素 分析 解法 判断变位词 分析 解法 求助 删除指定字符 本题为<剑指Offer>"面试题35:第一个只出现一次的字符"一节中的"相关题目". 定义一个函数,输入两个字符串,从第一个字符串中删除在第二个字符串中出现过的所有字符. 分析 字符是一个长度为8的数据类型,共256种可能.创建一个长度为256的bool型数组,数组下标为

[剑指Offer] 第3章课后题详解

[剑指Offer] 第3章课后题详解 目录 剑指Offer 第3章课后题详解 目录 大数加法 分析 解法 优化 链表的中间节点 分析 解法 环形链表 分析 解法 反转链表 分析 解法 大数加法 本题为<剑指Offer>"面试题12:打印1到最大的n位数"一节中的"相关题目". 定义一个函数,在该函数中可以实现任意两个整数的加法. 分析 由于没有限定输入两个数的大小范围,所以需要把它当做大数问题来处理.大数无法用int,long甚至long long类型来

剑指offer:顺时针打印矩阵

剑指offer上的第20题,九度OJ上测试通过. 题目描述: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10. 输入: 输入可能包含多个测试样例,对于每个测试案例, 输入的第一行包括两个整数m和n(1<=m,n<=1000):表示矩阵的维数为m行n列. 返回栏目页:http://www

剑指offer:包含min函数的栈

剑指offer上的第21题,之前在Cracking the Coding interview上做过,思路参考这里,这次写了测试函数,在九度OJ上测试通过. 题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数. 输入: 输入可能包含多个测试样例,输入以EOF结束. 对于每个测试案例,输入的第一行为一个整数n(1<=n<=1000000), n代表将要输入的操作的步骤数. 接下来有n行,每行开始有一个字母Ci. Ci='s'时,接下有一个数字k,代表将k压入栈. Ci

剑指offer:栈的压入弹出序列

剑指offer上的第22题,九度OJ上AC. 题目描述: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列. 输入: 每个测试案例包括3行: 第一行为1个整数n(1<=n<=100000),表示序列的长度. 第二行包含n个整数,表示栈的压入顺序. 第三行包含n个整数,表示栈的弹出顺序

剑指Offer详解之左旋转字符串

(1)暴力移位法 这种方法可能是最直观,最容易想出的方法.但这也是最坏的方法.时间复杂度挺高,用这种方法容易超时. 这种方法是一位一位的移动实现左旋转操作. [代码] #include <stdio.h> #include <malloc.h> #include <string.h> char *str; char* Reverse(char* str,int n){ if(str == NULL || n < 0){ return ""; }

剑指offer系列之二十六:输出字符串的全排列

题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 结果请按字母顺序输出. 输入描述: 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母. 此题与剑指offer原题存在一些初入,在原题中并没有规定字符串中字符是否有重复的出现.不过思路是一致的,只不过是添加额外的判断罢了.下面说说我的思路吧:先不考虑是否出现重读字符,要对一个字符进行全排列,可以

[剑指Offer]7.从尾到头打印链表

题目1511:从尾到头打印链表 时间限制:1 秒 内存限制:128 兆 特殊判题:否 提交:1082 解决:350 题目描述: 输入一个链表,从尾到头打印链表每个节点的值. 输入: 每个输入文件仅包含一组测试样例. 每一组测试案例包含多行,每行一个大于0的整数,代表一个链表的节点.第一行是链表第一个节点的值,依次类推.当输入到-1时代表链表输入完毕.-1本身不属于链表. 输出: 对应每个测试案例,以从尾到头的顺序输出链表每个节点的值,每个值占一行. 样例输入: 1 2 3 4 5 -1 样例输出