久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

那些年,面试中常见的数据结构基础和算法题(下)

發布時間:2023/12/15 综合教程 21 生活家
生活随笔 收集整理的這篇文章主要介紹了 那些年,面试中常见的数据结构基础和算法题(下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

這是 數據結構和算法面試題系列的下半部分,這部分主要是算法類 包括二分查找、排序算法、遞歸算法、隨機算法、背包問題、數字問題等算法相關內容。本系列完整代碼在 github 建了個倉庫,所有代碼都重新整理和做了一些基本的測試,代碼倉庫地址在這里: shishujuan/dsalg: 數據結構與算法系列匯總,如有錯誤,請在文章下面評論指出或者在 github 給我留言,我好及時改正以免誤導其他朋友。

文章末尾有系列目錄,可以按需取閱,如果需要測試,亦可以將倉庫代碼 clone 下來進行各種測試。如有錯誤或者引用不全、有侵權的地方,請大家給我指出,我好及時調整改正。如果本系列有幫助到你,也歡迎點贊或者在 github 上 star✨✨,十分感謝。

數據結構和算法面試題系列—二分查找算法詳解

0.概述

二分查找本身是個簡單的算法,但是正是因為其簡單,更容易寫錯。甚至于在二分查找算法剛出現的時候,也是存在 bug 的(溢出的 bug),這個 bug 直到幾十年后才修復(見《編程珠璣》)。本文打算對二分查找算法進行總結,并對由二分查找引申出來的問題進行分析和匯總。若有錯誤,請指正。本文完整代碼在 這里 。

1.二分查找基礎

相信大家都知道二分查找的基本算法,如下所示,這就是二分查找算法代碼:

/**
 * 基本二分查找算法
 */
int binarySearch(int a[], int n, int t)
{
    int l = 0, u = n - 1;
    while (l <= u) {
        int m = l + (u - l) / 2; // 同(l+u)/ 2,這里是為了溢出
        if (t > a[m])
            l = m + 1;
        else if (t < a[m])
            u = m - 1;
        else
            return m;
    }
    return -(l+1);
}
復制代碼

算法的思想就是:從數組中間開始,每次排除一半的數據,時間復雜度為 O(lgN)。這依賴于數組有序這個性質。如果 t 存在數組中,則返回t在數組的位置;否則,不存在則返回 -(l+1)

這里需要解釋下為什么 t 不存在數組中時不是返回 -1 而要返回 -(l+1)。首先我們可以觀察 l 的值,如果查找不成功,則 l 的值恰好是 t 應該在數組中插入的位置。

舉個例子,假定有序數組 a={1, 3, 4, 7, 8}, 那么如果t = 0,則顯然t不在數組中,則二分查找算法最終會使得l = 0 > u=-1退出循環;如果 t = 9,則 t 也不在數組中,則最后 l = 5 > u = 4 退出循環。如果 t=5,則最后l=3 > u=2退出循環。因此在一些算法中,比如DHT(一致性哈希)中,就需要這個返回值來使得新加入的節點可以插入到合適的位置中,在求最長遞增子序列的 NlgN 算法中,也用到了這一點,參見博文最長遞增子序列算法。

還有一個小點就是之所以返回 -(l+1) 而不是直接返回 -l 是因為 l 可能為 0,如果直接返回 -l 就無法判斷是正常返回位置 0 還是查找不成功返回的 0。

2.查找有序數組中數字第一次出現位置

現在考慮一個稍微復雜點的問題,如果有序數組中有重復數字,比如數組 a={1, 2, 3, 3, 5, 7, 8},需要在其中找出 3 第一次出現的位置。這里3第一次出現位置為 2。這個問題在《編程珠璣》第九章有很好的分析,這里就直接用了。算法的精髓在于循環不變式的巧妙設計,代碼如下:

/**
 * 二分查找第一次出現位置
 */
int binarySearchFirst(int a[], int n, int t)
{
    int l = -1, u = n;
    while (l + 1 != u) {
        /*循環不變式a[l]<t<=a[u] && l<u*/
        int m = l + (u - l) / 2; //同(l+u)/ 2
        if (t > a[m])
            l = m;
        else
            u = m;
    }
    /*assert: l+1=u && a[l]<t<=a[u]*/
    int p = u;
    if (p>=n || a[p]!=t)
        p = -1;
    return p;
}
復制代碼

算法分析:設定兩個不存在的元素 a[-1]和 a[n],使得 a[-1] < t <= a[n],但是我們并不會去訪問者兩個元素,因為(l+u)/2 > l=-1, (l+u)/2 < u=n。循環不變式為l<u && t>a[l] && t<=a[u] 。循環退出時必然有 l+1=u, 而且 a[l] < t <= a[u]。循環退出后u的值為t可能出現的位置,其范圍為[0, n],如果 t 在數組中,則第一個出現的位置 p=u,如果不在,則設置 p=-1返回。該算法的效率雖然解決了更為復雜的問題,但是其效率比初始版本的二分查找還要高,因為它在每次循環中只需要比較一次,前一程序則通常需要比較兩次。

舉個例子:對于數組 a={1, 2, 3, 3, 5, 7, 8},我們如果查找 t=3,則可以得到 p=u=2,如果查找 t=4,a[3]<t<=a[4], 所以p=u=4,判斷 a[4] != t,所以設置p=-1。 一種例外情況是 u>=n, 比如t=9,則 u=7,此時也是設置 p=-1.特別注意的是,l=-1,u=n 這兩個值不能寫成l=0,u=n-1。雖然這兩個值不會訪問到,但是如果改成后面的那樣,就會導致二分查找失敗,那樣就訪問不到第一個數字。如在 a={1,2,3,4,5}中查找 1,如果初始設置 l=0,u=n-1,則會導致查找失敗。

擴展
如果要查找數字在數組中最后出現的位置呢?其實這跟上述算法是類似的,稍微改一下上面的算法就可以了,代碼如下:

/**
 * 二分查找最后一次出現位置
 */
int binarySearchLast(int a[], int n, int t)
{
    int l = -1, u = n;
    while (l + 1 != u) {
        /*循環不變式, a[l] <= t < a[u]*/
        int m = l + (u - l) / 2;
        if (t >= a[m])
            l = m;
        else
            u = m;
    }
    /*assert: l+1 = u && a[l] <= t < a[u]*/
    int p = l;
    if (p<=-1 || a[p]!=t)
        p = -1;
    return p;
}
復制代碼

當然還有一種方法可以將查詢數字第一次出現和最后一次出現的代碼寫在一個程序中,只需要對原始的二分查找稍微修改即可,代碼如下:

/**
 * 二分查找第一次和最后一次出現位置
 */
int binarySearchFirstAndLast(int a[], int n, int t, int firstFlag)
{
    int l = 0;
    int u = n - 1;
    while(l <= u) {
        int m = l + (u - l) / 2;
        if(a[m] == t) { //找到了,判斷是第一次出現還是最后一次出現
            if(firstFlag) { //查詢第一次出現的位置
                if(m != 0 && a[m-1] != t)
                    return m;
                else if(m == 0)
                    return 0;
                else
                    u = m - 1;
            } else {   //查詢最后一次出現的位置
                if(m != n-1 && a[m+1] != t)
                    return m;
                else if(m == n-1)
                    return n-1;
                else
                    l = m + 1;
            }
        }
        else if(a[m] < t)
            l = m + 1;
        else
            u = m - 1;
    }
<span class="hljs-built_in">return</span> -1;

}
復制代碼

3.旋轉數組元素查找問題

題目

把一個有序數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。例如數組{3, 4, 5, 1, 2}為{1, 2, 3, 4, 5}的一個旋轉。現在給出旋轉后的數組和一個數,旋轉了多少位不知道,要求給出一個算法,算出給出的數在該數組中的下標,如果沒有找到這個數,則返回 -1。要求查找次數不能超過 n。

分析

由題目可以知道,旋轉后的數組雖然整體無序了,但是其前后兩部分是部分有序的。由此還是可以使用二分查找來解決該問題的。

解1:兩次二分查找

首先確定數組分割點,也就是說分割點兩邊的數組都有序。比如例子中的數組以位置2分割,前面部分{3,4,5}有序,后半部分{1,2}有序。然后對這兩部分分別使用二分查找即可。代碼如下:

/**
 * 旋轉數組查找-兩次二分查找
 */
int binarySearchRotateTwice(int a[], int n, int t)
{
    int p = findRotatePosition(a, n); //找到旋轉位置
    if (p == -1)
        return binarySearchFirst(a, n, t); //如果原數組有序,則直接二分查找即可
int left = binarySearchFirst(a, p+1, t); //查找左半部分
<span class="hljs-keyword">if</span> (left != -1)
    <span class="hljs-built_in">return</span> left; //左半部分找到,則直接返回

int right = binarySearchFirst(a+p+1, n-p-1, t); //左半部分沒有找到,則查找右半部分
<span class="hljs-keyword">if</span> (right == -1)
    <span class="hljs-built_in">return</span> -1;

<span class="hljs-built_in">return</span> right+p+1;  //返回位置,注意要加上p+1

}

/**

查找旋轉位置
*/
int findRotatePosition(int a[], int n)
{
int i;
for (i = 0; i < n-1; i++) {
if (a[i+1] < a[i])
return i;
}
return -1;
}
復制代碼

解2:一次二分查找

二分查找算法有兩個關鍵點:1)數組有序;2)根據當前區間的中間元素與t的大小關系,確定下次二分查找在前半段區間還是后半段區間進行。

仔細分析該問題,可以發現,每次根據 lu 求出 m 后,m 左邊([l, m])和右邊([m, u])至少一個是有序的。a[m]分別與a[l]和a[u]比較,確定哪一段是有序的。

如果左邊是有序的,若 t<a[m] && t>a[l], 則 u=m-1;其他情況,l =m+1
如果右邊是有序的,若 t> a[m] && t<a[u]l=m+1;其他情況,u =m-1
代碼如下:

/**
 * 旋轉數組二分查找-一次二分查找
 */
int binarySearchRotateOnce(int a[], int n, int t)
{
    int l = 0, u = n-1;
    while (l <= u) {
        int m = l + (u-l) / 2;
        if (t == a[m])
            return m;
        if (a[m] >= a[l]) { //數組左半有序
            if (t >= a[l] && t < a[m])
                u = m - 1;
            else
                l = m + 1;
        } else {       //數組右半段有序
            if (t > a[m] && t <= a[u])
                l = m + 1;
            else
                u = m - 1;
        }   
    }   
    return -1; 
}
復制代碼

數據結構和算法面試題系列—排序算法之基礎排序

0.概述

排序算法也是面試中常常提及的內容,問的最多的應該是快速排序、堆排序。這些排序算法很基礎,但是如果平時不怎么寫代碼的話,面試的時候總會出現各種 bug。雖然思想都知道,但是就是寫不出來。本文打算對各種排序算法進行一個匯總,包括插入排序、冒泡排序、選擇排序、計數排序、歸并排序,基數排序、桶排序、快速排序等。快速排序比較重要,會單獨寫一篇,而堆排序見本系列的二叉堆那篇文章即可。

需要提到的一點就是:插入排序,冒泡排序,歸并排序,計數排序都是穩定的排序,而其他排序則是不穩定的。本文完整代碼在 這里。

1.插入排序

插入排序是很基本的排序,特別是在數據基本有序的情況下,插入排序的性能很高,最好情況可以達到O(N),其最壞情況和平均情況時間復雜度都是 O(N^2)。代碼如下:

/**
 * 插入排序
 */
void insertSort(int a[], int n)
{
    int i, j;
    for (i = 1; i < n; i++) {
        /*
         * 循環不變式:a[0...i-1]有序。每次迭代開始前,a[0...i-1]有序,
         * 循環結束后i=n,a[0...n-1]有序
         * */
        int key = a[i];
        for (j = i; j > 0 && a[j-1] > key; j--) {
            a[j] = a[j-1];
        }
        a[j] = key;
    }
}
復制代碼

2.希爾排序

希爾排序內部調用插入排序來實現,通過對 N/2,N/4...1階分別排序,最后得到整體的有序。

/**
 * 希爾排序
 */
void shellSort(int a[], int n)
{
    int gap;
    for (gap = n/2; gap > 0; gap /= 2) {
        int i;
        for (i = gap; i < n; i++) {
            int key = a[i], j;
            for (j = i; j >= gap && key < a[j-gap]; j -= gap) {
                a[j] = a[j-gap];
            }
            a[j] = key;
        }
    }
}
復制代碼

3.選擇排序

選擇排序的思想就是第 i 次選取第 i 小的元素放在位置 i。比如第 1 次就選擇最小的元素放在位置 0,第 2 次選擇第二小的元素放在位置 1。選擇排序最好和最壞時間復雜度都為 O(N^2)。代碼如下:

/**
 * 選擇排序
 */
void selectSort(int a[], int n)
{
    int i, j, min, tmp;
    for (i = 0; i < n-1; i++) {
        min = i;
        for (j = i+1; j < n; j++) {
            if (a[j] < a[min])
                min = j;
        }
        if (min != i)
            tmp = a[i], a[i] = a[min], a[min] = tmp; //交換a[i]和a[min]
    }
}
復制代碼

循環不變式:在外層循環執行前,a[0...i-1]包含 a 中最小的 i 個數,且有序。

初始時,i=0a[0...-1] 為空,顯然成立。

每次執行完成后,a[0...i] 包含 a 中最小的 i+1 個數,且有序。即第一次執行完成后,a[0...0] 包含 a 最小的 1 個數,且有序。

循環結束后,i=n-1,則 a[0...n-2]包含 a 最小的 n-1 個數,且已經有序。所以整個數組有序。

4.冒泡排序

冒泡排序時間復雜度跟選擇排序相同。其思想就是進行 n-1 趟排序,每次都是把最小的數上浮,像魚冒泡一樣。最壞情況為 O(N^2)。代碼如下:

/**
 * 冒泡排序-經典版
 */
void bubbleSort(int a[], int n)
{
    int i, j, tmp;
    for (i = 0; i < n; i++) {
        for (j = n-1; j >= i+1; j--) {
            if (a[j] < a[j-1])
                tmp = a[j], a[j] = a[j-1], a[j-1] = tmp;
        }
    }
}
復制代碼

循環不變式:在循環開始迭代前,子數組 a[0...i-1] 包含了數組 a[0..n-1]i-1 個最小值,且是排好序的。

對冒泡排序的一個改進就是在每趟排序時判斷是否發生交換,如果一次交換都沒有發生,則數組已經有序,可以不用繼續剩下的趟數直接退出。改進后代碼如下:

/**
 * 冒泡排序-優化版
 */
void betterBubbleSort(int a[], int n)
{
    int tmp, i, j;
    for (i = 0; i < n; i++) {
        int sorted = 1;
        for (j = n-1; j >= i+1; j--) {
            if (a[j] < a[j-1]) {
                tmp = a[j], a[j] = a[j-1], a[j-1] = tmp;
                sorted = 0;
            }   
        }   
        if (sorted)
            return ;
    }   
}
復制代碼

5.計數排序

假定數組為 a[0...n-1] ,數組中存在重復數字,數組中最大數字為k,建立兩個輔助數組 b[]c[]b[] 用于存儲排序后的結果,c[] 用于存儲臨時值。時間復雜度為 O(N),適用于數字范圍較小的數組。

計數排序原理如上圖所示,代碼如下:

/**
 * 計數排序
 */
void countingSort(int a[], int n) 
{
    int i, j;
    int *b = (int *)malloc(sizeof(int) * n);
    int k = maxOfIntArray(a, n); // 求數組最大元素
    int *c = (int *)malloc(sizeof(int) * (k+1));  //輔助數組
<span class="hljs-keyword">for</span> (i = 0; i &lt;= k; i++)
    c[i] = 0;

<span class="hljs-keyword">for</span> (j = 0; j &lt; n; j++)
    c[a[j]] = c[a[j]] + 1; //c[i]包含等于i的元素個數

<span class="hljs-keyword">for</span> (i = 1; i &lt;= k; i++)
    c[i] = c[i] + c[i-1];  //c[i]包含小于等于i的元素個數

<span class="hljs-keyword">for</span> (j = n-1; j &gt;= 0; j--) {  // 賦值語句
    b[c[a[j]]-1] = a[j]; //結果存在b[0...n-1]中
    c[a[j]] = c[a[j]] - 1;
}

/*方便測試代碼,這一步賦值不是必須的*/
<span class="hljs-keyword">for</span> (i = 0; i &lt; n; i++) {
    a[i] = b[i];
}

free(b);
free(c);

}
復制代碼

擴展: 如果代碼中的給數組 b[] 賦值語句 for (j=n-1; j>=0; j--) 改為 for(j=0; j<=n-1; j++),該代碼仍然正確,只是排序不再穩定。

6.歸并排序

歸并排序通過分治算法,先排序好兩個子數組,然后將兩個子數組歸并。時間復雜度為 O(NlgN)。代碼如下:

/*
 * 歸并排序-遞歸
 * */
void mergeSort(int a[], int l, int u) 
{
    if (l < u) {
        int m = l + (u-l)/2;
        mergeSort(a, l, m);
        mergeSort(a, m + 1, u);
        merge(a, l, m, u);
    }
}

/**

歸并排序合并函數
*/
void merge(int a[], int l, int m, int u)
{
int n1 = m - l + 1;
int n2 = u - m;

int left[n1], right[n2];
int i, j;
for (i = 0; i < n1; i++) /* left holds a[l..m] */
left[i] = a[l + i];

for (j = 0; j < n2; j++) /* right holds a[m+1..u] */
right[j] = a[m + 1 + j];

i = j = 0;
int k = l;
while (i < n1 && j < n2) {
if (left[i] < right[j])
a[k++] = left[i++];
else
a[k++] = right[j++];
}
while (i < n1) /* left[] is not exhausted /
a[k++] = left[i++];
while (j < n2) /
right[] is not exhausted */
a[k++] = right[j++];
}
復制代碼

擴展:歸并排序的非遞歸實現怎么做?

歸并排序的非遞歸實現其實是最自然的方式,先兩兩合并,而后再四四合并等,就是從底向上的一個過程。代碼如下:

/**
 * 歸并排序-非遞歸
 */
void mergeSortIter(int a[], int n)
{
    int i, s=2;
    while (s <= n) {
        i = 0;
        while (i+s <= n){
            merge(a, i, i+s/2-1, i+s-1);
            i += s;
        }
    //處理末尾殘余部分
    merge(a, i, i+s/2-1, n-1);
    s*=2;
}
//最后再從頭到尾處理一遍
merge(a, 0, s/2-1, n-1);

}
復制代碼

7.基數排序、桶排序

基數排序的思想是對數字每一位分別排序(注意這里必須是穩定排序,比如計數排序等,否則會導致結果錯誤),最后得到整體排序。假定對 N 個數字進行排序,如果數字有 d 位,每一位可能的最大值為 K,則每一位的穩定排序需要 O(N+K) 時間,總的需要 O(d(N+K)) 時間,當 d 為常數,K=O(N) 時,總的時間復雜度為O(N)。

而桶排序則是在輸入符合均勻分布時,可以以線性時間運行,桶排序的思想是把區間 [0,1) 劃分成 N 個相同大小的子區間,將 N 個輸入均勻分布到各個桶中,然后對各個桶的鏈表使用插入排序,最終依次列出所有桶的元素。

這兩種排序使用場景有限,代碼就略過了,更詳細可以參考《算法導論》的第8章。

數據結構和算法面試題系列—排序算法之快速排序

0.概述

快速排序也是基于分治模式,類似歸并排序那樣,不同的是快速排序劃分最后不需要merge。對一個數組 A[p..r] 進行快速排序分為三個步驟:

劃分: 數組 A[p...r] 被劃分為兩個子數組 A[p...q-1]A[q+1...r],使得 A[p...q-1] 中每個元素都小于等于 A[q],而 A[q+1...r] 每個元素都大于 A[q]。劃分流程見下圖。
解決: 通過遞歸調用快速排序,對子數組分別排序即可。
合并:因為兩個子數組都已經排好序了,且已經有大小關系了,不需要做任何操作。

快速排序算法不算復雜的算法,但是實際寫代碼的時候卻是最容易出錯的代碼,寫的不對就容易死循環或者劃分錯誤,本文代碼見 這里。

1.樸素的快速排序

