剑指offer-第三章-高质量的代码

面试题16:数值的整数次方

时间开销是O(n)的方法: 最简单的思路是暴力方法,循环base次
时间复杂为O(log(n))的方法:考虑到很多计算的中间结果是能够重复利用的,例如a^4=a^2*a^2,a^5=a^2*a^2*a
        可以将计算a^b写为 
        a^b = a^(b/2)*a^(b/2)  (b%2==0)
        a^b = a^(b/2)*a^(b/2)*a  (b%2!=0)
优化:
    除以2的过程用右移代替:if(exponent&0x1==1)//表示是2的倍数
总结注意事项:
    考虑负数
    0的0次方是没有数学意义的,输出0或1都是可以接受的
    考虑除以0的情况出现,牛客网的测试中没有这项内容的检测。

class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent == 0)
            return 1.0;
        if(exponent == 1)
            return base;
        if(exponent < 0)
        {
            return 1.0/Power(base, -exponent);
        }
        double result = Power(base, exponent>> 1);
        result *=result;
        if(exponent&0x1 == 1)
            return result*=base;
        return result;
    }
};

面试题21:调整数组顺序使奇数位于偶数之前

在牛客网上,这道题添加了限制条件,使奇数、偶数他们的相对顺序不改变。

结题思路:从数组头部开始遍历,每次遇到偶数,就先保存这个偶数,并将后面的数组整体向前移动,并在数组的末尾添加这个遇到的偶数。

注意事项:

  • 因为每次移动之后,相当于后面的值往前进了,所以不能在循环里让下标自动加1,需要判断,只有当前的下标对应的数值是奇数的时候才移动下标。
  • 当后面没有遍历的数的个数等于之前出现过的偶数个数时就遍历结束了
class Solution {
public:
    void reOrderArray(vector<int> &array) {
        int len = array.size();
        int count = 0;
        int i = 0;
        int j = 0;
        for(i = 0;i < len-count;)
        {
            if(array[i] % 2 == 0)
            {
                int t = array[i];
                for(j = i; j+1 < len; j++)
                {
                    array[j] = array[j+1];
                }
                array[len-1] = t;
                count++;
            }
            else
            {
                i++;
            }
        }
    }
};

如果不对顺序限制,可以使用两个指针,一个从头开始遍历,一个从末尾开始遍历,当左边指针寻找没有交换的偶数,右边指针寻找没有交换的奇数,然后交换两个指针对应的值。

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        int len = array.size();
        int start=0;
        int end = len-1;
        while(start < end)
        {
    	    while(array[start] & 0x1 == 1 && start < end)start++;
    	    while(array[end] & 0x1 == 0 && start < end)end--;
    	    if(start < end)
    	    {
    		    int t = array[start];
    		    array[start] = array[end];
    		    array[end] = t;
    		    start++;
    	    	end--;
	   	    }
	    }
    }
};

 

面试题22:链表中倒数第k个节点

注意事项:

  • 考虑k=0时候,倒数第0个不存在
  • 使用长短指针,注意判断的是end->next=nullptr的时候停止,而不是end = nullptr
/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        //注意k的取值范围,k>0如果k=0,该节点不存在
        if(k == 0)
        {
            return nullptr;
        }
        ListNode *pk = nullptr;
        ListNode *end = pListHead;
        int count = 1;
        while(end!=nullptr && count < k)
        {
            end = end->next;
            count++;
        }
        if(end == nullptr)return nullptr;  //不存在的时候是返回空吗
        pk = pListHead;
        while(end->next !=nullptr)
        {
            pk = pk->next;
            end = end->next;
        }
        return pk;
    }
};

面试题24:反转链表

注意事项:

  • 所需的三个指针分别是什么意思需要有明确的定义,以免太乱
  • 考虑特殊情况,头指针为空,只有一个节点的情况
