剑指Offer(面试题3~5)

面试题3:二维数组中的查找

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

通过分析发现规律:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束;如果该数字大于要查找的数字,剔除这个数字所在的列;如果该数字小于要查找的数字,剔除这个数组所在的行。也就是说如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。

bool Find(int* matrix,int rows,int columns,int number)
{
    bool found=false;

    if(matrix != NULL && rows>0&&columns>0)
    {
        int row=0;
        int column=columns-1;
        while(row<rows&&column>=0)
        {
            if(matrix[row*columns+column]==number)
            {
                found=true;
                break;
            }
            else if(matrix[row*columns+column]>number)
                --column;
            else
                ++row;
        }
    }
    return found;
}

面试题4:替换空格

题目:请实现一个函数,把字符串中的每个空格替换成“%20”。例如输入“We are happy.”,则输出“We%20are%20happy.”。

时间复杂度为O(n)的解法:
我们可以先遍历一次字符串,这样就能统计出字符串中空格的总数,并可以由此计算出替换之后的字符串的总长度。每替换一个空格,长度增加2,因此替换以后字符串的长度等于原来的长度加上2乘以空格数目。
我们从字符串的后面开始复制和替换。首先准备两个指针,P1和P2。P1指向原始字符串的末尾,而P2指向替换之后的字符串的末尾。接下来我们向前移动指针P1,逐个把它指向的字符复制到P2指向的位置,直到碰到第一个空格为止。碰到第一个空格之后,把P1向前移动一格,在P2之前插入字符串“%20”。由于“%20”的长度为3,同时也把P2向前移动3格。接着向前执行。

//length 为字符数组string的总容量
void ReplaceBlank(char string[],int length)
{
    if(string == NULL||length<=0)
        return;
    //originalLength为字符串string的实际长度
    int originalLength = 0;
    int numberOfBlank =0;
    int i=0;
    while(string[i]!='\0')
    {
        ++originalLength;
        if(string[i] == ' ')
            ++numberOfBlank;
        ++i;
    }

    /*newLength为把空格替换成‘%20’之后的长度*/
    int newLength=originalLength+numberOfBlank*2;
    if(newLength>length)
        return;

    int indexOfOriginal = originalLength;
    int indexOfNew = newLength;
    while(indexOfOriginal >= 0 && indexOfNew > indexOfOriginal)
    {
        if(string[indexOfOriginal]==' ')
        {
            string[indexOfNew--] = '0';
            string[indexOfNew--] = '2';
            string[indexOfNew--] = '%';
        }
        else
        {
            string[indexOfNew--]=string[indexOfOriginal];
        }
        -- indexOfOriginal;
    }
}

链表

如果单向链表的结点定义如下:

struct ListNode
{
    int         m_nValue;
    ListNode*   m_pNext;
};

那么往该链表的末尾中添加一个结点的C++代码如下:

void AddToTail(ListNode** pHead,int value)
{
    ListNode* pNew = new ListNode();
    pNew->m_nValue = value;
    pNew->m_pNext = NULLif(*pHead == NULL)
    {
        *pHead = pNew;
    }
    else
    {
        ListNode* pNode = *pHead;
        while(pNode->m_pNext != NULL)
            pNode=pNode->pNext;

        pNode->m_pNext = pNew;
    }
}

在上面的代码中,函数的第一个参数pHead是一个指向指针的指针。当我们往一个空链表中插入一个结点时,新插入的结点就是链表的头指针。由于此时会改动头指针,因此必须把pHead参数设为指向指针的指针,否则出了这个函数pHead仍然是一个空指针。

下面是在链表中找到第一个含有某值的结点并删除该结点的代码:

void RemoveNode(ListNode** pHead,int value)
{
    if(pHead == NULL || *phead == NULL)
        return;

    ListNode* pToBeDeleted = NULL;
    if((*pHead)->m_nValue == value)
    {
        pToBeDeleted = *pHead;
        *pHead=(*pHead)->m_pNext;
    }
    else
    {
        ListNode* pNode = *pHead;
        while(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue != value)
            pNode = pNode->m_pNext;

        if(pNode->m_pNext != NULL && pNode->m_pNext-m_nValue==value)
        {
            pToBeDeleted = pNode->m_pNext;
            pNode->m_pNext=pNode->m_pNext->m_pNext;
        }
    }

    if(pToBeDeleted != NULL)
    {
        delete pToBeDeleted;
        pToBeDeleted = NULL;
    }
}

面试题5:从尾到头打印链表

题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值。
链表定义如下:

struct ListNode
{
    int         m_nKey;
    ListNode*   m_pNext;
};

遍历的顺序是从头到尾的顺序,可输出的顺序却是从尾到头。也就是说第一个遍历到的结点最后一个输出,而最后一个遍历到的结点第一个输出。这就是典型的“后进先出”,我们可以用栈来实现这种顺序。这种思路的代码实现如下:

struct ListNode
{
    int         m_nValue;
    ListNode*   m_pNext;
};

void PrintListReversingly_Iteratively(ListNode* pHead)
{
    std::stack<ListNode*> nodes;

    ListNode* pNode = pHead;
    while(pNode != NULL)
    {
        nodes.push(pNode);
        pNode = pNode->m_pNext;
    }

    while(!nodes.empty())
    {
        pNode=nodes.top();
        printf("%d\t",pNode->m_nValue);
        nodes.pop();
    }
}

使用递归实现如下:

void PrintListReversingly_Recursively(ListNode* pHead)
{
    if(pHead != NULL)
    {
        if(pHead->m_pNext != NULL)
        {
            PrintListReversingly_Recursively(pHead->m_pNext);
        }
        printf("%d\t",pHead->m_nValue);
    }
}

上面的基于递归的代码看起来很简洁,但有个问题:当链表非常长的时候,就会导致函数调用的层级很深,从而有可能导致函数调用栈溢出。显式用栈基于循环实现的代码鲁棒性要好些。

参考书籍:《剑指Offer》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值