一看就懂的K近鄰算法(KNN),K-D樹,並實現手寫數字識別!

1. 什麼是KNN

1.1 KNN的通俗解釋

何謂K近鄰算法,即K-Nearest Neighbor algorithm,簡稱KNN算法,單從名字來猜測,能夠簡單粗暴的認爲是:K個最近的鄰居,當K=1時,算法便成了最近鄰算法,即尋找最近的那個鄰居。html

用官方的話來講,所謂K近鄰算法,便是給定一個訓練數據集,對新的輸入實例,在訓練數據集中找到與該實例最鄰近的K個實例(也就是上面所說的K個鄰居),這K個實例的多數屬於某個類,就把該輸入實例分類到這個類中。git

如上圖所示,有兩類不一樣的樣本數據,分別用藍色的小正方形和紅色的小三角形表示,而圖正中間的那個綠色的圓所標示的數據則是待分類的數據。也就是說,如今,咱們不知道中間那個綠色的數據是從屬於哪一類(藍色小正方形or紅色小三角形),KNN就是解決這個問題的。github

若是K=3,綠色圓點的最近的3個鄰居是2個紅色小三角形和1個藍色小正方形,少數從屬於多數,基於統計的方法,斷定綠色的這個待分類點屬於紅色的三角形一類。面試

若是K=5,綠色圓點的最近的5個鄰居是2個紅色三角形和3個藍色的正方形,仍是少數從屬於多數,基於統計的方法,斷定綠色的這個待分類點屬於藍色的正方形一類。算法

於此咱們看到,當沒法斷定當前待分類點是從屬於已知分類中的哪一類時,咱們能夠依據統計學的理論看它所處的位置特徵,衡量它周圍鄰居的權重,而把它歸爲(或分配)到權重更大的那一類。這就是K近鄰算法的核心思想。數據結構

1.2 近鄰的距離度量

咱們看到,K近鄰算法的核心在於找到實例點的鄰居,這個時候,問題就接踵而至了,如何找到鄰居,鄰居的斷定標準是什麼,用什麼來度量。這一系列問題即是下面要講的距離度量表示法。機器學習

