剑指offer整理(附python代码)——数组

Content

1.二维数组中的查找

问题描述

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

考察点:数组查找

思路1

从右上角开始扫描,情况及方案如下:
- 该数大于要找的数:删除该数所在的列
- 该数小于要找的数:删除该数所在的行
- 该数等于要找的数:直接返回该数

class Solution1:
    def Find(self, target, array):
        rows = len(array)
        cols = len(array[0])
        row = 0
        col = cols-1
        icon = False
        while (row<rows)&(col>=0):
            if (array[row][col]==target):
                icon = True
                break
            elif array[row][col]>target:
                col = col-1
            else:
                row = row+1
        return icon
if __name__ == '__main__':
    s = Solution1()
    array = [[1,3,6],[2,4,7],[3,5,9]]
    print s.Find(7,array)
True

2.旋转数组的最小数字

问题描述

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

关键:二分查找(折半查找)

要求:线性表必须采用顺序存储结构;元素按关键字有序排列;
1. 将表的中间位置的记录与关键字进行比较,若同,则查找成功;否则将表按关键字分为左右两部分
2. 若小于关键字,则从右半部分继续查找
3. 若大于关键字,则从左半部分继续查找

思路:

  1. 设置两个指针pl,pr,分别指向数组的首尾。
  2. 找到数组的中间元素,与两个指针进行比较,若大于等于pl,则将pl指向中间元素;若小于等于pr,则将pr指向中间元素。
  3. 重复步骤2,直到两个指针最后左右相邻,此时pr即为最小值。
    [bug]1.没考虑数组未经过旋转的情况2.当中间元素和首尾元素相同时,无法判断pl,pr的指向。(如:0 1 1 1 1;旋转后:1 0 1 1 1;或 1 1 1 0 1;)
# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        left = 0
        right = len(rotateArray)-1
        mid = 0
        while(rotateArray[left]>=rotateArray[right]):
            # 递归终止条件,两个指针最后左右相邻,此时pr即为最小值。
            if ((right-left)==1):
                mid = right
                break
            mid = (left+right)/2
            #bug2:当数组中的 首=尾=中间元素 时,用search()方法遍历数组,找最小。
            if ((rotateArray[left]==rotateArray[right]) and (rotateArray[mid]==rotateArray[left])):
                return search(rotateArray)
            if(rotateArray[mid]>=rotateArray[left]):
                left = mid
            else:
                right = mid
        return rotateArray[mid]
    def search(self,rotateArray):
        # 遍历数组,查找最小元素
        mid = rotateArray[0]
        for i in range(1,len(rotateArray)):
            if rotateArray[i]<mid:
                mid = rotateArray[i]
        return mid
s = Solution()
print s.minNumberInRotateArray([3,4,5,1,2])
1

3.数值的整数次方

问题描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

考察点:代码完整性

要考虑exponent的取值:负整数、0、正整数,分三种情况

思路

n次方变为n次的积,首先将exponent取绝对值进行计算,然后根据exponent的取值:负整数、0、正整数,分情况输出。

class Solution:
    def Power(self, base, exponent):
        # write code here
        base1 = base
        # 首先将exponent取绝对值,计算
        for i in range(abs(exponent)-1):
            base = base1*base
        #根据exponent的取值:负整数、0、正整数,分情况输出
        if exponent < 0:
            return 1.0 / base
        elif exponent == 0:
            return 1
        else:
            return base
if __name__ == '__main__':
    s = Solution()
    print s.Power(2,-3)
0.125

4.调整数组顺序使奇数位于偶数前面

问题描述

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

考察点:代码完整性

数组全奇数、全偶数、为空、

思路1

  • 1st遍历,判断数组是否全奇数、全偶数、为空?返回:2nd遍历;
  • 2nd遍历,新建等长数组,奇数从前开始插入,偶数从(数组长-偶数个数)位置开始插入
  • 额外空间:等长数组、数组总长、偶数个数、已存储的奇数/偶数个数
