剑指offer-学习笔记-数组

本文介绍了如何在Python中输入一维和二维数组,并提供了两种不同的输入方法。接着,文章详细讨论了如何在特殊排序的二维数组中查找特定数值,提出了从右上角开始的查找策略,以达到O(n)的时间复杂度。此外,还探讨了在旋转数组中寻找最小值的问题,提出了一种基于二分查找的解决方案,能在O(logN)的时间内找到最小值。
摘要由CSDN通过智能技术生成

概念提要

1.输入一维数组

#输入一维数组的方法:
# 方法1
num = [int(n) for n in input().split()]
print(num)
# 方法二
num = list(map(int, input().strip().split()))
print(num)
这两种输入方法的结果是一样的:

注:

.split()函数

拆分字符串。通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list);

a=list.(map(int,input().split()))

创建一个列表,使用split()函数进行分割

map()函数根据提供的函数对指定序列做映射,就是转化为int型

strip()方法

用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。 

 2.输入二维数组

#输入指定大小的数组
M = int(input())  # 行
N = int(input())  # 列
A = [[0]*N] * M  # 初始化二维矩阵
for i in range(M):  # 从键盘输入矩阵
    A[i] = input().split(" ")
    A[i] = [int(j) for j in A[i]]
    '''
    此处还有第二种写法,可将A[i]=[int(j) for j in A[i]]替换为:
    for j in range(N):
        A[i][j]=int(A[i][j])
    '''
 
print(A)
#输入指定行不定列的数组
M = int(input())  # 行
A = [[0]] * M  # 初始化二维矩阵
for i in range(M):  # 从键盘输入矩阵
    A[i] = input().split(" ")
    A[i] = [int(j) for j in A[i]]
    '''
    此处还有第二种写法,可将A[i]=[int(j) for j in A[i]]替换为:
    for j in range(N):
        A[i][j]=int(A[i][j])
    '''
 
print(A)

原文链接:https://blog.csdn.net/hitzijiyingcai/article/details/90757095


1、《剑指offer》1 查找特殊二维数组中的数 √

题目描述:

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组一个整数,判断数组中是否含有该整数。

解题思路:

很明显,由于该二维数组上到下递增,左到右递增的特殊性,遍历整个矩阵进行查找不是该题目的意图所在。总结规律我们可以发现:应该从矩阵的右上角或者左下角开始查找。

以右上角为例,首先选取右上角的数字,如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则说明该列其他元素都大于要查找的数字,便可以删掉该列;如果该数字小于要查找的数字,则说明该行其他元素也都小于要查找的数字,便可以删掉该行。

这样,每一次比较都可以剔除一行或者一列,进而缩小查找范围,时间复杂度为O(n)

举例:

比如在下面的二维数组中查找数字7,查找过程如下:

在这里插入图片描述

提交答案:

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        rows=len(array)-1
        col=len(array[0])-1
        i=rows
        j=0
        while j<=col and i>=0:
            if target<array[i][j]:
                i-=1
            elif target>array[i][j]:
                j+=1
            else:
                return True
        return False

本地测试:

#从键盘输入二维数组
M=int(input())    #行
N=int(input())    #列 
A=[[0]*N]*M       #初始化二维矩阵
for i in range(M):      #从键盘输入矩阵
    A[i]=input().split(" ")
    A[i]=[int(j) for j in A[i]]
    '''
    此处还有第二种写法,可将A[i]=[int(j) for j in A[i]]替换为:
    for j in range(N):
        A[i][j]=int(A[i][j])
    '''
#附:输入一维数组的方法:
#方法1
#num = [int(n) for n in input().split()]
#方法二
#num = list(map(int, input().strip().split()))
 
def Find(target, array):
    rows=len(array)-1    #len(array)为矩阵的行数
    col=len(array[0])-1  #len(array[0])为矩阵的列数
    i=rows
    j=0
    while j<=col and i>=0:
        if target<array[i][j]:
            i-=1
        elif target>array[i][j]:
            j+=1
        else:
            return True
    return False
 