有哪些距離度量的表示法(普及知識點,能夠跳過):函數

  1. 歐氏距離,最多見的兩點之間或多點之間的距離表示法,又稱之爲歐幾里得度量,它定義於歐幾里得空間中,如點 x = (x1,...,xn) 和 y = (y1,...,yn) 之間的距離爲:學習

    $$d(x,y)=\sqrt{(x_1-y_1)^2+(x_2-y_2)^2+...+(x_n-y_n)^2}=\sqrt{\sum_{i=1}^{n}(x_i-y_i)^2}$$測試

    • 二維平面上兩點a(x1,y1)與b(x2,y2)間的歐氏距離:

      $$d_{12}=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}$$

    • 三維空間兩點a(x1,y1,z1)與b(x2,y2,z2)間的歐氏距離:

      $$d_{12}=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}$$

    • 兩個n維向量a(x11,x12,…,x1n)與 b(x21,x22,…,x2n)間的歐氏距離:

      $$d_{12}=\sqrt{\sum_{k=1}^{n}(x_{1k}-x_{2k})^2}$$

      也能夠用表示成向量運算的形式:

      $$d_{12}=\sqrt{(a-b)(a-b)^T}$$

  2. 曼哈頓距離,咱們能夠定義曼哈頓距離的正式意義爲L1-距離或城市區塊距離,也就是在歐幾里得空間的固定直角座標系上兩點所造成的線段對軸產生的投影的距離總和。例如在平面上,座標(x1, y1)的點P1與座標(x2, y2)的點P2的曼哈頓距離爲:$|x_1-x_2|+|y_1-y_2|$,要注意的是,曼哈頓距離依賴座標系統的轉度,而非系統在座標軸上的平移或映射。

    通俗來說,想象你在曼哈頓要從一個十字路口開車到另一個十字路口,駕駛距離是兩點間的直線距離嗎?顯然不是,除非你能穿越大樓。而實際駕駛距離就是這個「曼哈頓距離」,此即曼哈頓距離名稱的來源, 同時,曼哈頓距離也稱爲城市街區距離(City Block distance)。

    • 二維平面兩點a(x1,y1)與b(x2,y2)間的曼哈頓距離

      $$d_{12}=|x_1-x_2|+|y_1-y_2|$$

    • 兩個n維向量a(x11,x12,…,x1n)與 b(x21,x22,…,x2n)間的曼哈頓距離

      $$d_{12}=\sum_{k=1}^{n}|x_{1k}-x_{2k}|$$

  3. 切比雪夫距離,若二個向量或二個點p 、and q,其座標分別爲Pi及qi,則二者之間的切比雪夫距離定義以下:

    $$D_{Chebyshev}(p,q)=max_i(|p_i-q_i|)$$

    這也等於如下Lp度量的極值:$\lim_{x \to \infty}(\sum_{i=1}^{n}|p_i-q_i|^k)^{1/k}$,所以切比雪夫距離也稱爲L∞度量。

    以數學的觀點來看,切比雪夫距離是由一致範數(uniform norm)(或稱爲上確界範數)所衍生的度量,也是超凸度量(injective metric space)的一種。

    在平面幾何中,若二點p及q的直角座標系座標爲(x1,y1)及(x2,y2),則切比雪夫距離爲:

    $$D_{Chess}=max(|x_2-x_1|,|y_2-y_1|)$$

    玩過國際象棋的朋友或許知道,國王走一步可以移動到相鄰的8個方格中的任意一個。那麼國王從格子(x1,y1)走到格子(x2,y2)最少須要多少步?。你會發現最少步數老是max( | x2-x1 | , | y2-y1 | ) 步 。有一種相似的一種距離度量方法叫切比雪夫距離。

    • 二維平面兩點a(x1,y1)與b(x2,y2)間的切比雪夫距離 :

      $$d_{12}=max(|x_2-x_1|,|y_2-y_1|)$$

    • 兩個n維向量a(x11,x12,…,x1n)與 b(x21,x22,…,x2n)間的切比雪夫距離:

      $$d_{12}=max_i(|x_{1i}-x_{2i}|)$$

      這個公式的另外一種等價形式是

      $$d_{12}=lim_{k\to\infin}(\sum_{i=1}^{n}|x_{1i}-x_{2i}|^k)^{1/k}$$

  4. 閔可夫斯基距離(Minkowski Distance),閔氏距離不是一種距離,而是一組距離的定義。

    兩個n維變量a(x11,x12,…,x1n)與 b(x21,x22,…,x2n)間的閔可夫斯基距離定義爲:

    $$d_{12}=\sqrt[p]{\sum_{k=1}^{n}|x_{1k}-x_{2k}|^p}$$

    其中p是一個變參數。 當p=1時,就是曼哈頓距離 當p=2時,就是歐氏距離 當p→∞時,就是切比雪夫距離
    根據變參數的不一樣,閔氏距離能夠表示一類的距離。

  5. 標準化歐氏距離,標準化歐氏距離是針對簡單歐氏距離的缺點而做的一種改進方案。標準歐氏距離的思路:既然數據各維份量的分佈不同,那先將各個份量都「標準化」到均值、方差相等。至於均值和方差標準化到多少,先複習點統計學知識。

    假設樣本集X的數學指望或均值(mean)爲m,標準差(standard deviation,方差開根)爲s,那麼X的「標準化變量」X*表示爲:(X-m)/s,並且標準化變量的數學指望爲0,方差爲1。 即,樣本集的標準化過程(standardization)用公式描述就是:

    $$X^*=\frac{X-m}{s}$$

    標準化後的值 = ( 標準化前的值 - 份量的均值 ) /份量的標準差   通過簡單的推導就能夠獲得兩個n維向量a(x11,x12,…,x1n)與 b(x21,x22,…,x2n)間的標準化歐氏距離的公式:

    $$d_{12}=\sqrt{\sum_{k=1}^{n}(\frac{x_{1k}-x_{2k}}{s_k})^2}$$

  6. 馬氏距離

    有M個樣本向量X1~Xm,協方差矩陣記爲S,均值記爲向量μ,則其中樣本向量X到u的馬氏距離表示爲:

    $$D(X)=\sqrt{(X-u)^TS^{-1}(X_i-X_j)}$$

    • 若協方差矩陣是單位矩陣(各個樣本向量之間獨立同分布),則公式就成了,也就是歐氏距離了:

      $$D(X_i,X_j)=\sqrt{(X_i-X_j)^T(X_i-X_j)}$$

    • 若協方差矩陣是對角矩陣,公式變成了標準化歐氏距離。

    馬氏距離的優缺點:量綱無關,排除變量之間的相關性的干擾。

  7. 巴氏距離

    在統計中,巴氏距離距離測量兩個離散或連續機率分佈的類似性。它與衡量兩個統計樣品或種羣之間的重疊量的巴氏距離係數密切相關。巴氏距離距離和巴氏距離係數以20世紀30年代曾在印度統計研究所工做的一個統計學家A. Bhattacharya命名。同時,Bhattacharyya係數能夠被用來肯定兩個樣本被認爲相對接近的,它是用來測量中的類分類的可分離性。

    對於離散機率分佈 p和q在同一域 X,它被定義爲:

    $$D_B(p,q)=-ln(BC(p,q))$$

    其中:

    $$BC(p,q)=\sum_{x\in_{}X}\sqrt{p(x)q(x)}$$

    是Bhattacharyya係數。

  8. 漢明距離

    兩個等長字符串s1與s2之間的漢明距離定義爲將其中一個變爲另一個所須要做的最小替換次數。例如字符串「1111」與「1001」之間的漢明距離爲2。應用:信息編碼(爲了加強容錯性,應使得編碼間的最小漢明距離儘量大)。

  9. 夾角餘弦

    幾何中夾角餘弦可用來衡量兩個向量方向的差別,機器學習中借用這一律念來衡量樣本向量之間的差別。

    • 在二維空間中向量A(x1,y1)與向量B(x2,y2)的夾角餘弦公式:

      $$cos\theta=\frac{x_1x_2+y_1y_2}{\sqrt{x_1^2+y_1^2}\sqrt{x_2^2+y_2^2}}$$

    • 兩個n維樣本點a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夾角餘弦:

      $$cos\theta=\frac{a*b}{|a||b|}$$

    夾角餘弦取值範圍爲[-1,1]。夾角餘弦越大表示兩個向量的夾角越小,夾角餘弦越小表示兩向量的夾角越大。當兩個向量的方向重合時夾角餘弦取最大值1,當兩個向量的方向徹底相反夾角餘弦取最小值-1。

  10. 傑卡德類似係數

    兩個集合A和B的交集元素在A,B的並集中所佔的比例,稱爲兩個集合的傑卡德類似係數,用符號J(A,B)表示。傑卡德類似係數是衡量兩個集合的類似度一種指標。

    $$J(A,B)=\frac{|A\cap_{}B|}{|A\cup_{}B|}$$

    與傑卡德類似係數相反的概念是傑卡德距離:

    $$J_{\delta}(A,B)=1-J(A,B)=\frac{|A\cup_{}B|-|A\cap_{}B|}{|A\cup_{}B|}$$

  11. 皮爾遜係數

    在統計學中,皮爾遜積矩相關係數用於度量兩個變量X和Y之間的相關(線性相關),其值介於-1與1之間。一般狀況下經過如下取值範圍判斷變量的相關強度:

    0.8-1.0 極強相關 0.6-0.8 強相關 0.4-0.6 中等程度相關 0.2-0.4 弱相關 0.0-0.2 極弱相關或無相關

