【数据结构与算法(二十三)】

题目

二叉搜索树的第k大节点

给定一个二叉搜索树,请找出其中第k大的节点。例如,在下图的二叉搜索树里,按节点数值大小顺序,第三大节点的值是4
这里写图片描述

思路

1、如果按照中序遍历的顺序遍历一棵二叉搜索树,则遍历序列的数值是递增排序的。例如,在上图中二叉搜索树的中序遍历序列是{2,3,4,5,6,7,8}。因此,只需要用中序遍历算法遍历一棵二叉搜索树,就可以找出它的第k大节点。

struct BinaryTreeNode
{
    int value;
    BinaryTreeNode* leftNode;
    BinaryTreeNode* rightNode;
}

BinaryTreeNode* KthNode(BinaryTreeNode* rootNode,unsigned int k)
{
    if(rootNode==nullptr || k==0)
        return nullptr;

    return KthNodeCore(rootNode,k);
}

BinaryTreeNode* KthNodeCore(BinaryTreeNode* rootNode,unsigned int& k)
{
    BinaryTreeNode* targret=nullptr;
    if(rootNode->leftNode!=nullptr)
    //k的值是会改变的,在没有找到target之前k一直在减一
        targret=KthNodeCore(rootNode->leftNode,k);
    if(targret==nullptr)
    {
        if(k==1)
            targret=rootNode;
        k--;
    }
    if(targret==nullptr&&rootNode->rightNode!=nullptr)
        KthNodeCore(rootNode->rightNode,k);

    return targret;
}

和为s的两个数字

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可

思路

1、例如,输入数组{1,2,4,7,11,15}和数字15.由于4+11=15,因此输出4和11
2、一种效率极低的方法:固定一个数,再依次判断数组中剩余的n-1个数字与它的和是不是等于s
3、一种更好的算法:先在数组中选择两个数字,如果它们的和等于输入的s,那么我们就找到了要找的两个数字。如果和小于s,我们希望两个数字的和再大一些,由于数组是已经排好序的递增数组,我们可以选择较小的数字后面的数字。因为排在后面的数字要大一些,那么两个数字的和也要大一些,就有可能等于输入的s。同样,当两个数字的和大于输入的数字时,我们可以选择较大的数字前面的数字,因为排在数组前面的数字要小一些
4、看表格,以“1、”中的例子

步骤较小的数字较大的数字与s相比较下一步操作
111516大于选择15之前的数字
211112小于选择1之后的数字
321113小于选择2之后的数字
441115等于
//sum1和sum2是为了将最终的两个结果数字传回去
//还是类似使用了两个指针
bool FindNumbersWithSum(int data[],int length,int sum,int* num1,int* num2)
{
    bool found=false;
    if(length<1||num1==nullptr||num2==nullptr)
        return found;
    int ahead=length-1;
    int behind=0;
    while(behind<ahead)
    {
        long long curSum=data[ahead]+data[behind];
        if(curSum==sum)
        {
            *num1=data[behind];
            *num2=data[ahead];
            found=true;
            break;
        }
        else if(curSum<sum)
            behind++;
        else
            ahead--;    
    }
    return found;
}

和为s的连续正数序列

输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。例如,输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以知道打印出3个连续序列1~5,4~6,7和8

思路

1、考虑使用两个数big和small分别表示最大值和最小值。
2、首先把small初始化为1,big初始化为2,如果从small到big的序列的和大于s,则可以从序列中去掉较小的值,也就是增大small的值。如果从small到big的序列的和小于s,则可以增大big,让这个序列包含更多的数字。因为这个序列至少有两个数字,我们一直增加到small为(1+s)/2【为什么是这个?比如11,超过small就不可能符合条件了】
3、用以上的例子,做出下列过程表。s=9

步骤smallbig序列序列和与s相比较下一步操作
1121,23小于增加big
2131,2,36小于增加big
3141,2,3,410大于增加small
4242,3,49等于打印序列,增加big
5252,3,4,514大于增加small
6353,4,512大于增加small
7454,59等于打印序列(已经只有两个数了)
void FindContinousSequences(int sum)
{
    if(sum<3)
        return;
    int samll=1;
    int big=2;
    int middle=(1+sum)/2;
    int curSum=small+big;

    while(small<middle)
    {
        if(curSum==sum)
            PrintContinousSequence(small,big);
        while(curSum>sum&&samll<middle){
            curSum-=small;
            small++;
            if(curSum==sum)
                PrintContinousSequence(small,big);
        }
        big++;
        curSum+=big;
    }
}
void PrintContinousSequence(int small,int big)
{
    for(int i=small;i<=big;i++)
        cout<<i<<" ";
    cout<<endl;
}

翻转单词顺序

输入一个英文句子。翻转句子中单词的顺序。但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如“I am a student.”则输出“student. a am I”

思路

1、第一步翻转句子中所有的字符,包括单词内的字符都被翻转了
2、第二部再翻转每个单词中字符的顺序

void Reverse(char* pBegin,char* pEnd)
{
    if(pBegin==nullptr||pEnd==nullptr)
        return;
    while(pBegin<pEnd){
        char temp=*pBegin;
        *pBegin=*pEnd;
        *pEnd=temp;

        pEnd--;
        pBegin++;
    }
}

char* ReverseSentence(char *pData)
{
    if(pData==nullptr)
        return nullptr;
    char *pBegin=pData;

    char *pEnd=pData;
    while(*pEnd!='\0')
        pEnd++;
    pEnd--;
    //翻转所有字符
    Reverse(pBegin,pEnd);
    //翻转单词内的字符
    pBegin=pEnd=pData;
    while(*pBegin!='\0')
    {
        if(*pBegin==' ')
        {
            pBegin++;
            pEnd++;
        }
        else if(*pEnd==' '||*pEnd=='\0')
        {
            Reverse(pBegin,--pEnd);
            pBegin=++pEnd;
        }else
            pEnd++;
    }
    return pData;
}

左旋转字符串

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串“abcdefg”和数字2,该函数将返回“cdefgab”

思路

1、以“abcdefg”为例,我们把它分为两部分。
2、由于想把它前面的两个字符转移到后面,我们就把前面两个字符分到第一部分,把后面的所有字符分到第二部分。
3、我们先分别翻转这两个部分,于是就得到“bagfedc”。接下来翻转整个字符串,得到的“cdefgab”刚好就是原始字符串左旋转两位的结果

char* LeftRotateString(char* pStr,int n)
{
    if(pStr!=nullptr)
    {
        int nLength=static_cast<int>(strlen(pStr));
        if(nLength>0 && n>0 && n<nLength)
        {
            char* pFirstStart=pStr;
            char* pFirstEnd=pStr+n;
            char* pSecondStart=pStr+n+1;
            char* pSecondEnd=pStr+nLength-1;

            //翻转前面n个字符
            Reverse(pFirstStart,pFirstEnd);
            //翻转后面的字符
            Reverse(pSecondStart,pSecondEnd);
            //翻转全部字符
            Reverse(pFirstStart,pSecondEnd);
        }
    }
    return pStr;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值