• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            雪竹的天空

            theorix

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              34 隨筆 :: 0 文章 :: 20 評論 :: 0 Trackbacks

            1 數(shù)據(jù)結(jié)構(gòu):hash_map原理

            這是一節(jié)讓你深入理解hash_map的介紹,如果你只是想囫圇吞棗,不想理解其原理,你倒是可以略過這一節(jié),但我還是建議你看看,多了解一些沒有壞處。

            hash_map基于hash table(哈希表)。 哈希表最大的優(yōu)點,就是把數(shù)據(jù)的存儲和查找消耗的時間大大降低,幾乎可以看成是常數(shù)時間;而代價僅僅是消耗比較多的內(nèi)存。然而在當(dāng)前可利用內(nèi)存越來越多的情況下,用空間換時間的做法是值得的。另外,編碼比較容易也是它的特點之一。

            其基本原理是:使用一個下標(biāo)范圍比較大的數(shù)組來存儲元素。可以設(shè)計一個函數(shù)(哈希函數(shù),也叫做散列函數(shù)),使得每個元素的關(guān)鍵字都與一個函數(shù)值(即數(shù)組下標(biāo),hash值)相對應(yīng),于是用這個數(shù)組單元來存儲這個元素;也可以簡單的理解為,按照關(guān)鍵字為每一個元素“分類”,然后將這個元素存儲在相應(yīng)“類”所對應(yīng)的地方,稱為桶。

            但是,不能夠保證每個元素的關(guān)鍵字與函數(shù)值是一一對應(yīng)的,因此極有可能出現(xiàn)對于不同的元素,卻計算出了相同的函數(shù)值,這樣就產(chǎn)生了“沖突”,換句話說,就是把不同的元素分在了相同的“類”之中。 總的來說,“直接定址”與“解決沖突”是哈希表的兩大特點。

            hash_map,首先分配一大片內(nèi)存,形成許多桶。是利用hash函數(shù),對key進(jìn)行映射到不同區(qū)域(桶)進(jìn)行保存。其插入過程是:

            1. 得到key
            2. 通過hash函數(shù)得到hash值
            3. 得到桶號(一般都為hash值對桶數(shù)求模)
            4. 存放key和value在桶內(nèi)。
            其取值過程是:
            1. 得到key
            2. 通過hash函數(shù)得到hash值
            3. 得到桶號(一般都為hash值對桶數(shù)求模)
            4. 比較桶的內(nèi)部元素是否與key相等,若都不相等,則沒有找到。
            5. 取出相等的記錄的value。
            hash_map中直接地址用hash函數(shù)生成,解決沖突,用比較函數(shù)解決。這里可以看出,如果每個桶內(nèi)部只有一個元素,那么查找的時候只有一次比較。當(dāng)許多桶內(nèi)沒有值時,許多查詢就會更快了(指查不到的時候).

            由此可見,要實現(xiàn)哈希表, 和用戶相關(guān)的是:hash函數(shù)和比較函數(shù)。這兩個參數(shù)剛好是我們在使用hash_map時需要指定的參數(shù)。

            2 hash_map 使用

            2.1 一個簡單實例

            不要著急如何把"岳不群"用hash_map表示,我們先看一個簡單的例子:隨機(jī)給你一個ID號和ID號相應(yīng)的信息,ID號的范圍是1~2的31次方。如何快速保存查找。
            #include <hash_map>
                                                #include <string>
                                                using namespace std;
                                                int main(){
                                                hash_map<int, string> mymap;
                                                mymap[9527]="唐伯虎點秋香";
                                                mymap[1000000]="百萬富翁的生活";
                                                mymap[10000]="白領(lǐng)的工資底線";
                                                ...
                                                if(mymap.find(10000) != mymap.end()){
                                                ...
                                                }
            
                                                
            夠簡單,和map使用方法一樣。這時你或許會問?hash函數(shù)和比較函數(shù)呢?不是要指定么?你說對了,但是在你沒有指定hash函數(shù)和比較函數(shù)的時候,你會有一個缺省的函數(shù),看看hash_map的聲明,你會更加明白。下面是SGI STL的聲明:
            template <class _Key, class _Tp, class _HashFcn = hash<_Key>,
                                                class _EqualKey = equal_to<_Key>,
                                                class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
                                                class hash_map
                                                {
                                                ...
                                                }
            
                                                
            也就是說,在上例中,有以下等同關(guān)系:
            ...
                                                hash_map<int, string> mymap;
                                                //等同于:
                                                hash_map<int, string, hash<int>, equal_to<int> > mymap;
            
                                                
            Alloc我們就不要取關(guān)注太多了(希望深入了解Allocator的朋友可以參看標(biāo)準(zhǔn)庫 STL :Allocator能做什么)

            2.2 hash_map 的hash函數(shù)

            hash< int>到底是什么樣子?看看源碼:
            struct hash<int> {
                                                size_t operator()(int __x) const { return __x; }
                                                };
            
                                                
            原來是個函數(shù)對象。在SGI STL中,提供了以下hash函數(shù):
            struct hash<char*>
                                                struct hash<const char*>
                                                struct hash<char>
                                                struct hash<unsigned char>
                                                struct hash<signed char>
                                                struct hash<short>
                                                struct hash<unsigned short>
                                                struct hash<int>
                                                struct hash<unsigned int>
                                                struct hash<long>
                                                struct hash<unsigned long> 
            
                                                
            也就是說,如果你的key使用的是以上類型中的一種,你都可以使用缺省的hash函數(shù)。當(dāng)然你自己也可以定義自己的hash函數(shù)。對于自定義變量,你只能如此,例如對于string,就必須自定義hash函數(shù)。例如:
            struct str_hash{
                                                size_t operator()(const string& str) const
                                                {
                                                unsigned long __h = 0;
                                                for (size_t i = 0 ; i < str.size() ; i ++)
                                                __h = 5*__h + str[i];
                                                return size_t(__h);
                                                }
                                                };
                                                //如果你希望利用系統(tǒng)定義的字符串hash函數(shù),你可以這樣寫:
                                                struct str_hash{
                                                size_t operator()(const string& str) const
                                                {
                                                return return __stl_hash_string(str.c_str());
                                                }
                                                };
            
                                                
            在聲明自己的哈希函數(shù)時要注意以下幾點:
            1. 使用struct,然后重載operator().
            2. 返回是size_t
            3. 參數(shù)是你要hash的key的類型。
            4. 函數(shù)是const類型的。
            如果這些比較難記,最簡單的方法就是照貓畫虎,找一個函數(shù)改改就是了。

            現(xiàn)在可以對開頭的"岳不群"進(jìn)行哈希化了 smile . 直接替換成下面的聲明即可:

            map<string, string> namemap;
                                                //改為:
                                                hash_map<string, string, str_hash> namemap;
            
                                                
            其他用法都不用邊。當(dāng)然不要忘了吧str_hash的聲明以及頭文件改為hash_map。

            你或許會問:比較函數(shù)呢?別著急,這里就開始介紹hash_map中的比較函數(shù)。

            2.3 hash_map 的比較函數(shù)

            在map中的比較函數(shù),需要提供less函數(shù)。如果沒有提供,缺省的也是less< Key> 。在hash_map中,要比較桶內(nèi)的數(shù)據(jù)和key是否相等,因此需要的是是否等于的函數(shù):equal_to< Key> 。先看看equal_to的源碼:
            //本代碼可以從SGI STL
                                                //先看看binary_function 函數(shù)聲明,其實只是定義一些類型而已。
                                                template <class _Arg1, class _Arg2, class _Result>
                                                struct binary_function {
                                                typedef _Arg1 first_argument_type;
                                                typedef _Arg2 second_argument_type;
                                                typedef _Result result_type;
                                                };
                                                //看看equal_to的定義:
                                                template <class _Tp>
                                                struct equal_to : public binary_function<_Tp,_Tp,bool>
                                                {
                                                bool operator()(const _Tp& __x, const _Tp& __y) const { return __x == __y; }
                                                };
            
                                                
            如果你使用一個自定義的數(shù)據(jù)類型,如struct mystruct, 或者const char* 的字符串,如何使用比較函數(shù)?使用比較函數(shù),有兩種方法. 第一種是:重載==操作符,利用equal_to;看看下面的例子:
            struct mystruct{
                                                int iID;
                                                int  len;
                                                bool operator==(const mystruct & my) const{
                                                return (iID==my.iID) && (len==my.len) ;
                                                }
                                                };  
            
                                                
            這樣,就可以使用equal_to< mystruct>作為比較函數(shù)了。另一種方法就是使用函數(shù)對象。自定義一個比較函數(shù)體:
            struct compare_str{
                                                bool operator()(const char* p1, const char*p2) const{
                                                return strcmp(p1,p2)==0;
                                                }
                                                };  
            
                                                
            有了compare_str,就可以使用hash_map了。
            typedef hash_map<const char*, string, hash<const char*>, compare_str> StrIntMap;
                                                StrIntMap namemap;
                                                namemap["岳不群"]="華山派掌門人,人稱君子劍";
                                                namemap["張三豐"]="武當(dāng)掌門人,太極拳創(chuàng)始人";
                                                namemap["東方不敗"]="第一高手,葵花寶典";
            
                                                

            2.4 hash_map 函數(shù)

            hash_map的函數(shù)和map的函數(shù)差不多。具體函數(shù)的參數(shù)和解釋,請參看:STL 編程手冊:Hash_map,這里主要介紹幾個常用函數(shù)。
            1. hash_map(size_type n) 如果講究效率,這個參數(shù)是必須要設(shè)置的。n 主要用來設(shè)置hash_map 容器中hash桶的個數(shù)。桶個數(shù)越多,hash函數(shù)發(fā)生沖突的概率就越小,重新申請內(nèi)存的概率就越小。n越大,效率越高,但是內(nèi)存消耗也越大。
            2. const_iterator find(const key_type& k) const. 用查找,輸入為鍵值,返回為迭代器。
            3. data_type& operator[](const key_type& k) . 這是我最常用的一個函數(shù)。因為其特別方便,可像使用數(shù)組一樣使用。不過需要注意的是,當(dāng)你使用[key ]操作符時,如果容器中沒有key元素,這就相當(dāng)于自動增加了一個key元素。因此當(dāng)你只是想知道容器中是否有key元素時,你可以使用find。如果你希望插入該元素時,你可以直接使用[]操作符。
            4. insert 函數(shù)。在容器中不包含key值時,insert函數(shù)和[]操作符的功能差不多。但是當(dāng)容器中元素越來越多,每個桶中的元素會增加,為了保證效率,hash_map會自動申請更大的內(nèi)存,以生成更多的桶。因此在insert以后,以前的iterator有可能是不可用的。
            5. erase 函數(shù)。在insert的過程中,當(dāng)每個桶的元素太多時,hash_map可能會自動擴(kuò)充容器的內(nèi)存。但在sgi stl中是erase并不自動回收內(nèi)存。因此你調(diào)用erase后,其他元素的iterator還是可用的。

            3 相關(guān)hash容器

            hash 容器除了hash_map之外,還有hash_set, hash_multimap, has_multiset, 這些容器使用起來和set, multimap, multiset的區(qū)別與hash_map和map的區(qū)別一樣,我想不需要我一一細(xì)說了吧。

            4 其他

            這里列幾個常見問題,應(yīng)該對你理解和使用hash_map比較有幫助。

            4.1 hash_map和map的區(qū)別在哪里?

            • 構(gòu)造函數(shù)。hash_map需要hash函數(shù),等于函數(shù);map只需要比較函數(shù)(小于函數(shù)).
            • 存儲結(jié)構(gòu)。hash_map采用hash表存儲,map一般采用紅黑樹(RB Tree)實現(xiàn)。因此其memory數(shù)據(jù)結(jié)構(gòu)是不一樣的。

            4.2 什么時候需要用hash_map,什么時候需要用map?

            總體來說,hash_map 查找速度會比map快,而且查找速度基本和數(shù)據(jù)數(shù)據(jù)量大小,屬于常數(shù)級別;而map的查找速度是log(n)級別。并不一定常數(shù)就比log(n)小,hash還有hash函數(shù)的耗時,明白了吧,如果你考慮效率,特別是在元素達(dá)到一定數(shù)量級時,考慮考慮hash_map。但若你對內(nèi)存使用特別嚴(yán)格,希望程序盡可能少消耗內(nèi)存,那么一定要小心,hash_map可能會讓你陷入尷尬,特別是當(dāng)你的hash_map對象特別多時,你就更無法控制了,而且hash_map的構(gòu)造速度較慢。

            現(xiàn)在知道如何選擇了嗎?權(quán)衡三個因素: 查找速度, 數(shù)據(jù)量, 內(nèi)存使用。

            這里還有個關(guān)于hash_map和map的小故事,看看:http://dev.csdn.net/Develop/article/14/14019.shtm

            4.3 如何在hash_map中加入自己定義的類型?

            你只要做兩件事, 定義hash函數(shù),定義等于比較函數(shù)。下面的代碼是一個例子:
            -bash-2.05b$ cat my.cpp
                                                #include <hash_map>
                                                #include <string>
                                                #include <iostream>
                                                using namespace std;
                                                //define the class
                                                class ClassA{
                                                public:
                                                ClassA(int a):c_a(a){}
                                                int getvalue()const { return c_a;}
                                                void setvalue(int a){c_a;}
                                                private:
                                                int c_a;
                                                };
                                                //1 define the hash function
                                                struct hash_A{
                                                size_t operator()(const class ClassA & A)const{
                                                //  return  hash<int>(classA.getvalue());
                                                return A.getvalue();
                                                }
                                                };
                                                //2 define the equal function
                                                struct equal_A{
                                                bool operator()(const class ClassA & a1, const class ClassA & a2)const{
                                                return  a1.getvalue() == a2.getvalue();
                                                }
                                                };
                                                int main()
                                                {
                                                hash_map<ClassA, string, hash_A, equal_A> hmap;
                                                ClassA a1(12);
                                                hmap[a1]="I am 12";
                                                ClassA a2(198877);
                                                hmap[a2]="I am 198877";
                                                cout<<hmap[a1]<<endl;
                                                cout<<hmap[a2]<<endl;
                                                return 0;
                                                }
                                                -bash-2.05b$ make my
                                                c++  -O -pipe -march=pentiumpro  my.cpp  -o my
                                                -bash-2.05b$ ./my
                                                I am 12
                                                I am 198877
            
                                                
            typedef map<Key, Value> KeyMap;
            
                                                

            當(dāng)你希望使用hash_map來替換的時候,只需要修改:

            typedef hash_map<Key, Value> KeyMap;
            
                                                

            其他的基本不變。當(dāng)然,你需要注意是否有Key類型的hash函數(shù)和比較函數(shù)。

             

             

             

            //////////////////////////////////////

            參數(shù)使用說明

            來源http://www.stlchina.org/twiki/bin/view.pl/Main/STLHashMap

            /////////////////////////////////////////////

            hash_map<Key, Data, HashFcn, EqualKey, Alloc>

            Category: containers

            Description

            Hash_map 是一種使用hash 關(guān)聯(lián)容器,把Key 和value數(shù)據(jù)對應(yīng)存儲; Hash_map 同樣是一個Pair 的關(guān)聯(lián)容器,這意味著其元素類型是pair<const Key, Data>; Hash_map 還是Unique 關(guān)聯(lián)容器,即使用EqualKey比較函數(shù)來判斷不存在兩個元素的key值相等。

            由于hash_map在通過key值查找時具有很高的效率,所以hash_map對于一些互不相干的元素的存儲非常有用。如果元素的某種順序比較重要,使用map更合適一些。

            Example

            struct eqstr
                                                {
                                                bool operator()(const char* s1, const char* s2) const
                                                {
                                                return strcmp(s1, s2) == 0;
                                                }
                                                };
                                                int main()
                                                {
                                                hash_map<const char*, int, hash<const char*>, eqstr> months;
                                                months["january"] = 31;
                                                months["february"] = 28;
                                                months["march"] = 31;
                                                months["april"] = 30;
                                                months["may"] = 31;
                                                months["june"] = 30;
                                                months["july"] = 31;
                                                months["august"] = 31;
                                                months["september"] = 30;
                                                months["october"] = 31;
                                                months["november"] = 30;
                                                months["december"] = 31;
                                                cout << "september -> " << months["september"] << endl;
                                                cout << "april     -> " << months["april"] << endl;
                                                cout << "june      -> " << months["june"] << endl;
                                                cout << "november  -> " << months["november"] << endl;
                                                }
            
                                                

            Definition Defined in the header hash_map, and in the backward-compatibility header hash_map.h. This class is an SGI extension; it is not part of the C++ standard.

            Template parameters

             

             

             

             

             

             

            Parameter Description

             

            Default
            Key The hash_map's key type. This is also defined as hash_map::key_type.  

             

            Data The hash_map's data type. This is also defined as hash_map::data_type.  
            HashFcn The hash function used by the hash_map. This is also defined as hash_map::hasher. hash<Key>
            EqualKey The hash_map key equality function: a binary predicate that determines whether two keys are equal. This is also defined as hash_map::key_equal. equal_to<Key>

             

            Alloc The hash_map's allocator, used for all internal memory management. alloc

            Members

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            Member Where defined Description
            key_type Associative Container The hash_map's key type, Key.

             

            data_type Pair Associative Container The type of object associated with the keys.
            value_type Pair Associative Container The type of object, pair<const key_type, data_type>, stored in the hash_map.
            hasher Hashed Associative Container The hash_map's hash function.
            key_equal Hashed Associative Container Function object that compares keys for equality.
            pointer Container Pointer to T.
            reference Container Reference to T

            const_reference

            Container Const reference to T
            size_type

             

            Container An unsigned integral type.
            difference_type Container

             

            A signed integral type.
            iterator Container Iterator used to iterate through a hash_map. [1]

             

            const_iterator Container Const iterator used to iterate through a hash_map.
            iterator begin() Container Returns an iterator pointing to the beginning of the hash_map.

             

            iterator end() Container Returns an iterator pointing to the end of the hash_map.

             

            const_iterator begin() const Container Returns an const_iterator pointing to the beginning of the hash_map.

             

            const_iterator end() const Container Returns an const_iterator pointing to the end of the hash_map.

             

            size_type size() const Container Returns the size of the hash_map.
            size_type max_size() const Container Returns the largest possible size of the hash_map.
            bool empty() const Container true if the hash_map's size is 0.

             

            size_type bucket_count() const Hashed Associative Container Returns the number of buckets used by the hash_map.
            void resize(size_type n) Hashed Associative Container Increases the bucket count to at least n.
            hasher hash_funct() const Hashed Associative Container Returns the hasher object used by the hash_map.

             

            key_equal key_eq() const Hashed Associative Container Returns the key_equal object used by the hash_map.

             

            hash_map() Container Creates an empty hash_map.
            hash_map(size_type n) Hashed Associative Container Creates an empty hash_map with at least n buckets.

             

            hash_map(size_type n,
                                                            const hasher& h)
                                                            
            Hashed Associative Container Creates an empty hash_map with at least n buckets, using h

            as the hash function.

            hash_map(size_type n,
                                                            const hasher& h,
                                                            const key_equal& k)
                                                            
            Hashed Associative Container

             

            Creates an empty hash_map with at least n buckets, using h as the hash function and k as the key equal function.
            template <class InputIterator>
                                                            hash_map(InputIterator f, InputIterator l)
                                                            [2]
                                                            
            Unique Hashed Associative Container

             

            Creates a hash_map with a copy of a range.
            template <class InputIterator>
                                                            hash_map(InputIterator f, InputIterator l,
                                                            size_type n)
                                                            [2]
                                                            
            Unique Hashed Associative Container Creates a hash_map with a copy of a range and a bucket count of at least n.
            template <class InputIterator>
                                                            hash_map(InputIterator f, InputIterator l,
                                                            size_type n, const hasher& h)
                                                            [2]
                                                            
            Unique Hashed Associative Container Creates a hash_map with a copy of a range and a bucket count of at least n, using h as the hash function.

             

            template <class InputIterator>
                                                            hash_map(InputIterator f, InputIterator l,
                                                            size_type n, const hasher& h,
                                                            const key_equal& k)
                                                            [2]
                                                            

             

            Unique Hashed Associative Container Creates a hash_map with a copy of a range and a bucket count of at least n, using h as the hash function and k as the key equal function.
            hash_map(const hash_map&) Container The copy constructor.
            hash_map& operator=(const hash_map&) Container The assignment operator
            void swap(hash_map&) Container Swaps the contents of two hash_maps.
            pair<iterator, bool>
                                                            insert(const value_type& x)
                                                            
            Unique Associative Container Inserts x into the hash_map.

             

            template <class InputIterator>
                                                            void insert(InputIterator f, InputIterator l)
                                                            [2]
                                                            
            Unique Associative Container

             

            Inserts a range into the hash_map.
            void erase(iterator pos) Associative Container Erases the element pointed to by pos.
            size_type erase(const key_type& k) Associative Container

             

            Erases the element whose key is k.
            void erase(iterator first, iterator last) Associative Container Erases all elements in a range.
            void clear() Associative Container Erases all of the elements.
            const_iterator find(const key_type& k) const Associative Container Finds an element whose key is k.

             

            iterator find(const key_type& k) Associative Container Finds an element whose key is k.

             

            size_type count(const key_type& k) const Unique Associative Container Counts the number of elements whose key is k.

             

            pair<const_iterator, const_iterator>
                                                            equal_range(const key_type& k) const
                                                            
            Associative Container

             

            Finds a range containing all elements whose key is k.
            pair<iterator, iterator>
                                                            equal_range(const key_type& k)
                                                            

             

            Associative Container Finds a range containing all elements whose key is k.
            data_type&
                                                            operator[](const key_type& k) [3]
                                                            
            hash_map See below.

             

            bool operator==(const hash_map&,
                                                            const hash_map&)
                                                            
            Hashed Associative Container Tests two hash_maps for equality. This is a global function, not a member function.

            New members

            These members are not defined in the Unique Hashed Associative Container and Pair Associative Container requirements, but are specific to hash_map.

             

             

             

            Member Description
            data_type&
                                                            operator[](const key_type& k) [3]
                                                            
            Returns a reference to the object that is associated with a particular key. If the hash_map does not already contain such an object, operator[] inserts the default object data_type(). [3]

             

            Notes

            [1] Hash_map::iterator is not a mutable iterator, because hash_map::value_type is not Assignable. That is, if i is of type hash_map::iterator and p is of type

            hash_map::value_type, then *i = p is not a valid expression. However, hash_map::iterator isn't a constant iterator either, because it can be used to modify the object that it points to. Using the same notation as above, (*i).second = p is a valid expression.

            [2] This member function relies on member template functions, which at present (early 1998) are not supported by all compilers. If your compiler supports member templates, you can call this function with any type of input iterator. If your compiler does not yet support member templates, though, then the arguments must either be of type const value_type* or of type

            hash_map::const_iterator.

            [3] Since operator[] might insert a new element into the hash_map, it can't possibly be a const member function. Note that the definition of operator[] is extremely simple: m[k] is equivalent to

            (*((m.insert(value_type(k, data_type()))).first)).second. Strictly speaking, this member function is unnecessary: it exists only for convenience.

            posted on 2008-09-08 23:23 雪竹的天空( theorix ) 閱讀(7006) 評論(0)  編輯 收藏 引用 所屬分類: 算法資料
            无码国内精品久久人妻蜜桃| 国产成人久久AV免费| 天天做夜夜做久久做狠狠| 热久久最新网站获取| 久久精品人人做人人爽97| 国产亚洲欧美成人久久片| 尹人香蕉久久99天天拍| 久久精品综合网| 青青青国产精品国产精品久久久久| 久久99国产精品久久99| 国内高清久久久久久| 99久久人人爽亚洲精品美女| 久久久女人与动物群交毛片| 久久天天躁狠狠躁夜夜2020老熟妇| 久久国产色av免费看| 伊人色综合久久天天| 国产精品久久毛片完整版| 亚洲va国产va天堂va久久| 国产成人精品综合久久久久| 久久久久九九精品影院| 2021精品国产综合久久| 久久综合给久久狠狠97色| 亚洲成色WWW久久网站| 国内高清久久久久久| 99久久99久久精品免费看蜜桃| 久久亚洲av无码精品浪潮| 久久久久婷婷| 国产69精品久久久久9999APGF | 97久久国产亚洲精品超碰热| 一本色道久久88综合日韩精品| 欧美日韩精品久久久久| 人妻精品久久无码区| 国产成人AV综合久久| 99久久精品免费看国产一区二区三区 | 久久99精品久久久大学生| 欧美午夜精品久久久久免费视| 97久久天天综合色天天综合色hd| 91久久精品国产免费直播| 精品国产99久久久久久麻豆| 久久久久夜夜夜精品国产| 一本久道久久综合狠狠躁AV|