簡單說來,各類「距離」的應用場景簡單歸納爲,

  • 空間:歐氏距離,
  • 路徑:曼哈頓距離,國際象棋國王:切比雪夫距離,
  • 以上三種的統一形式:閔可夫斯基距離,
  • 加權:標準化歐氏距離,
  • 排除量綱和依存:馬氏距離,
  • 向量差距:夾角餘弦,
  • 編碼差異:漢明距離,
  • 集合近似度:傑卡德相似係數與距離,
  • 相關:相關係數與相關距離。

1.3 K值選擇

  1. 若是選擇較小的K值,就至關於用較小的領域中的訓練實例進行預測,「學習」近似偏差會減少,只有與輸入實例較近或類似的訓練實例纔會對預測結果起做用,與此同時帶來的問題是「學習」的估計偏差會增大,換句話說,K值的減少就意味着總體模型變得複雜,容易發生過擬合;
  2. 若是選擇較大的K值,就至關於用較大領域中的訓練實例進行預測,其優勢是能夠減小學習的估計偏差,但缺點是學習的近似偏差會增大。這時候,與輸入實例較遠(不類似的)訓練實例也會對預測器做用,使預測發生錯誤,且K值的增大就意味着總體的模型變得簡單。
  3. K=N,則徹底不足取,由於此時不管輸入實例是什麼,都只是簡單的預測它屬於在訓練實例中最多的累,模型過於簡單,忽略了訓練實例中大量有用信息。

