剑指offer —— 第3章高质量的代码 面试题 11 - 18

11.数值的整数次方

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

思路:首先处理指数为正数的情况,然后如果指数为负且底数不为0,结果取倒数。注意double类型相等的比较。

class Solution {
    
public:
    bool g_InvalidInput = false;
    double Power(double base, int exponent) {
        g_InvalidInput = false;
        if(equal(base,0.0) && exponent < 0){
            g_InvalidInput = true;
            return 0.0;
        }
        unsigned int absExponent = (unsigned int)(exponent);
        if(exponent < 0)
           absExponent = (unsigned int)(-exponent);
        double result = PowerWithUnsignedExponent(base, absExponent);
        if(exponent < 0)
            result = 1.0 /result;
        return result;
    }
    double PowerWithUnsignedExponent(double base,unsigned int exponent){
        if(exponent == 0) return 1;
        if(exponent == 1) return base;
        double result = PowerWithUnsignedExponent(base, exponent >> 1);
        result *= result;
        if(exponent & 0x1 == 1)
            result *= base;
        return result;
    }
    bool equal(double num1, double num2){
        if((num1 - num2 < 0.0000001) &&
           (num1 - num2 > -0.0000001))
            return true;
        else 
            return false;
    }
    
};

12.打印1到最大的n位数

思路:

void Print1ToMaxOfNDigits_1(int n)
{
    if (n <= 0)
        return;

    char *number = new char[n + 1];
    memset(number, '0', n);
    number[n] = '\0';

    while (!Increment(number))
    {
        PrintNumber(number);
    }

    delete[]number;
}

// 字符串number表示一个数字,在 number上增加1
// 如果做加法溢出,则返回true;否则为false
bool Increment(char* number)
{
    bool isOverflow = false;
    int nTakeOver = 0;
    int nLength = strlen(number);

    for (int i = nLength - 1; i >= 0; i--)
    {
        int nSum = number[i] - '0' + nTakeOver;
        if (i == nLength - 1)
            nSum++;

        if (nSum >= 10)
        {
            if (i == 0)
                isOverflow = true;
            else
            {
                nSum -= 10;
                nTakeOver = 1;
                number[i] = '0' + nSum;
            }
        }
        else
        {
            number[i] = '0' + nSum;
            break;
        }
    }

    return isOverflow;
}
void PrintNumber(char* number)
{
    bool isBeginning0 = true;
    int nLength = strlen(number);

    for (int i = 0; i < nLength; ++i)
    {
        if (isBeginning0 && number[i] != '0')
            isBeginning0 = false;

        if (!isBeginning0)
        {
            printf("%c", number[i]);
        }
    }

    printf("\t");
}

13.在O(1)时间删除链表结点

给定单向链表的头指针和一个结点指针

思路:根据时间复杂度要求,此题目的前提是要删除的结点的确在链表中。令删除结点的值等于其后一个结点,并且指向其后一个结点的next。

void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
    if(!pListHead || !pToBeDeleted)
        return;

    // 要删除的结点不是尾结点
    if(pToBeDeleted->m_pNext != nullptr)
    {
        ListNode* pNext = pToBeDeleted->m_pNext;
        pToBeDeleted->m_nValue = pNext->m_nValue;
        pToBeDeleted->m_pNext = pNext->m_pNext;
 
        delete pNext;
        pNext = nullptr;
    }
    // 链表只有一个结点,删除头结点(也是尾结点)
    else if(*pListHead == pToBeDeleted)
    {
        delete pToBeDeleted;
        pToBeDeleted = nullptr;
        *pListHead = nullptr;
    }
    // 链表中有多个结点,删除尾结点
    else
    {
        ListNode* pNode = *pListHead;
        while(pNode->m_pNext != pToBeDeleted)
        {
            pNode = pNode->m_pNext;            
        }
 
        pNode->m_pNext = nullptr;
        delete pToBeDeleted;
        pToBeDeleted = nullptr;
    }
}

14.调整数组顺序使奇数位于偶数的前面

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

思路:双向指针,交换,解耦合。拓展:类似冒泡排序,为了保持相对位置,只能交换相邻的两个元素。

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        Reorder(array, isEven);
    }
private:
     static bool isEven(int n){
        return (n & 1) == 0;
    }
    void Reorder(vector<int> &array, bool (func)(int)){
        if(array.empty()) return;
        int left = 0, right = array.size() - 1;
        while(left < right){
            while(left < right && !func(array[left]))
                left++;
            while(left < right && func(array[right]))
                right--;
            if(left < right){
                int temp = array[left];
                array[left] = array[right];
                array[right] = temp;
            }
        }
    }
};
class Solution {
public:
    void reOrderArray(vector<int> &array) {
        for(int i = 0;i < array.size();i++){
            for(int j = array.size()-1; j>i;j--){
                if(array[j]%2==1&&array[j-1]%2==0)
                    swap(array[j],array[j-1]);
            }
        }
    }
};

15.链表中倒数第k个结点

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

思路:考虑k=0以及k大于结点数的情况。先后指针,先指针经过k个结点,此时后指针从头结点开始于其同步移动。当先指针走完剩下的n-k个结点,后指针走了n-k步,指向了倒数第k个结点。(正数的n-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) {
        if(pListHead == NULL || k == 0) return NULL;
        ListNode *pAhead = pListHead;
        ListNode *pBehind = NULL;
        for(unsigned int i = 0; i < k - 1; i++){
            if(pAhead -> next != NULL)
                 pAhead = pAhead -> next;
            else return NULL;
        }
        pBehind = pListHead;
        while(pAhead -> next != NULL){
            pAhead = pAhead -> next;
            pBehind = pBehind -> next;
        }
        return pBehind;
    }
};

16.反转链表

思路:利用两个指针分别指向当前结点与前一个结点。一旦当前结点的下一个结点为NULL时,该结点就是新的头结点。当前结点指向前一个结点。前一个结点=当前结点,当前结点=下一个结点。

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

17.合并两个排序的链表

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

思路:比较两个链表的头结点,取小的作为合并链表的头结点,递归调用函数,传入参数时,较小结点指向下一个结点。

/*
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 == NULL) return pHead2;
        if(pHead2 == NULL) return pHead1;
        ListNode* pMergedHead = NULL;
        if(pHead1 -> val < pHead2 -> val){
            pMergedHead = pHead1;
            pMergedHead -> next = Merge(pHead1 -> next, pHead2);
        }
        else{
            pMergedHead = pHead2;
            pMergedHead -> next = Merge(pHead1, pHead2 -> next);
        }
        return pMergedHead;
    }
};

18.树的子结构

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

思路:首先比较根节点是否相等,如果不等则递归调用函数,看树A的左右子树是否含有树B。如果相等,则调用新函数一一比较每个结点。

/*
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)
    {
        bool result = false;
        if(pRoot1 != NULL && pRoot2 != NULL)
        {
            if(pRoot1->val == pRoot2->val)
                result = DoesTree1HaveTree2(pRoot1, pRoot2);
            if(!result)
                result = HasSubtree(pRoot1->left, pRoot2);
            if(!result)
                result = HasSubtree(pRoot1->right, pRoot2);
        }
        return result;
    }
private:
    bool DoesTree1HaveTree2(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2 == NULL) return true;
        if(pRoot1 == NULL) return false;
        if(pRoot1->val != pRoot2->val) return false;
        return DoesTree1HaveTree2(pRoot1->left, pRoot2->left) &&
            DoesTree1HaveTree2(pRoot1->right, pRoot2->right);
    }
};

猜你喜欢

转载自blog.csdn.net/qq_27012963/article/details/81736687