剑指offer合集(第二波)

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路:这道题为比较简单的状态转移动态规划题。f(n)=f(n-1)+f(n-2)+f(n-3)....f(1)+f(0);定义一个数组,二层for循环可以得到答案,题目数据量应该不大,数组定义100*int足够。

class Solution {
public:
    int jumpFloorII(int number) {
        a[0]=1;a[1]=1;
        for(int i=2;i<=99;i++)
        {
            for(int j=0;j<i;j++)
            {
                a[i]+=a[j];
            }
        }
        return a[number];
    }
private:
    int a[100]={0};
};

题目描述

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

解题思路:同类型的题目,依然可以是使用斐波那契数列递推求解,f(n)=f(n-1)+f(n-2);因为由f(n-1)到f(n)只有一种放置方法,由f(n-2)不经过f(n-1)直接到f(n)也只有一种放置方法。

class Solution {
public:
    int rectCover(int number) {
        memset(a,0,sizeof(a));
        a[0]=0;a[1]=1;a[2]=2;
        for(int i=3;i<=99;i++)
        {
            a[i]=a[i-1]+a[i-2];
        }
        return a[number];
    }
private:
    int a[100];
};

题目描述

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

解题思路:利用二进制按位与操作的方式最简便,但是要注意负数按照符号>>右移的时候,高位补充的是1。可以按照<<<右移,这时会忽略高位得补充值,结果也是正确的。还有就是可以自己定义一个flag=1,然后不断左移flag,while的终止条件为flag=0(flag不断左移出了int的范围会重新变为0).

一定要注意按位与操作要加括号!

class Solution {
public:
     int  NumberOf1(int n) {
         int flag =1;int count=0;
         while(flag!=0)
         {
             if((flag&n)!=0)
                 count++;
             flag=flag<<1;
         }
         return count;
     }
};

题目描述

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

方法一:暴力解法,没什么好说的,直接指数为多少就乘多少次就行。

//暴力解法
class Solution {
public:
    double Power(double base, int exponent) {
        double res = 1;int x= exponent>0?exponent:-exponent;
        if(exponent == 0)
        {
            return 1;
        }
        else 
        {
            for(int i=0;i<x;i++)
            {
                res*=base;
            }
        }
        return exponent>0?res:1/res;
    }
};

方法二:

base的exponent可以转换成二进制,即最终的表达也可以用指数为二进制来表示。因为是对exponent的逐位求解, 所以每次将exponent右移一位,同时将需要乘的基准数乘以本身,可以比较简洁且简单地求出一个数的n次幂。

class Solution {
public:
    double Power(double base, int exponent) {
        double res = 1;
        if(exponent == 0)
        {
            return 1;
        }
        else
        {
            int x = exponent>0?exponent:-exponent;
            while(x)
            {
                if(x&1)
                {
                    res*=base;
                }
                x=x>>1;
                base*=base;
            }
        }
        return exponent>0?res:1/res;
    }
};

问题:如果遇到非常大的次数,应该是需要用到大数乘法的知识的,不知道这里应该怎么求解呢

题目描述

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

方法一(自己的思路)

遍历两次,第一次先求出整个链表的深度,第二次根据总的深度确定倒数第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) {
    ListNode *tmp = pListHead;
    ListNode *res = pListHead;
    unsigned int n = 0,i=0;
        while(tmp!= NULL)
        {
            n++;
            tmp = tmp->next;
        }
        tmp = pListHead;
        while(tmp!= NULL)
        {
            if(n-i==k)
            {
               res= tmp;
               break;
            }
            i++;tmp = tmp->next;
        }
        if(i==n) return NULL;
        return res;
    }
};

方法二:

设置两个链表指针,并且两个都指向表头,第一个先走k-1步,然后第二个和第一个一起走,当第一个指针走到头时,则第二个指针所处的位置即为倒数第k个节点。

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    ListNode *m = pListHead,*n = pListHead;
        int i = 0;
        for(;n!=NULL;i++)
        {
            if(i>=k)
                m = m->next;
            n = n->next;
        }
        return i<k?NULL:m;
    }
};

题目描述

输入一个链表,反转链表后,输出新链表的表头。

方法:设定三个值分别存储现在的链表节点,下一个链表节点和前一个链表节点,每次将现在的链表节点反指向上一个链表节点,然后将现在的节点赋给上一个节点,下一个节点赋给现在的节点,下一个点的next赋给自身,直循环到下一个结点为NULL为止,然后返回现在的节点即为反转链表的新节点。注意特殊情况的判断。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
    ListNode *tmp = pHead;
    if(pHead == NULL) return NULL;
    if(pHead->next == NULL)
    {
        return pHead;
    }
    ListNode *cur = pHead->next;
    ListNode *next1 = cur->next;
        cur->next = tmp; tmp->next = NULL;
        while(next1!=NULL)
        {
            tmp = cur;
            cur = next1;
            next1 = cur->next;
            cur->next = tmp;
        }
        return cur;
    }
};

题目描述

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

方法一:暴力解法,感觉思路存在问题。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode *fir,*sec;
        fir = pHead1;
        if(pHead1 == NULL) return pHead2;
        if(pHead2 == NULL) return pHead1;
        sec = pHead1->next;
        ListNode *cur = pHead2,*nex = pHead2->next;
        while(sec!=NULL)
        {
            if(cur->val<=sec->val&&cur->val>=fir->val)
            {
                fir->next = cur;
                cur->next = sec;
                fir = sec;
                sec = sec->next;
                cur = nex;
                nex = nex->next;
            }
            else
            {
                fir = sec;
                sec = sec->next;
            }
        }
        if(cur->val>=fir->val) fir->next=cur;
        if(cur->val<=pHead1->val) cur->next        
        return pHead1;
    }
};

方法二:递归

其实利用递归的思想能非常简单地解决这一道题,首先判断return条件,如过第一个链表为空,则返回第二个,如果第二个链表为空,则返回第一个。然后看链表一的第一个元素和链表二的第一个元素谁小,较小的即为这两个链表这中的最小值,它的指向为剩下的值组成的一个新的链表。由此递归思想可以简洁明了的解决这道题目。

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL) return pHead2;
        else if(pHead2 == NULL) return pHead1;
        ListNode *res;
        if(pHead1->val<pHead2->val)
        {
            pHead1->next = Merge(pHead1->next,pHead2);
            res = pHead1;
        }
        else
        {
            pHead2->next = Merge(pHead1,pHead2->next);
            res = pHead2;
        }
        return res;
    }
};

方法三(非递归版本)

采用模拟的方法,定义一个merge后链表的头部mergehead以及一个current指针,然后使用模拟的方法,current指针指向未遍历到的元素中最小的元素,指向下一个次小的元素,具体过程看代码。记得各种边界条件和代码的鲁棒性处理。

 

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL) return pHead2;
        if(pHead2 == NULL) return pHead1;
        ListNode *current = NULL,*mergehead = NULL;
        while(pHead1!=NULL&&pHead2!=NULL)
        {
            if(pHead1->val<=pHead2->val)
            {
                if(current == NULL)
                {
                    current = mergehead = pHead1;
                }
                else
                {
                    current->next = pHead1;
                    current = current->next;
                }
                pHead1 = pHead1->next;
            }
            else
            {
                if(current == NULL)
                {
                    current = mergehead = pHead2;
                }
                else
                {
                    current->next = pHead2;
                    current = current->next;
                }
                pHead2 = pHead2->next;
            }
        }
        if(pHead1 == NULL) current->next = pHead2;
        if(pHead2 == NULL) current->next = pHead1;
        return mergehead;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值