在實際應用中,K值通常取一個比較小的數值,例如採用交叉驗證法(簡單來講,就是一部分樣本作訓練集,一部分作測試集)來選擇最優的K值。

1.4 KNN最近鄰分類算法的過程

  1. 計算測試樣本和訓練樣本中每一個樣本點的距離(常見的距離度量有歐式距離,馬氏距離等);
  2. 對上面全部的距離值進行排序;
  3. 選前 k 個最小距離的樣本;
  4. 根據這 k 個樣本的標籤進行投票,獲得最後的分類類別;

2. KDD的實現:KD樹

Kd-樹是K-dimension tree的縮寫,是對數據點在k維空間(如二維(x,y),三維(x,y,z),k維(x1,y,z..))中劃分的一種數據結構,主要應用於多維空間關鍵數據的搜索(如:範圍搜索和最近鄰搜索)。本質上說,Kd-樹就是一種平衡二叉樹。

首先必須搞清楚的是,k-d樹是一種空間劃分樹,說白了,就是把整個空間劃分爲特定的幾個部分,而後在特定空間的部份內進行相關搜索操做。想像一個三維(多維有點爲難你的想象力了)空間,kd樹按照必定的劃分規則把這個三維空間劃分了多個空間,以下圖所示:

2.1 構建KD樹

kd樹構建的僞代碼以下圖所示:

再舉一個簡單直觀的實例來介紹k-d樹構建算法。假設有6個二維數據點{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},數據點位於二維空間內,以下圖所示。爲了能有效的找到最近鄰,k-d樹採用分而治之的思想,即將整個空間劃分爲幾個小部分,首先,粗黑線將空間一分爲二,而後在兩個子空間中,細黑直線又將整個空間劃分爲四部分,最後虛黑直線將這四部分進一步劃分。

6個二維數據點{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}構建kd樹的具體步驟爲:

  1. 肯定:split域=x。具體是:6個數據點在x,y維度上的數據方差分別爲39,28.63,因此在x軸上方差更大,故split域值爲x;
  2. 肯定:Node-data = (7,2)。具體是:根據x維上的值將數據排序,6個數據的中值(所謂中值,即中間大小的值)爲7,因此Node-data域位數據點(7,2)。這樣,該節點的分割超平面就是經過(7,2)並垂直於:split=x軸的直線x=7;
  3. 肯定:左子空間和右子空間。具體是:分割超平面x=7將整個空間分爲兩部分:x<=7的部分爲左子空間,包含3個節點={(2,3),(5,4),(4,7)};另外一部分爲右子空間,包含2個節點={(9,6),(8,1)};
  4. 如上算法所述,kd樹的構建是一個遞歸過程,咱們對左子空間和右子空間內的數據重複根節點的過程就能夠獲得一級子節點(5,4)和(9,6),同時將空間和數據集進一步細分,如此往復直到空間中只包含一個數據點。

與此同時,通過對上面所示的空間劃分以後,咱們能夠看出,點(7,2)能夠爲根結點,從根結點出發的兩條紅粗斜線指向的(5,4)和(9,6)則爲根結點的左右子結點,而(2,3),(4,7)則爲(5,4)的左右孩子(經過兩條細紅斜線相連),最後,(8,1)爲(9,6)的左孩子(經過細紅斜線相連)。如此,便造成了下面這樣一棵k-d樹:

對於n個實例的k維數據來講,創建kd-tree的時間複雜度爲O(knlogn)。

k-d樹算法能夠分爲兩大部分,除了上部分有關k-d樹自己這種數據結構創建的算法,另外一部分是在創建的k-d樹上各類諸如插入,刪除,查找(最鄰近查找)等操做涉及的算法。下面,我們依次來看kd樹的插入、刪除、查找操做。

