日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

【leetcode】二叉树与经典问题

發布時間:2023/11/27 生活经验 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【leetcode】二叉树与经典问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • 筆記
      • leetcode [114. 二叉樹展開為鏈表](https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/)
        • 解法一: 后序遍歷、遞歸
      • leetcode [226. 翻轉二叉樹](https://leetcode-cn.com/problems/invert-binary-tree/)
        • 思路與算法
        • 復雜度分析
      • leetcode [劍指 Offer 32 - I. 從上到下打印二叉樹](https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/)
        • BFS算法流程:
        • 復雜度分析:
      • leetcode [107. 二叉樹的層序遍歷 II](https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/)
      • 學會二叉樹的層序遍歷,可以一口氣擼完leetcode一下八道題目:
      • 102.二叉樹的層序遍歷
      • 199.二叉樹的右視圖
      • 637.二叉樹的層平均值
      • leetcode [589. N 叉樹的前序遍歷](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/)
        • 樹的遍歷(Traversal)
        • 遞歸算法的三個要素。每次寫遞歸,都按照這三要素來寫,可以保證大家寫出正確的遞歸算法!
          • 確定遞歸函數的參數和返回值:
          • 確定終止條件:
          • 確定單層遞歸的邏輯:
      • 前序遍歷(preorder, 按照先訪問根節點的順序)
      • 中序遍歷:(inorder, 按照先訪問順序,左 根 右)
      • 后序遍歷:(lastorder, 按照先訪問順序,左 右 根 )
      • 144.二叉樹的前序遍歷 迭代法
      • 94.二叉樹的中序遍歷 中序遍歷(迭代法)
      • 145.二叉樹的后序遍歷(迭代法)
      • 二叉樹前中后迭代方式統一寫法
    • N叉樹的前序遍歷
    • N叉樹的后序遍歷
        • 總結
      • leetcode [589. N 叉樹的前序遍歷](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/)
      • 429. N叉樹的層序遍歷
        • 思路:這道題依舊是模板題**,**只不過一個節點有多個孩子了**
      • 515.在每個樹行中找最大值
        • 思路:層序遍歷,取每一層的最大值
      • 116.填充每個節點的下一個右側節點指針
        • 思路:本題依然是層序遍歷,只不過在單層遍歷的時候記錄一下本層的頭部節點,然后在遍歷的時候讓前一個節點指向本節點就可以了
      • 117.填充每個節點的下一個右側節點指針II
        • 思路:這道題目說是二叉樹,但116題(上題)目說是完整二叉樹,其實沒有任何差別,一樣的代碼一樣的邏輯一樣的味道
        • 總結
      • leetcode [103. 二叉樹的鋸齒形層序遍歷](https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/)
      • leetcode [110. 平衡二叉樹](https://leetcode-cn.com/problems/balanced-binary-tree/)
      • leetcode [112. 路徑總和](https://leetcode-cn.com/problems/path-sum/)
        • **方法一:深度優先搜索遞歸**
        • 方法二:廣度優先搜索
        • 方法三:棧模擬遞歸(回溯)
      • leetcode [105. 從前序與中序遍歷序列構造二叉樹](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
        • 遞歸的解法 1 找根位置 2 遞歸建立左子樹 3遞歸建立右子樹
        • 2.使用指針解決
      • leetcode [222. 完全二叉樹的節點個數](https://leetcode-cn.com/problems/count-complete-tree-nodes/)
        • 方法一:適合所有類型的樹的節點計算
        • 方法二:dfs
          • 1.如果根節點的左子樹深度等于右子樹深度,則說明左子樹為滿二叉樹。
          • 如果根節點的左子樹深度大于右子樹深度,則說明右子樹為滿二叉樹
      • 三種做法的代碼
        • 1.暴力求解,深度遞歸dfs
        • 2.運用完全二叉樹的性質(和滿二叉樹結合)
        • 3.方法三:二分查找 + 位運算
      • leetcode [劍指 Offer 54. 二叉搜索樹的第k大節點](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/)
        • 方法一:遞歸的方式
        • 方法二:先求前序遍歷數組,在根據數組求第k大的值
      • leetcode [劍指 Offer 26. 樹的子結構](https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/)
        • 方法1:遞歸
      • leetcode [662. 二叉樹最大寬度](https://leetcode-cn.com/problems/maximum-width-of-binary-tree/)
        • 方法 0:寬度優先搜索 [Accepted]
        • 方法 1:寬度優先搜索 [Accepted]
        • 方法 2:深度優先搜索 [Accepted]
      • leetcode [968. 監控二叉樹](https://leetcode-cn.com/problems/binary-tree-cameras/)
        • 第一種解法
        • 情況1:左右節點都有覆蓋
        • 情況2:左右節點至少有一個無覆蓋的情況
        • 情況3:左右節點至少有一個有攝像頭
        • 情況4:頭結點沒有覆蓋
        • 情況3:左右節點至少有一個有攝像頭
        • 情況4:頭結點沒有覆蓋

筆記

節點:集合

邊: 關系

1對多映射


leetcode 114. 二叉樹展開為鏈表

解法一: 后序遍歷、遞歸

依據二叉樹展開為鏈表的特點,使用后序遍歷完成展開。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/TreeNode* last = nullptr;
class Solution {
public:void flatten(TreeNode* root) {if (root == nullptr) return;flatten(root->left);flatten(root->right);if (root->left != nullptr) {auto pre = root->left;while (pre->right != nullptr) pre = pre->right;pre->right = root->right;root->right = root->left;root->left = nullptr;}root = root->right;return;}
};

解法二: 非遞歸,不使用輔助空間及全局變量
前面的遞歸解法實際上也使用了額外的空間,因為遞歸需要占用額外空間。下面的解法無需申請棧,也不用全局變量,是真正的 In-Place 解法。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:void flatten(TreeNode* root) {while (root != nullptr) {if (root->left != nullptr) {auto most_right = root->left; // 如果左子樹不為空, 那么就先找到左子樹的最右節點while (most_right->right != nullptr) most_right = most_right->right; // 找最右節點most_right->right = root->right; // 然后將跟的右孩子放到最右節點的右子樹上root->right = root->left; // 這時候跟的右孩子可以釋放, 因此我令左孩子放到右孩子上root->left = nullptr; // 將左孩子置為空}root = root->right; // 繼續下一個節點}return;}
};

leetcode 226. 翻轉二叉樹

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-M5Zo096g-1619446816610)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1619309137259.png)]

//根 左 右

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* invertTree(TreeNode* root) {if(root==NULL) return root;swap(root->left,root->right);invertTree(root->left);invertTree(root->right);return root;}
};

思路與算法

這是一道很經典的二叉樹問題。顯然,我們從根節點開始,遞歸地對樹進行遍歷,并從葉子結點先開始翻轉。如果當前遍歷到的節點 root 的左右兩棵子樹都已經翻轉,那么我們只需要交換兩棵子樹的位置,即可完成以 root 為根節點的整棵子樹的翻轉。

// 左 右 根

class Solution {
public:TreeNode* invertTree(TreeNode* root) {if (root == nullptr) {return nullptr;}TreeNode* left = invertTree(root->left);TreeNode* right = invertTree(root->right);root->left = right;root->right = left;return root;}
};

復雜度分析

時間復雜度:O(N),其中 N 為二叉樹節點的數目。我們會遍歷二叉樹中的每一個節點,對每個節點而言,我們在常數時間內交換其兩棵子樹。

空間復雜度:O(N)。使用的空間由遞歸棧的深度決定,它等于當前節點在二叉樹中的高度。在平均情況下,二叉樹的高度與節點個數為對數關系,即O(logN)。而在最壞情況下,樹形成鏈狀,空間復雜度為 O(N)。

leetcode 劍指 Offer 32 - I. 從上到下打印二叉樹

BFS算法流程:

特例處理: 當樹的根節點為空,則直接返回空列表 [] ;
初始化: 打印結果列表 res = [] ,包含根節點的隊列 queue = [root] ;
BFS 循環: 當隊列 queue 為空時跳出;
出隊: 隊首元素出隊,記為 node;
打印: 將 node對應val 添加至列表 ans尾部;
添加子節點: 若 node 的左(右)子節點不為空,則將左(右)子節點加入隊列 queue ;
返回值: 返回打印結果列表 ans即可。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
//BFS
class Solution {
public:vector<int> levelOrder(TreeNode* root){vector<int> ans;if(root == NULL) return ans;queue<TreeNode*> q;q.push(root);while(!q.empty()){TreeNode* node=q.front();q.pop();ans.push_back(node->val);if(node->left) q.push(node->left);if(node->right) q.push(node->right);}return ans;}//DFS// void getResult(TreeNode *root, int k,vector<vector<int>> &ans){//     if(root == NULL) return;//     if(k == ans.size())  ans.push_back(vector<int>());//     ans[k].push_back(root->val);//     getResult(root->left, k + 1, ans);//     getResult(root->right, k + 1, ans);//     return;// }// vector<vector<int>> levelOrder(TreeNode* root) {//     vector<vector<int>> ans;//     getResult(root , 0 , ans);//     return ans;// }
};

復雜度分析:

時間復雜度 O(N) : N 為二叉樹的節點數量,即 BFS 需循環 N 次。
空間復雜度 O(N) : 最差情況下,即當樹為平衡二叉樹時,最多有 N/2 個樹節點同時在 queue 中,使用 O(N) 大小的額外空間。

leetcode 107. 二叉樹的層序遍歷 II

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:void getResult(TreeNode *root,int k, vector<vector<int>> &ans){if(root==NULL) return;if(k == ans.size()) ans.push_back(vector<int>());ans[k].push_back(root->val);getResult(root->left,k+1,ans);getResult(root->right,k+1,ans);return;}//前序遍歷 //根 左 右    [3],[9,20], [15,7]vector<vector<int>> levelOrderBottom(TreeNode* root) {vector<vector<int>> ans;getResult(root,0,ans);for(int i=0,j=ans.size()-1;i<j;i++,j--){swap(ans[i],ans[j]);}return ans;}
};

學會二叉樹的層序遍歷,可以一口氣擼完leetcode一下八道題目:

102.二叉樹的層序遍歷
107.二叉樹的層次遍歷II
199.二叉樹的右視圖
637.二叉樹的層平均值
429.N叉樹的前序遍歷
515.在每個樹行中找最大值
116.填充每個節點的下一個右側節點指針
117.填充每個節點的下一個右側節點指針II

102.二叉樹的層序遍歷

給你一個二叉樹,請你返回其按 層序遍歷 得到的節點值。 (即逐層地,從左到右訪問所有節點)。

思路
我們之前講過了三篇關于二叉樹的深度優先遍歷的文章:

接下來我們再來介紹二叉樹的另一種遍歷方式:層序遍歷。

層序遍歷一個二叉樹。就是從左到右一層一層的去遍歷二叉樹。這種遍歷的方式和我們之前講過的都不太一樣。

需要借用一個輔助數據結構即隊列來實現,隊列先進先出,符合一層一層遍歷的邏輯而用棧先進后出適合模擬深度優先遍歷也就是遞歸的邏輯。

而這種層序遍歷方式就是圖論中的廣度優先遍歷,只不過我們應用在二叉樹上。

使用隊列實現二叉樹廣度優先遍歷,動畫如下:


這樣就實現了層序從左到右遍歷二叉樹。

代碼如下:這份代碼也可以作為二叉樹層序遍歷的模板,以后再打后面的題目就靠它了

BFS樹/圖的層序遍歷,或者廣度優先搜索,基于queue數據結構實現

class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<vector<int>> result;while (!que.empty()) {int size = que.size();vector<int> vec;// 這里一定要使用固定大小size,不要使用que.size(),因為que.size是不斷變化的for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();vec.push_back(node->val);if (node->left) que.push(node->left);if (node->right) que.push(node->right);}result.push_back(vec);}return result;}
};

199.二叉樹的右視圖

給定一棵二叉樹,想象自己站在它的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值。


思路
層序遍歷的時候,判斷是否遍歷到單層的最后面的元素,如果是,就放進result數組中,隨后返回result就可以了。

class Solution {
public:vector<int> rightSideView(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<int> result;while (!que.empty()) {int size = que.size();for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();if (i == (size - 1)) result.push_back(node->val);// 將每一層的最后元素放入result數組中if (node->left) que.push(node->left);if (node->right) que.push(node->right);}}return result;}
};

637.二叉樹的層平均值

給定一個非空二叉樹, 返回一個由每層節點平均值組成的數組。

思路 本題就是層序遍歷的時候把一層求個總和在取一個均值

class Solution {
public:vector<double> averageOfLevels(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<double> result;while (!que.empty()) {int size = que.size();double sum = 0; // 統計每一層的和for (int i = 0; i < size; i++) { TreeNode* node = que.front();que.pop();sum += node->val;if (node->left) que.push(node->left);if (node->right) que.push(node->right);}result.push_back(sum / size); // 將每一層均值放進結果集}return result;}
};

leetcode 589. N 叉樹的前序遍歷


/*// Definition for a Node.
class Node {
public:int val;vector<Node*> children;Node() {}Node(int _val) { val = _val;}Node(int _val, vector<Node*> _children) {val = _val;children = _children;}
};
*/
class Solution {
public:void __preorder(Node* root,vector<int> &ans){if(root==NULL) return ;ans.push_back(root->val);for(auto x:root->children){__preorder(x,ans);}}vector<int> preorder(Node* root) {vector<int> ans;__preorder(root,ans);return ans;}
};

樹的遍歷(Traversal)

如下圖, 三種遍歷方式, 可用同一種遞歸思想實現

遞歸算法的三個要素。每次寫遞歸,都按照這三要素來寫,可以保證大家寫出正確的遞歸算法!

確定遞歸函數的參數和返回值:

確定哪些參數是遞歸的過程中需要處理的,那么就在遞歸函數里加上這個參數, 并且還要明確每次遞歸的返回值是什么進而確定遞歸函數的返回類型。

確定終止條件:

寫完了遞歸算法, 運行的時候,經常會遇到棧溢出的錯誤,就是沒寫終止條件或者終止條件寫的不對,操作系統也是用一個棧的結構來保存每一層遞歸的信息,如果遞歸沒有終止,操作系統的內存棧必然就會溢出。

確定單層遞歸的邏輯:

確定每一層遞歸需要處理的信息。在這里也就會重復調用自己來實現遞歸的過程。

以下以前序遍歷為例:

確定遞歸函數的參數和返回值:因為要打印出前序遍歷節點的數值,所以參數里需要傳入vector在放節點的數值,除了這一點就不需要在處理什么數據了也不需要有返回值,所以遞歸函數返回類型就是void,代碼如下:

void traversal(TreeNode* cur, vector<int>& vec)

確定終止條件:在遞歸的過程中,如何算是遞歸結束了呢,當然是當前遍歷的節點是空了,那么本層遞歸就要要結束了,所以如果當前遍歷的這個節點是空,就直接return,代碼如下:

if (cur == NULL) return;

確定單層遞歸的邏輯:前序遍歷是中左右的循序,所以在單層遞歸的邏輯,是要先取中節點的數值,代碼如下:

vec.push_back(cur->val);    // 中
traversal(cur->left, vec);  // 左
traversal(cur->right, vec); // 右

單層遞歸的邏輯就是按照中左右的順序來處理的,這樣二叉樹的前序遍歷,基本就寫完了,在看一下完整代碼:

前序遍歷(preorder, 按照先訪問根節點的順序)

class Solution {
public:void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;vec.push_back(cur->val);    // 根traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右}vector<int> preorderTraversal(TreeNode* root) {vector<int> result;traversal(root, result);return result;}
};

中序遍歷:(inorder, 按照先訪問順序,左 根 右)

void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;traversal(cur->left, vec);  // 左vec.push_back(cur->val);    // 根traversal(cur->right, vec); // 右
}

后序遍歷:(lastorder, 按照先訪問順序,左 右 根 )

void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右vec.push_back(cur->val);    // 根
}

此時大家可以做一做leetcode上三道題目,分別是:

144.二叉樹的前序遍歷 迭代法

為什么可以用迭代法(非遞歸的方式)來實現二叉樹的前后中序遍歷呢?

我們在棧與隊列:匹配問題都是棧的強項中提到了,遞歸的實現就是:每一次遞歸調用都會把函數的局部變量、參數值和返回地址等壓入調用棧中,然后遞歸返回的時候,從棧頂彈出上一次遞歸的各項參數,所以這就是遞歸為什么可以返回上一層位置的原因。

此時大家應該知道我們用棧也可以是實現二叉樹的前后中序遍歷了。

前序遍歷(迭代法)
我們先看一下前序遍歷。

前序遍歷是中左右,每次先處理的是中間節點,那么先將跟節點放入棧中,然后將右孩子加入棧,再加入左孩子

為什么要先加入 右孩子,再加入左孩子呢? 因為這樣出棧的時候才是中左右的順序

動畫如下:

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> result;if (root == NULL) return result;st.push(root);while (!st.empty()) {TreeNode* node = st.top();                       // 中st.pop();result.push_back(node->val);if (node->right) st.push(node->right);           // 右(空節點不入棧)if (node->left) st.push(node->left);             // 左(空節點不入棧)}return result;}
};

94.二叉樹的中序遍歷 中序遍歷(迭代法)

為了解釋清楚,我說明一下 剛剛在迭代的過程中,其實我們有兩個操作:

處理:將元素放進result數組中
訪問:遍歷節點
分析一下為什么剛剛寫的前序遍歷的代碼,不能和中序遍歷通用呢,因為前序遍歷的順序是中左右,先訪問的元素是中間節點,要處理的元素也是中間節點,所以剛剛才能寫出相對簡潔的代碼,因為要訪問的元素和要處理的元素順序是一致的,都是中間節點。

那么再看看中序遍歷,中序遍歷是左中右,先訪問的是二叉樹頂部的節點,然后一層一層向下訪問,直到到達樹左面的最底部,再開始處理節點(也就是在把節點的數值放進result數組中),這就造成了處理順序和訪問順序***是不一致的。****

那么在使用迭代法寫中序遍歷,就需要借用指針的遍歷來幫助訪問節點,棧則用來處理節點上的元素。

動畫如下:

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> st;TreeNode* cur = root;while (cur != NULL || !st.empty()) {if (cur != NULL) { // 指針來訪問節點,訪問到最底層st.push(cur); // 將訪問的節點放進棧cur = cur->left;                // 左} else {cur = st.top(); // 從棧里彈出的數據,就是要處理的數據(放進result數組里的數據)st.pop();result.push_back(cur->val);     // 中cur = cur->right;               // 右}}return result;}
};

145.二叉樹的后序遍歷(迭代法)

再來看后序遍歷,先序遍歷是中左右,后續遍歷是左右中,那么我們只需要調整一下先序遍歷的代碼順序,就變成中右左的遍歷順序,然后在反轉result數組,輸出的結果順序就是左右中了,如下圖:

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> result;if (root == NULL) return result;st.push(root);while (!st.empty()) {TreeNode* node = st.top();st.pop();result.push_back(node->val);if (node->left) st.push(node->left); // 相對于前序遍歷,這更改一下入棧順序 (空節點不入棧)if (node->right) st.push(node->right); // 空節點不入棧}reverse(result.begin(), result.end()); // 將結果反轉之后就是左右中的順序了return result;}
};

此時我們實現了前后中遍歷的三種迭代法,是不是發現迭代法實現的先中后序,其實風格也不是那么統一,除了先序和后序,有關聯,中序完全就是另一個風格了,一會用棧遍歷,一會又用指針來遍歷。

二叉樹前中后迭代方式統一寫法

之后我們發現迭代法實現的先中后序,其實風格也不是那么統一,除了先序和后序,有關聯,中序完全就是另一個風格了,一會用棧遍歷,一會又用指針來遍歷。

實踐過的同學,也會發現使用迭代法實現先中后序遍歷,很難寫出統一的代碼,不像是遞歸法,實現了其中的一種遍歷方式,其他兩種只要稍稍改一下節點順序就可以了。

其實針對三種遍歷方式,使用迭代法是可以寫出統一風格的代碼!

重頭戲來了,接下來介紹一下統一寫法。

那我們就將訪問的節點放入棧中,把要處理的節點也放入棧中但是要做標記。

如何標記呢,就是要處理的節點放入棧之后,緊接著放入一個空指針作為標記。 這種方法也可以叫做標記法。

迭代法中序遍歷
中序遍歷代碼如下:(詳細注釋)

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> st;if (root != NULL) st.push(root);while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop(); // 將該節點彈出,避免重復操作,下面再將右中左節點添加到棧中if (node->right) st.push(node->right);  // 添加右節點(空節點不入棧)st.push(node);                          // 添加中節點st.push(NULL); // 中節點訪問過,但是還沒有處理,加入空節點做為標記。if (node->left) st.push(node->left);    // 添加左節點(空節點不入棧)} else { // 只有遇到空節點的時候,才將下一個節點放進結果集st.pop();           // 將空節點彈出node = st.top();    // 重新取出棧中元素st.pop();result.push_back(node->val); // 加入到結果集}}return result;}
};

迭代法前序遍歷
迭代法前序遍歷代碼如下: (注意此時我們和中序遍歷相比僅僅改變了兩行代碼的順序)

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> st;if (root != NULL) st.push(root);while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop();if (node->right) st.push(node->right);  // 右if (node->left) st.push(node->left);    // 左st.push(node);                          // 中st.push(NULL);} else {st.pop();node = st.top();st.pop();result.push_back(node->val);}}return result;}
};

迭代法后序遍歷
后續遍歷代碼如下: (注意此時我們和中序遍歷相比僅僅改變了兩行代碼的順序)

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> st;if (root != NULL) st.push(root);while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop();st.push(node);                          // 中st.push(NULL);if (node->right) st.push(node->right);  // 右if (node->left) st.push(node->left);    // 左} else {st.pop();node = st.top();st.pop();result.push_back(node->val);}}return result;}};

N叉樹的前序遍歷

和二叉樹的道理是一樣的,直接給出代碼了

遞歸C++代碼

class Solution {
private:vector<int> result;void traversal (Node* root) {if (root == NULL) return;result.push_back(root->val);for (int i = 0; i < root->children.size(); i++) {traversal(root->children[i]);}}public:vector<int> preorder(Node* root) {result.clear();traversal(root);return result;}
};

迭代法C++代碼

class Solution {public:vector<int> preorder(Node* root) {vector<int> result;if (root == NULL) return result;stack<Node*> st;st.push(root);while (!st.empty()) {Node* node = st.top();st.pop();result.push_back(node->val);// 注意要倒敘,這樣才能達到前序(中左右)的效果for (int i = node->children.size() - 1; i >= 0; i--) {if (node->children[i] != NULL) {st.push(node->children[i]);}}}return result;}
};

N叉樹的后序遍歷

遞歸C++代碼o

class Solution {
private:vector<int> result;void traversal (Node* root) {if (root == NULL) return;for (int i = 0; i < root->children.size(); i++) { // 子孩子traversal(root->children[i]);}result.push_back(root->val); // 中}public:vector<int> postorder(Node* root) {result.clear();traversal(root);return result;}};

迭代法C++代碼

class Solution {
public:vector<int> postorder(Node* root) {vector<int> result;if (root == NULL) return result;stack<Node*> st;st.push(root);while (!st.empty()) {Node* node = st.top();st.pop();result.push_back(node->val);for (int i = 0; i < node->children.size(); i++) { // 相對于前序遍歷,這里反過來if (node->children[i] != NULL) {st.push(node->children[i]);}}}reverse(result.begin(), result.end()); // 反轉數組return result;}
};

總結

對于二叉樹,我們寫出了前中后序的遞歸,以及對應的迭代法,然后分析出為什么寫出統一風格的迭代法比較難。

進而給出了前中后序統一風格的迭代法代碼。

我們可以寫出了統一風格的迭代法,不用在糾結于前序寫出來了,中序寫不出來的情況了。

但是統一風格的迭代法并不好理解,而且想在面試直接寫出來還有難度的。

所以大家根據自己的個人喜好,對于二叉樹的前中后序遍歷,選擇一種自己容易理解的遞歸和迭代法。

最后在給出N叉樹的前后序遍歷的遞歸與迭代。理解了以上二叉樹的遍歷方式,N叉樹就容易很多了,都是一個套路。

leetcode 589. N 叉樹的前序遍歷


/*
// Definition for a Node.
class Node {
public:int val;vector<Node*> children;Node() {}Node(int _val) { val = _val;}Node(int _val, vector<Node*> _children) {val = _val;children = _children;}
};
*/
class Solution {
public:void __preorder(Node* root,vector<int> &ans){if(root==NULL) return ;ans.push_back(root->val);for(auto x:root->children){__preorder(x,ans);}}vector<int> preorder(Node* root) {vector<int> ans;__preorder(root,ans);return ans;}
};

429. N叉樹的層序遍歷

給定一個 N 叉樹,返回其節點值的層序遍歷。 (即從左到右,逐層遍歷)。

例如,給定一個 3叉樹 :

返回其層序遍歷:

[
[1],
[3,2,4],
[5,6]
]

思路:這道題依舊是模板題**,只不過一個節點有多個孩子了

class Solution {
public:vector<vector<int>> levelOrder(Node* root) {queue<Node*> que;if (root != NULL) que.push(root);vector<vector<int>> result;while (!que.empty()) {int size = que.size();vector<int> vec;for (int i = 0; i < size; i++) { Node* node = que.front();que.pop();vec.push_back(node->val);for (int i = 0; i < node->children.size(); i++) { // 將節點孩子加入隊列if (node->children[i]) que.push(node->children[i]);}}result.push_back(vec);}return result;}
};

515.在每個樹行中找最大值

您需要在二叉樹的每一行中找到最大的值。

思路:層序遍歷,取每一層的最大值

class Solution {
public:vector<int> largestValues(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<int> result;while (!que.empty()) {int size = que.size();int maxValue = INT_MIN; // 取每一層的最大值for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();maxValue = node->val > maxValue ? node->val : maxValue;if (node->left) que.push(node->left);if (node->right) que.push(node->right);}result.push_back(maxValue); // 把最大值放進數組}return result;}
};

116.填充每個節點的下一個右側節點指針

給定一個完美二叉樹,其所有葉子節點都在同一層,每個父節點都有兩個子節點。二叉樹定義如下:

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每個 next 指針,讓這個指針指向其下一個右側節點。如果找不到下一個右側節點,則將 next 指針設置為 NULL。

初始狀態下,所有 next 指針都被設置為 NULL。

思路:本題依然是層序遍歷,只不過在單層遍歷的時候記錄一下本層的頭部節點,然后在遍歷的時候讓前一個節點指向本節點就可以了

class Solution {
public:Node* connect(Node* root) {queue<Node*> que;if (root != NULL) que.push(root);while (!que.empty()) {int size = que.size();vector<int> vec;Node* nodePre;Node* node;for (int i = 0; i < size; i++) {if (i == 0) {nodePre = que.front(); // 取出一層的頭結點que.pop();node = nodePre;} else {node = que.front();que.pop();nodePre->next = node; // 本層前一個節點next指向本節點nodePre = nodePre->next;}if (node->left) que.push(node->left);if (node->right) que.push(node->right);}nodePre->next = NULL; // 本層最后一個節點指向NULL}return root;}
};

117.填充每個節點的下一個右側節點指針II

思路:這道題目說是二叉樹,但116題(上題)目說是完整二叉樹,其實沒有任何差別,一樣的代碼一樣的邏輯一樣的味道

class Solution {
public:Node* connect(Node* root) {queue<Node*> que;if (root != NULL) que.push(root);while (!que.empty()) {int size = que.size();vector<int> vec;Node* nodePre;Node* node;for (int i = 0; i < size; i++) {if (i == 0) {nodePre = que.front(); // 取出一層的頭結點que.pop();node = nodePre;} else {node = que.front();que.pop();nodePre->next = node; // 本層前一個節點next指向本節點nodePre = nodePre->next;}if (node->left) que.push(node->left);if (node->right) que.push(node->right);}nodePre->next = NULL; // 本層最后一個節點指向NULL}return root;}
};

總結

二叉樹的層序遍歷,就是圖論中的廣度優先搜索在二叉樹中的應用,需要借助隊列來實現(此時是不是又發現隊列的應用了)。(雖然不能一口氣打十個,打八個也還行。)

leetcode 103. 二叉樹的鋸齒形層序遍歷

  1. 先進行層序遍歷,將遍歷結果存儲到二維數組中,
  2. 依次遍歷存儲所有結果,
  3. 若對應行數為偶數,倒序該數組,反之,正常存儲。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:void getResult(TreeNode* root,int k,vector<vector<int>> &ans){if(root==NULL) return;if(k==ans.size()) ans.push_back(vector<int>());ans[k].push_back(root->val);getResult(root->left,k+1,ans);getResult(root->right,k+1,ans);return;}void reverse(vector<int> &ans){for(int i=0,j=ans.size()-1;i<j;i++,j--){swap(ans[i],ans[j]);}return;}vector<vector<int>> zigzagLevelOrder(TreeNode* root) {vector<vector<int>> ans;getResult(root,0,ans);for(int i=1;i<ans.size();i+=2){reverse(ans[i]);}return ans;}
};

leetcode 110. 平衡二叉樹

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int getHeight(TreeNode* root){if(root==NULL) return 0;int l=getHeight(root->left);//遞歸定義,當樹不平衡時候,返回負值int r=getHeight(root->right);if(l<0||r<0) return -2;  //不平衡返回負值-2if(abs(l-r)>1) return -2;//平衡>=0return max(l,r)+1;}bool isBalanced(TreeNode* root) {return getHeight(root)>=0;}
};

討論:遞歸函數的意義? 可以表示樹高 可表示樹平衡。。。

leetcode 112. 路徑總和

方法一:深度優先搜索遞歸

注意到本題的要求是,詢問是否有從「根節點」到某個「葉子節點」經過的路徑上的節點之和等于目標和。核心思想是對樹進行一次遍歷,在遍歷時記錄從根節點到當前節點的路徑和,以防止重復計算。

需要特別注意的是,給定的 root 可能為空

思路及算法

觀察要求我們完成的函數,我們可以歸納出它的功能:詢問是否存在從當前節點 root 到葉子節點的路徑,滿足其路徑和為 sum。

假定從根節點到當前節點的值之和為 val,我們可以將這個大問題轉化為一個小問題:是否存在從當前節點的子節點到葉子的路徑,滿足其路徑和為 sum - val。

不難發現這滿足遞歸的性質,若當前節點就是葉子節點,那么我們直接判斷 sum 是否等于 val 即可(因為路徑和已經確定,就是當前節點的值,我們只需要判斷該路徑和是否滿足條件)。若當前節點不是葉子節點,我們只需要遞歸地詢問它的子節點是否能滿足條件即可。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:bool hasPathSum(TreeNode* root, int targetSum) {if(root==NULL) return false;if(root->left == NULL && root->right ==NULL) return root->val==targetSum;if(root->left  && hasPathSum(root->left, targetSum- root->val)) return true;//左節點不為空,返回該節點的值與if(root->right && hasPathSum(root->right,targetSum- root->val)) return true;return false;}
};

復雜度分析

時間復雜度:O(N),其中 N 是樹的節點數。對每個節點訪問一次。

空間復雜度:O(H),其中 H 是樹的高度。空間復雜度主要取決于遞歸時棧空間的開銷,最壞情況下,樹呈現鏈狀,空間復雜度為 O(N)。平均情況下樹的高度與節點數的對數正相關,空間復雜度為 O(log N)。

‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

方法二:廣度優先搜索

思路及算法

首先我們可以想到使用廣度優先搜索的方式,記錄從根節點到當前節點的路徑和,以防止重復計算。

這樣我們使用兩個隊列,分別存儲將要遍歷的節點,以及根節點到這些節點的路徑和即可。

  bool hasPathSum(TreeNode* root, int targetSum) {if(root==NULL) return false;queue<TreeNode*> node;queue<int> val;node.push(root);val.push(root->val);while(!node.empty()){TreeNode* temp_node=node.front();int temp_val =val.front();node.pop();val.pop();if(temp_node->left == NULL && temp_node->right == NULL){if(temp_val == targetSum){return true;}continue;}if (temp_node->left != NULL){node.push(temp_node->left);val.push(temp_node->left->val + temp_val);}if (temp_node->right != NULL){node.push(temp_node->right);val.push(temp_node->right->val +temp_val);}}return false;}

復雜度分析

時間復雜度:O(N),其中 N 是樹的節點數。對每個節點訪問一次。

空間復雜度:O(N),其中 N 是樹的節點數。空間復雜度主要取決于隊列的開銷,隊列中的元素個數不會超過樹的節點數。

方法三:棧模擬遞歸(回溯)

如果使用棧模擬遞歸的話,那么如果做回溯呢?

此時棧里一個元素不僅要記錄該節點指針,還要記錄從頭結點到該節點的路徑數值總和。

C++就我們用pair結構來存放這個棧里的元素。

定義為:pair<TreeNode*, int> pair<節點指針,路徑數值> , 這個為棧里的一個元素。

如下代碼是使用棧模擬的前序遍歷,如下: 根左右,棧中順序,相反

 bool hasPathSum(TreeNode* root, int sum) {if(root ==NULL) return false;// 棧里要放的是pair<節點指針,路徑數值>stack<pair<TreeNode*,int>> node;node.push(pair<TreeNode*,int>(root,root->val));//初始化while( !node.empty()){pair<TreeNode*,int> temp=node.top();node.pop();//如果當前節點為葉子節點,p判斷該節點的路徑數就等于sum,滿足 返回trueif(!temp.first->left &&!temp.first->right&& sum == temp.second) return true;// 右節點,壓進去一個節點的時候,將該節點的路徑數值也記錄下來if(temp.first->right){int a = temp.first->right->val + temp.second;node.push(pair<TreeNode*,int>(temp.first->right,a));}// 左節點,壓進去一個節點的時候,將該節點的路徑數值也記錄下來if(temp.first->left){int b = temp.first->left->val + temp.second;node.push(pair<TreeNode*,int>(temp.first->left,b));}}wsx  return false;}

leetcode 105. 從前序與中序遍歷序列構造二叉樹

遞歸的解法 1 找根位置 2 遞歸建立左子樹 3遞歸建立右子樹

我們知道前序遍歷的第一個元素肯定是根節點,那么前序遍歷的第一個節點在中序位置之前的都是根節點的左子節點,之后的都是根節點的右子節點,我們來簡單畫個圖看一下

這里是隨便舉個例子,我們看到前序遍歷的3肯定是根節點,那么在中序遍歷中,3前面的都是3左子節點的值,3后面的都是3右子節點的值,他真正的結構是這樣的


TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {if(preorder.size()==0) return NULL;int pos=0;while(inorder[pos]!=preorder[0])  ++pos;vector<int> l_pre,l_in,r_pre,r_in;for(int i=0;i<pos;i++){l_pre.push_back(preorder[i+1]);l_in.push_back(inorder[i]);}for(int i=pos+1;i<preorder.size();i++){r_pre.push_back(preorder[i]);r_in.push_back(inorder[i]);}TreeNode *root=new TreeNode(preorder[0]);root->left=buildTree(l_pre,l_in);root->right=buildTree(r_pre,r_in);return root;}

2.使用指針解決

我們只需要使用3個指針即可。一個是preStart,他表示的是前序遍歷開始的位置,一個是inStart,他表示的是中序遍歷開始的位置。一個是inEnd,他表示的是中序遍歷結束的位置,我們主要是對中序遍歷的數組進行拆解,下面就以下面的這棵樹來畫個圖分析下

他的前序遍歷是:[3,9,8,5,2,20,15,7]

他的中序遍歷是:[5,8,9,2,3,15,20,7]

這里只要找到了前序遍歷的結點在中序遍歷的位置,我們就可以把中序遍歷數組分解為兩部分了。如果index是前序遍歷的某個值在中序遍歷數組中的索引,以index為根節點劃分的話,那么中序遍歷中

[0,index-1]就是根節點左子樹的所有節點,

[index+1,inorder.length-1]就是根節點右子樹的所有節點。

中序遍歷好劃分,那么前序遍歷呢,如果是左子樹:

preStart=index+1;

如果是右子樹就稍微麻煩點,

preStart=preStart+(index-instart+1);

preStart是當前節點比如m先序遍歷開始的位置,index-instart+1就是當前節點m左子樹的數量加上當前節點的數量,所以preStart+(index-instart+1)就是當前節點m右子樹前序遍歷開始的位置,我們來看下完整代碼

public TreeNode buildTree(int[] preorder, int[] inorder) {return helper(0, 0, inorder.length - 1, preorder, inorder);
}public TreeNode helper(int preStart, int inStart, int inEnd, int[] preorder, int[] inorder) {if (preStart > preorder.length - 1 || inStart > inEnd) {return null;}//創建結點TreeNode root = new TreeNode(preorder[preStart]);int index = 0;//找到當前節點root在中序遍歷中的位置,然后再把數組分兩半for (int i = inStart; i <= inEnd; i++) {if (inorder[i] == root.val) {index = i;break;}}root.left = helper(preStart + 1, inStart, index - 1, preorder, inorder);root.right = helper(preStart + index - inStart + 1, index + 1, inEnd, preorder, inorder);return root;
}

3,使用棧解決
如果使用棧來解決首先要搞懂一個知識點,就是前序遍歷挨著的兩個值比如m和n,他們會有下面兩種情況之一的關系。

1,n是m左子樹節點的值。

2,n是m右子樹節點的值或者是m某個祖先節點的右節點的值。

對于第一個知識點我們很容易理解,如果m的左子樹不為空,那么n就是m左子樹節點的值。

對于第二個問題,如果一個結點沒有左子樹只有右子樹,那么n就是m右子樹節點的值,如果一個結點既沒有左子樹也沒有右子樹,那么n就是m某個祖先節點的右節點,我們只要找到這個祖先節點就好辦了。

搞懂了這點,代碼就很容易寫了,下面看下完整代碼

public TreeNode buildTree(int[] preorder, int[] inorder) {if (preorder.length == 0)return null;Stack<TreeNode> s = new Stack<>();//前序的第一個其實就是根節點TreeNode root = new TreeNode(preorder[0]);TreeNode cur = root;for (int i = 1, j = 0; i < preorder.length; i++) {//第一種情況if (cur.val != inorder[j]) {cur.left = new TreeNode(preorder[i]);s.push(cur);cur = cur.left;} else {//第二種情況j++;//找到合適的cur,然后確定他的右節點while (!s.empty() && s.peek().val == inorder[j]) {cur = s.pop();j++;}//給cur添加右節點cur = cur.right = new TreeNode(preorder[i]);}}return root;
}

leetcode 222. 完全二叉樹的節點個數

方法一:適合所有類型的樹的節點計算

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int countNodes(TreeNode* root) {if(root==NULL) return 0;return countNodes(root->left)+countNodes(root->right)+1;}
};

方法二:dfs

滿二叉樹
定義:一個二叉樹,如果每一個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。
也就是說,如果一個二叉樹的層數為K(從1開始的),每一層的節點個數為2^(k-1) ,且結點總數是(2^k)-1.
代碼:k為子樹的層數,那么子樹的節點總數(1<<k)-1;加減運算優先級高于位移運算符

完全二叉樹
定義:它是一棵空樹或者它的葉子節點只出在最后兩層,若最后一層不滿則葉子節點只在最左側。

1.如果根節點的左子樹深度等于右子樹深度,則說明左子樹為滿二叉樹。
//(1<<left_level)注意括號,子樹節點數目為(1<<left_level)-1,再+1(root節點)
if(left_level == right_level){return (1<<left_level) + count(root->right);
} 

  1. 如果根節點的左子樹深度大于右子樹深度,則說明右子樹為滿二叉樹
if(left_level != right_level) {return count(root->left) + (1<<right_level);
}

三種做法的代碼

1.暴力求解,深度遞歸dfs

class Solution {
public:int countNodes(TreeNode* root) {return root == NULL? 0:countNodes(root->left)+countNodes(root->right)+1;}
};

2.運用完全二叉樹的性質(和滿二叉樹結合)

class Solution {
public://計算子樹層數int countlevel(TreeNode* root){int level = 0;while(root!=NULL){root = root->left;level++;}return level;}int countNodes(TreeNode* root) {if(root==NULL)return 0;int leftlevel = countlevel(root->left);int rightlevel = 0;int count = 0;while(root){rightlevel = countlevel(root->right);//左邊子樹是滿二叉樹if(leftlevel == rightlevel){count += (1<<leftlevel);root = root->right;}//右邊子樹是滿二叉樹else{count += (1<<rightlevel);root = root->left;}leftlevel--;    }return count;}
};
  1. 運用編碼和位運算
  • 通過二分查找,尋找葉子節點是否存在
class Solution {
public: bool exit(int index,TreeNode* root,int level){//level=3,k子樹的層數對應 100,按位取&計算index,往左還是往右int k = 1<<(level-1);while(root && k>0){//1右移if(index & k){root = root->right;}//0右移else{root = root->left;}k>>=1;}return root!=NULL;}int countNodes(TreeNode* root) {if(root==NULL)return 0;int level = -1;TreeNode* root1 = root;while(root){root=root->left;level++;}int low = (1<<level);int high = (1<<(level+1))-1;int mid = 0;while(low < high){//mid存在,往右側找//因為high = mid-1,縮小的右邊;需要向上取整mid = (high - low + 1) / 2 + low;// mid = low+((high-low+1)>>1);if(exit(mid,root1,level)){low = mid;}//mid不存在,左側找else{high = mid-1;}}return low;}
};

3.方法三:二分查找 + 位運算

對于任意二叉樹,都可以通過廣度優先搜索或深度優先搜索計算節點個數,時間復雜度和空間復雜度都是 O(n),其中 n 是二叉樹的節點個數。這道題規定了給出的是完全二叉樹,因此可以利用完全二叉樹的特性計算節點個數。

規定根節點位于第 00 層,完全二叉樹的最大層數為 h。根據完全二叉樹的特性可知,完全二叉樹的最左邊的節點一定位于最底層,因此從根節點出發,每次訪問左子節點,直到遇到葉子節點,該葉子節點即為完全二叉樹的最左邊的節點,經過的路徑長度即為最大層數h。

當 0≤i<h 時,第 i層包含2^i ,最底層包含的節點數最少為 1,最多為 2^h 。

當最底層包含 1個節點時,完全二叉樹的節點個數是
∑i=0h?12i+1=2h\sum_{i=0}^{h-1}{2^i+1=2^h} i=0h?1?2i+1=2h
當最底層包含 2^h個節點時,完全二叉樹的節點個數是
∑i=0h2i=2h+1?1\sum_{i=0}^{h}{2^i}=2^{h+1}-1 i=0h?2i=2h+1?1
因此對于最大層數為 h的完全二叉樹,節點個數一定在 [2h,2{h+1}-1][2 h ,2 h+1 ?1] 的范圍內,可以在該范圍內通過二分查找的方式得到完全二叉樹的節點個數。

具體做法是,根據節點個數范圍的上下界得到當前需要判斷的節點個數 k,如果第 k個節點存在,則節點個數一定大于或等于 k,如果第 k個節點不存在,則節點個數一定小于 k,由此可以將查找的范圍縮小一半,直到得到節點個數。

如何判斷第 k 個節點是否存在呢?如果第 k 個節點位于第 h 層,則 k 的二進制表示包含 h+1 位,其中最高位是 1,其余各位從高到低表示從根節點到第 k個節點的路徑,0表示移動到左子節點,1表示移動到右子節點。通過位運算得到第 k 個節點對應的路徑,判斷該路徑對應的節點是否存在,即可判斷第 k 個節點是否存在。

class Solution {
public:int countNodes(TreeNode* root) {if (root == nullptr) {return 0;}int level = 0;TreeNode* node = root;while (node->left != nullptr) {level++;node = node->left;}int low = 1 << level, high = (1 << (level + 1)) - 1;while (low < high) {int mid = (high - low + 1) / 2 + low;if (exists(root, level, mid)) {low = mid;} else {high = mid - 1;}}return low;}bool exists(TreeNode* root, int level, int k) {int bits = 1 << (level - 1);TreeNode* node = root;while (node != nullptr && bits > 0) {if (!(bits & k)) {node = node->left;} else {node = node->right;}bits >>= 1;}return node != nullptr;}
};

  1            h = 0/ \2   3          h = 1/ \  /
4  5 6           h = 2
現在這個樹中的值都是節點的編號,最底下的一層的編號是[2^h ,2^h - 1],現在h = 2,也就是4, 5, 6, 7。
4, 5, 6, 7對應二進制分別為 100 101 110 111 不看最左邊的1,從第二位開始,0表示向左,1表示向右,正好可以表示這個節點相對于根節點的位置。
比如4的 00 就表示從根節點 向左 再向左。6的 10 就表示從根節點 向右 再向左那么想訪問最后一層的節點就可以從節點的編號的二進制入手。從第二位開始的二進制位表示了最后一層的節點相對于根節點的位置。
那么就需要一個bits = 2^(h - 1) 這里就是2,對應二進制為010。這樣就可以從第二位開始判斷。
比如看5這個節點存不存在,先通過位運算找到編號為5的節點相對于根節點的位置。010 & 101 發現第二位是0,說明從根節點開始,第一步向左走。
之后將bit右移一位,變成001。001 & 101 發現第三位是1,那么第二步向右走。
最后bit為0,說明已經找到編號為5的這個節點相對于根節點的位置,看這個節點是不是空,不是說明存在,exist返回真
編號為5的節點存在,說明總節點數量一定大于等于5。所以二分那里low = mid再比如看7存不存在,010 & 111 第二位為1,第一部從根節點向右;001 & 111 第三位也為1,第二步繼續向右。
然后判斷當前節點是不是null,發現是null,exist返回假。
編號為7的節點不存在,說明總節點數量一定小于7。所以high = mid - 1

leetcode 劍指 Offer 54. 二叉搜索樹的第k大節點

方法一:遞歸的方式

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:int getCount(TreeNode *root){if(root==NULL) return 0;return getCount(root->left)+getCount(root->right)+1;}int kthLargest(TreeNode* root, int k) {int cnt_r=getCount(root->right);if(k<=cnt_r) return kthLargest(root->right,k);if(k==cnt_r+1) return root->val;return kthLargest(root->left,k-cnt_r-1);}
};

方法二:先求前序遍歷數組,在根據數組求第k大的值

 void in_order(TreeNode *root,vector<int> &ans){if(root == NULL) return;in_order(root->left,ans);ans.push_back(root->val);in_order(root->right,ans);return;}int kthLargest(TreeNode* root, int k) {vector<int> ans;in_order(root,ans);return ans[ans.size() -k];}

本文解法基于此性質:二叉搜索樹的中序遍歷為 遞增序列

根據以上性質,易得二叉搜索樹的 中序遍歷倒序 為 遞減序列 。
因此,求 “二叉搜索樹第 k大的節點” 可轉化為求 “此樹的中序遍歷倒序的第 k 個節點”。

中序遍歷 為 “左、根、右” 順序,遞歸法代碼如下

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DTqA705W-1619446816627)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1619020119201.png)]

復雜度分析:
時間復雜度 O(N)O(N) : 當樹退化為鏈表時(全部為右子節點),無論 kk 的值大小,遞歸深度都為 NN ,占用 O(N)O(N) 時間。
空間復雜度 O(N)O(N) : 當樹退化為鏈表時(全部為右子節點),系統使用 O(N)O(N) 大小的棧空間。

leetcode 劍指 Offer 26. 樹的子結構

方法1:遞歸

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:bool isMatch(TreeNode *A,TreeNode *B){if(B==NULL) return true;if(A==NULL) return false;if(A->val !=B->val) return false;return isMatch(A->left,B->left) && isMatch(A->right,B->right);}bool isSubStructure(TreeNode* A, TreeNode* B) {if(B==NULL) return false;if(A==NULL) return false;if(A->val == B->val && isMatch(A,B)) return true;return isSubStructure(A->left,B) || isSubStructure(A->right,B);}
};
 bool isSubStructure(TreeNode* A, TreeNode* B) {if(!A || !B) return false;bool res = false;// 如果在 A 中匹配到了與 B 的根節點的值一樣的節點if(A -> val == B -> val) res = doesAHaveB(A, B);// 如果匹配不到,A 往左if(!res) res = isSubStructure(A -> left, B);// 還匹配不到,A 往右if(!res) res = isSubStructure(A -> right, B);return res;}bool doesAHaveB(TreeNode *r1, TreeNode *r2){// 如果 B 已經遍歷完了,trueif(!r2) return true;// 如果 B 還有,但 A 遍歷完了,那 B 剩下的就沒法匹配了,falseif(!r1) return false;// 不相等,falseif(r1 -> val != r2 -> val) return false;return doesAHaveB(r1 -> left, r2 -> left) && doesAHaveB(r1 -> right, r2 -> right);}

leetcode 662. 二叉樹最大寬度

方法 0:寬度優先搜索 [Accepted]

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:typedef pair<TreeNode*,int> PNI;int widthOfBinaryTree(TreeNode* root) {int ans=0;queue<PNI> q;q.push(PNI(root,0));while(!q.empty()){int cnt = q.size();int l=q.front().second,r=q.front().second;for(int i=0;i<cnt;i++){TreeNode *n=q.front().first;int ind=q.front().second;r =ind;if(n->left) q.push(PNI(n->left,(ind-l)*2));if(n->right) q.push(PNI(n->right,(ind-l)*2+1));q.pop();}ans =max(ans,r-l+1);}return ans;}
};

方法框架
解釋

由于我們需要將給定樹中的每個節點都訪問一遍,我們需要遍歷樹。我們可以用深度優先搜索或者寬度優先搜索將樹遍歷。

這個問題中的主要想法是給每個節點一個 position 值,如果我們走向左子樹,那么 position -> position * 2,如果我們走向右子樹,那么 position -> position * 2 + 1。當我們在看同一層深度的位置值 L 和 R 的時候,寬度就是 R - L + 1。

方法 1:寬度優先搜索 [Accepted]

想法和算法

寬度優先搜索順序遍歷每個節點的過程中,我們記錄節點的 position 信息,對于每一個深度,第一個遇到的節點是最左邊的節點,最后一個到達的節點是最右邊的節點。

class Solution {
public:int widthOfBinaryTree(TreeNode* root) {vector<TreeNode*> bfs={root}; //根入隊int Depth=0;int max_width=0;while(1){int sum=bfs.size();int start=0,end=0;bool flag=0;int count=0;//遍歷當前隊列中的元素,即同層的所有節點for(int i=0;i<sum;i++){if(bfs[i]!=NULL){end=i;if(flag==0)  start=i;flag=1;//當前隊列的子節點(下一層)入隊bfs.push_back(bfs[i]->left);bfs.push_back(bfs[i]->right);}else{count++;//如果為空,那么兩個空入隊bfs.push_back(NULL);bfs.push_back(NULL);}}if(count==pow(2,Depth))//一層全為空break;if((end-start+1)>max_width)max_width=end-start+1;//當前節點(當前層)出隊bfs.erase(bfs.begin(),bfs.begin()+sum);Depth++;}return max_width;}
};

剛開始用BFS做,主要思想就是:把空節點也入隊,這樣BFS時,就能得到完整的一層節點(空和非空都存在),然后再遍歷該層節點,定位寬度即可,但是這樣做會因為倒數第二個的變態輸入而超時:

復雜度分析

時間復雜度: O(N)O(N),其中 NN 是輸入樹的節點數目,我們遍歷每個節點一遍。

空間復雜度: O(N)O(N),這是 queue 的大小。

方法 2:深度優先搜索 [Accepted]

想法和算法

按照深度優先的順序,我們記錄每個節點的 position 。對于每一個深度,第一個到達的位置會被記錄在 left[depth] 中。

然后對于每一個節點,它對應這一層的可能寬度是 pos - left[depth] + 1 。我們將每一層這些可能的寬度去一個最大值就是答案。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:vector<int> left;int max_width=0;void dfs(TreeNode *root, unsigned long long index,int level){if(root ==NULL) return;if(level >left.size())left.push_back(index);if((index-left[level-1]+1)>max_width)max_width=index-left[level-1] +1;dfs(root->left,2*index,level+1);dfs(root->right,2*index+1,level+1);}int widthOfBinaryTree(TreeNode* root) {if(root ==NULL) return 0;dfs(root,1,1);return max_width;}
};

只需要利用樹和數組轉換關系:若假設當前節點在數組中的下標為i,那么左節點為下標為:2i,右節點為:2i+1
此外,還需要用一個容器保存每層最左節點的下標,容器的大小就是遍歷的深度,這樣遍歷每個節點時,比較當前下標-最左節點下標+1和最大寬度,取較大者,就可以得到樹的最大寬度。
那么怎么判斷當前節點是該層最左節點?根據前序遍歷的特點,最左節點一定是同層中第一個出現的節點,也就是level大于容器大小時,left容器push一個當前下標,這樣再遍歷同層其他節點時,由于left已經push了一個,該判斷也就不會為真了。

leetcode 968. 監控二叉樹

第一種解法

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Zs0HVPbY-1619446816633)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1619222543902.png)]

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:void getDP(TreeNode* root,int dp[2][2]){//dp[0][0] //父節點不放攝像頭,本節點也不放攝像頭,像覆蓋掉所有子樹,最少放置的攝像頭數量//dp數組中存放的攝像頭數量 為當前節點的數量,只能為0或者1,10000表示當前這種狀態不存在if(root ==NULL){dp[0][0] =0;dp[0][1] =10000;dp[1][0] =0;dp[1][1] =10000;return;}if(root->left ==NULL && root->right ==NULL){dp[0][0] =10000;dp[0][1] =1;dp[1][0] =0;dp[1][1] =1;}int l[2][2],r[2][2];getDP(root->left,l);getDP(root->right,r);dp[0][0] =min(min(l[0][1]+r[0][0],l[0][0]+r[0][1]),l[0][1]+r[0][1]);//表示三種狀態取最小,l[0][1]表示父節點不放,當前左節點放;r[0][1]表示父節點不放,當		  //前右節點放dp[1][0] =min(dp[0][0],l[0][0]+r[0][0]);//dp[1][0]4種情況,表示當前節點的子節點放不放均可dp[0][1] =min(min(l[1][0]+r[1][0],l[1][1]+r[1][1]),min(l[1][0]+r[1][1],l[1][1]+r[1][0]))+1;//求4種情況下和的最小值,前面2種情況,要么多放,要么都不放;后面2種情況一個放,一個不放;dp[1][1] =dp[0][1];//當前節點放一個攝像頭,可以覆蓋周圍return;}int minCameraCover(TreeNode* root) {int dp[2][2];getDP(root,dp);return min(dp[0][1],dp[0][0]);}
};

解題思路
這道題目其實不是那么好理解的,題目舉的示例不是很典型,會誤以為攝像頭必須要放在中間,其實放哪里都可以只要覆蓋了就行。

這道題目難在兩點:

需要確定遍歷方式
需要狀態轉移的方程
我們之前做動態規劃的時候,只要最難的地方在于確定狀態轉移方程,至于遍歷方式無非就是在數組或者二維數組上。

本題并不是動態規劃,其本質是貪心,但我們要確定狀態轉移方式,而且要在樹上進行推導,所以難度就上來了,一些同學知道這道題目難,但其實說不上難點究竟在哪。

需要確定遍歷方式
首先先確定遍歷方式,才能確定轉移方程,那么該如何遍歷呢?

在安排選擇攝像頭的位置的時候,我們要從底向上進行推導,因為盡量讓葉子節點的父節點安裝攝像頭,這樣攝像頭的數量才是最少的 ,這也是本道貪心的原理所在!

如何從低向上推導呢?

就是后序遍歷也就是左右中的順序,這樣就可以從下到上進行推導了。

后序遍歷代碼如下:

    int traversal(TreeNode* cur) {// 空節點,該節點有覆蓋if (終止條件) return ;int left = traversal(cur->left);    // 左int right = traversal(cur->right);  // 右邏輯處理                            // 中return ;}

注意在以上代碼中我們取了左孩子的返回值,右孩子的返回值,即 left 和 right, 以后推導中間節點的狀態

需要狀態轉移的方程
確定了遍歷順序,再看看這個狀態應該如何轉移,先來看看每個節點可能有幾種狀態:

可以說有如下三種:

該節點無覆蓋
本節點有攝像頭
本節點有覆蓋
我們分別有三個數字來表示:

0:該節點無覆蓋
1:本節點有攝像頭
2:本節點有覆蓋
大家應該找不出第四個節點的狀態了。

一些同學可能會想有沒有第四種狀態:本節點無攝像頭,其實無攝像頭就是 無覆蓋 或者 有覆蓋的狀態,所以一共還是三個狀態。

那么問題來了,空節點究竟是哪一種狀態呢? 空節點表示無覆蓋? 表示有攝像頭?還是有覆蓋呢?

回歸本質,為了讓攝像頭數量最少,我們要盡量讓葉子節點的父節點安裝攝像頭,這樣才能攝像頭的數量最少。

那么空節點不能是無覆蓋的狀態,這樣葉子節點就可以放攝像頭了,空節點也不能是有攝像頭的狀態,這樣葉子節點的父節點就沒有必要放攝像頭了,而是可以把攝像頭放在葉子節點的爺爺節點上。

所以空節點的狀態只能是有覆蓋,這樣就可以在葉子節點的父節點放攝像頭了

接下來就是遞推關系。

那么遞歸的終止條件應該是遇到了空節點,此時應該返回 2(有覆蓋),原因上面已經解釋過了。

代碼如下:

        // 空節點,該節點有覆蓋if (cur == NULL) return 2;

遞歸的函數,以及終止條件已經確定了,再來看單層邏輯處理。

主要有如下四類情況:

情況1:左右節點都有覆蓋

左孩子有覆蓋,右孩子有覆蓋,那么此時中間節點應該就是無覆蓋的狀態了。

如圖:

代碼如下:

        // 左右節點都有覆蓋if (left == 2 && right == 2) return 0;

情況2:左右節點至少有一個無覆蓋的情況

如果是以下情況,則中間節點(父節點)應該放攝像頭:

left == 0 && right == 0 左右節點無覆蓋
left == 1 && right == 0 左節點有攝像頭,右節點無覆蓋
left == 0 && right == 1 左節點有無覆蓋,右節點攝像頭
left == 0 && right == 2 左節點無覆蓋,右節點覆蓋
left == 2 && right == 0 左節點覆蓋,右節點無覆蓋

這個不難理解,畢竟有一個孩子沒有覆蓋,父節點就應該放攝像頭。

此時攝像頭的數量要加一,并且 return 1,代表中間節點放攝像頭。

代碼如下:

    if (left == 0 || right == 0) {result++;return 1;}

情況3:左右節點至少有一個有攝像頭

如果是以下情況,其實就是 左右孩子節點有一個有攝像頭了,那么其父節點就應該是2(覆蓋的狀態)

left == 1 && right == 2 左節點有攝像頭,右節點有覆蓋
left == 2 && right == 1 左節點有覆蓋,右節點有攝像頭
left == 1 && right == 1 左右節點都有攝像頭

代碼如下:

    if (left == 1 || right == 1) return 2;

從這個代碼中,可以看出,如果 left == 1, right == 0 怎么辦?其實這種條件在情況 2 中已經判斷過了,如圖:

這種情況也是大多數同學容易迷惑的情況。

情況4:頭結點沒有覆蓋

以上都處理完了,遞歸結束之后,可能頭結點 還有一個無覆蓋的情況,如圖:

所以遞歸結束之后,還要判斷根節點,如果沒有覆蓋,result++,代碼如下:

int minCameraCover(TreeNode* root) {result = 0;if (traversal(root) == 0) { // root 無覆蓋result++;}return result;
}

以上四種情況我們分析完了,代碼也差不多了,整體代碼如下:

(以下我的代碼是可以精簡的,但是我是為了把情況說清楚,特別把每種情況列出來,因為精簡之后的代碼讀者不好理解。)

C++

class Solution {
private:
int result;
int traversal(TreeNode* cur) {

    // 空節點,該節點有覆蓋if (cur == NULL) return 2;int left = traversal(cur->left);    // 左int right = traversal(cur->right);  // 右// 情況1// 左右節點都有覆蓋if (left == 2 && right == 2) return 0;// 情況2// left == 0 && right == 0 左右節點無覆蓋// left == 1 && right == 0 左節點有攝像頭,右節點無覆蓋// left == 0 && right == 1 左節點有無覆蓋,右節點攝像頭// left == 0 && right == 2 左節點無覆蓋,右節點覆蓋// left == 2 && right == 0 左節點覆蓋,右節點無覆蓋if (left == 0 || right == 0) {result++;return 1;}// 情況3// left == 1 && right == 2 左節點有攝像頭,右節點有覆蓋// left == 2 && right == 1 左節點有覆蓋,右節點有攝像頭// left == 1 && right == 1 左右節點都有攝像頭// 其他情況前段代碼均已覆蓋if (left == 1 || right == 1) return 2;// 以上代碼我沒有使用else,主要是為了把各個分支條件展現出來,這樣代碼有助于讀者理解// 這個 return -1 邏輯不會走到這里。return -1;
}
public:int minCameraCover(TreeNode* root) {result = 0;// 情況4if (traversal(root) == 0) { // root 無覆蓋result++;}return result;}
};

t == 0 || right == 0) {
result++;
return 1;
}

情況3:左右節點至少有一個有攝像頭

如果是以下情況,其實就是 左右孩子節點有一個有攝像頭了,那么其父節點就應該是2(覆蓋的狀態)

left == 1 && right == 2 左節點有攝像頭,右節點有覆蓋
left == 2 && right == 1 左節點有覆蓋,右節點有攝像頭
left == 1 && right == 1 左右節點都有攝像頭

代碼如下:

    if (left == 1 || right == 1) return 2;

從這個代碼中,可以看出,如果 left == 1, right == 0 怎么辦?其實這種條件在情況 2 中已經判斷過了,如圖:

這種情況也是大多數同學容易迷惑的情況。

情況4:頭結點沒有覆蓋

以上都處理完了,遞歸結束之后,可能頭結點 還有一個無覆蓋的情況,如圖:

所以遞歸結束之后,還要判斷根節點,如果沒有覆蓋,result++,代碼如下:

int minCameraCover(TreeNode* root) {result = 0;if (traversal(root) == 0) { // root 無覆蓋result++;}return result;
}

以上四種情況我們分析完了,代碼也差不多了,整體代碼如下:

(以下我的代碼是可以精簡的,但是我是為了把情況說清楚,特別把每種情況列出來,因為精簡之后的代碼讀者不好理解。)

C++

class Solution {
private:
int result;
int traversal(TreeNode* cur) {

    // 空節點,該節點有覆蓋if (cur == NULL) return 2;int left = traversal(cur->left);    // 左int right = traversal(cur->right);  // 右// 情況1// 左右節點都有覆蓋if (left == 2 && right == 2) return 0;// 情況2// left == 0 && right == 0 左右節點無覆蓋// left == 1 && right == 0 左節點有攝像頭,右節點無覆蓋// left == 0 && right == 1 左節點有無覆蓋,右節點攝像頭// left == 0 && right == 2 左節點無覆蓋,右節點覆蓋// left == 2 && right == 0 左節點覆蓋,右節點無覆蓋if (left == 0 || right == 0) {result++;return 1;}// 情況3// left == 1 && right == 2 左節點有攝像頭,右節點有覆蓋// left == 2 && right == 1 左節點有覆蓋,右節點有攝像頭// left == 1 && right == 1 左右節點都有攝像頭// 其他情況前段代碼均已覆蓋if (left == 1 || right == 1) return 2;// 以上代碼我沒有使用else,主要是為了把各個分支條件展現出來,這樣代碼有助于讀者理解// 這個 return -1 邏輯不會走到這里。return -1;
}
public:int minCameraCover(TreeNode* root) {result = 0;// 情況4if (traversal(root) == 0) { // root 無覆蓋result++;}return result;}
};

總結

以上是生活随笔為你收集整理的【leetcode】二叉树与经典问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

午夜精品久久久久久久久久久久久久 | 国产亚洲精品xxoo | 五月开心激情 | 欧美一级特黄aaaaaa大片在线观看 | 免费视频一二三区 | 狠狠网亚洲精品 | 久久久久久久久久亚洲精品 | 天天爱天天操天天射 | 中文字幕久久精品 | 国产女教师精品久久av | 日韩有色 | 久久精品国产免费 | 国产黄影院色大全免费 | 久久99国产精品视频 | 西西44人体做爰大胆视频 | 精品国产乱码久久久久久天美 | 中文字幕在线日亚洲9 | 亚洲精品1234区 | 一区二区三区四区久久 | 丁香六月在线观看 | 美女免费视频观看网站 | 婷婷福利影院 | 视频在线播放国产 | 日韩在线观看视频在线 | 国产精品免费久久 | 亚洲国产精品99久久久久久久久 | 国产永久免费 | 国产午夜精品视频 | av中文在线影视 | 国产黄色片免费 | 91精品爽啪蜜夜国产在线播放 | 黄色毛片大全 | 四虎www.| 亚洲成av人片一区二区梦乃 | 日韩中文字幕在线观看 | 国产精品人成电影在线观看 | 国产亚洲视频中文字幕视频 | 日日干夜夜骑 | 国产精品亚 | 日本中文字幕视频 | 99 精品 在线 | 国产精品1000 | 天天操天天操天天 | 91成人午夜 | 91在线视频| 日韩电影久久 | 日韩视频免费观看高清 | 国内成人精品2018免费看 | 久久久国产一区 | 日韩欧美精品在线 | 国产艹b视频 | 韩国一区二区三区在线观看 | 成年人免费看片 | 99热精品国产 | 美女视频永久黄网站免费观看国产 | 在线观看视频91 | 深夜精品福利 | 色91在线视频 | 亚洲日本在线视频观看 | 在线视频专区 | 最近最新中文字幕 | 伊人成人久久 | 精品久久一 | 99视频 | 日日日干 | 欧美精品黑人性xxxx | 麻豆国产精品视频 | 天天操天天操天天干 | 久久成人国产精品一区二区 | 五月婷婷六月丁香在线观看 | 久久国产精品区 | 97国产精品久久 | 国产视频精品在线 | 久久综合五月婷婷 | 久久9精品| 久久亚洲影院 | 亚洲 欧美变态 另类 综合 | 91精品免费视频 | 欧美精品在线一区二区 | 国产女教师精品久久av | 日日干精品| 日韩视频一区二区三区在线播放免费观看 | 亚洲三级网站 | 久久精品欧美 | 婷婷av电影 | 一区中文字幕 | 亚洲免费视频观看 | 久黄色 | 最近中文国产在线视频 | 97超碰中文字幕 | 亚洲精品一区二区三区在线观看 | 韩国在线视频一区 | 中文 一区二区 | 久久夜色精品国产欧美乱极品 | 国产精品久久久久9999 | 欧美日韩一区二区三区在线观看视频 | 免费av观看 | 国产999| 国产一区视频在线播放 | 日日干视频| 人人爽人人爽人人片 | 日韩特级片 | 一区二区三区www | 九九视频精品免费 | 一区二区精品国产 | 曰韩精品 | 国产精品高| 97超碰在线视 | 中文资源在线播放 | 日韩中文字幕免费在线观看 | 国产99中文字幕 | 日日躁天天躁 | 色瓜| 久久精品视频免费观看 | 国产精品一区二区av | 国产精品一区二区三区观看 | 日日激情| 国产涩涩网站 | 亚洲国产成人av网 | 三级av免费看 | 四虎免费在线观看视频 | 99视频网址| 亚洲永久精品视频 | 国产精品中文字幕在线 | 开心色婷婷 | 久久精品综合 | 天天综合区 | 91精品视频播放 | 99九九视频 | 精品国产一区二区三区av性色 | 欧美久久久久久久久中文字幕 | 正在播放日韩 | 91在线精品视频 | 免费a网 | 日韩大片免费在线观看 | 99视频久 | 中文字幕在线观看91 | 高清不卡毛片 | 丁香激情综合久久伊人久久 | 欧美一级淫片videoshd | 免费色视频网站 | 国产精品99久久久久久人免费 | 一区二区伦理电影 | 欧美精品一区二区性色 | 在线播放国产一区二区三区 | 91麻豆精品久久久久久 | 欧美日韩免费在线观看视频 | 亚洲精品ww | 中文字幕亚洲欧美日韩 | 超碰夜夜| 中文日韩在线视频 | 一本之道乱码区 | 国产午夜精品一区二区三区在线观看 | 色在线视频 | 偷拍精品一区二区三区 | 五月花激情 | 国产精品一区二区在线观看免费 | 超碰在线日本 | 国产精品18久久久久久vr | 视频国产区| 亚洲黄色免费观看 | 国产一区二区手机在线观看 | 99视频在线观看免费 | 91成人天堂久久成人 | 又黄又刺激 | 亚洲蜜桃在线 | 精品伊人久久久 | 国产成人三级在线观看 | 99爱在线观看 | 91成人天堂久久成人 | 久久精品毛片基地 | 国产一线天在线观看 | www,黄视频 | 欧美激情片在线观看 | 免费99| 中文字幕av网站 | 国产第一二区 | 日日夜夜网 | av高清在线 | 天天操天天干天天操天天干 | 欧美日韩中 | 久草视频在线观 | 成全在线视频免费观看 | 色网站国产精品 | 日韩二区在线观看 | 国产福利91精品张津瑜 | 草樱av | 中文字幕中文字幕在线中文字幕三区 | 天天爽天天爽天天爽 | 欧美日韩国产页 | 2019中文字幕第一页 | 西西444www大胆无视频 | 久久久久久久久精 | 四虎伊人 | 久久国产精品色av免费看 | 国产精品久久嫩一区二区免费 | 黄色资源网站 | 天天草天天草 | 免费三级影片 | 深爱婷婷激情 | 在线中文字母电影观看 | 亚洲精品456在线播放 | 毛片网站在线观看 | av亚洲产国偷v产偷v自拍小说 | 久操免费视频 | 久久久精品免费看 | 在线免费精品视频 | 免费看国产视频 | 亚洲成人av一区 | 婷婷视频在线 | 亚洲欧美日韩精品久久奇米一区 | 亚洲精品视频免费看 | av午夜电影 | 黄色成人在线观看 | 国产精品久久久久免费 | 国产成人免费av电影 | 一级片在线 | 丁香花中文在线免费观看 | 成人av一区二区兰花在线播放 | 91秒拍国产福利一区 | 97超碰人人澡人人爱学生 | 免费观看www7722午夜电影 | av五月婷婷 | 男女全黄一级一级高潮免费看 | 免费视频99 | 97在线视频免费 | 久久国产乱 | 特级西西444www大胆高清无视频 | 国产成人精品一区二区三区在线观看 | 亚洲成人av影片 | 亚a在线| 91经典在线| 中文字幕在线观看的网站 | 国产天天爽 | 欧洲亚洲激情 | 日韩在观看线 | 欧美日韩一区二区免费在线观看 | 中文字幕区 | 最近高清中文字幕 | 精品影院一区二区久久久 | 蜜桃视频成人在线观看 | 国产视频高清 | 亚洲男人天堂a | 国产亚洲欧美一区 | 国产精品一区二区电影 | 日韩精品免费一线在线观看 | 超碰官网 | 国产乱码精品一区二区三区介绍 | 午夜影院在线观看18 | 韩国av不卡 | 日韩欧美视频一区二区三区 | 免费日韩 精品中文字幕视频在线 | 亚洲天堂毛片 | 国产精品一区二区三区四区在线观看 | 四虎小视频 | 中字幕视频在线永久在线观看免费 | 色欧美成人精品a∨在线观看 | 人人爽人人爽人人爽 | 国产精品一区二区白浆 | 一区二区三区日韩在线 | 欧美精品久久久久久久免费 | 国产一级片久久 | 国产成人精品午夜在线播放 | 中文字幕亚洲不卡 | 亚洲女人天堂成人av在线 | 十八岁免进欧美 | 亚洲国产精品视频 | 日韩精品无 | 国产美女精品 | 91免费日韩| 久久久久蜜桃 | 天天摸日日操 | 国产精品www | 国产精品麻豆一区二区三区 | 91视频 - 114av | 人人搞人人搞 | 日韩两性视频 | 日韩激情片在线观看 | 啪一啪在线 | 丝袜网站在线观看 | 亚洲无吗av | 日韩精品国产一区 | 欧美久久久一区二区三区 | 国产高清久久久 | 黄色小说视频网站 | 九九热免费在线视频 | 在线免费视 | 国产欧美三级 | 日韩成人欧美 | 国产精品不卡一区 | 最新91在线视频 | 久久国产系列 | 免费黄色av片 | 国产精品18久久久久vr手机版特色 | 久操视频在线免费看 | 天天操天天干天天爽 | 天天爱天天干天天爽 | 人人射人人爱 | 久久视频这里有精品 | 欧美一级久久久 | 日本激情中文字幕 | av爱干| 亚洲国产精品久久久久婷婷884 | 91成人在线观看高潮 | 天天拍天天操 | 色爱成人网 | 色噜噜日韩精品欧美一区二区 | 天天干亚洲 | 天天操福利视频 | 午夜影院三级 | 亚洲成人黄色在线观看 | 久久久99国产精品免费 | 国产精品99久久久久久武松影视 | 在线观看一 | 中文字幕色综合网 | 蜜臀久久99静品久久久久久 | 日韩 在线a | 亚洲电影图片小说 | 欧美色综合久久 | 国产精品久久久久久久久久久久午夜片 | 成人中心免费视频 | 国产高清中文字幕 | 亚洲国产合集 | 久久,天天综合 | 五月婷婷综合色拍 | 99久久精品免费看国产四区 | 99免费在线观看视频 | 免费h漫在线观看 | 成在线播放 | 日日夜夜网 | 国产黄色精品在线观看 | 99中文字幕在线观看 | 96亚洲精品久久久蜜桃 | 久久激情电影 | 一区二区三区在线免费观看视频 | 天天摸天天舔 | 国产视频午夜 | 成人性生交大片免费观看网站 | 91资源在线播放 | 在线看片视频 | 丁香激情婷婷 | 五月婷婷综合激情网 | 91传媒在线 | 九精品| 亚洲情感电影大片 | 中文字幕色在线视频 | 日韩精品免费在线观看 | 久久综合狠狠综合久久狠狠色综合 | 黄色av电影在线观看 | 国产一级片网站 | 操操操日日 | 欧美视频二区 | 国产成人精品一区二区三区在线观看 | av超碰免费在线 | 婷婷激情av | 久久免费试看 | 亚洲综合在线播放 | 精品一区二三区 | 日韩午夜大片 | 婷婷激情在线 | 最近更新好看的中文字幕 | 久99久精品 | 国产色爽 | 久久国产精品精品国产色婷婷 | 久久永久免费视频 | 中文字幕国产一区二区 | 三上悠亚在线免费 | 日本黄色免费在线观看 | 久久免费视频国产 | 国产成人99av超碰超爽 | 国产一二三四在线视频 | 国产成人av网址 | 亚洲成人av免费 | 伊人天堂网 | 国产最顶级的黄色片在线免费观看 | 久久精品视频在线播放 | 91九色视频在线观看 | 日韩激情小视频 | 亚洲乱亚洲乱妇 | ww亚洲ww亚在线观看 | 久久99网站 | 亚洲精品一区二区三区四区高清 | 日日干日日色 | 中文字幕在线观看第三页 | 午夜精品久久久久久久99水蜜桃 | 亚洲激情国产精品 | 制服丝袜成人在线 | 一本到在线| 亚洲精品成人 | 国产精品久久在线观看 | 91九色porn在线资源 | 国产一线二线三线在线观看 | 999久久久| a天堂免费 | 国产精品久久久久久久久大全 | 在线播放一区二区三区 | 精品国产电影 | 五月天九九 | 久久综合精品国产一区二区三区 | 日韩精品一区二区免费视频 | 国产亚洲精品成人av久久影院 | 婷婷丁香色综合狠狠色 | 国产精品1区2区 | 麻豆精品传媒视频 | 99久久婷婷国产综合精品 | 国产色婷婷在线 | 久在线观看| 国产高清中文字幕 | 国产福利精品在线观看 | 亚洲国产午夜精品 | 国产黄色av影视 | 成人av片免费观看app下载 | 在线观看免费av网 | 在线观看免费观看在线91 | 中文理论片 | 久久久色 | 久久免费成人精品视频 | 天天操夜夜操天天射 | 亚洲精品影院在线观看 | 欧美91精品国产自产 | 亚洲精品乱码久久久一二三 | 在线观看一区二区视频 | 免费看黄网站在线 | 伊人久久五月天 | 国产在线综合视频 | 成人免费观看视频网站 | 天天干天天拍天天操 | 日本精品在线看 | 欧美美女激情18p | 青青草在久久免费久久免费 | 欧美一区成人 | 国产精品露脸在线 | 激情五月激情综合网 | 国产精品资源在线观看 | 成人在线视频论坛 | 亚洲精品高清一区二区三区四区 | 国产精品3| 国产一区二区久久久 | 日韩免费观看一区二区 | 国产成人综合在线观看 | 欧美日韩中文视频 | 国内精品久久久久久久影视简单 | 成人av教育 | 激情久久影院 | 天天爽人人爽夜夜爽 | 三级黄色片子 | 久久久久久国产精品久久 | 国产婷婷一区二区 | 狠狠色丁香婷婷综合视频 | 免费看日韩 | 国产精品国产三级国产不产一地 | 91成人精品一区在线播放 | 免费看成年人 | 色精品视频 | 久久夜色精品国产欧美乱极品 | 91成人精品视频 | 中文字幕韩在线第一页 | 欧美午夜精品久久久久 | 日韩视频一区二区三区 | 黄色电影小说 | 91福利在线观看 | 亚洲精品乱码久久久久久写真 | 精品国产乱码久久久久久1区二区 | 天天综合操 | 国产精品1区2区在线观看 | 91精品国产成人www | 久久美女视频 | 色视频在线免费观看 | 免费观看成人网 | 中文字幕在线观看第一区 | 国产男女免费完整视频 | 91在线视频导航 | 综合色综合色 | 麻豆一精品传二传媒短视频 | 天天操天天操天天操天天操天天操 | 亚洲国产免费网站 | 91精品啪在线观看国产线免费 | 国色天香av| 黄色最新网址 | av视屏在线| av福利超碰网站 | 国产亚洲亚洲 | 97超视频免费观看 | 在线观av| 久久久久久久久毛片 | 欧美精品视 | 丁香午夜 | 婷婷免费在线视频 | 成年人免费在线播放 | 99久久99热这里只有精品 | 久久ww | 99久久9 | 97理论电影| 国产高清在线免费 | 欧美不卡视频在线 | 成人精品影视 | 国产亚洲在线观看 | 国内久久精品视频 | 激情狠狠干| 少妇精品久久久一区二区免费 | 91精品久久久久久综合乱菊 | 国产精品成人免费精品自在线观看 | 91麻豆国产| 日本中文在线观看 | 欧洲精品码一区二区三区免费看 | 久草精品在线播放 | 91精品久久久久久久久久入口 | 91福利小视频 | 欧美日韩裸体免费视频 | 色九九影院| 黄色精品一区二区 | 97视频人人澡人人爽 | 91视频免费看网站 | 国产群p视频 | 视频在线一区 | 国产成人av免费在线观看 | 国产成在线观看免费视频 | 天天射天天 | 婷婷丁香花五月天 | 天天插夜夜操 | 免费中午字幕无吗 | 大型av综合网站 | 99视频在线免费看 | 91精品婷婷国产综合久久蝌蚪 | 在线免费视频 你懂得 | 麻豆视频免费入口 | 永久黄网站色视频免费观看w | 午夜狠狠操 | 日韩欧美在线观看一区二区三区 | 亚洲精品国产精品99久久 | 综合激情久久 | 亚洲一级片免费观看 | 91成人免费在线视频 | 国产只有精品 | 久久精品国产免费 | 亚洲 欧美 综合 在线 精品 | 婷婷丁香自拍 | 99精品国产一区二区三区不卡 | 国产最新视频在线观看 | 黄色看片 | 亚洲成av| 久久精品永久免费 | 免费三及片| 国语自产偷拍精品视频偷 | 黄色小视频在线观看免费 | a级免费观看 | 国产美女被啪进深处喷白浆视频 | 日韩有码在线观看视频 | 天天操天天射天天舔 | 亚洲va韩国va欧美va精四季 | 啪啪小视频网站 | 日韩中文字幕国产精品 | 在线观看亚洲免费视频 | 天天综合中文 | 国产黑丝一区二区三区 | 中文字幕在线看视频国产中文版 | 在线视频福利 | 日韩成人免费在线 | 日韩免费视频一区二区 | 欧美美女激情18p | 久久久久久高潮国产精品视 | 欧美激情第八页 | 伊甸园av在线 | 免费视频区 | 亚洲精品97 | 91禁在线观看 | 亚欧日韩av | 99色在线视频 | 香蕉视频网址 | zzijzzij日本成熟少妇 | 久久国产精品99久久久久久丝袜 | 成人av在线电影 | 午夜免费在线观看 | 在线精品在线 | 91成年人视频 | 久久最新| 亚洲一级黄色片 | 91久久丝袜国产露脸动漫 | 草久久精品 | 欧美性色综合网站 | 中文字幕丝袜美腿 | 国产福利免费看 | 五月激情五月激情 | 日韩中文在线视频 | 国产一区二区高清不卡 | 欧美午夜精品久久久久久孕妇 | 天天色播| 99久久国产免费看 | 激情深爱五月 | 99视频精品免费视频 | 国产精品九色 | 美女视频黄是免费的 | 日韩中文字幕视频在线 | 色噜噜狠狠狠狠色综合 | 久久99精品国产麻豆宅宅 | 中文字幕在线观看1 | 日韩高清一 | 亚洲国产精品500在线观看 | 国产在线观看你懂得 | 黄色精品免费 | 黄网站大全 | 天天操夜夜操国产精品 | 国产婷婷在线观看 | 国产精品一区二区av麻豆 | 四虎最新入口 | 伊人射 | 久久夜色精品国产欧美一区麻豆 | av中文字幕网站 | 天天干天天干天天 | 99久精品视频 | 青草草在线 | 成人动漫一区二区三区 | 精品国产一区二区三区久久影院 | 视频成人| 国产精品久久久久久久久久久久午夜片 | 激情五月综合网 | 深爱激情久久 | 国产流白浆高潮在线观看 | 久久精品123| 久久久999 | 丁香婷婷在线观看 | 天天干天天射天天爽 | 日本久久久久久科技有限公司 | 婷婷久月| 亚洲精品久久在线 | 天天色天天操综合 | 国产精品99久久久久久久久久久久 | 日韩成人xxxx | 天天综合网久久综合网 | 美女视频黄免费网站 | 久草视频中文在线 | 黄色.com| 色网站在线免费 | 999久久国精品免费观看网站 | 玖玖玖影院 | 日韩在线观看第一页 | 天天爱天天射天天干天天 | 欧美一级电影 | 午夜美女视频 | 国产精品第一页在线 | 成人午夜电影久久影院 | 久久久久久久久久久网 | japanesexxxhd奶水 91在线精品一区二区 | 激情五月色播五月 | 免费观看久久久 | 国内少妇自拍视频一区 | 99久久久精品 | 久久久官网 | 999久久久免费精品国产 | 992tv成人免费看片 | 日本在线成人 | 成人久久毛片 | 91在线视频观看 | 亚洲激情网站免费观看 | 国产v亚洲v| 国产资源在线免费观看 | 日韩理论片在线观看 | 国产一区二区三区午夜 | 草久久影院 | 免费av网站在线看 | 国产精品涩涩屋www在线观看 | 亚洲激情久久 | 久久99精品久久只有精品 | 91成人精品一区在线播放 | 成人全视频免费观看在线看 | 97视频人人澡人人爽 | 成人黄色片免费 | 久久免费黄色大片 | 人人看人人做人人澡 | 欧美 亚洲 另类 激情 另类 | 黄色av电影免费观看 | 免费看一及片 | 色综合久久久久综合体 | 久草在线视频新 | 国产美女无遮挡永久免费 | 99久久精品日本一区二区免费 | 欧美激情va永久在线播放 | 亚洲精选在线 | 日韩两性视频 | 在线观看91久久久久久 | 日韩二区三区在线观看 | 婷婷资源站 | 久久久久久久久久久影院 | 日韩在线不卡视频 | 91夜夜夜 | 欧美日韩精品网站 | av大全在线看| 久草在线资源网 | 免费网站看av片 | 夜色资源网 | 亚洲国产中文字幕在线 | 亚洲精品日韩一区二区电影 | 日韩激情视频在线 | 免费看的黄色录像 | 午夜黄网 | 成人a视频在线观看 | 五月天激情综合 | 特级西西444www大精品视频免费看 | 99久久精品久久久久久清纯 | 国产一级不卡视频 | 天天av天天| 欧美视频一区二 | 亚州欧美视频 | 91中文字幕在线观看 | 在线看成人 | 三级黄色免费 | 91香蕉国产在线观看软件 | 国产精品免费观看久久 | 久久综合色8888| 日韩色区 | 亚洲小视频在线观看 | 精品女同一区二区三区在线观看 | 欧美一级专区免费大片 | 91大神在线观看视频 | 波多野结衣视频一区二区三区 | 中文一区在线观看 | 免费国产一区二区视频 | 99久久www | 午夜体验区 | 成人国产网站 | 在线a人片免费观看视频 | 久草在线视频在线观看 | 少妇bbw搡bbbb搡bbb | 国产做爰视频 | 亚洲妇女av | 国产精品久久电影观看 | 国产精品一区二区三区视频免费 | 亚洲 欧美变态 另类 综合 | 成人丁香花| 日韩高清在线一区二区 | 国产123av| 草免费视频| aaa毛片视频| 91成人短视频在线观看 | 在线观看色网 | 成人精品久久 | 999久久久久| 天天搞天天 | 久久五月激情 | 午夜精品视频一区 | 四虎国产精品成人免费影视 | 狠狠网| www夜夜| 免费看黄色小说的网站 | 国产精品久久久久毛片大屁完整版 | 超碰人人超 | 欧美老少交| 日韩大片在线免费观看 | 欧美经典久久 | 久久不色 | 日韩理论片在线 | 开心综合网| 国产手机在线播放 | 99一级片| 国产精品99精品久久免费 | 婷婷中文字幕综合 | 久久精品国亚洲 | 成人在线观看日韩 | 色五月色开心色婷婷色丁香 | 欧美一区成人 | 97在线公开视频 | 成人在线免费视频观看 | 欧美精品久久久久a | 最近中文字幕 | 日韩高清免费在线 | 欧美日韩二区在线 | 久久精品久久久久久久 | 五月婷婷综合激情 | av网址aaa | 日韩中文字幕视频在线观看 | 在线视频观看国产 | 最近日本韩国中文字幕 | 欧美一级久久久 | 日本狠狠色 | 日本精品va在线观看 | 91激情在线视频 | 国产精品伦一区二区三区视频 | 国产精品久久久久久久午夜 | 日韩视频一区二区三区在线播放免费观看 | 综合天天色 | 免费电影一区二区三区 | 亚洲国产精品久久久久久 | 五月婷婷一级片 | 手机在线观看国产精品 | 中文字幕在线视频一区 | 韩日三级在线 | 亚洲成人av影片 | 免费看的黄网站软件 | 欧美性色网站 | 欧美精品一区二区性色 | 午夜av一区| 欧美色综合天天久久综合精品 | 久久久久欠精品国产毛片国产毛生 | 五月天激情开心 | 精品国产一区二区三区蜜臀 | 超碰人人在线 | 中文字幕视频网 | 在线观看免费色 | av在线激情 | 亚洲午夜精品一区 | 国内精品久久久久国产 | 国产精品欧美久久久久三级 | 成人蜜桃视频 | 国产精品综合在线观看 | 91九色国产 | 91香蕉视频污在线 | 热九九精品 | 精品夜夜嗨av一区二区三区 | 国产成人久久 | 亚州国产视频 | 亚洲综合色播 | 91爱爱网址 | 国产精品久久久久久久久久免费 | 国产精品久久人 | 一级黄色片在线播放 | 久久精品国产亚洲精品 | .国产精品成人自产拍在线观看6 | 91精品国产一区二区在线观看 | 国产一区二区三区网站 | 日本精品视频一区 | 91亚洲精品在线 | 久久久国产精品人人片99精片欧美一 | 国产麻豆成人传媒免费观看 | 亚洲精品国产第一综合99久久 | 狠狠色狠狠色终合网 | 国产一级二级在线观看 | 在线观看视频一区二区三区 | av观看网站| 久草在线一免费新视频 | 天天射天天射天天 | 五月天堂网 | 国产黄色观看 | 草久久久久 | 特黄特色特刺激视频免费播放 | 久草视频一区 | 九九电影在线 | 五月天婷婷在线观看视频 | 91av在线看 | 久久免费视频在线观看6 | 国产成人精品国内自产拍免费看 | 欧洲精品视频一区 | 日本精品一区二区在线观看 | 91高清不卡| 久草视频在线资源站 | 婷婷综合在线 | 欧美激情视频久久 | 看片一区二区三区 | 香蕉在线播放 | 日韩网页 | 色网站在线 | 欧美日韩国产色综合一二三四 | 日日爱av | 69av在线播放 | 日本不卡123 | 欧美激情精品久久久 | 日韩久久一区 | 成人黄色小说视频 | 国产精品久久久久永久免费 | 在线观看视频中文字幕 | 久久成人国产精品一区二区 | 中文字幕色网站 | 97香蕉久久超级碰碰高清版 | 日韩中文在线字幕 | 国产精品99久久久久久大便 | 国产一区二区三区在线 | 国产精品久久在线 | 欧美精品久久久久久久亚洲调教 | 日本黄色黄网站 | 狠狠ri| 亚洲精品成人在线 | 丁香av在线| 国产视频精品久久 | 日本在线中文在线 | 久精品视频免费观看2 | 在线导航av | 欧美日韩首页 | 偷拍精偷拍精品欧洲亚洲网站 | 中文字幕视频三区 | 国产精品6999成人免费视频 | 91久久精品一区二区二区 | 国产精品九色 | 久久99精品一区二区三区三区 | 岛国av在线不卡 | 91精品一区二区三区蜜桃 | 亚洲久久视频 | 国产精品精品国产 | 日韩在线观看网址 | www.夜夜| 成年人在线免费看片 | 久久视频在线观看免费 | 瑞典xxxx性hd极品 | 91精品国自产在线偷拍蜜桃 | www.av小说| 欧美亚洲国产一卡 | 狠狠狠操 | 伊人伊成久久人综合网站 | 久久久久国产精品午夜一区 | 国产精品久久婷婷六月丁香 | 久久少妇免费视频 | 涩涩在线| 国产免费大片 | 欧美日韩久久一区 | 亚洲少妇自拍 | 精品日韩在线一区 | 亚洲国产美女久久久久 | 久久国产成人午夜av影院潦草 | 黄色电影网站在线观看 | 91夜夜夜| 成人蜜桃网 | 国产精品久久久久av | 亚洲精品综合一二三区在线观看 | 国产精品第十页 | 手机成人免费视频 | 亚洲黄色三级 | 在线观看国产区 | 免费精品视频在线观看 | 91喷水 | 日韩一区视频在线 | 日韩av三区 | 国产99久久九九精品 | 在线观看中文字幕一区二区 | 五月天亚洲精品 | 国产高清不卡一区二区三区 | 国内三级在线观看 | 国产成人三级 | 99视频精品免费观看, | 麻豆视频国产精品 | 24小时日本在线www免费的 | 天天干夜夜夜操天 | 欧产日产国产69 | 久久久 精品 | 又色又爽又黄高潮的免费视频 | 中文字幕国产一区二区 | 国产在线最新 | 欧美激情亚洲综合 | 中文字幕资源在线 | 日韩在线视频看看 | 狠狠亚洲 | 黄色三级免费看 | 欧美日韩三级在线观看 | 九九久久电影 | 久草线| 国产精品一区免费观看 | 国产在线黄色 | 日韩久久久 | 日韩亚洲国产精品 | 亚洲一区二区三区四区精品 | 首页av在线 | 成人久久国产 | 日本在线观看中文字幕无线观看 | 91免费网站在线观看 | 热99在线 | av超碰在线观看 | 狠狠干美女 | 久久少妇免费视频 | 丁香一区二区 | 粉嫩av一区二区三区入口 | 日韩中文字幕免费视频 | 国产精品区一区 | 国产在线观看一区 | 国产资源中文字幕 | 久草在线精品观看 | 伊人伊成久久人综合网站 | 日韩成人免费在线观看 | 久久久精品免费看 | 9999精品 | 91人人澡人人爽人人精品 | 日本激情视频中文字幕 | 五月婷婷丁香激情 | 中文字幕在线观看亚洲 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 久久99国产一区二区三区 | 亚洲国产午夜视频 | 国产裸体无遮挡 | 亚洲欧洲一区二区在线观看 | 亚洲激情综合 | 亚州激情视频 | 69视频网站 | 天堂av网在线 | 日本二区三区在线 | 日韩理论电影网 | 韩日在线一区 | 中文字幕在线观看播放 | 亚洲三级网 | 欧美日韩免费视频 | 韩日电影在线观看 | 一区二区在线不卡 | 999视频在线观看 | 亚洲码国产日韩欧美高潮在线播放 | 久久人人爽视频 | 欧美日韩国产高清视频 | 在线国产精品一区 | 中文字幕在线视频一区 | 色婷婷色 | 国产精品理论视频 | 叶爱av在线 | 免费观看视频黄 | 亚洲视频精选 |