《剑指offer》刷题打卡第5天

面试题10:矩形覆盖
我们可以用2 * 1的小矩形横着或者竖着去覆盖更大的矩形。请问n个2 * 1的小矩形无重叠地覆盖一个2 * n的大矩形,总共有多少种方法?

题意解释:
在这里插入图片描述
解题思路;
在这里插入图片描述
代码实现:

class Solution {
public:
    int rectCover(int number) {
      if(number < 0)
      {
          return -1;
      }
       int result[] = {0,1,2};
        if(number < 3)
        {
            return result[number];
        }
        
         int a = 1;
         int b = 2;
         int c = 0;
         int i = 3; 
        for(i;i<=number;i++)
        {
            c = a + b;
            
            a = b;
            b = c;
        }
        return c;
    }
};

面试题11:二进制中1的个数
输入一个整数,输出该函数二进制表示中1的个数。其中负数用补码表示。
例如:把9表示成二进制是1001,有2位是1。因此,如果输入9,则该函数输出2。
补充知识:
位运算:
位运算是把数字用二进制表示之后,对每一位上0或者1的运算。
在这里插入图片描述
左移运算符m<<n表示把m左移n位。在左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0.比如:
00001010 <<2=00101000 左移两位
10001010<<3=01010000左移三位

右移运算符m>>n表示把m右移n位。在右移n位的时候,最右边的n位将被丢弃。但右移时处理最左边位的情形要稍微复杂一点。如果数字是一个无符号数值,则用0填补最左边的n位;如果数字是一个有符号数值,则用数字的符号位填补最左边的n位。
00001010>>2=00000010(正数右移2位,直接在左边补2个0)
10001010>>3=11110001(负数右移3位,直接在左边补3个1)
解题思路:
先判断整数二进制表示中最右边的1位是不是1;接着把输入的整数右移一位,此时,原来处于从右边数起的第二位被移到最右边了,再判断是不是1;这样每移动一位,直到整个整数变成0为止。
现在的问题转化为如何判断整数最右边是不是1?
把整数和1做位运算看结果是不是0就知道了。1除最右边的一位之外所有位都为0。
如果该数与1做与运算,结果为1,则表示该位为1,结果为0,则表示该位为0。
代码实现:

int NumberOf1(int n)
{
 int count=0;
while(n)
{
if(n&1)
count++;
n=n>>1;
}
return count;
}

在这里插入图片描述
在这里插入图片描述
代码实现:

int NumberOf1(int n)
{
 int count=0;
unsigned int flag = 1;
while(flag)
{
 if(n&flag)
count++;

flag=flag<<1;
}
return count;
}

优秀解法:
思路:把一个整数减去1,再和原整数做与运算,会把该整数做与运算,会把该整数最右边的1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。

代码实现:

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

面试题12:数值的整数次方
实现函数double Power(double base,int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

全面考虑边界条件:
当指数为负数的时候,可以先对指数求绝对值,算出次方的结果之后再取倒数。
既然要求倒数,就需要考虑底数是0且指数为负数的情况下,如果不进行特殊处理,就会出现对0求倒数,从而导致程序运行出错。
怎么告诉函数调用者出现了这种错误?
可以通过:返回值、全局变量、异常这三种方式,需要讨论这三种方式的优缺点,然后选择合适的方式。
由于0的0次方在数学上是没有意义的,因此无论输出是0还是1都是可以接受的,但这都需要和面试官说清楚,表明我们已经考虑到这个边界值了。

解题思路:
在这里插入图片描述
在这里插入图片描述
代码实现:

class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent>0)
        {
            if(exponent==1)
                return base;
            if((exponent&0x1)==0)
           // if(exponent%2==0)
                return Power(base,exponent/2)*Power(base,exponent/2);
            else
                return Power(base,exponent/2)*Power(base,exponent/2+1);
        }
        else if (exponent==0)
        {
            return 1;
        }
        else
        {
            return 1/Power(base,0-exponent);
        }
    }
};

面试题13:调整数组顺序使奇数位于偶数前
在这里插入图片描述
解题思路:
声明两个指针,第一个指针初始化时指向数组的第一个数字,它只向后移动;第二个指针初始化时指向数组的最后一个数字,它只向前移动。在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数,并且第二个指针指向的数字是奇数,则交换这两个数字。

扫描二维码关注公众号,回复: 5814343 查看本文章

举例说明:
在这里插入图片描述
在这里插入图片描述
代码实现:

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        //unsigned int length;
        if(array.size() == nullptr)
        {
            return;
        }
        int *pBegin = arry.begin();
        int *pEnd = array.size()  - 1;
        
        while(pBegin < pEnd)
        {
            //向后移动pBegin,直到它指向偶数
            while(pBegin < pEnd && (*pBegin&0x1) != 0)
                pBegin ++;
            
            //向前移动pEnd,直到它指向奇数
            while(pBegin < pEnd && (*pEnd & 0x1) == 0)
                pEnd --;
            
            if(pBegin < pEnd)
            {
                int temp = *pBegin;
                *pBegin = *pEnd;
                *pEnd = temp;
            }
            
        }
    }
};

面试题14:链表中倒数第K个节点
在这里插入图片描述

struct ListNode
{
int m_nValue;
ListNode *m_pNext;
};

解题思路:
为了实现只遍历一次就能找到倒数第k个节点,我们可以定义两个指针。第一个指针从链表的头指针开始遍历向前走k-1,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在K-1,当第一个(走在前面的)指针到达链表的尾节点时,第二个指针(走在后面的)指针正好是倒数第k个结点。
在这里插入图片描述
在这里插入图片描述
注意检查代码的鲁棒性
在这里插入图片描述
代码实现:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
   //如果输入的链表头指针为nullptr,那么整个链表为空,此时
   //查找倒数第k个节点自然应该返回nullptr。
   //如果输入的k是0,也就是试图查找倒数第0个节点,由于我们计数是从1开始的,因此输入0没有实际意义。
   //也可以返回nullptr。
        if(pListHead == nullptr || k == 0)
        return nullptr;
        
        ListNode *pAhead = pListHead;//第一个指针
        ListNode *pBehind = nullptr;//第二个指针
        
        for(unsigned int i = 0;i < k-1; i++)
        {
            //加for循环
            if(pAhead->next!=nullptr)//如果链表的节点数少于k,那么在for循环中遍历链表可能回出现指向nullptr的next.
                pAhead = pAhead->next;//第一个指针走k-1步
            else{
                return nullptr;
            }
        }
        pBehind = pListHead;
        while(pAhead->next!=nullptr )
        {
            //两个指针同时走
            pAhead = pAhead->next;
            pBehind = pBehind->next;
        }
        return pBehind;//返回第二个指针,即链表中倒数第k个节点。
    }
};

面试题15:反转链表
在这里插入图片描述
代码实现:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {

        ListNode* pReversedHead = nullptr;
        ListNode* pNode = pHead;
        ListNode* pPrev = nullptr;
        
        while(pNode != nullptr)
        {
            ListNode* pNext = pNode->next;
            
            if(pNext == nullptr)
                pReversedHead = pNode;
            
            pNode->next = pPrev;
            
            pPrev = pNode;
            pNode = pNext;
        }
        return pReversedHead;
    }
};

猜你喜欢

转载自blog.csdn.net/tpf930726/article/details/88952918