網易2020校招資料分析方向提前批筆試題解析

jialun0116發表於2020-11-10

網易2020校招資料分析方向提前批筆試題

選擇題

  1. 有一類二叉樹用三叉連結串列來儲存的時候除了帶有指向左右孩子節點的兩個指標,還有指向父節點的指標,那麼這樣一棵二叉樹有2個節點,那麼有多少指標指向NULL(注:根節點的父指標指向NULL,對於不存在的節點表示為NULL)?

題解 : 4個指標 畫個圖可知

  1. 下列最短路徑演算法的敘述中正確的是(B)
    A. Dijkstra演算法通常用於求每一對頂點間的最短路徑;
    B. Dijkstra演算法不允許圖中帶有負權值的邊,而Floyd演算法則可以適用;
    C. Floyd演算法通常用於求某一頂點到其他各頂點的最短路徑;
    D. Floyd演算法允許有包含負權值的邊組成的迴路,而Dijkstra演算法不允許;

題解:
A. Floyd演算法通常用於求每一對頂點間的最短路徑
C. Dijstra演算法通常同於求某一頂點到其他個頂點的最短路徑
D. Floyd演算法允許包含負權值的邊,但是不允許有負迴路

  • 單源點的最短路徑問題——Dijkstra演算法
    • 將圖中所有點分成 S(已求出解) 和 U(未求出解)2個點集. dist[i]表示v0到v[i]當前已求得得最短路徑.A[n]為邊集
    • 步驟
      • 從剩下的邊集合中選出dist最短的邊並將邊的另一頂點vi從U中加入S.
      • 更新與vi連線的所有且並未在S中的點的dist矩陣值,dist[vk]=min(dist[vk],dist[vi]+A(i,k)).
    • 重複上述操作直到U中無與S中的點相連的點.
    • 時間複雜度:O(n2)
  • 成對的最短路徑問題——Floyd演算法
    • 定義:是一種在具有正或負邊緣權重(但沒有負週期)的加權圖中找到最短路徑的演算法。即對於圖中的每一個頂點,該演算法都會找出從頂點v到v所能達到的任何其他頂點w的最短路徑
    • 步驟
      • 動態規劃法: Dis(i,j) =min(Dis(i,j), Dis(i,k) + Dis(k,j)).
      • for(k=0;k<n;k++){for(i=0;i<n;i++) for(j=0;j<n;j++) if(d[i][j]>(d[i][k]+d[k][j])){d[i][j]=d[i][k]+d[k][j];path[i][j]=k;}}
    • 時間複雜度:O(n3)
  1. 一個盒子中有三個大小相同的球,這三個球可能是紅和藍兩種顏色,並且一個球是紅的還是藍的是等可能的。已知其中有一個是紅色的,那麼至少有一個球是藍色的概率是

設P(A)為至少有一個紅球的概率,P(B)為至少有一個藍球的概率,故題設求P(B|A) = P(AB)/P(A), 其中P (A1)為一個紅球都不存在的概率

至少有一個紅色的概率:

P ( A ) = 1 − P ( A 1 ) = 1 − C 3 3 ( 1 / 2 ) 3 P(A)=1-P(A_1)=1-C_3^3(1/2)^3 P(A)=1P(A1)=1C33(1/2)3 = 7 / 8 7/8 7/8

在至少有一個球是紅色的,另外兩個球中有一個是藍色的情況 紅藍藍 + 紅紅藍 :

P ( A B ) = C 3 1 ( 1 / 2 ) 1 ( 1 / 2 ) 2 + C 3 2 ( 1 / 2 ) 2 ( 1 / 2 1 ) = 3 / 4 P(AB) = C_3^1(1/2)^1(1/2)^2 + C^2_3(1/2)^2(1/2^1) = 3/4 P(AB)=C31(1/2)1(1/2)2+C32(1/2)2(1/21)=3/4

在至少有一個為紅色的前提下,另外兩個球至少有一個為藍色的條件概率

P ( B / A ) = P ( A B ) / P ( A ) = 6 / 7 P(B/A) = P(AB)/P(A) = 6/7 P(B/A)=P(AB)/P(A)=6/7

  1. 在10件產品中有五件是殘次品,從中任取五件,求其中至少有兩件是殘次品的概率(113/126)

P = 1 − ( C 5 1 C 5 4 + C 5 5 ) / C 10 5 ) = 113 / 226 P = 1 - (C^1_5C^4_5 + C^5_5)/C^5_{10}) = 113/226 P=1(C51C54+C55)/C105)=113/226

  1. 小明在玩擲骰子的遊戲,將一顆骰子擲了兩次,兩次骰子的點數之和為7,求其中一次擲到6點的概率 (1/3)

