剑指offer解题思路锦集11-20题

又来更新剑指offer上的题目思路啦。

11、【二进制中1的个数】

题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

eg:NumberOf1(1)=1
NumberOf1(2)=0
NumberOf1(3)=2
NumberOf1(4)=1
NumberOf1(5)=2
NumberOf1(6)=2
NumberOf1(7)=3

思路:每次都将数字n的最后一位1反转成0,不断反转到这个数字变成0,然后我们统计反转了多少次,这样不就可以成功得到这个数字有多少位了吗?

难点:如何反转一个数字的最后一位1,解决方案如下:

n = n & (n - 1);

代码:

     int  NumberOf1(int n) {
         int number = 0;
         while (n != 0)
         {
         	n = n & (n - 1);
         	++number;
         }
         return number;
     }

思路2:可不可以尝试不去改变这个数字呢?答案也可以的。假设是int类型,如果我循环32次,判断每个位是否为1,这样不也可以得到这个数字有多少位为1吗?诚然,这样也是存在着缺点的。

缺点:比思路1的时间复杂度略高,不过也是常数级别的,因为数字的位数是有限制的,两者可能系数上存在着差别吧!~~~还有一个缺点,就是这样的代码移植性较差~~

存在坑:

(temp & n) != 0

这里的括号不能去掉,因为&的优先级比 != 低。。。。

代码:

     int  NumberOf1(int n) {
         int number = 0;
         int temp = 1;
         for (int i = 0; i < 32; ++i)
         {
             if ((temp & n) != 0)
             {
                 ++number;
             }
             temp = temp<<1;
         }
         return number;
     }

12、【数值的整数次方】

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

例子:2^10

思路1:2*2*2*2*2*2*2*2*2*2,循环N次即可。时间复杂度O(n)没啥好说的,跳过。

思路2: 分治想法,假设是求a^n

  如果n为偶数,那么转化成求x=a^(n/2),然后返回x*x即可。

  如果n为奇数,那么转化成求x=a^(n-1/2),然后返回x*x*a即可。

  时间复杂度是O(lgn)。。。怎么证明?

应用算法导论中的主方法即可。

这里,有T(n) = T(n/2) + 1,即 b = 2, a = 1,f(n)=1。因此满足条件2,因此时间复杂度为O(nlogb(a))=O(nlog2(1)*lgn)=O(1*lgn)=O(lgn)。

哈哈,是不是瞬间感觉写难了。。。。

13、【调整数组顺序使奇数位于偶数前面】

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

思路:两个数组,一个按顺序保存奇数,另一个按顺序保存偶数。。。时间复杂度O(N),空间复杂度O(N)。

思路2:创建一个数组,循环2次,第一次只扫描奇数,丢进数组中。第二次只扫描偶数,丢进数组。。十分简单。。。

思路3:类似冒泡算法,前偶后奇数就交换。

    void reOrderArray(vector<int> &arr)
    {//双指针
        int n = arr.size();
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n-i-1; ++j)
            {
                if(arr[j]%2 == 0 && arr[j+1]%2 == 1) swap(arr[j],arr[j+1]);
            }
        }
    }

思路4:两个指针咯,一个指针1从头到尾,另外一个指针2从尾到头,如果指针1发现偶数,那么停下来,如果指针2发现奇数,那么也停下来。再交换两个指针指向的数就Ok了。

   问题:这样是真的可以吗?答案是不可以的哦~哈哈,因为这样违反了题目要求——要求奇数和偶数顺序不能改变。。。嗯,是的,如果是让顺序可以随意的话,那么这就是很nice的解。

14、【链表中倒数第k个结点】

题目:输入一个链表,输出该链表中倒数第k个结点。

思路1:遍历得到链表长度n,然后再遍历一次,给返回第n-k+1个元素。这里遍历了两次,时间复杂度为0(n),空间复杂度为O(1)。

思路2:思路1是否存在优化的地方呢?是存在的,我们可以考虑考虑能不能不遍历两次,这时候我们想能不能用空间换时间。

    好比,将链表转化成数组保存下来,那么我们第二次遍历链表操作可以换成获取数组中的n-k+1个元素操作了,只需要遍历一次链表就可以了。但是这种方法的空间复杂度为O(N)。

思路3:两个指针,也就是传说中的快慢指针。一个指针先走k步,然后慢指针才开始跑。快指针到头了,那么落后了k步的慢指针不就是倒数第k个元素了吗?

