编程之旅-Day18

目录

Day18-学习内容:

1.剑指Offer

面试题54:二叉搜索树的第k大节点

面试题51:数组中的逆序对

 2.Leetcode

例1:逆序链表的加法

例2:求数组元素之和等于特定数的索引位置

 3.2017年腾讯校招编程题

例2:纸牌游戏

例3:贪吃的小Q

4.2017年阿里巴巴秋招笔试题


1.剑指Offer

面试题54:二叉搜索树的第k大节点

题目描述:给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

思路:中序遍历

代码:

方法1:递归实现

class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(pRoot==nullptr||k<=0){
            return nullptr;
        }
        return KthNodeCore(pRoot,k);
    }
    TreeNode* KthNodeCore(TreeNode* pRoot, int& k){
        TreeNode* target=nullptr;
        if(pRoot->left!=nullptr){
            target=KthNodeCore(pRoot->left,k);
        }
        if(target==nullptr){
            if(k==1){
                target=pRoot;
            }
            k--;
        }
        if(target==nullptr&&pRoot->right!=nullptr){
            target=KthNodeCore(pRoot->right,k);
        }
        return target;
    } 
};

注意:要在“TreeNode* KthNodeCore(TreeNode* pRoot, int& k)”语句中k前加&,否则会报错。

           “if(pRoot==nullptr||k<=0)”判断条件应该是k<=0而不是k==0,并且用||连接。

class Solution {
    int count = 0;
public:
    TreeNode* KthNode(TreeNode* pRoot, unsigned int k)
    {
        if(pRoot){ 
                TreeNode *ret = KthNode(pRoot->left, k);
                if(ret) return ret;
                if(++count == k) return pRoot;
                ret = KthNode(pRoot->right,k);
                if(ret) return ret;
        }
        return nullptr;
    }
};

方法2:中序遍历所有节点放入数组,取出第k-1个元素。

class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        //中序遍历的结果就是有序序列,第K个元素就是vec[K-1]存储的节点指针;
        if(pRoot==NULL||k<=0) return NULL;
        vector vec;
        Inorder(pRoot,vec);
        if(k>vec.size()){
            return NULL;
        }
        return vec[k-1];
    }
    void Inorder(TreeNode* pRoot,vector& vec){
        //中序遍历,将节点依次压入vector中
        if(pRoot==NULL) return;
        Inorder(pRoot->left,vec);
        vec.push_back(pRoot);
        Inorder(pRoot->right,vec);
    }  
};

面试题51:数组中的逆序对

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

示例1

  输入

1,2,3,4,5,6,7,0

输出
7

思路:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。

代码:

class Solution {
public:
    int InversePairs(vector data) {
        if(data.empty()||data.size()==0) return 0;
        int len=data.size();
        vector copy;
        for(int i=0;i &data,vector ©,int start, int end){
        if(start==end){
            copy[start]=data[start];
            return 0;
        }
        
        int length=(end-start)/2;
        long long left=InversePairsCore(copy,data,start,start+length);
        long long right=InversePairsCore(copy,data,start+length+1,end);
        
        int i=start+length;
        int j=end;
        int index=end;
        long long count=0;
        while(i>=start&&j>=start+length+1){
            if(data[i]>data[j]){
                copy[index--]=data[i--];
                count+=j-start-length;
            }
            else{
                copy[index--]=data[j--];
            }
        }
        for(;i>=start;--i){
            copy[index--]=data[i];
        }
        for(;j>=start+length+1;--j){
            copy[index--]=data[j];
        }
        return left+right+count;       
    }
};

 

 2.Leetcode

例1:逆序链表的加法

题目描述:

You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8

思路:本题两个数字相加和传统加法唯一的不同就是此题的加法是从左往右算的,进位也是从左往右进。

     * 例子

         2  4  3

       + 5  6  4

       ——————————

         7  0  8

     * 正常加法应该先算3+4,接着4+6,进一位,最后2+5,加之前的进位1,得到8;

