牛客网剑指offer编程实践31-40题

31、 整数中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、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析

1、根据设定的整数位置,对n进行分割,分为两部分,高位n/i,低位n%i

2、当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a%10+1)*100个点的百位为1

3、当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a%10(最高两位0-30)次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有(a%10*100)+(b+1),这些点百位对应为1

4、当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10=31(最高两位0~30)

5、综合以上三种情况,当百位对应0或>=2时,有(a+8)/10次包含所有100个点,还有当百位为1(a%10==1),需要增加局部点b+1

6、之所以补8,是因为当百位为0,则a/10==(a+8)/10,当百位>=2,补8会产生进位位,效果等同于(a/10+1)

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        i = 1
        count = 0
        while i <= n:
            a = n / i
            b = n % i
            count = count + (a+8)/10*i + (a%10==1)*(b+1)
            i *= 10
        return count

32、把数组排成最小的数

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

解答:

方法:比较字符串s1和s2大小时,即比较s1+s2和s2+s1的大小,小的放在前面,比较某一位的字符时,需要将这个位置和其后面所有位置的字符串进行比较。时间复杂度为O(n^2),注意两点:1、数组为空时要返回‘’;2、数组中全为0时,返回的是0,而不是00000.

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        if numbers == []:
            return ''
        for i in range(len(numbers)):
            for j in range(i+1,len(numbers)):
                tmp0 = int(str(numbers[i]) + str(numbers[j]))
                tmp1 = int(str(numbers[j]) + str(numbers[i]))
                if tmp0 > tmp1:
                    numbers[i],numbers[j] = numbers[j],numbers[i]
        res = ''
        for i in numbers:
            res += str(i)
        if int(res) == 0:
            return 0
        else:
            return res

33、丑数

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

解答:

方法1:暴力求解,任何丑数p,2p,3p,5p结果仍是偶数,1是最小的丑数,从1开始,将12,13,15比较,得到的最小丑数2,将得到的丑数2也同样2,3,*5,比较最小的数

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index < 7:
            return index
        res = [0] * index
        res[0] = 1
        tmp2 = 0
        tmp3 = 0
        tmp5 = 0
        for i in range(1,index):
            res[i] = min(res[tmp2]*2,res[tmp3]*3,res[tmp5]*5)
            if res[i] == res[tmp2]*2:
                tmp2 += 1
            if res[i] == res[tmp3]*3:
                tmp3 += 1
            if res[i] == res[tmp5]*5:
                tmp5 += 1
        return res[-1]

34、第一个只出现一次的字符

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

解答:

# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        res_dict = {}
        for i in range(len(s)):
            if s[i] not in res_dict:
                res_dict[s[i]] = 0
            res_dict[s[i]] += 1
        for i in range(len(s)):
            if res_dict[s[i]] == 1:
                return i
        return -1

35、数组中的逆序对

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

解答:

方法:归并排序,在合并时,当前面的数组值array[i]大于后面数组值array[j]时,则前面

数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.count = 0
    def InversePairs(self, data):
        if len(data) == 0:
            return 0
        # write code here
        self.MergeSort(data,0,len(data)-1)
        return self.count%1000000007
    
    def MergeSort(self,array,low,high):
        if low < high:
            mid = (low + high) >> 1
            self.MergeSort(array,low,mid)
            self.MergeSort(array,mid+1,high)
            self.count += self.MergeArray(array,low,mid,high)
​
    def MergeArray(self,array,low,mid,high):
        i = low
        j = mid + 1
        tmp = []
        count = 0
        while i <= mid and j <= high:
            if array[i] < array[j]:
                tmp.append(array[i])
                i += 1
            else:
                tmp.append(array[j])
                j += 1
                count += mid - i + 1
        while i <= mid:
            tmp.append(array[i])
            i += 1
        while j <= high:
            tmp.append(array[j])
            j += 1
        for k in range(len(tmp)):
            array[low + k] = tmp[k]
        return count

36、两个链表的第一个公共结点

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

解答:

方法1:

长度相同有公共结点,第一次就遍历到;没有公共结点,走到尾部NULL相遇,返回NULL  长度不同有公共结点,第一遍差值就出来了,第二遍一起到公共结点;没有公共,一起到结尾NULL。

方法2:

遍历两个链表,如果链表长度相同,找到第一个相同的结点;不相同时,先将长的链表移动到和短的链表相同长度的位置,然后找到第一个相同的结点

# -*- 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
        p1 = pHead1
        p2 = pHead2
        while p1 != p2:
            if not p1:
                p1 = pHead2
            else:
                p1 = p1.next
            if not p2:
                p2 = pHead1
            else:
                p2 = p2.next
        return p1
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        sum1 = self.get_length(pHead1)
        sum2 = self.get_length(pHead2)
        if sum1 > sum2:
            pHead1 = self.get_equal(pHead1,sum1-sum2)
        else:
            pHead2 = self.get_equal(pHead2,sum2-sum1)
        while pHead1 != pHead2:
            pHead1 = pHead1.next
            pHead2 = pHead2.next
        return pHead1
    
    def get_equal(self,pHead,s):
        while s > 0:
            pHead = pHead.next
            s -= 1
        return pHead
    
    def get_length(self,pHead):
        summ = 0
        while pHead:
            pHead = pHead.next
            summ += 1
        return summ

