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}