     * 在本题就应该先算2+5,接着4+6,进一位到3+4中,3+4+1=8,最后得到708。

代码:

class Solution {
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        int carry=0;
        ListNode *root=new ListNode(0),*n=root;
        while(l1||l2||carry){
            int v1=0,v2=0;
            if(l1){
                v1=l1->val;
                l1=l1->next;
            }
            if(l2){
                v2=l2->val;
                l2=l2->next;
            }
            int val=(v1+v2+carry)%10;
            carry=(v1+v2+carry)>=10?1:0;
            ListNode *cur=new ListNode(val);
            n->next=cur;
            n=n->next;     
        }
        return root->next;
    }
};

例2:求数组元素之和等于特定数的索引位置

题目描述:

Given an array of integers, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution.

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

思路:使用一个哈希表来解,第一遍扫描,保存到哈希表中,第二遍扫,看target-n在不在哈希表中,时间复杂度为O(n)。

代码:

方法1:顺序查找

class Solution {
public:
    vector twoSum(vector &numbers, int target) {
        vector res;
        int len=numbers.size();
        bool flag=false;
        for(int i=0;i

方法2:哈希表

class Solution {
public:
    vector twoSum(vector &numbers, int target) {
        map map;
        vector res;
        int len=numbers.size();
        for(int i=0;i

 

 3.2017年腾讯校招编程题

例2:纸牌游戏

题目描述:

牛牛和羊羊正在玩一个纸牌游戏。这个游戏一共有n张纸牌, 第i张纸牌上写着数字ai。

牛牛和羊羊轮流抽牌, 牛牛先抽, 每次抽牌他们可以从纸牌堆中任意选择一张抽出, 直到纸牌被抽完。
他们的得分等于他们抽到的纸牌数字总和。
现在假设牛牛和羊羊都采用最优策略, 请你计算出游戏结束后牛牛得分减去羊羊得分等于多少。

输入描述:

输入包括两行。
第一行包括一个正整数n(1 <= n <= 105),表示纸牌的数量。
第二行包括n个正整数ai(1 <= ai <= 109),表示每张纸牌上的数字。

输出描述:

输出一个整数, 表示游戏结束后牛牛得分减去羊羊得分等于多少。

输入例子1:

3
2 7 4

输出例子1:

5

思路:采用最优策略就需要对数组从大到小排序,然后每隔一个累加求和。

代码:

#include 
#include 
#include 
using namespace std;

int main(){
    int n;
    cin>>n;
    vector vec;
    for(int i=0;i>a;
        vec.push_back(a);
    }
    sort(vec.rbegin(),vec.rend());   //注意从大到小排列,因为采用的是最优策略
    int sum1=0,sum2=0;
    for(int i=0;i

 

例3:贪吃的小Q

题目描述:小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力

思路:二分查找

代码:

#include 
using namespace std;

int n,m;
//计算第一天吃s个巧克力一共需要多少个多少个巧克力
int sum(int num){
    int sum=0;
    for(int i=0;i>1;  //向上取整
    }
    return sum;
}
//二分查找
int func(){
    if(n==1) return m; 
    int low=1; //第一天的巧克力一定是大于等于1小于等于m的
    int high=m;
    while(low>1;  //向上取整
        if(sum(mid)==m) return mid;   //如果第一天吃mid个巧克力,刚刚好吃完所有巧克力,那么直接返回
        if(sum(mid)>n>>m;
    int res=func();
    cout<

 

4.2017年阿里巴巴秋招笔试题

例11:一个二叉树有100个子节点数为2的节点,100个子节点数为1的节点,那么个子节点数为0的节点(叶节点)的个数为: (A)

A.101 B.100 C.200 D.300 E.99 F.1

解析:首先,假设该二叉树有n个节点,则有n - 1条边,这是因为除了根节点,其余的每个节点都有且只有一个父节点,那么这n个节点恰好为树贡献了n-1 条边。这是从下往上的思考,而从上往下(从树根到叶节点)的思考,容易得到每个节点的度数和 0*n0 + 1*n1 + 2*n2 即为边的个数。

因此,我们有等式 n-1 = n1 + 2*n2,把n用n0+n1+n2替换,

得到n0 + n1 + n2 - 1 = n1 + 2*n2,于是有

n0 = n2 + 1。

n0=n2+1=100+1=101

 

例12:某种类型的双核 CPU 的性能提升了 1/3 ,假定该提升是通过对每条指令缩短执行时间实现的,那么它每条指令执行时间缩短了 (A)

A.1/4 B.1/8 C.1/3 D.1/6 E.1/5 F.1/2

解析:

原来:1秒执行1条指令

现在:1秒执行4/3条指令

现在执行一条指令花费1秒/(4/3)=3/4秒

所以

每条指令执行时间缩短了1秒-3/4秒=1/4秒

 

例13:一个map-reduce任务由m个mapper和r个reducer构成,计算的效率可以认为正比于mr的乘积(数据管道的个数),在限定任务的mr乘积约等于10000的情况下,假定每个mapper和每个reducer的成本分别为1和7,那么最佳的资源分配最接近于以下哪个方案? (C)

A.mapper100个,reducer100个
B.Mapper200个,reducer50个
C.Mapper264个,reducer38个
D.Mapper316个,reducer32个
E.Mapper500个,reducer20个
F.Mapper1000个,reducer10个

解析:假定每个mapper和每个reducer的成本分别为1和7, 其实就是求最后和最小的方案

A : 100 + 100*7 = 800

B:  200 + 50*7 = 550

C: 264 + 38*7 = 530

D: 316 + 32*7 = 540

E: 500 + 20 * 7 = 640

F 1000 + 10*7 = 1070

所以C答案组合方式是最小成本组合。

C: Mapper264个,reducer38个

 

例14:如果你有相关经验,很多景点的餐馆商铺经营方式很有趣。以下描述错误的是: (F)

A.由于景点的大部分顾客是一次性的,因此商铺的信用在其他条件相同时可能更低
B.景点的餐馆为了招揽顾客使用托儿会比居民区的餐馆使用托儿效果好
C.景点常常卖一些当地特产,比如青岛的海边会有卖贝壳的,这些贝壳产自附近的海域
D.店铺使用托儿的有效原因是,人们常常做出多数人做出的选择,而忽视自己自然状态下的决策
E.一些景点在出口位置上安排一个商店,商店内的通道曲折,为的是顾客多花些时间看东西
F.景点内一些玩射箭的场所经营状况会比在居民区附近设置的类似场所好,原因是景点内的游人玩起来更在状态

解析:智力题,游客状态更好说明射箭越准,越有可能获取奖励,商家从单个游客处获取的利润就越低(搞不好亏本)。

 

例15:一个机器人玩抛硬币的游戏,一直不停的抛一枚不均匀的硬币,硬币有A,B两面,A面的概率为3/4,B面的概率为1/4。问第一次出现连续的两个A面的时候,机器人抛硬币的次数的期望是多少? (F)

A.9/4 B.11/4 C.15/4 D.4 E.5 F.28/9

解析:

假设T为扔的次数(期望)。 那么如果扔到B,则重新开始扔,即再扔T次。

第一次扔到B,则重新扔,即1/4*(1+T);这时1+T是结束游戏所扔次数;

第一次扔到A,第二次扔到B,重新扔,即3/4*1/4*(2+T);2+T是结束游戏所仍次数;

第一次扔到A,第二次扔到A,结束游戏。3/4*3/4*2;2为结束游戏所仍次数;

所以T=1/4*(1+T)+3/4 *1/4*(2+T)+3/4 *3/4 *2;算得T为28/9

 

例16.小a和小b一起玩一个游戏,两个人一起抛掷一枚硬币,正面为H,反面为T。两个人把抛到的结果写成一个序列。如果出现HHT则小a获胜,游戏结束。如果HTT出现则小b获胜。小a想问一下他获胜的概率是多少?(C)

A.3/4  B.1/2  C.2/3  D.5/9  E.1/3  F.1/4

解析:假设b赢得概率为p;则有两种情况:H,T,T;H,T,H...;第一种情况概率为;第二种情况为,因为第二种情况H出现后就相当于又回到开始状态了,这是小b获胜概率仍为p;所以有等式;可解出p;然后1-p即为所求。

 

编程之旅-Day18_第1张图片

 

 

 

 

你可能感兴趣的:(计算机,编程,算法,数据结构,算法,数据结构)