這個樸素的快速排序有個缺陷就是在一些極端情況如所有元素都相等時(或者元素本身有序,如 a[] = {1,2,3,4,5}等),樸素的快速算法時間復雜度為 O(N^2),而如果能夠平衡劃分數組則時間復雜度為 O(NlgN)

/**
 * 快速排序-樸素版本
 */
void quickSort(int a[], int l, int u)
{
    if (l >= u) return;
int q = partition(a, l, u);
quickSort(a, l, q-1);
quickSort(a, q+1, u);

}

/**

快速排序-劃分函數
*/
int partition(int a[], int l, int u)
{
int i, q=l;
for (i = l+1; i <= u; i++) {
if (a[i] < a[l])
swapInt(a, i, ++q);
}
swapInt(a, l, q);
return q;
}
復制代碼

2.改進-雙向劃分的快速排序

一種改進方法就是采用雙向劃分,使用兩個變量 iji 從左往右掃描,移過小元素,遇到大元素停止;j 從右往左掃描,移過大元素,遇到小元素停止。然后測試i和j是否交叉,如果交叉則停止,否則交換 ij 對應的元素值。

注意,如果數組中有相同的元素,則遇到相同的元素時,我們停止掃描,并交換 ij 的元素值。雖然這樣交換次數增加了,但是卻將所有元素相同的最壞情況由 O(N^2) 變成了差不多 O(NlgN) 的情況。比如數組 A={2,2,2,2,2}, 則使用樸素快速排序方法,每次都是劃分 n 個元素為 1 個和 n-1 個,時間復雜度為 O(N^2),而使用雙向劃分后,第一次劃分的位置是 2,基本可以平衡劃分兩部分。代碼如下:

/**
 * 快速排序-雙向劃分函數
 */
int partitionLR(int a[], int l, int u, int pivot)
{
    int i = l;
    int j = u+1;
    while (1) {
        do {
            i++;
        } while (a[i] < pivot && i <= u); //注意i<=u這個判斷條件,不能越界。
    <span class="hljs-keyword">do</span> {
        j--;
    } <span class="hljs-keyword">while</span> (a[j] &gt; pivot);

    <span class="hljs-keyword">if</span> (i &gt; j) <span class="hljs-built_in">break</span>;

    swapInt(a, i, j);
}

// 注意這里是交換l和j,而不是l和i,因為i與j交叉后,a[i...u]都大于等于樞紐元t,
// 而樞紐元又在最左邊,所以不能與i交換。只能與j交換。
swapInt(a, l, j);

<span class="hljs-built_in">return</span> j;

}

/**

快速排序-雙向劃分法
*/
void quickSortLR(int a[], int l, int u)
{
if (l >= u) return;

int pivot = a[l];
int q = partitionLR(a, l, u, pivot);
quickSortLR(a, l, q-1);
quickSortLR(a, q+1, u);
}
復制代碼

雖然雙向劃分解決了所有元素相同的問題,但是對于一個已經排好序的數組還是會達到 O(N^2) 的復雜度。此外,雙向劃分還要注意的一點是代碼中循環的寫法,如果寫成 while(a[i]<t) {i++;} 等形式,則當左右劃分的兩個值都等于樞紐元時,會導致死循環。

3.繼續改進—隨機法和三數取中法取樞紐元

為了解決上述問題,可以進一步改進,通過隨機選取樞紐元或三數取中方式來獲取樞紐元,然后進行雙向劃分。三數取中指的就是從數組A[l... u]中選擇左中右三個值進行排序,并使用中值作為樞紐元。如數組 A[] = {1, 3, 5, 2, 4},則我們對 A[0]、A[2]、A[4] 進行排序,選擇中值 A[4](元素4) 作為樞紐元,并將其交換到 a[l] ,最后數組變成 A[] = {4 3 5 2 1},然后跟之前一樣雙向排序即可。

/**
 * 隨機選擇樞紐元
 */
int pivotRandom(int a[], int l, int u)
{
    int rand = randInt(l, u);
    swapInt(a, l, rand); // 交換樞紐元到位置l
    return a[l];
}

/**

三數取中選擇樞紐元
*/
int pivotMedian3(int a[], int l, int u)
{
int m = l + (u-l)/2;

/*

三數排序
*/
if( a[l] > a[m] )
swapInt(a, l, m);

if( a[l] > a[u] )
swapInt(a, l, u);

if( a[m] > a[u] )
swapInt(a, m, u);

/* assert: a[l] <= a[m] <= a[u] */
swapInt(a, m, l); // 交換樞紐元到位置l

return a[l];
}
復制代碼

此外,在數據基本有序的情況下,使用插入排序可以得到很好的性能,而且在排序很小的子數組時,插入排序比快速排序更快,可以在數組比較小時選用插入排序,而大數組才用快速排序。

4.非遞歸寫快速排序

非遞歸寫快速排序著實比較少見,不過練練手總是好的。需要用到棧,注意壓棧的順序。代碼如下:

/**
 * 快速排序-非遞歸版本
 */
void quickSortIter(int a[], int n)
{
    Stack *stack = stackNew(n);
    int l = 0, u = n-1;
    int p = partition(a, l, u);
<span class="hljs-keyword">if</span> (p-1 &gt; l) { //左半部分兩個邊界值入棧
    push(stack, p-1); 
    push(stack, l);
}

<span class="hljs-keyword">if</span> (p+1 &lt; u) { //右半部分兩個邊界值入棧
    push(stack, u);
    push(stack, p+1);
}

<span class="hljs-keyword">while</span> (!IS_EMPTY(stack)) { //棧不為空,則循環劃分過程
    l = pop(stack);
    u = pop(stack);
    p = partition(a, l, u);

    <span class="hljs-keyword">if</span> (p-1 &gt; l) {
        push(stack, p-1);
        push(stack, l);
    }

    <span class="hljs-keyword">if</span> (p+1 &lt; u) {
        push(stack, u);
        push(stack, p+1);
    }
}

}
復制代碼

數據結構和算法面試題系列—隨機算法總結

0.概述

隨機算法涉及大量概率論知識,有時候難得去仔細看推導過程,當然能夠完全了解推導的過程自然是有好處的,如果不了解推導過程,至少記住結論也是必要的。本文總結最常見的一些隨機算法的題目,是幾年前找工作的時候寫的。需要說明的是,這里用到的隨機函數 randInt(a, b) 假定它能隨機的產生范圍 [a,b] 內的整數,即產生每個整數的概率相等(雖然在實際中并不一定能實現,不過不要太在意,這個世界很多事情都很隨機)。本文代碼在 這里。

1.隨機排列數組

假設給定一個數組 A,它包含元素 1 到 N,我們的目標是構造這個數組的一個均勻隨機排列。

一個常用的方法是為數組每個元素 A[i] 賦一個隨機的優先級 P[i],然后依據優先級對數組進行排序。比如我們的數組為 A = {1, 2, 3, 4},如果選擇的優先級數組為 P = {36, 3, 97, 19},那么就可以得到數列 B={2, 4, 1, 3},因為 3 的優先級最高(為97),而 2 的優先級最低(為3)。這個算法需要產生優先級數組,還需使用優先級數組對原數組排序,這里就不詳細描述了,還有一種更好的方法可以得到隨機排列數組。

產生隨機排列數組的一個更好的方法是原地排列(in-place)給定數組,可以在 O(N) 的時間內完成。偽代碼如下:

RANDOMIZE-IN-PLACE ( A , n ) 
	for i ←1 to n do 
		swap A[i] ↔ A[RANDOM(i , n )]
復制代碼

如代碼中所示,第 i 次迭代時,元素 A[i] 是從元素 A[i...n]中隨機選取的,在第 i 次迭代后,我們就再也不會改變 A[i]

A[i] 位于任意位置j的概率為 1/n。這個是很容易推導的,比如 A[1] 位于位置 1 的概率為 1/n,這個顯然,因為 A[1] 不被1到n的元素替換的概率為 1/n,而后就不會再改變 A[1] 了。而 A[1] 位于位置 2 的概率也是 1/n,因為 A[1] 要想位于位置 2,則必須在第一次與 A[k] (k=2...n) 交換,同時第二次 A[2]A[k]替換,第一次與 A[k] 交換的概率為(n-1)/n,而第二次替換概率為 1/(n-1),所以總的概率是 (n-1)/n * 1/(n-1) = 1/n。同理可以推導其他情況。

當然這個條件只能是隨機排列數組的一個必要條件,也就是說,滿足元素 A[i] 位于位置 j 的概率為1/n 不一定就能說明這可以產生隨機排列數組。因為它可能產生的排列數目少于 n!,盡管概率相等,但是排列數目沒有達到要求,算法導論上面有一個這樣的反例。

算法 RANDOMIZE-IN-PLACE可以產生均勻隨機排列,它的證明過程如下:

首先給出k排列的概念,所謂 k 排列就是從n個元素中選取k個元素的排列,那么它一共有 n!/(n-k)! 個 k 排列。

循環不變式:for循環第i次迭代前,對于每個可能的i-1排列,子數組A[1...i-1]包含該i-1排列的概率為 (n-i+1)! / n!

初始化:在第一次迭代前,i=1,則循環不變式指的是對于每個0排列,子數組A[1...i-1]包含該0排列的概率為 (n-1+1)! / n! = 1。A[1...0]為空的數組,0排列則沒有任何元素,因此A包含所有可能的0排列的概率為1。不變式成立。

維持:假設在第i次迭代前,數組的i-1排列出現在 A[1...i-1] 的概率為 (n-i+1) !/ n!,那么在第i次迭代后,數組的所有i排列出現在 A[1...i] 的概率為 (n-i)! / n!。下面來推導這個結論:

考慮一個特殊的 i 排列 p = {x1, x2, ... xi},它由一個 i-1 排列 p' ={x1, x2,..., xi?1} 后面跟一個 xi 構成。設定兩個事件變量E1和E2:

E1為該算法將排列 p' 放置到 A[1...i-1]的事件,概率由歸納假設得知為 Pr(E1) =(n-i+1)! / n!

E2為在第 i 次迭代時將 xi 放入到 A[i] 的事件。
因此我們得到 i 排列出現在 A[1...i] 的概率為 Pr {E2 ∩ E1} = Pr {E2 | E1} Pr {E1}。而Pr {E2 | E1}= 1/(n ? i + 1),所以
Pr {E2 ∩ E1} = Pr {E2 | E1} Pr {E1}= 1 /(n ? i + 1) *(n ? i + 1)! /n!= (n ? i )! /n!

結束:結束的時候 i=n+1,因此可以得到 A[1...n] 是一個給定 n 排列的概率為 1/n!

C實現代碼如下:

void randomInPlace(int a[], int n)
{
    int i;
    for (i = 0; i < n; i++) {
        int rand = randInt(i, n-1);
        swapInt(a, i, rand);
    }
}
復制代碼

擴展

如果上面的隨機排列算法寫成下面這樣,是否也能產生均勻隨機排列?

PERMUTE-WITH-ALL( A , n ) 
	for i ←1 to n do 
		swap A[i] ↔A[RANDOM(1 , n )]
復制代碼

注意,該算法不能產生均勻隨機排列。假定 n=3,則該算法可以產生 3*3*3=27 個輸出,而 3 個元素只有3!=6個不同的排列,要使得這些排列出現概率等于 1/6,則必須使得每個排列出現次數 m 滿足 m/27=1/6,顯然,沒有這樣的整數符合條件。而實際上各個排列出現的概率如下,如 {1,2,3} 出現的概率為 4/27,不等于 1/6

排 列 概 率
<1, 2, 3> 4/27
<1, 3, 2> 5/27
<2, 1, 3> 5/27
<2, 3, 1> 5/27
<3, 1, 2> 4/27
<3, 2, 1> 4/27

2.隨機選取一個數字

題: 給定一個未知長度的整數流,如何隨機選取一個數?(所謂隨機就是保證每個數被選取的概率相等)

解1: 如果數據流不是很長,可以存在數組中,然后再從數組中隨機選取。當然題目說的是未知長度,所以如果長度很大不足以保存在內存中的話,這種解法有其局限性。

解2: 如果數據流很長的話,可以這樣:

如果數據流在第1個數字后結束,那么必選第1個數字。
如果數據流在第2個數字后結束,那么我們選第2個數字的概率為1/2,我們以1/2的概率用第2個數字替換前面選的隨機數,得到新的隨機數。
......
如果數據流在第n個數字后結束,那么我們選擇第n個數字的概率為1/n,即我們以1/n的概率用第n個數字替換前面選的隨機數,得到新的隨機數。

一個簡單的方法就是使用隨機函數 f(n)=bigrand()%n,其中 bigrand() 返回很大的隨機整數,當數據流到第 n 個數時,如果 f(n)==0,則替換前面的已經選的隨機數,這樣可以保證每個數字被選中的概率都是 1/n。如當 n=1 時,則 f(1)=0,則選擇第 1 個數,當 n=2 時,則第 2 個數被選中的概率都為 1/2,以此類推,當數字長度為 n 時,第 n 個數字被選中的概率為 1/n。代碼如下(注:在 Linux/MacOS 下,rand() 函數已經可以返回一個很大的隨機數了,就當做bigrand()用了):

void randomOne(int n)
{
    int i, select = 0;
    for (i = 1; i < n; i++) {
        int rd = rand() % n;
        if (rd == 0) {
            select = i;
        }
    }
    printf("%d
", select);
}
復制代碼

3.隨機選取M個數字

: 程序輸入包含兩個整數 m 和 n ,其中 m<n,輸出是 0~n-1 范圍內的 m 個隨機整數的有序列表,不允許重復。從概率角度來說,我們希望得到沒有重復的有序選擇,其中每個選擇出現的概率相等。

解1: 先考慮個簡單的例子,當 m=2,n=5 時,我們需要從 0~4 這 5 個整數中等概率的選取 2 個有序的整數,且不能重復。如果采用如下條件選取:bigrand() % 5 < 2,則我們選取 0 的概率為2/5。但是我們不能采取同樣的概率來選取 1,因為選取了 0 后,我們應該以 1/4 的概率來選取 1,而在沒有選取 0 的情況下,我們應該以 2/4 的概率選取 1。選取的偽代碼如下:

select = m
remaining = n
for i = [0, n)
    if (bigrand() % remaining < select)
         print i
         select--
    remaining--
復制代碼

只要滿足條件 m<=n,則程序輸出 m 個有序整數,不多不少。不會多選,因為每選擇一個數,select--,這樣當 select 減到 0 后就不會再選了。同時,也不會少選,因為每次都會remaining--,當 select/remaining=1 時,一定會選取一個數。每個子集被選擇的概率是相等的,比如這里5選2則共有 C(5,2)=10 個子集,如 {0,1},{0,2}...等,每個子集被選中的概率都是 1/10

更一般的推導,n選m的子集數目一共有 C(n,m) 個,考慮一個特定的 m 序列,如0...m-1,則選取它的概率為m/n * (m-1)/(n-1)*....1/(n-m+1)=1/C(n,m),可以看到概率是相等的。

Knuth 老爺爺很早就提出了這個算法,他的實現如下:

void randomMKnuth(int n, int m)
{
    int i;
    for (i = 0; i < n; i++) {
        if ((rand() % (n-i)) < m) {
            printf("%d ", i);
            m--;
        }
    }
}
復制代碼

解2: 還可以采用前面隨機排列數組的思想,先對前 m 個數字進行隨機排列,然后排序這 m 個數字并輸出即可。代碼如下:

void randomMArray(int n, int m)
{
    int i, j;
    int *x = (int *)malloc(sizeof(int) * n);
<span class="hljs-keyword">for</span> (i = 0; i &lt; n; i++)
    x[i] = i;

// 隨機數組
<span class="hljs-keyword">for</span> (i = 0; i &lt; m; i++) {
    j = randInt(i, n-1);
    swapInt(x, i, j);
}

// 對數組前 m 個元素排序
<span class="hljs-keyword">for</span> (i = 0; i &lt; m; i++) {
    <span class="hljs-keyword">for</span> (j = i+1; j&gt;0 &amp;&amp; x[j-1]&gt;x[j]; j--) {
        swapInt(x, j, j-1);
    }
}

<span class="hljs-keyword">for</span> (i = 0; i &lt; m; i++) {
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, x[i]);
}

<span class="hljs-built_in">printf</span>(<span class="hljs-string">"
"</span>);

}
復制代碼

4.rand7 生成 rand10 問題

題: 已知一個函數rand7()能夠生成1-7的隨機數,每個數概率相等,請給出一個函數rand10(),該函數能夠生成 1-10 的隨機數,每個數概率相等。

解1: 要產生 1-10 的隨機數,我們要么執行 rand7() 兩次,要么直接乘以一個數字來得到我們想要的范圍值。如下面公式(1)和(2)。

idx = 7 * (rand7()-1) + rand7() ---(1) 正確
idx = 8 * rand7() - 7           ---(2) 錯誤
復制代碼

上面公式 (1) 能夠產生 1-49 的隨機數,為什么呢?因為 rand7() 的可能的值為 1-7,兩個 rand7() 則可能產生 49 種組合,且正好是 1-49 這 49 個數,每個數出現的概率為 1/49,于是我們可以將大于 40 的丟棄,然后取 (idx-1) % 10 + 1 即可。公式(2)是錯誤的,因為它生成的數的概率不均等,而且也無法生成49個數字。

   1  2  3  4  5  6  7
1  1  2  3  4  5  6  7
2  8  9 10  1  2  3  4
3  5  6  7  8  9 10  1
4  2  3  4  5  6  7  8
5  9 10  1  2  3  4  5
6  6  7  8  9 10  *  *
7  *  *  *  *  *  *  *
復制代碼

該解法基于一種叫做拒絕采樣的方法。主要思想是只要產生一個目標范圍內的隨機數,則直接返回。如果產生的隨機數不在目標范圍內,則丟棄該值,重新取樣。由于目標范圍內的數字被選中的概率相等,這樣一個均勻的分布生成了。代碼如下:

int rand7ToRand10Sample() {
    int row, col, idx;
    do {
        row = rand7();
        col = rand7();
        idx = col + (row-1)*7;
    } while (idx > 40);
<span class="hljs-built_in">return</span> 1 + (idx-1) % 10;

}
復制代碼

由于row范圍為1-7,col范圍為1-7,這樣idx值范圍為1-49。大于40的值被丟棄,這樣剩下1-40范圍內的數字,通過取模返回。下面計算一下得到一個滿足1-40范圍的數需要進行取樣的次數的期望值:

E(# calls to rand7) = 2 * (40/49) +
                      4 * (9/49) * (40/49) +
                      6 * (9/49)2 * (40/49) +
                      ...

                      ∞
                    = ∑ 2k * (9/49)k-1 * (40/49)
                      k=1

                    = (80/49) / (1 - 9/49)2
                    = 2.45
復制代碼

解2: 上面的方法大概需要 2.45 次調用 rand7 函數才能得到 1 個 1-10 范圍的數,下面可以進行再度優化。對于大于 40 的數,我們不必馬上丟棄,可以對 41-49 的數減去 40 可得到 1-9 的隨機數,而rand7可生成 1-7 的隨機數,這樣可以生成 1-63 的隨機數。對于 1-60 我們可以直接返回,而 61-63 則丟棄,這樣需要丟棄的數只有 3 個,相比前面的 9 個,效率有所提高。而對于 61-63 的數,減去60后為 1-3,rand7 產生 1-7,這樣可以再度利用產生 1-21 的數,對于 1-20 我們則直接返回,對于 21 則丟棄。這時,丟棄的數就只有1個了,優化又進一步。當然這里面對rand7的調用次數也是增加了的。代碼如下,優化后的期望大概是 2.2123。

int rand7ToRand10UtilizeSample() {
    int a, b, idx;
    while (1) {
        a = randInt(1, 7);
        b = randInt(1, 7);
        idx = b + (a-1)*7;
        if (idx <= 40)
            return 1 + (idx-1)%10;

        a = idx-40;
        b = randInt(1, 7);
        // get uniform dist from 1 - 63
        idx = b + (a-1)*7;
        if (idx <= 60)
            return 1 + (idx-1)%10;

        a = idx-60;
        b = randInt(1, 7);
        // get uniform dist from 1-21
        idx = b + (a-1)*7;
        if (idx <= 20)
            return 1 + (idx-1)%10;
    }
}
復制代碼

5.趣味概率題

1)稱球問題

: 有12個小球,其中一個是壞球。給你一架天平,需要你用最少的稱次數來確定哪個小球是壞的,并且它到底是輕了還是重了。

: 之前有總結過二分查找算法,我們知道二分法可以加快有序數組的查找。相似的,比如在數字游戲中,如果要你猜一個介于 1-64 之間的數字,用二分法在6次內肯定能猜出來。但是稱球問題卻不同。稱球問題這里 12 個小球,壞球可能是其中任意一個,這就有 12 種可能性。而壞球可能是重了或者輕了這2種情況,于是這個問題一共有 12*2 = 24 種可能性。每次用天平稱,天平可以輸出的是 平衡、左重、右重 3 種可能性,即稱一次可以將問題可能性縮小到原來的 1/3,則一共 24 種可能性可以在 3 次內稱出來(3^3 = 27)。

為什么最直觀的稱法 6-6 不是最優的?在 6-6 稱的時候,天平平衡的可能性是0,而最優策略應該是讓天平每次稱量時的概率均等,這樣才能三等分答案的所有可能性。

具體怎么實施呢? 將球編號為1-12,采用 4, 4 稱的方法。

我們先將 1 2 3 45 6 7 8 進行第1次稱重。
如果第1次平衡,則壞球肯定在 9-12 號中。則此時只剩下 9-12 4個球,可能性為 9- 10- 11- 12- 9+ 10+ 11+ 12+ 這8種可能。接下來將 9 10 111 2 3稱第2次:如果平衡,則 12 號小球為壞球,將12號小球與1號小球稱第3次即可確認輕還是重。如果不平衡,則如果重了說明壞球重了,繼續將9和10號球稱量,重的為壞球,平衡的話則11為壞球。
如果第1次不平衡,則壞球肯定在 1-8號中。則還剩下的可能性是 1+ 2+ 3+ 4+ 5- 6- 7- 8- 或者 1- 2- 3- 4- 5+ 6+ 7+ 8+,如果是1 2 3 4 這邊重,則可以將 1 2 63 4 5 稱,如果平衡,則必然是 7 8 輕了,再稱一次7和1,便可以判斷7和8哪個是壞球了。如果不平衡,假定是 1 2 6 這邊重,則可以判斷出 1 2 重了或者 5 輕了,為什么呢?因為如果是3+ 4+ 6-,則 1 2 3 45 6 7 8 重,但是 1 2 6 應該比 3 4 5 輕。其他情況同理,最多3次即可找出壞球。

下面這個圖更加清晰說明了這個原理。

2)生男生女問題

