剑指offer 算法(栈和队列 查找和排序)

题目描述

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

解析:用栈来模拟队列。我们首先插入一个元素a到stack1中,再压入两个元素bc,此时栈中有元素abc,其中c位于栈顶,而stack2仍然为空。我们试着删除一个元素。按照队列先进先出的原则,我们应该先删除元素a。元素a存放在stack1中且不在栈顶,因此不能直接删除。注意到stack2还未使用,我们把stack1中的元素逐个弹出并压入stack2中,stack2中的元素是cba,栈顶元素是a,我们现在可以直接删除元素a了。

当stack2中不为空时,在stack2中的栈顶元素是最先进入队列的元素,可以弹出。如果stack2为空时,我们把stack1中的元素逐个弹出并压入stack2中。由于先进入队列的元素被压到stakc1的低端,经过弹出和压入之后就处于stack2的顶端了,可以直接弹出。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty())
        {
	    if(stack1.size()==0)
                return -1;
            while(stack1.size()>0)
            {
            	stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int val=stack2.top();
        stack2.pop();
        return val;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

解析:例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小元素为1.

这道题最直观的解法并不难,从头到尾遍历一次,我们就能找到最小的元素。这种思路的时间复杂度为O(n)。但是这个思路没有利用输入的旋转数组的特性,肯定达不到面试官的要求。

我们注意到旋转之后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都是大于或者等于后面子数组的元素。我们还注意到最小的元素刚好是这两个子数组的分界线。在排序的数组中我们可以利用二分查找来实现O(logn)的查找。本题给出的数组在一定程度上是排序的,因此我们可以试着用二分查找的思路来寻找这个最小的元素。

以前面的例子为例,我们先把第一个指针指向第0个元素,把第二个指针指向第4个元素,如图所示。位于两个指针中间(在数组的下标是2)的数字是5,它大于第一个指针指向的数字。因此中间数字5一定位于第一个递增字数组中,并且最小的数字一定位于它的后面。因此我们可以移动第一个指针让它指向数组的中间。

此时位于这两个指针中间的数字为1,它小于第二个指针指向的数字。因此这个中间数字为1一定位于第二个递增子数组中,并且最小的数字一定位于它的前面或者它自己就是最小的数字。因此我们可以移动第二个指针指向两个指针中间的元素即下标为3的元素。

此时两个指针的距离为1,表明第一个指针已经指向了第一个递增子数组的末尾,而第二个指针指向第二个递增子数组的开头。第二个子数组的第一个数字就是最小的数字,因此第二个指针指向的数字就是我们查找的结果。

技术分享

技术分享
上述方法是否就一定够完美了呢?面试官会告诉你其实不然。他将提示我们再仔细分析小标leftIndex和rightIndex分别和途中P1和P2相对应)的两个数相同的情况。在前面,当着两个数相同,并且它们中间的数相同的也相同时,我们把IndexMid赋给了leftIndex,也就是认为此时最小的数字位于中间数字的后面。是不是一定一样?

我们再来看一个例子。数组{1,0,1,1,1}和数组{1,1,1,0,1}都可以堪称递增排序数组{0,1,1,1,1}的旋转,图2分别画出它们由最小数字分隔开的两个子数组。

技术分享

技术分享
这两种情况中,第一个指针和第二个指针指向的数字都是1,并且两个指针中间的数字也是1,这3个数字相同。在第一种情况中,中间数字(下标为2)位于后面是子数组;在第二种情况中,中间数字(下标为2)位于前面的子数组中。因此,当两个指针指向的数字及它们中间的数字三者相同的时候,我们无法判断中间的数字是位于前面的子数组中还是后面的子数组中国,也无法移动两个指针来缩小查找的范围。此时,我们不得不采用顺序查找的方法。


class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int length=rotateArray.size();
        int index1=0;
        int index2=length-1;
        int indexMid=0;
        if(length==0)
            return 0;
        while(rotateArray.at(index1)>=rotateArray.at(index2))
        {
            if((index2-index1)==1)
            {
                indexMid=index2;
                break;
            }
            indexMid=(index1+index2)/2;
            //特殊情况:顺序查找
            if((rotateArray.at(index1)==rotateArray.at(indexMid))&&(rotateArray.at(indexMid)==rotateArray.at(index2)))
            {
               	return InOrder(rotateArray,index1,index2);
            } 
            if(rotateArray.at(indexMid)>=rotateArray.at(index1))
            {
                index1=indexMid;
            }
            else if(rotateArray.at(indexMid)<=rotateArray.at(index2))
            {
                index2=indexMid;
            }
        }
        return rotateArray.at(indexMid);
    }
    int InOrder(vector<int> rotateArray,int index1,int index2)
    {
        int i;
        int min=rotateArray.at(index1);
        for(i=index1+1;i<=index2;i++)
        {
            if(rotateArray.at(i)<min)
                min=rotateArray.at(i);
        }
        return min;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值