精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

DFS 算法秒殺五道島嶼問題

開發 前端 算法
島嶼問題是經典的面試高頻題,雖然基本的島嶼問題并不難,但是島嶼問題有一些有意思的擴展,比如求子島嶼數量,求形狀不同的島嶼數量等等,本文就來把這些問題一網打盡。

 [[429450]]

本文轉載自微信公眾號「labuladong」,作者labuladong。轉載本文請聯系labuladong公眾號。

島嶼問題是經典的面試高頻題,雖然基本的島嶼問題并不難,但是島嶼問題有一些有意思的擴展,比如求子島嶼數量,求形狀不同的島嶼數量等等,本文就來把這些問題一網打盡。

島嶼系列問題的核心考點就是用 DFS/BFS 算法遍歷二維數組。

本文主要來講解如何用 DFS 算法來秒殺島嶼系列問題,不過用 BFS 算法的核心思路是完全一樣的,無非就是把 DFS 改寫成 BFS 而已。

那么如何在二維矩陣中使用 DFS 搜索呢?如果你把二維矩陣中的每一個位置看做一個節點,這個節點的上下左右四個位置就是相鄰節點,那么整個矩陣就可以抽象成一幅網狀的「圖」結構。

根據 學習數據結構和算法的框架思維,完全可以根據二叉樹的遍歷框架改寫出二維矩陣的 DFS 代碼框架:

  1. // 二叉樹遍歷框架 
  2. void traverse(TreeNode root) { 
  3.     traverse(root.left); 
  4.     traverse(root.right); 
  5.  
  6. // 二維矩陣遍歷框架 
  7. void dfs(int[][] grid, int i, int j, boolean[] visited) { 
  8.     int m = grid.length, n = grid[0].length; 
  9.     if (i < 0 || j < 0 || i >= m || j >= n) { 
  10.         // 超出索引邊界 
  11.         return
  12.     } 
  13.     if (visited[i][j]) { 
  14.         // 已遍歷過 (i, j) 
  15.         return
  16.     } 
  17.     // 進入節點 (i, j) 
  18.     visited[i][j] = true
  19.     dfs(grid, i - 1, j); // 上 
  20.     dfs(grid, i + 1, j); // 下 
  21.     dfs(grid, i, j - 1); // 左 
  22.     dfs(grid, i, j + 1); // 右 
  23.     // 離開節點 (i, j) 
  24.     visited[i][j] = false

因為二維矩陣本質上是一幅「圖」,所以遍歷的過程中需要一個visited布爾數組防止走回頭路,如果你能理解上面這段代碼,那么搞定所有島嶼問題都很簡單。

這里額外說一個處理二維數組的常用小技巧,你有時會看到使用「方向數組」來處理上下左右的遍歷,和前文 圖遍歷框架 的代碼很類似:

  1. // 方向數組,分別代表上、下、左、右 
  2. int[][] dirs = new int[][]{{-1,0}, {1,0}, {0,-1}, {0,1}}; 
  3.  
  4. void dfs(int[][] grid, int i, int j, boolean[] visited) { 
  5.     int m = grid.length, n = grid[0].length; 
  6.     if (i < 0 || j < 0 || i >= m || j >= n) { 
  7.         // 超出索引邊界 
  8.         return
  9.     } 
  10.     if (visited[i][j]) { 
  11.         // 已遍歷過 (i, j) 
  12.         return
  13.     } 
  14.  
  15.     // 進入節點 (i, j) 
  16.     visited[i][j] = true
  17.     // 遞歸遍歷上下左右的節點 
  18.     for (int[] d : dirs) { 
  19.         int next_i = i + d[0]; 
  20.         int next_j = j + d[1]; 
  21.         dfs(grid, next_i, next_j); 
  22.     } 
  23.     // 離開節點 (i, j) 
  24.     visited[i][j] = false

這種寫法無非就是用 for 循環處理上下左右的遍歷罷了,你可以按照個人喜好選擇寫法。

島嶼數量

這是力扣第 200 題「島嶼數量」,最簡單也是最經典的一道島嶼問題,題目會輸入一個二維數組grid,其中只包含0或者1,0代表海水,1代表陸地,且假設該矩陣四周都是被海水包圍著的。

我們說連成片的陸地形成島嶼,那么請你寫一個算法,計算這個矩陣grid中島嶼的個數,函數簽名如下:

  1. int numIslands(char[][] grid); 

比如說題目給你輸入下面這個grid有四片島嶼,算法應該返回 4:

思路很簡單,關鍵在于如何尋找并標記「島嶼」,這就要 DFS 算法發揮作用了,我們直接看解法代碼:

  1. // 主函數,計算島嶼數量 
  2. int numIslands(char[][] grid) { 
  3.     int res = 0; 
  4.     int m = grid.length, n = grid[0].length; 
  5.     // 遍歷 grid 
  6.     for (int i = 0; i < m; i++) { 
  7.         for (int j = 0; j < n; j++) { 
  8.             if (grid[i][j] == '1') { 
  9.                 // 每發現一個島嶼,島嶼數量加一 
  10.                 res++; 
  11.                 // 然后使用 DFS 將島嶼淹了 
  12.                 dfs(grid, i, j); 
  13.             } 
  14.         } 
  15.     } 
  16.     return res; 
  17.  
  18. // 從 (i, j) 開始,將與之相鄰的陸地都變成海水 
  19. void dfs(char[][] grid, int i, int j) { 
  20.     int m = grid.length, n = grid[0].length; 
  21.     if (i < 0 || j < 0 || i >= m || j >= n) { 
  22.         // 超出索引邊界 
  23.         return
  24.     } 
  25.     if (grid[i][j] == '0') { 
  26.         // 已經是海水了 
  27.         return
  28.     } 
  29.     // 將 (i, j) 變成海水 
  30.     grid[i][j] = '0'
  31.     // 淹沒上下左右的陸地 
  32.     dfs(grid, i + 1, j); 
  33.     dfs(grid, i, j + 1); 
  34.     dfs(grid, i - 1, j); 
  35.     dfs(grid, i, j - 1); 

為什么每次遇到島嶼,都要用 DFS 算法把島嶼「淹了」呢?主要是為了省事,避免維護visited數組。

因為dfs函數遍歷到值為0的位置會直接返回,所以只要把經過的位置都設置為0,就可以起到不走回頭路的作用。

PS:這類 DFS 算法還有個別名叫做 FloodFill 算法,現在有沒有覺得 FloodFill 這個名字還挺貼切的~

這個最最基本的島嶼問題就說到這,我們來看看后面的題目有什么花樣。

封閉島嶼的數量

上一題說二維矩陣四周可以認為也是被海水包圍的,所以靠邊的陸地也算作島嶼。

力扣第 1254 題「統計封閉島嶼的數目」和上一題有兩點不同:

1、用0表示陸地,用1表示海水。

2、讓你計算「封閉島嶼」的數目。所謂「封閉島嶼」就是上下左右全部被1包圍的0,也就是說靠邊的陸地不算作「封閉島嶼」。

函數簽名如下:

  1. int closedIsland(int[][] grid) 

比如題目給你輸入如下這個二維矩陣:

算法返回 2,只有圖中灰色部分的0是四周全都被海水包圍著的「封閉島嶼」。

那么如何判斷「封閉島嶼」呢?其實很簡單,把上一題中那些靠邊的島嶼排除掉,剩下的不就是「封閉島嶼」了嗎?

有了這個思路,就可以直接看代碼了,注意這題規定0表示陸地,用1表示海水:

  1. // 主函數:計算封閉島嶼的數量 
  2. int closedIsland(int[][] grid) { 
  3.     int m = grid.length, n = grid[0].length; 
  4.     for (int j = 0; j < n; j++) { 
  5.         // 把靠上邊的島嶼淹掉 
  6.         dfs(grid, 0, j); 
  7.         // 把靠下邊的島嶼淹掉 
  8.         dfs(grid, m - 1, j); 
  9.     } 
  10.     for (int i = 0; i < m; i++) { 
  11.         // 把靠左邊的島嶼淹掉 
  12.         dfs(grid, i, 0); 
  13.         // 把靠右邊的島嶼淹掉 
  14.         dfs(grid, i, n - 1); 
  15.     } 
  16.     // 遍歷 grid,剩下的島嶼都是封閉島嶼 
  17.     int res = 0; 
  18.     for (int i = 0; i < m; i++) { 
  19.         for (int j = 0; j < n; j++) { 
  20.             if (grid[i][j] == 0) { 
  21.                 res++; 
  22.                 dfs(grid, i, j); 
  23.             } 
  24.         } 
  25.     } 
  26.     return res; 
  27.  
  28. // 從 (i, j) 開始,將與之相鄰的陸地都變成海水 
  29. void dfs(int[][] grid, int i, int j) { 
  30.     int m = grid.length, n = grid[0].length; 
  31.     if (i < 0 || j < 0 || i >= m || j >= n) { 
  32.         return
  33.     } 
  34.     if (grid[i][j] == 1) { 
  35.         // 已經是海水了 
  36.         return
  37.     } 
  38.     // 將 (i, j) 變成海水 
  39.     grid[i][j] = 1; 
  40.     // 淹沒上下左右的陸地 
  41.     dfs(grid, i + 1, j); 
  42.     dfs(grid, i, j + 1); 
  43.     dfs(grid, i - 1, j); 
  44.     dfs(grid, i, j - 1); 

只要提前把靠邊的陸地都淹掉,然后算出來的就是封閉島嶼了。

PS:處理這類島嶼問題除了 DFS/BFS 算法之外,Union Find 并查集算法也是一種可選的方法,前文 Union Find 算法運用 就用 Union Find 算法解決了一道類似的問題。

這道島嶼題目的解法稍微改改就可以解決力扣第 1020 題「飛地的數量」,這題不讓你求封閉島嶼的數量,而是求封閉島嶼的面積總和。

其實思路都是一樣的,先把靠邊的陸地淹掉,然后去數剩下的陸地數量就行了,注意第 1020 題中1代表陸地,0代表海水:

  1. int numEnclaves(int[][] grid) { 
  2.     int m = grid.length, n = grid[0].length; 
  3.     // 淹掉靠邊的陸地 
  4.     for (int i = 0; i < m; i++) { 
  5.         dfs(grid, i, 0); 
  6.         dfs(grid, i, n - 1); 
  7.     } 
  8.     for (int j = 0; j < n; j++) { 
  9.         dfs(grid, 0, j); 
  10.         dfs(grid, m - 1, j); 
  11.     } 
  12.  
  13.     // 數一數剩下的陸地 
  14.     int res = 0; 
  15.     for (int i = 0; i < m; i++) { 
  16.         for (int j = 0; j < n; j++) { 
  17.             if (grid[i][j] == 1) { 
  18.                 res += 1; 
  19.             } 
  20.         } 
  21.     } 
  22.  
  23.     return res; 
  24.  
  25. // 和之前的實現類似 
  26. void dfs(int[][] grid, int i, int j) { 
  27.     // ... 

篇幅所限,具體代碼我就不寫了,我們繼續看其他的島嶼問題。

島嶼的最大面積

這是力扣第 695 題「島嶼的最大面積」,0表示海水,1表示陸地,現在不讓你計算島嶼的個數了,而是讓你計算最大的那個島嶼的面積,函數簽名如下:

  1. int maxAreaOfIsland(int[][] grid) 

比如題目給你輸入如下一個二維矩陣:

其中面積最大的是橘紅色的島嶼,算法返回它的面積 6。

這題的大體思路和之前完全一樣,只不過dfs函數淹沒島嶼的同時,還應該想辦法記錄這個島嶼的面積。

我們可以給dfs函數設置返回值,記錄每次淹沒的陸地的個數,直接看解法吧:

  1. int maxAreaOfIsland(int[][] grid) { 
  2.     // 記錄島嶼的最大面積 
  3.     int res = 0; 
  4.     int m = grid.length, n = grid[0].length; 
  5.     for (int i = 0; i < m; i++) { 
  6.         for (int j = 0; j < n; j++) { 
  7.             if (grid[i][j] == 1) { 
  8.                 // 淹沒島嶼,并更新最大島嶼面積 
  9.                 res = Math.max(res, dfs(grid, i, j)); 
  10.             } 
  11.         } 
  12.     } 
  13.     return res; 
  14.  
  15. // 淹沒與 (i, j) 相鄰的陸地,并返回淹沒的陸地面積 
  16. int dfs(int[][] grid, int i, int j) { 
  17.     int m = grid.length, n = grid[0].length; 
  18.     if (i < 0 || j < 0 || i >= m || j >= n) { 
  19.         // 超出索引邊界 
  20.         return 0; 
  21.     } 
  22.     if (grid[i][j] == 0) { 
  23.         // 已經是海水了 
  24.         return 0; 
  25.     } 
  26.     // 將 (i, j) 變成海水 
  27.     grid[i][j] = 0; 
  28.  
  29.     return dfs(grid, i + 1, j) 
  30.          + dfs(grid, i, j + 1) 
  31.          + dfs(grid, i - 1, j) 
  32.          + dfs(grid, i, j - 1) + 1; 

解法和之前相比差不多,我也不多說了,接下來的兩道島嶼問題是比較有技巧性的,我們重點來看一下。

子島嶼數量

如果說前面的題目都是模板題,那么力扣第 1905 題「統計子島嶼」可能得動動腦子了:

這道題的關鍵在于,如何快速判斷子島嶼?肯定可以借助 Union Find 并查集算法 來判斷,不過本文重點在 DFS 算法,就不展開并查集算法了。

什么情況下grid2中的一個島嶼B是grid1中的一個島嶼A的子島?

當島嶼B中所有陸地在島嶼A中也是陸地的時候,島嶼B是島嶼A的子島。

反過來說,如果島嶼B中存在一片陸地,在島嶼A的對應位置是海水,那么島嶼B就不是島嶼A的子島。

那么,我們只要遍歷grid2中的所有島嶼,把那些不可能是子島的島嶼排除掉,剩下的就是子島。

依據這個思路,可以直接寫出下面的代碼:

  1. int countSubIslands(int[][] grid1, int[][] grid2) { 
  2.     int m = grid1.length, n = grid1[0].length; 
  3.     for (int i = 0; i < m; i++) { 
  4.         for (int j = 0; j < n; j++) { 
  5.             if (grid1[i][j] == 0 && grid2[i][j] == 1) { 
  6.                 // 這個島嶼肯定不是子島,淹掉 
  7.                 dfs(grid2, i, j); 
  8.             } 
  9.         } 
  10.     } 
  11.     // 現在 grid2 中剩下的島嶼都是子島,計算島嶼數量 
  12.     int res = 0; 
  13.     for (int i = 0; i < m; i++) { 
  14.         for (int j = 0; j < n; j++) { 
  15.             if (grid2[i][j] == 1) { 
  16.                 res++; 
  17.                 dfs(grid2, i, j); 
  18.             } 
  19.         } 
  20.     } 
  21.     return res; 
  22.  
  23. // 從 (i, j) 開始,將與之相鄰的陸地都變成海水 
  24. void dfs(int[][] grid, int i, int j) { 
  25.     int m = grid.length, n = grid[0].length; 
  26.     if (i < 0 || j < 0 || i >= m || j >= n) { 
  27.         return
  28.     } 
  29.     if (grid[i][j] == 0) { 
  30.         return
  31.     } 
  32.  
  33.     grid[i][j] = 0; 
  34.     dfs(grid, i + 1, j); 
  35.     dfs(grid, i, j + 1); 
  36.     dfs(grid, i - 1, j); 
  37.     dfs(grid, i, j - 1); 

這道題的思路和計算「封閉島嶼」數量的思路有些類似,只不過后者排除那些靠邊的島嶼,前者排除那些不可能是子島的島嶼。

不同的島嶼數量

這是本文的最后一道島嶼題目,作為壓軸題,當然是最有意思的。

力扣第 694 題「不同的島嶼數量」,題目還是輸入一個二維矩陣,0表示海水,1表示陸地,這次讓你計算 不同的 (distinct) 島嶼數量,函數簽名如下:

  1. int numDistinctIslands(int[][] grid) 

比如題目輸入下面這個二維矩陣:

其中有四個島嶼,但是左下角和右上角的島嶼形狀相同,所以不同的島嶼共有三個,算法返回 3。

很顯然我們得想辦法把二維矩陣中的「島嶼」進行轉化,變成比如字符串這樣的類型,然后利用 HashSet 這樣的數據結構去重,最終得到不同的島嶼的個數。

如果想把島嶼轉化成字符串,說白了就是序列化,序列化說白了遍歷嘛,前文 二叉樹的序列化和反序列化 講了二叉樹和字符串互轉,這里也是類似的。

首先,對于形狀相同的島嶼,如果從同一起點出發,dfs函數遍歷的順序肯定是一樣的。

因為遍歷順序是寫死在你的遞歸函數里面的,不會動態改變:

  1. void dfs(int[][] grid, int i, int j) { 
  2.     // 遞歸順序: 
  3.     dfs(grid, i - 1, j); // 上 
  4.     dfs(grid, i + 1, j); // 下 
  5.     dfs(grid, i, j - 1); // 左 
  6.     dfs(grid, i, j + 1); // 右 

所以,遍歷順序從某種意義上說就可以用來描述島嶼的形狀,比如下圖這兩個島嶼:

假設它們的遍歷順序是:

下,右,上,撤銷上,撤銷右,撤銷下

如果我用分別用1, 2, 3, 4代表上下左右,用-1, -2, -3, -4代表上下左右的撤銷,那么可以這樣表示它們的遍歷順序:

2, 4, 1, -1, -4, -2

你看,這就相當于是島嶼序列化的結果,只要每次使用dfs遍歷島嶼的時候生成這串數字進行比較,就可以計算到底有多少個不同的島嶼了。

要想生成這段數字,需要稍微改造dfs函數,添加一些函數參數以便記錄遍歷順序:

  1. void dfs(int[][] grid, int i, int j, StringBuilder sb, int dir) { 
  2.     int m = grid.length, n = grid[0].length; 
  3.     if (i < 0 || j < 0 || i >= m || j >= n  
  4.         || grid[i][j] == 0) { 
  5.         return
  6.     } 
  7.     // 前序遍歷位置:進入 (i, j) 
  8.     grid[i][j] = 0; 
  9.     sb.append(dir).append(','); 
  10.  
  11.     dfs(grid, i - 1, j, sb, 1); // 上 
  12.     dfs(grid, i + 1, j, sb, 2); // 下 
  13.     dfs(grid, i, j - 1, sb, 3); // 左 
  14.     dfs(grid, i, j + 1, sb, 4); // 右 
  15.  
  16.     // 后序遍歷位置:離開 (i, j) 
  17.     sb.append(-dir).append(','); 

dir記錄方向,dfs函數遞歸結束后,sb記錄著整個遍歷順序,其實這就是前文 回溯算法核心套路 說到的回溯算法框架,你看到頭來這些算法都是相通的。

有了這個dfs函數就好辦了,我們可以直接寫出最后的解法代碼:

  1. int numDistinctIslands(int[][] grid) { 
  2.     int m = grid.length, n = grid[0].length; 
  3.     // 記錄所有島嶼的序列化結果 
  4.     HashSet<String> islands = new HashSet<>(); 
  5.     for (int i = 0; i < m; i++) { 
  6.         for (int j = 0; j < n; j++) { 
  7.             if (grid[i][j] == 1) { 
  8.                 // 淹掉這個島嶼,同時存儲島嶼的序列化結果 
  9.                 StringBuilder sb = new StringBuilder(); 
  10.                 // 初始的方向可以隨便寫,不影響正確性 
  11.                 dfs(grid, i, j, sb, 666); 
  12.                 islands.add(sb.toString()); 
  13.             } 
  14.         } 
  15.     } 
  16.     // 不相同的島嶼數量 
  17.     return islands.size(); 

這樣,這道題就解決了,至于為什么初始調用dfs函數時的dir參數可以隨意寫,這里涉及 DFS 和回溯算法的一個細微差別,前文 圖算法基礎 有寫,這里就不展開了。

 

以上就是全部島嶼系列問題的解題思路,也許前面的題目大部分人會做,但是最后兩題還是比較巧妙的,希望本文對你有幫助。

 

責任編輯:武曉燕 來源: labuladong
相關推薦

2025-02-24 00:00:00

DeepSeek機器人模型

2009-12-29 12:56:34

2022-06-20 11:51:57

基礎設施保護網絡攻擊

2009-03-13 19:11:07

2023-07-10 08:01:13

島嶼問題算法

2023-11-07 08:36:34

JavaScriptJS挑戰

2018-09-20 17:32:09

華為華為全聯接大會HC

2015-12-17 11:13:26

2025-07-25 15:02:23

AI模型系統

2025-02-26 05:00:00

DFS算法遞歸

2009-03-10 10:06:33

面試題筆試題微軟

2015-06-15 15:41:56

高考考生特點

2009-06-02 15:30:35

Hibernate面試筆試題

2009-06-16 13:41:19

Hibernate面試Hibernate面試

2021-03-02 11:29:50

算法算法分析前端

2019-07-23 13:32:13

Java開發代碼

2020-10-14 07:20:53

高并發

2019-02-20 09:10:40

Java并發隊列編程語言

2022-08-12 09:35:36

JavaScript面試

2020-09-01 07:47:32

Redis秒殺微信
點贊
收藏

51CTO技術棧公眾號

久久综合五月天| 国产精品白丝在线| 欧美一级淫片播放口| 国产精品成人一区二区三区电影毛片 | 怡红院在线播放| 国产精一区二区三区| 欧美激情一区二区三级高清视频| 美国黄色一级毛片| 精品女同一区二区三区在线观看| 亚洲黄色免费网站| 狼狼综合久久久久综合网| 国产天堂第一区| 欧美午夜在线视频| 亚洲欧美在线播放| 欧美激情国内自拍| 国产乱码午夜在线视频| 国产精品每日更新在线播放网址| 高清一区二区三区视频| 最近国语视频在线观看免费播放| 精品成人久久| 日韩中文字幕在线播放| 亚洲欧美视频在线播放| 99er精品视频| 色哟哟在线观看一区二区三区| 在线一区高清| 免费人成在线观看网站| 国产成人av影院| 国产欧美日韩综合精品| 99超碰在线观看| 亚洲天堂男人| 日韩在线观看av| 色偷偷亚洲男人天堂| 精品一区在线| 国产真实夫妇交换视频 | 一区中文字幕| 欧美日韩美女在线观看| 玖玖精品在线视频| 国产原创av在线| 成人毛片老司机大片| 国产精品爽黄69天堂a| 天天操天天操天天操天天| 欧美日韩国产一区精品一区| 日韩亚洲在线观看| 国产三级在线观看完整版| 亚洲另类春色校园小说| 亚洲二区在线播放视频| 4438x全国最大成人| 亚洲成人毛片| 欧美日韩色综合| 欧美成人黄色网址| free欧美| 91高清视频在线| 99久久激情视频| 免费成人直播| 色婷婷av一区二区三区大白胸| 久久黄色片视频| 日韩脚交footjobhd| 精品久久久久久国产91| 人妻熟妇乱又伦精品视频| 国产夫妻在线播放| 亚洲成人在线免费| heyzo亚洲| 韩日毛片在线观看| 亚洲已满18点击进入久久| 91免费国产精品| 日本动漫同人动漫在线观看| 一区二区在线看| 日韩一级片免费视频| 国产精品69xx| 狠狠躁夜夜躁久久躁别揉| av免费观看国产| 热色播在线视频| 欧美丝袜一区二区| 午夜免费精品视频| 婷婷久久免费视频| 日韩欧美国产系列| 污污内射在线观看一区二区少妇| 五月激激激综合网色播| 亚洲一二三在线| 视频国产一区二区| 欧美日本在线| 日韩美女视频免费看| 中文字幕无线码一区| 韩国三级电影一区二区| 超碰97在线人人| 牛牛澡牛牛爽一区二区| 国产精品理论片| 国产在线视频在线| 成人免费看黄| 欧美二区乱c少妇| 91传媒理伦片在线观看| 国内精品久久久久久久久电影网| 日韩有码在线电影| 精品国产欧美日韩不卡在线观看| 亚洲精品1234| 国产伦精品一区二区三区精品视频| 99久久亚洲精品日本无码| 成人性生交大片| 亚洲视频sss| 丁香花视频在线观看| 在线观看精品一区| 欧美熟妇精品一区二区| 国产精品欧美日韩一区| 不卡av在线播放| 麻豆成人免费视频| 精品一区二区三区在线观看国产| 国产亚洲福利社区| 天堂аⅴ在线地址8| 精品福利免费观看| 亚洲一二区在线观看| 色综合www| 欧美高跟鞋交xxxxhd| 亚洲精品久久久久久久蜜桃| 懂色av中文一区二区三区| 日本午夜精品电影| 川上优av中文字幕一区二区| 欧美另类高清zo欧美| 久久久午夜精品福利内容| 香港欧美日韩三级黄色一级电影网站| 911国产网站尤物在线观看| 91精品国产综合久| 久久精品一级爱片| 精品视频在线观看一区| 久久精品一级| 最近2019中文免费高清视频观看www99 | 日韩有码第一页| 亚洲精品免费看| 怡红院亚洲色图| 国产亚洲第一伦理第一区| 国内精品久久久久久| 99国产精品99| 日韩毛片视频在线看| 天天色综合天天色| 久久不见久久见国语| 9.1国产丝袜在线观看| 亚洲黄色片视频| 亚洲品质自拍视频网站| 污版视频在线观看| 欧美色婷婷久久99精品红桃| 国产91色在线| 人操人视频在线观看| 激情成人中文字幕| 人妻体内射精一区二区三区| 欧美另类女人| 国产99在线播放| 青草影视电视剧免费播放在线观看| 这里只有精品电影| 免费在线观看黄色小视频| 麻豆中文一区二区| 亚洲图色在线| 99精品美女视频在线观看热舞| 亚洲视频在线免费观看| 天天爱天天做天天爽| 国产亚洲成av人在线观看导航| 成年人视频网站免费观看| 香蕉久久夜色精品国产使用方法| 97成人精品区在线播放| 亚洲欧美日韩综合在线| 欧美性xxxxxxx| 男人舔女人下部高潮全视频 | 国产精品91久久久久久| 国产一级片在线| 欧美日韩一区二区三区四区| 国产又粗又猛又爽又黄的视频小说| 美女看a上一区| 中文字幕中文字幕在线中心一区| 国产精品一站二站| 欧美疯狂xxxx大交乱88av| 丰满人妻一区二区| 日韩欧美国产中文字幕| 日本美女bbw| 国产一区二区三区免费观看| 国产成人一区二区三区别| 久久91在线| 国产精品草莓在线免费观看 | 韩国v欧美v日本v亚洲| 免费国产精品视频| 91激情在线视频| 亚洲伦理一区二区三区| 丁香天五香天堂综合| 欧美爱爱视频免费看| 成人激情免费视频| 国产精品免费在线免费| av毛片在线看| 亚洲美女性生活视频| 中文字幕在线观看1| 一区二区激情视频| 成人黄色免费网址| 国产精品一二三四| 男女av免费观看| 在线一区免费| 欧美精品在线一区| av在线国产精品| 2019中文字幕在线观看| 老司机午夜在线视频| 日韩第一页在线| 国产一区二区在线视频聊天| 精品成人av一区| 欧美日韩色视频| 26uuu精品一区二区| 亚洲女人在线观看| 日韩电影在线观看电影| 精品国产凹凸成av人导航| 最新中文字幕av| 国产成人在线视频网站| 久久国产色av免费观看| 国产一区清纯| 亚洲一区二区三区精品在线观看| 国产精品中文字幕制服诱惑| 国产欧美日韩免费| 女人让男人操自己视频在线观看| 精品国产区一区二区三区在线观看 | 性欧美一区二区| 成人污视频在线观看| 嫩草视频免费在线观看| 亚洲欧美久久久| 欧美视频在线观看视频| 香蕉视频官网在线观看日本一区二区| 久久综合狠狠综合久久综青草 | 看片的网站亚洲| 九一久久久久久| 国产凹凸在线观看一区二区| 久久久三级国产网站| xvideos亚洲人网站| 国产精品第5页| 亚洲免费观看视频| 97在线观看免费视频| 成人av电影在线播放| 国产探花在线观看视频| 老司机午夜精品| 欧美精品一区二区三区免费播放| 亚洲国内自拍| 2019日韩中文字幕mv| 欧美精品国产| 久久久久久久久网| 国产国产精品| 综合视频免费看| 日韩理论在线| 亚洲欧美日韩精品久久久| 久草成人在线| 欧美一区二区视频17c| 人体久久天天| 91精品久久久久久久99蜜桃| 亚洲欧美综合精品久久成人| 国产精品扒开腿做| 三级在线免费观看| 俄罗斯毛片基地| 国产系列在线观看| 国内毛片久久| 国产成人自拍在线| 欧美蜜桃一区二区三区| 日韩一区二区三区国产| 九九九九九国产| 日韩午夜在线| 女人和拘做爰正片视频| 欧美亚洲免费| 91淫黄看大片| 麻豆成人久久精品二区三区小说| 亚洲福利精品视频| 久久精品国产99国产| 午夜精品中文字幕| 精品一区二区免费视频| 日本女人黄色片| 成人毛片视频在线观看| 激情综合丁香五月| 国产亚洲精品超碰| 美国一级片在线观看| 一区二区三区毛片| 日韩av电影网址| 在线免费视频一区二区| 97在线播放免费观看| 精品国产亚洲在线| 男人av在线| 俺去亚洲欧洲欧美日韩| 欧美色图天堂| 日本视频久久久| 日日夜夜精品| 国产日韩精品一区观看| 国产精品午夜一区二区三区| 亚洲资源在线网| 悠悠资源网久久精品| 欧美激情成人网| 国产综合色视频| 久久一区二区电影| 国产精品乱码妇女bbbb| 日韩精品――中文字幕| 在线观看视频一区二区欧美日韩 | 色素色在线综合| 亚洲天堂自拍偷拍| 精品日韩一区二区三区免费视频| 日本ー区在线视频| 成年人精品视频| 中文在线аv在线| 国产精品久久中文| 成人爽a毛片免费啪啪红桃视频| 欧美人xxxxx| 欧美日韩亚洲一区| 日本成人中文字幕在线| 国产精品亚洲а∨天堂免在线| 欧美色图亚洲激情 | 日韩大片免费在线观看| 欧美日韩一级视频| 少妇高潮一区二区三区69| 日韩在线欧美在线国产在线| www.日日操| jizzjizzxxxx| 国产原创精品视频| 久久视频这里只有精品| 大菠萝精品导航| 成人精品一区二区三区电影免费 | 亚洲国产精品影院| 一区二区小视频| 精品亚洲一区二区三区四区五区| 成人福利片网站| 国产精品久久久久久久久久久新郎| 日本一区二区三区播放| 色一情一乱一伦一区二区三欧美| 欧美午夜电影在线观看| 999在线精品视频| 国产色一区二区| 探花视频在线观看| 欧美成人精品高清在线播放| 日本天堂在线观看| 日本国产一区二区三区| 国产欧美三级电影| 免费日韩在线观看| 精品一区二区久久| 精品一区二区6| 在线一区二区三区做爰视频网站| 欧美 日韩 国产 成人 在线 91 | 熟女av一区二区| 欧美亚洲日本一区| 九色国产在线观看| 欧美亚洲国产视频小说| 精品嫩草影院| www插插插无码视频网站| 国产高清亚洲一区| 极品久久久久久| 3d成人动漫网站| 日本成a人片在线观看| 国产精品久久婷婷六月丁香| 精品免费视频| 成人亚洲视频在线观看| 久久久亚洲精品石原莉奈| www.com国产| 亚洲欧美日韩中文视频| 在线免费日韩片| 欧美成ee人免费视频| 久久尤物视频| 中字幕一区二区三区乱码| 在线观看91精品国产入口| 国产尤物视频在线| 国产精品尤物福利片在线观看| 日韩精品欧美激情一区二区| a在线观看免费视频| 亚洲色图另类专区| www天堂在线| 午夜精品久久久久久久99黑人| 欧美精品国产白浆久久久久| 两根大肉大捧一进一出好爽视频| 久久亚区不卡日本| 国产女主播喷水视频在线观看 | 粉嫩一区二区三区在线观看| 佐佐木明希av| 成人黄色一级视频| 国产一级片毛片| 中文字幕国产亚洲| 91成人小视频| 日韩网站在线免费观看| www成人在线观看| 中文字幕人妻互换av久久| 久久这里有精品视频| 97久久精品| 波多野结衣作品集| 中文字幕日韩一区| 免费观看黄一级视频| 国产精品wwww| 亚洲第一偷拍| 亚洲成人av免费在线观看| 欧美三区免费完整视频在线观看| 国产淫片在线观看| 欧美精品一区在线| 激情综合色播五月| 日本天堂在线视频| 在线视频日本亚洲性| 婷婷视频一区二区三区| 啊啊啊一区二区| 国产精品福利一区二区| 丁香六月天婷婷| 国产精品视频区| 国一区二区在线观看| 精品无码人妻一区二区免费蜜桃| 91精品国产手机| 免费成人直播| 日韩视频一二三| 国产偷国产偷亚洲高清人白洁| 国产女人18毛片水18精| 日本精品中文字幕| 国内自拍视频一区二区三区| 中文字幕黄色网址|