題: 在重男輕女的國家里,男女的比例是多少?在一個重男輕女的國家里,每個家庭都想生男孩,如果他們生的孩子是女孩,就再生一個,直到生下的是男孩為止。這樣的國家,男女比例會是多少?

解: 還是1:1。在所有出生的第一個小孩中,男女比例是1:1;在所有出生的第二個小孩中,男女比例是1:1;....在所有出生的第n個小孩中,男女比例還是1:1。所以總的男女比例是1:1。

3)約會問題

題: 兩人相約5點到6點在某地會面,先到者等20分鐘后離去,求這兩人能夠會面的概率。

解: 設兩人分別在5點X分和5點Y分到達目的地,則他們能夠會面的條件是 |X-Y| <= 20,而整個范圍為 S={(x, y): 0 =< x <= 60, 0=< y <= 60},如果畫出坐標軸的話,會面的情況為坐標軸中表示的面積,概率為 (60^2 - 40^2) / 60^2 = 5/9

4)帽子問題

題: 有n位顧客,他們每個人給餐廳的服務生一頂帽子,服務生以隨機的順序歸還給顧客,請問拿到自己帽子的顧客的期望數是多少?

解: 使用指示隨機變量來求解這個問題會簡單些。定義一個隨機變量X等于能夠拿到自己帽子的顧客數目,我們要計算的是 E[X]。對于 i=1, 2 ... n,定義 Xi =I{顧客i拿到自己的帽子},則 X=X1+X2+...Xn。由于歸還帽子的順序是隨機的,所以每個顧客拿到自己帽子的概率為1/n,即 Pr(Xi=1)=1/n,從而 E(Xi)=1/n,所以E(X)=E(X1 + X2 + ...Xn)= E(X1)+E(X2)+...E(Xn)=n*1/n = 1,即大約有1個顧客可以拿到自己的帽子。

5)生日悖論

題: 一個房間至少要有多少人,才能使得有兩個人的生日在同一天?

解: 對房間k個人中的每一對(i, j)定義指示器變量 Xij = {i與j生日在同一天} ,則i與j生日相同時,Xij=1,否則 Xij=0。兩個人在同一天生日的概率 Pr(Xij=1)=1/n 。則用X表示同一天生日的兩人對的數目,則 E(X)=E(∑ki=1∑kj=i+1Xij) = C(k,2)*1/n = k(k-1)/2n,令 k(k-1)/2n >=1,可得到 k>=28,即至少要有 28 個人,才能期望兩個人的生日在同一天。

6)概率逆推問題

題: 如果在高速公路上30分鐘內看到一輛車開過的幾率是0.95,那么在10分鐘內看到一輛車開過的幾率是多少?(假設常概率條件下)

解: 假設10分鐘內看到一輛車開過的概率是x,那么沒有看到車開過的概率就是1-x,30分鐘沒有看到車開過的概率是 (1-x)^3,也就是 0.05。所以得到方程 (1-x)^3 = 0.05 ,解方程得到 x 大約是 0.63。

數據結構和算法面試題系列—遞歸算法總結

0.概述

前面總結了隨機算法,這次再把以前寫的遞歸算法的文章梳理一下,這篇文章主要是受到宋勁松老師寫的《Linux C編程》的遞歸章節啟發寫的。最能體現算法精髓的非遞歸莫屬了,希望這篇文章對初學遞歸或者對遞歸有困惑的朋友們能有所幫助,如有錯誤,也懇請各路大牛指正。二叉樹的遞歸示例代碼請參見倉庫的 binary_tree 目錄,本文其他代碼在 這里。

1.遞歸算法初探

本段內容主要摘自《linux C一站式編程》,作者是宋勁松老師,這是我覺得目前看到的國內關于Linux C編程的最好的技術書籍之一,強烈推薦下!

關于遞歸的一個簡單例子是求整數階乘,n!=n*(n-1)!,0!=1 。則可以寫出如下的遞歸程序:

int factorial(int n)
{
	if (n == 0)
		return 1;
	else {
		int recurse = factorial(n-1);
		int result = n * recurse;
		return result;
	}
}
復制代碼

factorial這個函數就是一個遞歸函數,它調用了它自己。自己直接或間接調用自己的函數稱為遞歸函數。如果覺得迷惑,可以把 factorial(n-1) 這一步看成是在調用另一個函數--另一個有著相同函數名和相同代碼的函數,調用它就是跳到它的代碼里執行,然后再返回 factorial(n-1) 這個調用的下一步繼續執行。

為了證明遞歸算法的正確性,我們可以一步步跟進去看執行結果。記得剛學遞歸算法的時候,老是有丈二和尚摸不著頭腦的感覺,那時候總是想著把遞歸一步步跟進去看執行結果。遞歸層次少還算好辦,但是層次一多,頭就大了,完全不知道自己跟到了遞歸的哪一層。比如求階乘,如果只是factorial(3)跟進去問題還不大,但是若是factorial(100)要跟進去那真的會煩死人。

事實上,我們并不是每個函數都需要跟進去看執行結果的,比如我們在自己的函數中調用printf函數時,并沒有鉆進去看它是怎么打印的,因為我們相信它能完成打印工作。 我們在寫factorial函數時有如下代碼:

int recurse = factorial(n-1);
int result = n * recurse;
復制代碼

這時,如果我們相信factorial是正確的,那么傳遞參數為n-1它就會返回(n-1)!,那么result=n*(n-1)!=n!,從而這就是factorial(n)的結果。

當然這有點奇怪:我們還沒寫完factorial這個函數,憑什么要相信factorial(n-1)是正確的?如果你相信你正在寫的遞歸函數是正確的,并調用它,然后在此基礎上寫完這個遞歸函數,那么它就會是正確的,從而值得你相信它正確。

這么說還是有點玄乎,我們從數學上嚴格證明一下 factorial 函數的正確性。剛才說了,factorial(n) 的正確性依賴于 factorial(n-1) 的正確性,只要后者正確,在后者的結果上乘個 n 返回這一步顯然也沒有疑問,那么我們的函數實現就是正確的。因此要證明factorial(n) 的正確性就是要證明 factorial(n-1) 的正確性,同理,要證明factorial(n-1) 的正確性就是要證明 factorial(n-2) 的正確性,依此類推下去,最后是:要證明 factorial(1) 的正確性就是要證明 factorial(0) 的正確性。而factorial(0) 的正確性不依賴于別的函數調用,它就是程序中的一個小的分支return 1; 這個 1 是我們根據階乘的定義寫的,肯定是正確的,因此 factorial(1) 的實現是正確的,因此 factorial(2) 也正確,依此類推,最后 factorial(n) 也是正確的。

其實這就是在中學時學的數學歸納法,用數學歸納法來證明只需要證明兩點:Base Case 正確,遞推關系正確。寫遞歸函數時一定要記得寫 Base Case,否則即使遞推關系正確,整個函數也不正確。如果 factorial 函數漏掉了 Base Case,那么會導致無限循環。

2.遞歸經典問題

從上一節的一個關于求階乘的簡單例子的論述,我們可以了解到遞歸算法的精髓:要從功能上理解函數,同時你要相信你正在寫的函數是正確的,在此基礎上調用它,那么它就是正確的。 下面就從幾個常見的算法題來看看如何理解遞歸,這是我的一些理解,歡迎大家提出更好的方法。

2.1)漢諾塔問題

題: 漢諾塔問題是個常見問題,就是說有n個大小不等的盤子放在一個塔A上面,自底向上按照從大到小的順序排列。要求將所有n個盤子搬到另一個塔C上面,可以借助一個塔B中轉,但是要滿足任何時刻大盤子不能放在小盤子上面。

解: 基本思想分三步,先把上面的 N-1 個盤子經 C 移到 B,然后將最底下的盤子移到 C,再將 B 上面的N-1個盤子經 A 移動到 C。總的時間復雜度 f(n)=2f(n-1)+1,所以 f(n)=2^n-1

/**
 * 漢諾塔
 */
void hano(char a, char b, char c, int n) {
    if (n <= 0) return;
hano(a, c, b, n-1);
move(a, c);
hano(b, a, c, n-1);

}

void move(char a, char b)
{
printf("%c->%c
", a, b);
}
復制代碼

2.2)求二叉樹的深度

這里的深度指的是二叉樹從根結點到葉結點最大的高度,比如只有一個結點,則深度為1,如果有N層,則高度為N。

int depth(BTNode* root)  
{  
    if (root == NULL)  
        return 0;  
    else {  
        int lDepth = depth(root->left);  //獲取左子樹深度  
        int rDepth = depth(root->right); //獲取右子樹深度  
        return lDepth>rDepth? lDepth+1: rDepth+1; //取較大值+1即為二叉樹深度  
    }  
}  
復制代碼

那么如何從功能上理解 depth 函數呢?我們可以知道定義該函數的目的就是求二叉樹深度,也就是說我們要是完成了函數 depth,那么 depth(root) 就能正確返回以 root 為根結點的二叉樹的深度。因此我們的代碼中 depth(root->left) 返回左子樹的深度,而depth(root->right) 返回右子樹的深度。盡管這個時候我們還沒有寫完 depth 函數,但是我們相信 depth 函數能夠正確完成功能。因此我們得到了 lDepthrDepth,而后通過比較返回較大值加1為二叉樹的深度。

如果不好理解,可以想象在 depth 中調用的函數 depth(root->left) 為另外一個同樣名字完成相同功能的函數,這樣就好理解了。注意 Base Case,這里就是當 root==NULL 時,則深度為0,函數返回0

2.3)判斷二叉樹是否平衡

一顆平衡的二叉樹是指其任意結點的左右子樹深度之差不大于1。判斷一棵二叉樹是否是平衡的,可以使用遞歸算法來實現。

int isBalanceBTTop2Down(BTNode *root)
{
    if (!root) return 1;
int leftHeight = btHeight(root-&gt;left);
int rightHeight = btHeight(root-&gt;right);
int hDiff = abs(leftHeight - rightHeight);

<span class="hljs-keyword">if</span> (hDiff &gt; 1) <span class="hljs-built_in">return</span> 0;

<span class="hljs-built_in">return</span> isBalanceBTTop2Down(root-&gt;left) &amp;&amp; isBalanceBTTop2Down(root-&gt;right);

}
復制代碼

該函數的功能定義是二叉樹 root 是平衡二叉樹,即它所有結點的左右子樹深度之差不大于1。首先判斷根結點是否滿足條件,如果不滿足,則直接返回 0。如果滿足,則需要判斷左子樹和右子樹是否都是平衡二叉樹,若都是則返回1,否則0。

2.4)排列算法

排列算法也是遞歸的典范,記得當初第一次看時一層層跟代碼,頭都大了,現在從函數功能上來看確實好理解多了。先看代碼:

/**
 * 輸出全排列,k為起始位置,n為數組大小
 */
void permute(int a[], int k, int n)
{
    if (k == n-1) {
        printIntArray(a, n); // 輸出數組
    } else {
        int i;
        for (i = k; i < n; i++) {
            swapInt(a, i, k); // 交換
            permute(a, k+1, n); // 下一次排列
            swapInt(a, i, k); // 恢復原來的序列
        }
    }
}
復制代碼

首先明確的是 perm(a, k, n) 函數的功能:輸出數組 a 從位置 k 開始的所有排列,數組長度為 n。這樣我們在調用程序的時候,調用格式為 perm(a, 0, n),即輸出數組從位置 0 開始的所有排列,也就是該數組的所有排列。基礎條件是 k==n-1,此時已經到達最后一個元素,一次排列已經完成,直接輸出。否則,從位置k開始的每個元素都與位置k的值交換(包括自己與自己交換),然后進行下一次排列,排列完成后記得恢復原來的序列。

假定數組a
aan?na a
=3,則程序調用 perm(a, 0, 3) 可以如下理解:
第一次交換 0,0,并執行perm(a, 1, 3),執行完再次交換0,0,數組此時又恢復成初始值。
第二次交換 1,0(注意數組此時是初始值),并執行perm(a, 1, 3),執行完再次交換1,0,數組此時又恢復成初始值。
第三次交換 2,0,并執行perm(a, 1, 3),執行完成后交換2,0,數組恢復成初始值。

也就是說,從功能上看,首先確定第0個位置,然后調用perm(a, 1, 3)輸出從1開始的排列,這樣就可以輸出所有排列。而第0個位置可能的值為a[0], a[1],a[2],這通過交換來保證第0個位置可能出現的值,記得每次交換后要恢復初始值。

如數組 a={1,2,3},則程序運行輸出結果為:1 2 3 ,1 3 2 ,2 1 3 ,2 3 1 ,3 2 1 ,3 1 2。即先輸出以1為排列第一個值的排列,而后是2和3為第一個值的排列。

2.5)組合算法

組合算法也可以用遞歸實現,只是它的原理跟0-1背包問題類似。即要么選要么不選,注意不能選重復的數。完整代碼如下:

/*
 * 組合主函數,包括選取1到n個數字
 */ 
void combination(int a[], int n)
{
    int *select = (int *)calloc(sizeof(int), n); // select為輔助數組,用于存儲選取的數
    int k;
    for (k = 1; k <= n; k++) {
        combinationUtil(a, n, 0, k, select);
    }
}

/*

組合工具函數:從數組a從位置i開始選取k個數
*/
void combinationUtil(int a[], int n, int i, int k, int *select)
{
if (i > n) return; //位置超出數組范圍直接返回,否則非法訪問會出段錯誤

if (k == 0) { //選取完了,輸出選取的數字
int j;
for (j = 0; j < n; j++) {
if (select[j])
printf("%d ", a[j]);
}
printf(" ");
} else {
select[i] = 1;
combinationUtil(a, n, i+1, k-1, select); //第i個數字被選取,從后續i+1開始選取k-1個數
select[i] = 0;
combinationUtil(a, n, i+1, k, select); //第i個數字不選,則從后續i+1位置開始還要選取k個數
}
}
復制代碼

2.6) 逆序打印字符串

這個比較簡單,代碼如下:

void reversePrint(const char *str) 
{
    if (!*str)
        return;
reversePrint(str + 1);
putchar(*str);

}
復制代碼

2.7) 鏈表逆序

鏈表逆序通常我們會用迭代的方式實現,但是如果要顯得特立獨行一點,可以使用遞歸,如下,代碼請見倉庫的 aslist 目錄。

/**
 * 鏈表逆序,遞歸實現。
 */
ListNode *listReverseRecursive(ListNode *head)
{
    if (!head || !head->next) {
        return head;
    }
ListNode *reversedHead = listReverseRecursive(head-&gt;next);
head-&gt;next-&gt;next = head;
head-&gt;next = NULL;
<span class="hljs-built_in">return</span> reversedHead;

}
復制代碼

數據結構和算法面試題系列—背包問題總結

0.概述

背包問題包括0-1背包問題、完全背包問題、部分背包問題等多種變種。其中,最簡單的是部分背包問題,它可以采用貪心法來解決,而其他幾種背包問題往往需要動態規劃來求解。本文主要來源于《背包問題九講》,我選擇了比較簡單的0-1背包問題和完全背包問題進行匯總。同時給出實現代碼,如有錯誤,請各位大蝦指正。本文代碼在 這里。

1.部分背包問題

部分背包問題描述: 有 N 件物品和一個容量為 C 的背包。第 i 件物品的重量是 w[i],價值是 v[i]。求解將哪些物品裝入背包可使價值總和最大。注意這里不要求把物品整個裝入,可以只裝入一個物品的部分。

解法: 部分背包問題常采用貪心算法來解決,先對每件物品計算其每單位重量價值 v[i]/w[i],然后從具有最大單位價值的物品開始拿,然后拿第二大價值的物品,直到裝滿背包。按照這種貪心策略拿到的必然是價值總和最大,這個比較簡單,實現代碼就略去了。

2. 0-1背包問題

0-1背包問題描述

有 N 件物品和一個容量為 C 的背包。第 i 件物品的重量是 w[i],價值是v[i]。求解將哪些物品裝入背包可使價值總和最大。注意物品只能要么拿要么不拿,這也正是 0-1 的意義所在。可以把部分背包問題看作是拿金粉,而 0-1 背包問題則是拿金塊,一個可分,一個不可分。

分析

這是最基礎的背包問題,特點是:每種物品僅有一件,可以選擇放或不放。用子問題定義狀態:即 f[i][w] 表示前 i 件物品恰放入一個容量為 c 的背包可以獲得的最大價值。則其狀態轉移方程便是:

f[i][c] = max{f[i-1][c], f[i-1][c-w[i]]+v[i]} 
復制代碼

這個方程非常重要,基本上所有跟背包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:將前 i 件物品放入容量為 c 的背包中 這個子問題,若只考慮第i件物品的策略(放或不放),那么就可以轉化為一個只牽扯前 i-1 件物品的問題。

如果不放第 i 件物品,那么問題就轉化為 前 i-1 件物品放入容量為 v 的背包中,價值為 f[i-1][c]
如果放第i件物品,那么問題就轉化為 前 i-1 件物品放入剩下的容量為 c-w[i] 的背包中,此時能獲得的最大價值就是 f[i-1][c-w[i]]再加上通過放入第 i 件物品獲得的價值 v[i]。

優化空間復雜度