兩次骰子的點數之和為7的情況:1+6、2+5、3+4、6+1、5+2、4+3

其中有一次為6的情況:1+6、6+1

P = 2 / 6 P = 2/6 P=2/6

  1. 在5張卡片上按順序寫上laval這五個字母,並依次放入5個盒中,有人從中任意取出兩張卡片使用,但是在放回時,忘記了兩張卡片各自的位置,求此人將卡片隨意放回兩個空盒子後卡片順序仍為laval的概率 ( 3/5 )

抽中相同的字元,任意放回不會改變順序 l+l、a+a

抽中不同的字元,有1/2的機率不改變順序

總共: C 5 2 = 10 C^2_5 = 10 C52=10種情況

P = ( 2 + 8 ∗ ( 1 / 2 ) ) / 10 = 3 / 5 P = (2 + 8*(1/2)) / 10 = 3/5 P=(2+8(1/2))/10=3/5

  1. 泊松分佈概率密度函式的是(B)

在這裡插入圖片描述

題解:

第一個為伯努利分佈

第二個為泊松分佈

第三個為指數分佈

第四個為均勻分佈

  1. 判斷一個陣列或序列是正序,倒序還是亂序,需要我們將這個陣列完整的遍歷一遍通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應的位置並插入的排序演算法是(插入排序)
  • 選擇排序:每次從陣列中選出一個最小數(最大數)放到陣列最前面,存放在序列的起始位置,直到全部待排序的資料元素排完。

  • 希爾排序:設定增量分割陣列,逐步進行直接插入排序,增量逐趟減少,並最後使得整個陣列基本有序,再對整體進行直接插入排序。

  • 插入排序:構建有序序列,未排序資料依次從已排序資料按從後往前比較,插入到合適的位置。

  • 歸併排序:把序列分成兩個長度為n/2的子序列,對這兩個子序列分別歸併排序(迴圈將兩個陣列的第一個值比較,並彈出第一個值, 直到陣列長度都不存在),將兩個排序好的子序列合併成一個最終的排序序列

  1. 佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,佇列是一種操作受限制的線性表,進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭,若用一個大小為6的陣列來實現迴圈佇列,陣列下標為[0,5],且當前rear和front的值分別為0和3,當從佇列中刪除一個元素,再加入兩個元素後,rear和front的值分別為多少?(2、4 )

順序佇列:

入隊 rear += 1

出隊 front += 1

佇列長度 = rear - front

迴圈佇列:

入隊 rear = (rear + 1) % size

出隊 front = (front + 1) % size

佇列長度 = (rear - front + size) % size

  1. 訪問記錄visit表中包含日期date,使用者user_id,訪問的頁面url 3個欄位,以下哪個選項不能計算’2019-03-11’這一天訪問過頁面的所有使用者數(4)
1. SELECT count(user_id) FROM (SELECT user_id FROM visit WHERE date = '2019-03-11'  GROUP BY user_id) f
2. SELECT count(user_id) FROM (SELECT DISTINCT user_id FROM visit WHERE date = '2019-03-11') f
3. SELECT count(DISTINCT user_id) FROM visit WHERE date = '2019-03-11'
4. SELECT count(user_id) FROM visit WHERE date = '2019-03-11' GROUP BY date

問題:
1.user_id 未去重
2.where 已經選出 date 在用group by 沒用
correct_4. SELECT count(user_id) FROM visit WHERE date = '2019-03-11' GROUP BY user_id

