题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
分析
首先分析题意,它要求我们给一个二维数组,并且每行每列都是递增的等长度数组,所以我们自然而然想到一个最简单的矩阵为:
(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
)
\begin{pmatrix} 1&2 &3 &4 \\ 5&6 &7 &8 \\ 9&10 &11 &12 \\ 13&14 &15 &16 \end{pmatrix}
⎝⎜⎜⎛15913261014371115481216⎠⎟⎟⎞
上面这个矩阵满足题目所有的要求,在程序里可以这样定义:
import numpy as np
array = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
思路一:循环遍历
所以我们可以针对这个矩阵进行下一步的分析,首先我想到的是通过遍历去获取target,那么很显然,遍历完数据就可以直接找值了:
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
row=len(array)
col=len(array[0])
for i in range(row):
for j in range(col):
if array[i][j]==target:
return True
return False
这种相当于直接暴力求解,技术点一般,如果是在慕课网,我感觉应该是通不过的。
思路二:二分查找
既然想到了全部遍历,那肯定还会有更快的代码,所以我就点进了讨论里看了下大佬们java和C的代码,看到了一份java版的二分法,就准备开始自己尝试。
python二分法伪代码
#encoding: utf-8
def half_search(search_arr, search_str):
lb = 0
ub = len(search_arr) - 1
for i in range(ub/2 + 1):
if lb > ub:
return -1
mid = (ub + lb)/2
if search_arr[mid] == search_str:
return mid
elif search_arr[mid] > search_str:
ub = mid - 1
else:
lb = mid + 1
上述是伪代码,然后我们就能写出本题的二分查找:
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# col = len(array[0])
cow = len(array[0]) - 1
for i in range(len(array)):
col = 0
print(i)
while col < cow:
mid = int((cow + col) / 2)
if target > array[i][mid]:
col = mid + 1
elif target < array[i][mid]:
cow = mid - 1
else:
return True
return False
中间改写的时候出了很多新的错误,在这里记录一下:
- (only integer scalar arrays can be converted to a scalar index)只有整数标量数组才能转换成标量索引,是我在遍历的时候没有计算长度
- (only integers, slices (
:
), ellipsis (...
), numpy.newaxis (None
) and integer or boolean arrays are valid indices) ,mid = int((cow + col) / 2),我没有加int - 另外就是如果索引没有减1,会出现索引错误,但遍历的时候不需要减1,刚开始搞混了,让我一度怀疑是不是python解释器有问题,找不到真实的地址。。。
思路三:判断角标
首先看二维数组中右上角的数字,如果该数字等于要查找的数字,则查找结束;如果该数字大于要查找的数字,则剔除这一列;如果该数字小于要查找的数字,则剔除该数字所在的行。
我们可以用图形说明:
然后我们就能编写代码:
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
rows = len(array)
cols = len(array[0])
if rows > 0 and cols > 0:
row = 0
col = cols - 1
while row < rows and col >= 0:
if target == array[row][col]:
return True
elif target < array[row][col]:
col -= 1
else:
row += 1
return False
第三种是看了别人代码,然后自己照着实现了一下,总结本题,还是思路二花的时间多,有些东西还是不熟。
思路四:使用numpy内置方法
此方法在牛客网调节不出来,但确实可以运行成功。
首先在我们的python中,对于遍历来讲,不如C、Java和MATLAB一样,可以用一个for循环显示全部,前两者因为比较深入底层,可以根据字节地址偏移来实现,后者本身就是偏向于数学工程,数组构成方式有些不一样。
但python里没有指针的概念,所以所有的指针功能都是通过引用实现的,这就导致了python的执行效率会降低,另外就是可能我们定义的变量并不是真实的指针地址,而是中间多了一层引用,所以我们无法对我们定义的数组空间做任何修改。可按照常理来讲,一个for循环为什么不能遍历全部数据?这里就要提及numpy中的flat方法了:
import numpy as np
import pandas as pd
array = np.array([[1,2,3],[4,5,6],[7,8,9]])
class Solution:
# array 二维列表
def Find(self, target, array):
# col = len(array[0])
for element in array.flat:
if target == element:
return True
return False
s = Solution()
s.Find(6,array)
"""
True
"""
另外还有flatten方法,将多维数组转化为一维数组:
array1 = array.flatten(order="C")
array1
"""
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
"""
关于更详细的解释,可以看下面的链接:
numpy中flat/flatten用法区别