2.2 KD樹的插入

元素插入到一個K-D樹的方法和二叉檢索樹相似。本質上,在偶數層比較x座標值,而在奇數層比較y座標值。當咱們到達了樹的底部,(也就是當一個空指針出現),咱們也就找到告終點將要插入的位置。生成的K-D樹的形狀依賴於結點插入時的順序。給定N個點,其中一個結點插入和檢索的平均代價是O(log2N)。

插入的過程以下:

應該清楚,這裏描述的插入過程當中,每一個結點將其所在的平面分割成兩部分。因比,Chicago 將平面上全部結點分紅兩部分,一部分全部的結點x座標值小於35,另外一部分結點的x座標值大於或等於35。一樣Mobile將全部x座標值大於35的結點以分紅兩部分,一部分結點的Y座標值是小於10,另外一部分結點的Y座標值大於或等於10。後面的Toronto、Buffalo也按照一分爲二的規則繼續劃分。

2.3 KD樹的刪除

KD樹的刪除能夠用遞歸程序來實現。咱們假設但願從K-D樹中刪除結點(a,b)。若是(a,b)的兩個子樹都爲空,則用空樹來代替(a,b)。不然,在(a,b)的子樹中尋找一個合適的結點來代替它,譬如(c,d),則遞歸地從K-D樹中刪除(c,d)。一旦(c,d)已經被刪除,則用(c,d)代替(a,b)。假設(a,b)是一個X識別器,那麼,它得替代節點要麼是(a,b)左子樹中的X座標最大值的結點,要麼是(a,b)右子樹中x座標最小值的結點。

下面來舉一個實際的例子(來源:中國地質大學電子課件,原課件錯誤已經在下文中訂正),以下圖所示,原始圖像及對應的kd樹,如今要刪除圖中的A結點,請看一系列刪除步驟:

要刪除上圖中結點A,選擇結點A的右子樹中X座標值最小的結點,這裏是C,C成爲根,以下圖:

從C的右子樹中找出一個結點代替先前C的位置,

這裏是D,並將D的左子樹轉爲它的右子樹,D代替先前C的位置,以下圖:

在D的新右子樹中,找X座標最小的結點,這裏爲H,H代替D的位置,

在D的右子樹中找到一個Y座標最小的值,這裏是I,將I代替原先H的位置,從而A結點從圖中順利刪除,以下圖所示:

從K-D樹中刪除一個結點是代價很高的,很清楚刪除子樹的根受到子樹中結點個數的限制。用TPL(T)表示樹T總的路徑長度。可看出樹中子樹大小的總和爲TPL(T)+N。 以隨機方式插入N個點造成樹的TPL是O(N*log2N),這就意味着從一個隨機造成的K-D樹中刪除一個隨機選取的結點平均代價的上界是O(log2N) 。

2.4 KD樹的最近鄰搜索算法

k-d樹查詢算法的僞代碼以下所示:

我寫了一個遞歸版本的二維kd tree的搜索函數你對比的看看:

舉例

星號表示要查詢的點查詢點(2,4.5)。經過二叉搜索,順着搜索路徑很快就能找到最鄰近的近似點。而找到的葉子節點並不必定就是最鄰近的,最鄰近確定距離查詢點更近,應該位於以查詢點爲圓心且經過葉子節點的圓域內。爲了找到真正的最近鄰,還須要進行相關的‘回溯'操做。也就是說,算法首先沿搜索路徑反向查找是否有距離查詢點更近的數據點。

  1. 二叉樹搜索:先從(7,2)查找到(5,4)節點,在進行查找時是由y = 4爲分割超平面的,因爲查找點爲y值爲4.5,所以進入右子空間查找到(4,7),造成搜索路徑<(7,2),(5,4),(4,7)>,但(4,7)與目標查找點的距離爲3.202,而(5,4)與查找點之間的距離爲3.041,因此(5,4)爲查詢點的最近點;
  2. 回溯查找:以(2,4.5)爲圓心,以3.041爲半徑做圓,以下圖所示。可見該圓和y = 4超平面交割,因此須要進入(5,4)左子空間進行查找,也就是將(2,3)節點加入搜索路徑中得<(7,2),(2,3)>;因而接着搜索至(2,3)葉子節點,(2,3)距離(2,4.5)比(5,4)要近,因此最近鄰點更新爲(2,3),最近距離更新爲1.5;
  3. 回溯查找至(5,4),直到最後回溯到根結點(7,2)的時候,以(2,4.5)爲圓心1.5爲半徑做圓,並不和x = 7分割超平面交割,以下圖所示。至此,搜索路徑回溯完,返回最近鄰點(2,3),最近距離1.5。

