剑指offer(python)---数组类

01-二维数组的查找

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

  • 方法1----暴力求解

思路1 :这个题目按照一般的想法就是双层嵌套for语句, 容易出错点分析:
1.数组下标越界 nRow-1,nCol-1 数组中的下标是从0开始的
2.没有考虑到数组为空的情况—考虑特殊情况
3.二维数组[[]]它不为空,它是有一个元素为列表,但列表为空的数组,等价于if colcount == 0:#这里要考虑到二维数组为空的情况,如[[]]
知道行数和列数逐一比较,复杂度是行数乘以列数

class Solution:
    def Find(self, target, array):
        rows=len(array)
        cols=len(array[0])
        row=0
        col=0
        while row<rows and col<cols:
            if target==array[row][col]:
                return True
            elif target>array[row][col]:
                row+=1
            else:
                col+=1
        return False
        
import numpy as np
if __name__ == '__main__':
    a= np.array([[1,2,0,1],[1,6,9,55],[7,8,9,5]])
    target=4
    print(Solution().Find(target,a))
if __name__=='__main__':         
    a = Solution()
    print(a.Find([[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]],7))
  • 方法2-----有序求解

    因为矩阵是有序的,从右上角来看,行数row, 列数cols, 向下数字递增,向左数字递减,因此从右上角开始查找,当要查找数字比右上角数字大时。列数左移;要查找数字比右上角数字小时,行数+1下移;复杂度是行数加上列数
    在这里插入图片描述

class Solution:
    # array 二维列表
    def Find(self, target, array):
        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]:
                    row+=1
                else:
                    col-=1
            return False

02-数组中重复的数字

题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

  • 方法1—字典映射
    -设置空字典,如果在字典里,value值加1,不在的话设置为1,通过map字典确定。然后再从字典里取出首个重复的数字。
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        dict={}
        for num in numbers:
            if num not in dict:
                dict[num]=1
            else:
                dict[num]+=1
        for k in dict:
            if dict[k]>=2:
                duplication[0]=k
                return True
        return False

03-构建乘积数组

题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)

  • 方法1–直接法

思路:按上图把每行被1分割的两部分乘积都计算出来,这样可以从首尾分别用累乘算出两个列表,然后两个列表首尾相乘就是B的元素
在这里插入图片描述

解释下代码,设有数组大小为5。 对于第一个for循环
第一步:b[0] = 1;
第二步:b[1] = b[0] * a[0] = a[0]
第三步:b[2] = b[1] * a[1] = a[0] * a[1];
第四步:b[3] = b[2] * a[2] = a[0] *a[1] * a[2];
第五步:b[4] = b[3] * a[3] = a[0] * a[1] * a[2] * a[3];
然后对于第二个for循环
第一步 temp *= a[4] = a[4];
b[3] = b[3] * temp = a[0] *a[1] * a[2] * a[4];
第二步 temp *= a[3] = a[4] * a[3]; b[2] = b[2] * temp= a[0] * a[1] * a[4] * a[3];
第三步 temp *= a[2] = a[4] * a[3] * a[2]; b[1] = b[1] * temp = a[0] * a[4] * a[3] * a[2];
第四步 temp *= a[1] = a[4] * a[3] * a[2] * a[1]; b[0] = b[0] * temp = a[4] * a[3] * a[2] *
a[1]; 由此可以看出从b[4]到b[0]均已经得到正确计算。

class Solution:
    def multiply(self, A):
        B = [1]*len(A)  #将1复制len(A)次
        for i in range(0,len(A)):
            for j in range(0,len(B)):
                if i!=j:
                    B[i] *= A[j]
        return B

04- 空格替换

题目描述 2请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are
Happy.则经过替换之后的字符串为We%20Are%20Happy。

  • 方法1:直接使用replace函数
# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        return s.replace(' ','%20')
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        return "%20".join(list(s.split(" ")))

. join(): 连接字符串数组。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串

  • 方法2–直接替换
python
# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        new_s = ''
        for j in s:
            if j == ' ':
                new_s=new_s + '%20'
            else:
                new_s=new_s + j
        return new_s

05–旋转数组的最小元素

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

思路1:对于数组中的元素,两两比较

方法1–两两比较

class Solution:
    def minNumberInRotateArray(self, array):
        if len(array)==0:
            return 0
        i=0
        for i in range(len(array)):
            if (array[i]>array[i+1]):
                return array[i+1]
        return array[0]

思路2:二分查找法

