八、排序与查找-下
本系列博客基于“ (北京大学)数据结构与算法python版”慕课,课程在中国大学慕课和bilibili上均可找到。
1. 内容
- 散列函数的定义和设计
- 散列冲突的解决方法
- 区块链技术
2. 课程代码
在GitHub中下载
3. OJ作业
所有代码均可在github中下载
3.1 字符串中所有重排
题目内容:
给定一个字符串s与待查找字符串p,请给出使得s[i: i+len ( p ) ]是p的一个字母重排的所有下标i。题目保证字符串p非空,且各字符串仅由小写字母构成
输入格式: 两行字符串,第一行为s,第二行为p
输出格式:所有满足条件的下标从小到大排列,以空格分隔输出。若无对应下标,则输出字符串"none"
输入样例:
abababa
ab
输出样例:
0 1 2 3 4 5
输入样例2:
a
b
输出样例2:
none
方法:通过一个变换,让几个字母任意组合的单词的结果都相同 方法: ascii码相加得到一个hash key。需要注意的是,直接相加会导致ac bb的key是相同的,所以给每个ascii码加上权重。最终函数为 ord(element)*(ord(element-96)) # 加上了24个字母自己的位置信息
def findAnagrams(s, p):
# code here
word_lenth = len(p)
search_key = hash_key(p) # 将需要搜索的字符转化为key
record_index = [] # 记录s可以的index
for i in range(0, len(s)-word_lenth+1): # [0, len(s)-len(p)+1]
current_key = hash_key(s[i:i+word_lenth]) # 将当前取的切片转化为key
if current_key == search_key:
record_index.append(i)
if len(record_index): # 如果找到了
string = ' '.join(str(item) for item in record_index)
print(string)
else: # 没找到
print('none')
return
def hash_key(word):
key_sum = 0
for element in word: # 取出每一位字母
key_sum += ord(element)*(ord(element)-ord('a'))
return key_sum
s = input()
p = input()
findAnagrams(s, p)
3.2 列表出现最频繁的元素
题目内容:
给定一个列表与数字K,按出现次数倒序输出列表中前K个出现最频繁的元素;若少于K个元素则返回所有元素。若有两个或以上的元素出现次数相等,按元素的值进行顺序输出,小的在前。
输入格式: 输入为两行。第一行为给定列表,以合法的Python表达式给出。第二行为数字K
输出格式:不多于K个数字,以空格分隔
输入样例:
[2,1,1,1,2,2,3]
2
输出样例:
1 2
方法:建立一个字典 存储 数字,数字出现次数。再把字典变成一个二维列表,对二维列表的第二维进行倒序排列,第二维是数字出现次数。再建立一个二维列表,将相同次数的数字放在一起,进行正序排序 输出时再把大家合并
代码
def topKFrequent(lst, k):
# code here
count_dict = {}
for item in lst:
if item in count_dict: # 已经记录了该item
count_dict[item] += 1
else: # 第一次出现
count_dict[item] = 1
count_list = []
for key in count_dict.keys(): # 将字典变为二维数组
count_list.append([key, count_dict[key]])
count_list.sort(key=(lambda x: x[1]), reverse=True) # 使用出现次数对数组进行排序
string = [] # 输出序列
# 将出现次数相同的数字切片,排序后加入到输出序列
i = 0 # 索引
same_num_list = [] # 数字表
same_count = -1 # 次数
while i < len(count_list): # 当还没有搜索到最后一位
if same_count != count_list[i][1]: # 如果现在次数不相等
if len(same_num_list): # 且此时相同数字列表不为空(说明不是第一次)
string.extend(sorted(same_num_list)) # 将数字表正序排列后加入到输出序列
# 为空的话跳过
same_num_list = [] # 清空列表
same_count = count_list[i][1] # 赋值新次数
while i < len(count_list) and same_count == count_list[i][1]: # 相等的话,都加入到相同数字表
same_num_list.append(count_list[i][0])
i = i+1
string.extend(sorted(same_num_list)) # 将最后一组加入
if k < len(string): # 输出前k个
joined_string = ' '.join(str(item) for item in string[:k])
else:
joined_string = ' '.join(str(item) for item in string)
print(joined_string)
return
lst = eval(input())
k = int(input())
topKFrequent(lst, k)
3.3 散列表
题目内容:
给定一个指定大小N的散列表,并输入一系列数字:若找到空槽,则插入该数字,并返回槽位置;若该数字在散列表中存在,则直接输出其位置。
注:使用下标增加的二次探测法解决散列冲突
注2:散列表实际大小应确定为不小于用户输入N的最小质数
输入格式: 两行。第一行为用户指定散列表大小N。第二行为一系列数字,以空格分隔
输出格式:逐个输出对应数字在散列表中位置,以空格分隔。若该数字无法插入,则输出“-”
输入样例:
4
10 6 4 10 15
输出样例:
0 1 4 0 -
方法:由于题目规定了三列表大小 所以先求大于等于N的最小质数 方法就是逐步加1 判断是否是质数。
插入方法: 找到开始位置,当位置小于列表长度并且没有停止–继续循环: 如果该位置有数且等于要查找数,停止并返回位置 如果该位置没数,填入数并返回位置并停止
其余情况k=k+1 position = position+k*k
最后一个样例没有通过
代码:
def createHashTable(n):
# 建立哈希表
create_size = n # 从n+1开始遍历
stop = False
while not stop:
for divisor in range(2, create_size//2+1): # 对于10,遍历 2,3,4,5
if create_size % divisor == 0: # 不是质数
create_size = create_size+1
break
else: # 每一位都不能除尽 所以是素数
stop = True
return [None]*create_size
def insertNumbers(table, nums):
# code here
t_len = len(table) # 散列表长度
insert_index_list = [] # 存储插入的位置
for item in nums: # 取出每一位数字
position = item % t_len # 最开始的位置
k = 1 # skip
stop = False
record = [] # 记录查找了哪些位置了
while not stop:
if table[position] == None: # 没有数
table[position] = item
insert_index_list.append(str(position))
stop = True
elif table[position] == item: # 存在数并相等
insert_index_list.append(str(position))
stop = True
else: # 寻找下一个位置
position = (item+k*k) % t_len
if (position in record) or k >= t_len: # 如果已经查找过该位置 说明找不到
stop = True
insert_index_list.append('-')
k = k+1
record.append(position)
string = ' '.join(item for item in insert_index_list)
print(string)
return
n = int(input()) # 用户决定的散列表大小
nums = list(map(int, input().split()))
table = createHashTable(n)
insertNumbers(table, nums)