python-剑指offer21-40

Posted 西西嘛呦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python-剑指offer21-40相关的知识,希望对你有一定的参考价值。

21、树

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        root=sequence[-1]
        n=len(sequence)-1
        split=len(sequence)-1
        for i in range(0,n):
            if sequence[i]>root:
                split=i
                break
        for i in range(split,n):
            if sequence[i]<root:
                return False
        left=True
        if split>0:
            left=self.VerifySquenceOfBST(sequence[:split]) 
        right=True
        if split<len(sequence)-1:
            left=self.VerifySquenceOfBST(sequence[split:n]) 
        return left and right

解题思路:首先得了解什么是二叉搜索树(二叉排序树),注意二叉排序树的中序遍历是一个从小到大排好序的;然后是后序遍历的性质,最后一个节点是根节点,然后:

1)利用根节点,划分左右子树;

2)检查,右子树的值必须都大于根节点;

3)递归下去,判断左子树和右子树,且两个子树的结果均ok,才算ok;

22、

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

class Solution:
    def FindPath(self, root, expectNumber):
        # write code here
        result = []
        if not root:
            return result
        if not root.left and not root.right and root.val == expectNumber:
            return [[root.val]]
        else:
            left = self.FindPath(root.left,expectNumber - root.val)
            right = self.FindPath(root.right,expectNumber - root.val)
            for item in left+right:
                result.append([root.val]+item)
            return result

或者拆开来:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def FindPath(self, root, expectNumber):
        # write code here
        target = expectNumber
        if not root:
            return []
        res = []
        path = []
        self.Find(root,target,res,path)
        return res
    
    def Find(self,root,target,res,path):
        if not root:
            return
        path.append(root.val)
        isleaf = root.left is None and root.right is None
        if isleaf and root.val == target:
            res.append(path[:])
        if root.left:
            self.Find(root.left, target - root.val, res, path)
        if root.right:
            self.Find(root.right, target - root.val, res, path)
        path.pop()

23、链表

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if pHead == None:
            return None
        #复制节点在原节点之后
        pCur = pHead
        while(pCur != None):
            node = RandomListNode(pCur.label)
            node.next = pCur.next
            pCur.next = node
            pCur = node.next
        #复制random节点
        pCur = pHead
        while(pCur != None):
            if pCur.random != None:
                pCur.next.random = pCur.random.next
            pCur = pCur.next.next
        head = pHead.next
        cur = head
        #将新旧链表分离
        pCur = pHead
        while(pCur != None):
            pCur.next = pCur.next.next
            if cur.next != None:
                cur.next = cur.next.next
            cur = cur.next
            pCur = pCur.next
        return head

解题思路:考虑对链表的操作;分三步走:复制next、复制random、拆分。

24、树、链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.listHead = None
        self.listTail = None
    def Convert(self, pRootOfTree):
        if pRootOfTree==None:
            return
        r = pRootOfTree
        self.Convert(r.left)
        if self.listHead == None:
            self.listHead = r
            self.listTail = r
        else:
            self.listTail.right = r
            r.left = self.listTail
            self.listTail = r
        self.Convert(r.right)
        return self.listHead

解题思路:基本思想是中序遍历,先遍历到最左节点,改变left,root之间的连接,在转到root,改变root,right之间的连接,以此类推。

25、字符串、递归

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.res = []
    
    def Permutation(self, ss):
        # write code here
        if not ss:
            return []
        chars = list(ss)
        self.permute(chars,0)
        self.res = list(set(self.res))
        self.res.sort()
        return self.res
    
    def permute(self,string,begin):
        if begin == len(string):
            self.res.append("".join(string))
        else:
            for i in range(begin,len(string)):
                string[i], string[begin] = string[begin], string[i]
                # 固定首位,递归余下的
                self.permute(string, begin+1)
                # 记得换回去
                string[i], string[begin] = string[begin], string[i]

解题思路:这相当于一个排列组合的问题。以abc为例,思考一下如果人工做这个题的思路。首先是固定首位为a,那么剩下bc,第二位若排b,第三位则排c,否则二c三b,所以可以排出abc,acb。然后首位为b,剩下ac,可以排出bac,bca。然后首位为c,剩下ab,可以排出cab,cba。

整体思路应该是,先固定首位,然后余下的部分做排列;余下部分固定首位,再余下做排列……是一种递归的思路。

