3.1.17 快速排序
【考点映射】
- 排序
- 分治法
【出现频度】★★★★☆
【难度】★★★☆
【参考答案】
快速排序(Quick Sort)使用分治法策略。它的基本思想是:在数据序列中选择一个元素作为基准值,每躺从数据序列的两端开始交替进行,将小于基准值元素交换到序列前端,将大于基准值的元素交换到序列后端,介于两者之间的位置则成为基准值的最终位置。同时,序列被划分成两个子序列,再分别对两个子序列进行快速排序,直到子序列长度为1,则完成排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
动画图解:

代码示例(Python):
def quick_sort(lists, left, right): ''' 快速排序(升序)【不稳定】 原理: 分治递归,给定一组序列,把序列分为两部分,前部分的所有元素比后部分的所有元素小,然后再对前后两部分进行快速排序,递归该过程,直到所有记录 均有序为止。分三步走: (1)分解,array[m,...,n] 划分成 array1[m,..,k] 和 array2[k+1,...,n], 其中 array1 所有元素 < array2 所有元素 (2)递归求解,递归array1, array2 (3)合并,array[m,...,n] 自动排好序 :param lists: :param left: :param right: :return lists: ''' if left >= right: return lists key = lists[left] low = left high = right while left < right: while left < right and lists[right] >= key: right -= 1 lists[left] = lists[right] while left < right and lists[left] <= key: left += 1 lists[right] = lists[left] lists[right] = key quick_sort(lists, low, left - 1) quick_sort(lists, left + 1, high) return lists if __name__ == '__main__': # 测试代码 lst = [5, 4, 3, 2, 1] # 调用归并排序 sorted_list = quick_sort(lst, 0, len(lst) - 1) print(sorted_list) # 输出:[1, 2, 3, 4, 5]
3.1.18 斐波那契数列
【考点映射】
- 递归
- 循环
【出现频度】★★★☆
【难度】★★
【题目描述】
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、…… 基于python用多种方式,找到前n项斐波那契数列或找到第n项斐波那契数列的值。
【参考答案】
下面介绍3种解法:
解法一:递归法
递归法主要的好处在于,算法思路比较清晰,我们都知道除了前2个数之外,其他数都是由它的前两个数相加而得到。但是通过递归,我们只能获取第n项斐波那契数列的值,却难以得知前n项数列元素分别是什么。
解法二:迭代法
迭代法可以用一个列表,可以把前n项的斐波那契数列的数值都存储起来,这样我们就可以知道前n项数列元素都有哪些。但是假如n特别大,对于内存的占用也会较高。
解法三:升级版迭代法
可以用生成器代替列表,因为生成器不会把值都存入到内存中,仅当用到时才会动态计算,所以对于内存的占用会小很多。
代码示例(Python):
# (1)递归法 返回 idx 位的数值,缺点:只能返回最终返回值 def fib_recursion(idx): if idx <= 2: return 1 else: return fib_recursion(idx-1) + fib_recursion(idx-2) # (2)迭代法 返回 idx 位之前的fib数列 def fib_iteration(idx): lst = [] n,a,b = 0,0,1 while n < idx: lst.append(b) a,b = b, a+b n += 1 return lst # (3)生成器法 def fib_generator(idx): n,a,b = 0,0,1 while n < idx: yield b a,b = b, a+b n += 1 if __name__ == '__main__': idx = 6 # 递归法调用 numb = fib_recursion(idx) print(numb) # 输出: 8 # 迭代法调用 lst = fib_iteration(idx) print(lst) # 输出: [1, 1, 2, 3, 5, 8] # 生成器法 lst1 = fib_generator(idx) print(list(lst1)) # 输出: [1, 1, 2, 3, 5, 8]
3.1.18 丑数
【考点映射】
- 动态规划
【出现频度】★★☆
【难度】★★★☆
【题目描述】
只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。一般地,认为1是第一个丑数,1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18是最前面的13个丑数
【参考答案】
原理:本题可采用动态规划思想解题。后面的丑数一定是由前面的丑数乘以2、3或5得到。
所以第n个丑数一定是由前n-1个数中的某3个丑数(分别记为index2、index3、index5)分别乘以2、3或者5得到的数中的最小数,index2,index3,index5有个特点,即分别乘以2、3、5得到的数一定含有比第n-1个丑数大(可利用反证法:否则第n-1个丑数就是它们当中的一个)最小丑数,即第n个丑数由u[index2]2、u[index3]3、u[index5]*5中的最小数得出。让它们分别和第n个丑数比较,若和第n个丑数相等,则更新它们的值。
注:一次最少更新一个值(如遇到第n个丑数是6时,index2和index3都要更新)。
代码示例(Python):
def ugly_num(n): # 边界值判断 if n <= 0: return False t1 = 0 t2 = 0 t3 = 0 res = [1] while len(res) < n: res.append(min(res[t1] * 2, res[t2] * 3, res[t3] * 5)) if res[-1] == res[t1] * 2: t1 += 1 if res[-1] == res[t2] * 3: t2 += 1 if res[-1] == res[t3] * 5: t3 += 1 return res[-1] if __name__ == '__main__': # 测试代码 # 求第10位丑数 ugly_number = ugly_num(10) print(ugly_number) # 输出 12
3.1.19 如何对字符串进行反转
【考点映射】
- 双指针迭代
- 字符串
【出现频度】★★★★☆
【难度】☆
【题目描述】
给定一个字符串,反转该字符串。
例如:s = "hello world" => s = "dlrow olleh"
【参考答案】
此题有两种解法:
解法1:双指针迭代法
解法2:python切片
解法1 是大家必须要掌握的方法,双指针迭代法运用非常广泛,特别适用于处理线性表,像反转链表、移动零、线性排序算法等等,都常常会用到双指针迭代。
解法2 是一种讨巧的办法,在工作中可以使用。但在面试中,如果用第二种方法可能会达不到面试官的考核目的,很大可能会被追问:“还有没有别的解法”,如果这个时候答不出其他的解法,可能会对面试结果产生影响。
代码示例(Python):
# 普通方法:使用双指针遍历,交换两元素位置 def reverse_str(input_str): char_list = list(input_str) lens=len( char_list ) i=0 j=lens-1 while i < j: tmp= char_list[i] char_list[i] = char_list[j] char_list[j]=tmp i+=1 j-=1 return ''.join(char_list) # 切片法(Python) new_str = input_str[::-1]
3.1.20 统计字符串中每个字母出现的次数
【考点映射】
- 哈希表
- 字符串
【出现频度】★★★☆
【难度】★★
【题目描述】
统计字符串s='I love python'中,每个字母出现的次数
【参考答案】
此题有三种解法:(Python版)
解法1:使用第三方库collections
解法2:利用字符串自带的count()方法进行字符统计
解法3:利用字典的特性来解题
代码示例(Python):
# 给定一个字符串 s = 'I love python' # 解法1:使用collections库中的Counter类去进行次数统计 from collections import Counter d1 = dict(Counter(s)) # 结果:d = {'I': 1, ' ': 2, 'l': 1, 'o': 2, 'v': 1, 'e': 1, 'p': 1, 'y': 1, 't': 1, 'h': 1, 'n': 1} # 假如说想要去掉空格的统计,只需要 d.pop(' ')即可,结果是: # {'I': 1, 'l': 1, 'o': 2, 'v': 1, 'e': 1, 'p': 1, 'y': 1, 't': 1, 'h': 1, 'n': 1} # 解法2:利用字符串自带count()方法进行字符统计 d2 = {} for i in s: d2[i] = s.count(i) # 结果:d = {'I': 1, ' ': 2, 'l': 1, 'o': 2, 'v': 1, 'e': 1, 'p': 1, 'y': 1, 't': 1, 'h': 1, 'n': 1} # 解法3:利用循环和字典key不重复的特性,去统计字符 from collections import defaultdict s = 'I love python' d3 = defaultdict(int) for i in range(len(s)): d3[s[i]] += 1 # 结果:defaultdict(<class 'int'>, {'I': 1, ' ': 2, 'l': 1, 'o': 2, 'v': 1, 'e': 1, 'p': 1, 'y': 1, 't': 1, 'h': 1, 'n': 1})