簡答題

  1. 使用者分析是電商資料分析中重要的模組,在對使用者特徵深度理解和使用者需求充分挖掘基礎上,進行全生命週期的運營管理(拉新—>活躍—>留存—>價值提升—>忠誠),請嘗試回答以下3個問題:

    1. 使用者第一單購買的行為往往反映了使用者對平臺的信任度和消費能力。現在資料庫中有一張使用者交易表order,其中有userid(使用者ID)、amount(消費金額)、paytime(支付時間),請寫出對應的SQL語句,查出每個使用者第一單的消費金額。
      select userid,amount 
      from order where userid in(
          select userid, min(paytime)
          from order 
          group by userid
      )
    
    1. 當你發現本月的支付使用者數環比上月大幅下跌(超30%),你會如何去探查背後的原因?請描述你的思路和其中涉及的關鍵指標

    本小題主要考查使用者流失分析的基本思路:
    使用者流失該如何分析
    如何進行使用者流失原因調研?

    1. 兩層模型:細分使用者、產品、渠道,看到底是哪裡使用者流失了。注意由於是使用者流失問題,所以這裡細分使用者時可以細分使用者處在生命週期的哪個階段。
    2. 指標拆解:使用者流失數量 = 該群體使用者數量*流失率。拆解,看是因為到了這個階段的使用者數量多了(比如說大部分使用者到了衰退期),還是這個使用者群體的流失率比較高內外部分析:
      a. 內部:新手上手難度大、收費不合理、產品服務出現重大問題、活動質量低、缺少留存手段、使用者參與度低等
      b. 外部:市場、競爭對手、社會環境、節假日等
    1. 為了更好的理解使用者,我們通常會基於使用者的特徵對使用者進行分類,便於更加精細化的理解使用者,設計產品和運營玩法,請你設計對應的聚類方法,包括重點的使用者特徵的選擇及聚類演算法並說明其基本原理和步驟

    本小題主要考查使用者分類

    1. 採用RFM使用者價值模型確定使用者特徵
      R:最近一次消費(recency),代表使用者距離當前最後一次消費的時間,當然是最近一次消費的時間距今越短越好,對我們來說更有價值,更可能有效的去觸達他們。
      F:消費頻次(frequency),使用者在一段時間內,在產品內的消費頻次,重點是我們對一段時間的定義。
      M:消費金額(monetary),代表使用者的價值貢獻

    2. 聚類方法
      知識點:使用者分析六大方法論

      對於連續資料且資料量較大的資料,用Kmeans聚類法。基本原理:先隨機選取K個物件作為初始的聚類中心,然後計算每個物件與各種子聚類中心之間的距離,把每個物件分配給他最近的聚類中心。一旦全部物件被分配了,每個聚類的聚類中心會根據聚類中現有的物件被重新計算。這個過程將不斷重複直至滿足某個終止條件。

      • 用Python實現Kmeans的步驟:
        1. 匯入pandas,numpy,matplotlib等包,用read_csv/table等匯入資料
        2. 資料預處理,用astype方法做型別轉換;用duplicated方法加any方法識別冗餘資料、並用drop duplicated清除冗餘資料;用Python的isnull方法識別缺失值,並選用刪除法/插補法/替補法等處理。
        3. 用dist()函式定義距離並計算,一般使用歐式距離
        4. 先用scatter()作資料散點圖,根據散點圖設定K值,用np.random,randint隨機獲得中心點
        5. 迭代,用while迴圈,一般需要設定最大迭代次數
        6. 用matplotlib.subplots()作圖
  2. 網易嚴選是網易旗下原創生活類自營電商品牌,深度貫徹“好的生活,沒那麼貴”的品牌理念。商品覆蓋居家、餐廚、配件、服裝、洗護、母嬰、原生態飲食等幾大類目,兼具品質和價效比,得到使用者的廣泛好評。若你是網易嚴選負責商品的資料分析師,當面對以下業務問題時,你會如何解決?

    1. 使用者增長團隊期望選擇一批合適的商品用於吸引新客,期望你幫助從資料的角度篩選出一批合適的商品,你會如何幫助他們進行篩選?請描述你的思路。

題解:本小題主要考查精準營銷

  • 精準營銷的五個方面:在合適的時間、合適的地點、將合適的產品以合適的方式提供給合適的人
  1. 確定目標客戶。如母嬰產品,最主要的目標客戶孕婦或者一些準爸爸;
  2. 獲取最佳潛客的資料來源;
  3. 通過資料發掘確認潛客的“前期徵兆”;如孕婦前期可能會購買防輻射服
  4. 制定獲得顧客的流程;
  5. 採用KPI(關鍵指標)測試並不斷修正獲客流程。

參考文獻:

  1. 用大資料方法獲得新顧客
  2. 如何通過資料分析達到精準營銷的目的?
  1. 商品研發負責人期望能有一套指標幫助衡量開發的商品表現,請你幫助設計對應的評估方案,包括設計思路、涉及的資料指標等。