class Solution:
    def minNumberInRotateArray(self, array):
        if len(array) ==0:
            return 0
        left=0
        right = len(array)-1
        mid=-1
        while(array[left]>array[right]):
            if(right-left==1):
                mid=right
                break
        mid=left+(right-left)/2
        if(array[mid]>=array[left]):
            left=mid
        if (array[mid]<array[right]):
            right=mid
        return array[mid]
循环太大

思路3–sort排序

方法3:先sort排序
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if not rotateArray:
            return 0
        else:
            rotateArray.sort()
            return rotateArray[0]

思路4–直接min函数

class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if not rotateArray:
            return 0
        else:
            return min(rotateArray)

07-函数调整顺序

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

class Solution:
    def reOrderArray(self, array):
        l1=[]
        l2=[]
        for i in range(len(array)):
            if array[i]%2==0:
                l2.append(array[i])
            else:
                l1.append(array[i])
        return l1+l2

## 08-查找数组中次数较多的元素
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路--这种类型的通过字典映射

```python
# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        dict={}
        for num in numbers:
            if num not in dict:
                dict[num]=1
            else:
                dict[num]+=1
            if dict[num]>len(numbers)/2:
                return num
        return 0

09-连续子数组的最大和

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

  • 思路:1、第一个不为负数

2、如果前面数的累加值加上当前数后的值会比当前数小,说明累计值对整体和是有害的;如果前面数的累加值加上当前数后的值比当前数大或者等于,则说明累计值对整体和是有益的。
步骤:

1、定义两个变量,一个用来存储之前的累加值,一个用来存储当前的最大和。遍历数组中的每个元素,假设遍历到第i个数时:

①如果前面的累加值为负数或者等于0,那对累加值清0重新累加,把当前的第i个数的值赋给累加值。

②如果前面的累加值为整数,那么继续累加,即之前的累加值加上当前第i个数的值作为新的累加值。

2、判断累加值是否大于最大值:如果大于最大值,则最大和更新;否则,继续保留之前的最大和

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        if len(array)<=0:
            return []
        temp=0
        linum=[]
        for i in array:
            temp=temp+i  注意这个是加i
            linum.append(temp)
            if temp>0:
                continue
            else:
                temp=0
        return max(linum)

10-连续子数组的最大和

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
思路,转化为字符串进行比较,

# -*- coding:utf-8 -*-
  if not numbers: 
            return ""
        numbers = list(map(str, numbers))
        numbers.sort(cmp=lambda x, y: cmp(x + y, y + x)) #降序排列
        return "".join(numbers).lstrip('0') or'0'

11-数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。
即输出P%1000000007

思路1:
第一反应是顺序扫描整个数组。每扫描到一个数组的时候,逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和O(n)这个数字比较,因此这个算法的时间复杂度为O(n^2)。

def test(array):
    t=0
    for i in range(len(array)-1,0,-1):
        flag=False
        for j in range(i):
            if array[j]>array[j+1]:
                array[j],array[j+1]=array[j+1],array[j]
                t+=1
                flag=True
        if not flag:
            break
    return t%(2*(10**5))

但是这个时间复杂度太高,过不了

from collections import deque 
class Solution:
    def __init__(self):
        self.count = 0
    def InversePairs(self, lis):
        self.mergeSort(lis)
        return self.count%1000000007
    def mergeSort(self, lis):
        if len(lis) <= 1:
            return lis
        middle = int(len(lis)//2)
        left = self.mergeSort(lis[:middle])
        right = self.mergeSort(lis[middle:])
        return self.merge(left, right)                      
    def merge(self, left,right):
        merged,left,right = deque(),deque(left),deque(right)
        while left and right:
            if left[0] > right[0]:
                self.count += len(left)
                merged.append(right.popleft())
            else:
                merged.append(left.popleft())
        if right:
            merged.extend(right)
        else:
            merged.extend(left)
        return merged
  • 方法2

先将原序列排序,然后从排完序的数组中取出最小的,它在原数组中的位置表示有多少比它大的数在它前面,每取出一个在原数组中删除该元素,保证后面取出的元素在原数组中是最小的,这样其位置才能表示有多少比它大的数在它前面,即逆序对数。

class Solution:
    def InversePairs(self, data):
        count = 0
        copy = []
        for i in data:
            copy.append(i)
        copy.sort()
        for i in range(len(copy)):
            count += data.index(copy[i])
            data.remove(copy[i])
        return count%1000000007
a=[1,2,3,4,5,6,7,0]
Solution().InversePairs(a)

12–统计一个数字在排序数组中出现的次数。

class Solution:
    def GetNumberOfK(self, data, k):
        dict={}
        for num in data:
            if num not in dict:
                dict[num]=1
            else:
                dict[num]+=1
        if  k in dict:
            return dict[k]
        else:
            return 0

13-一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

也是使用字典

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        hashMap = {}
        for i in array:
            if str(i) in hashMap:
                hashMap[str(i)] += 1
            else:
                hashMap[str(i)] = 1
        res = []
        for k in hashMap.keys():
            if hashMap[k] == 1:
                res.append(int(k))
        return res

09-数据流的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
题目描述:
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

输出描述: 如果当前字符流没有存在出现一次的字符,返回#字符。

思路:
用字典(Java中的HashMap结构)来做。字典中的键值对中,键key存储字符;值value存储字符出现的次数。
和前面的那道字符串中只出现一次的字符相似而不相同,前面那道是固定长度字符串,而本题是字符流,也就是会增长的,每次字符串多一个字符,就要重新判断是哪个只出现一次的字符

因为牛客网里剑指offer的python只有2.7,没有3.0以上的版本,而python2.7的字典遍历通常不是有序的(python3通常有序),所以只能再借助一个列表来存储全部字符串,遍历字符串从而寻找

class Solution:
    # 返回对应char
    def __init__(self):
        self.charDict = {}#存放字符和对应的数量
        self.charlist = []#存放字符
    def FirstAppearingOnce(self):
        # write code here
        for key in self.charlist:
            if self.charDict[key]==1:
                return key
        return '#'
    def Insert(self, char):
        # write code here
        self.charDict[char]=1 if char not in self.charDict else self.charDict[char]+1
        self.charlist.append(char)

其实再想一下,把字典去掉也完全可以啊,这跟那道固定长度的只出现一次字符串没有本质的区别

class Solution:
    # 返回对应char
    def __init__(self):
        self.charlist = []
    def FirstAppearingOnce(self):
        # write code here
        for key in self.charlist:
            if self.charlist.count(key)==1:
                return key
        return '#'
    def Insert(self, char):
        # write code here
        self.charlist.append(char)

第二个作者

charDict = {}
def FirstAppearingOnce(self):
    for key in self.charDict:
        if self.charDict[key] == 1:
            return key
    return '#'

def Insert(self, char):
    self.charDict[char] = 1 if char not in self.charDict else self.charDict[char]+1
--------------------- 
作者:goddaniel 
来源:CSDN 
原文:https://blog.csdn.net/u010005281/article/details/80232522 
版权声明:本文为博主原创文章,转载请附上博文链接!
但上述代码只能用于Python3。虽说Python中字典序为无序,但默认情况下,Python3中的字典序为“顺序”,但Python2中存储字典的算法和Python3中不同,遍历字典键值对时的顺序亦不同。

改进的思路是用列表存储字符流中的顺序,之后以列表中存入的字符流顺序寻找第一个不重复的字符。但此法依旧只适用于Python3..

charDict = {}
list = []
def FirstAppearingOnce(self):
    for key in self.list:
        if self.charDict[key] == 1:
            return key
    return '#'

def Insert(self, char):
    self.charDict[char] = 1 if char not in self.charDict else self.charDict[char]+1
    self.list.append(char)
用字符串来存储字符流的顺序,适用于Python2,但不适用Python3..

charDict = {}
s = ""
def FirstAppearingOnce(self):
    for key in self.s:
        if self.charDict[key] == 1:
            return key
    return '#'

def Insert(self, char):
    self.charDict[char] = 1 if char not in self.charDict else self.charDict[char]+1
    self.s += char

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.arr=[]
    def Insert(self, num):
        self.arr.append(num)
        self.arr.sort()
    def GetMedian(self,fuck):
        length=len(self.arr)
        if length%2==1:
            return self.arr[length//2]
        else:
            return(self.arr[length//2]+self.arr[length//2-1])/2.0


## 滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}{2,[3,4,2],6,2,5,1}{2,3,[4,2,6],2,5,1}{2,3,4,[2,6,2],5,1}{2,3,4,2,[6,2,5],1}{2,3,4,2,6,[2,5,1]}。

```python
# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, nums, k):
        # write code here
        if not k or k>len(nums):return []
        cur_max = max(nums[:k])
        max_nums = [cur_max]
        for i in range(0, len(nums) - k):
            if nums[i] == cur_max:
                cur_max = max(nums[i + 1:i + k + 1])
            elif nums[i + k] > cur_max:
                cur_max = nums[i + k]
            max_nums.append(cur_max)
        return max_nums
        # write code here
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值