37、数字在排序数组中出现的次数

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

解答:

方法1:

排序数组是升序排列,且数组中的数都是整数,则可以使用tmp0 = k – 0.5 和tmp1 = k + 0.5两个数,这两个数在数组中都没有,但可以找到第一个大于tmp0和tmp1的数的index,相减就是最后k出现的次数

方法2:

如果数组中的数不是整数,则利用二分法找到k值的第一次出现的index和最后一次出现的index;注意考虑没有k值的情况。

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        tmp0 = k - 0.5
        tmp1 = k + 0.5
        count = self.get_index(data,tmp1) - self.get_index(data,tmp0)
        return count
​
    def get_index(self,data,k):
        begin = 0
        end = len(data) - 1
        while begin <= end:
            mid = (begin + end) >> 1
            if data[mid] < k:
                begin = mid + 1
            elif data[mid] > k :
                end = mid - 1
        return begin
    
class Solution:
    def GetNumberOfK(self, data, k):
        tmp0 = self.get_first(data,k,0,len(data)-1)
        tmp1 = self.get_last(data, k, 0, len(data) - 1)
        if tmp0 != -1 and tmp1 != -1:
            return tmp1 - tmp0 + 1
        return 0
​
​
    def get_first(self,data,k,begin,end):
        if begin > end:
            return -1
        mid = (begin + end) >> 1
        if data[mid] > k:
            return self.get_first(data,k,begin,mid-1)
        elif data[mid] < k:
            return self.get_first(data,k,mid+1,end)
        elif mid - 1 >= begin and data[mid-1] == k:
            return self.get_first(data,k,begin,mid-1)
        else:
            return mid
​
    def get_last(self,data,k,begin,end):
        while begin <= end:
            mid = (begin + end) >> 1
            if data[mid] > k:
                end = mid - 1
            elif data[mid] < k:
                begin = mid + 1
            elif mid + 1 <= end and data[mid+1] == k:
                begin = mid + 1
            else:
                return mid
        return -1
​

 

38、 二叉树的深度

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

解答:

方法1:

递归,递归实际也是深度优先的思想(DFS),时间复杂度为O(lgN),但是空间复杂度最坏为O(N),当二叉树退化为链表的时候。

方法2:

非递归,广度优先遍历BFS,时间复杂度O(N);利用两个辅助值sum_count和count;sum_count记录每层的结点个数,当count == sum_count时说明一层的结点都已经遍历完毕,depth+1

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def TreeDepth(self, pRoot):
        if not pRoot:
            return 0
        left = int(self.TreeDepth(pRoot.left))
        right = int(self.TreeDepth(pRoot.right))
        return max(left,right)+1
    
    def TreeDepth(self, pRoot):
        # write code here
        if not pRoot:
            return 0
        stack = []
        stack.append(pRoot)
        sum_count = 1
        count = 0
        depth = 0
        while stack:
            node = stack.pop(0)
            count += 1
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
            if count == sum_count:
                count = 0
                sum_count = len(stack)
                depth += 1
        return depth
        

39、平衡二叉树

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

解答:

方法1:递归法

有了求二叉树的深度的经验之后,很容易想到一个思路:遍历每个结点的时候,得到它的左右结点的深度。如果每个结点的左右二叉树的深度相差都不超过1,就是平衡二叉树。

但是这个方法每个结点都被重复遍历,效率不高

方法2:自底向上

如果我们用后序遍历的方式遍历二叉树的每一个结点,在遍历到一个结点之前我们就已经遍历了它的左右子树。只要在遍历每个结点的时候几下它的深度,就可以一次遍历判断每个结点是不是平衡二叉树。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        depth = self.get_depth(pRoot)
        if depth == -1:
            return False
        return True
    
    def get_depth(self,pRoot):
        if not pRoot:
            return 0
        left = self.get_depth(pRoot.left)
        if left == -1:
            return -1
        right = self.get_depth(pRoot.right)
        if right == -1:
            return -1
        if abs(left-right) <= 1:
            return max(left,right) + 1
        else:
            return -1
        
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return True
        left = self.get_depth(pRoot.left)
        right = self.get_depth(pRoot.right)
        return abs(left - right) <= 1 and self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
​
    def get_depth(self, pRoot):
        if not pRoot:
            return 0
        return max(self.get_depth(pRoot.left), self.get_depth(pRoot.right)) + 1

40、 数组中只出现一次的数字

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

解答:

方法:

1、考虑数组中只有一个数只出现一次,则将所有数字异或就可以得到这个数。

2、两个数不一样,则异或的结果至少有一位为1,我们找到第一个为1的位置,记作n;

找到数字k第一个为1的位

3、根据第n为是否为1的标准可以将数组分为2个部分,这两个只出现一次的数就分别在这两个部分中,在通过异或即可得到结果

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        res = 0
        for i in array:
            res ^= i
        index = self.get_index(res)
        num0 = num1 = 0
        for i in array:
            if self.get_sep(i,index) == 0:
                num0 ^= i
            else:
                num1 ^= i
        return num0,num1
​
    def get_index(self,k):
        index = 0
        while k & 1 == 0:
            k = k >> 1
            index += 1
        return index
    
    def get_sep(self,k,index):
        k = k >> index
        return k & 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值