class Solution4:
    # 1st遍历,判断数组是否全奇数、全偶数、为空?返回:2nd遍历;
    # 2nd遍历,新建等长数组,奇数从前开始插入,偶数从(数组长-偶数个数)位置开始插入
    # 额外空间:等长数组、数组总长、偶数个数、已存储的奇数/偶数个数
    def reOrderArray(self, array):
        # write code here
        a_len = len(array)
        new_array = [0]*a_len
        or_numb = 0
        for i in array:
            if i%2 == 0:
                or_numb += 1
        #数组全奇数、全偶数、为空,直接返回原数组
        if or_numb == a_len or or_numb==0:
            return array
        else:
            oddicon = 0
            oricon = 0
            for i in array:
                if i%2 == 0:
                    print "oricon",oricon,i
                    new_array[a_len-or_numb+oricon] = i
                    oricon += 1
                else:
                    print "oddicon",oddicon,i
                    new_array[oddicon] = i
                    oddicon += 1
            return new_array
if __name__ == '__main__':
    s = Solution4()
    print s.reOrderArray([1,4,6,8,9])
oddicon 0 1
oricon 0 4
oricon 1 6
oricon 2 8
oddicon 1 9
[1, 9, 4, 6, 8]

5.把字符串转成整数

问题描述

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
输入一个字符串,包括数字字母符号,可以为空。若非合法的数值表达,则返回0。

思路

  1. 用正则表达式判断字符串中是非法(含字母),字符串比较判断符号(+、-),负数则用整数-1【指数函数10^x,可以用pow(10,x),或*10**x,两个表示指数】
  2. 直接用int(),将字符串转整数
import re
print re.search(r'[a-zA-Z]','232Z43')
None
# -*- coding:utf-8 -*-
import re
import math
import time
class Solution:
    def StrToInt(self, s):
        # write code here
        numb = len(s)-1
        sums = 0
        if s=='0'or re.search(r'[a-zA-Z]',s) or s=='':
            return 0
        elif s[0]=='-':
            for i in range(1,numb+1):
                sums=sums+int(s[i])*math.pow(10,numb-i)
            return (-1)*sums
        elif s[0]=='+':
            for i in range(1,numb+1):
                sums=sums+int(s[i])*math.pow(10,numb-i)
            return sums
        else:
            for i in range(numb+1):
                print i,s[i]
                sums+=int(s[i])*math.pow(10,numb-i)
            return sums

if __name__ == '__main__':
    start = time.time()
    s = Solution()
    print s.StrToInt('-123')       
    end = time.time()
    print 'time:',end-start
-123.0
time: 0.0
# 时间更短
# -*- coding:utf-8 -*-
class Solution:
    def StrToInt(self, s):
        # write code here
        try:
            return int(s)
        except Exception as e:
            return 0
if __name__ == '__main__':
    start = time.time()
    s = Solution()
    print s.StrToInt('-123')       
    end = time.time()
    print 'time:',end-start
-123
time: 0.0

6.顺时针打印矩阵

问题描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 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(较快)

  1. 每次打印第一行,打印完后对数组进行逆时针旋转。(魔方的逆时针旋转)
  2. 循环上述过程

思路2

  1. 考虑循环的圈数(min(m,n)+1)//2 及边界条件.
  2. 打印过程分为四部(右下左上)
import time
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        result = []
        while(matrix):
            result+=matrix.pop(0)
            if not matrix or not matrix[0]:
                break
            matrix = self.turn(matrix)
        return result
    def turn(self,matrix):
        num_r = len(matrix)
        num_c = len(matrix[0])
        newmat = []
        for i in range(num_c):
            newmat2 = []
            for j in range(num_r):
                newmat2.append(matrix[j][i])
            newmat.append(newmat2)
        newmat.reverse()
        return newmat