以上方法的時間和空間復雜度均為 O(CN),其中時間復雜度應該已經不能再優化了,但空間復雜度卻可以優化到 O(N)。 由于在計算 f[i][c] 的時候,我們只需要用到 f[i-1][c]f[i-1][c-w[i]],所以完全可以通過一維數組保存它們的值,這里用到的小技巧就是需要從 c=C...0 開始反推,這樣就能保證在求 f[c] 的時候 f[c-w[i]] 保存的是 f[i-1][c-w[i]] 的值。注意,這里不能從 c=0...C 這樣順推,因為這樣會導致 f[c-w[i]] 的值是 f[i][c-w[i]] 而不是 f[i-1][c-w[i]。這里可以優化下界,其實只需要從 c=C...w[i] 即可,可以避免不需要的計算。偽代碼如下所示:

for i=0..N-1
    for c=C..w[i]
        f[c]=max{f[c],f[c-w[i]]+v[i]};
復制代碼

最終實現代碼如下:

int knap01(int N, int C, int w[], int v[])
{
    int *f = (int *)calloc(sizeof(int), C+1);
    int i, c;
<span class="hljs-keyword">for</span> (i = 0; i &lt; N; i++) {
    <span class="hljs-keyword">for</span> (c = C; c &gt;= w[i]; c--) {
        f[c] = max(f[c], f[c-w[i]] + v[i]);
    }
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d: "</span>, i+1);
    <span class="hljs-built_in">print</span>IntArray(f, C+1); // 打印f數組
}
<span class="hljs-built_in">return</span> f[C];

}
復制代碼

測試結果如下,即在背包容量為 10 的時候裝第1和第2個物品(索引從0開始),總重量為 4+5=9,最大價值為 5+6=11。

參數:
w = [3, 4, 5] //物品重量列表
v = [4, 5, 6] //物品價值列表
C = 10

結果(打印數組f,i為選擇的物品索引,c為背包重量,值為背包物品價值):
         
i/c 0 1 2 3 4 5 6 7 8 9 10
 0: 0 0 0 4 4 4 4 4 4 4 4 
 1: 0 0 0 4 5 5 5 9 9 9 9 
 2: 0 0 0 4 5 6 6 9 10 11 11 

KNap01 max: 11
復制代碼

初始化的細節問題

我們看到的求最優解的背包問題題目中,事實上有兩種不太相同的問法。有的題目要求“恰好裝滿背包”時的最優解,有的題目則并沒有要求必須把背包裝滿。一種區別這兩種問法的實現方法是在初始化的時候有所不同。

如果是第一種問法,要求恰好裝滿背包,那么在初始化時除了 f[0] 為 0 其它 f[1..C] 均設為 -∞,這樣就可以保證最終得到的 f[N] 是一種恰好裝滿背包的最優解。如果并沒有要求必須把背包裝滿,而是只希望價格盡量大,初始化時應該將 f[0..C] 全部設為0。

為什么呢?可以這樣理解:初始化的 f 數組事實上就是在沒有任何物品可以放入背包時的合法狀態。如果要求背包恰好裝滿,那么此時只有容量為 0 的背包可能被價值為 0 的東西 “恰好裝滿”,其它容量的背包均沒有合法的解,屬于未定義的狀態,它們的值就都應該是 -∞ 了。如果背包并非必須被裝滿,那么任何容量的背包都有一個合法解“什么都不裝”,這個解的價值為0,所以初始時狀態的值也就全部為0了。

3.完全背包問題

問題描述

有 N 種物品和一個容量為 C 的背包,每種物品都有無限件可用。第i種物品的重量是 w[i],價值是v[i]。求解將哪些物品裝入背包可使這些物品的重量總和不超過背包容量,且價值總和最大,物品不能只裝部分。

基本思路

這個問題非常類似于0-1背包問題,所不同的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關的策略已并非取或不取兩種,而是有取0件、取1件、取2件...等很多種。如果仍然按照解01背包時的思路,令 f[i][c] 表示前 i 種物品恰放入一個容量為 c 的背包的最大權值。仍然可以按照每種物品不同的策略寫出狀態轉移方程,像這樣:

f[i][c] = max{f[i-1][c-k*w[i]]+ k*w[i]| 0<=k*w[i]<=c }
復制代碼

這跟0-1背包問題一樣有O(CN)個狀態需要求解,但求解每個狀態的時間已經不是常數了,求解狀態 f[i][c] 的時間是 O(c/w[i]),總的復雜度可以認為是 O(CN*Σ(c/w[i])),是比較大的。實現代碼如下:

/*
 * 完全背包問題
 */
int knapComplete(int N, int C, int w[], int v[])
{
    int *f = (int *)calloc(sizeof(int), C+1);
    int i, c, k;
    for (i = 0; i < N; i++) {
        for (c = C; c >= 0; c--) {
            for (k = 0; k <= c/w[i]; k++) {
                f[c] = max(f[c], f[c-k*w[i]] + k*v[i]);
            }
        }
        printf("%d: ", i+1);
        printIntArray(f, C+1);
    }
    return f[C];
}
復制代碼

使用與0-1背包問題相同的例子,運行程序結果如下,最大價值為 13,即選取 2個重量3,1個重量4的物品,總價值最高,為 4*2 + 5 = 13

i/c: 0 1 2 3 4 5 6 7 8 9 10
0:   0 0 0 4 4 4 8 8 8 12 12 
1:   0 0 0 4 5 5 8 9 10 12 13 
2:   0 0 0 4 5 6 8 9 10 12 13 

KNapComplete max: 13
復制代碼

轉換為0-1背包問題

既然01背包問題是最基本的背包問題,那么我們可以考慮把完全背包問題轉化為01背包問題來解。最簡單的想法是,考慮到第i種物品最多選 C/w[i] 件,于是可以把第 i 種物品轉化為 C/w[i] 件費用及價值均不變的物品,然后求解這個01背包問題。這樣完全沒有改進基本思路的時間復雜度,但這畢竟給了我們將完全背包問題轉化為01背包問題的思路:將一種物品拆成多件物品。

更高效的轉化方法是:把第 i 種物品拆成重量為 w[i]*2^k、價值為 w[i]*2^k 的若干件物品,其中 k 滿足 w[i]*2^k<=C。這是二進制的思想,因為不管最優策略選幾件第 i 種物品,總可以表示成若干個 2^k 件物品的和。這樣把每種物品拆成 O(log C/w[i]) 件物品,是一個很大的改進。但我們有更優的 O(CN) 的算法。

進一步優化—O(CN)解法

我們可以采用與0-1背包問題相反的順序遍歷,從而可以得到 O(CN) 的解法,偽代碼如下:

for i=0..N-1
    for c=w[i]..C
        f[c]=max{f[c],f[c-w[i]]+v[i]};
復制代碼

這個偽代碼與0-1背包偽代碼只是 C 的循環次序不同而已。0-1背包之所以要按照 v=V..0的逆序來循環。這是因為要保證第i次循環中的狀態 f[i][c] 是由狀態 f[i-1][c-w[i]] 遞推而來。換句話說,這正是為了保證每件物品只選一次,保證在考慮“選入第i件物品”這件策略時,依據的是一個絕無已經選入第i件物品的子結果 f[i-1][c-w[i]]。而現在完全背包的特點恰是每種物品可選無限件,所以在考慮“加選一件第i種物品”這種策略時,卻正需要一個可能已選入第i種物品的子結果 f[i][c-w[i]],所以就可以并且必須采用 c=w[i]..C 的順序循環。這就是這個簡單的程序為何成立的道理。實現代碼如下:

/**
 * 完全背包問題-仿01背包解法
 */
int knapCompleteLike01(int N, int C, int w[], int v[])
{
    int *f = (int *)calloc(sizeof(int), C+1);
    int i, c;
    for (i = 0; i < N; i++) {
        for (c = w[i]; c <= C; c++) {
            f[c] = max(f[c], f[c-w[i]] + v[i]);
        }
        printf("%d: ", i+1);
        printIntArray(f, C+1);
}
<span class="hljs-built_in">return</span> f[C];

}
復制代碼

數據結構和算法面試題系列—數字題總結

0.概述

數學是科學之基礎,數字題往往也是被面試玩出花來。數學本身是有趣味的一門學科,前段時間有點不務正業,對數學產生了濃厚的興趣,于是看了幾本數學史論的書,也買了《幾何原本》和《陶哲軒的實分析》,看了部分章節,受益良多,有興趣的朋友可以看看。特別是幾何原本,歐幾里得上千年前的著作,里面的思考和證明方式真的富有啟發性,老少咸宜。本文先總結下面試題中的數字題,我盡量增加了一些數學方面的證明,如有錯誤,也請指正。本文代碼都在 這里。

1.找質數問題

題: 寫一個程序,找出前N個質數。比如N為100,則找出前100個質數。

分析: 質數(或者叫素數)指在大于1的自然數中,除了1和該數自身外,無法被其他自然數整除的數,如 2,3,5...。最基本的想法就是對 1 到 N 的每個數進行判斷,如果是質數則輸出。一種改進的方法是不需要對 1 到 N 所有的數都進行判斷,因為除了 2 外的偶數肯定不是質數,而奇數可能是質數,可能不是。然后我們可以跳過2與3的倍數,即對于 6n,6n+1, 6n+2, 6n+3, 6n+4, 6n+5,我們只需要判斷 6n+16n+5 是否是質數即可。

判斷某個數m是否是質數,最基本的方法就是對 2 到 m-1 之間的數整除 m,如果有一個數能夠整除 m,則 m 就不是質數。判斷 m 是否是質數還可以進一步改進,不需要對 2 到 m-1 之間的數全部整除 m,只需要對 2 到 根號m 之間的數整除m就可以。如用 2,3,4,5...根號m 整除 m。其實這還是有浪費,因為如果2不能整除,則2的倍數也不能整除,同理3不能整除則3的倍數也不能整除,因此可以只用2到根號m之間小于根號m的質數去除即可。

解: 預先可得2,3,5為質數,然后跳過2與3的倍數,從7開始,然后判斷11,然后判斷13,再是17...規律就是從5加2,然后加4,然后加2,然后再加4。如此反復即可,如下圖所示,只需要判斷 7,11,13,17,19,23,25,29... 這些數字。

判斷是否是質數采用改進后的方案,即對2到根號m之間的數整除m來進行判斷。需要注意一點,不能直接用根號m判斷,因為對于某些數字,比如 121 開根號可能是 10.999999,所以最好使用乘法判斷,如代碼中所示。

/**
 * 找出前N個質數, N > 3
 */
int primeGeneration(int n)
{
    int *prime = (int *)malloc(sizeof(int) * n);
    int gap = 2;            
    int count = 3;
    int maybePrime = 5;
    int i, isPrime;
/* 注意:2, 3, 5 是質數 */
prime[0] = 2;
prime[1] = 3;
prime[2] = 5;

<span class="hljs-keyword">while</span> (count &lt; n)  {
     maybePrime += gap;
     gap = 6 - gap;
     isPrime = 1; 
     <span class="hljs-keyword">for</span> (i = 2; prime[i]*prime[i] &lt;= maybePrime &amp;&amp; isPrime; i++)
          <span class="hljs-keyword">if</span> (maybePrime % prime[i] == 0)
               isPrime = 0;

     <span class="hljs-keyword">if</span> (isPrime)
          prime[count++] = maybePrime;
}

<span class="hljs-built_in">printf</span>(<span class="hljs-string">"
First %d Prime Numbers are :
"</span>, count);

<span class="hljs-keyword">for</span> (i = 0; i &lt; count; i++) {
     <span class="hljs-keyword">if</span> (i % 10 == 0) <span class="hljs-built_in">printf</span>(<span class="hljs-string">"
"</span>);
     <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%5d"</span>, prime[i]);
}
<span class="hljs-built_in">printf</span>(<span class="hljs-string">"
"</span>);
<span class="hljs-built_in">return</span> 0;

}
復制代碼

2.階乘末尾含0問題

題: 給定一個整數N,那么N的階乘N!末尾有多少個0呢?(該題取自《編程之美》)

解1: 流行的解法是,如果 N!= K10M,且K不能被10整除,則 N!末尾有 M 個0。考慮 N!可以進行質因數分解,N!= (2X) * (3Y) * (5Z)..., 則由于10 = 25,所以0的個數只與 XZ 相關,每一對2和5相乘得到一個 10,所以 0 的個數 M = min(X, Z),顯然 2 出現的數目比 5 要多,所以 0 的個數就是 5 出現的個數。由此可以寫出如下代碼:

/**
 * N!末尾0的個數
 */
int numOfZero(int n)
{
    int cnt = 0, i, j;
    for (i = 1; i <= n; i++) {
        j = i;
        while (j % 5 == 0) {
            cnt++;
            j /= 5;
        }
    }
    return cnt;
}
復制代碼

解2: 繼續分析可以改進上面的代碼,為求出1到N的因式分解中有多少個5,令 Z=N/5 + N/(52) + N/(53)+... 即 N/5 表示 1 到 N 的數中 5 的倍數貢獻一個 5,N/(52) 表示 52 的倍數再貢獻一個 5...。舉個簡單的例子,比如求1到100的數因式分解中有多少個5,可以知道5的倍數有20個,25的倍數有4個,所以一共有24個5。代碼如下:

/**
 * N!末尾0的個數-優化版
 */
int numOfZero2(int n)
{
    int cnt = 0;
    while (n) {
        cnt += n/5;
        n /= 5;
    }
    return cnt;
}
復制代碼

總結: 上面的分析乏善可陳,不過需要提到的一點就是其中涉及到的一條算術基本定理,也就是 任意大于1的自然數都可以分解為質數的乘積,而且該分解方式是唯一的。 定理證明分為兩個部分,存在性和唯一性。證明如下:

存在性證明

使用反證法來證明,假設存在大于1的自然數不能寫成質數的乘積,把最小的那個稱為n。自然數可以根據其可除性(是否能表示成兩個不是自身的自然數的乘積)分成3類:質數、合數和1。

首先,按照定義,n大于1。
其次,n 不是質數,因為質數p可以寫成質數乘積:p=p,這與假設不相符合。因此n只能是合數,但每個合數都可以分解成兩個嚴格小于自身而大于1的自然數的積。設 n = a*b,a 和 b都是大于1小于n的數,由假設可知,a和b都可以分解為質數的乘積,因此n也可以分解為質數的乘積,所以這與假設矛盾。由此證明所有大于1的自然數都能分解為質數的乘積。

唯一性證明

當n=1的時候,確實只有一種分解。
假設對于自然數 n>1,存在兩種因式分解: n=p1...pm
= q1...qk,p1<=...<=pm, q1<=...<=qk,其中 p 和 q 都是質數,我們要證明 p1=q1,p2=q2...如果不相等,我們可以設 p1 < q1,從而 p1 小于所有的 q。由于 p1 和 q1 是質數,所以它們的最大公約數為1,由歐幾里德算法可知存在整數 a 和 b 使得 a * p1 + b * q1 = 1。因此
a * p1 * q2...qk + b * q1 * q2...qk = q2...qk (等式1)。由于 q1...qk = n,因此等式1左邊是 p1 的整數倍,從而等式1右邊的 q2...qk 也必須是 p1 的整數倍,因此必然有 p1 = qi,i > 1。而這與前面 p1 小于所有的 q 矛盾,因此,由對稱性,對 p1 > q1 這種情況可以得到類似結論,故可以證明 p1 = q1,同理可得 p2 = q2...pm=qk,由此完成唯一性的證明。

3.1-N正整數中1的數目

題: 給定一個十進制正整數N,求出從 1 到 N 的所有整數中包含 1 的個數。比如給定 N=23,則包含1的個數為13。其中個位出現1的數字有 1,11,21,共3個,十位出現1的數字有 10,11...19 共10個,所以總共包含 1 的個數為 3 + 10 = 13 個。

分析: 最自然的想法莫過于直接遍歷1到N,求出每個數中包含的1的個數,然后將這些個數相加就是總的 1 的個數。需要遍歷 N 個數,每次計算 1 的個數需要 O(log10N),該算法復雜度為 O(Nlog10N)。當數字N很大的時候,該算法會耗費很長的時間,應該還有更好的方法。

解: 我們可以從1位數開始分析,慢慢找尋規律。

當 N 為 1 位數時,對于 N>=1,1 的個數 f(N) 為1。

當 N 為 2 位數時,則個位上1的個數不僅與個位數有關,還和十位數字有關。

當 N=23 時,個位上 1 的個數有 1、11、21 共3個,十位上1的個數為 10,11...19 共10個,所以 1 的個數 f(N) = 3+10 = 13。如果 N 的個位數 >=1,則個位出現1的次數為十位數的數字加1;如果 N 的個位數為0,則個位出現 1 的次數等于十位數的數字。
十位數上出現1的次數類似,如果N的十位數字等于1,則十位數上出現1的次數為各位數字加1;如果N的十位數字大于1,則十位數上出現1的次數為10。

當 N 為 3 位數時,同樣分析可得1的個數。如 N=123,可得 1出現次數 = 13+20+24 = 57

當 N 為 4,5...K 位數時,我們假設 N=abcde,則要計算百位上出現1的數目,則它受到三個因素影響:百位上的數字,百位以下的數字,百位以上的數字。

如果百位上數字為0,則百位上出現1的次數為更高位數字決定。如 N=12013,則百位出現1的數字有100~199, 1000~1199, 2100~2199...11100~111999 共 1200 個,等于百位的更高位數字(12)*當前位數(100)。
如果百位上數字為1,則百位上出現1的次數不僅受更高位影響,還受低位影響。如12113,則百位出現1的情況共有 1200+114=1314 個,也就是高位影響的 12 * 100 + 低位影響的 113+1 = 114 個。
如果百位上數字為其他數字,則百位上出現1的次數僅由更高位決定。如 12213,則百位出現1的情況為 (12+1)*100=1300。

有以上分析思路,寫出下面的代碼。其中 low 表示低位數字,curr 表示當前考慮位的數字,high 表示高位數字。一個簡單的分析,考慮數字 123,則首先考慮個位,則 curr 為 3,低位為 0,高位為 12;然后考慮十位,此時 curr 為 2,低位為 3,高位為 1。其他的數字可以以此類推,實現代碼如下:

/**
 * 1-N正整數中1的個數
 */
int numOfOne(int n)
{
    int factor = 1, cnt = 0;  //factor為乘數因子
    int low = 0, curr = 0, high = 0;
    while (n / factor != 0) {
        low = n - n/factor * factor;  //low為低位數字,curr為當前考慮位的數字,high為高位數字
        curr = n/factor % 10;
        high = n/(factor * 10);
    switch(curr) {
        <span class="hljs-keyword">case</span> 0:   //當前位為0的情況
            cnt += high * factor;
            <span class="hljs-built_in">break</span>;
        <span class="hljs-keyword">case</span> 1:   //當前位為1的情況
            cnt += high * factor + low + 1;
            <span class="hljs-built_in">break</span>;
        default:  //當前位為其他數字的情況
            cnt += (high+1) * factor;
            <span class="hljs-built_in">break</span>;
    }
    factor *= 10;
}
<span class="hljs-built_in">return</span> cnt;

}
復制代碼

4.和為N的正整數序列

題: 輸入一個正整數數N,輸出所有和為N連續正整數序列。例如輸入 15,由于 1+2+3+4+5=4+5+6=7+8=15,所以輸出 3 個連續序列 1-5、4-6和7-8。

解1:運用數學規律

假定有 k 個連續的正整數和為 N,其中連續序列的第一個數為 x,則有 x+(x+1)+(x+2)+...+(x+k-1) = N。從而可以求得 x = (N - k*(k-1)/2) / k。當 x<=0 時,則說明已經沒有正整數序列的和為 N 了,此時循環退出。初始化 k=2,表示2個連續的正整數和為 N,則可以求出 x 的值,并判斷從 x 開始是否存在2個連續正整數和為 N,若不存在則 k++,繼續循環。

/**
 * 查找和為N的連續序列
 */
int findContinuousSequence(int n) 
{
    int found = 0;
    int k = 2, x, m, i;  // k為連續序列的數字的數目,x為起始的值,m用于判斷是否有滿足條件的值。
    while (1) { 
        x = (n - k*(k-1)/2) / k;  // 求出k個連續正整數和為n的起始值x
        m = (n - k*(k-1)/2) % k; // m用于判斷是否有滿足條件的連續正整數值
    <span class="hljs-keyword">if</span> (x &lt;= 0)
        <span class="hljs-built_in">break</span>;    // 退出條件,如果x&lt;=0,則循環退出。

    <span class="hljs-keyword">if</span> (!m) {     // m為0,表示找到了連續子序列和為n。
        found = 1;
        <span class="hljs-built_in">print</span>ContinuousSequence(x, k);
    }
    k++;
}
<span class="hljs-built_in">return</span> found;

}

/**

打印連續子序列
*/
void printContinuousSequence(int x, int k)
{
int i;
for (i = 0; i < k; i++) {
printf("%d ", x++);
}
printf("
");
}
復制代碼

解2:序列結尾位置法

因為序列至少有兩個數,可以先判定以數字2結束的連續序列和是否有等于 n 的,然后是以3結束的連續序列和是否有等于 n 的,...以此類推。此解法思路參考的何海濤先生的博文,代碼如下:

/**
 * 查找和為N的連續序列-序列結尾位置法
 */
int findContinuousSequenceEndIndex(int n) 
{
    if (n < 3) return 0;
int found = 0;
int begin = 1, end = 2;
int mid = (1 + n) / 2;
int sum = begin + end;

<span class="hljs-keyword">while</span> (begin &lt; mid) {
    <span class="hljs-keyword">if</span> (sum &gt; n) {
        sum -= begin;
        begin++;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">if</span> (sum == n) {
            found = 1;
            <span class="hljs-built_in">print</span>ContinuousSequence(begin, end-begin+1);
        }

        end++;
        sum += end;
    }
}

<span class="hljs-built_in">return</span> found;

}
復制代碼

擴展: 是不是所有的正整數都能分解為連續正整數序列呢?

答案: 不是。并不是所有的正整數都能分解為連續的正整數和,如 32 就不能分解為連續正整數和。對于奇數,我們總是能寫成 2k+1 的形式,因此可以分解為 [k,k+1],所以總是能分解成連續正整數序列。對于每一個偶數,均可以分解為質因數之積,即 n = 2i * 3j * 5k...,如果除了i之外,j,k...均為0,那么 n = 2i,對于這種數,其所有的因數均為偶數,是不存在連續子序列和為 n 的,因此除了2的冪之外的所有 n>=3 的正整數均可以寫成一個連續的自然數之和。

5.最大連續子序列和

題: 求取數組中最大連續子序列和,例如給定數組為 A = {1, 3, -2, 4, -5}, 則最大連續子序列和為 6,即 1 + 3 +(-2)+ 4 = 6

分析: 最大連續子序列和問題是個很老的面試題了,最佳的解法是 O(N) 復雜度,當然有些值得注意的地方。這里總結三種常見的解法,重點關注最后一種 O(N) 的解法即可。需要注意的是有些題目中的最大連續子序列和如果為負,則返回0;而本題如果是全為負數,則返回最大的負數即可。

解1: 因為最大連續子序列和只可能從數組 0 到 n-1 中某個位置開始,我們可以遍歷 0 到 n-1 個位置,計算由這個位置開始的所有連續子序列和中的最大值。最終求出最大值即可。

/**
 * 最大連續子序列和
 */
int maxSumOfContinuousSequence(int a[], int n)
{
    int max = a[0], i, j, sum; // 初始化最大值為第一個元素
    for (i = 0; i < n; i++) {
        sum = 0; // sum必須清零
        for (j = i; j < n; j++) { //從位置i開始計算從i開始的最大連續子序列和的大小,如果大于max,則更新max。
            sum += a[j];
            if (sum > max)
                max = sum;
        }
    }
    return max;
}
復制代碼

解2: 該問題還可以通過分治法來求解,最大連續子序列和要么出現在數組左半部分,要么出現在數組右半部分,要么橫跨左右兩半部分。因此求出這三種情況下的最大值就可以得到最大連續子序列和。

/**
 * 最大連續子序列和-分治法
 */
int maxSumOfContinuousSequenceSub(int a[], int l, int u)
{
    if (l > u) return 0;
    if (l == u) return a[l];
    int m = (l + u) / 2;
/*求橫跨左右的最大連續子序列左半部分*/
int lmax = a[m], lsum = 0;
int i;

<span class="hljs-keyword">for</span> (i = m; i &gt;= l; i--) {
    lsum += a[i];
    <span class="hljs-keyword">if</span> (lsum &gt; lmax)
        lmax = lsum;
}

/*求橫跨左右的最大連續子序列右半部分*/
int rmax=a[m+1], rsum = 0;
<span class="hljs-keyword">for</span> (i = m+1; i &lt;= u; i++) {
    rsum += a[i];
    <span class="hljs-keyword">if</span> (rsum &gt; rmax)
        rmax = rsum;
}

<span class="hljs-built_in">return</span> max3(lmax+rmax, maxSumOfContinuousSequenceSub(a, l, m),
    maxSumOfContinuousSequenceSub(a, m+1, u)); //返回三者最大值

}
復制代碼

解3: 還有一種更好的解法,只需要 O(N) 的時間。因為最大 連續子序列和只可能是以位置 0~n-1 中某個位置結尾。當遍歷到第 i 個元素時,判斷在它前面的連續子序列和是否大于0,如果大于0,則以位置 i 結尾的最大連續子序列和為元素 i 和前面的連續子序列和相加;否則,則以位置 i 結尾最大連續子序列和為a[i]。

/**
 * 最打連續子序列和-結束位置法
 */
int maxSumOfContinuousSequenceEndIndex(int a[], int n)
{
    int maxSum, maxHere, i;
    maxSum = maxHere = a[0];   // 初始化最大和為a[0]

    for (i = 1; i < n; i++) {
        if (maxHere <= 0)
            maxHere = a[i];  // 如果前面位置最大連續子序列和小于等于0,則以當前位置i結尾的最大連續子序列和為a[i]
        else
            maxHere += a[i]; // 如果前面位置最大連續子序列和大于0,則以當前位置i結尾的最大連續子序列和為它們兩者之和

        if (maxHere > maxSum) {
            maxSum = maxHere;  //更新最大連續子序列和
        }
    }
    return maxSum;
}
復制代碼

6.最大連續子序列乘積

題: 給定一個整數序列(可能有正數,0和負數),求它的一個最大連續子序列乘積。比如給定數組a[] = {3, -4, -5, 6, -2},則最大連續子序列乘積為 360,即 3*(-4)*(-5)*6=360

解: 求最大連續子序列乘積與最大連續子序列和問題有所不同,因為其中有正有負還有可能有0,可以直接利用動歸來求解,考慮到可能存在負數的情況,我們用 max[i] 來表示以 a[i] 結尾的最大連續子序列的乘積值,用 min[i] 表示以 a[i] 結尾的最小的連續子序列的乘積值,那么狀態轉移方程為:

max[i] = max{a[i], max[i-1]*a[i], min[i-1]*a[i]};
min[i] = min{a[i], max[i-1]*a[i], min[i-1]*a[i]};
復制代碼

初始狀態為 max[0] = min[0] = a[0]。代碼如下:

/**
 * 最大連續子序列乘積
 */
int maxMultipleOfContinuousSequence(int a[], int n)
{
    int minSofar, maxSofar, max, i;
    int maxHere, minHere;
    max = minSofar = maxSofar = a[0];
<span class="hljs-keyword">for</span>(i = 1; i &lt; n; i++){
    int maxHere = max3(maxSofar*a[i], minSofar*a[i], a[i]);
    int minHere = min3(maxSofar*a[i], minSofar*a[i], a[i]);
    maxSofar = maxHere, minSofar = minHere;
    <span class="hljs-keyword">if</span>(max &lt; maxSofar)
        max = maxSofar;
}
<span class="hljs-built_in">return</span> max;

}
復制代碼

7.比特位相關

1) 判斷一個正整數是否是2的整數次冪

題: 給定一個正整數 n,判斷該正整數是否是 2 的整數次冪。

解1: 一個基本的解法是設定 i=1 開始,循環乘以2直到 i>=n,然后判斷 i 是否等于 n 即可。

解2: 還有一個更好的方法,那就是觀察數字的二進制表示,如 n=101000,則我們可以發現n-1=100111。也就是說 n -> n-1 是將 n 最右邊的 1 變成了 0,同時將 n 最右邊的 1 右邊的所有比特位由0變成了1。因此如果 n & (n-1) == 0 就可以判定正整數 n 為 2的整數次冪。

/**
 * 判斷正整數是2的冪次
 */
int powOf2(int n)
{
    assert(n > 0);
    return !(n & (n-1));
}
復制代碼

2) 求一個整數二進制表示中1的個數

題: 求整數二進制表示中1的個數,如給定 N=6,它的二進制表示為 0110,二進制表示中1的個數為2。

解1: 一個自然的方法是將N和1按位與,然后將N除以2,直到N為0為止。該算法代碼如下:


int numOfBit1(int n)
{
    int cnt = 0;
    while (n) {
        if (n & 1)
            ++cnt;
        n >>= 1;
    }
    return cnt;
}
復制代碼

上面的代碼存在一個問題,如果輸入為負數的話,會導致死循環,為了解決負數的問題,如果使用的是JAVA,可以直接使用無符號右移操作符 >>> 來解決,如果是在C/C++里面,為了避免死循環,我們可以不右移輸入的數字i。首先 i & 1,判斷i的最低位是不是為1。接著把1左移一位得到2,再和i做與運算,就能判斷i的次高位是不是1...,這樣反復左移,每次都能判斷i的其中一位是不是1。

/**
 * 二進制表示中1的個數-解法1
 */
int numOfBit1(int n)
{
    int cnt = 0;
    unsigned int flag = 1;
    while (flag) {
        if(n & flag)
            cnt++;
    flag = flag &lt;&lt; 1;
    <span class="hljs-keyword">if</span> (flag &gt; n)
        <span class="hljs-built_in">break</span>;
}
<span class="hljs-built_in">return</span> cnt;

}
復制代碼

解2: 一個更好的解法是采用第一個題中類似的思路,每次 n&(n-1)就能把n中最右邊的1變為0,所以很容易求的1的總數目。

/**
 * 二進制表示中1的個數-解法2
 */
int numOfBit1WithCheck(int n)
{
    int cnt = 0;
    while (n != 0) {
        n = (n & (n-1));
        cnt++;
    }
    return cnt;
}
復制代碼

3) 反轉一個無符號整數的所有比特位

題: 給定一個無符號整數N,反轉該整數的所有比特位。例如有一個 8 位的數字 01101001,則反轉后變成 10010110,32 位的無符號整數的反轉與之類似。

解1: 自然的解法就是參照字符串反轉的算法,假設無符號整數有n位,先將第0位和第n位交換,然后是第1位和第n-1位交換...注意一點就是交換兩個位是可以通過異或操作 XOR 來實現的,因為 0 XOR 0 = 0, 1 XOR 1 = 0, 0 XOR 1 = 1, 1 XOR 0 = 1,使用 1 異或 0/1 會讓其值反轉。

/**
 * 反轉比特位
 */
uint swapBits(uint x, uint i, uint j)
{
    uint lo = ((x >> i) & 1);  // 取x的第i位的值
    uint hi = ((x >> j) & 1);  // 取x的第j位的值
    if (lo ^ hi) {             
        x ^= ((1U << i) | (1U << j)); // 如果第i位和第j位值不同,則交換i和j這兩個位的值,使用異或操作實現
    }
    return x;
}

/**

反轉整數比特位-仿字符串反轉
*/
uint reverseXOR(uint x)
{
uint n = sizeof(x) * 8;
uint i;
for (i = 0; i < n/2; i++) {
x = swapBits(x, i, n-i-1);
}
return x;
}
復制代碼

解2: 采用分治策略,首先交換數字x的相鄰位,然后再是 2 個位交換,然后是 4 個位交換...比如給定一個 8 位數 01101010,則首先交換相鄰位,變成 10 01 01 01,然后交換相鄰的 2 個位,得到 01 10 01 01,然后再是 4 個位交換,得到 0101 0110。總的時間復雜度為 O(lgN),其中 N 為整數的位數。下面給出一個反轉32位整數的代碼,如果整數是64位的可以以此類推。

/**
 * 反轉整數比特位-分治法
 */
uint reverseMask(uint x)
{
    assert(sizeof(x) == 4); // special case: only works for 4 bytes (32 bits).
    x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1);
    x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2);
    x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4);
    x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8);
    x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16);
    return x;
}
復制代碼

