algorithm 题集八 (18.03.25)

本文一共12题,均来自《剑指offer》,不复杂。在平时的练习中收集了部分组合而成。

(1)输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

(2)用两个栈来实现一个队列,完成队列的Push和Pop操作。队列中的元素为int类型。

(3)用两个队列实现一个栈。完成栈的Push,Pop,top等操作。

(4)把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

(5)大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39。(斐波那契数列:1、1、2、3、5、8、13、21、34、…… 在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2))

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

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

(8)链表反转。

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

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

(11)输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

(12)定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。


思路:
(1)递归查找顶点。
(2)一个栈专门负责进,另一个栈专门负责出。
(3)一个队列负责主存储,另一个队列负责临时存储(用于辅助实现pop操作)。
(4)第一种思路:暴力,直接从后面向前遍历,直到找到最小值。第二种思路:由于原始数组是非递减数组,那么旋转后的数组后面第二部分的任意一个元素一定是小于等于前面任何一个元素。然后,我们可以使用二分不断缩小比较的空间。
思路二的大致过程:
先确定left = 0, right = size -1。二分得到mid = (left+right)>>1
如果array[mid] > array[left], 那么array[mid]属于第一部分,left = mid
如果array[mid] < array[left],那么array[mid]属于第二部分,right = mid
如果array[mid] == array[left],那么有可能是3 1 3 3 3,也有可能是 3 3 3 1 3。两种情况均满足条件。故无法判断,直接暴力搜索。
(5)个人觉得,最快的方法是在构造函数中打表,然后解答函数直接输出array[n]。
(6)动态规划问题,当青蛙跳到第n级,可供选择的路径共有F(n-1) + F[n-2]。所以递推式就是菲波那切数列。
(7)寻找规律:

f[1] = 1
f[2] = f[1]+1 = 2
f[3] = f[1]+f[2]+1 = 4
f[4] = f[1]+f[2]+f[3]+1 = 8
f[5] = f[1]+f[2]+f[3]+f[4]+1 = 16
==> f[n] = 2^(n-1)

(8)放两个指针记录遍历指针的前节点和后节点。然后改变遍历指针的next指向。一趟遍历完整就完成了任务。

    ListNode* ReverseList(ListNode* pHead) {
        ListNode* next = NULL;
        ListNode* pre = NULL;
        while(pHead){
            next = pHead->next;
            pHead->next = pre;
            pre = pHead;
            pHead = next;
        }
        return pre;
    }

(9)将麻烦事交给计算机,我们指出关键步骤即可。递归无敌!

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

(10)首先需要确定满足“相同(子结构)”
然后左右子树递归查找。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool isSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
        if(pRoot2 == NULL) return true;
        if(pRoot1 == NULL) return false;
        if(pRoot1->val == pRoot2->val){
            return isSubtree(pRoot1->left, pRoot2->left) && isSubtree(pRoot1->right, pRoot2->right);
        }
        else  return false;
    }
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL || pRoot2 == NULL) return false;
        return isSubtree(pRoot1, pRoot2) || \
                HasSubtree(pRoot1->left,pRoot2) || \
                HasSubtree(pRoot1->right,pRoot2); 
    }
};

(11)我的方案:找到需要打印的圈数和左上顶点,打印外围数字。这样由外向内循环下去。
然后,看到其他网友的解法。有一个非常的巧妙:打印第一行,然后删除此行,接着逆时针旋转90度构造新的矩形。重新这个步骤。

(12)添加一个辅助栈,用于每次push元素压入新的最小元素,每次pop元素弹出栈顶元素,min函数直接查询辅助栈的栈顶元素即可。
当然,我没想那么多(呵呵),直接去暴力实现了。

class Solution {
    int *data;
    unsigned int length;
    unsigned int size;

public:
    Solution(){
        length = 0;
        size = 0;
        data = NULL;
    }
    ~Solution(){
        length = 0;
        size = 0;
        delete data;
        data = NULL;
    }
    unsigned int _align_it(unsigned int newSize){
        return newSize+(sizeof(int)-1) & ~(sizeof(int)-1);
    }
    unsigned int recommend(unsigned int newSize){
        return std::max(2*length, _align_it(newSize));
    }
    void reserve(unsigned int size){
        if(size > length) {
            int old_length = length;
            int *old_data = data;
            length = recommend(size);
            data = new int[length];
            for(int i=0; i<old_length; i++){
                data[i] = old_data[i];
            }
            delete old_data;
        }
    }
    void push(int value) {
        reserve(++size);
        data[size-1] = value;
    }
    void pop() {
        if(size == 0){
            return ;
        }
        size--;
    }
    int top() {
        try{
            if(size == 0) throw "there is no number in data.";
            return data[size-1];
        }
        catch(char *str){
            cout<<str<<endl;
            return -1;
        }
    }
    int min() {
        try{
            if(length == 0) throw "length is equal to zero";
            int minVal = data[0];
            for(int i=1;i<size;i++){
                minVal = minVal<data[i]?minVal:data[i];
            }
            return minVal;
        }
        catch(char *str){
            cout<<str<<endl;
            return -1;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值