if __name__=="__main__":
    S = Solution()
    print S.printMatrix([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
    print S.printMatrix([[1,2,3,1],[4,5,6,1],[4,5,6,1]])
[1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10]
[1, 2, 3, 1, 1, 1, 6, 5, 4, 4, 5, 6]
# 实际有问题,牛客上测试没问题
# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        res=[]
        n=len(matrix)  # 行
        m=len(matrix[0])  # 列
        if n==1 and m==1:  # 若矩阵只有一个数
            res=[matrix[0][0]]
            return res
        # f//x 先除法、再下取整,表示不大于结果的整数(注意负数)。-3//2=-2。
        for o in xrange((min(m,n)+1)//2): #圈数
            # 从左到右
            [res.append(matrix[o][i]) for i in xrange(o,m-o-1)] 
            # 从上到下,并判断是否重复
            [res.append(matrix[j][m-1-o]) for j in xrange(o,n-o-1) ]
            [res.append(matrix[n-1-o][k]) for k in xrange(m-1-o,o,-1) ]
            [res.append(matrix[l][o]) for l in xrange(n-1-o,o-1,-1) ]
        return res
if __name__=="__main__":
    S = Solution()
    print S.printMatrix([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
    print S.printMatrix([[1,2,3,1],[4,5,6,1],[4,5,6,1]])
[1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 1, 6, 7, 11, 10, 6]
[1, 2, 3, 1, 1, 1, 6, 5, 4, 4, 1, 5, 6, 5]
# 实际有问题,牛客上测试没问题
class Solution:
    def printMatrix(self, matrix):
        row = len(matrix)
        column = len(matrix[0])
        res = []
        left = 0
        top = 0
        right = column-1
        bottom = row-1
        while(left<=right and top<=bottom):
            for i in range(left,right+1):
                res.append(matrix[top][i])
            for i in range(top+1,bottom+1):
                res.append(matrix[i][right])
            if top != bottom:
                for i in range(right-1,left-1,-1):
                    res.append(matrix[bottom][i])
            if left != right:
                for i in range(bottom-1,top+1,-1):
                    res.append(matrix[i][left])
            left += 1
            top += 1
            right -=1
            bottom -=1
        return res
if __name__=="__main__":
    print S.printMatrix([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
    print S.printMatrix([[1,2,3,1],[4,5,6,1],[4,5,6,1]])
[1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 1, 6, 7, 11, 10, 6]
[1, 2, 3, 1, 1, 1, 6, 5, 4, 4, 1, 5, 6, 5]

7.数组中出现次数超过一半的数组

问题描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

考察点:时间效率

思路1:基于数组特点,不修改原数组,O(n)

出现次数超过数组长度的一半,证明出现次数比其他所有元素出现次数之和还要大。
1. 遍历数组时保存两个值:一是数组中一个数字,一是次数。
2. 遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。
3. 遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        if not numbers:
            print 0
        numb = numbers[0]
        count = 1
        for i in range(len(numbers)):
            if numbers[i] == numb:
                count += 1
            else:
                count -= 1
            if count==0:
                numb = numbers[i]
                count = 1
        count = 0
        for i in range(len(numbers)):
            if numbers[i] == numb:
                count += 1
        if count*2>len(numbers):
            return numb
        else:
            return 0
s = Solution()
s.MoreThanHalfNum_Solution([1,2,3,2,2,2,5,4,2])
2

8.连续子数组的最大和

问题描述

常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

考察点:子数组,时间效率

思路1: 动态规划

【核心】找到以第i个数字结尾的子数组的最大和:f(i)

【方法】
已知原数数组为array
1. 如果f(i-1)<=0,或者i=0 :则f(i)=array[i]
2. 如果f(i-1)>0,且i≠0 :则f(i)=f(i-1)+array[i]

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return 0
        cursum = 0 
        # float('-inf')表示:负无穷大
        greatsum = float('-inf')
        for i in array:
            # 动态规划
            if cursum <= 0:
                cursum = i 
            else:
                cursum += i 
            if cursum>greatsum:
                greatsum = cursum
        return greatsum 
s = Solution()
print s.FindGreatestSumOfSubArray([6,-3,-2,7,-15,1,2,2])
8

9.整数中1出现的次数

问题描述

求任意非负整数区间中1出现的次数

考察点:时间效率

思路1: 数的规律

我们从低位到高位求每位1出现的次数,累加求和即可。例如:求0~abcde中1的个数,现在我们求c这一位中1出现的次数,其他位雷同。有两部分组成
1. 第一部分:ab * 100,表示当ab这两位在0~ab-1范围内时,de可以从0~99取值
2. 第二部分:
- 如果c>1时,当ab固定时1的个数为:100个,取值为(ab100,ab199)
- 如果c=1时,当ab固定时1的个数为:(de+1)个
- 如果c=0时,当ab固定时1的个数为:0个

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        res=0
        tmp=n
        base=1
        while tmp:
            last=tmp%10
            tmp=tmp/10
            res+=tmp*base
            if last==1:
                res+=n%base+1
            elif last>1:
                res+=base
            base*=10
        return res
# 思路应该同上
# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        '''
        我们从低位到高位求每位1出现的次数,累加求和即可
        例如:求0~abcde中1的个数,现在我们求c这一位中1出现的次数,其他位雷同
        有两部分组成
        第一部分:ab * 100,表示当ab这两位在0~ab-1范围内时,de可以从0~99取值
          第二部分:如果c>1时,当ab为ab时1的个数为0~99
                  如果c=1时,当ab为ab时1的个数为de + 1
                如果c<0时,当ab为ab是1的个数为0
        '''
        # write code here
        exp=1
        count=0      
        while n/exp:
            #step1
            #当前位exp=100时,对于12345保留1200,当前exp是1时,对12345保留1234
            #一句话,保留比exp位要大的部分,再除以10。
            count+=(n/(10*exp))*exp    

            #step2
            #判断当前位是否大于  等于  小于 1 从而决定第二部分应该加多少
            if (n/exp)%10>1:
                count+=exp
            elif (n/exp)%10==1:
                #只保留当前位或者比当前位大的部分
                count+=n-(n/exp)*exp+1         
            exp*=10                       
        return count

10.把数组排成最小的数

问题描述

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

考察点:时间效率,大数间的比较

  • 全排列:n个数能拼成n!个数,但为O(nlogn)。
  • 注意拼接后可能形成“大数”,用int可能溢出。
  • 解决“大数”,直观的可以用“字符串”
  • 比较拼接后的mn与nm的大小,可以直接用“字符串的大小比较规则”

思路1:

  1. 将整数转为字符串
  2. 定义新的“比较规则”,比较拼接后字符串的大小:若ab>ba,则b应该在a的前面。
  3. 用qsort()进行排序
# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        if not numbers:
            return ""
        # 将数值转为str
        num=map(str,numbers)
        # lambda:定义匿名函数,冒号前为参数,冒号后为方法;
        # cmp(x+y,y+x)比较拼接后的字符串的大小,x<y:返回-1;x=y:返回0;x>y:返回1.
        # sort(cmp):cmp可选,指定后,则按cmp方法排序。排序方法:默认升序。
        num.sort(lambda x,y:cmp(x+y,y+x))
        return ''.join(num)
s = Solution()
s.PrintMinNumber([12,53,2])
'12253'

11.丑数

问题描述

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

考察点:

  • 丑数:就是乘一些2、3、5。
  • 1是最小的丑数
  • 关键:记录最新选出的丑数,是由多少个2/3/5相乘得到,需要3个变量来记录。

思路1:

  1. 从1开始,让其分别乘2、3、5,选出最小的作为下一个丑数。
  2. 判断所选的下一个丑数,是乘了几(2/3/5),然后相应的次数加1。(关键)
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index<1:
            return 0
        uglyList = [1]
        id2,id3,id5=[0,0,0] #表示当前数乘了几次2、3、5
        for i in range(index-1):
            temp = min(uglyList[id2]*2,uglyList[id3]*3,uglyList[id5]*5)
            uglyList.append(temp)
            # 判断当前值是乘几得到的
            if (temp % 2 == 0):
                id2 += 1
            if (temp % 3 == 0):
                id3 += 1
            if (temp % 5 == 0):
                id5 += 1
        return uglyList[-1]
s = Solution()
s.GetUglyNumber_Solution(7)
8

12.数组中的逆序对

问题描述

  • 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。
  • 输入的数组中没有的相同的数字
  • 数据范围:对于%50的数据,size<=10^4;对于%75的数据,size<=10^5;对于%100的数据,size<=2*10^5

考察点:

  • 归并排序:先使子序列有序,再使子序列段间有序,并进行合并。

思路1:

  1. 将数组分割成子数组(只含1个元素),统计子数组内部逆序对的数目。
  2. 对两个相邻子数组进行归并排序,统计两个相邻子数组之间逆序对的数目。
# -*- coding:utf-8 -*-
class Solution:
    def InversePairs(self, data):
        # write code here
        length = len(data)
        if length<=0:
            return 0
        copy = data # 逆序存储每次比较时的较大的数。(初始化)
        count = self.InversePairsCore(data,copy,0,length-1)
        return count
    def InversePairsCore(self,data,copy,low,high):
        if low == high:
            return 0
        mid = (low+high)>>1 #右移,相当于除2
        leftCount = InversePairsCore(data,copy,low,mid)%1000000007
        rightCount = InversePairsCore(data,copy,mid,high)%1000000007
        count = 0
            # i,j设置两个指针,分别指向待比较子数组的末尾
        i = mid
        j = high
        lcopy = high
        while(i>=low and j>mid):
            if data[i]>data[j]:
                    # 若前数组中的某值大于后数组中的data[j],则逆序对数增加j-mid个
                count += j-mid
                    # 向copy中存储较大值
                lcopy -= 1
                i -= 1
                copy[lcopy] = data[i] 
                if (count>=1000000007): # 数值过大,则求余数
                    count %= 1000000007
            else:
                lcopy -= 1
                j -= 1
                copy[lcopy] = data[j]
        for i in range(low,-1,-1):
            lcopy -= 1
            copy[lcopy] = data[i]
        for i in range(mid-1,-1,-1):
            lcopy -= 1
            copy[lcopy] = data[i]
        for i in range(low,high+1):
            data[i] = copy[i]
        return (leftCount+rightCount+count)%1000000007


# s = Solution()
# s.InversePairs([1,2,3,4,0])

13.数字在排序数组中出现的次数

问题描述

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

思路1

常规想法,遍历数组,找到相应元素,统计。(自己统计/内置算法count())

思路2:有序数组——二分查找

class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        count = len(data)
        i = 0
        for j in range(count):
            if data[j] == k:
                i += 1
        return i
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        left = 0
        right = len(data) - 1
        k_sum = 0
        while left <= right:
            mid = (left + right) / 2
            if data[mid] == k:
                k_sum += 1
                high = mid + 1
                low = mid - 1
                while high < len(data) and data[high] == k:#get the one not equal.
                    k_sum += 1
                    high += 1
                while low >= 0 and data[low] == k:
                    k_sum += 1
                    low -= 1
                return k_sum #once capture one.
            elif data[mid] < k:
                left = mid + 1
            else:
                right = mid - 1
        return k_sum

14.数组中只出现一次的数字

问题描述

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

考察点:知识迁移

异或:任何一个数字异或它自己都等于0.

思路1:直接用字典存储每个数字出现的次数

思路2:用元组存储,若发现已存在,则删除已有的(牛客上最快)

  • 用元组存储数字,若发现已存在,从元组中删除,最后剩下的即只出现一次的

思路3:考虑问题的特殊性,异或(牛客上最慢)

  • 对所有数字依次进行异或,得到的结果为:两个只出现一次的数字异或的结果。
  • 找到异或结果(二进制)的第一个非0的位置n,将所有数字按照该位置是否为0,分为两组。(相同元素肯定分到了一组,两个只出现一次的数字因为不同,肯定分到了不同的组)
  • 对两组元素分别重复操作1,得到的两个数即为结果。
# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        if not array:
            return []
        mdict = {}
        for i in array:
            if mdict.has_key(i):
                mdict[i] += 1
            else:
                mdict[i] = 1
        res = []
        for i in array:
            if mdict[i]==1:
                res.append(i)
        return res
# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        tmp = set()
        for a in array:
            if a in tmp:
                tmp.remove(a)
            else:
                tmp.add(a)
        return list(tmp)
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        if not array:
            return []
        res = 0
        for i in array:
            res ^= i 
        index = 0
        while res&1 ==0:
            res = res>>1
            index += 1
        num1,num2 = [0,0]
        for i in array:
            if (i>>index)&1 ==0:
                num1 ^= i 
            else:
                num2 ^= i 
        return  [num1,num2] 
s = Solution()
s.FindNumsAppearOnce([2,4,3,6,3,2,5,5])
[4, 6]

15.和为S的连续正数序列

问题描述

  • 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
  • 输出:输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

考察点:知识迁移

思路:

  • 初始化两个变量,small=1,big=2,求序列(small,big)的和msum,与tsum进行比较
  • 若msum==tsum,则存储该序列,并继续增大big寻找下一个序列;若msum
class Solution:
    def FindContinuousSequence(self, tsum):
        if tsum < 3:
            return []
        small = 1
        big = 2
        middle = (tsum + 1)>>1 #相当于(tsum + 1)/2,但是“右移1”的速度比‘/’快.
        curSum = small + big
        output = []
        while small < middle:
            if curSum == tsum:
                output.append(range(small, big+1))
                big += 1
                curSum += big
            elif curSum > tsum:
                curSum -= small
                small += 1
            else:
                big += 1
                curSum += big
        return output

16.和为s的两个数字

问题描述

  • 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
  • 对应每个测试案例,输出两个数,小的先输出。

考察点:知识迁移

  • 注意:递增序列

思路1:O(n^2)

  • 直接遍历列表,依次判断当前元素与其后的元素之和是否为tsum

思路2:O(n)(JZ上思路,java实现,python有问题)

  1. 设置两个指针,头指针指向首位元素(最小),尾指针指向尾部元素(最大)
  2. 计算两个指针的和,若大于S,则尾指针前移;若小于,则头指针后移。
# 顺序遍历列表,每遍历一个元素,判断其后的值相加是否为tsum。O(n)
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        ls = []
        # 判断一个对象是否是一个已知的类型
        if not isinstance(array, list):
            return ls
        # 枚举array中的数(顺序遍历)
        for i, v in enumerate(array):
            for v1 in array[i:]:
                if (v + v1) == tsum:
                    ls.append([v, v1])
        if ls:
            return ls[0]
        else:
            return ls
# 设置两个指针,夹逼法 (Java实现)
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (array == null || array.length < 2) {
            return list;
        }
        int i=0,j=array.length-1;
        while(i<j){
            if(array[i]+array[j]==sum){
            list.add(array[i]);
            list.add(array[j]);
                return list;
           }else if(array[i]+array[j]>sum){
                j--;
            }else{
                i++;
            }

        }
        return list;
    }
}

17.扑克牌的顺子

问题描述

  • 他去买了一副扑克牌,发现里面居然有2个大王,2个小王。(原本只有1大1小,54张)
  • 他从中抽出了5张牌,判断是不是一个顺子(是否连续,并且没有“对子”,即相同值),A看作1,J为11,Q为12,K为13,大\小王可以看成任何数字,为了统计方便,给出的数组中0代表大/小王。
  • 输出:如果牌能组成顺子就输出true,否则就输出false。

考察点:邮箱建模能力

思路

  1. 数组排序(快排),统计0的个数zero,及间隔的个数gap
  2. 若zero>=gap,返回True.

总结

  • 统计间隔数,设置两个下标,依次递增,比较差是否为1.
  • 快速排序(非递归方法,待实现)
# -*- coding:utf-8 -*-
class Solution:
    def qsort(self, myList,start,end):
    #判断low是否小于high,如果为false,直接返回
        if start < end:
            i,j = start,end
        #设置基准数
            base = myList[i]
            while i < j:
            #如果列表后边的数,比基准数大或相等,则前移一位直到有比基准数小的数出现
                while (i < j) and (myList[j] >= base):
                    j = j - 1
            #如找到,则把第j个元素赋值给第个元素i,此时表中i,j个元素相等
                myList[i] = myList[j]
            #同样的方式比较前半区
                while (i < j) and (myList[i] <= base):
                    i = i + 1
                myList[j] = myList[i]
        #做完第一轮比较之后,列表被分成了两个半区,并且i=j,需要将这个数设置回base
            myList[i] = base
        #递归前后半区
            self.qsort(myList, start, i - 1)
            self.qsort(myList, j + 1, end)
        return myList
    def IsContinuous(self, numbers):
        # write code here
        zero = 0
        gap = 0
        mlen = len(numbers)
        if (not numbers) or (mlen == 0):
            return False
        numbers = self.qsort(numbers,0,mlen-1)
        print numbers
        # 统计数组中0的个数
        for i in numbers:
            if i == 0:
                zero += 1
        # 统计数组中间隔数
        n1 = zero #第一个非0位下标
        n2 = zero+1 #第2个非0位下标
        while(n2<mlen):
            if (numbers[n1]==numbers[n2]):
                return False
            gap += numbers[n2]-numbers[n1]-1
            n1 = n2
            n2 += 1
        return True if zero>=gap else False
s=Solution()
s.IsContinuous([1,3,2,6,4])
[1, 2, 3, 4, 6]





False

18.孩子们的游戏(圆圈中最后剩下的数)

问题描述:约瑟夫环问题

0,1,…n-1这n个数围成一个圆圈,从数字0开始,每次从圆圈中删除第m个数。求出圆圈最后剩下的数。

考察点:抽象建模能力

思路1:直接用环形链表模拟圆圈O(nm),空间O(n)

  1. 创建环形链表,每次删除第m个元素
  2. 重复,知道只剩下一个元素

思路2:找每次删除元素的数字规律,创建递归函数(待分析)。时间O(n),空间O(1)

  • 分析得,f(n,m)=[f(n-1,m)+m]%n, n>1; f(n,m)=0, n=1。
class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        if n<1 or m<1:
            return -1
        last = 0
        for i in range(2,n+1):
            last = (last+m)%i
        return last
s=Solution()
s.LastRemaining_Solution(5,3)
3

19.求1+2+…+n

问题描述

  • 求1+2+3+…+n
  • 要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

考察点:发散思维

思路:利用逻辑与(&&-java, and-python)的短路功能

  • 若and前面为0,则后面不计算。

总结

python中的‘逻辑与’and
- 存在假值(0,[],”),返回第一个假值
- 所有为真,返回最后一个真值

# -*- coding:utf-8 -*-
class Solution: 
    def Sum_Solution(self, n):
        return n and (n+self.Sum_Solution(n-1))
s=Solution()
s.Sum_Solution(3)
6

20.不用加减乘除做加法

问题描述

  • 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号

考察点:发散思维

思路1:借助内置函数sum()

思路2:借助位运算(python运行较慢,没看懂)

以5+7为例:5(101)+7(111)=12(1100)
1. 计算每个位置上的和,不进位,相当于“异或”。101^111=010,即2。
2. 计算进位的值,各位做“与”,再左移1位。101&111=101,101<<1=1010,即10。
3. 若进位为0,则第一步为最终结果,跳出循环;否则,重复上两步,不过相加的值为上两步的结果。

总结:

  1. 0xFFFFFFFF表示16位数,其中每个F为1111,所以该数转为2进制为32个1。转为10进制,为4294967296
  2. 大数相加,要防止溢出:num1 if num1 >> 31 == 0 else num1 - 4294967296
class Solution:
    def Add(self, num1, num2):
        # write code here
        return sum([num1,num2])
# 牛客上运行超时!!!
class Solution:
    def Add(self, num1, num2):
        # write code here
        while num2!=0:
            temp = num1^num2
            num2 = (num1&num2)<<1
            num1 = temp
        return num1
s=Solution()
s.Add(2,3)
5
# 牛客上运行通过
class Solution:
    def Add(self, num1, num2):
        # write code here
        while num2 != 0:
            temp = num1 ^ num2
            num2 = (num1 & num2) << 1
            # 为后面的移位操作做准备
            num1 = temp & 0xFFFFFFFF
            print num1>>31
        # 防止溢出
        return num1 if num1 >> 31 == 0 else num1 - 4294967296
s=Solution()
s.Add(2,3)
0
0





5L

21.数组中重复的数字

问题描述

  • 数组长为n,数值范围(0,n-1)。 数组中存在一些重复,不知道有几个,也不知道重复几次。请找出任意一个有重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
  • 输出:若有重复数字,选择任意一个赋值给duplication[0],并返回True.

思路1:无需额外数组或hash【时间O(n)】,有bug,牛客没检测到

已知原数组为numbs,长度为length。不过有个bug,当给定的数组中的值,范围不在(0,n-1)内时,比如>=n,会出错。
1. 访问元素x后,设置numbs[x]+=length
2. 访问新元素y时,若发现numbs[y]>=length,则证明y已出现过(即在第1步被访问过)。

思路2:【更快】

  1. 访问元素x时,判断numbs[x]==x,‘否’则进行元素交换;‘是’则证明重复。

思路3:设置n个标志位存储是否已访问。【最快,占用空间也最少】

  1. 创建长度为length的数组icon,存储True/False.(第i个值即表示值i是否已存在)
  2. 每访问一个元素x,判断icon[x]=True;若否,则设置其为True.
# 有bug,还没改对
# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False 
    def duplicate(self, numbers, duplication):
        # write code here
        mlen = len(numbers)
        for i in range(mlen):
            if i >= mlen:
                i = float('-inf')
        for i in range(mlen):
            index = numbers[i]
            if index >= mlen:
                index -= mlen
            # 有bug,没考虑原数组中的值>=mlen的情况
            if numbers[index]>=mlen:
                duplication[0] = index
                print duplication[0]
                return True
            numbers[index]=numbers[index]+mlen
            print numbers
        return False
s=Solution()
s.duplicate([2,1,5,1,4],[-1])
2





True
# 更快
# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        for i in range(len(numbers)):
            if numbers[i] != i:
                temp = numbers[numbers[i]]
                if temp == numbers[i]:
                    duplication[0] = numbers[i]
                    print duplication[0]
                    return True
                else:
                    numbers[numbers[i]] = numbers[i]
                    numbers[i] = temp
        return False
s=Solution()
s.duplicate([2,1,5,1,4],[-1])    
1





True
# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        mlen = len(numbers)
        # 设置列表,存储是否存在,因为规定输入的数值范围为(0,n-1),所以设置mlen个标志。
        icon = [False]*mlen
        for i in range(mlen):
            # 若数值超过(0,n-1),忽略不计
            if numbers[i]>=mlen:
                continue
            if icon[numbers[i]]==True:
                duplication[0]=numbers[i]
                print duplication[0]
                return True
            icon[numbers[i]]=True
        return False
s=Solution()
s.duplicate([2,1,5,1,4],[-1])
1





True

22.构建乘积数组

问题描述

  • 给定一个数组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[i]为A中所有元素出(除A[i]外)相乘。

思路1(更快)

将B[i]的值看做矩阵M中每行的乘积,这样B可以分两部分计算:上三角+下三角。(其中M:列为B[0],B[1],…B[n-1];行为A[0],A[1],…A[n-1];其中A[i]=B[i]=1)
1. 计算矩阵M的下三角连乘,暂时作为B的值。
2. 计算矩阵M的上三角连乘,并与上步得到的B的值相乘。

思路2

用两个循环,控制连乘时A、B下标不同
- 定义外层循环,控制B的下标
- 定义内层循环,获取A元素(除A[i])的所有连乘积,并赋值给B[i]

# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        if not A:
            return []
        mlen = len(A)
        B = [None]*mlen
        B[0] = 1
        # 计算“下三角”的连乘值。
        for i in range(1,mlen):
            B[i] = B[i-1]*A[i-1]
        temp = 1
        # 计算“上三角”的连乘值并用中间变量存储,然后与上步得到B的值相乘。
        for i in range(mlen-2,-1,-1):
            temp *= A[i+1]
            B[i] *= temp
        return B
# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        if not A:
            return []
        B = A[:]
        index = 0
        mlen = len(A)
        for i in range(mlen):
            sum = 1
            index = i
            # 计算A的连乘积,连乘除A[i]外的所有元素
            for j in range(mlen):
                if j!=index:
                    sum *= A[j]
            B[index] = sum
        return B    

猜你喜欢

转载自blog.csdn.net/jinfeixibi123456789/article/details/81042084