july算法课笔记
来源:互联网 发布:计算机专业与大数据 编辑:程序博客网 时间:2024/06/08 17:22
# coding=utf-8# 第一题'''给定某字符串S,该字符串中有若干空格,删除这些空格,并返回修改后的字符串;要求时间复杂度O(N),空间复杂度O(1)。如:“I_have_a___dream!”,返回“Ihaveadream”注:有可能两个单词间存在若干空格。'''import copyimport pprintimport randomimport reimport collectionsdef is_remove(a_str): pattern = re.compile(r"[\u4e00-\u9fa5]") find_list = pattern.findall(a_str) if find_list: return False else: return Truedef remove_blank(a_in_str, is_remove): size = len(a_in_str) str_list = list(a_in_str) i = 0 # 遍历所有的字符 j = 0 # 遍历所有的有效字符,因此j<=i while (i < size): if is_remove(str_list[i]): i += 1 else: if j < i: str_list[j] = str_list[i] i += 1 j += 1 return "".join(str_list[:j]) # print remove_blank("asdb dfd",is_remove)# 第二题:'''给定一个数组,找出该数组最大的数。'''def find_max(a_int_list): if not a_int_list: return max = a_int_list[0] for i in a_int_list: if i > max: max = i return max# print find_max([1,3,4,5,6,4,5])# 第三题'''给定一个数组,找出该数组最大和次大的两个数。'''def find_two_max(a_int_list): if not a_int_list: return if len(a_int_list) < 2: return max = a_int_list[0] max_2 = a_int_list[1] for i in a_int_list: if i > max: max = i elif i > max_2: max_2 = i return max, max_2# print find_two_max([1,3,4,5,6,4,5])# 哈夫曼编码'''Huffman编码Huffman编码是一种无损压缩编码方案。思想:根据源字符出现的(估算)概率对字符编码,概率高的字符使用较短的编码,概率低的使用较长的编码,从而使得编码后的字符串长度期望最小。Huffman编码是一种贪心算法:每次总选择两个最小概率的字符结点合并。称字符出现的次数为频数,则概率约等于频数除以字符总长;因此,概率可以用频数代替。'''# 计算给定字符串中每个字符的个数def calc_frequency(a_in_str): counter = collections.defaultdict(lambda: 0) for a_char in a_in_str: counter[a_char] += 1 return counter# 定义树节点class HuffmanNode(): def __init__(self): self.value = 0 self.parent = None self.left_child = None self.right_child = Nonedef select_two_mini(hn_list): # 选出两个较小的节点 # if not hn_list: return mini_1 = None mini_2 = None i = 0 for node in hn_list: if not node.value or node.parent is not None: # 如果已经在树的叶子节点,则continue i = i + 1 continue else: if mini_1 is None: mini_1 = i i += 1 continue if mini_2 is None: mini_2 = i i += 1 continue elif node.value < hn_list[mini_1].value: mini_1 = i elif node.value < hn_list[mini_2].value: mini_2 = i i += 1 return mini_1, mini_2def HuffmanCoding(char_counter_dict): if not char_counter_dict: return # 字符列表 char_list = char_counter_dict.keys() # 值列表 value_list = char_counter_dict.values() leaf = len(char_counter_dict) # 共有2n-1 个节点 node_num = 2 * leaf - 1 # 先把所有的节点生成 hn_list = [HuffmanNode() for _ in range(node_num)] # 先把leaf节点放在hn_list的前n个位置上 for i in range(leaf): hn_list[i].value = value_list[i] # 每次选择最小的两个节点建树,填补后面的hn_list,parent,child字段全部放的是索引号 for i in range(leaf, node_num): node1, node2 = select_two_mini(hn_list) hn_list[node1].parent = hn_list[node2].parent = i hn_list[i].left_child = node1 hn_list[i].right_child = node2 hn_list[i].value = hn_list[node1].value + hn_list[node2].value print hn_list # 根据建立好的哈夫曼树来计算从叶子到根计算每个叶子节点的哈夫曼编码 huffman_codding = [] for i in range(leaf): a_codding = "" child = i pt = hn_list[i].parent while (pt): if hn_list[pt].left_child == child: a_codding += "0" else: a_codding += "1" child = pt pt = hn_list[pt].parent a_codding = a_codding[::-1] huffman_codding.append(a_codding) return dict(zip(char_list, huffman_codding))# a_dict = {"A": 15, "B": 7, "C": 6, "D": 6, "E": 5}# print HuffmanCoding(a_dict)'''思考题:字符串循环左移给定一个字符串S[0…N-1],要求把S的前k个字符移动到S的尾部,如把字符串“abcdef”前面的2个字符‘a’、‘b’移动到字符串的尾部,得到新字符串“cdefab”:即字符串循环左移k。循环左移n+k位和k位的效果相同。多说一句:循环左移k位等价于循环右移n-k位。算法要求:时间复杂度为O(n),空间复杂度为O(1)。解法:首先将字符串a分为两段,要移动的前i位为b,剩余部分为c,则字符串可表示为bc,左移i位的结果其实就是cb,则算法的目的就是将bc转换为cb。首先将b逆序为b',再将c逆序为c',最后将(b'c')'整体逆序即得到cb。'''def reverse_str(a_str_list, start, end): if not a_str_list: return while (end > start): tem = a_str_list[start] a_str_list[start] = a_str_list[end] a_str_list[end] = tem start += 1 end -= 1def circulation(a_in_str, k_len): a_list = list(a_in_str) reverse_str(a_list, 0, k_len - 1) reverse_str(a_list, k_len, len(a_in_str) - 1) reverse_str(a_list, 0, len(a_in_str) - 1) return "".join(a_list)# print circulation("abcde",2)##############################################第二课链表、队列、堆栈#####################################################'''Eratosthenes筛法求素数给定正整数N,求小于等于N的全部素数。Eratosthenes筛法将2到N写成一排;记排头元素为x,则x是素数;除x以外,将x的倍数全部划去;重复以上操作,直到没有元素被划去,则剩余的即小于等于N的全部素数。为表述方面,将排头元素称为“筛数”。'''# 算法改进: 当筛a时,a*a 以内的数已经完成了筛选,不包括a*a# 注意这种标记法def Eratosthenes(end_num): all_num_flags = [True for i in xrange(1, end_num + 2)] all_num_flags[0] = False # 为了编写代码方便,0位置设为不可用 # 首先默认全部数是素数 p = 2 # 第一个筛子 end_range = p * p while end_range <= end_num: # 任何一个筛子p 的第一个合数是 p*p cp = end_range while cp <= end_num: all_num_flags[cp] = False cp += p # 筛选下一个筛子 p += 1 while not all_num_flags[p]: p += 1 end_range = p * p i = 0 prime_num_list = [] for flag in all_num_flags: if flag: prime_num_list.append(i) i += 1 return prime_num_list'''链表相加给定两个链表,分别表示两个非负整数。它们的数字逆序存储在链表中,且每个结点只存储一个数字,计算两个数的和,并且返回和的链表头指针。如:输入:2→4→3、5→6→4,输出:7→0→8'''class AddNode(): def __init__(self): self.value = None self.next = Nonedef add_node_list(nl1, nl2): if not nl2: return nl1 if not nl1: return nl2 head = AddNode() cur = head carry = 0 # 进位 while (nl1 and nl2): value = nl1.value + nl2.value + carry carry = value // 10 value = value % 10 new_node = AddNode() new_node.value = value cur.next = new_node cur = cur.next nl1 = nl1.next nl2 = nl2.next # 将长的链表的未向附在后边 if nl1: long_nl = nl1 else: long_nl = nl2 while (long_nl): value = long_nl.value + carry new_node = AddNode() new_node.value = value long_nl = long_nl.next cur.next = new_node cur = cur.next return head.nextdef print_node_list(nl): head = nl while (head): print head.value, head = head.next print "\n"def test_add_node_list(): nl1 = AddNode() cur = nl1 for i in range(3): new_node = AddNode() new_node.value = random.randint(1, 9) cur.next = new_node cur = cur.next nl2 = AddNode() cur = nl2 for i in range(4): new_node = AddNode() new_node.value = random.randint(1, 9) cur.next = new_node cur = cur.next print_node_list(nl1.next) print_node_list(nl2.next) head = add_node_list(nl1.next, nl2.next) print_node_list(head)# test_add_node_list()"""链表的部分翻转给定一个链表,翻转该链表从m到n的位置。要求直接翻转而非申请新空间。如:给定^-1→2→3→4→5,m=2,n=4,返回^-1→4→3→2→5。假定给出的参数满足:1≤m≤n≤链表长度。"""class int_node(): def __init__(self): self.value = None self.next = Nonedef part_reverse(nl, start, end): if end <= start: return p_head = nl # 头插的头部节点 for i in range(start - 1): p_head = nl.next p_end = p_head.next # 最终的尾部 p_cur = p_end.next # 当前要头插的节点 for _ in range(end - start): # 头插法 # 1 先释放p_cur.next : 结果的尾部指向当前要改变的尾部 p_end.next = p_cur.next # 2 再填补上p_cur.next :当前的尾部指向头的下一个节点 p_cur.next = p_head.next # 3 再填补上p_head.next :头的下一个节点指向当前 p_head.next = p_cur # 4 完成当前移动之后,让p_cur 指向下一个需要移动的节点 p_cur = p_end.nextdef test_part_reverse(): head = int_node() p_cur = head head.value = -1 for i in range(10): new_node = int_node() new_node.value = random.randint(1, 9) p_cur.next = new_node p_cur = new_node print_node_list(head) part_reverse(head, 1, 10) print "翻转后" print_node_list(head)# test_part_reverse()'''最长公共子序列,即Longest CommonSubsequence,LCS。一个序列S任意删除若干个字符得到新序列T,则T叫做S的子序列;两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列。 字符串13455与245576的最长公共子序列为455 字符串acdfg与adfc的最长公共子序列为adf注意区别最长公共子串(Longest Common Substring) 最长公共字串要求连续解法:属于动态规划问题需要前面所有规划路径上的解才能解决问题一般情况下都是先求长度,再求具体值初始条件:LCS(X,0) = 0状态转移方程:LCS(Xm,Yn)=LCS(Xm-1,Yn-1) 当xm = ym 时LCS(Xm,Yn)=MAX(LCS(Xm-1,Yn),LCS(Xm,Yn-1)) 当xm != ym 时得到迭代过程中的全部解之后,回溯棋盘可得最终解空间状态转移方程变为数据结构:Xm序列和Yn序列分别横置和竖置,变为一个(m+1)*(n+1)的数组棋盘(i,j)格子代表Xm[:i],Yn[:j]的最大公共子序列的长度当(i,j)对应字符不相等时,取左、上中较大的数当(i,j)对应字符相等时,取对角的数+1从末尾元素回溯棋盘,记录走斜线的格子,就是解空间'''def lcs(Xm, Yn): m = len(Xm) n = len(Yn) chess = [] first_row = [] for j in range(n + 1): first_row.append(0) for i in range(m + 1): chess.append(copy.deepcopy(first_row)) for i in range(1, m + 1): for j in range(1, n + 1): if Xm[i - 1] == Yn[j - 1]: chess[i][j] = chess[i - 1][j - 1] + 1 else: chess[i][j] = max([chess[i - 1][j], chess[i][j - 1]]) # pprint.pprint (chess) return chessdef recall_lcs(Xm, Yn, chess): print chess m = len(Xm) n = len(Yn) result = [] while (m != 0 and n != 0): if Xm[m - 1] == Yn[n - 1]: result.append(Yn[n - 1]) m -= 1 n -= 1 else: if chess[m][n - 1] >= chess[m - 1][n]: n -= 1 else: m -= 1 result.reverse() print "".join(result)# Xm="abcdefg"# Yn="bcafegd"# chess = lcs(Xm,Yn)# recall_lcs(Xm,Yn,chess)def recall_lcs_dfs(Xm, Yn, m, n, chess, result): ''' 深度优先算法,发现可能有重复解,两条路径有交叉点的情况 递归的核心是把上下文放入参数当中,这样在遇到n岔路时可以把上下文传入, 递归执行n-1个路口,然后返回,继续执行最后一个路口,递归结束 :param Xm: :param Yn: :param m: :param n: :param chess: :param result: :return: ''' rl = list(result) while (m != 0 and n != 0): if Xm[m - 1] == Yn[n - 1]: rl.append(Yn[n - 1]) m -= 1 n -= 1 else: if chess[m][n - 1] > chess[m - 1][n]: n -= 1 elif chess[m][n - 1] == chess[m - 1][n]: # 当前节点有两条路可选,选一条路进行递归,把上下文打包传入 recall_lcs_dfs(Xm, Yn, m - 1, n, chess, "".join(rl)) # 回到岔路口走下一条路 n -= 1 else: m -= 1 rl.reverse() print "".join(rl)def test_recall_lcs_dfs(): Xm = "efghab" Yn = "egfahb" chess = lcs(Xm, Yn) print chess result = "" recall_lcs_dfs(Xm, Yn, 6, 6, chess, result)#'''原数组为A {5,6,7,1,2,8}排序后:A’{1,2,5,6,7,8}因为,原数组A的子序列顺序保持不变,而且排序后A’本身就是递增的,这样,就保证了两序列的最长公共子序列的递增特性。如此,若想求数组A的最长递增子序列,其实就是求数组A与它的排序数组A’的最长公共子序列。 此外,本题也可以直接使用动态规划/贪心法来求解贪心法就是一阶马尔科夫模型动态规划就是高阶马尔科夫模型都具有无后效性:只需要看前面的结果,而前面的结果是不改变的。总结:历史是不可改变的,未来是不可预期的,现在是能从历史推演出来的!最长递增子序列: 动态规划解法:贪心法'''# 动态规划法def lis_dp(a_int_list): # 构造二维数组 results = [] # for i in range(len(a_int_list)): # results.append([]) j = 0 max = 0 # 考察每个元素,每个元素的初始长度为其本身, for i in a_int_list: results.append([i]) # 循环查询前面的j个结果,如果能更新,就更新二维数组 for h in range(j): if i > results[h][-1] and len(results[h]) + 1 > len(results[j]): temp2 = copy.deepcopy(results[h]) temp2.append(i) if len(temp2) > len(results[j]): results[j] = temp2 if len(results[j]) > len(results[max]): max = j j += 1 print results print max return results[max]# a_int_list = [3, 5, 8, 7, 4, 9, 12, 6]# print lis_dp(a_int_list)# 贪心法def greedy_lis(a_int_list): temp = [] fl = [] for _ in a_int_list: fl.append(False) fl[0] = True for num in a_int_list: if not temp: temp.append(num) continue if num > temp[-1]: temp.append(num) else: # 替换掉temp中比num稍微大一点点的元素 # for i in range(len(temp)): # if temp[i]>num: # temp[i]=num # 改为折半查找: low = 0 high = len(temp) - 1 while (low < high): mid = (low + high) / 2 if num < temp[mid]: high = mid - 1 else: low = mid + 1 if temp[low] > num: temp[low] = num else: temp[low + 1] = num print temp print len(temp)# a_int_list = [3, 5, 8, 7, 4, 9, 12, 6]# greedy_lis(a_int_list)'''给定字符串S[0…N-1],设计算法,枚举S的全排列一般这种题就是考递归的思想的,解法:'''def swap(a_int_list, a, b): if a == b: return else: temp = a_int_list[a] a_int_list[a] = a_int_list[b] a_int_list[b] = tempdef is_duplicate(a_int_list,i_start,i): while(i_start<i): if a_int_list[i_start] == a_int_list[i]: return True i_start+=1 else: return False#def Permutation(a_int_list,size,i_start): # 这个递归上下文又开始点i_start 和 size 唯一确定, # 每次i_start 都向下移动,而size不变 # 出口条件为i_start 移动到了最后一个元素,也就是任何单个元素的全排列只有他自己 #i_start 从0开始计数 if i_start == size-1: print a_int_list return swaped = set([]) for i in range(i_start,size): # a[i]是当前要交换的元素 # if is_duplicate(a_int_list,i_start,i): # # a[i]是否与[i_start,i)重复,如果当前要交换的元素,在之前已经交换过了,就不交换了 # continue # 考空间换时间 if a_int_list[i] in swaped: continue swaped.add(a_int_list[i] ) swap(a_int_list,i,i_start) Permutation(a_int_list,size,i_start+1) swap(a_int_list,i_start,i)#Permutation([1,2,3,2],4,0)# KMP算法,面试常考,必考!'''字符串查找问题 给定文本串text和模式串pattern,从文本串text中找出模式串pattern第一次出现的位置。最基本的字符串匹配算法 暴力求解(Brute Force) :时间复杂度O(m*n)KMP算法是一种线性时间复杂度的字符串匹配算法,它是对BF算法改进。记:文本串长度为N,模式串长度为M BF算法的时间复杂度O(M*N),空间复杂度O(1) KMP算法的时间复杂度O(M+N),空间复杂度O(M)'''# 暴力求解方案def brute_force_search(src_str, aim_str): src_size = len(src_str) aim_size = len(aim_str) remove_len = src_size - aim_size src_cur = 0 # 源窜指针 aim_cur = 0 # 目标窜指针 while src_cur < remove_len and aim_cur < aim_size: if src_str[src_cur+aim_cur] == aim_str[aim_cur]: aim_cur += 1 else: src_cur += 1 aim_cur = 0 if aim_cur >= aim_size: return src_cur return -1# print brute_force_search("abcdefg", "cde")# KMP算法# step1 递归法求next数组'''模式串 a b a a b c a b anext -1 0 0 1 1 2 0 1 2例如:j=5时,考察字符串“abaab”的最大相等k前缀和k后缀 为ab,因此c对应的next数为2递归关系,当我知道前5个数的next数组时,考察c的next数因为已经知道第五个元素b对应1,因此,只需要考察第二个元素和第五个元素是否相等,若相等则为1+1, 若不相等则'''def get_next(src_str): size = len(src_str) next_list = [-1, ] # k 表示当前考察元素的前一个元素对应的next数 # 即,k=next[j-1] k = -1 # j 表示当前考察的元素 j = 0 while j < size - 1: if k == -1 or src_str[k] == src_str[j]: j += 1 k += 1 next_list.append(k) else: k = next_list[k] return next_list# print get_next("abaa")# get next数组这个程序太吊了,完全理解不了def kmp(src_str,aim_str): next_list = get_next(aim_str) ans = -1 src_size = len(src_str) aim_size = len(aim_str) src_cur = 0 # 源窜指针 aim_cur = 0 # 目标窜指针 while src_cur < src_size : if aim_cur==-1 or src_str[src_cur] == aim_str[aim_cur]: src_cur += 1 aim_cur += 1 else: # aim_cur变为下一个个比较的位置,因为由于aim窜的前后缀相同性,可以认为前面的比较已经通过了 aim_cur = next_list[aim_cur] if aim_cur == aim_size: ans = src_cur - aim_size break return ans# print kmp("acfcfeccfcecfcfecdcdefg", "cfcfecd")
1 0
- july算法课笔记
- July深度学习笔记之神经网络与反向传播算法
- July算法研究1
- july的算法博客地址
- github-july-完美洗牌算法
- 关于链表的算法..对于july大神的文章的学习笔记
- 关于堆栈的算法...对于july大神的文章的学习笔记
- 关于数组的算法...对于july大神的文章的学习笔记
- 微软算法题--采自July博客
- 【转july】一、A*搜索算法
- 【转july】二、Dijkstra 算法初探
- Adaboost 算法的原理与推导(JUly)
- kaggle 案例实战 七月 july 算法
- 微软等数据结构+算法面试100题 by July
- 数据结构+算法面试100题~~~摘自CSDN,作者July
- 数据结构+算法面试100题~~~摘自CSDN,作者July
- 快速排序算法的深入分析 -- 十二 July
- 数据结构+算法面试100题~~~摘自CSDN,作者July
- 深入理解Tomcat系列之六:Servlet工作原理
- hadoop学习之总目录(1):安装和使用完全分布式hadoop-2.7.2及其家族其他成员
- hadoop学习结构图
- 《A Survey of Image Segmentation techniques》 Thakur 2014文章的阅读思维导图
- 使用API,手工生成 Windows应用程序全过程(图解+文字说明)
- july算法课笔记
- 项目1-三角行雏形设计三角行带参的构造函数
- C++学习笔记55——类模板的输入输出操作符
- Javascript中apply、call、bind
- 深入理解Tomcat系列之七:详解URL请求
- ThreadLocal类源码
- 何为大,世间没有比生命更大,珍惜生命
- c#+windows环境 编写mysql集群服务
- 1个viewController怎么关联2个XIB,一个是iphone的XIB,一个是ipad的xib