有序数组查找:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0
- 注意,这是一个有序数组的旋转,其实整体分为2个部分,仍然属于有序数组查找的变形,因此采用二分法。
- 在二分法中,要注意三点:
- 比的是 mid位置处的值和最左边位置处的值以及最右边位置处的值,不是直接比最左边和最右边的值,是要用mid处的值进行比较。
- 循环结束条件是left<=right;而不单单是left<right.
- 注意改变左右位置的时候是right = mid -1;left = mid + 1;记住二者加减不同,同时还要有1的余量。
- 对于上面的题目采用的算法如下:
-
def minNumberInRotateArray(self, rotateArray): # write code here left = 0 right = len(rotateArray) - 1 while left <= right: mid = (left+right) // 2 # 等价于除以2 if rotateArray[mid] < rotateArray[mid-1]: return rotateArray[mid] if rotateArray[mid] < rotateArray[right]: right = mid-1 else: left = mid+1 return 0
这是python代码
-
class Solution { public: int minNumberInRotateArray(vector<int> rotateArray) { int left = 0; int right = rotateArray.size()-1; while(left<=right){ int mid = (left + right) / 2; if(rotateArray[mid]<rotateArray[mid-1]) return rotateArray[mid]; else{ if(rotateArray[mid]<rotateArray[right]) right = mid - 1; else left = mid + 1; } } return 0; } };
这是C++代码,也没什么太大的区别。
-
看一下二分查找最原始的代码:
def minNumberInRotateArray(rotateArray, target): # write code here left = 0 right = len(rotateArray) - 1 while left <= right: if rotateArray[mid] == target: # 目标的找到 return mid; if rotateArray[mid] < rotateArray[right]: # 更新 right = mid - 1 else: left = mid + 1 return 0
-
那么此时对本题目分析一下:用例子分析:用中值进行比较,用中值进行对比,如果比右面小,则找的值一定在左侧,(此时分为两种情况,第一中就是比左边的大,第二种就是比左边的小。可以发现当中值小于右面的值时,无论中值比左边的值大还是小,都不依影响对结论的判断,所以就像直接的二分查找一样,仅仅用右边的值和中值进行比较,而不在使用左边的值进行比较了,因为没有必要了,这个题目也是这样的,这就是上面这样写代码的原因)。同理,如果中值小于右侧的值,那么所找一定在左侧,此时和中值比右端的值大小关系不大。
-
所以记住结论:凡是涉及到有序的数组和有序数组的变形的查找基本都是二分法的变形。变形的代码就是上面,变得就是 目标如何找到,以及更新的条件的比较。
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
- 这个题目的直接解法就是利用空间换取时间,在申请新的list,然后将奇数存储起来,然后将偶数存储起来,然后合并即可。
oushu = [] jishu = [] for i in array: if i % 2 == 0: oushu.append(i) else: jishu.append(i) jishu.extend(oushu) return jishu 不借助另外的数组
显然这个的时间复杂度是O(n),空间复杂度是O(2n)=O(n),思路简单
-
那么另外的一种做法就是不利用外围的数组,仅仅利用一个变量,实现相关的操作,思路就是循环一次,但是遇到奇数,就判断前面一个元素是不是偶数,如果是交互,直到前面的元素是奇数,总之就是奇数往前移动移动到偶数移动后的位置。代码如下:
def reOrderArray(self, array): # write code here # 此时借助其它数组,是O(n)的时间复杂度,但是空间复杂度上来了 for i in range(0, len(array)): if array[i] % 2 != 0: # 奇数 for j in range(i-1, -1, -1): if array[j] % 2 == 0: temp = array[j] array[j] = array[i] array[i] = temp i = i - 1 # 要注意这一点,当前交换了之后,继续往前移动 else: break return array
该算法的空间复杂度是O(1),时间复杂度上来了。所以本题想说明的就是利用时间换空间,也可以利用空间换时间。
最后一个题目是模拟栈,同时要求求取最小时间的复杂度是O(1),本质上是把功夫做在min函数外面。
- 这里有一个结论,求取栈的最小值的方法就是除了栈的list之外,伴随一个list,当栈每次压入一个值的时候,伴随的list每次压入当栈的最小值。为什么要这样做才能获取栈的最小值呢?是因为栈pop的时候是需要弹出一个元素的,如果不这样涉及一个伴随list的话,当栈pop一个元素时,最小值可能就改变了,所以不能利用一个变量保存最小值,大概的代码如下:
class Solution: def __init__(self): self.stack = [] self.minValue = [] # 这里是为了保存栈的最小值,用空间换时间 def push(self, node): # write code here self.stack.append(node) if len(self.minValue) == 0: self.minValue.append(node) else: self.minValue.append(node if self.minValue[-1] > node else self.minValue[-1]) def pop(self): # write code here if len(self.stack)!= 0: self.stack.pop() self.minValue.pop() def top(self): # write code here if len(self.stack)!= 0: return self.stack[-1] else: return None def min(self): # write code here if len(self.stack) != 0: return self.minValue[-1] else: return None # 对于栈的模拟,一定要对最开始未压入任何值的临界进行判断。直白点意思就是空栈不能pop,也不能取min,取top的话就是None