41、数组
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
# -*- coding:utf-8 -*- class Solution: def ReverseSentence(self, s): # write code here if s is None: return "" alist = list(s.split(" ")) return " ".join(alist[::-1])
解题思路:列表的翻转
42、数组
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
# -*- coding:utf-8 -*- class Solution: def IsContinuous(self, numbers): # write code here if len(numbers) == 0: return False numbers = sorted(numbers) count_0 = 0 for i in range(0,len(numbers)-1): if numbers[i] == 0: count_0 += 1 else: delta = numbers[i+1] - numbers[i] if delta < 1: return False elif delta > 1: count_0 = count_0 - delta + 1 if count_0 < 0: return False return True
解题思路:大小王可以视作0,然后把数组排序。从头开始读数组,统计0的个数,然后读后面的数字,相邻两个数字之间如有间隔差值,则用0的个数来填补,如果后面的数字本来就是连续的则肯定ok,如果中间有不连续且刚好能被0填补,则也是顺子。
43、环、数组
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
# -*- coding:utf-8 -*- (140ms、5724k) class Solution: def LastRemaining_Solution(self, n, m): # write code here if n == 0 or m == 0: return -1 a = [i for i in range(n)] while (len(a) > 1): r = int((m - 1) % len(a)) a.remove(a[r]) a = a[r:] + a[:r] return a[0]
解题思路:我自己的解法就是找到,删除,调整数组。速度有点慢
另一种就是只保存删除的索引,效率较高:
class Solution: def LastRemaining_Solution(self, n, m): # write code here if n < 1: return -1 con = list(range(n)) final = -1 start = 0 while con: k = (start + m - 1) % n final = con.pop(k) n -= 1 start = k return final
44、加法
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
# -*- coding:utf-8 -*- class Solution: def Sum_Solution(self, n): ans = n temp = ans and self.Sum_Solution(n-1) ans = ans + temp return ans
解题思路:递归。注意 python 的写法。当and和or等短路运算符,用作普通值而不是布尔值时,短路运算符的返回值是最后一次评估的参数。也就是说下面的句子中,如果ans为0,则temp为0, 如果ans不为0, 则temp就是递归了之后的值。
通过位运算的方法:
首先看十进制是如何做的: 5+7=12,三步走
第一步:相加各位的值,不算进位,得到2。
第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。
第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。
同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111
第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。
第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111) << 1。
第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010) << 1。 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。
public class Solution { public int Add(int num1,int num2) { while (num2!=0) { int temp = num1^num2; num2 = (num1&num2)<<1; num1 = temp; } return num1; } } # -*- coding:utf-8 -*- class Solution: def Add(self, a, b): while(b): a, b = (a^b) & 0xFFFFFFFF,((a&b)<<1) & 0xFFFFFFFF return a if a<=0x7FFFFFFF else ~(a^0xFFFFFFFF)
补充一下python位运算:
x >> y # 返回 x 向右移 y 位得到的结果
x << y # 返回 x 向左移 y 位得到的结果
x & y # 与操作,xy对应的每一位,只有都为1时才为1
x | y # 或操作,xy对应的每一位,只有都为0时才为0
~x # 按位取反操作,x的每一位如果为0则变为1,如果为1则变为0,从十进制来看,结果是 -x - 1(例如,~8 = -9)
x ^ y # 异或运算,xy对应的每一位,数字不同则为1,数字相同则为0
45、字符串
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
# -*- coding:utf-8 -*- class Solution: def StrToInt(self, s): # write code here minus = 1 number = 0 if not s: return number if s[0] == '+': if len(s) > 1: s = s[1:] else: return 0 elif s[0] == '-': minus = -1 if len(s) > 1: s = s[1:] else: return 0 for i in range(0, len(s)): if '0' <= s[i] <= '9': number = number * 10 + ord(s[i]) - ord('0') else: return 0 return number * minus
解题思路:记得ord(‘0’)=48,ord(‘a’)=97,ord(‘A’)=65
46、数组
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
# -*- coding:utf-8 -*- class Solution: # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0] # 函数返回True/False def duplicate(self, numbers, duplication): # write code here dis = {} for i in list(numbers): if i not in dis: dis[i] = 1 else: dis[i] += 1 j = 0 for i in dis: if dis[i] == 1: continue elif dis[i] > 1: j = i break if j == 0: return False else: duplication[0] = j return True
解题思路:hash表
47、数组
给定一个数组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]。不能使用除法。
class Solution: def multiply(self, A): ifnot A: return [] b = [1] * len(A) for i in range(1,len(A)): b[i] = b[i] * A[i-1] tmp = 1 for i in range(len(A)-2,1,-1): tmp = tmp * A[i+1] b[i] = b[i] *tmp return b
解题思路:将乘积分为两部分。
48、字符串匹配
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
# -*- coding:utf-8 -*- class Solution: # s, pattern都是字符串 def match(self, s, pattern): # 如果s与pattern都为空,则True if len(s) == 0 and len(pattern) == 0: return True # 如果s不为空,而pattern为空,则False elif len(s) != 0 and len(pattern) == 0: return False # 如果s为空,而pattern不为空,则需要判断 elif len(s) == 0 and len(pattern) != 0: # pattern中的第二个字符为*,则pattern后移两位继续比较 if len(pattern) > 1 and pattern[1] == '*': return self.match(s, pattern[2:]) else: return False # s与pattern都不为空的情况 else: # pattern的第二个字符为*的情况 if len(pattern) > 1 and pattern[1] == '*': # s与pattern的第一个元素不同,则s不变,pattern后移两位,相当于pattern前两位当成空 if s[0] != pattern[0] and pattern[0] != '.': return self.match(s, pattern[2:]) else: # 如果s[0]与pattern[0]相同,且pattern[1]为*,这个时候有三种情况 # pattern后移2个,s不变;相当于把pattern前两位当成空,匹配后面的 # pattern后移2个,s后移1个;相当于pattern前两位与s[0]匹配 # pattern不变,s后移1个;相当于pattern前两位,与s中的多位进行匹配,因为*可以匹配多位 return self.match(s, pattern[2:]) or self.match(s[1:], pattern[2:]) or self.match(s[1:], pattern) # pattern第二个字符不为*的情况 else: if s[0] == pattern[0] or pattern[0] == '.': return self.match(s[1:], pattern[1:]) else: return False
49、类似字符串匹配
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
# -*- coding:utf-8 -*- class Solution: # s字符串 def isNumeric(self, s): # 标记符号、小数点、e是否出现过 sign = False decimal = False has_e = False for i in range(len(s)): if s[i] == 'e' or s[i] == 'E': if i == len(s) - 1: return False # E后面必须跟数字,E不可以在字符串结尾 if has_e: return False # 不可以出现两个E has_e = True elif s[i] == '+' or s[i] == '-': if sign and (s[i-1] != 'e' and s[i-1] != 'E'): return False # 如果是第二次出现符号,符号必须跟在E之后 if (not sign) and (i > 0) and (s[i-1] != 'e' and s[i-1] != 'E'): return False # 如果第一次出现符号,且不是在开头出现,则必须紧跟在E之后 sign = True elif s[i] == '.': if has_e or decimal: return False # E后面不能出现小数点,或小数点不能出现两次 decimal = True elif s[i] < '0' or s[i] > '9': return False # 不合法字符 return True
50、hash
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
class Solution: def __init__(self): self.stream = [] self.counter = {} # 返回对应char def FirstAppearingOnce(self): for c in self.stream: if self.counter[c] == 1: return c return '#' def Insert(self, char): if char in self.counter: self.counter[char] += 1 else: self.counter[char] = 1 self.stream.append(char)
51、环、链表
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
# -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def EntryNodeOfLoop(self, pHead): # write code here if not pHead: return None slow = pHead fast = pHead while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: slow2 = pHead while slow2 != slow: slow = slow.next slow2 = slow2.next return slow
解题思路:快慢指针法,当slow与fast相遇时,slow走过的就是环的长度。
52、链表
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
# -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def deleteDuplication(self, pHead): # write code here if (not pHead) or (not pHead.next): return pHead head = ListNode(0) head.next = pHead pre = head last = head.next while last: if last.next and last.val == last.next.val: while last.next and last.val == last.next.val: last = last.next pre.next = last.next last = last.next else: pre = pre.next last = last.next return head.next
解题思路:注意构建新的链表。
53、树
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
# -*- coding:utf-8 -*- # class TreeLinkNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None # self.next = None class Solution: def GetNext(self, pNode): # write code here if not pNode: return None if pNode.right != None: r = pNode.right while r and r.left != None: r = r.left elif pNode.next != None: if pNode.left == None: if pNode.next.left == pNode: r = pNode.next elif pNode.next.next.left != pNode.next: return None else: r = pNode.next.next else: r = pNode.next else: r = None return r
解题思路:要考虑多种情况
54、树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
# -*- coding:utf-8 -*- # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isSymmetrical(self, pRoot): if not pRoot: return True else: return self.check(pRoot.left, pRoot.right) def check(self, left, right): if (not left) and (not right): return True elif not(left and right): return False if left.val != right.val: return False return self.check(left.left, right.right) and self.check(right.left, left.right)
解题思路:递归加判断。
55、树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
# -*- coding:utf-8 -*- # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def Print(self, pRoot): # write code here if not pRoot: return [] stack = [pRoot] res = [] while stack: tmp = [] for i in range(len(stack)): t = stack.pop(0) tmp.append(t.val) if t.left != None: stack.append(t.left) if t.right != None: stack.append(t.right) res.append(tmp) for i in range(len(res)): if i & 1 == 1: res[i] = res[i][::-1] return res
解题思路:先层次遍历,再转换。
56、树
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
# -*- coding:utf-8 -*- # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: # 返回二维列表[[1,2],[4,5]] def Print(self, pRoot): # write code here if not pRoot: return [] stack = [pRoot] res = [] while stack: tmp = [] for i in range(len(stack)): t = stack.pop(0) tmp.append(t.val) if t.left != None: stack.append(t.left) if t.right != None: stack.append(t.right) res.append(tmp) return res
解题思路:就是层次遍历。
57、树
请实现两个函数,分别用来序列化和反序列化二叉树
# -*- coding:utf-8 -*- # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def __init__(self): self.flag = -1 def Serialize(self, root): # write code here if not root: return '#,' return str(root.val)+','+self.Serialize(root.left)+self.Serialize(root.right) def Deserialize(self, s): # write code here self.flag += 1 l = s.split(',') if self.flag >= len(s): return None root = None if l[self.flag] != '#': root = TreeNode(int(l[self.flag])) root.left = self.Deserialize(s) root.right = self.Deserialize(s) return root
解题思路:递归前序遍历二叉树,对每个节点的孩子如果遇到空就以#来代替,最终得到一个字符串。读取字符串,当前字符作为根,下一个字符作为当前的左孩子,依此递归进行前序遍历,恢复树结构。
58、二叉排序树
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
# -*- coding:utf-8 -*- # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: # 返回对应节点TreeNode def __init__(self): self.cur = 0 def KthNode(self, pRoot, k): # write code here if k <= 0: return None if pRoot == None: return None left_val = self.KthNode(pRoot.left,k) if left_val: return left_val self.cur += 1 if self.cur == k: return pRoot right_val = self.KthNode(pRoot.right, k) if right_val: return right_val return None
解题思路:递归左子树,并判断有无返回节点。如果有,停止递归,返回所要返回的节点;当左子树没有返回节点时,判断当前的根节点是不是第k个遍历到的值(即第k大)。如果是,则返回该节点,停止递归;当左子树和根节点都没有返回节点时,递归右子树,并判断有无返回节点。如果有,停止递归,返回所要返回的节点。
59、堆
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
# -*- coding:utf-8 -*- class Solution: def __init__(self): self.arr=[] def Insert(self, num): self.arr.append(num) self.arr.sort() def GetMedian(self,fuck): length=len(self.arr) return self.arr[length//2] if length%2==1 else (self.arr[length//2]+self.arr[length//2-1])/2.0
这是一般人的思想,另一种是考虑用堆;
import math import heapq class Solution: nums = [] def Insert(self, num): heapq.heappush(self.nums, num) def GetMedian(self): mid = math.ceil(len(self.nums)/2) return (heapq.nlargest(mid, self.nums)[-1] + heapq.nsmallest(mid, self.nums)[-1])/2.0
60、数组
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
# -*- coding:utf-8 -*- class Solution: def maxInWindows(self, num, size): # write code here if size <= 0: return [] left = 0 right = size - 1 res = [] while right <= len(num) - 1: tmp = num[left:right+1] m = max(tmp) res.append(m) left += 1 right += 1 return res
61、递归、回溯、匹配
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
class Solution: def hasPath(self, matrix, rows, cols, path): # write code here if not matrix and rows <= 0 and cols <= 0 and path == None: return False # 模拟的字符矩阵 markmatrix = [0] * (rows * cols) pathlength = 0 # 从第一个开始递归,当然第一二个字符可能并不位于字符串之上,所以有这样一个双层循环找起点用的,一旦找到第一个符合的字符串,就开始进入递归, # 返回的第一个return Ture就直接跳出循环了。 for row in range(rows): for col in range(cols): if self.hasPathCore(matrix, rows, cols, row, col, path, pathlength, markmatrix): return True return False def hasPathCore(self, matrix, rows, cols, row, col, path, pathlength, markmatrix): # 说明已经找到该路径,可以返回True if len(path) == pathlength: return True hasPath = False if row >= 0 and row < rows and col >= 0 and col < cols and matrix[row * cols + col] == path[pathlength] and not \ markmatrix[row * cols + col]: pathlength += 1 markmatrix[row * cols + col] = True # 进行该值上下左右的递归 hasPath = self.hasPathCore(matrix, rows, cols, row - 1, col, path, pathlength, markmatrix) \ or self.hasPathCore(matrix, rows, cols, row, col - 1, path, pathlength, markmatrix) \ or self.hasPathCore(matrix, rows, cols, row + 1, col, path, pathlength, markmatrix) \ or self.hasPathCore(matrix, rows, cols, row, col + 1, path, pathlength, markmatrix) # 对标记矩阵进行布尔值标记 if not hasPath: pathlength -= 1 markmatrix[row * cols + col] = False return hasPath
62、递归、回溯
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
# -*- coding:utf-8 -*- class Solution: def movingCount(self, threshold, rows, cols): # write code here markmatrix = [False] * (rows * cols) count = self.GetNum(threshold, rows, cols, 0, 0, markmatrix) return count def GetNum(self, threshold, rows, cols, row, col, markmatrix): count = 0 if self.GetSum(threshold, rows, cols, row, col, markmatrix): markmatrix[row * cols + col] = True count = 1 + self.GetNum(threshold, rows, cols, row - 1, col, markmatrix) + \ self.GetNum(threshold, rows, cols, row, col - 1, markmatrix) + \ self.GetNum(threshold, rows, cols, row + 1, col, markmatrix) + \ self.GetNum(threshold, rows, cols, row, col + 1, markmatrix) return count def GetSum(self, threshold, rows, cols, row, col, markmatrix): if row >= 0 and row < rows and col >= 0 and col < cols and self.getDigit(row) + self.getDigit( col) <= threshold and not markmatrix[row * cols + col]: return True return False def getDigit(self, number): sumNum = 0 while number > 0: sumNum += number % 10 number = number // 10 return sumNum
解题思路:回溯法,递归,注意判断条件。