/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead == nullptr)
            return nullptr;
        ListNode *pNow = pHead;
        ListNode *pPre = nullptr;
        ListNode *pNext = nullptr;
        // 定义好三者的状态,pNow是与pPre分离开的,pNow与pNext是连在一起的
        // ...p<-pPre pNow->pNext->...
        while(pNow != nullptr)
        {
            pNext = pNow->next;
            pNow->next = pPre;
            pPre = pNow;
            pNow = pNext;
        }
        return pPre;
    }
};

面试题25:合并两个排序链表

注意事项:

  • 要考虑空指针
  • 考虑两个链表不等长,有其中一个会为空
  • 注意保留表头,新建链表的过程中,需要一个移动的指针pResPtr

最开始写的代码,考虑的比较复杂,代码没有优化

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

优化后的代码

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        // 采用递归的思想
        if(pHead1 == nullptr)
            return pHead2;
        if(pHead2 == nullptr)
            return pHead1;
        ListNode *pRes = nullptr;
        if(pHead1->val < pHead2->val)
        {
            pRes = pHead1;
            pRes->next = Merge(pHead1->next, pHead2);
        }
        else
        {
            pRes = pHead2;
            pRes->next = Merge(pHead1, pHead2->next);
        }
        return pRes;
    }
};

面试题26:树的子结构

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

注意事项:

  • 在书上树的节点的值是double,所以在判断的时候需要注意d1-d2<0.0000001认为是相等的,而不能直接用等号判断
  • 使用递归将问题分解
  • 注意边界条件
/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2==nullptr || pRoot1 == nullptr)return false;
        bool res = true;
        if(pRoot1->val == pRoot2->val)
        {
            if(pRoot2->right != nullptr)
            {
                if(HasSubtree(pRoot1->right, pRoot2->right)==false)
                {
                    res = false;
                }
            }
            if(pRoot2->left != nullptr)
            {
                if(HasSubtree(pRoot1->left, pRoot2->left)==false)
                {
                    res = false;
                }
            }
            if(res)
            {
                return true;
            }
        }
        if(HasSubtree(pRoot1->left, pRoot2)||HasSubtree(pRoot1->right, pRoot2))
        {
            return true;
        }
        return false;
    }
};

面试题19:正则表达式匹配

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

注意事项:

  • 字符串可以通过判断'\0'检查结束
  • 注意.*也是可以同时出现的
  • 注意当模式为.的时候,虽然可以匹配任何字符,但要注意字符串为空的情况是不能匹配的
class Solution {
public:
    bool match(char* str, char* pattern)
    {
        if(str == nullptr || pattern == nullptr)
            return false;
        if(*str=='\0' && *pattern=='\0')
            return true;
        char* pstr = str;
        char* p1 = pattern;
        char* p2 = pattern+1;
        if(*p2 =='*')// 表示有重复模式
        {
            if(*pstr == *p1 || (*pstr !='\0' && *p1=='.'))
            {
                return match(pstr+1, p1)||match(pstr, p2+1);
            }
            else
            {
                return match(pstr, p2+1);
            }
        }
        else{
            if((*p1 == '.'&& *pstr!='\0') || *pstr == *p1)
            {
                return match(pstr+1, p1+1);
            }
            return false;
        }
    }
};

面试题23:链表中环的入口节点

解题思路:先用长短指针判断是否有环以及环中的元素个数n,然后使用距离差为n的两个指针,步长一致,查找入口。

注意事项

  • 注意长短指针的空指针判断
  • 注意考虑没有环的情况
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode *p1 = pHead;
        ListNode *p2 = pHead->next;
        int count = 0;
        while(p2 != nullptr && p2->next != nullptr && p1 !=nullptr)
        {
            count++;
            if(p1 == p2)
            {
                break;
            }
            p1 = p1->next;
            p2 = p2->next->next;
        }
        if(p1 != p2)
            return nullptr;
        //count表示环中节点的个数
        ListNode *p0 = pHead;
        ListNode *pn = pHead;
        for(int i = 0; i < count; i++)
        {
            pn = pn->next;
        }
        while(p0 != pn)
        {
            p0 = p0->next;
            pn = pn->next;
        }
        return p0;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值