衡量商品表現的指標體系建立:參考文章
1、產品規模:
(1)基於使用者屬性拆分為:日(月)新增、日(月)迴流、日(月)持續活躍,也可以用日(月)新增、日(月)迴流、日(月)持續活躍來預測日活/月活,作為制定目標的依據。
(2)基於商業屬性可以拆分為:日(月)付費使用者、日(月)非付費使用者
2、使用者健康度:
(1)產品基礎指標,主要評價產品本身的執行狀態:PV、UV、新使用者數;
(2)流量質量指標,主要評價使用者流量的質量高低:跳出率、人均瀏覽次數、人均停留時間、使用者留存率、使用者回訪率;
(3)產品營收指標,主要評價產品的盈利能力與可持續性:使用者支付金額(GMV)、客單價(ARPU)、訂單轉化率;
3、產品質量:crash率、啟動耗時、頁面載入速度等,視訊播放產品還會有播放成功率、播放載入速度等;產品的下載:安裝率、解除安裝率、push關閉率也是關注指標。
4、使用者屬性(使用者畫像):
(1)人口屬性:包括性別、年齡等人的基本資訊;
(2)興趣特徵:瀏覽內容、收藏內容、閱讀諮詢、購買物品偏好等;
(3)位置特徵:使用者所處城市、所處居住區域、使用者移動軌跡等;
(4)裝置屬性:使用的終端特徵等;
(5)行為資料:訪問時間、瀏覽路徑等使用者在網站的行為日誌資料;
(6)社交資料:使用者社交相關資料;

程式設計題

	題目:
	小易在維護資料的時候遇到一個需求,具體來說小易有一系列資料,這些資料了構成一個長度為n的數字序列,接下來小易會在這個序列上進行q次操作。每次操作有一個查詢的數字x,小易需要將序列資料中所有大於等於x的數字都減一,並輸出在本次操作中有多少個數字被減一了。小易犯了難,希望你能幫幫他。
	
	輸入描述:
	第一行n,q,表示數字個數和操作個數。 
	接下來一行n個數表示初始的數字。 
	接下來q行,每行一個數,表示指定的數字x。
	1 <= n,q <= 200000,1 <= ai,x <= n
	  
	輸出描述:
	對於每個查詢,輸出一個數字表示答案
	
	輸入例子1:
	4 3
	1 2 3 4
	4
	3
	1
	輸出例子1:
	1
	2
	4
	
	輸入例子2:
	3 2
	1 2 3
	3
	3
	輸出例子:
	1
	0
	
	  
	題解:
	  1. 每次查詢一個數字x,使得大於等於x的數字都會-1,那麼數列還是有序的。也就是數列始終都是有序的
	  2. 排序後找就可以
      
	#include <stdio.h>
	#include <iostream>
	#include <vector>
	#include <algorithm>
	using namespace std;
	int main()
	{
	    vector<int> nums;
	    int m,n;
	    cin>>m>>n;
	    for(int i = 0;i < m; i++){
	        int x ; cin>>x;
	        nums.push_back(x);
	    }
	    sort(nums.begin(),nums.end(),greater<int>());
	    while(n--){
	        int x,ans = 0; cin>>x;
	        for (int i = 0; i < nums.size(); i++){
	            if (nums[i] >= x){
	                nums[i] -= 1;
	                ans ++ ;
	            }else{
	                break;
	            }
	        }
	        cout<<ans<<endl;
	    }
	    return 0;
	}
