STL中的二分查找——lower_bound 、upper_bound 、binary_search
二分查找很简单,原理就不说了。STL中关于二分查找的函数有三个lower_bound 、upper_bound 、binary_search 。这三个函数都运用于有序区间(当然这也是运用二分查找的前提)。
其中如果寻找的value存在,那么lower_bound返回一个迭代器指向其中第一个这个元素。upper_bound返回一个迭代器指向其中最后一个这个元素的下一个位置(明确点说就是返回在不破坏顺序的情况下,可插入value的最后一个位置)。如果寻找的value不存在,那么lower_bound和upper_bound都返回“假设这样的元素存在时应该出现的位置”。要指出的是lower_bound和upper_bound在源码中只是变换了if—else语句判定条件的顺序,就产生了最终迭代器位置不同的效果。
binary_search试图在已排序的[first,last)中寻找元素value,若存在就返回true,若不存在则返回false。返回单纯的布尔值也许不能满足需求,而lower_bound、upper_bound能提供额外的信息。事实上由源码可知binary_search便是利用lower_bound求出元素应该出现的位置,然后再比较该位置 的值与value的值。该函数有两个版本一个是operator< ,另外一个是利用仿函数comp进行比较。
具体分析见源码:
1 //这是forward版本 2 template <class ForwardIterator, class T> 3 inline ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, 4 const T& value) { 5 return __lower_bound(first, last, value, distance_type(first), 6 iterator_category(first)); 7 } 8 9 // 这是版本一的 forward_iterator 版本 10 template <class ForwardIterator, class T, class Distance> 11 ForwardIterator __lower_bound(ForwardIterator first, ForwardIterator last, 12 const T& value, Distance*, 13 forward_iterator_tag) { 14 Distance len = 0; 15 distance(first, last, len); // 求取整个范围的长度,ForwardIterator没有-n操作 16 Distance half; 17 ForwardIterator middle; 18 19 while (len > 0) { //为了跳出循环,而定义了len,如果用while(true) 然后每次判定长度在break,也行,不过没这个好 20 half = len >> 1; // 除以2,注意这种移位写法,不需编译器进行优化 21 middle = first; // 这两行令middle 指向中间位置 22 advance(middle, half); //ForwardIterator没有+n的操作 23 if (*middle < value) { // 如果中间位置的元素值 < 标的值,value在后半区间 24 first = middle; // 这两行令 first 指向 middle 的下一位置 25 ++first; 26 len = len - half - 1; // 修正 len,回头测试循环条件 27 } 28 else // 注意如果是相等的话,那么执行的是else语句,在前半部分找 29 // 与opper_bound进行比较 30 len = half; // 修正 len,回头测试循环条件 31 } 32 return first; 33 } 34 // 这是带comp反函数的 forward_iterator 版本 35 template <class ForwardIterator, class T, class Compare, class Distance> 36 ForwardIterator __lower_bound(ForwardIterator first, ForwardIterator last, 37 const T& value, Compare comp, Distance*, 38 forward_iterator_tag) { 39 Distance len = 0; 40 distance(first, last, len); 41 Distance half; 42 ForwardIterator middle; 43 44 while (len > 0) { 45 half = len >> 1; 46 middle = first; 47 advance(middle, half); 48 if (comp(*middle, value)) { 49 first = middle; 50 ++first; 51 len = len - half - 1; 52 } 53 else 54 len = half; 55 } 56 return first; 57 } 58 59 // 这是random_access_iterator版本 60 template <class ForwardIterator, class T, class Compare> 61 inline ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, 62 const T& value, Compare comp) { 63 return __lower_bound(first, last, value, comp, distance_type(first), 64 iterator_category(first)); 65 } 66 67 // 这是版本一的 random_access_iterator 版本 68 template <class RandomAccessIterator, class T, class Distance> 69 RandomAccessIterator __lower_bound(RandomAccessIterator first, 70 RandomAccessIterator last, const T& value, 71 Distance*, random_access_iterator_tag) { 72 Distance len = last - first; //求取整个范围的长度,与ForwarIterator版本进行比较 73 Distance half; 74 RandomAccessIterator middle; 75 76 while (len > 0) { 77 half = len >> 1; 78 middle = first + half; 79 if (*middle < value) { 80 first = middle + 1; 81 len = len - half - 1; //修正 len,回头测试循环条件,RamdonAccessIterator版本 82 } 83 else 84 len = half; 85 } 86 return first; 87 } 88 89 90 91 //这是带comp仿函数 random_access_iterator 版本 92 template <class RandomAccessIterator, class T, class Compare, class Distance> 93 RandomAccessIterator __lower_bound(RandomAccessIterator first, 94 RandomAccessIterator last, 95 const T& value, Compare comp, Distance*, 96 random_access_iterator_tag) { 97 Distance len = last - first; 98 Distance half; 99 RandomAccessIterator middle; 100 101 while (len > 0) { 102 half = len >> 1; 103 middle = first + half; 104 if (comp(*middle, value)) { 105 first = middle + 1; 106 len = len - half - 1; 107 } 108 else 109 len = half; 110 } 111 return first; 112 } 113 114 // 这是forward_iterator版本 115 template <class ForwardIterator, class T> 116 inline ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, 117 const T& value) { 118 return __upper_bound(first, last, value, distance_type(first), 119 iterator_category(first)); 120 } 121 122 // 这是版本一的 forward_iterator 版本 123 template <class ForwardIterator, class T, class Distance> 124 ForwardIterator __upper_bound(ForwardIterator first, ForwardIterator last, 125 const T& value, Distance*, 126 forward_iterator_tag) { 127 Distance len = 0; 128 distance(first, last, len); 129 Distance half; 130 ForwardIterator middle; 131 132 while (len > 0) { 133 half = len >> 1; 134 middle = first; 135 advance(middle, half); 136 if (value < *middle) // 如果中间位置的元素值大于标的值,证明在前半部分 137 len = half; // 修正len 138 else { // 注意如果元素值相等的话,那么是在后半部分找 139 // 与lower_bound进行比较 140 first = middle; // 在下半部分,令first指向middle的下一个位置 141 ++first; 142 len = len - half - 1; // 修正 len 143 } 144 } 145 return first; 146 } 147 148 // 这是版本一的 random_access_iterator 版本 149 template <class RandomAccessIterator, class T, class Distance> 150 RandomAccessIterator __upper_bound(RandomAccessIterator first, 151 RandomAccessIterator last, const T& value, 152 Distance*, random_access_iterator_tag) { 153 Distance len = last - first; 154 Distance half; 155 RandomAccessIterator middle; 156 157 while (len > 0) { 158 half = len >> 1; 159 middle = first + half; 160 if (value < *middle) 161 len = half; 162 else { 163 first = middle + 1; 164 len = len - half - 1; 165 } 166 } 167 return first; 168 } 169 170 // 这是带comp的版本 171 template <class ForwardIterator, class T, class Compare> 172 inline ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, 173 const T& value, Compare comp) { 174 return __upper_bound(first, last, value, comp, distance_type(first), 175 iterator_category(first)); 176 } 177 178 // 这是带comp的 forward_iterator 版本 179 template <class ForwardIterator, class T, class Compare, class Distance> 180 ForwardIterator __upper_bound(ForwardIterator first, ForwardIterator last, 181 const T& value, Compare comp, Distance*, 182 forward_iterator_tag) { 183 Distance len = 0; 184 distance(first, last, len); 185 Distance half; 186 ForwardIterator middle; 187 188 while (len > 0) { 189 half = len >> 1; 190 middle = first; 191 advance(middle, half); 192 if (comp(value, *middle)) 193 len = half; 194 else { 195 first = middle; 196 ++first; 197 len = len - half - 1; 198 } 199 } 200 return first; 201 } 202 203 // 这是带comp的 random_access_iterator 版本 204 template <class RandomAccessIterator, class T, class Compare, class Distance> 205 RandomAccessIterator __upper_bound(RandomAccessIterator first, 206 RandomAccessIterator last, 207 const T& value, Compare comp, Distance*, 208 random_access_iterator_tag) { 209 Distance len = last - first; 210 Distance half; 211 RandomAccessIterator middle; 212 213 while (len > 0) { 214 half = len >> 1; 215 middle = first + half; 216 if (comp(value, *middle)) 217 len = half; 218 else { 219 first = middle + 1; 220 len = len - half - 1; 221 } 222 } 223 return first; 224 } 225 226 // 版本一 227 template <class ForwardIterator, class T> 228 bool binary_search(ForwardIterator first, ForwardIterator last, 229 const T& value) { 230 ForwardIterator i = lower_bound(first, last, value); 231 //这里的实现就是调用的lower_bound ,并且如果元素不存在那么lower_bound指向的元素一定是 232 //operator < 为ture的地方。 233 return i != last && !(value < *i); 234 } 235 236 // 版本二 237 template <class ForwardIterator, class T, class Compare> 238 bool binary_search(ForwardIterator first, ForwardIterator last, const T& value, 239 Compare comp) { 240 ForwardIterator i = lower_bound(first, last, value, comp); 241 return i != last && !comp(value, *i); 242 }
函数lower_bound(first , last , val)在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置
举例如下:
一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标
则
pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。
pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。
pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。
所以,要记住:函数lower_bound(first , last , val)在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!~
返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置
测试代码如下:
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 #include <vector> 5 6 using namespace std; 7 8 9 int main() 10 { 11 const int VECTOR_SIZE = 8 ; 12 13 // Define a template class vector of int 14 typedef vector<int > IntVector ; 15 16 //Define an iterator for template class vector of strings 17 typedef IntVector::iterator IntVectorIt ; 18 19 IntVector Numbers(VECTOR_SIZE) ; 20 21 IntVectorIt start, end, it, location ; 22 23 // Initialize vector Numbers 24 Numbers[0] = 4 ; 25 Numbers[1] = 10; 26 Numbers[2] = 11 ; 27 Numbers[3] = 30 ; 28 Numbers[4] = 69 ; 29 Numbers[5] = 70 ; 30 Numbers[6] = 96 ; 31 Numbers[7] = 100; 32 33 start = Numbers.begin() ; // location of first 34 // element of Numbers 35 36 end = Numbers.end() ; // one past the location 37 // last element of Numbers 38 39 // print content of Numbers 40 cout << "Numbers { " ; 41 for(it = start; it != end; it++) 42 cout << *it << " " ; 43 cout << " }\n" << endl ; 44 45 // return the first location at which 10 can be inserted 46 // in Numbers 47 location = lower_bound(start, end, 1) ; 48 49 cout << "First location element 10 can be inserted in Numbers is: " 50 << location - start<< endl ; 51 }
函数upper_bound(first , last , val)返回的在前闭后开区间查找的关键字的上界,如一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)
返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置
测试代码如下:
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 #include <vector> 5 using namespace std; 6 7 void main() 8 { 9 const int VECTOR_SIZE = 8 ; 10 11 // Define a template class vector of int 12 typedef vector<int, allocator<int> > IntVector ; 13 14 //Define an iterator for template class vector of strings 15 typedef IntVector::iterator IntVectorIt ; 16 17 IntVector Numbers(VECTOR_SIZE) ; 18 19 IntVectorIt start, end, it, location, location1; 20 21 // Initialize vector Numbers 22 Numbers[0] = 4 ; 23 Numbers[1] = 10; 24 Numbers[2] = 10 ; 25 Numbers[3] = 30 ; 26 Numbers[4] = 69 ; 27 Numbers[5] = 70 ; 28 Numbers[6] = 96 ; 29 Numbers[7] = 100; 30 31 start = Numbers.begin() ; // location of first 32 // element of Numbers 33 34 end = Numbers.end() ; // one past the location 35 // last element of Numbers 36 37 // print content of Numbers 38 cout << "Numbers { " ; 39 for(it = start; it != end; it++) 40 cout << *it << " " ; 41 cout << " }\n" << endl ; 42 43 //return the last location at which 10 can be inserted 44 // in Numbers 45 location = lower_bound(start, end, 9) ; 46 location1 = upper_bound(start, end, 10) ; 47 48 cout << "Element 10 can be inserted at index " 49 << location - start<< endl ; 50 cout << "Element 10 can be inserted at index " 51 << location1 - start<< endl ; 52 }