《剑指offer》python实现

这段时间事情略多,马上秋招,得赶紧把这些数据结构的习题做完,继续剑指offer的刷题之旅



字符串的排列

题目描述

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

输入描述:

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

思路

本题的主要思路是使用递归,个人认为递归最难的地方在于终止条件的判定,以及递归如何传递参数,这方面的学习还要继续加强

在本题中,递归的函数主要有两个变量,一个是字符串,一个是之前递归遍历过的字符串的一个排列,本题可以认为 对于一个字符串,任取一个位置的字符,对于剩下的子字符串进行递归找出所有的全排列,递归结束的条件为字符串所有的元素都取完,这时候将之前的字符串放在结果当中即可。

具体代码如下:

class Solution:
    def __init__(self):
        self.all=[]

    def Permutation(self, ss):
        # write code here
        if len(ss)==0:
            return self.all;
        else:
            self.listall(ss,"") #递归实现字符串的全排列
            result=list(set(self.all)) #这里使用set函数可以去除列表中的重复元素
            return sorted(result) #这里使用sorted函数可以返回一个对列表的元素进行排序后的列表,注意区别与sort函数
    
    def listall(self,ss,before):
        if not ss: #递归至需要递归的字符串的字符长度为0的时候停止
            self.all.append(before) #向结果列表中增加至此的所有排列组合
        else:
            for i in range(len(ss)):
                self.listall(ss[:i]+ss[i+1:],before+ss[i]) #对于每一个字符串去掉第i个位置的字符,与之前去掉的所有字符形成一个全排列
                #注意python中字符串的切割,s[m,n]是从m开始到n结束(不包括m)的字符串

具体来讲,在本代码中,分析一下对于"abc",调用
listall("abc","")

 def listall(self,ss,before):
        if not ss: 
            self.all.append(before)
        else:
            for i in range(len(ss)):
                self.listall(ss[:i]+ss[i+1:],before+ss[i]) 

程序进入else从i从0到2开始循环,此处仅分析i=0的时刻,继续调用本函数listall("bc","a"),即取第一个字符作为不变的字符,找bc的全排列,之后的调用为:

listall("c","ab")
listall("","abc")  此时向结果序列中增加“abc”
listall("b","ac")
listall("","acb")  此时向结果序列中增加“acb”
返回继续i=1,继续找寻另外字母作为首字母的全排

由此可见该递归的关键在于储存之前递归找到的某一个排列

另外在python中可以使用itertools库中自带的排列组合函数实现本题,方法如下

import itertools

class Solution:

    def Permutation(self, ss):
        # write code here
        if not ss:
            return []
        all=[]
        for i in itertools.permutations(ss):
            if ''.join(i) not in all:
                all.append(''.join(i))
        return all

数组中出现次数超过一半的数字

题目描述

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

思路

本题使用字典记录每一个数字出现的次数 找到出现次数超过一半的元素即可

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        store={}
        for i in numbers:
            if i in store: #使用in判断字典中是否含有i的引索元素
                store[i]+=1
            else:
                store[i]=1
            if store[i]>len(numbers)/2: #一旦找到出现次数超过数组长度一般的数字直接return
                return i
        return 0

最小的K个数

题目描述

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

思路

本题对数组进行排序排序,然后输出最小的k个数字即可。注意对于k大于数组长度的情况

class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        tinput.sort() #使用sort函数对数组进行排序
        if k>len(tinput): #对于k大于数组长度的情况 直接返回空的数组
            return []
        return tinput[:k]

连续子数组的最大和

题目描述

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

思路

本题使用动态规划法,记录从左开始的和,一旦和比之前存储的最大值大,更新和,由于会有负数元素,因此,一旦某一位数已经比之前所有的和大了,前面的所有累加对于找到最大子序列已经没有帮助了,此时,将和更新为该数,并将最大值更新为该数

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

整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

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

思路

一开始将本题看成找数字中出现过1的数字的个数,想着怎么去除11这种出现多个1的重复,说明审题还是很重要的

本题的主要思路为:
对于计算1-n中1出现的次数,分别计算每一位出现1的次数的情况,最后累加即可。

即:
F(n)=个位出现1的次数+十位出现1的次数+百位出现1的次数+千位出现1的次数+……

其中计算每一位的时候,需要注意的是:

计算该位出现1的次数,需要结合该位前的数字(注意数字的末位的三种情况),以及该位后的首位数字看。

  • 假设该n为54321:

    百位前的数字存储在tmp中,tmp=54,tmp末尾是4>1

    可以分析出:从001XX-541XX均满足要求,因此百位1出现的次数为(tmp+1)*100

  • 若该n为51321:

    百位前的数字tmp=51,tmp末尾是1=1

    因此从001XX-501XX,均满足要求,同时51000-51321也满足条件,因此百位1出现的次数为tmp*100+321+1

  • 若该n为50321:

    百位前的数字tmp=50,tmp末尾是0<1

    因此从001XX-491XX,均满足要求,因此百位出现1的次数为tmp*100

总结:

  1. 对于i位,如果i+1位的数字大于1,则i位出现1的次数为:(i位前的数字+1)*10i-1
  2. 对于i位,如果i+1位的数字等于1,则i位出现1的次数为:(i位前的数字)*10i-1+(i位后的数字+1)
  3. 对于i位,如果i+1为的数字小于1,则i位出现1的次数为:(i位前的数字)*10i-1
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        ones=0
        mul=1
        store=n #储存n的值,用于对某一高位的第一位为1的情况进行计算
        while(n!=0):
            tmp=n//10 #临时储存n的高位数字
            #根据当前低位的首位三种情况分别进行计算
            if n%10==0:
                ones+=tmp*mul
            elif n%10==1:
                ones+=tmp*mul
                ones+=store%mul+1
            else:
                ones+=(tmp+1)*mul
            n=n//10
            mul*=10
        return ones
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值