系列文章目錄

0. 數據結構和算法面試題系列—C指針、數組和結構體
1. 數據結構和算法面試題系列—字符串
2. 數據結構和算法面試題系列—鏈表
3. 數據結構和算法面試題系列—棧
4. 數據結構和算法面試題系列—二叉堆
5. 數據結構和算法面試題系列—二叉樹基礎
6. 數據結構和算法面試題系列—二叉樹面試題匯總
7. 數據結構和算法面試題系列—二分查找算法詳解
8. 數據結構和算法面試題系列—排序算法之基礎排序
9. 數據結構和算法面試題系列—排序算法之快速排序
10. 數據結構和算法面試題系列—隨機算法總結
11. 數據結構和算法面試題系列—遞歸算法總結
12. 數據結構和算法面試題系列—背包問題總結
13. 數據結構和算法面試題系列—數字題總結

其他

此外,在我 簡書的博客 上還整理有《docker相關技術筆記》、《MIT6.828操作系統學習筆記》、《python源碼剖析筆記》等文章,請大家指正。

參考資料

編程珠璣第二版第九章
旋轉數組的二分查找
《算法導論》
歸并排序(遞歸實現+非遞歸實現+自然合并排序) - geeker
《數據結構和算法-C語言實現》
Implement Rand10() Using Rand7() - LeetCode Articles
數學之美番外篇:快排為什么那樣快 – 劉未鵬 | Mind Hacks
網上整理的google面試題
宋勁松老師《Linux C編程》遞歸章節
數據結構與算法-C語言實現
背包問題九講
編程之美
具體數學
微軟、Google等面試題
Reverse Bits – LeetCode

原文地址:https://juejin.im/post/5bbc202de51d450e496826e7

總結

