2014-03-21 20:55
题目:给定一个旋转过的升序排序好的数组,不知道旋转了几位。找出其中是否存在某一个值。
解法1:如果数组的元素都不重复,那么我的解法是先找出旋转的偏移量,然后进行带偏移量的二分搜索。两个过程都是对数级的。
代码:
1 // 11.3 Given a sorted array rotated by a few positions, find out if a value exists in the array. 2 // Suppose all elements in the array are unique. 3 #include <algorithm> 4 #include <vector> 5 #include <cstdio> 6 using namespace std; 7 8 int rotatedBinarySearch(vector<int> &v, int n, int key) 9 { 10 int offset; 11 12 if ((int)v.size() < n || n <= 0) { 13 return -1; 14 } 15 16 int ll, rr, mm; 17 18 if (v[0] < v[n - 1]) { 19 offset = 0; 20 } else { 21 ll = 0; 22 rr = n - 1; 23 while (rr - ll > 1) { 24 mm = (ll + rr) / 2; 25 if (v[mm] > v[ll]) { 26 ll = mm; 27 } else { 28 rr = mm; 29 } 30 } 31 offset = rr; 32 } 33 34 ll = 0; 35 rr = n - 1; 36 while (ll <= rr) { 37 mm = (ll + rr) / 2; 38 if (key < v[(mm + offset) % n]) { 39 rr = mm - 1; 40 } else if (key > v[(mm + offset) % n]) { 41 ll = mm + 1; 42 } else { 43 return (mm + offset) % n; 44 } 45 } 46 return -1; 47 } 48 49 int main() 50 { 51 int n; 52 int i; 53 vector<int> v; 54 55 while (scanf("%d", &n) == 1 && n > 0) { 56 v.resize(n); 57 for (i = 0; i < n; ++i) { 58 scanf("%d", &v[i]); 59 } 60 scanf("%d", &i); 61 printf("%d\n", rotatedBinarySearch(v, n, i)); 62 } 63 64 return 0; 65 }
解法2:如果数组的元素可能存在重复,那么我的思路仍然是先二分查找找出偏移量,然后执行带偏移量的二分搜索。不过,在找偏移量的过程中可能会出现无法决定向左还是向右的情况,比如这两个例子{1, 3, 1, 1, 1}{1, 1, 1, 3, 1},在第一次二分时,左中右的元素都是‘1’,无法确定应该往哪边走。这时就得扫描整段,{1, 1, 1}全部是同一元素,{1, 3, 1}存在不同元素,所以应该选择{1, 3, 1}进行二分,因为在首尾相同的情况下,中间如果有不同元素的话,表示旋转的偏移量应该会落在这个区间里。找到偏移量以后,之后的查找就是严格二分的了。
代码:
1 // 11.3 Given a sorted array rotated by a few positions, find out if a value exists in the array. 2 // Suppose the array may contain duplicates, what's it gonna be then? 3 #include <algorithm> 4 #include <vector> 5 #include <cstdio> 6 using namespace std; 7 8 int rotatedBinarySearch(vector<int> &v, int n, int key) 9 { 10 int offset; 11 12 if ((int)v.size() < n || n <= 0) { 13 return -1; 14 } 15 16 int ll, rr, mm; 17 int i; 18 19 ll = 0; 20 rr = n - 1; 21 while (rr - ll > 1 && v[ll] == v[rr]) { 22 mm = (ll + rr) / 2; 23 if (v[mm] > v[ll]) { 24 ll = mm; 25 break; 26 } else if (v[mm] < v[ll]) { 27 rr = mm; 28 break; 29 } else { 30 for (i = ll; i < mm - 1; ++i) { 31 if (v[i] != v[i + 1]) { 32 break; 33 } 34 } 35 if (i < mm - 1) { 36 rr = mm; 37 break; 38 } 39 for (i = mm; i < rr - 1; ++i) { 40 if (v[i] != v[i + 1]) { 41 break; 42 } 43 } 44 if (i < rr - 1) { 45 break; 46 } 47 48 // if all elements are the same, it ends here 49 return (v[0] == key) ? 0 : -1; 50 } 51 } 52 53 if (v[ll] < v[rr]) { 54 offset = 0; 55 } else { 56 // here it is guaranteed v[ll] != v[rr] 57 while (rr - ll > 1) { 58 mm = (ll + rr) / 2; 59 if (v[mm] >= v[ll]) { 60 ll = mm; 61 } else { 62 rr = mm; 63 } 64 } 65 offset = rr; 66 } 67 68 // the binary search part remains the same, difference lies in how we find the 'offset'. 69 ll = 0; 70 rr = n - 1; 71 while (ll <= rr) { 72 mm = (ll + rr) / 2; 73 if (key < v[(mm + offset) % n]) { 74 rr = mm - 1; 75 } else if (key > v[(mm + offset) % n]) { 76 ll = mm + 1; 77 } else { 78 return (mm + offset) % n; 79 } 80 } 81 82 return -1; 83 } 84 85 int main() 86 { 87 int n; 88 int i; 89 vector<int> v; 90 91 while (scanf("%d", &n) == 1 && n > 0) { 92 v.resize(n); 93 for (i = 0; i < n; ++i) { 94 scanf("%d", &v[i]); 95 } 96 scanf("%d", &i); 97 printf("%d\n", rotatedBinarySearch(v, n, i)); 98 } 99 100 return 0; 101 }