思路还可以,实现的时候要想清楚细节。

在具体实现中,固定首位取余下,代码方面可以这样做:字符串的首位依次与后边交换,比如说 abc,首位a先与它自己a换,即abc,然后递归bc;首位a再与b换,即bac,然后递归ac(递归后,记得再换回去);首位a再与c换,得到cba,然后递归ba……

因为题目要求字典序打印,而且输入中会有重复字符,所以对最终得到的list要做去重,然后字典序排序。

26、数组、查找

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

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        if len(numbers) == 0:
            return 0
        if len(numbers) == 1:
            return numbers[-1]
        numbers.sort()
        tmp = numbers[(len(numbers)//2) +1]
        if numbers.count(tmp) > len(numbers) / 2:
            return tmp
        else:
            return 0

如果数组已经排好序,那么位于n/2位置的数字,就是要找到的数字。(如果数组不一定有解的话,再检查一遍n/2位置数字出现的次数是否满足多于一半的要求即可)。

但其实,不一定非要排好序,也能找到位于中间的数字。借鉴了快速排序的思路,在随机快速排序中,先随机取一个数,比它小的移到它左边,比它大的移到它右边。如果移动之后该数字的下标是n/2说明它就是中间的,如果大于n/2说明中位数在它左边,如果小于n/2说明中位数在它右边,递归继续找就行。最后找到了应当位于n/2位置的数,再数一遍检查是否满足要求即可。

27、数组、排序

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        if k > len(tinput):
            return []
        # write code here
        n = len(tinput)
        for i in range(n):
            if i <= k-1:
                for j in range(n - 1, i, -1):
                    if tinput[j] < tinput[j-1]:
                        tinput[j], tinput[j - 1] = tinput[j -1], tinput[j]
    return tinput[:k]

解题思路:利用排序算法(冒泡或者堆),将前几个最小的取出来,并不用给整个数组进行排序。(实例给出的是冒泡排序)

28、数组、辅助记录

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

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return 0
        current_sum = -10000
        max_sum = current_sum
        for i in array:
            if current_sum < 0:
                current_sum = i
            else:
                current_sum += i
            if current_sum > max_sum:
                max_sum = current_sum
        return max_sum

解题思路:得一个数,看A如果小于0,说明此时历史带来的是负面影响,直接抛弃历史,将A更新为当前数;如果A大于0,则A加上当前数。更新A之后,如果A大于B则更新B,否则继续读下一个数。(注意:A<0抛弃历史,A可以更新为当前数,但B不可以直接更新,因为B记录的是历史最大值)

29、递归

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n < 10:
            return 1 if n >= 1 else 0
        digit = self.get_digits(n)  # 位数
        low_nums = self.get_1_digits(digit-1)  # 最高位之前的1的个数
        high = int(str(n)[0])  # 最高位
        low = n - high * 10 ** (digit-1)  # 低位

        if high == 1:
            high_nums = low + 1  # 最高位上1的个数
            all_nums = high_nums
        else:
            high_nums = 10 ** (digit - 1)
            all_nums = high_nums + low_nums * (high - 1)  # 最高位大于1的话,统计每个多位数后面包含的1
        return low_nums + all_nums + self.NumberOf1Between1AndN_Solution(low)
    
    def get_digits(self,n):
        # 求整数n的位数
        ret = 0
        while n:
            ret += 1
            n /= 10
        return ret
    
    def get_1_digits(self,n):
        """
        获取每个位数之间1的总数
        :param n: 位数
        """
        if n <= 0:
            return 0
        if n == 1:
            return 1
        current = 9 * self.get_1_digits(n-1) + 10 ** (n-1)
        return self.get_1_digits(n-1) + current

1位数,1-9中,1一共出现了1次;

2位数,10-99中,10-19的十位上一共出现了10*1=10次,对于每个十位开头的数字10-19、20-29,每个数个位上出现的是1-9中1出现的次数,共有9个区间9*1=9次;

3位数,100-999,100-199百位上出现了10**2=100次,对于每个百位数开头,例如100-199,200-299,低位上其实就是0-99这个区间上1出现的次数,一共9个区间 9*19=171次;

由此推测,对于1-9,10-99,100-999,每个n位数中包含1的个数公式为:

      f(1) = 1

      f(2) = 9 * f(1) + 10 ** 1

      f(3) = 9 * f(2) + 10 ** 2

      f(n) = 9 * f(n-1) + 10 ** (n-1)

通过以上分析,我们可以确定对于任意一个给定的数,例如23456这个5位数,10000之前的数中包含的个数是确定的了,为f(1)+f(2)+f(3)+f(4),这是一个递归的过程,

通过上面的分析,我们知道了23456中,1-10000之间一共出现了多少个1.下一步需要分析10000-23456中包含的1.

     我们首先把最高位单独拿出来分析一下,求出最高位上1的个数,如果最高位是1,则最高位上一共会出现的1的次数是低位上数字+1,例如12345,最高位上一共出现了2346个1;如果最高位大于1,则会一共出现的次数是10000-19999一共10**4个数。

     然后,根据最高位的不同,计算出该高位前面的相同位数范围中的所有数中1的个数。例如对于34567,需要计算出10000-19999,20000-29999中一的个数,这时候计算一的个数,也就是计算0-9999中1的个数,这就可以转化成上面的f(n)来计算了,调用上面函数可以直接得到,然后用得到的值和最高位和1的差值(这里最高位是3)相乘就可以了。

分析完上面的部分后,我们现在只剩下最高位后面的部分了,我们发现剩下的部分还是一个整数,例如23456剩下了3456,这时候直接使用递归处理剩下的3456就行了。

class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        ones = 0
        m = 1
        while m <= n:
            a = n // m
            b = n % m
            ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1)
            m *= 10
        return ones

先以个位数字为例,每0-9中,个位上就会出现1个1。

以十位数字为例,每0-99中,十位上就会出现10个1。

以百位数字为例,每0-999中,百位上就会出现100个1。

以千位数字为例,每0-9999中,千位上就会出现1000个1。

按照如上规律,可以从个位开始,逐个分析每一位上能出现的1的个数,加总起来就可以了。

有很需要注意的细节问题。

例如1133中,1133/10是113,但实际上由于个位是3,0-3相当于也能凑够一个0-9,所以应当是114个个位的1。

同理,1133中,要考虑十位上的1的个数,1133/100是11,但由于十位是3,0-3相当于0-9,所以有12x10个十位的1。

如果数字是1113,要考虑十位上1的个数呢?情况变得更加复杂,因为1133能凑够12个100,但1113只能凑够11个整的100,余下的13,其十位上也有一个1,所以就是11个整的一百x每一百就有10个十位上的一,外加单独13中的4个十位上的1。

如果数字是1103呢?那就只有11个整的100,余下的03十位上没有1。

综合以上的叙述,大概形成下面的代码。虽然代码看起来只有几行,但道理比较难想通。举个例子按代码跑一遍就能好理解一些。以n=113为例。

m=1, a=113, b=0, a+8=121, (a+8)/10 = 12, 12x1=12. a%10=3,所以由于这个3中包含的1已经在前面a+8里考虑进去了,所以不再管了。

m=10, a=11, b=3, a+8=19, (a+8)/10=1(此时113中只考虑了0-99,而100-113中的十位的1没有考虑进去),1x10=10. a%10=1,所以单独拎出来考虑100-113,这当中的十位上有多少个1呢,从110-113,有4个十位上的1,所以b+1=4。

m=100, a=1, b=13, a+8=9, (a+8)/10=0 (所以此时并没有凑够0-999),而由于 a%10=1,所以单独考虑100-113的百位上的1,数量为13+1=14。

30、数组

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

# -*- coding:utf-8 -*-
Import functools
class Solution:
    def PrintMinNumber(self, numbers):
        if not numbers:
            return ""
        str_list = [str(n) for n in numbers]
        # python中,sort可以传入自定义的排序方法
        str_list.sort(functools.cmp_to_key(Sort))
        return int(‘‘.join(str_list))
def Sort(x, y):
    # 如何判断3和32谁应该排在前面呢?
    # 只要将他俩正反拼接一下,就知道了!这个排序的比较方法很骚气,完全不需要定义什么复杂的字典序规则!
    left = x + y
    right = y + x
    if left > right:
        return 1
    else:
        return -1

解题思路:注意cmp函数的使用。主要是依次考虑最高位,将最小的放到最前面。

31、数的除法

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

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index < 7:
            return index
        res = [1]
        two = 0
        three =0
        five =0
        while len(res) <index:
            a = res[two]*2
            b = res[three]*3
            c = res[five]*5
            tmp = min(a,b,c)
            res.append(tmp)
            if res[-1] == res[two]*2:
                two+=1
            if res[-1] == res[three]*3:
                three+=1
            if res[-1] == res[five]*5:
                five+=1
        return res[-1]

解题思路:靠先前的丑数来生成下面的,主要是用三个指针。我自己最先的思路是直接判断每一个,这样耗费的时间很多,因为要判断每个数是否都是丑数。只通过了84%的用例,有的没通过就是Index太大了,超过了时间的限制。

class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        res = [1]
        m = 10000
        for i in range(2,m):
            n = i
            while n!=1:
                while n % 5 == 0:
                    n = int(n / 5)
                while n % 3 == 0:
                    n = int(n / 3)
                while n % 2 == 0:
                    n = int(n / 2)
                if n == 1:
                    res.append(i)
                else:
                    break
        return res[index-1]

32、hash表

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        if not s:
            return -1
        # write code here
        res = {}
        for i in s:
            if i in res:
                res[i] +=1
            else:
                res[i] = 1
        for index,char in enumerate(s):
            if res[char] == 1:
                return index
        return -1

解题思路:首先利用hash表存储每个字母出现的次数,在python中Hash表用字典表示,然后在遍历字符串找到hash表中只出现一次的字符,并返回Index。

33、归并排序求逆序对

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

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.count=0
    def merge_count(self,arr):
        if len(arr)==1:
            return arr
        left=self.merge_count(arr[:len(arr)//2])
        right=self.merge_count(arr[len(arr)//2:])
        out,temp=self.merge(left,right)
        self.count+=temp
        return out
    def merge(self,left,right):
        # left和right都是两个升序排列的列表
        temp=0
        out=[]
        # 指针就是一个指明位置的标识,不需要必须是指针类型
        while(len(left)>0 and len(right)>0):
            if left[0]>right[0]:
                # 如果左边序列的当前值大于右边序列的当前值:
                temp+=len(left)
                out.append(right.pop(0))
            else:
                out.append(left.pop(0))
        out.extend(left)
        out.extend(right)
        return out,temp

    def InversePairs(self, data):
        # write code here
        if len(data)<=1:
            return 0
        if len(data)==2:
            if data[1]>data[0]:
                return 1
            else:
                return 0
        self.merge_count(data)
        return self.count

34、链表

输入两个链表,找出它们的第一个公共结点。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        if not pHead1 or not pHead2:
            return None
        len1 = 0
        len2 = 0
        p1 = pHead1
        p2 = pHead2
        while p1:
            len1+=1
            p1 = p1.next
        while p2:
            len2+=1
            p2 = p2.next  
        p1 = pHead1
        p2 = pHead2
        if len1 > len2:
            l = len1-len2
            while l:
                p1 = p1.next
                l-=1
        else:
            l = len2-len1
            while l:
                p2 = p2.next
                l-=1
        while id(p1) != id(p2):
            p1 = p1.next
            p2 = p2.next
        return p1

解题思路:注意链表公共节点的定义:地址和值都要一致,即第一个公共节点之后的节点都是公共节点。其中一个方法:先判断两个链表的长度,让长的先走,走到剩下的长度与另一个一致,再一起走,直到找到第一个公共节点。另一种方法:先遍历存入栈,然后一起出栈,找到第一个不同的

35、数组、hash表

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

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        dis = {}
        for i in data:
            if i not in dis:
                dis[i] = 1
            else:
                dis[i] += 1
        if k in dis:
            return dis[k]
        else:
            return 0

解题思路:还是使用hash表存储。

36、树、递归

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

# -*- coding:utf-8 -*- (25ms、5728k)
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        if not pRoot:
            return 0
        stack = [pRoot]
        count1 = 1
        count2 = 1
        while stack:
            for i in stack:
                t = stack.pop(0)
                if t.left:
                    count1 += 1
                    stack.append(t.left)
                if t.right:
                    count2 += 1
                    stack.append(t.right)
        return max(count1,count2)

解题思路:自己写的思路,就是层次遍历,有多少层,树的深度就是多少。

另一中思路:树的先序遍历。

class Solution: (28ms、5852k)
    def TreeDepth(self, pRoot):
        # write code here
        if not pRoot:
            return 0
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        return max(left, right) + 1

36、平衡二叉树、递归

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

class Solution: (31ms、6668k)
    def IsBalanced_Solution(self, pRoot):
        if pRoot is None:
            return True
        nleft = self.depth(pRoot.left)
        nright = self.depth(pRoot.right)
        if abs(nright-nleft)>1:
            return False
        return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
        
        
    def depth(self,pRoot):
        if pRoot is None:
            return 0
        left = self.depth(pRoot.left)
        right = self.depth(pRoot.right)
        return max(left,right)+1

解题思路:最好想的思路就是如上的思路,判断每一个节点的平衡度,但存在效率问题。

如下方法可解决:

class Solution: (26ms、5668k)
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return True
        # 判断左子树是否平衡,不平衡则直接终止判断,返回false
        is_left = self.IsBalanced_Solution(pRoot.left)
        if not is_left:
            return False
        # 判断右子树是否平衡,不平衡则直接终止判断,返回false
        is_right = self.IsBalanced_Solution(pRoot.right)
        if not is_right:
            return False
        # 左右均平衡,则计算深度,判断当前为根的树是否平衡
        left_depth = pRoot.left.depth if pRoot.left else 0
        right_depth = pRoot.right.depth if pRoot.right else 0
        pRoot.depth = max(left_depth, right_depth) + 1
        return abs(left_depth - right_depth) <= 1

37、数组、hash

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

class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        data = set()
        for d in array:
            if d in data:
                data.remove(d)
            else:
                data.add(d)
        return list(data)

解题思路:利用集合的特性

另一种是利用hash表:

    def FindNumsAppearOnce(self, array):
        # write code here
        count = {}      # 建立字典
        for i in array:                 
      if i not in count:
                count[i] = 1
            else:
                count[i] += 1      
        ls = []
        for i in array:    
            if count[i] == 1:
                ls.append(i)
        return ls

38、数组

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

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        if not tsum:
            return []
        result = []
        small = 1
        big = 2
        current_sum = small + big
        while small < (tsum + 1) / 2:
            while current_sum < tsum:
                big += 1
                current_sum += big
            while sum(range(small, big + 1)) > tsum:
                current_sum -= small
                small += 1
            if current_sum == tsum and small != big:
                result.append(list(range(small, big + 1)))
                big += 1
                current_sum += big
        return result

解题思路:用两个指针,一个small,一个big。开始时,small指向1,big指向2。我们以s=9为例,一开始{1,2},和为3,小于s,则让big往后移动,{1,2,3},再继续{1,2,3,4},此时和为10,大于s,则让small往后移动,{2,3,4},等于9,找到一个。然后继续增加big,{2,3,4,5}大于s,让small往后,{3,4,5}大于s,让small往后,{4,5}等于9,找到一个。再让big往后,{4,5,6}大于s,让small往后,{5,6},此时small已经大于9的一半了,不再继续寻找。

39、数组

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

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        if len(array) == 0:
            return []
        a = array
        i = 0
        j = len(a)-1
        small = a[i]
        big = a[j]
        res = []
        while i < j:
            csum = small + big
            while csum < tsum:
                i+=1
                small = a[i]
                csum = small + big
            while csum > tsum:
                j-=1
                big = a[j]
                csum = small+big
            if csum == tsum and i<j:
                res = []
                res.append(small)
                res.append(big)
                break
        return res

解题思路:两个指针,一个指向首端,一个指向尾端,找到第一个就是乘积最小的。(或者用hash表的方法)

def FindNumbersWithSum (array,target):
    r = []
    for i,value in enumerate(array):
        if target - value in array [i+1:]:
            r.append((value, target -value))
    return r

扩展:对于3个值、4个值等,只需要多加一层循环。

40、字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if len(s) == 0:
            return ""
        l = len(s)
        if n % l == 0:
            return s
        else:
            r = n % l
            return s[r:]+s[:r]

解题思路:判断长度。

以上是关于python-剑指offer21-40的主要内容,如果未能解决你的问题,请参考以下文章

Notes5剑指offer_21-40题

剑指Offer算法类题目[Python版]

剑指Offer算法类题目[Python版]

剑指offer反转链表python

剑指Offer[Python版]

剑指offer顺时针打印矩阵python