2.5 kd樹近鄰搜索算法的改進:BBF算法

實例點是隨機分佈的,那麼kd樹搜索的平均計算複雜度是O(logN),這裏的N是訓練實例樹。因此說,kd樹更適用於訓練實例數遠大於空間維數時的k近鄰搜索,當空間維數接近訓練實例數時,它的效率會迅速降低,一降降到「解放前」:線性掃描的速度。

也正由於上述k最近鄰搜索算法的第4個步驟中的所述:「回退到根結點時,搜索結束」,每一個最近鄰點的查詢比較完成過程最終都要回退到根結點而結束,而致使了許多沒必要要回溯訪問和比較到的結點,這些多餘的損耗在高維度數據查找的時候,搜索效率將變得至關之地下,那有什麼辦法能夠改進這個原始的kd樹最近鄰搜索算法呢?

從上述標準的kd樹查詢過程能夠看出其搜索過程當中的「回溯」是由「查詢路徑」決定的,並無考慮查詢路徑上一些數據點自己的一些性質。一個簡單的改進思路就是將「查詢路徑」上的結點進行排序,如按各自分割超平面(也稱bin)與查詢點的距離排序,也就是說,回溯檢查老是從優先級最高(Best Bin)的樹結點開始。

仍是以上面的查詢(2,4.5)爲例,搜索的算法流程爲:

  1. 將(7,2)壓人優先隊列中;
  2. 提取優先隊列中的(7,2),因爲(2,4.5)位於(7,2)分割超平面的左側,因此檢索其左子結點(5,4)。
  3. 同時,根據BBF機制」搜索左/右子樹,就把對應這一層的兄弟結點即右/左結點存進隊列」,將其(5,4)對應的兄弟結點即右子結點(9,6)壓人優先隊列中
  4. 此時優先隊列爲{(9,6)},最佳點爲(7,2);而後一直檢索到葉子結點(4,7),此時優先隊列爲{(2,3),(9,6)},「最佳點」則爲(5,4);
  5. 提取優先級最高的結點(2,3),重複步驟2,直到優先隊列爲空。

2.6 KD樹的應用

SIFT+KD_BBF搜索算法,詳細參考文末的參考文獻。

3. 關於KNN的一些問題

  1. 在k-means或kNN,咱們是用歐氏距離來計算最近的鄰居之間的距離。爲何不用曼哈頓距離?

    **答:**咱們不用曼哈頓距離,由於它只計算水平或垂直距離,有維度的限制。另外一方面,歐式距離可用於任何空間的距離計算問題。由於,數據點能夠存在於任何空間,歐氏距離是更可行的選擇。例如:想象一下國際象棋棋盤,象或車所作的移動是由曼哈頓距離計算的,由於它們是在各自的水平和垂直方向的運動。

  2. KD-Tree相比KNN來進行快速圖像特徵比對的好處在哪裏?

    答:極大的節約了時間成本.點線距離若是 > 最小點,無需回溯上一層,若是<,則再上一層尋找。

4. 參考文獻

從K近鄰算法、距離度量談到KD樹、SIFT+BBF算法

5. 手寫數字識別案例

KNN手寫數字識別系統

機器學習通俗易懂系列文章

3.png


做者:@mantchs

GitHub:https://github.com/NLP-LOVE/ML-NLP

歡迎你們加入討論!共同完善此項目!羣號:【541954936】<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=863f915b9178560bd32ca07cd090a7d9e6f5f90fcff5667489697b1621cecdb3"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="NLP面試學習羣" title="NLP面試學習羣"></a>

原文出處:https://www.cnblogs.com/mantch/p/11287075.html

相關文章
相關標籤/搜索