sorted_rotated/lib.rs
1//! # sorted_rotated
2//!
3//! Suppose you have a sorted, then rotated list of numbers,
4//! It will find the number you searching in `O(logN)` time.
5//!
6//! The crate will not fail if the list is not rotated, or it
7//! has duplicate items. In this case it will travel the list
8//! twice, causing some performance drop. But it will surely
9//! fail if the list is not sorted (in ascending order).
10//!
11//! The method is simple: perform a binary search to find the pivot,
12//! then minimize the range for binary search to find your desired
13//! number. If there's no pivot it will simply do binary search on
14//! the list again.
15//!
16//! In both cases, it returns an `Option` type either with the index
17//! of the found number or `None`. So you have to explicitly set up
18//! `match` arms to extract the value.
19//!
20//! # Quick Start
21//! ```rust
22//! fn main() {
23//! let list: Vec<i32> = vec![5, 6, 1, 2, 3, 4];
24//!
25//! // Or an array can be used as well:
26//! let list: [i32; 6] = [5, 6, 1, 2, 3, 4];
27//!
28//! let desired: i32 = 2;
29//!
30//! let mut element = Elements::new(list, desired);
31//!
32//! match element.find_pivot().find_desired().result() {
33//! Some(idx) => println!("index: {}", idx),
34//! None => println!("Not found"),
35//! }
36//! }
37//! ```
38
39pub struct Elements<'list> {
40 list: &'list [i32],
41 len: usize,
42 pivot: Option<usize>,
43 desired: i32,
44 result: Option<usize>,
45}
46
47impl<'list> Elements<'list> {
48 /// Returns a new instance of `Elements` struct.
49 pub fn new(list: &'list [i32], desired: i32) -> Self {
50 Elements {
51 list,
52 len: list.len(),
53 pivot: None,
54 desired,
55 result: None,
56 }
57 }
58
59 /// Performs a binary search to find the pivot point.
60 pub fn find_pivot(&mut self) -> &mut Self {
61 let list: &[i32] = self.list;
62
63 let len: usize = self.len;
64
65 // As the algorithm is `O(logN)`, so we can decide how many turns
66 // it will take, by taking the log of N base 2.
67 let turns: usize = (len as f32).log(2.0).abs() as usize + 1;
68
69 let mut left: usize = 0;
70
71 let mut right: usize = len - 1;
72
73 for _ in 0..turns {
74 let mid: usize = (left + right) / 2;
75
76 if list[mid] > list[mid + 1] {
77 self.pivot = Some(mid);
78 break;
79 }
80
81 // The idea is list[0] is always greater than list[n-1].
82 if list[mid] > list[left] {
83 left = mid;
84 } else if list[mid] < list[left] {
85 right = mid;
86 }
87 }
88
89 self
90 }
91
92 /// Again performs a binary search, but this time within a reduced
93 /// range.
94 pub fn find_desired(&mut self) -> &Self {
95 let list: &[i32] = self.list;
96
97 let len: usize = self.len;
98
99 let desired: i32 = self.desired;
100
101 let pivot: Option<usize> = self.pivot;
102
103 let mut left: usize = 0;
104
105 let mut right: usize = len - 1;
106
107 if list[left] == desired {
108 self.result = Some(left);
109 return self;
110 } else if list[right] == desired {
111 self.result = Some(right);
112 return self;
113 }
114
115 let mut distance: Option<usize> = None;
116
117 match pivot {
118 Some(pivot_idx) => {
119 if list[pivot_idx] == desired {
120 self.result = Some(pivot_idx);
121 return self;
122 }
123
124 if desired > list[left] {
125 right = pivot_idx;
126 distance = Some(pivot_idx - left);
127 } else if desired < list[left] {
128 left = pivot_idx;
129 distance = Some(right - pivot_idx);
130 }
131 }
132
133 None => {
134 distance = Some(right - left);
135 }
136 };
137
138 let mut turns: usize = 0;
139
140 match distance {
141 Some(0) => (),
142
143 _ => {
144 turns = (distance.unwrap() as f64).log(2.0).abs() as usize + 1;
145 }
146 }
147
148 for _ in 0..turns {
149 let mid: usize = (left + right) / 2;
150
151 if list[mid] == desired {
152 self.result = Some(mid);
153 break;
154 }
155
156 if list[mid] < desired {
157 left = mid;
158 } else if list[mid] > desired {
159 right = mid;
160 }
161 }
162
163 self
164 }
165
166 /// Returns an `Option` type containing either the index or `None`.
167 pub fn result(&self) -> Option<usize> {
168 self.result
169 }
170}