如果有什么问题,请多多指教。
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray)==0:
return 0
l=0
r=len(rotateArray)-1
while l<r:
mid=(l+r)//2
if rotateArray[mid]>rotateArray[r]:
l=mid+1
else:
r=mid
return rotateArray[l]
需要改进:
采用二分法解答这个问题,
mid = low + (high - low)/2
需要考虑三种情况:
(1)array[mid] > array[high]:
出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。
low = mid + 1
(2)array[mid] == array[high]:
出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边
还是右边,这时只好一个一个试 ,
high = high - 1
(3)array[mid] < array[high]:
出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左
边。因为右边必然都是递增的。
high = mid
注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字
比如 array = [4,6]
array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;
如果high = mid - 1,就会产生错误, 因此high = mid
但情形(1)中low = mid + 1就不会错误
改进代码:
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray)==0:
return 0
l=0
r=len(rotateArray)-1
while l<r:
mid=(l+r)//2
if rotateArray[mid]>rotateArray[r]:
l=mid+1
elif rotateArray[mid]>rotateArray[r]:
mid=mid-1
else:
r=mid
return rotateArray[l]
javaj解法:
参考别人的代码思路:
解题思路:
本题的直观解法很简单,直接对数组进行一次遍历就可以找到最小值,复杂度为O(n),但是显然这不是本题的意图所在,因为没有利用到任何旋转数组的特性。
进一步分析,如果整个数组是有序的,那我们一定会想到用折半查找来实现。对于旋转数组,我们发现,它实际上可以划分为两个排序的子数组,而且前面数组的元素都不小于后面数组的元素,并且最小值正好就是这两个数组的分界线,由此,我们可以得出以下解决方法。
首先用两个指针low和high分别指向数组的第一个元素和最后一个元素,然后可以找到中间元素mid。对于这个中间元素,有以下两种情况:(1)该元素大于等于low指向的元素,此时最小的元素说明在mid的后面,可以把low=mid;(2)中间元素小于等于high指向的元素,那么最小元素在mid之前,可以high=mid。特别注意:这里不要+1或者-1,因为只有这样才能保证low始终在第一个数组,high始终在第二个数组。依次循环,当最后low和high相差1时,low指向第一个数组的最后一个,high指向第二个数组的第一个(即为我们要找的最小值)。
很明显,以上查找的时间复杂度为O(logN)。
将数组前0个元素移动到后面(相当于没有旋转,数组整体有序)。明显我们上面的分析没有包含这种情况,需要特殊处理,方法也很简单,将第一个元素和最后一个元素相比,若第一个元素小于最后一个元素,则说明最小值就是的第一个元素,可以直接返回。
首尾指针指向的数字和中间元素三者都相等时,无法判断中间元素位于哪个子数组,无法缩小问题规模。此时,只能退而求其次,进行顺序查找。
public int minNumberInRotateArray(int [] array) {
/*
三种情况:
(1)把前面0个元素搬到末尾,也就是排序数组本身,第一个就是最小值
(2)一般情况二分查找,当high-low=1时,high就是最小值
(3)如果首尾元素和中间元素都相等时,只能顺序查找
*/
int len=array.length;
if(len==0)
return 0;
int low=0,high=len-1;
if(array[low]<array[high]) //排序数组本身
return array[low];
while(low<high){
int mid=low+(high-low)/2;
if(array[low]==array[mid] && array[high]==array[mid])
return minInOrder(array);
if(array[mid]>=array[low])
low=mid;
else if(array[mid]<=array[high])
high=mid;
if(high-low==1)
return array[high];
}
return -1;
}
public int minInOrder(int [] array) { //顺序查找
int min=array[0];
for(int num:array){
if(num<min)
min=num;
}
return min;
}
解法三:大佬的思路,orz~
public class Solution {
public int minNumberInRotateArray(int[] array) {
int i = 0, j = array.length - 1;
while (i < j) {
if (array[i] < array[j]) {
return array[i];
}
int mid = (i + j) >> 1;
if (array[mid] > array[i]) {
i = mid + 1;
} else if (array[mid] < array[j]) {
j = mid; // 如果是mid-1,则可能会错过最小值,因为找的就是最小值
} else i++; // 巧妙避免了offer书上说的坑点(1 0 1 1 1)
}
return array[i];
}
}
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
for i in range(len(array)):
line = array[i]
for j in range(len(line)):
num = line[j]
if num == target:
return (i, j)
return False
if __name__ == "__main__":
s = Solution()
res = s.Find(4, [[2, 3, 4], [1, 2, 4], [4, 5, 6]])
print(res)
当然这种就不是最优解。
解法二:根 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。据题目说有有序。
解题思路:
很明显,由于该二维数组上到下递增,左到右递增的特殊性,遍历整个矩阵进行查找不是该题目的意图所在。总结规律我们可以发现:应该从矩阵的右上角或者左下角开始查找。
以右上角为例,首先选取右上角的数字,如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则说明该列其他元素都大于要查找的数字,便可以删掉该列;如果该数字小于要查找的数字,则说明该行其他元素也都小于要查找的数字,便可以删掉该行。
这样,每一次比较都可以剔除一行或者一列,进而缩小查找范围,时间复杂度为O(n)。
比如在下面的二维数组中查找数字7,查找过程如下:
java 代码实现:
public class Solution {
public boolean Find(int target, int [][] array) {
/*
思路:从左下角(或者右上角)开始查找,因为该行右边大于它,上边小于它,每次比较可以删除某一行或者某一列
注意:左上和右下不可以,因为无法减小问题规模(行和列都无法删除)
*/
if(array==null)
return false;
int row=array.length; //行数
int col=array[0].length; //列数
for(int i=row-1,j=0;i>=0&&j<col;){ //从左下角开始查找
if(array[i][j]==target) //找到
return true;
else if(array[i][j]>target) //不可能在该行,跳过该行
i--;
else //不可能在该列,跳过该列
j++;
}
return false;
}
}
当然自己根据这个思路写了下python的代码:(左下角开始查找)
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if len(array)==0:
return False
row=len(array)-1
col=0
#左下角开始查找
while row>=0 and col<=len(array[0])-1:
if array[row][col]==target:
return (row,col)
elif array[row][col]>target:
row-=1
else:
col+=1
return False
if __name__ == "__main__":
s = Solution()
res = s.Find(7, [[1, 2, 8,9], [2, 4, 9,12], [4, 7, 10,13],[6,8,11,15]])
print(res)
当然自己根据这个思路写了下python的代码:(右上角开始查找)
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if len(array)==0:
return False
row=0
col=len(array[0])-1
#右上角开始查找
while col>=0 and row<=len(array)-1:
if array[row][col]==target:
return (row,col)
elif array[row][col]>target:
col-=1
else:
row+=1
return False
if __name__ == "__main__":
s = Solution()
res = s.Find(7, [[1, 2, 8,9], [2, 4, 9,12], [4, 7, 10,13],[6,8,11,15]])
print(res)
总结:
算法的技巧在于思维,不停的看到新的思维方式,才能想到不一样的解法。当然,看题也很重要。
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param array int整型一维数组
# @return int整型一维数组
#
class Solution:
def reOrderArray(self , array ):
# write code here
l=[]
r=[]
for i in array:
if i%2==0:
r.append(i)
else:
l.append(i)
l.extend(r)
return l
解法二:
首先,如果不考虑奇数和奇数,偶数和偶数的相对位置,那么我们有一种双指针解法来求解,类似于快排,维护两个指针,第一个指针指向数组的第一个数字,第二个指针指向数组的最后一个数字。第一个指针向后移,第二个指针向前移,如果第一个指针指向偶数,第二个指针指向的是奇数,则交换着两个数字,接着继续移动直到两指针相遇。
上面的方法看似不错,但是对本题不适用,因为本题有相对位置不变的要求,直接交换会导致相对位置改变。因此,我们采用下面的思路来解决本题。
本题解法:对数组进行遍历,设置两个指针even和odd,even指向当前第一个偶数,odd从这个偶数之后开始查找,找到第一个奇数,此时为了相对位置不变,不能直接交换even和odd,而是将从even到odd-1的元素都依次向后移一个位置,将odd指向的那个奇数放到even的位置。然后再找下一个偶数,重复这一过程,最终就可以将奇数都放到偶数的前面,并且保证了相对位置的不变。
public void reOrderArray(int [] array) {
int len=array.length;
int even=0,odd=0; //当前序列的第一个奇数和第一个偶数
while(odd<len && even<len){
while(even<len && array[even]%2!=0) //找到第一个偶数even
even++;
odd=even+1;
//找偶数之后的第一个奇数
while(odd<len && array[odd]%2==0)
odd++;
if(odd>=len) //注意判断,防止溢出
break;
//把奇数取出来,从even到odd-1的元素都向后移
int temp=array[odd];
for(int i=odd;i>even;i--)
array[i]=array[i-1];
array[even]=temp; //奇数放在原来even的位置
even++;
}
}
本题有以下三种方法可解:
方法一:首先对数组进行排序,在一个有序数组中,次数超过一半的必定是中位数,那么可以直接取出中位数,然后遍历数组,看中位数是否出现次数超过一半,这取决于排序的时间复杂度,最快为O(nlogn)。
方法二:遍历数组,用 HashMap 保存每个数出现的次数,这样可以从map中直接判断是否有超过一半的数字,这种算法的时间复杂度为O(n),但是这个性能提升是用O(n)的空间复杂度换来的。
方法三(最优解法):根据数组特点得到时间复杂度为O(n)的算法。根据数组特点,数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数之和还要多。因此,我们可以在遍历数组的时候设置两个值:一个是数组中的数result,另一个是出现次数times。当遍历到下一个数字的时候,如果与result相同,则次数加1,不同则次数减一,当次数变为0的时候说明该数字不可能为多数元素,将result设置为下一个数字,次数设为1。这样,当遍历结束后,最后一次设置的result的值可能就是符合要求的值(如果有数字出现次数超过一半,则必为该元素,否则不存在),因此,判断该元素出现次数是否超过一半即可验证应该返回该元素还是返回0。这种思路是对数组进行了两次遍历,复杂度为O(n)。
解法一:
//思路2:用hashmap保存每个数出现的次数
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null)
return 0;
Map<Integer,Integer> res=new HashMap<>();
int len = array.length;
for(int i=0;i<array.length;i++){
res.put(array[i],res.getOrDefault(array[i],0)+1);
if(res.get(array[i])>len/2)
return array[i];
}
return 0;
}
//思路3:根据数组特点得到时间复杂度为O(n)的算法
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null||array.length==0)
return 0;
int len = array.length;
int result=array[0];
int times=1;
for(int i=1;i<len;i++){
if(times==0){
result=array[i];
times=1;
continue;
}
if(array[i]==result)
times++;
else
times--;
}
//检查是否符合
times=0;
for(int i=0;i<len;i++){
if(array[i]==result)
times++;
if(times>len/2)
return result;
}
return 0;
}
解法二:
哈希法:显然可以先遍历一遍数组,在map中存每个元素出现的次数,然后再遍历一次数组,找出众数。
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
dict={}
for var in numbers:
if var not in dict.keys():
dict[var]=1
else:
dict[var] += 1
for key,value in dict.items():
if value>len(numbers)/2:
return key
return None
if __name__ == "__main__":
s = Solution()
res = s.MoreThanHalfNum_Solution([1,2,3,2,2,2,5,4,2])
print(res)