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