'''
	題目:
	小易學習了輾轉相除法之後,就開始實踐這個演算法在求解最大公約數上。
	牛牛給小易出了一道不同尋常的求解最大公約數: 求解a和b的最大公約數,但是a和b的範圍特別大。
	小易遇到了困難,向聰明的你尋求幫助,希望你能幫幫他。
	
	輸入描述:
	第一行數字a,第二行數字b
	1 <= a <= 10^(10^5), 1 <= b <= 10^18
	
	輸出描述:
	一行一個數字表示答案
	
	輸入例子1:
	6
	4
	輸出例子1:
	2
	
	輸入例子2:
	7951346523609888
	6998915114363550
	輸出例子2:
	1013754
	
	題解:
	輾轉相除法
	'''
	
	def fun(x,y):
	    a = max(x,y)
	    b = min(x,y)
	    if a%b == 0:
	        return b
	    else:
	    		return fun(b, a%b)
	if __name__ == '__main__':
	    x = int(input())
	    y = int(input())
	    print(fun(x,y))
	題目:
	小易有一個初始為空的數字集合,支援兩種操作:
	1、加入數字x到集合中。
	2、詢問集合中是否存在一個子集,滿足子集中所有數字的Or值恰好為k。
	Or為二進位制按位或操作,C++中表示為"|"。
	
	輸入描述:
	第一行數字q,表示操作個數 
	接下來q行,每行兩個數字: 
	1 x 表示插入數字x 
	2 x 表示詢問數字x(即題設中詢問的數值k)
	
	輸出描述:
	對於每個詢問,輸出"YES"或者"NO"表示是否存在。
	
	輸入例子1:
	9
	1 4
	2 5
	1 9
	1 15
	2 4
	1 11
	2 10
	2 7
	2 9
	
	輸出例子1:
	NO
	YES
	NO
	NO
	YES
	
	題解:
	1. 題目要求是否存在沒說 哪個子集滿足
	2. 這些子集需要滿足的條件: 和 x 與一下等於x
	3. 用臨時數字記錄 這些能和x與一下 x不變的數字
	4. 最後判斷 臨時陣列和x是否一樣
	
	#include <stdio.h>
	#include <iostream>
	#include <vector>
	using namespace std;
	int main()
	{
	    vector<int> nums;
	    bool visit[100010]={0};
	    int m;cin >> m;
	    while(m--){
	        int x,y;
	        cin>>x>>y;
	        if(x == 1 && visit[y] == false){
	            nums.push_back(y);
	            visit[y] = true;
	        }else if (x == 2){
	            int tmp  = 0;
	            for(int i = 0;i < nums.size();i++){
	                if((nums[i] | y) == y)
	                    tmp |= nums[i];
	            }
	            if(tmp == y)
	                cout<<"YES"<<endl;
	            else
	                cout<<"NO"<<endl;
	        }
	    }
	    return 0;
	}
	題目:
	小易給定了一個長度為n的數字序列,對於每一個1<=k<=n,小易希望能求解出所有長度為k的連續子序列的最大值中的最小值。
	
	輸入描述:
	第一行數字n 
	接下來一行是一個長度為n的數字序列
	1<=n<=100000,0<=ai<=10^9
	
	輸出描述:
	一行n個數字,第i個數字表示k = i時的答案
	
	示例1輸入:
	6
	1 3 2 4 6 5
	
	示例1輸出:
	1 3 3 4 6 6
	
	示例1說明:
	當k = 2的時候
	子序列分別是:
	1 3 最大值為 3
	3 2 最大值為 3
	2 4 最大值為 4
	4 6 最大值為 6
	6 5 最大值為 6
	所有最大值中的最小值為3
	
	
	#include <iostream>
	#include <algorithm>
	#include <unordered_map>
	#include <vector>
	#include <stack>
	
	using namespace std;
	const int N = 3000010;
	int V[N];
	int main(){
	    int n;
	    cin>>n;
	    for(int i = 0;i < n;++ i) scanf("%d",&V[i]);
	    vector<int> L(n,-1); //左邊第一個比下標對應的數大的數離該下標的長度
	    vector<int> R(n,-1); //右邊第一個比下標對應的數大的數離該下標的長度
	    //單調棧生產L和R陣列資訊
	    stack<int> S;
	    for(int i = 0;i < n;++ i){
	        while(!S.empty()&&V[S.top()]<V[i]){
	            int t = S.top();
	            R[t] = i - t;
	            S.pop();
	        }
	        S.push(i);
	    }
	    stack<int> S2;
	    for(int i = n-1;i >= 0;-- i){
	        while(!S2.empty()&&V[S2.top()]<V[i]){
	            int t = S2.top();
	            L[t] = t - i;
	            S2.pop();
	        }
	        S2.push(i);
	    }
	    vector<int> last(n,-1); //last[i]表示,在第i個位置,以V[i]為最大值的最大視窗的長度
	    for(int i = 0;i < n;++ i){
	        int left = 0,right = 0;
	        left = L[i] == -1? i + 1 : L[i];
	        right = R[i] == -1? n - i : R[i];
	        last[i] = left + right - 1;
	    }
	    vector<int> res(n+2,1e9);//res[k]代表長度為k的視窗能取到的最大值
	
	    /**************************************************************************
	    生成答案演算法:
	    對於每一個V[i]用其能達到的最大長度last[i],去更新所對應的res[last[i]]:
	    res[last[i]] = min(res[last[i]],V[i]);
	    最後,將res陣列中,沒有更新到的數進行補齊,res[k]跟新就是從k開始,找一個
	    已經更新過的res[i],遇到的第一個res[i]就是res[k]的值,所以:
	    res[i] = min(res[i],res[i+1]);
	    **************************************************************************/
	    for(int i = 0;i < n;i ++){
	        res[last[i]] = min(V[i],res[last[i]]);
	    }
	    for(int i = n;i;i --){
	        res[i] = min(res[i],res[i+1]);
	    }
	    for(int i = 1;i <= n;++ i) printf("%d ",res[i]);
	    return 0;
	}

相關文章