以上是生活随笔為你收集整理的那些年,面试中常见的数据结构基础和算法题(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

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

精品国产麻豆免费人成网站 | 久久久精品人妻久久影视 | 日日鲁鲁鲁夜夜爽爽狠狠 | 无码av免费一区二区三区试看 | 又大又紧又粉嫩18p少妇 | 亚洲a无码综合a国产av中文 | 国产又爽又黄又刺激的视频 | 美女扒开屁股让男人桶 | 欧美熟妇另类久久久久久多毛 | 国产又爽又黄又刺激的视频 | 日本乱人伦片中文三区 | 亚洲精品午夜国产va久久成人 | 亚洲a无码综合a国产av中文 | 粗大的内捧猛烈进出视频 | av人摸人人人澡人人超碰下载 | 性欧美大战久久久久久久 | 日韩精品无码免费一区二区三区 | 国产肉丝袜在线观看 | 国内少妇偷人精品视频免费 | 成年美女黄网站色大免费全看 | 精品少妇爆乳无码av无码专区 | 秋霞成人午夜鲁丝一区二区三区 | 国产绳艺sm调教室论坛 | 亚洲国产成人av在线观看 | 性欧美熟妇videofreesex | 伊在人天堂亚洲香蕉精品区 | 国产亚洲精品久久久闺蜜 | 久久这里只有精品视频9 | 少妇的肉体aa片免费 | 日本精品人妻无码免费大全 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 国产激情综合五月久久 | 久久精品人人做人人综合试看 | 奇米影视7777久久精品人人爽 | 亚洲色欲色欲欲www在线 | 欧美日韩综合一区二区三区 | 强开小婷嫩苞又嫩又紧视频 | 国产精品福利视频导航 | 久久精品国产精品国产精品污 | 激情综合激情五月俺也去 | 国内少妇偷人精品视频 | 玩弄少妇高潮ⅹxxxyw | 极品嫩模高潮叫床 | 欧美成人免费全部网站 | 国产精品亚洲lv粉色 | 日韩无套无码精品 | 亚洲色在线无码国产精品不卡 | 国产精品久久久久9999小说 | 97久久精品无码一区二区 | 午夜丰满少妇性开放视频 | 国产精品二区一区二区aⅴ污介绍 | 人人澡人摸人人添 | 欧美丰满少妇xxxx性 | 又大又硬又爽免费视频 | 精品无码av一区二区三区 | 一本无码人妻在中文字幕免费 | 亚洲男女内射在线播放 | 亚洲自偷自拍另类第1页 | 九月婷婷人人澡人人添人人爽 | 人妻人人添人妻人人爱 | 久久精品丝袜高跟鞋 | 色妞www精品免费视频 | 国产偷抇久久精品a片69 | 亚洲熟熟妇xxxx | 黑人巨大精品欧美一区二区 | 精品aⅴ一区二区三区 | 巨爆乳无码视频在线观看 | 国产美女精品一区二区三区 | 久久综合给合久久狠狠狠97色 | 无码av免费一区二区三区试看 | 扒开双腿吃奶呻吟做受视频 | 日本一区二区三区免费播放 | 强开小婷嫩苞又嫩又紧视频 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 成人无码视频在线观看网站 | 荫蒂添的好舒服视频囗交 | 久久亚洲精品成人无码 | 亚洲а∨天堂久久精品2021 | 国产精品久久精品三级 | 日韩精品无码一区二区中文字幕 | 色综合久久久无码中文字幕 | 97久久国产亚洲精品超碰热 | 大色综合色综合网站 | 亚洲大尺度无码无码专区 | 四十如虎的丰满熟妇啪啪 | 日韩亚洲欧美中文高清在线 | 亚洲成av人影院在线观看 | 奇米影视7777久久精品人人爽 | 18精品久久久无码午夜福利 | 国产熟妇另类久久久久 | 夜夜躁日日躁狠狠久久av | 国产香蕉97碰碰久久人人 | 熟妇激情内射com | 一本无码人妻在中文字幕免费 | 在线 国产 欧美 亚洲 天堂 | 亚洲精品综合一区二区三区在线 | av无码不卡在线观看免费 | 精品成在人线av无码免费看 | 最近免费中文字幕中文高清百度 | 成人一区二区免费视频 | 四十如虎的丰满熟妇啪啪 | 少妇人妻av毛片在线看 | 亚洲精品成人av在线 | 黑人大群体交免费视频 | 国产精品怡红院永久免费 | 国产9 9在线 | 中文 | 乱中年女人伦av三区 | 内射欧美老妇wbb | 麻豆国产人妻欲求不满谁演的 | 少妇厨房愉情理9仑片视频 | 国产 精品 自在自线 | 久久国产劲爆∧v内射 | 亚洲人成网站免费播放 | 亚洲日韩中文字幕在线播放 | 粗大的内捧猛烈进出视频 | 成年女人永久免费看片 | 大地资源网第二页免费观看 | 狠狠色欧美亚洲狠狠色www | 性欧美疯狂xxxxbbbb | 国产成人无码午夜视频在线观看 | 一区二区三区乱码在线 | 欧洲 | 久久亚洲日韩精品一区二区三区 | 亚洲熟悉妇女xxx妇女av | 狂野欧美性猛xxxx乱大交 | 奇米影视7777久久精品 | 青草青草久热国产精品 | 精品无码一区二区三区的天堂 | 在教室伦流澡到高潮hnp视频 | 麻豆国产人妻欲求不满 | 欧美放荡的少妇 | 亚洲 激情 小说 另类 欧美 | 人妻尝试又大又粗久久 | 亚洲精品国产第一综合99久久 | 免费人成网站视频在线观看 | 精品无码成人片一区二区98 | 性欧美videos高清精品 | 99久久久无码国产精品免费 | 亚洲а∨天堂久久精品2021 | 亚洲一区二区三区无码久久 | 国产精品va在线播放 | 国产亚洲欧美在线专区 | 国产亚洲tv在线观看 | 天天综合网天天综合色 | 纯爱无遮挡h肉动漫在线播放 | 2020最新国产自产精品 | 亚洲成a人一区二区三区 | 久久成人a毛片免费观看网站 | 久久zyz资源站无码中文动漫 | 人妻无码αv中文字幕久久琪琪布 | 麻豆果冻传媒2021精品传媒一区下载 | 99riav国产精品视频 | 国产色xx群视频射精 | www国产亚洲精品久久久日本 | 精品无码成人片一区二区98 | 国产成人精品优优av | 色婷婷久久一区二区三区麻豆 | 亚洲综合另类小说色区 | 亚洲精品久久久久avwww潮水 | 亚洲综合精品香蕉久久网 | 中文字幕色婷婷在线视频 | 偷窥村妇洗澡毛毛多 | 97夜夜澡人人爽人人喊中国片 | 无码国产色欲xxxxx视频 | 精品国产一区二区三区av 性色 | 免费看少妇作爱视频 | 国精产品一品二品国精品69xx | 国产一区二区三区四区五区加勒比 | 亚洲精品综合五月久久小说 | 色妞www精品免费视频 | 国产av一区二区精品久久凹凸 | 国产av无码专区亚洲awww | 欧美 亚洲 国产 另类 | 日本丰满护士爆乳xxxx | 亚洲娇小与黑人巨大交 | 亚洲色大成网站www | 亚洲一区二区三区无码久久 | 黑人粗大猛烈进出高潮视频 | 欧洲欧美人成视频在线 | 十八禁视频网站在线观看 | 3d动漫精品啪啪一区二区中 | 国产xxx69麻豆国语对白 | 一二三四社区在线中文视频 | 无码吃奶揉捏奶头高潮视频 | 99国产欧美久久久精品 | 国产成人无码区免费内射一片色欲 | 日韩精品一区二区av在线 | 在线精品国产一区二区三区 | 成人无码精品一区二区三区 | 免费无码一区二区三区蜜桃大 | 国产国语老龄妇女a片 | 亚洲色大成网站www | 国产suv精品一区二区五 | 无码国模国产在线观看 | 国产真人无遮挡作爱免费视频 | 性开放的女人aaa片 | 久久久久亚洲精品男人的天堂 | 极品尤物被啪到呻吟喷水 | 国产凸凹视频一区二区 | 国産精品久久久久久久 | 欧美国产日韩亚洲中文 | 成人亚洲精品久久久久软件 | 在线欧美精品一区二区三区 | 人人超人人超碰超国产 | 国产偷抇久久精品a片69 | 国产乱人偷精品人妻a片 | 国产三级久久久精品麻豆三级 | 国产成人一区二区三区在线观看 | 国产精品久久国产三级国 | 熟妇人妻中文av无码 | 亚洲成色www久久网站 | 欧美激情内射喷水高潮 | 2019午夜福利不卡片在线 | 国产性生交xxxxx无码 | 亚洲精品成人av在线 | 无码人妻出轨黑人中文字幕 | 欧美性猛交内射兽交老熟妇 | 久久 国产 尿 小便 嘘嘘 | 国产精品a成v人在线播放 | 丰满少妇高潮惨叫视频 | 婷婷六月久久综合丁香 | 欧美激情一区二区三区成人 | 亚洲天堂2017无码中文 | 亚无码乱人伦一区二区 | 18禁黄网站男男禁片免费观看 | 国产成人无码a区在线观看视频app | 亚洲精品国偷拍自产在线麻豆 | 亚洲国产精品一区二区第一页 | 蜜桃视频插满18在线观看 | 亚洲精品中文字幕乱码 | 精品国产一区二区三区av 性色 | 色狠狠av一区二区三区 | 国产热a欧美热a在线视频 | 色婷婷欧美在线播放内射 | 欧美变态另类xxxx | 亚洲 另类 在线 欧美 制服 | 亚洲无人区午夜福利码高清完整版 | 亚洲大尺度无码无码专区 | 中文字幕无码免费久久9一区9 | 大肉大捧一进一出好爽视频 | 99精品视频在线观看免费 | 日本xxxx色视频在线观看免费 | 无码免费一区二区三区 | 国产无遮挡又黄又爽又色 | 精品水蜜桃久久久久久久 | 欧美精品无码一区二区三区 | 少妇无码一区二区二三区 | 激情人妻另类人妻伦 | 日韩视频 中文字幕 视频一区 | 亚洲熟悉妇女xxx妇女av | 精品乱码久久久久久久 | 人人妻人人澡人人爽人人精品浪潮 | 国产乱人偷精品人妻a片 | 国产精品亚洲а∨无码播放麻豆 | 国内精品人妻无码久久久影院 | 日韩在线不卡免费视频一区 | 亚洲国产欧美日韩精品一区二区三区 | 亚洲国产av精品一区二区蜜芽 | 日本va欧美va欧美va精品 | 免费观看激色视频网站 | 亚洲中文字幕av在天堂 | 青春草在线视频免费观看 | av香港经典三级级 在线 | 亚洲日本在线电影 | 国产做国产爱免费视频 | 国产无套内射久久久国产 | 亚洲欧美日韩国产精品一区二区 | 欧美熟妇另类久久久久久不卡 | 久久精品人人做人人综合试看 | 婷婷色婷婷开心五月四房播播 | 欧美阿v高清资源不卡在线播放 | 一本久久伊人热热精品中文字幕 | 日日天干夜夜狠狠爱 | 日日摸天天摸爽爽狠狠97 | 国产精品人人妻人人爽 | 亚洲人成无码网www | 亚洲小说图区综合在线 | 无码人妻久久一区二区三区不卡 | 丰满少妇弄高潮了www | 99视频精品全部免费免费观看 | 欧美 日韩 人妻 高清 中文 | 亚洲精品无码人妻无码 | 国产内射爽爽大片视频社区在线 | 四虎国产精品免费久久 | 四十如虎的丰满熟妇啪啪 | 免费国产成人高清在线观看网站 | 亚洲va中文字幕无码久久不卡 | 国产成人午夜福利在线播放 | 精品国产福利一区二区 | 日本一本二本三区免费 | 无码一区二区三区在线 | 国产激情综合五月久久 | 好爽又高潮了毛片免费下载 | 午夜福利一区二区三区在线观看 | 国产成人无码av片在线观看不卡 | 欧美阿v高清资源不卡在线播放 | 熟妇人妻无码xxx视频 | 国产精品亚洲lv粉色 | 精品人妻中文字幕有码在线 | 人妻无码αv中文字幕久久琪琪布 | 性色欲网站人妻丰满中文久久不卡 | 午夜丰满少妇性开放视频 | 无码毛片视频一区二区本码 | 国产莉萝无码av在线播放 | 夜夜躁日日躁狠狠久久av | 亚洲人成人无码网www国产 | 久久久亚洲欧洲日产国码αv | 国产精品理论片在线观看 | 国产精品久久精品三级 | 曰本女人与公拘交酡免费视频 | 国产精品99久久精品爆乳 | 又粗又大又硬又长又爽 | 国产精品内射视频免费 | 国产精品va在线观看无码 | 大地资源中文第3页 | 亚洲精品综合五月久久小说 | 午夜无码人妻av大片色欲 | 性色欲网站人妻丰满中文久久不卡 | 免费观看激色视频网站 | 午夜肉伦伦影院 | 亚洲欧美综合区丁香五月小说 | а√资源新版在线天堂 | 亚洲成av人片在线观看无码不卡 | 国产精品免费大片 | 日本欧美一区二区三区乱码 | 国产亚洲日韩欧美另类第八页 | 色欲综合久久中文字幕网 | 黑人大群体交免费视频 | 最新国产乱人伦偷精品免费网站 | 亚洲日韩乱码中文无码蜜桃臀网站 | 在线观看国产一区二区三区 | 最新版天堂资源中文官网 | 国产电影无码午夜在线播放 | 久久精品女人天堂av免费观看 | 欧美怡红院免费全部视频 | a在线观看免费网站大全 | 久久无码专区国产精品s | 国产精品沙发午睡系列 | 丰满护士巨好爽好大乳 | 久久99精品久久久久久 | 国产av久久久久精东av | 无码乱肉视频免费大全合集 | 老司机亚洲精品影院无码 | 亚洲区小说区激情区图片区 | 精品国产国产综合精品 | 伊人色综合久久天天小片 | 捆绑白丝粉色jk震动捧喷白浆 | 日韩精品a片一区二区三区妖精 | 精品国产一区二区三区av 性色 | 久久精品人人做人人综合试看 | 免费看少妇作爱视频 | 国产熟女一区二区三区四区五区 | aa片在线观看视频在线播放 | 国产精品久久久久久久影院 | 国产超碰人人爽人人做人人添 | 一区二区三区乱码在线 | 欧洲 | 亚洲另类伦春色综合小说 | 人妻天天爽夜夜爽一区二区 | 亚洲成av人影院在线观看 | 少妇性俱乐部纵欲狂欢电影 | 在线欧美精品一区二区三区 | 少妇被粗大的猛进出69影院 | 久久99久久99精品中文字幕 | 天天拍夜夜添久久精品 | 美女极度色诱视频国产 | 国产精品美女久久久久av爽李琼 | 学生妹亚洲一区二区 | 真人与拘做受免费视频一 | 色狠狠av一区二区三区 | 精品无码一区二区三区爱欲 | 亚洲 a v无 码免 费 成 人 a v | 国产精品无码mv在线观看 | 丰满妇女强制高潮18xxxx | 国产情侣作爱视频免费观看 | 久久精品国产99精品亚洲 | 无码人妻出轨黑人中文字幕 | 未满小14洗澡无码视频网站 | 欧美人与善在线com | 在线成人www免费观看视频 | 少妇性荡欲午夜性开放视频剧场 | 图片区 小说区 区 亚洲五月 | 麻豆成人精品国产免费 | 大乳丰满人妻中文字幕日本 | 51国偷自产一区二区三区 | 国产精品无码一区二区三区不卡 | 亚洲色大成网站www | 国产亚洲精品久久久久久 | 午夜性刺激在线视频免费 | 奇米影视7777久久精品 | 鲁大师影院在线观看 | 国产区女主播在线观看 | 国产精品亚洲lv粉色 | 高清不卡一区二区三区 | 久久伊人色av天堂九九小黄鸭 | 欧美自拍另类欧美综合图片区 | 男人和女人高潮免费网站 | 久久久久久a亚洲欧洲av冫 | 天堂а√在线地址中文在线 | 在线天堂新版最新版在线8 | 丰满肥臀大屁股熟妇激情视频 | 曰本女人与公拘交酡免费视频 | 1000部啪啪未满十八勿入下载 | 噜噜噜亚洲色成人网站 | 色欲人妻aaaaaaa无码 | 超碰97人人做人人爱少妇 | 色偷偷人人澡人人爽人人模 | 亚洲 a v无 码免 费 成 人 a v | 无码国产激情在线观看 | 久久久久久久久888 | 激情人妻另类人妻伦 | 又粗又大又硬又长又爽 | 国产偷抇久久精品a片69 | 天下第一社区视频www日本 | 人人爽人人澡人人高潮 | 精品日本一区二区三区在线观看 | 一个人看的视频www在线 | 亚洲色大成网站www | 在线观看欧美一区二区三区 | 激情综合激情五月俺也去 | 婷婷五月综合激情中文字幕 | 亚洲精品久久久久久久久久久 | a在线观看免费网站大全 | 国产一区二区不卡老阿姨 | 成人一区二区免费视频 | 无码人妻出轨黑人中文字幕 | 亚洲国产精品久久久天堂 | 青青草原综合久久大伊人精品 | 日本护士毛茸茸高潮 | 老司机亚洲精品影院无码 | 欧美日本精品一区二区三区 | 给我免费的视频在线观看 | 国产亚洲视频中文字幕97精品 | 国产成人精品无码播放 | 亚洲人成网站在线播放942 | 欧美日韩视频无码一区二区三 | 色偷偷人人澡人人爽人人模 | 免费看男女做好爽好硬视频 | 永久黄网站色视频免费直播 | 日韩精品无码免费一区二区三区 | 国产热a欧美热a在线视频 | 午夜性刺激在线视频免费 | 精品国精品国产自在久国产87 | 国产舌乚八伦偷品w中 | 国产高潮视频在线观看 | 亚洲熟妇色xxxxx亚洲 | 亚洲熟悉妇女xxx妇女av | 亚洲国产一区二区三区在线观看 | 亚洲一区二区三区四区 | 久9re热视频这里只有精品 | 色一情一乱一伦一区二区三欧美 | 久久综合久久自在自线精品自 | 亚洲精品一区二区三区四区五区 | 国产口爆吞精在线视频 | 国产农村妇女高潮大叫 | 好屌草这里只有精品 | 亲嘴扒胸摸屁股激烈网站 | 久久久久亚洲精品中文字幕 | 成人影院yy111111在线观看 | 国产亚洲人成a在线v网站 | 波多野结衣av一区二区全免费观看 | 亚洲毛片av日韩av无码 | 久久人妻内射无码一区三区 | 久久久久成人精品免费播放动漫 | 国内精品久久毛片一区二区 | 国产亚洲精品久久久久久大师 | 婷婷综合久久中文字幕蜜桃三电影 | 久久99精品久久久久久动态图 | 亚洲欧美日韩成人高清在线一区 | 精品无码一区二区三区爱欲 | 国产亚洲tv在线观看 | а天堂中文在线官网 | 国产亚洲视频中文字幕97精品 | 中文字幕人妻丝袜二区 | 亚洲中文字幕在线观看 | 亚洲精品综合五月久久小说 | 国产99久久精品一区二区 | 国产乱码精品一品二品 | 国内丰满熟女出轨videos | 国产热a欧美热a在线视频 | 久热国产vs视频在线观看 | 亚洲熟妇色xxxxx欧美老妇 | 欧洲熟妇精品视频 | 久久精品国产精品国产精品污 | 一个人看的www免费视频在线观看 | 在线精品亚洲一区二区 | 国产精品久久久久无码av色戒 | 亚洲熟熟妇xxxx | 欧美真人作爱免费视频 | 97无码免费人妻超级碰碰夜夜 | 亚洲色欲色欲欲www在线 | 国内精品九九久久久精品 | 99麻豆久久久国产精品免费 | 免费人成在线观看网站 | 人妻体内射精一区二区三四 | 少妇性l交大片欧洲热妇乱xxx | 免费国产成人高清在线观看网站 | 男女超爽视频免费播放 | 久久久久亚洲精品中文字幕 | 久久精品国产大片免费观看 | 午夜福利一区二区三区在线观看 | 日韩av无码一区二区三区 | 国产av久久久久精东av | 亚洲日韩av片在线观看 | www成人国产高清内射 | 在线视频网站www色 | 嫩b人妻精品一区二区三区 | 任你躁国产自任一区二区三区 | 沈阳熟女露脸对白视频 | 亚洲 a v无 码免 费 成 人 a v | 成人综合网亚洲伊人 | 免费男性肉肉影院 | 亚洲国产av精品一区二区蜜芽 | 国产人妖乱国产精品人妖 | 亚洲伊人久久精品影院 | 久久国产精品偷任你爽任你 | 久久久中文字幕日本无吗 | 一区二区传媒有限公司 | 久久精品中文字幕大胸 | 久久精品国产精品国产精品污 | 中文字幕乱码人妻二区三区 | 一本久道高清无码视频 | 久久99精品国产麻豆蜜芽 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 在线欧美精品一区二区三区 | а天堂中文在线官网 | 在线观看国产午夜福利片 | 亚洲成av人综合在线观看 | 国产女主播喷水视频在线观看 | 亚洲aⅴ无码成人网站国产app | 亚洲小说图区综合在线 | 国产激情艳情在线看视频 | 影音先锋中文字幕无码 | 人妻尝试又大又粗久久 | 中文久久乱码一区二区 | 一本久久a久久精品亚洲 | 青青久在线视频免费观看 | 日韩亚洲欧美中文高清在线 | 久久国语露脸国产精品电影 | 麻豆精品国产精华精华液好用吗 | 99久久久国产精品无码免费 | 人妻无码αv中文字幕久久琪琪布 | 国内揄拍国内精品少妇国语 | 日本www一道久久久免费榴莲 | 日本一区二区三区免费播放 | 日本熟妇人妻xxxxx人hd | 国产三级久久久精品麻豆三级 | 97夜夜澡人人爽人人喊中国片 | 国产激情无码一区二区 | 人妻互换免费中文字幕 | 性欧美熟妇videofreesex | 国产精品第一国产精品 | 国产片av国语在线观看 | 精品乱码久久久久久久 | 国产在线aaa片一区二区99 | 中文字幕 亚洲精品 第1页 | 欧美人与禽猛交狂配 | 99国产欧美久久久精品 | 高潮毛片无遮挡高清免费视频 | 帮老师解开蕾丝奶罩吸乳网站 | 亚洲男女内射在线播放 | 日韩 欧美 动漫 国产 制服 | 自拍偷自拍亚洲精品10p | 亚洲日本一区二区三区在线 | 国产9 9在线 | 中文 | 亚洲国产精品成人久久蜜臀 | 精品亚洲韩国一区二区三区 | 亚洲乱亚洲乱妇50p | 日本大乳高潮视频在线观看 | 极品尤物被啪到呻吟喷水 | 四十如虎的丰满熟妇啪啪 | 在线a亚洲视频播放在线观看 | 久久久av男人的天堂 | 日韩精品一区二区av在线 | 亚洲国产欧美日韩精品一区二区三区 | 一本大道伊人av久久综合 | 国产av无码专区亚洲awww | 国产精品爱久久久久久久 | 男人的天堂2018无码 | 又粗又大又硬又长又爽 | 国产熟女一区二区三区四区五区 | 麻豆国产人妻欲求不满谁演的 | 国产精品无套呻吟在线 | 国产亚洲精品久久久闺蜜 | 131美女爱做视频 | 色妞www精品免费视频 | 秋霞特色aa大片 | 久久久精品人妻久久影视 | 天天摸天天透天天添 | 国产精品亚洲а∨无码播放麻豆 | 免费乱码人妻系列无码专区 | 日本饥渴人妻欲求不满 | 精品国产福利一区二区 | 狂野欧美性猛交免费视频 | 丰满人妻翻云覆雨呻吟视频 | 天堂久久天堂av色综合 | 日日摸夜夜摸狠狠摸婷婷 | 午夜无码人妻av大片色欲 | 久久精品无码一区二区三区 | 久久亚洲精品成人无码 | 玩弄少妇高潮ⅹxxxyw | 精品厕所偷拍各类美女tp嘘嘘 | 撕开奶罩揉吮奶头视频 | 亚洲伊人久久精品影院 | 男女超爽视频免费播放 | 国产熟妇另类久久久久 | 两性色午夜视频免费播放 | 97色伦图片97综合影院 | 日本一本二本三区免费 | 中文精品无码中文字幕无码专区 | 99久久久无码国产aaa精品 | 国产网红无码精品视频 | 国产精品无码成人午夜电影 | 好爽又高潮了毛片免费下载 | 学生妹亚洲一区二区 | 国色天香社区在线视频 | 亚洲国产精品美女久久久久 | 18无码粉嫩小泬无套在线观看 | 午夜性刺激在线视频免费 | 色婷婷欧美在线播放内射 | 精品乱码久久久久久久 | 国产超级va在线观看视频 | 亚洲自偷精品视频自拍 | 精品久久久久香蕉网 | 51国偷自产一区二区三区 | 亚洲成色www久久网站 | 欧美人与动性行为视频 | 内射爽无广熟女亚洲 | 欧美变态另类xxxx | 国产精品免费大片 | 国内精品人妻无码久久久影院蜜桃 | 亚洲色在线无码国产精品不卡 | 国精品人妻无码一区二区三区蜜柚 | 国产精品亚洲一区二区三区喷水 | 人人妻人人澡人人爽精品欧美 | 亚洲精品成a人在线观看 | 亚洲成av人在线观看网址 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲自偷精品视频自拍 | 真人与拘做受免费视频 | 7777奇米四色成人眼影 | 精品少妇爆乳无码av无码专区 | 色婷婷av一区二区三区之红樱桃 | 色 综合 欧美 亚洲 国产 | 婷婷五月综合激情中文字幕 | 亚洲中文字幕在线无码一区二区 | 国产精品无码一区二区桃花视频 | 少妇无码av无码专区在线观看 | 东京无码熟妇人妻av在线网址 | 亚洲精品国产品国语在线观看 | 夜夜高潮次次欢爽av女 | 强伦人妻一区二区三区视频18 | 日韩视频 中文字幕 视频一区 | 亚洲狠狠婷婷综合久久 | 日韩亚洲欧美精品综合 | 成人精品视频一区二区 | 性啪啪chinese东北女人 | 亚洲毛片av日韩av无码 | 国产电影无码午夜在线播放 | 狠狠色色综合网站 | 窝窝午夜理论片影院 | 亚洲国产精品毛片av不卡在线 | 欧洲美熟女乱又伦 | 午夜肉伦伦影院 | 国产精品久久久久无码av色戒 | 欧美老熟妇乱xxxxx | 国产午夜精品一区二区三区嫩草 | 成人av无码一区二区三区 | 日韩精品无码一本二本三本色 | 人妻体内射精一区二区三四 | 人妻aⅴ无码一区二区三区 | 亚洲日韩乱码中文无码蜜桃臀网站 | 婷婷五月综合缴情在线视频 | 亚洲欧洲无卡二区视頻 | 蜜桃av抽搐高潮一区二区 | 欧美丰满少妇xxxx性 | 天下第一社区视频www日本 | 又湿又紧又大又爽a视频国产 | 宝宝好涨水快流出来免费视频 | 久久久久se色偷偷亚洲精品av | 日本精品人妻无码77777 天堂一区人妻无码 | 精品乱码久久久久久久 | 中文精品无码中文字幕无码专区 | 精品熟女少妇av免费观看 | 国产成人无码av片在线观看不卡 | 成人一在线视频日韩国产 | 国产偷国产偷精品高清尤物 | 天堂无码人妻精品一区二区三区 | 亚洲中文字幕无码一久久区 | 中文字幕av无码一区二区三区电影 | 无码吃奶揉捏奶头高潮视频 | 国产内射爽爽大片视频社区在线 | 国产手机在线αⅴ片无码观看 | 日本大乳高潮视频在线观看 | 免费人成在线观看网站 | 午夜精品久久久内射近拍高清 | 激情亚洲一区国产精品 | 久久久久亚洲精品男人的天堂 | 国产精品18久久久久久麻辣 | 日韩精品无码一本二本三本色 | 日日鲁鲁鲁夜夜爽爽狠狠 | 国产熟妇高潮叫床视频播放 | 欧美激情一区二区三区成人 | 男女下面进入的视频免费午夜 | 亚洲色成人中文字幕网站 | 日韩欧美群交p片內射中文 | 国产乱子伦视频在线播放 | 亚洲精品无码国产 | 人人妻人人澡人人爽欧美一区 | 日本大香伊一区二区三区 | 曰本女人与公拘交酡免费视频 | 国内精品久久毛片一区二区 | 欧美性猛交内射兽交老熟妇 | 精品一区二区三区无码免费视频 | 国产69精品久久久久app下载 | 成在人线av无码免观看麻豆 | www一区二区www免费 | 精品人妻av区 | 免费看少妇作爱视频 | 久久成人a毛片免费观看网站 | 性欧美牲交xxxxx视频 | 成人欧美一区二区三区黑人免费 | 野狼第一精品社区 | 99久久久无码国产aaa精品 | 欧美激情综合亚洲一二区 | 荫蒂被男人添的好舒服爽免费视频 | 青草青草久热国产精品 | 免费播放一区二区三区 | 久久无码人妻影院 | 欧美 日韩 亚洲 在线 | 妺妺窝人体色www在线小说 | 18禁止看的免费污网站 | 国产乱人偷精品人妻a片 | 国产人成高清在线视频99最全资源 | 高潮毛片无遮挡高清免费 | 国产人妻精品午夜福利免费 | 男女猛烈xx00免费视频试看 | 中文字幕 亚洲精品 第1页 | 亚洲一区二区三区四区 | 日韩精品a片一区二区三区妖精 | 蜜桃臀无码内射一区二区三区 | 国产精品丝袜黑色高跟鞋 | 97夜夜澡人人爽人人喊中国片 | 亚洲综合色区中文字幕 | 天天摸天天透天天添 | 娇妻被黑人粗大高潮白浆 | 午夜理论片yy44880影院 | 日韩av无码一区二区三区不卡 | 少妇人妻偷人精品无码视频 | 精品久久久中文字幕人妻 | 丰满人妻被黑人猛烈进入 | 永久免费观看国产裸体美女 | 国产日产欧产精品精品app | 国产农村妇女高潮大叫 | 美女毛片一区二区三区四区 | 精品国偷自产在线 | 久久久国产精品无码免费专区 | 亚洲综合精品香蕉久久网 | 人妻少妇被猛烈进入中文字幕 | 妺妺窝人体色www在线小说 | 欧美自拍另类欧美综合图片区 | 欧美亚洲日韩国产人成在线播放 | 精品无码av一区二区三区 | 亚洲日本一区二区三区在线 | 精品一区二区三区波多野结衣 | 欧美激情内射喷水高潮 | 18禁黄网站男男禁片免费观看 | 强奷人妻日本中文字幕 | 亚洲日韩乱码中文无码蜜桃臀网站 | 亚洲精品成人av在线 | 99久久无码一区人妻 | 久久国产自偷自偷免费一区调 | 久久久久久国产精品无码下载 | 色一情一乱一伦一视频免费看 | 欧美成人高清在线播放 | 国产精品人人爽人人做我的可爱 | 欧美乱妇无乱码大黄a片 | 国产午夜精品一区二区三区嫩草 | 波多野结衣av一区二区全免费观看 | 精品无码国产自产拍在线观看蜜 | 蜜桃无码一区二区三区 | 天天拍夜夜添久久精品 | 亚洲精品一区二区三区四区五区 | 国产人妻精品午夜福利免费 | 国产精品美女久久久网av | 天干天干啦夜天干天2017 | 欧美黑人巨大xxxxx | 久久99国产综合精品 | 中国女人内谢69xxxxxa片 | 亚洲日韩av一区二区三区四区 | 久久亚洲国产成人精品性色 | 国产亲子乱弄免费视频 | 精品国产一区二区三区四区 | 麻豆av传媒蜜桃天美传媒 | аⅴ资源天堂资源库在线 | 夜先锋av资源网站 | 久久久久av无码免费网 | 亚洲国产午夜精品理论片 | 亚洲日韩乱码中文无码蜜桃臀网站 | 成人精品一区二区三区中文字幕 | 国产亚洲tv在线观看 | www一区二区www免费 | 精品无码一区二区三区的天堂 | 亚洲色大成网站www国产 | 国产人妻久久精品二区三区老狼 | 人妻少妇精品无码专区二区 | 日本丰满护士爆乳xxxx | 丰满少妇人妻久久久久久 | 成人免费视频在线观看 | 精品无码一区二区三区的天堂 | 国产乱人无码伦av在线a | 无码精品国产va在线观看dvd | 在线播放免费人成毛片乱码 | 久久亚洲中文字幕无码 | 成 人 网 站国产免费观看 | 国产在热线精品视频 | 99精品视频在线观看免费 | 成年美女黄网站色大免费全看 | 天堂а√在线地址中文在线 | 午夜精品一区二区三区的区别 | 少妇性l交大片欧洲热妇乱xxx | 正在播放东北夫妻内射 | 国产69精品久久久久app下载 | 亚洲欧美日韩综合久久久 | 久久99国产综合精品 | 亚洲国产精品成人久久蜜臀 | 99riav国产精品视频 | 乱人伦人妻中文字幕无码 | 在线精品国产一区二区三区 | 两性色午夜视频免费播放 | 啦啦啦www在线观看免费视频 | 奇米影视888欧美在线观看 | 成人综合网亚洲伊人 | 天天拍夜夜添久久精品 | 无遮挡国产高潮视频免费观看 | 国产精品怡红院永久免费 | 国产无遮挡又黄又爽又色 | 青草青草久热国产精品 | 欧美丰满老熟妇xxxxx性 | 少妇无套内谢久久久久 | 麻豆人妻少妇精品无码专区 | 波多野结衣高清一区二区三区 | 亚洲中文字幕久久无码 | 成人一区二区免费视频 | 一个人免费观看的www视频 | 三上悠亚人妻中文字幕在线 | 亚洲国产精品无码久久久久高潮 | 国产精品美女久久久 | 又大又硬又黄的免费视频 | 亚洲一区二区三区偷拍女厕 | 窝窝午夜理论片影院 | 精品久久久久久人妻无码中文字幕 | 欧美熟妇另类久久久久久不卡 | 久久www免费人成人片 | 思思久久99热只有频精品66 | √8天堂资源地址中文在线 | 日韩亚洲欧美精品综合 | 精品水蜜桃久久久久久久 | 亚洲精品国偷拍自产在线观看蜜桃 | 在线观看国产午夜福利片 | 久久精品一区二区三区四区 | 亚洲成av人片天堂网无码】 | 亚洲日韩av一区二区三区中文 | 曰韩少妇内射免费播放 | 未满小14洗澡无码视频网站 | 男女爱爱好爽视频免费看 | 天天拍夜夜添久久精品 | 国产免费无码一区二区视频 | 国产精品高潮呻吟av久久4虎 | 色欲人妻aaaaaaa无码 | 亚洲人成网站色7799 | 国产电影无码午夜在线播放 | 亚无码乱人伦一区二区 | 国产精品久久久久影院嫩草 | 少妇性俱乐部纵欲狂欢电影 | 国内精品久久毛片一区二区 | 影音先锋中文字幕无码 | 露脸叫床粗话东北少妇 | 又紧又大又爽精品一区二区 | 无人区乱码一区二区三区 | 免费无码肉片在线观看 | 人妻夜夜爽天天爽三区 | 欧美丰满老熟妇xxxxx性 | 亚洲色大成网站www | 2020最新国产自产精品 | 强开小婷嫩苞又嫩又紧视频 | 丰腴饱满的极品熟妇 | 亚洲天堂2017无码 | 免费无码肉片在线观看 | 夜精品a片一区二区三区无码白浆 | 久热国产vs视频在线观看 | 国产免费久久久久久无码 | 久久综合香蕉国产蜜臀av | 午夜丰满少妇性开放视频 | 久久久亚洲欧洲日产国码αv | 狠狠cao日日穞夜夜穞av | 精品久久久久久人妻无码中文字幕 | 久久精品人妻少妇一区二区三区 | 欧美黑人巨大xxxxx | 国产热a欧美热a在线视频 | 国产另类ts人妖一区二区 | 日韩在线不卡免费视频一区 | 一本大道伊人av久久综合 | 亚洲熟妇自偷自拍另类 | 男人的天堂2018无码 | 国产色xx群视频射精 | 国产精品久久国产精品99 | 亚欧洲精品在线视频免费观看 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产一区二区三区四区五区加勒比 | 成人一区二区免费视频 | 日本护士xxxxhd少妇 | 激情内射亚州一区二区三区爱妻 | 日日橹狠狠爱欧美视频 | √天堂中文官网8在线 | 亚洲成av人片天堂网无码】 | 欧美成人午夜精品久久久 | 久久久久久亚洲精品a片成人 | 亚洲成av人片天堂网无码】 | 四虎永久在线精品免费网址 | 久久这里只有精品视频9 | 精品人妻人人做人人爽 | 成人片黄网站色大片免费观看 | 午夜男女很黄的视频 | 大屁股大乳丰满人妻 | 成在人线av无码免费 | 国内精品九九久久久精品 | 国产亚洲tv在线观看 | 人妻无码αv中文字幕久久琪琪布 | 国产激情艳情在线看视频 | 俺去俺来也在线www色官网 | 曰韩少妇内射免费播放 | 少妇厨房愉情理9仑片视频 | 久久综合给合久久狠狠狠97色 | 亚洲综合久久一区二区 | 欧美怡红院免费全部视频 | 2020久久超碰国产精品最新 | 国产综合色产在线精品 | 亚洲色大成网站www国产 | 精品人人妻人人澡人人爽人人 | 免费网站看v片在线18禁无码 | 欧美性黑人极品hd | 国产乱人伦av在线无码 | 久久综合给合久久狠狠狠97色 | 老子影院午夜精品无码 | 漂亮人妻洗澡被公强 日日躁 | 国产精品美女久久久久av爽李琼 | 精品水蜜桃久久久久久久 | 成在人线av无码免观看麻豆 | 亚洲欧美国产精品久久 | 日本丰满熟妇videos | 亚洲人成无码网www | 国产精品久久国产精品99 | 在线а√天堂中文官网 | av无码电影一区二区三区 | 精品偷自拍另类在线观看 | 妺妺窝人体色www在线小说 | 国产区女主播在线观看 | 99精品视频在线观看免费 | 精品成在人线av无码免费看 | 国产乱人偷精品人妻a片 | 国产精品无码一区二区三区不卡 | 男女爱爱好爽视频免费看 | 国产色视频一区二区三区 | 日本护士毛茸茸高潮 | 小泽玛莉亚一区二区视频在线 | 国产手机在线αⅴ片无码观看 | 日欧一片内射va在线影院 | 天天做天天爱天天爽综合网 | 亚洲精品鲁一鲁一区二区三区 | 综合激情五月综合激情五月激情1 | 欧美高清在线精品一区 | 欧美亚洲国产一区二区三区 | 青青青爽视频在线观看 | 2019nv天堂香蕉在线观看 | 宝宝好涨水快流出来免费视频 | 97资源共享在线视频 | 日本一卡2卡3卡四卡精品网站 | 久久久久se色偷偷亚洲精品av | 天堂一区人妻无码 | 99麻豆久久久国产精品免费 | 国产精品亚洲一区二区三区喷水 | 欧美变态另类xxxx | 精品国精品国产自在久国产87 | av在线亚洲欧洲日产一区二区 | 人人妻人人藻人人爽欧美一区 | 色 综合 欧美 亚洲 国产 | 天下第一社区视频www日本 | 中文无码精品a∨在线观看不卡 | 国产肉丝袜在线观看 | 精品久久久久久人妻无码中文字幕 | 亚洲人成网站色7799 | 国产av人人夜夜澡人人爽麻豆 | 呦交小u女精品视频 | 久青草影院在线观看国产 | 国产xxx69麻豆国语对白 | 性色欲网站人妻丰满中文久久不卡 | 麻花豆传媒剧国产免费mv在线 | 曰韩少妇内射免费播放 | 国产色精品久久人妻 | 久久人人爽人人人人片 | 3d动漫精品啪啪一区二区中 | 国产xxx69麻豆国语对白 | 麻豆md0077饥渴少妇 | 在线播放无码字幕亚洲 | 国产成人午夜福利在线播放 | 草草网站影院白丝内射 | 亚洲人成网站免费播放 | 国产欧美亚洲精品a | 精品一二三区久久aaa片 | 红桃av一区二区三区在线无码av | 蜜桃视频插满18在线观看 | 精品一区二区不卡无码av | 久久综合九色综合97网 | 又大又黄又粗又爽的免费视频 | 国产人妖乱国产精品人妖 | 亚洲理论电影在线观看 | 亚洲成a人片在线观看日本 | 国产亚洲视频中文字幕97精品 | 久久综合网欧美色妞网 | 亚洲色成人中文字幕网站 | 无套内谢的新婚少妇国语播放 | 99久久久无码国产精品免费 | 激情人妻另类人妻伦 | 亚洲人成人无码网www国产 | www成人国产高清内射 | 18禁黄网站男男禁片免费观看 | 夜先锋av资源网站 | 无码人妻av免费一区二区三区 | 日本va欧美va欧美va精品 | 东京一本一道一二三区 | 欧美熟妇另类久久久久久不卡 | 欧美zoozzooz性欧美 | 欧美人与善在线com | 亚洲国产精品久久久久久 | 国产人妻久久精品二区三区老狼 | 午夜理论片yy44880影院 | 狠狠亚洲超碰狼人久久 | 久久www免费人成人片 | 无码成人精品区在线观看 | 国产成人精品无码播放 | 色综合久久久无码中文字幕 | 国産精品久久久久久久 | 天堂在线观看www | 亚洲一区av无码专区在线观看 | 中文字幕无码视频专区 | 久久久久久亚洲精品a片成人 | 色综合久久久无码网中文 | 在线观看欧美一区二区三区 | 扒开双腿吃奶呻吟做受视频 | 一本久久a久久精品亚洲 | 国产两女互慰高潮视频在线观看 | 中文字幕无码av激情不卡 | 人人澡人人透人人爽 | 欧美性生交xxxxx久久久 | 搡女人真爽免费视频大全 | 久久综合色之久久综合 | 黑森林福利视频导航 | 成熟妇人a片免费看网站 | 伊在人天堂亚洲香蕉精品区 | 无码中文字幕色专区 | 自拍偷自拍亚洲精品被多人伦好爽 | 性色av无码免费一区二区三区 | 日日摸天天摸爽爽狠狠97 | 人人澡人人妻人人爽人人蜜桃 | 国产成人午夜福利在线播放 | 亚洲综合无码久久精品综合 | 无码精品国产va在线观看dvd | 蜜桃av抽搐高潮一区二区 | 欧美刺激性大交 | 成人av无码一区二区三区 | 精品少妇爆乳无码av无码专区 | 亚洲日韩中文字幕在线播放 | 国产黄在线观看免费观看不卡 | 久久久久久久久蜜桃 | 女人被爽到呻吟gif动态图视看 | 精品久久久久久亚洲精品 | 亚洲中文字幕无码一久久区 | 97久久精品无码一区二区 | 亚洲国产精品无码一区二区三区 | 精品夜夜澡人妻无码av蜜桃 | 2020久久超碰国产精品最新 | 国产乱人伦偷精品视频 | 欧美日韩一区二区免费视频 | 午夜理论片yy44880影院 | 国产免费观看黄av片 | 午夜精品一区二区三区在线观看 | 国产一区二区三区四区五区加勒比 | 精品人妻人人做人人爽夜夜爽 | 无码av中文字幕免费放 | 少妇愉情理伦片bd | 日本熟妇大屁股人妻 | 又大又硬又爽免费视频 | 强开小婷嫩苞又嫩又紧视频 | 国产又爽又黄又刺激的视频 | 夫妻免费无码v看片 | 日韩精品a片一区二区三区妖精 | 超碰97人人做人人爱少妇 | 亚洲国产精品无码久久久久高潮 | 76少妇精品导航 | 图片区 小说区 区 亚洲五月 | 少妇愉情理伦片bd | 人妻少妇精品无码专区二区 | 久久亚洲中文字幕精品一区 | 小泽玛莉亚一区二区视频在线 | 亚洲天堂2017无码 | 中文字幕乱码人妻二区三区 | 国色天香社区在线视频 | 中文字幕无码日韩专区 | 内射欧美老妇wbb | 在线播放免费人成毛片乱码 | 精品乱码久久久久久久 | 国产口爆吞精在线视频 | 99精品无人区乱码1区2区3区 | 丰满少妇女裸体bbw | 欧美国产日韩亚洲中文 | 一本一道久久综合久久 | 欧美人与禽猛交狂配 | www国产亚洲精品久久网站 | 国产偷国产偷精品高清尤物 | 18精品久久久无码午夜福利 | 成人无码视频在线观看网站 | av在线亚洲欧洲日产一区二区 | 黑森林福利视频导航 | 爱做久久久久久 | 欧美真人作爱免费视频 | 亚洲 高清 成人 动漫 | 国产成人人人97超碰超爽8 | 欧美午夜特黄aaaaaa片 | 曰本女人与公拘交酡免费视频 | 久久久久久久久蜜桃 | 天天躁日日躁狠狠躁免费麻豆 | 精品国产精品久久一区免费式 | 风流少妇按摩来高潮 | 人人妻人人澡人人爽欧美一区九九 | 午夜无码人妻av大片色欲 | 国产精品高潮呻吟av久久4虎 | 亚洲一区二区三区 | 国产极品美女高潮无套在线观看 | 精品久久久无码人妻字幂 | 久久97精品久久久久久久不卡 | 色婷婷久久一区二区三区麻豆 | 亚洲国产精品久久久久久 | 久久99精品久久久久婷婷 | 国产激情无码一区二区app | 强伦人妻一区二区三区视频18 | 亚洲の无码国产の无码影院 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 一本久道久久综合狠狠爱 | 久久久久久久女国产乱让韩 | 久久久精品成人免费观看 | 亚洲高清偷拍一区二区三区 | 久久精品99久久香蕉国产色戒 | 丝袜足控一区二区三区 | 亚洲一区二区三区国产精华液 | 亚洲色无码一区二区三区 | 久久99久久99精品中文字幕 | 国产精品久久久久久无码 | 国产69精品久久久久app下载 | 日本一区二区更新不卡 | 国产两女互慰高潮视频在线观看 | 久久精品人人做人人综合试看 | 久久久精品人妻久久影视 | 色综合久久网 | 国产精品免费大片 | 澳门永久av免费网站 | 久久综合九色综合97网 | 久久久久久a亚洲欧洲av冫 | 精品无码一区二区三区的天堂 | 国产精品成人av在线观看 | 国产精品va在线观看无码 | 午夜精品一区二区三区的区别 | 老司机亚洲精品影院无码 | 一区二区传媒有限公司 | 97精品国产97久久久久久免费 | 亚洲国产高清在线观看视频 | а√资源新版在线天堂 | 国模大胆一区二区三区 | 成人免费视频视频在线观看 免费 | 牲欲强的熟妇农村老妇女视频 | 亚洲爆乳大丰满无码专区 | 一本久道久久综合婷婷五月 | 亚洲精品欧美二区三区中文字幕 | 色综合视频一区二区三区 | 亚洲の无码国产の无码影院 | 国产精品永久免费视频 | 性史性农村dvd毛片 | 国产精品亚洲а∨无码播放麻豆 | 伊人久久大香线蕉亚洲 | 国产精品办公室沙发 | 麻豆人妻少妇精品无码专区 | 熟女体下毛毛黑森林 | 国产办公室秘书无码精品99 | 水蜜桃亚洲一二三四在线 | 99久久精品国产一区二区蜜芽 | 51国偷自产一区二区三区 | 成人精品视频一区二区三区尤物 | 欧美黑人乱大交 | 亚洲国产成人av在线观看 | 亚洲中文字幕无码中字 | 天堂а√在线中文在线 | 精品熟女少妇av免费观看 | 亚洲精品一区二区三区婷婷月 | 精品国产一区av天美传媒 | 一本久久a久久精品亚洲 | 理论片87福利理论电影 | 大乳丰满人妻中文字幕日本 | 18禁止看的免费污网站 | 久久久久久a亚洲欧洲av冫 | 丰满妇女强制高潮18xxxx | 蜜桃无码一区二区三区 | 亚洲狠狠婷婷综合久久 | 欧美国产亚洲日韩在线二区 | 水蜜桃av无码 | 成人动漫在线观看 | 久久精品女人天堂av免费观看 | 高中生自慰www网站 | 在线播放免费人成毛片乱码 | 欧美zoozzooz性欧美 | 超碰97人人射妻 | 高潮毛片无遮挡高清免费视频 | 99麻豆久久久国产精品免费 | 永久黄网站色视频免费直播 | 久久无码人妻影院 | 天堂а√在线地址中文在线 | 乱人伦人妻中文字幕无码 | 亚洲精品美女久久久久久久 | 日本肉体xxxx裸交 | 98国产精品综合一区二区三区 | 色五月五月丁香亚洲综合网 | 亚洲gv猛男gv无码男同 | 国产精品办公室沙发 | 风流少妇按摩来高潮 | 国内老熟妇对白xxxxhd | 人人澡人人妻人人爽人人蜜桃 | 欧美 日韩 亚洲 在线 | 欧美zoozzooz性欧美 | 久久97精品久久久久久久不卡 | 欧美精品在线观看 | 欧美丰满熟妇xxxx性ppx人交 | 亚洲精品成人av在线 | 麻豆精品国产精华精华液好用吗 | 精品国产麻豆免费人成网站 | 乌克兰少妇xxxx做受 | 亚洲国产欧美日韩精品一区二区三区 | 久久国产精品精品国产色婷婷 | 亚洲成av人在线观看网址 | 高潮毛片无遮挡高清免费视频 | 少妇激情av一区二区 | 国产婷婷色一区二区三区在线 | 麻豆精产国品 | 国产成人无码午夜视频在线观看 | 免费无码av一区二区 | 精品国产av色一区二区深夜久久 | 成人免费视频在线观看 | 中文无码伦av中文字幕 | 真人与拘做受免费视频 | 国产精品久久久久久久9999 | 人妻互换免费中文字幕 | 亚洲阿v天堂在线 | 人妻少妇精品无码专区二区 | 国产成人无码a区在线观看视频app | 国内精品久久久久久中文字幕 | 日本丰满熟妇videos | 国内精品人妻无码久久久影院 | 国产卡一卡二卡三 | 亚洲成a人片在线观看无码3d | 亚洲精品一区国产 | 3d动漫精品啪啪一区二区中 | 国产精品人妻一区二区三区四 | 内射白嫩少妇超碰 | 人妻无码αv中文字幕久久琪琪布 | 九九久久精品国产免费看小说 | 狠狠cao日日穞夜夜穞av | 三级4级全黄60分钟 | 亚洲国产欧美在线成人 | 亚洲精品久久久久avwww潮水 | 一区二区三区高清视频一 | 色婷婷综合激情综在线播放 | av无码不卡在线观看免费 | 夜夜躁日日躁狠狠久久av | 国产97人人超碰caoprom | 亚洲国产精品一区二区第一页 | 国产69精品久久久久app下载 | 在线播放亚洲第一字幕 | 国产麻豆精品一区二区三区v视界 | 成年美女黄网站色大免费视频 | 亚洲日本va中文字幕 | 亚洲中文字幕无码一久久区 | 亚洲区小说区激情区图片区 | 日韩人妻系列无码专区 | 亚洲日本va午夜在线电影 | 中文字幕无码热在线视频 | 亚洲中文字幕在线无码一区二区 | 丝袜 中出 制服 人妻 美腿 | 亚洲精品国产a久久久久久 | 亚洲区小说区激情区图片区 | 成人性做爰aaa片免费看 | 久久人人爽人人爽人人片av高清 | 国产精品久久久 | 亚洲aⅴ无码成人网站国产app | 亚洲成av人片在线观看无码不卡 | 青青草原综合久久大伊人精品 | 夜先锋av资源网站 | 午夜精品久久久久久久 | 3d动漫精品啪啪一区二区中 | 久久国内精品自在自线 | 精品久久久久久人妻无码中文字幕 | 国产特级毛片aaaaaa高潮流水 | 国产亚洲精品久久久久久久久动漫 | 三级4级全黄60分钟 | 国产av一区二区精品久久凹凸 | 亚洲精品久久久久久一区二区 | 亚洲色欲色欲天天天www | 日韩av无码一区二区三区不卡 | 2020久久香蕉国产线看观看 | 亚洲国产精品无码一区二区三区 | 精品一二三区久久aaa片 | 精品无码成人片一区二区98 | 国产亚洲tv在线观看 | 夫妻免费无码v看片 | 午夜男女很黄的视频 | 国产猛烈高潮尖叫视频免费 | 久久伊人色av天堂九九小黄鸭 | 亚洲精品成人福利网站 | 99精品视频在线观看免费 | 欧美日韩在线亚洲综合国产人 | 精品亚洲韩国一区二区三区 | 亚洲另类伦春色综合小说 | 高清不卡一区二区三区 | 国产黑色丝袜在线播放 | 日日躁夜夜躁狠狠躁 | 一区二区传媒有限公司 | 欧美日本免费一区二区三区 | av人摸人人人澡人人超碰下载 | 久久无码专区国产精品s | 免费观看的无遮挡av | 精品国产麻豆免费人成网站 | 又大又紧又粉嫩18p少妇 | 成人亚洲精品久久久久软件 | 青草视频在线播放 | 欧美日韩亚洲国产精品 | 荫蒂被男人添的好舒服爽免费视频 | 丰满少妇女裸体bbw | 中文字幕无码av波多野吉衣 | 我要看www免费看插插视频 | 免费乱码人妻系列无码专区 | 白嫩日本少妇做爰 | 精品久久久久久人妻无码中文字幕 | 少女韩国电视剧在线观看完整 | 亚洲欧美日韩成人高清在线一区 | 久青草影院在线观看国产 | 欧美肥老太牲交大战 | 国产精品无码久久av | 夫妻免费无码v看片 | 福利一区二区三区视频在线观看 | 无码av免费一区二区三区试看 | 四虎永久在线精品免费网址 | 强开小婷嫩苞又嫩又紧视频 | 狠狠色噜噜狠狠狠7777奇米 | 玩弄人妻少妇500系列视频 | 国产精品毛片一区二区 | 青草视频在线播放 | 久久综合久久自在自线精品自 | 任你躁国产自任一区二区三区 | 亚洲国产高清在线观看视频 | 精品水蜜桃久久久久久久 | 国产成人精品一区二区在线小狼 | 爽爽影院免费观看 | 一个人免费观看的www视频 | 鲁鲁鲁爽爽爽在线视频观看 | 国产在线精品一区二区高清不卡 | 无码任你躁久久久久久久 | 国产乡下妇女做爰 | 最近免费中文字幕中文高清百度 | 亚洲色欲久久久综合网东京热 | 88国产精品欧美一区二区三区 | 台湾无码一区二区 | 风流少妇按摩来高潮 | 国产人妻大战黑人第1集 | 思思久久99热只有频精品66 | 国产精品久久久午夜夜伦鲁鲁 | 成 人 免费观看网站 | 国产午夜亚洲精品不卡下载 | 精品国产一区二区三区四区在线看 | 色 综合 欧美 亚洲 国产 | 人人妻人人藻人人爽欧美一区 | 精品欧美一区二区三区久久久 | 午夜福利一区二区三区在线观看 | 午夜精品一区二区三区在线观看 | 东京热无码av男人的天堂 | 日本一区二区更新不卡 | 国产精品久久精品三级 | 1000部啪啪未满十八勿入下载 | av人摸人人人澡人人超碰下载 | 国产免费久久久久久无码 | 伊在人天堂亚洲香蕉精品区 | 欧美丰满熟妇xxxx | 东京热男人av天堂 | 奇米影视7777久久精品人人爽 | 久久久久亚洲精品中文字幕 | 婷婷丁香六月激情综合啪 | 99国产欧美久久久精品 | 国产97在线 | 亚洲 | 久久国产精品精品国产色婷婷 | 亚洲精品成a人在线观看 | 亚洲人成影院在线无码按摩店 | 中文无码成人免费视频在线观看 | 午夜丰满少妇性开放视频 | 午夜精品一区二区三区在线观看 | 婷婷丁香五月天综合东京热 | 国产精品99久久精品爆乳 | 亚洲自偷精品视频自拍 | 国产色xx群视频射精 | 中文字幕无码人妻少妇免费 | 色一情一乱一伦一视频免费看 | 国产精品人人爽人人做我的可爱 | 久久国产自偷自偷免费一区调 | 7777奇米四色成人眼影 | 精品国产精品久久一区免费式 | 色老头在线一区二区三区 | 欧美 丝袜 自拍 制服 另类 | 国产成人精品无码播放 | 欧美阿v高清资源不卡在线播放 | 亚洲呦女专区 | 乱人伦人妻中文字幕无码久久网 | 国产超级va在线观看视频 | 国产成人无码区免费内射一片色欲 | 狂野欧美性猛xxxx乱大交 | 亚洲精品久久久久avwww潮水 | 国产成人无码a区在线观看视频app | 捆绑白丝粉色jk震动捧喷白浆 | 任你躁在线精品免费 | 学生妹亚洲一区二区 | 国产成人无码a区在线观看视频app | 图片小说视频一区二区 | 国产精品美女久久久 | 日本精品人妻无码免费大全 | 国产人妖乱国产精品人妖 | 无码人妻精品一区二区三区下载 | 精品偷拍一区二区三区在线看 | 欧美精品一区二区精品久久 | а√资源新版在线天堂 | 精品偷自拍另类在线观看 | 丰满岳乱妇在线观看中字无码 | 欧美freesex黑人又粗又大 | www成人国产高清内射 | 蜜桃臀无码内射一区二区三区 | 天堂а√在线地址中文在线 | 图片小说视频一区二区 | 国产小呦泬泬99精品 | 无套内谢老熟女 | 少妇厨房愉情理9仑片视频 | 久久久久久久久蜜桃 | 国产精品久久久 | 中文字幕乱码人妻无码久久 | 色综合久久88色综合天天 | 亚洲精品美女久久久久久久 | 动漫av一区二区在线观看 | 日韩精品无码一本二本三本色 | 大乳丰满人妻中文字幕日本 | 澳门永久av免费网站 | 欧美性猛交内射兽交老熟妇 | 伊人久久婷婷五月综合97色 | 少妇无码吹潮 | 综合激情五月综合激情五月激情1 | 人人妻人人澡人人爽欧美一区九九 | 久久人人爽人人爽人人片ⅴ | 欧美性猛交内射兽交老熟妇 | 国产欧美熟妇另类久久久 | 亚洲成色www久久网站 | 乱人伦人妻中文字幕无码 | 日韩视频 中文字幕 视频一区 | 51国偷自产一区二区三区 | 乱人伦人妻中文字幕无码久久网 | 精品无人国产偷自产在线 | 国产猛烈高潮尖叫视频免费 | 免费无码的av片在线观看 | 3d动漫精品啪啪一区二区中 | 爽爽影院免费观看 | 久久无码人妻影院 | 亚洲男人av天堂午夜在 | 国产欧美熟妇另类久久久 | 少妇无码一区二区二三区 | 99国产精品白浆在线观看免费 | 色狠狠av一区二区三区 | 国产欧美熟妇另类久久久 | 亚洲国产成人av在线观看 | 一个人免费观看的www视频 | 性欧美牲交xxxxx视频 | 久久人人爽人人爽人人片av高清 | 黑人大群体交免费视频 | 欧美乱妇无乱码大黄a片 | 55夜色66夜色国产精品视频 | 人人妻人人澡人人爽欧美一区 | 在线播放亚洲第一字幕 | 国产 精品 自在自线 | 国内少妇偷人精品视频 | 国产口爆吞精在线视频 | 精品国偷自产在线视频 | 亚洲精品成人福利网站 | 亚洲欧洲日本综合aⅴ在线 | 国产av一区二区三区最新精品 | 性啪啪chinese东北女人 | 精品无码国产自产拍在线观看蜜 | 日日夜夜撸啊撸 | 中文字幕无线码免费人妻 | 装睡被陌生人摸出水好爽 | 老熟女重囗味hdxx69 | 大胆欧美熟妇xx | 久久五月精品中文字幕 | 中文字幕无码av波多野吉衣 | 亚洲精品一区二区三区在线观看 | 蜜桃视频插满18在线观看 | 麻豆人妻少妇精品无码专区 | 亚洲日韩一区二区 | 欧美精品无码一区二区三区 | 麻豆果冻传媒2021精品传媒一区下载 | 大肉大捧一进一出视频出来呀 | 欧美老熟妇乱xxxxx | 性生交大片免费看女人按摩摩 | 欧美怡红院免费全部视频 | 天堂一区人妻无码 | 久久亚洲中文字幕精品一区 | 人人妻人人澡人人爽精品欧美 | 亚洲无人区午夜福利码高清完整版 | 亚洲小说图区综合在线 | 亚洲 高清 成人 动漫 | 亚洲熟妇自偷自拍另类 | 欧美三级a做爰在线观看 | 中文字幕乱码中文乱码51精品 | 97久久超碰中文字幕 | 玩弄人妻少妇500系列视频 | 亚洲理论电影在线观看 | 丰满护士巨好爽好大乳 | 精品无码av一区二区三区 | 人人妻人人澡人人爽欧美一区 | 无码帝国www无码专区色综合 | 狠狠色欧美亚洲狠狠色www | 久久99久久99精品中文字幕 | 99久久精品国产一区二区蜜芽 | 女人色极品影院 | 高清国产亚洲精品自在久久 | 99er热精品视频 | 婷婷六月久久综合丁香 | 色综合久久88色综合天天 | 亚洲精品一区二区三区大桥未久 | 天堂一区人妻无码 | 国产精品第一国产精品 | 精品一区二区三区波多野结衣 | 国产69精品久久久久app下载 | 亚洲色欲久久久综合网东京热 | 久久久久久九九精品久 | 97色伦图片97综合影院 | 精品无码国产一区二区三区av | 国产精品久免费的黄网站 | av香港经典三级级 在线 | 中文字幕乱码人妻二区三区 | 夜夜夜高潮夜夜爽夜夜爰爰 | 波多野结衣高清一区二区三区 | 国产无遮挡吃胸膜奶免费看 | 激情内射亚州一区二区三区爱妻 | av人摸人人人澡人人超碰下载 | 久久伊人色av天堂九九小黄鸭 | 精品少妇爆乳无码av无码专区 | 精品一区二区不卡无码av | 亚洲人亚洲人成电影网站色 | 久久国产自偷自偷免费一区调 | 巨爆乳无码视频在线观看 | 亚洲国精产品一二二线 | 粗大的内捧猛烈进出视频 | 免费无码一区二区三区蜜桃大 | 牲交欧美兽交欧美 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 大乳丰满人妻中文字幕日本 | 麻豆蜜桃av蜜臀av色欲av | 98国产精品综合一区二区三区 | 亚洲成av人在线观看网址 | 人人妻人人澡人人爽人人精品 | 无码午夜成人1000部免费视频 | 久久亚洲精品中文字幕无男同 | 日产国产精品亚洲系列 | 久久熟妇人妻午夜寂寞影院 | 极品尤物被啪到呻吟喷水 | 熟女少妇人妻中文字幕 | 青青久在线视频免费观看 | 无码任你躁久久久久久久 | 久久久中文字幕日本无吗 | 中文字幕无码免费久久9一区9 | 亚洲一区二区观看播放 | 三上悠亚人妻中文字幕在线 | 中文字幕无线码免费人妻 | 中文字幕av日韩精品一区二区 | 熟女体下毛毛黑森林 | 无码人妻黑人中文字幕 | 久久精品国产99精品亚洲 | 中文无码成人免费视频在线观看 | √天堂资源地址中文在线 | 国内综合精品午夜久久资源 | 日韩人妻无码中文字幕视频 | 亚洲精品久久久久中文第一幕 | 美女毛片一区二区三区四区 | 俄罗斯老熟妇色xxxx | 精品乱子伦一区二区三区 | 无遮挡国产高潮视频免费观看 | 亚洲精品欧美二区三区中文字幕 | 性生交大片免费看女人按摩摩 | 伊人久久大香线蕉av一区二区 | √天堂资源地址中文在线 | 激情亚洲一区国产精品 | 东京热一精品无码av | 波多野结衣 黑人 | 午夜成人1000部免费视频 | 少妇愉情理伦片bd | 欧美一区二区三区 | 国产农村乱对白刺激视频 | 欧美怡红院免费全部视频 | 成在人线av无码免费 | 377p欧洲日本亚洲大胆 | 欧美第一黄网免费网站 | 国产亚洲欧美日韩亚洲中文色 | 精品熟女少妇av免费观看 | 四虎永久在线精品免费网址 | 无码av最新清无码专区吞精 | 亚洲国产精品无码一区二区三区 | 精品一区二区三区无码免费视频 | 成人精品天堂一区二区三区 | 波多野结衣av在线观看 | 国产深夜福利视频在线 | 精品偷自拍另类在线观看 | 亚洲精品国偷拍自产在线麻豆 | 狠狠色噜噜狠狠狠7777奇米 | 久久精品国产99精品亚洲 | 亚洲热妇无码av在线播放 | 国产黑色丝袜在线播放 | 久久久国产精品无码免费专区 | 欧美怡红院免费全部视频 | 国产成人无码a区在线观看视频app | 色欲综合久久中文字幕网 | 久久无码专区国产精品s | 天天做天天爱天天爽综合网 | 国产精品办公室沙发 | 人妻中文无码久热丝袜 | 久久久久久久人妻无码中文字幕爆 | 午夜福利试看120秒体验区 | 精品国产av色一区二区深夜久久 | 欧美日韩人成综合在线播放 | 久久综合九色综合欧美狠狠 | 国产一区二区三区四区五区加勒比 | 亚洲精品一区二区三区四区五区 | 日本熟妇大屁股人妻 | 亚洲熟悉妇女xxx妇女av | 国产日产欧产精品精品app | 亚洲国精产品一二二线 | 在教室伦流澡到高潮hnp视频 | 人妻中文无码久热丝袜 | 大地资源网第二页免费观看 | 亚洲 日韩 欧美 成人 在线观看 | 5858s亚洲色大成网站www | 最新国产麻豆aⅴ精品无码 | 给我免费的视频在线观看 | 波多野结衣乳巨码无在线观看 | 亚洲精品一区二区三区四区五区 | 一本久道久久综合婷婷五月 | 国产激情精品一区二区三区 | 国产精品多人p群无码 | 亚洲欧美国产精品专区久久 | 日韩欧美中文字幕在线三区 | 日本一区二区三区免费播放 | 久久精品国产一区二区三区肥胖 | 水蜜桃色314在线观看 | 初尝人妻少妇中文字幕 | 国产精品免费大片 | 中文无码精品a∨在线观看不卡 | 人人爽人人澡人人高潮 | 成人精品视频一区二区三区尤物 | 亚洲中文字幕无码中文字在线 | 国产精品99久久精品爆乳 |