代码:

    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
    {
        if(NULL == pListHead) return NULL;
        ListNode* pfront = pListHead;
        ListNode* pend = pListHead;
        for (int i = 0; i < k; i++)
        {
            if(pfront != NULL)
            {
                pfront = pfront->next;
            }
            else
            {
                return NULL;//超出了
            }
        }
        for(;pfront != NULL;pfront = pfront->next)
        {
            pend = pend->next;
        }
        return pend;
    }

15、【反转链表】

输入一个链表,反转链表后,输出链表的所有元素。

A->B->C->D->E

A->B->C->D<-E (D->NULL)

A->B->C<-D<-E (C->NULL)

A->B<-C<-D<-E (B->NULL)

A<-B<-C<-D<-E (A->NULL)

代码

    ListNode* ReverseList(ListNode* pHead)
    {
        if(pHead == NULL||pHead->next == NULL) return pHead;//得到链表尾部
        ListNode* tail = ReverseList(pHead->next);
        pHead->next->next = pHead;
        pHead->next = NULL;
        return tail;
    }

16、【合并两个排序的链表】

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

额,这题啥也不用说了吧,,,不就是归并排序的合并过程么。。。。直接贴代码吧。。。

    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode* res = NULL;
        ListNode* cur = NULL;
        ListNode* add = NULL;
        if(pHead1 == NULL) return pHead2;
        if(pHead2 == NULL) return pHead1;
        while(pHead1 != NULL &&pHead2 != NULL)//两个迭代器
        {
            add = pHead1->val >= pHead2->val ?pHead2:pHead1;
            if(res == NULL)  res = cur = add;////////////第一次才执行 在于选择一条链出来
            else
            {
                cur->next = add;
                cur = cur->next;
            }
            if(add == pHead1) pHead1=pHead1->next;
            else pHead2 = pHead2->next;
        }
        if(pHead1 != NULL) cur->next = pHead1;
        if(pHead2 != NULL) cur->next = pHead2;
        return res;
    }

17、【树的子结构】

题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

其实这里主要是靠对问题的拆分能力。

  子函数CompTree:判断两棵树是否相等。

  判断是否为子树:假设当前节点为根,那么是否为子结构?

代码:

class Solution {
private:
    bool CompTree(TreeNode* pRoot1, TreeNode* pRoot2)//判断2是否为1的子树。。。。
    {
        if(pRoot2 == NULL) return true;
        if(pRoot1 == NULL) return false;

        if(pRoot1->val == pRoot2->val)
            return CompTree(pRoot1->left,pRoot2->left)&&CompTree(pRoot1->right,pRoot2->right);
        return false;
    }
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL ||pRoot2 == NULL ) return false;
		return CompTree(pRoot1,pRoot2) || HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right, pRoot2);
    }
};

18、

原文地址:https://www.cnblogs.com/ccXgc/p/9031974.html

时间: 2024-09-30 15:24:41

剑指offer解题思路锦集11-20题的相关文章

java实现《剑指offer》(二)11~20 更新中