target=int(input())
find=Find(target,A)
print(find)

2、《剑指offer》6 查找旋转数组中的最小数字 √

题目描述:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路:

本题的直观解法很简单,直接对数组进行一次遍历就可以找到最小值,复杂度为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个元素移动到后面(相当于没有旋转,数组整体有序)。明显我们上面的分析没有包含这种情况,需要特殊处理,方法也很简单,将第一个元素和最后一个元素相比,若第一个元素小于最后一个元素,则说明最小值就是的第一个元素,可以直接返回。

首尾指针指向的数字和中间元素三者都相等时,无法判断中间元素位于哪个子数组,无法缩小问题规模。此时,只能退而求其次,进行顺序查找。

在这里插入图片描述

提交答案:

# 法一:遍历数组
class Solution:
# 运行时间:1127ms
# 占用内存:5864k
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        result = rotateArray[0]
        for num in rotateArray:
            if num < result:
                result = num
        return result

#法二:二分法
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        left,right = 0,len(rotateArray)-1

        if rotateArray[left] < rotateArray[right]:
            return rotateArray[left]
        else:
            while (right - left) > 1:
                middle = (left+right)//2
                if rotateArray[middle] <= rotateArray[right]:
                    right = middle
                elif rotateArray[middle] >= rotateArray[left]:
                    left = middle
                elif rotateArray[middle] == rotateArray[right] == rotateArray[left]:
                    minval = rotateArray[0] 
                    for num in rotateArray:
                        minval = min(minval, num)
                    return minval
        return rotateArray[right]

本地测试: 

3、《剑指offer》13 调整数组顺序,使得奇数在偶数前面 √

题目描述:

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变
解题思路:

首先,如果不考虑奇数和奇数,偶数和偶数的相对位置,那么我们有一种双指针解法来求解,类似于快排,维护两个指针,第一个指针指向数组的第一个数字,第二个指针指向数组的最后一个数字。第一个指针向后移,第二个指针向前移,如果第一个指针指向偶数,第二个指针指向的是奇数,则交换着两个数字,接着继续移动直到两指针相遇。

上面的方法看似不错,但是对本题不适用,因为本题有相对位置不变的要求,直接交换会导致相对位置改变。因此,我们采用下面的思路来解决本题。

本题解法:对数组进行遍历,设置两个指针even和odd,even指向当前第一个偶数,odd从这个偶数之后开始查找,找到第一个奇数,此时为了相对位置不变,不能直接交换even和odd,而是将从even到odd-1的元素都依次向后移一个位置,将odd指向的那个奇数放到even的位置。然后再找下一个偶数,重复这一过程,最终就可以将奇数都放到偶数的前面,并且保证了相对位置的不变。

思路1:新建数组

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        l1, l2 = [], []
        for ele in array:
            if ele%2 == 0:
                l2.append(ele)
            else:
                l1.append(ele)
        return l1+l2

这种思路更为简洁的写法为:

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        odd = [x for x in array if x%2]
        even = [x for x in array if not (x%2)]
        result = odd + even
        return result

思路2:不新建数组,类似于冒泡排序

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        for i in range(0,len(array)):
            for j in range(len(array)-1,i,-1):
                if array[j-1]%2 ==0 and array[j]%2==1:
                    temp = array[j-1]
                    array[j-1] = array[j]
                    array[j] = temp
        return array

注意:对于本题的变体,本人在面试中被问到过,被问的是“不开辟新空间的条件下,使奇数在前,偶数在后”,面试官给的答案是设置两个偶、奇指针,分别位于数组的开头和末尾,当遇到偶数、奇数时交换。  

4、《剑指offer》19 顺时针打印矩阵 √

题目描述:

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

解题思路:

我们把打印分成了四步:

第一步:从左到右打印一行

第二步:从上到下打印一列

第三步:从右到左打印一行

第四步:从下到上打印一列

在这里插入图片描述

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值