11.二进制中1的个数 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. (1)最优解 1 public class Solution { 2 public int NumberOf1(int n) { 3 int count=0; 4 while(n!=0){ 5 n = n&(n-1); 6 count++; 7 } 8 return count; 9 } 10 } (2) 1 public class Solution { 2 public int NumberOf1(in

【好书推荐】《剑指Offer》之硬技能(编程题7~11)

本文例子完整源码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/sword <[好书推荐]<剑指Offer>之软技能> <[好书推荐]<剑指Offer>之硬技能(编程题1~6)> 持续更新,敬请关注公众号:coderbuff,回复关键字“sword”获取相关电子书. 7.重建二叉树 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树.例如:输入前序

剑指Offer解题报告(Java版)——n个骰子的点数 43

问题 ? ? n个骰子朝上的数之和为s,求s的所有可能以及概率 ? ? 分析问题 ? ? 如果是用笨方法,一般人最开始都会想到笨方法,那就是枚举法 ? ? 举个例子,比如两个骰子,第一个骰子的结果为1,2,3,4,5,6,两个骰子的结果是2,3,4,5,6,7:3,4,5,6,7,8:4,5,6,7,8,9:--7,8,9,10,11,12,共三十六种,用n平方size的数组记录这36个结果 ? ? 仔细分析可以发现其实这其中有很多是重复的,所以去除重复,考虑最小的应该是2,也就是n,最大的应该

剑指Offer解题报告(Java版)——排序数组中某个数的个数 38

? ? 分析问题 ? ? 问题只需要找到排序数组中某个数K的个数,由于已经是排序了,K一定是在一堆的,所以我们只需要找到第一个K的index1,然后找到最后一个K的index2就可以了 ? ? 而寻找K的过程我们一般通过二分法查找,这样时间复杂度能降到logn ? ? 解决问题 ? ? 我们通过二分法寻找k,如果中间的数小于k,那么在前半段找k:如果中间的数大于k,那么在后半段找k,那么如何判断找到的k是否是一堆k的边界呢 ? ? 找第一个k的时候判断方式如下: 如果中间的数等于k,那么先判断前

剑指Offer解题报告(Java版)——字符串转换为数字 49

? ? 引言 ? ? STOI是很常见的一道题,leetcode上也有,字符串转换为数字一般都会有现成的函数去实现这样的功能,但有时候需要我们理解其中的具体实现,因为虽然是个很常见的问题,但实际上需要考虑的问题还是很多的,尤其是corner case的处理,而这类问题一般要考虑两点:一点是符号,另外一点是越界 ? ? 分析问题 ? ? 如果字符串前面有空格怎么办,一般来说中间是没有空格的,但是前后可能有空格,所以我们首先需要去掉多余的空格字符,用到trim函数 ? ? 然后就是字符串中有可能第一

剑指Offer解题报告(Java版)——不用加减乘除做加法 47

? ? 引言 ? ? 一般这种不能用四则运算的题都只有用位运算来做,目的是加强大家对计算机计算的理解,真是有点扯淡呢 ? ? 解决问题 ? ? 首先我们得思考计算机是怎样做加法的呢,比如3加4,如果转换成二进制是0011和0100,加起来是7,也就是0111,相当于是两个二进制的异或运算 ? ? 但是我们再举一个例子就是4加4,会发现,两个二进制是0100和0100,异或运算的话结果为0000,但是我们想要的是1000,这是因为异或运算没有考虑到进位的问题,看来我们还要用一个运算去考虑进位的问题

剑指Offer解题报告(Java版)——约瑟夫环 45

? ? 引言 ? ? 常见的约瑟夫环问题有用循环链表做的,有用数组做的,这里提供一个用数学公式做的,由此可见,很多计算机的问题如果最终用到数学的知识,时间复杂度会大大的降低 ? ? 分析问题 ? ? 首先我们对0到n-1删除第一个数进行分析,第一个被删除的数一定是序号为m-1的数,因为0号数了1,1号数了2,m-1号数了m,那么应该删掉m-1号,设m-1号是第k号,这里这样做是因为后面可以扩展,想扩展为m-1%n=k ? ? 剩下的数按照序列重排序如下 ? ? 因为删掉的是k,那么下面第一个就是

剑指Offer解题报告(Java版)——扑克牌顺子 44

? ? 分析问题 ? ? 这个题目的关键点在于大小王可以看作任意的数字,那么我们就把这个任意的数字看作0也未尝不可,因为扑克牌中1-15都有数字,所以可以用0代表大小王 ? ? 这样我们就将问题转换成了从0-15中抽5个数字,看是否连续的问题,由于0可以看作任意数字,所以我们可以用0去补那些空缺的数,比如0,1,3也算是连续的 ? ? 要记住关键点还是这个任意数字上,考虑一个问题,就是如果这5个数中0的个数大于等于空缺数,那么坑一定能补上,所以一定是连续的,如果0的个数小于空缺数,那么0的个数不

剑指Offer(Java版)第十二题:地上有一个m行n列的方格。一个机器人从坐标(0, 0)的格子开始移动, 它每一次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。 如,当k为18时,机器人能够进入方格(35, 37),因为3+5+3+7=18。但它不能进入方格(35, 38), 因为3+5+3+8=19。请问该机器人能够到达多少个格子?

/*地上有一个m行n列的方格.一个机器人从坐标(0, 0)的格子开始移动, 它每一次可以向左.右.上.下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子. 如,当k为18时,机器人能够进入方格(35, 37),因为3+5+3+7=18.但它不能进入方格(35, 38), 因为3+5+3+8=19.请问该机器人能够到达多少个格子?*/public class Class12 { public int moveCount(int rows, int cols, int threshold){