├── __init__.py ├── bit_algorithms ├── __init__.py └── bit_algorithms.py ├── math_algorithms ├── __init__.py └── math_algorithms.py ├── other_algorithms ├── __init__.py └── etc_algorithms.py ├── queue_algorithms └── __init__.py ├── stack_algorithms ├── __init__.py └── stack_algorithms.py ├── dynamic_programming ├── __init__.py ├── time_stat.py ├── longest_common_string.py └── fibonacci_sequence.py ├── linkedlist_algorithms ├── __init__.py └── linkedlist_algorithms.py ├── searching_algorithms ├── __init__.py └── searching_algorithms.py ├── sorting_algorithms ├── __init__.py └── sorting_algorithms.py ├── string_algorithms ├── __init__.py └── string_algorithms.py ├── .idea ├── misc.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── modules.xml ├── untitled.iml └── workspace.xml └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:45 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /bit_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:36 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /math_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:40 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /other_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:41 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /queue_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:43 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /stack_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:42 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /dynamic_programming/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:34 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /linkedlist_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:40 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /searching_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:37 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /sorting_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:38 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /string_algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:39 4 | # @Author : Cecil Charlie 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data_Structures_and_Algorithms 2 | A range of codes including data structures and algorithms for interviews with Python 3 | 4 | This python package contains much data structures and algorithms questions raised in interviews by famous IT companies in China. 5 | Users could use the solving methods integrated as functions with detailed explanations. 6 | Or, users could browse my blogs in "http://blog.csdn.net/dongrixinyu?ref=toolbar". 7 | More detailed instructions have been provided in there. 8 | 9 | -------------------------------------------------------------------------------- /.idea/untitled.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /dynamic_programming/time_stat.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | # @Time : 20-05-15 下午5:15 3 | # @Author : Cecil Charlie 4 | # ------------------------------------------------------------------------ 5 | # 计算耗时的工具 6 | 7 | import time 8 | import typing 9 | 10 | 11 | class TimeStat(object): 12 | def __init__(self, name: str = None): 13 | self.name = name 14 | self.total_time = 0 15 | self.start_time = 0 16 | 17 | def __enter__(self,): 18 | self.start_time = time.time() 19 | 20 | def __exit__(self, *args): 21 | 22 | self.total_time = time.time() - self.start_time 23 | if self.name is not None: 24 | print('`{0:s}` costs {1:.4f} seconds'.format( 25 | self.name. self.total_time)) 26 | else: 27 | print('Costs {:.4f} seconds'.format(self.total_time)) 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /math_algorithms/math_algorithms.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf-8 -*- 3 | 4 | import math 5 | 6 | class MathAlgorithms(object): 7 | def __init__(self): 8 | pass 9 | 10 | def greatest_common_divisor_1(self, num1, num2): 11 | ''' 12 | 数值计算寻找最大公约数,给定两个整数,计算其最大公约数,时间复杂度为 o(min(num1,num2)),取余运算复杂度高 13 | ''' 14 | gbc = 1 15 | for i in xrange(2, min(num1, num2)+1): 16 | if num2 % i == 0 and num1 % i == 0: 17 | gbc = i 18 | return gbc 19 | 20 | def greatest_common_divisor_2(self, num1, num2): 21 | ''' 22 | 辗转相减法,时间复杂度最差为 o(min(num1,num2)),一般情况下都比这个要好。相减运算要比除法方便很多 23 | ''' 24 | while num1 != num2: 25 | if num1 > num2: 26 | num1 = num1 - num2 27 | else: 28 | num2 = num2 - num1 29 | return num1 30 | 31 | def greatest_common_divisor_3(self, num1, num2): 32 | ''' 33 | 求余数法,取模运算比较麻烦,时间复杂度低 o(log max(num1, num2)) 34 | ''' 35 | while num1 != num2: 36 | if num1 > num2: 37 | if num1 % num2 == 0: 38 | return num2 39 | num1 = num1 % num2 40 | else: 41 | if num2 % num1 == 0: 42 | return num1 43 | num2 = num2 % num1 44 | return num1 45 | 46 | def greatest_common_divisor(self, num1, num2): 47 | ''' 48 | 求两个数的最大公约数 49 | 综合取余法和辗转相减法,既能得到较好的时间复杂度,又能避免取余运算,时间复杂度稳定 o(log max(num1,num2)) 50 | 如果取两个非常大的数的话,前面的方法很容易爆栈、取余困难等等,但是该方法没有问题 51 | a = 999999342353200 52 | b = 777774234 53 | print greatest_common_divisor(a, b) 54 | ''' 55 | factor = 1 56 | if num1 < num2: 57 | return greatest_common_divisor_1(num2, num1) 58 | while num1 != num2: 59 | if num1 & 1 is False and num2 & 1 is False: # 均为偶数 60 | num1 = num1 >> 1 61 | num2 = num2 >> 2 62 | factor *= 2 63 | elif num1 & 1 is False and num2 & 1 is True: 64 | num1 = num1 >> 1 65 | elif num1 & 1 is True and num2 & 1 is False: 66 | num2 = num2 >> 1 67 | else: 68 | if num1 > num2: 69 | num1 = num1 - num2 70 | else: 71 | num2 = num2 - num1 72 | return factor*num1 73 | 74 | 75 | a = 454242353200 76 | b = 1400 77 | mathalgorithms = MathAlgorithms() 78 | print mathalgorithms.greatest_common_divisor(a, b) 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /dynamic_programming/longest_common_string.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf8 -*- 2 | # @Time : 18-1-3 下午2:53 3 | # @Author : Cecil Charlie 4 | # ------------------------------------------------------------------------ 5 | ''' 6 | 计算两字符串的最长公共子串 7 | ------------------------------------------------------------------------ 8 | 字符串1:人工智能在自然语言处理上的应用 9 | 字符串2:自然语言处理的应用非常广泛 10 | 最长公共子串:自然语言处理 11 | 12 | 包括三种方法: 13 | 一、直接计算法,计算复杂度 o(n**3) 14 | 二、采用动态规划的方法保存下已经计算的变量,计算复杂度 o(n**2),空间复杂度 o(len(str1) * len(str2)) 15 | 三、删除临时保存的矩阵,节省空间复杂度 16 | ''' 17 | 18 | import numpy as np 19 | 20 | 21 | class LongestCommonStringDynamicProgramming(object): 22 | def __init__(self): 23 | pass 24 | 25 | def __call__(self, string_1, string_2, flag=True): 26 | ''' 动态规化法记录比较矩阵,若子串 str1 和 str2 是相同的, 27 | 则 str1[:-1] 和 str2[:-1] 一定是相同的。 28 | ''' 29 | length_1 = len(string_1) 30 | length_2 = len(string_2) 31 | 32 | map_matrix = np.zeros((length_1 + 1, length_2 + 1), dtype='int') 33 | 34 | m_max = 0 #最长匹配的长度 35 | position = 0 #最长匹配对应在 s1 中的最后一位 36 | for i in range(length_1): 37 | for j in range(length_2): 38 | # 对其中不必要的比较做删减,加快计算速度 39 | # rule1: 子串在 string_2 上已经不可能再长过当前的最大子串 40 | if flag: 41 | if length_2 - j + map_matrix[i][j] < m_max: 42 | break 43 | 44 | # rule2: 子串在 string_1 上已经不可能再长过当前最大子串 45 | if length_2 - i + map_matrix[i][j] < m_max: 46 | continue 47 | 48 | if string_1[i] == string_2[j]: 49 | map_matrix[i + 1][j + 1] = map_matrix[i][j] + 1 50 | if map_matrix[i + 1][j + 1] > m_max: 51 | m_max = map_matrix[i + 1][j + 1] 52 | position = i + 1 53 | ''' 54 | print(' ' + ' '.join(list(string_2))) 55 | for idx, i in enumerate(map_matrix): 56 | if idx == 0: 57 | print(' ', i) 58 | else: 59 | print(string_1[idx - 1], i) 60 | ''' 61 | return string_1[position - m_max: position], int(m_max) 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | if __name__ == '__main__': 73 | import time 74 | from time_stat import TimeStat 75 | 76 | lcs_dp = LongestCommonStringDynamicProgramming() 77 | string_1 = '123456778' 78 | string_2 = '129834567486782' 79 | max_iter = 10000 80 | 81 | with TimeStat() as ts: 82 | for i in range(max_iter): 83 | res = lcs_dp(string_1, string_2, flag=False) 84 | with TimeStat() as ts: 85 | for i in range(max_iter): 86 | res = lcs_dp(string_1, string_2, flag=True) 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /other_algorithms/etc_algorithms.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | 3 | import random 4 | 5 | 6 | class EightQueensPuzzle(object): 7 | ''' 8 | 八皇后问题求解 9 | 代码使用方法: 10 | eight_q = EightQueensPuzzle(4, 5) 11 | print "EIGHT QUEEDS PUZZLE:" 12 | result = eight_q.eight_queens_puzzle() 13 | for i in result: 14 | print i 15 | ''' 16 | def __init__(self, n, char): 17 | self.n = n # 棋盘维度 18 | self.char = char # 皇后标记字符 19 | 20 | def init_chess_board(self, n): 21 | ''' 22 | 初始化一个棋盘,棋盘规格可以按参数 n 随意选定,一般都讨论八皇后,就选择 8 23 | :return: 返回棋盘,是一个 8*8 矩阵 24 | ''' 25 | chess_board = [] 26 | for i in xrange(0, n): 27 | line = [] 28 | for j in xrange(0, n): 29 | line.append(0) 30 | chess_board.append(line) 31 | return chess_board 32 | 33 | def update_conflict_board(self, conflict_board, position): 34 | for k in xrange(0, self.n): # 为行添加 1 35 | conflict_board[position[0]][k] = 1 36 | for id in xrange(position[0]+1, self.n): 37 | conflict_board[id][position[1]] = 1 # 为列添加 1 38 | if position[0] + position[1] - id >= 0: # 为左斜添加 1 39 | conflict_board[id][position[0] + position[1] - id] = 1 40 | if position[1] - position[0] + id < self.n: # 为右斜添加 1 41 | conflict_board[id][position[1] - position[0] + id] = 1 42 | 43 | def queens_conflict(self, conflict_board, position): 44 | ''' 45 | 当前棋盘的状态是 conflict_board, 判定如果在 position 位置给一个皇后的话,会不会出现问题。 46 | 如果有问题则返回 False,如果没有问题返回 True 47 | ''' 48 | if conflict_board[position[0]][position[1]] != 0: 49 | return False 50 | else: 51 | return True 52 | 53 | def eight_queens_puzzle(self): 54 | ''' 55 | 给出一个八皇后的求解答案。 56 | :return:返回一个结果并打印. 57 | ''' 58 | import random 59 | while True: # 不停寻找符合条件的八皇后排列 60 | chess_board = self.init_chess_board(self.n) 61 | conflict_board = self.init_chess_board(self.n) 62 | for i in xrange(0, self.n): 63 | flag = 0 64 | for cnt in conflict_board[i]: 65 | if cnt != 0: 66 | flag += 1 67 | if flag == self.n: # 如果已经1被填满了,说明这个答案错误 68 | break 69 | 70 | while True: 71 | pos = [i, random.randint(0, self.n-1)] # 元组构成皇后的位置 72 | if self.queens_conflict(conflict_board, pos): # 如果没有冲突 73 | chess_board[i][pos[1]] = self.char 74 | self.update_conflict_board(conflict_board, pos) 75 | break 76 | if self.char in chess_board[self.n-1]: 77 | return chess_board 78 | 79 | -------------------------------------------------------------------------------- /bit_algorithms/bit_algorithms.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python2.7 2 | # -*- coding=utf8 -*- 3 | # @Time : 18-1-3 下午8:35 4 | # @Author : Cecil Charlie 5 | 6 | 7 | class BitAlgorithms(object): 8 | ''' 9 | 位运算算法合集 10 | ''' 11 | 12 | def __init__(self): 13 | pass 14 | 15 | def exchage_nums(self, a, b): 16 | ''' 17 | 不用额外变量交换两个整数值,空间节省了,时间负责度略高一些。 18 | ''' 19 | a = a ^ b 20 | b = a ^ b 21 | a = a ^ b 22 | return a, b 23 | 24 | def add_bit(self, a, b): 25 | ''' 26 | 位运算实现两个整数相加,python里只能针对正数来处理 27 | ''' 28 | sum = a 29 | while b != 0: 30 | sum = a ^ b 31 | b = (a & b) << 1 32 | a = sum 33 | return sum 34 | 35 | def count1(self, a): 36 | ''' 37 | 整数的二进制表达里有多少个1,复杂度为a的二进制长度。 38 | ''' 39 | num = 0 40 | while a != 0: 41 | num += a & 1 42 | a >>= 1 43 | return num 44 | 45 | def count2(self, a): 46 | ''' 47 | 整数的二进制表达里有多少个1,复杂度仅为1的个数 48 | ''' 49 | num = 0 50 | while a != 0: 51 | a = a & (a - 1) 52 | num += 1 53 | return num 54 | 55 | def print_odd_times_num1(self, arr): 56 | ''' 57 | 给定一个数组,数据都是整数,其中只有一个数字出现了奇数次,其它都是偶数次。找出那个奇数次的数字。 58 | 时间复杂度o(n),空间复杂度为o(1)。 59 | 整数 n 与 0 的异或结果为 n。整数 n 与 n的异或结果为0,异或运算满足交换律和结合律 60 | ''' 61 | odd = 0 62 | for i in arr: 63 | odd ^= i 64 | return odd 65 | 66 | def print_odd_times_num2(self, arr): 67 | ''' 68 | 给定一个数组,数据都是整数,其中只有2个数字出现了奇数次,其它都是偶数次。找出那2个奇数次的数字。 69 | 时间复杂度o(n),空间复杂度为o(1)。 70 | 如果有两个数字出现了奇数次,比如是 a 和 b,则最终 odd 结果为 a^b,但其中一定有差别。 71 | ''' 72 | odd, odd_one = 0, 0 73 | for i in arr: 74 | odd ^= i 75 | right = odd & (~odd + 1) # odd的第k位是1,即两个奇数个的数字的第k位一定不同 76 | for i in arr: 77 | if i & right != 0: 78 | odd_one ^= i 79 | return odd_one, odd_one ^ odd 80 | 81 | def get_num_from_k_arr(self, arr, k): 82 | ''' 83 | 在其它数都出现 k 次的数组中找到只出现一次的数。k 大于 1。 84 | 数组 arr 中,只有一个数出现了 1 次,其它都出现了 k 次,找出那个出现1次的数。 85 | 时间复杂度 o(n),额外空间复杂度为 o(1) 86 | 解法是:如果两个 k 进制数 a,b无进位相加,其结果一定是 (a(i)+b(i))%k,如果是 k 个 k进制数相加,其结果一定是 k 个 0 87 | ''' 88 | e0 = 0 89 | for i in arr: 90 | e0 = (e0 + i) % k # 错误的,有两个问题需要克服,k进制数如何处理,无进位相加如何处理。 91 | return e0 92 | 93 | 94 | bit = BitAlgorithms() 95 | print bit.add_bit(543, 240) 96 | print bit.count1(329) 97 | print bit.count2(329) 98 | print bit.print_odd_times_num1([3, 4, 1, 9, 55, 55, 4, 1, 9]) 99 | print bit.print_odd_times_num2([3, 4, 1, 9, 55, 55, 3, 82, 1, 9]) 100 | print bit.get_num_from_k_arr([3, 4, 4, 1, 1, 9, 55, 9, 55, 55, 4, 1, 9],3) -------------------------------------------------------------------------------- /dynamic_programming/fibonacci_sequence.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf8 -*- 2 | # @Time : 18-1-3 下午2:53 3 | # @Author : Cecil Charlie 4 | # ------------------------------------------------------------------------ 5 | ''' 6 | 计算斐波那契数列 7 | ------------------------------------------------------------------------ 8 | 斐波那契数列: 9 | 0 1 2 3 4 5 6 7 8 10 | 0 1 1 2 3 5 8 13 21 11 | 12 | 包括三种方法: 13 | 一、数列公式计算,即递归计算法,计算复杂度 o(2**n), 14 | 二、考虑到重复计算,采用循环的方式,每次保存了前一步的计算结果,速度大大加快,计算复杂度 o(n) 15 | 三、考虑采用矩阵运算,进一步利用较大的空间,使得速度上有加快,计算复杂度 o(log n)。 16 | ''' 17 | 18 | import os 19 | import sys 20 | import copy 21 | 22 | import numpy as np 23 | 24 | 25 | __all__ = ['FibonacciRecursion', 'FibonacciIteration', 26 | 'FibonacciMatrix'] 27 | 28 | 29 | class FibonacciRecursion(object): 30 | ''' 31 | 数列公式计算,即递归计算法,计算复杂度 o(2**n), 32 | ''' 33 | def __init__(self): 34 | # 用于指定最大迭代次数 35 | self.n = -1 36 | 37 | def __call__(self, n): 38 | ''' 39 | :param n: 数列的第n个索引 40 | :return: 索引n对应的值 41 | ''' 42 | # 配置相应的最大迭代次数 43 | if n < 0: 44 | return None 45 | 46 | if self.n < n and n > 1000: 47 | sys.setrecursionlimit(n) 48 | self.n = n 49 | 50 | if n < 1: 51 | return 0 52 | 53 | if n == 1 or n == 2: 54 | return 1 55 | 56 | return self.__call__(n-1) + self.__call__(n-2) 57 | 58 | 59 | class FibonacciIteration(object): 60 | ''' 61 | 采用循环迭代的方式计算,每次保存了前一步的计算结果,速度大大加快,计算复杂度 o(n) 62 | ''' 63 | def __init__(self): 64 | pass 65 | 66 | def __call__(self, n): 67 | """ 68 | 用循环替代递归,空间复杂度急剧降低,时间复杂度为o(n) 69 | """ 70 | if n < 1: 71 | return 0 72 | 73 | if n == 1 or n == 2: 74 | return 1 75 | 76 | res = 1 77 | tmp1 = 0 78 | tmp2 = 1 79 | for _ in range(1, n): 80 | res = tmp1 + tmp2 81 | tmp1 = tmp2 82 | tmp2 = res 83 | return res 84 | 85 | 86 | class FibonacciMatrix(object): 87 | ''' 88 | 考虑采用矩阵运算,进一步利用较大的空间,使得速度上有加快,计算复杂度 o(log n)。 89 | 当然了,这种方法需要额外计算矩阵,计算矩阵的时间开销没有算在内.其中还运用到了位运算。 90 | ''' 91 | def __init__(self): 92 | pass 93 | 94 | def __call__(self, n): 95 | base = [[1, 1], [1, 0]] 96 | if n < 1: 97 | return 0 98 | if n == 1 or n == 2: 99 | return 1 100 | res = self.__matrix_power(base, n-2) 101 | return res[0][0] + res[1][0] 102 | 103 | def __matrix_power(self, mat, n): 104 | """ 105 | 求一个方阵的幂,时间复杂度为 o(log n) 106 | """ 107 | if len(mat) != len(mat[0]): 108 | raise ValueError("The mat is not a square array.") 109 | 110 | if n < 0 or type(n) is not int: 111 | raise ValueError("The power is unsuitable.") 112 | 113 | product = np.identity(len(mat)) 114 | tmp = mat 115 | while n > 0: 116 | if (n & 1) != 0: 117 | # 按位与的操作,在幂数的二进制位为1时, 118 | # 乘到最终结果上,否则自乘 119 | product = self._multiply_matrix(product, tmp) 120 | tmp = self._multiply_matrix(tmp, tmp) 121 | n >>= 1 122 | 123 | return product 124 | 125 | @staticmethod 126 | def _multiply_matrix(mat1, mat2): 127 | """ 128 | 矩阵乘法 129 | :param m: 矩阵1,二维列表 130 | :param n: 矩阵2 131 | :return: numpy 格式的矩阵 132 | """ 133 | if len(mat1[0]) != len(mat2): 134 | raise ValueError('The dimention of matrix1 and matrix2 is not same') 135 | 136 | product = np.zeros((len(mat1), len(mat2[0]))) 137 | for i in range(0, len(mat1)): 138 | for j in range(0, len(mat2[0])): 139 | for k in range(0, len(mat1[0])): 140 | if mat1[i][k] != 0 and mat2[k][j] != 0: 141 | product[i][j] += mat1[i][k] * mat2[k][j] 142 | return product 143 | 144 | 145 | if __name__ == '__main__': 146 | fib_rec = FibonacciRecursion() 147 | fib_iter = FibonacciIteration() 148 | fib_mat = FibonacciMatrix() 149 | n = 180 150 | max_iter = 1000 151 | import time 152 | start_time = time.time() 153 | for i in range(max_iter): 154 | fib_rec(n) 155 | print(time.time() - start_time) 156 | 157 | start_time = time.time() 158 | for i in range(max_iter): 159 | fib_iter(n) 160 | print(time.time() - start_time) 161 | 162 | start_time = time.time() 163 | for i in range(max_iter): 164 | fib_mat(n) 165 | print(time.time() - start_time) 166 | 167 | 168 | -------------------------------------------------------------------------------- /stack_algorithms/stack_algorithms.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | 3 | 4 | class Node(object): 5 | # 栈结点 6 | def __init__(self, value, next=0): 7 | self.value = value 8 | self.next = next # 指针 9 | 10 | 11 | class Stack(object): 12 | # 由于 Python 难以对内存地址进行操作,所以这里给出了链栈的数据结构 13 | # 由于栈的操作比线性表少很多,所以顺序栈的表示法要比链栈方便快捷 14 | def __init__(self): 15 | self.head = 0 16 | 17 | def init_stack(self, data): 18 | self.head = Node(data[0]) 19 | p = self.head 20 | for i in data[1:]: 21 | p.next = Node(i) 22 | p = p.next 23 | 24 | def clear_stack(self): 25 | self.head = 0 26 | 27 | def is_empty(self): 28 | if self.head == 0: 29 | return True 30 | else: 31 | return False 32 | 33 | def get_length(self): 34 | p, length = self.head, 0 35 | while p != 0: 36 | length += 1 37 | p = p.next 38 | return length 39 | 40 | def push(self, value): # 向栈中添加一个结点 41 | if self.is_empty(): 42 | self.head = Node(value) 43 | else: 44 | p = self.head 45 | for _ in xrange(self.get_length() - 1): 46 | p = p.next 47 | p.next = Node(value) 48 | 49 | def get_top(self): # 获得栈顶元素 50 | if self.is_empty(): 51 | print 'This is an empty stack.' 52 | return 53 | else: 54 | p = self.head 55 | for _ in xrange(self.get_length()): 56 | p = p.next 57 | return p.value 58 | 59 | def pop(self): # 弹出栈顶元素 60 | length = self.get_length() 61 | if self.is_empty(): 62 | print 'This is an empty stack.' 63 | return 64 | elif length == 1: 65 | p = self.head 66 | self.head = 0 67 | return p.value 68 | elif length == 2: 69 | p = self.head 70 | value = p.next.value 71 | self.head.next = 0 72 | return value 73 | else: 74 | p = self.head 75 | for _ in xrange(1, length - 1): 76 | p = p.next 77 | pop = p.next 78 | p.next = 0 79 | return pop.value 80 | 81 | def show_stack(self): # 打印栈中的所有元素 82 | if self.is_empty(): 83 | print 'This is an empty stack.' 84 | else: 85 | p, container = self.head, [] 86 | for _ in xrange(self.get_length() - 1): 87 | container.append(p.value) 88 | p = p.next 89 | container.append(p.value) 90 | print container 91 | 92 | def get_min(self): 93 | ''' 94 | 返回链栈中的最小的元素的值,要求时间复杂度为 o(1),主要方法是用空间换时间,构建一个存放最小栈元素的栈 stackmin。 95 | 每次向栈中添加元素的时候,都判断这个元素是不是比栈中的元素小,如果是相等或更小,则将该数放在 stackmin 中。 96 | 每次弹出一个栈顶元素,判断该数是不是比 stackmin的栈顶元素大,如果大,则 stackmin不需要任何操作。 97 | 在查询时,直接返回stackmin的栈顶元素即可。 98 | ''' 99 | self.stackmin.pop() 100 | 101 | 102 | s = Stack() 103 | s.init_stack([1]) 104 | print s.get_length() 105 | s.show_stack() 106 | # print "top node: ", s.get_top() 107 | s.push(999) 108 | s.show_stack() 109 | print "pop: ", s.pop() 110 | s.show_stack() 111 | 112 | 113 | class StackAlgorithms(object): 114 | def __init__(self): 115 | pass 116 | 117 | 118 | 119 | class SpecialStack(object): 120 | def __init__(self): 121 | self.head = 0 122 | self.stackmin = [] 123 | 124 | def init_stack(self, data): 125 | self.head = Node(data[0]) 126 | if self.stackmin =[]: 127 | self.stackmin.append(data[0]) 128 | p = self.head 129 | for i in data[1:]: 130 | p.next = Node(i) 131 | pop = self.stackmin.pop() 132 | if pop >= i: 133 | self.stackmin.append(pop) 134 | self.stackmin.append(i) 135 | else: 136 | self.stackmin.append(pop) 137 | p = p.next 138 | 139 | def clear_stack(self): 140 | self.head = 0 141 | self.stackmin = [] 142 | 143 | def is_empty(self): 144 | if self.head == 0: 145 | return True 146 | else: 147 | return False 148 | 149 | def get_length(self): 150 | p, length = self.head, 0 151 | while p != 0: 152 | length += 1 153 | p = p.next 154 | return length 155 | 156 | def push(self, value): # 向栈中添加一个结点 157 | if self.is_empty(): 158 | self.head = Node(value) 159 | else: 160 | p = self.head 161 | for _ in xrange(self.get_length() - 1): 162 | p = p.next 163 | p.next = Node(value) 164 | pop = self.stackmin.pop() 165 | if pop >= value: 166 | self.stackmin.append(pop) 167 | self.stackmin.append(value) 168 | else: 169 | self.stackmin.append(pop) 170 | 171 | def get_top(self): # 获得栈顶元素 172 | if self.is_empty(): 173 | print 'This is an empty stack.' 174 | return 175 | else: 176 | p = self.head 177 | for _ in xrange(self.get_length()): 178 | p = p.next 179 | return p.value 180 | 181 | def pop(self): # 弹出栈顶元素 182 | length = self.get_length() 183 | if self.is_empty(): 184 | print 'This is an empty stack.' 185 | return 186 | elif length == 1: 187 | p = self.head 188 | self.head = 0 189 | self.stackmin = [] 190 | return p.value 191 | elif length == 2: 192 | p = self.head 193 | value = p.next.value 194 | self.head.next = 0 195 | if len(self.stackmin) == 2: 196 | self.stackmin.pop() 197 | return value 198 | else: 199 | p = self.head 200 | for _ in xrange(1, length - 1): 201 | p = p.next 202 | pop = p.next 203 | p.next = 0 204 | stack_min = self.stackmin.pop() 205 | if stack_min < pop.value: 206 | self.stackmin.append(stack_min) 207 | return pop.value 208 | 209 | def show_stack(self): # 打印栈中的所有元素 210 | if self.is_empty(): 211 | print 'This is an empty stack.' 212 | else: 213 | p, container = self.head, [] 214 | for _ in xrange(self.get_length() - 1): 215 | container.append(p.value) 216 | p = p.next 217 | container.append(p.value) 218 | print container 219 | 220 | def get_min(self): 221 | ''' 222 | 返回链栈中的最小的元素的值,要求时间复杂度为 o(1),主要方法是用空间换时间,构建一个存放最小栈元素的栈 stackmin。 223 | 每次向栈中添加元素的时候,都判断这个元素是不是比栈中的元素小,如果是相等或更小,则将该数放在 stackmin 中。 224 | 每次弹出一个栈顶元素,判断该数是不是比 stackmin的栈顶元素大,如果大,则 stackmin不需要任何操作。 225 | 在查询时,直接返回stackmin的栈顶元素即可。 226 | ''' 227 | pop = self.stackmin.pop() 228 | self.stackmin.append(pop) 229 | return pop 230 | -------------------------------------------------------------------------------- /searching_algorithms/searching_algorithms.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | 3 | # from sorting_algorithms import SortingAlgorithms 4 | 5 | 6 | class SearchingAlgorithms(object): 7 | ''' 8 | 七大查找算法 Python 版,所有待查序列里都是按主键来查找,即没有主键相同的两项。 9 | 所有的查找算法的空间复杂度都比较低,因为比较之后无需存储比较结果,也无需存储被比较的值。 10 | 时间复杂度根据被查找的值波动变化非常大,所以使用平均查找长度 ASL 来衡量算法性能。 11 | 12 | 静态查找主要包括:顺序查找,折半二分查找,斐波那契查找,插值查找 13 | 树查找主要包括:次优查找树,二叉查找树,平衡二叉树,2-3树,红黑树,b树,b+树,键树 14 | 15 | 按照分块思想的查找有:索引顺序查找,键值查找 16 | 17 | 18 | ''' 19 | 20 | def __init__(self): 21 | pass 22 | 23 | def sequential_search(self, lists, key): 24 | ''' 25 | 顺序查找算法,最简单的一种,静态查找,等概分布,针对没有排序的序列而言。平均查找长度 ASL = (n+1)/2 26 | 设立哨兵的方法,避免每次都检测是否检测完整个表,本函数没写哨兵。 27 | :param lists: 待查链表,顺序表等等。 28 | :param key: 待查关键字 29 | :return: 如果查到了,返回索引位置,如果没有查到,返回 -1 30 | ''' 31 | for i in xrange(0, len(lists)): 32 | if lists[i] == key: 33 | return i 34 | return -1 35 | 36 | def binary_search(self, lists, key): 37 | ''' 38 | 二分查找法,又叫折半查找,应用于等概分布的有序序列,利用了判定树,平均查找长度 ASL = (log2 n+1) - 1 39 | ''' 40 | if lists == []: 41 | return -1 42 | if len(lists) == 1: 43 | if key == lists[0]: 44 | return 0 45 | else: 46 | return -1 47 | lists = sorted(lists) # 先排序 48 | print lists # [-6, 2, 3, 4, 9, 10, 13, 47, 78, 90, 111, 125, 345, 908, 999] 49 | low, high = 0, len(lists) - 1 50 | while low < high: 51 | mid = (low + high) / 2 52 | if lists[mid] > key: 53 | high = mid - 1 54 | if lists[mid] < key: 55 | low = mid + 1 56 | if lists[mid] == key: 57 | return mid 58 | return -1 59 | 60 | def insertion_search(self, lists, key): 61 | ''' 62 | 插值查找,二分折半查找的改进型,应用于等概分布,其实计算 mid 值消耗的时间也比较多,因为有很多乘除法运算。 63 | 适用于有序的,均匀分布的,非常大数据量的查找。 64 | ''' 65 | if lists == []: 66 | return -1 67 | if len(lists) == 1: 68 | if key == lists[0]: 69 | return 0 70 | else: 71 | return -1 72 | lists = sorted(lists) # 先排序 73 | low, high = 0, len(lists) - 1 74 | while low < high: 75 | if key - lists[low] < 0: # 以免出现回退 76 | return -1 77 | mid = low + (key - lists[low]) / (lists[high] - lists[low]) * (high - low) 78 | if lists[mid] > key: 79 | high = mid - 1 80 | if lists[mid] < key: 81 | low = mid + 1 82 | if lists[mid] == key: 83 | return mid 84 | return -1 85 | 86 | def fibonacci_search(self, lists, key): 87 | ''' 88 | 斐波那契查找算法,插值查找的一种,针对有序静态查找表来做。整体性能优于折半查找,但是在最坏情况下,比折半查找要差。 89 | 相较于插值查找,不需要做乘除法运算,应用于等概分布 90 | ''' 91 | if lists == []: 92 | return -1 93 | if len(lists) == 1: 94 | if key == lists[0]: 95 | return 0 96 | else: 97 | return -1 98 | lists = sorted(lists) # 先排序 99 | 100 | # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34,...] 101 | def fibonacci(n): 102 | if n == 0: 103 | return 0 104 | if n == 1: 105 | return 1 106 | f = [0] 107 | f.append(1) 108 | for i in xrange(2, n + 1): 109 | f.append(f[i - 1] + f[i - 2]) 110 | return f.pop() 111 | 112 | low, high = 0, len(lists)-1 113 | i = 1 114 | while fibonacci(i) < len(lists): 115 | i += 1 116 | if key == lists[high]+1: 117 | return -1 118 | for _ in xrange(len(lists), fibonacci(i)): 119 | lists.append(lists[high]+1) 120 | high = len(lists) - 1 121 | #print "fibonacci: ", i, high, fibonacci(i) 122 | while low <= high: 123 | mid = fibonacci(i-1) + low 124 | if lists[mid] == key: 125 | return mid 126 | if lists[mid] < key: 127 | low = mid + 1 128 | i -= 2 129 | if lists[mid] > key: 130 | high = mid - 1 131 | i -= 1 132 | return -1 133 | 134 | def binary_search_tree_search(self, lists, key): 135 | ''' 136 | 二叉查找树,又称为二叉排序树,对于每一个节点,其左子节点一定比其小,右子节点一定比其大。一般采用二叉链表作为存储结构。 137 | 由于构建 BST 的过程是个动态的过程,所以二叉查找树是一个动态查找表的查找。 138 | 二叉树如果中序遍历,其结果就是有序数组。 139 | 平均查找长度是 2(1+ 1/n)(ln n), 时间复杂度为 o(log2 n),在最坏情况下,是 o(n),此时需要平衡化处理。 140 | 一、每次添加一个结点,一定是在 BST的叶子节点上,每次插入都只需修改某个叶子节点的指针即可。 141 | 二、每次删除一个结点, 142 | 1、若是叶子节点,由于其没有子树,所以只需修改其父节点的指针即可。 143 | 2、若是只有左子树或者右子树,则分情况讨论,若该结点是其双亲结点的左子树结点,则让这颗子树成为其父节点的左子树; 144 | 若该结点是其双亲结点的右子树结点,则让这颗子树成为其父结点的右子树。 145 | 3、若是左右子树都存在,则有两种处理方法,具体需要看图和代码来理解。 146 | ''' 147 | 148 | def balanced_binary_tree_search(self, lists, key): 149 | ''' 150 | 平衡二叉树,又叫 AVL 树,左右子树都是平衡二叉树,且左右子树的深度之差不超过 1。 151 | 由于二叉查找树性能恶化的原因在于树趋近于链表,也就是左右不平衡,所以平衡二叉树能够改善 BST的不稳定性能。 152 | 其平均查找时间同样是 o(log2 n),且效果比 BST 稳定。 153 | 将二叉排序树改造成平衡二叉树的方法,也就是插入和删除的时候保持平衡,较为复杂,书本上有就不细说了。 154 | 155 | 平衡二叉树分为 平衡2-3树和平衡红黑树,效率都比较高,但是真复杂啊。红黑树是 2-3树的一种简单实现。 156 | 157 | 平衡2-3树: 158 | "2-3查找树定义":和二叉树不一样,2-3树运行每个节点保存1个或者两个的值。对于普通的2节点(2-node),他保存1个key和左右两个自己点。 159 | 对应3节点(3-node),保存两个Key,2-3查找树的定义如下: 160 | 1)要么为空,要么: 161 | 2)对于2节点,该节点保存一个key及对应value,以及两个指向左右节点的节点,左节点也是一个2-3节点,所有的值都比key要小,右节点也 162 | 是一个2-3节点,所有的值比key要大。 163 | 3)对于3节点,该节点保存两个key及对应value,以及三个指向左中右的节点。左节点也是一个2-3节点,所有的值均比两个key中的最小的 164 | key还要小;中间节点也是一个2-3节点,中间节点的key值在两个跟节点key值之间;右节点也是一个2-3节点,节点的所有key值比两个 165 | key中的最大的key还要大。 166 | 167 | "2-3查找树的性质": 168 | 1)如果中序遍历2-3查找树,就可以得到排好序的序列; 169 | 2)在一个完全平衡的2-3查找树中,根节点到每一个为空节点的距离都相同。(这也是平衡树中“平衡”一词的概念,根节点到叶节点的最长距离 170 | 对应于查找算法的最坏情况,而平衡树中根节点到叶节点的距离都一样,最坏情况也具有对数复杂度。 171 | 172 | 平衡红黑树: 173 | 2-3查找树能保证在插入元素之后能保持树的平衡状态,最坏情况下即所有的子节点都是2-node,树的高度为lgn,从而保证了最坏情况下的时 174 | 间复杂度。但是2-3树实现起来比较复杂,于是就有了一种简单实现2-3树的数据结构,即红黑树(Red-Black Tree)。 175 | 176 |    基本思想:红黑树的思想就是对2-3查找树进行编码,尤其是对2-3查找树中的3-nodes节点添加额外的信息。红黑树中将节点之间的链接分为 177 | 两种不同类型,红色链接,他用来链接两个2-nodes节点来表示一个3-nodes节点。黑色链接用来链接普通的2-3节点。特别的,使用红色链接的两个 178 | 2-nodes来表示一个3-nodes节点,并且向左倾斜,即一个2-node是另一个2-node的左子节点。这种做法的好处是查找的时候不用做任何修改,和 179 | 普通的二叉查找树相同。 180 | 181 | “红黑树的定义”:红黑树是一种具有红色和黑色链接的平衡查找树,同时满足: 182 | 1、红色节点向左倾斜 183 | 2、一个节点不可能有两个红色链接 184 | 3、整个树完全黑色平衡,即从根节点到所以叶子结点的路径上,黑色链接的个数都相同。 185 |    下图可以看到红黑树其实是2-3树的另外一种表现形式:如果我们将红色的连线水平绘制,那么他链接的两个2-node节点就是2-3树中的一个 186 | 3-node节点了。 187 | 188 | “红黑树的性质”:整个树完全黑色平衡,即从根节点到所以叶子结点的路径上,黑色链接的个数都相同(2-3树的第2)性质,从根节点到叶子节 189 | 点的距离都相等)。 190 | “复杂度分析”:最坏的情况就是,红黑树中除了最左侧路径全部是由3-node节点组成,即红黑相间的路径长度是全黑路径长度的2倍。 191 | 192 | 193 | ''' 194 | 195 | def b_tree_search(self,lists, key): 196 | ''' 197 | B树查找:B树(B-tree)是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和\ 198 | 删除的数据结构。B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,B树为系统最优化大块数据的读和写操作。\ 199 | B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。 200 | “B树定义”: 201 | B树可以看作是对 2-3查找树的一种扩展,即他允许每个节点有 M-1个子节点。根节点至少有两个子节点,每个节点有 M-1个key,并且以升序排\ 202 | 列,位于 M-1和 M key的子节点的值位于 M-1 和 M key对应的 Value之间,其它节点至少有 M/2个子节点。 203 | 204 | B+树定义: 205 | B+树是对B树的一种变形树,它与B树的差异在于: 206 | 1、有k个子结点的结点必然有k个关键码; 207 | 2、非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。 208 | 3、树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。 209 | 210 | B和 B+树的区别在于,B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。 211 | 212 | B+ 树的优点在于: 213 | 由于B+树在内部节点上不好含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子几点上\ 214 | 关联的数据也具有更好的缓存命中率。B+树的叶子结点都是相链的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,\ 215 | 所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。 216 |   但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。 217 | 218 | B/B+树常用于文件系统和数据库系统中,它通过对每个节点存储个数的扩展,使得对连续的数据能够进行较快的定位和访问,能够有效减少查找时\ 219 | 间,提高存储的空间局部性从而减少IO操作。它广泛用于文件系统及数据库中,如: 220 | Windows:HPFS文件系统; 221 | Mac:HFS,HFS+文件系统; 222 | Linux:ResiserFS,XFS,Ext3FS,JFS文件系统; 223 | 数据库:ORACLE,MYSQL,SQLSERVER等中。 224 | ''' 225 | 226 | def digital_search_tree_search(self, lists, key): 227 | ''' 228 | 键树,又称数字查找树,利用的基本思想类似于我们查英文字典。也就是把序列中的关键字拆分成若干部分,这些部分可作为每一层键树种的结点。\ 229 | 组合起来之后即可得到要查询的关键字,最典型的应用也是查找英文单词。事先将所有的英文单词组织成一颗键树,达到效率最高。 230 | 键树的存储结构: 231 | 1、多重链表,最容易想到的存储结构; 232 | 2、孩子兄弟链表来表示键树,左子节点是孩子,右子节点是兄弟。两种表示方法即普通树和二叉树之间的互换。 233 | ''' 234 | 235 | def hash_search(self, lists, key): 236 | ''' 237 | 什么是哈希表(Hash)? 238 | 我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数\ 239 | 组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地\ 240 | 方。但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话\ 241 | 说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。 242 | 总的来说,"直接定址"与"解决冲突"是哈希表的两大特点。 243 | 244 | 什么是哈希函数? 245 | 哈希函数的规则是:通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。 246 | 算法思想:哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可\ 247 | 以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。 248 | 249 | 算法流程: 250 |   1)用给定的哈希函数构造哈希表; 251 |   2)根据选择的冲突处理方法解决地址冲突; 252 |     常见的解决冲突的方法:拉链法和线性探测法。 253 |   3)在哈希表的基础上执行哈希查找。 254 |   哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如\ 255 | 果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需\ 256 | 要调整哈希函数算法即可在时间和空间上做出取舍。 257 |    258 | 复杂度分析: 259 |   单纯论查找复杂度:对于无冲突的Hash表而言,查找复杂度为O(1)(注意,在查找之前我们需要构建相应的Hash表)。 260 | 261 |   使用Hash,我们付出了什么? 262 |   我们在实际编程中存储一个大规模的数据,最先想到的存储结构可能就是map,也就是我们常说的KV pair,经常使用Python的博友可能更有这种体\ 263 | 会。使用map的好处就是,我们在后续处理数据处理时,可以根据数据的key快速的查找到对应的value值。map的本质就是Hash表,那我们在获取了超高查找\ 264 | 效率的基础上,我们付出了什么? 265 | 266 |   Hash是一种典型以空间换时间的算法,比如原来一个长度为100的数组,对其查找,只需要遍历且匹配相应记录即可,从空间复杂度上来看,假如数\ 267 | 组存储的是byte类型数据,那么该数组占用100byte空间。现在我们采用Hash算法,我们前面说的Hash必须有一个规则,约束键与存储位置的关系,那么就需\ 268 | 要一个固定长度的hash表,此时,仍然是100byte的数组,假设我们需要的100byte用来记录键与位置的关系,那么总的空间为200byte,而且用于记录规则\ 269 | 的表大小会根据规则,大小可能是不定的。 270 | ''' 271 | 272 | search = SearchingAlgorithms() 273 | l = [4, 90, 125, 2, 9, 47, 999, -6, 111, 908, 13, 78, 345, 3, 10,909] 274 | print "Sequential search: ", search.sequential_search(l, 10) 275 | print "Binary search: ", search.binary_search([101], 101) 276 | print "Insertion search: ", search.insertion_search(l, 907) 277 | print "Fibonacci search: ", search.fibonacci_search(l, 4) 278 | print search.b_tree_search.__doc__ 279 | -------------------------------------------------------------------------------- /sorting_algorithms/sorting_algorithms.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | 3 | import time 4 | import random 5 | import unittest 6 | 7 | 8 | def timeCount(func): # 装饰器计算时间 9 | def wrapper(*arg,**kwarg): 10 | start = time.clock() 11 | func(*arg,**kwarg) 12 | end =time.clock() 13 | print 'used:', end - start 14 | return wrapper 15 | 16 | 17 | class Executor: 18 | def __init__(self, func, *args, **kwargs): 19 | self.func = func 20 | self.args = args 21 | self.kwargs = kwargs 22 | self.do() 23 | 24 | @timeCount 25 | def do(self): 26 | print '-----start:',self.func,'-----' 27 | self.ret = self.func(*self.args, **self.kwargs) 28 | 29 | def __del__(self): 30 | print '-----end-----' 31 | 32 | 33 | class TestSort(unittest.TestCase): # 测试类的使用 34 | 35 | def test_01_bubbleSort(self): 36 | Executor(SortingAlgorithms().bubble_sort, L[:]) 37 | 38 | def test_11_builtinSort(self): 39 | Executor(sorted, L[:]) 40 | 41 | 42 | class SortingAlgorithms(object): 43 | ''' 44 | 冒泡法:对比模型,原数组上排序,稳定,慢 45 | 插入法:对比模型,原数组上排序,稳定,慢 46 | 选择法:对比模型,原数组上排序,稳定,慢 47 | 归并法:对比模型,非原数组上排序,稳定,快 48 | 快速法:对比模型,原数组上排序,不稳定,快 49 | 堆排序:对比模型,原数组上排序,不稳定,快,空间复杂度为1个辅助空间 50 | 二叉树排序:对比模型,非数组上排序,不稳定,快 51 | 桶排序:非对比模型,非原数组上排序,不稳定,快 52 | 基数排序:非对比模型,非原数组上排序,稳定,快 53 | 基于比较模型的排序算法的复杂度最高可以做到 o(n*log2 n),这是二叉树形比较结构决定的,再有其他方法就只能是空间换时间。 54 | 一种广泛采取的排序算法,是在数据量很大的时候,采取快速排序的方式,而在当分组很小的时候,使用其他稳定的排序方法。 55 | 这样的混合型算法,综合效果是最好的,也就是一般内置排序使用的方法 56 | ''' 57 | def __init__(self): 58 | pass 59 | 60 | def straight_insertion_sort(self, lists): 61 | # 直接插入排序, 空间复杂度 o(1),时间复杂度为o(n^2),稳定排序 62 | if lists == [] or len(lists) == 1: 63 | return 64 | for i in range(1, len(lists)): 65 | key = lists[i] 66 | j = i-1 67 | while j >= 0: 68 | if lists[j] > key: 69 | lists[j+1] = lists[j] 70 | lists[j] = key 71 | j -= 1 72 | return lists 73 | 74 | def bubble_sort(self, lists): 75 | # 冒泡排序,快速排序的一种,稳定排序 76 | if lists == [] or len(lists) == 1: 77 | return lists 78 | for i in range(0, len(lists)): 79 | for j in range(i+1, len(lists)): 80 | if lists[i] > lists[j]: 81 | lists[j], lists[i] = lists[i], lists[j] 82 | return lists 83 | 84 | def quick_sort_1(self, lists): 85 | # 快速排序,对冒泡排序的改进,不稳定排序,时间复杂度为o(n*log 2n),目前公认的常数项因子最小的先进排序算法. 86 | # 其中的关键是分治策略,但是在 Python 中的分治要比 C 语言中的简单得多,但是我仍然用 Python 具体写了一下分治策略 87 | if lists == [] or len(lists) == 1: 88 | return lists 89 | low, high = 0, len(lists) - 1 90 | pivot = 0 91 | while low < high: 92 | while low < high: 93 | if lists[pivot] > lists[high]: 94 | lists[high], lists[low] = lists[low], lists[high] 95 | pivot = high 96 | break 97 | high -= 1 98 | while low < high: 99 | if lists[pivot] < lists[low]: 100 | lists[high], lists[low] = lists[low], lists[high] 101 | pivot = low 102 | break 103 | low += 1 104 | return self.quick_sort_1(lists[:pivot]) + [lists[pivot]] + self.quick_sort_1(lists[pivot+1:]) 105 | 106 | def quick_sort_2(self, lists): 107 | # 快速排序的另外一种写法,简单写法,但是时间消耗要比分治多一些 108 | if lists == [] or len(lists) == 1: 109 | return lists 110 | left = [i for i in lists[1:] if i < lists[0]] 111 | right = [i for i in lists[1:] if i >= lists[0]] 112 | return self.quick_sort_2(left) + [lists[0]] + self.quick_sort_2(right) 113 | 114 | def shell_sort(self, lists): 115 | ''' 116 | 希尔排序,插入排序的一种,时间复杂度为o(n^3/2),不稳定排序,基本思想是如果是正序的话,比较次数就少了很多 117 | i,j k 三个变量折腾的我有点懵逼, 118 | :param lists: 119 | :return: 120 | ''' 121 | if lists == [] or len(lists) == 1: 122 | return lists 123 | count = len(lists) 124 | step = 2 125 | group = count / step 126 | while group > 0: 127 | for i in range(0, group): 128 | j = i + group 129 | while j < count: 130 | k = j - group 131 | key = lists[j] 132 | while k >= 0: # 直接插入排序的方法 133 | if lists[k] > key: 134 | lists[k + group] = lists[k] 135 | lists[k] = key 136 | k -= group 137 | j += group 138 | group /= step 139 | return lists 140 | 141 | def simple_select_sort(self, lists): 142 | ''' 143 | 简单选择排序,稳定排序,时间复杂度为 o(n^2),查找每一次最小值的时候都要经过 n-1 次比较 144 | ''' 145 | length = len(lists) 146 | for i in xrange(0, length): 147 | key = i 148 | for j in xrange(i+1, length): 149 | if lists[key] > lists[j]: 150 | key = j 151 | lists[i], lists[key] = lists[key], lists[i] 152 | return lists 153 | 154 | def heap_sort(self, L): 155 | ''' 156 | 堆排序,是选择排序的一种优化,利用了树形结构,避免了选择最小元素时每次都经过 n-1 次比较,不稳定排序 157 | 辅助空间只需要一个,空间复杂度为o(1),时间复杂度为 o(n*log 2n),在最坏的情况下,复杂度依然是 o(n*log 2n) 158 | 这是相比快速排序优秀的地方,但是在序列中记录较少的时候不提倡使用,因为建立堆比较耗时。 159 | ''' 160 | if L == [] or len(L) == 1: 161 | return L 162 | 163 | def sift_down(L, start, end): # 调整找到一颗根节点最小的树 164 | root = start 165 | while True: 166 | child = 2*root + 1 # 左子节点,一个辅助空间,指针指示哪一个节点 167 | if child > end: # 子节点超出范围 168 | break 169 | if child+1 <= end and L[child] < L[child+1]: # 左子节点比右子节点小,转换到右子节点 170 | child += 1 171 | if L[root] < L[child]: # 若根节点比 相对较大的一个子节点小,则互换 172 | L[root], L[child] = L[child], L[root] 173 | root = child # 进行树下面下一层的替换 174 | else: 175 | break 176 | return 177 | for start in range((len(L)-2)/2, -1, -1): # 逆序构建一个堆,堆顶的值最大, 178 | sift_down(L, start, len(L)-1) 179 | #print L 180 | for end in range(len(L)-1, 0, -1): # 取堆顶的一个值,放在序列尾部 181 | L[0], L[end] = L[end], L[0] 182 | sift_down(L, 0, end-1) 183 | return L 184 | 185 | def merging_sort(self, lists): 186 | ''' 187 | 归并排序,需要的辅助空间大小和待排记录数量相等,时间复杂度也是 o(n*log 2n),稳定排序 188 | ''' 189 | if lists == [] or len(lists) == 1: 190 | return lists 191 | 192 | def merge(list1, list2): 193 | i, j = 0, 0 # 分别指向两个序列的指针 194 | result = [] 195 | while i < len(list1) and j < len(list2): 196 | if list1[i] > list2[j]: 197 | result.append(list2[j]) 198 | j += 1 199 | else: 200 | result.append(list1[i]) 201 | i += 1 202 | result += list1[i:] 203 | result += list2[j:] 204 | return result 205 | 206 | num = len(lists) / 2 207 | left = self.merging_sort(lists[:num]) 208 | right = self.merging_sort(lists[num:]) 209 | return merge(left, right) 210 | 211 | def bucket_sort(self, lists): 212 | ''' 213 | 桶排序,非比较模型,适用于数据量非常大,数据紧凑,相同数据很多的情况,比如统计每年高考学生的分数 214 | ''' 215 | if lists == [] or len(lists) == 1: 216 | return lists 217 | mini = min(lists) # 查找方法 218 | maxi = max(lists) 219 | bucket = [] 220 | for _ in xrange(0, maxi+1-mini): 221 | bucket.append(0) 222 | for i in lists: 223 | bucket[i-mini] += 1 224 | res = [] 225 | for i in xrange(0, len(bucket)): 226 | while bucket[i] > 0: 227 | res.append(i+mini) 228 | bucket[i] -= 1 229 | return res 230 | 231 | def radix_sort(self, lists): 232 | ''' 233 | 基数排序,针对“正整数”来写的算法函数,稳定排序,按照千位、百位、十位来排序 234 | ''' 235 | import math 236 | k = int(math.ceil(math.log(max(lists), 10))) 237 | bucket = [[] for _ in range(10)] 238 | for i in range(1, k+1): 239 | for j in lists: 240 | #num = 241 | bucket[(j%(10**i))/10**(i-1)].append(j) 242 | del lists[:] 243 | for z in bucket: 244 | lists += z 245 | del z[:] 246 | return lists 247 | 248 | 249 | class ExternalSortingAlgorithms(object): 250 | ''' 251 | 败者树外部排序的全部实现: 252 | 1、使用gen_k_data 函数产生一个 data,这个data中,k表示归并的路数,l表示每一路中包含数字的个数 253 | e.g. data = gen_k_data(10, 200) 254 | print "generate the data for loser tree: ", data 255 | 2、在 merging_soring 函数中,接收上一步产生的数据。并首先生成一个败者树,在这一步中,关键点就是创建败者树,以及调整败者树。 256 | 然后,每次产生一个 compare,用来存放当前需要比较的10个数值,取出最小的一个,再从对应的那一路中取出下一个数值放在compare里 257 | 对应的位置上。如果某一路都取完了,我们就给该路的compare值赋一个很大的值,比如说我在这里赋了10000,当compare里全部都是10000 258 | 说明已经排序完毕,返回result 就是我们的排序结果。 259 | e. g. merging_sorting(data) 260 | ''' 261 | def __init__(self): 262 | pass 263 | 264 | def gen_k_data(self, k, l): # 生成k 路随机数,并排序,存放在列表中, k表示归并的路数,l表示每一路多少个数 265 | if k < 0 or l < 0: 266 | print "Wrong given k number or l number." 267 | data = [] 268 | sort = SortingAlgorithms() 269 | for n in xrange(0, k): 270 | merge = [] 271 | small = random.randint(0, 100) 272 | large = random.randint(800, 1000) 273 | for _ in xrange(0, l): 274 | merge.append(random.randint(small, large)) # small 和 large 用来控制取值范围的。 275 | data.append(sort.heap_sort(merge)) 276 | return data 277 | 278 | def merging_sorting(self, data): # 生成一个归并序列,把每一路中的元素挨个排入compare数组中 279 | k = len(data) 280 | compare = [] 281 | for i in xrange(0, k): 282 | compare.append(data[i][0]) 283 | #data[i].remove(data[i][0]) 284 | print compare 285 | 286 | def adjust(loserTree, dataArray, n, s): # 败者树的核心代码 287 | t = (s + n) / 2 288 | while t > 0: # 从败者树的尾部开始进行比较 289 | if dataArray[s] > dataArray[loserTree[t]]: # 和败者结点比较 290 | s, loserTree[t] = loserTree[t], s # 如果比某个败者结点大,说明该结点失败了,将s结点存入败者树,把败者树的现在的胜结点拿去和其父节点比较。 291 | t /= 2 292 | loserTree[0] = s 293 | 294 | def createLoserTree(loserTree, dataArray, n): 295 | for i in range(n): 296 | loserTree.append(0) 297 | dataArray.append(i-n) # 这里是为了生成败者树用的, 298 | 299 | for i in range(n): 300 | adjust(loserTree, dataArray, n, n-1-i) 301 | 302 | loserTree = [] 303 | dataArray = [] 304 | createLoserTree(loserTree, dataArray, k) 305 | 306 | for i in xrange(k): 307 | dataArray[i] = compare[i] # 将数据替换成待排数据 308 | adjust(loserTree, dataArray, k, i) # 此步执行完毕,败者树才完全创建初始化完毕,正式开始排序归并 309 | 310 | result = [] 311 | while True: 312 | if data[loserTree[0]][0] > 9999: 313 | break 314 | result.append(data[loserTree[0]][0]) # 添加到 result 中,这是我们需要的结果 315 | data[loserTree[0]].remove(data[loserTree[0]][0]) # 从data 中删除头一个元素 316 | if data[loserTree[0]] == []: 317 | data[loserTree[0]].append(10000) 318 | compare[loserTree[0]] = data[loserTree[0]][0] # 将下一个元素添加到 compare数组中 319 | adjust(loserTree, compare, k, loserTree[0]) 320 | 321 | return result 322 | 323 | if __name__ == "__main__": 324 | sort = SortingAlgorithms() 325 | l = [4,90,125,2,9,47,999,-6,111,908,13,78,345,3,10] 326 | print "lists is :", l 327 | #print "Straight insertion sort: ", sort.straight_insertion_sort(l) 328 | # print "Bubble sort: ", sort.bubble_sort(l) 329 | # print "Quick sort 1: ", sort.quick_sort_1(l) 330 | # print "Quick sort 2: ", sort.quick_sort_2(l) 331 | #print "Shell sort: ", sort.shell_sort(l) 332 | #print "Simple select sort: ", sort.simple_select_sort(l) 333 | print "Heap sort: ", sort.heap_sort(l) 334 | print "Merging sort: ", sort.merging_sort(l) 335 | #print sort.bucket_sort(l) 336 | 337 | L = range(5000) 338 | random.shuffle(L) 339 | 340 | if __name__=="__main__": 341 | #unittest.main() 342 | print "radix_sort: ", sort.radix_sort(l) 343 | 344 | -------------------------------------------------------------------------------- /string_algorithms/string_algorithms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class StringAlgorithm(object): 5 | def __init__(self): 6 | pass 7 | 8 | def is_rotation(self, str1, str2): # 判断两子串是否互为变形词 9 | if str1 == "" or str2 == "" or len(str1) != len(str2): 10 | return False 11 | str_double = str1 + str1 12 | if str2 in str_double: 13 | return True 14 | else: 15 | return False 16 | 17 | def str_2_int(self, string): # 将符合规范的数字字符串转为数字 18 | if string == "": 19 | return 0 20 | if len(string) == 1: 21 | if 48 < ord(string) <= 57: 22 | return ord(string) - 48 23 | else: 24 | return 0 25 | else: # 字符串长度大于 1 26 | num = 0 27 | if 48 < ord(string[0]) <= 57: 28 | num = ord(string[0]) 29 | posi = 1 30 | elif string[0] == "-": 31 | if string[1] == "0": 32 | return 0 33 | posi = -1 34 | else: 35 | return 0 36 | for i in xrange(1, len(string)): 37 | if 48 <= ord(string[i]) <= 57: 38 | num = num * 10 + ord(string[i]) 39 | else: 40 | return 0 41 | return num * posi 42 | 43 | def replace_str(self, string, from_str, to_str): # 替换字符串中连续出现的指定字符串,有问题#### 44 | pass 45 | 46 | def get_count_str(self, string): # 获取字符串的统计字符串 47 | if string == "": 48 | return "" 49 | count = 0 50 | char = string[0] 51 | count_str = "" 52 | for i in xrange(0, len(string)): 53 | if char != string[i]: 54 | count_str += char + "_" + str(count) + "_" 55 | char = string[i] 56 | count = 1 57 | else: 58 | count += 1 59 | count_str += char + "_" + str(count) 60 | return count_str 61 | 62 | def is_unique_1(self, str_list): # 判断字符类型数组中的字符是否均只出现过一次,时间复杂度o(n) 63 | if str_list == "": 64 | return False 65 | i, map = 0, [0] 66 | while i < 256: 67 | map.append(0) 68 | i += 1 69 | for i in xrange(0, len(str_list)): 70 | map[ord(str_list[i])] += 1 71 | if map[ord(str_list[i])] != 1: 72 | return False 73 | return True 74 | 75 | def is_unique_2(self, str_list): # 判断字符类型数组中的字符是否均只出现过一次,空间复杂度o(1)###### 76 | if str_list == "": 77 | return False 78 | for i in xrange(0, len(str_list)): 79 | for j in xrange(i + 1, len(str_list)): 80 | if str_list[i] == str_list[j]: 81 | return False 82 | return True 83 | 84 | def get_index(self, str_list, str): # 在有序但包含None的数组中查找字符 85 | index = -1 86 | left = 0 87 | right = len(str_list) - 1 88 | if str is None: 89 | return -1 90 | while left <= right: 91 | mid = (left + right) / 2 92 | if str_list[mid] == str: 93 | index = mid 94 | right = mid - 1 95 | elif str_list[mid] == None: 96 | i = mid - 1 97 | while 0 < i < mid: 98 | if str_list[i] != None: 99 | break 100 | else: 101 | i -= i 102 | if ord(str_list[i]) == ord(str): 103 | index = i 104 | right = i - 1 105 | elif ord(str_list[i]) > ord(str): 106 | right = i 107 | else: 108 | left = mid + 1 109 | else: 110 | if ord(str_list[mid]) > ord(str): 111 | right = mid - 1 112 | else: 113 | left = mid + 1 114 | return index 115 | 116 | def replace_space(self, str_list): # 字符串的调整与替换,时间复杂度o(n),空间复杂度o(1) 117 | num = 1 118 | for i in xrange(0, len(str_list)): 119 | if str_list[i] == ' ': 120 | num += 1 121 | length = len(str_list) + 2 * num - 2 122 | i = k = len(str_list) 123 | while i < length: 124 | str_list.append('0') 125 | i += 1 126 | i -= 1 127 | for j in xrange(k - 1, -1, -1): 128 | if str_list[j] == ' ': 129 | str_list[i] = "0" 130 | i -= 1 131 | str_list[i] = "2" 132 | i -= 1 133 | str_list[i] = "%" 134 | i -= 1 135 | else: 136 | str_list[i] = str_list[j] 137 | i -= 1 138 | return str_list 139 | 140 | def replace_stars(self, str_list): # 将所有*号移动到数组的左侧 141 | j = len(str_list) - 1 142 | for i in xrange(len(str_list) - 1, -1, -1): 143 | if str_list[i] != '*': 144 | str_list[j] = str_list[i] 145 | j -= 1 146 | for i in xrange(0, j + 1): 147 | str_list[i] = '*' 148 | return str_list 149 | 150 | def rotate_word(self, str_list): # 翻转字符串,将一句英文中的单词全部逆序,单词本身不变##### 151 | if str_list == []: 152 | return [] 153 | 154 | def reverse_list(self, str_list, start, end): # 翻转一个数组,逆序存入同样的位置###### 155 | if str_list == [] or start == end: 156 | return str_list 157 | if start > end: 158 | print "WRONG index." 159 | return str_list 160 | for i in xrange(0, (end - start) / 2): 161 | tmp = str_list[end - i] 162 | str_list[end - i] = str_list[start + i] 163 | str_list[start + i] = tmp 164 | 165 | def pattern_match(self, str, pattern): # 字符串的模式匹配,返回匹配到的索引 166 | index = [] 167 | for _ in xrange(0, len(str)): 168 | i = _ 169 | j = 0 170 | while j < len(pattern): 171 | if i < len(str) and str[i] == pattern[j]: 172 | i += 1 173 | j += 1 174 | else: 175 | break 176 | if j == len(pattern): 177 | index.append(_) 178 | return index 179 | 180 | def kmp_pattern_match(self, str, pattern): 181 | index = [] 182 | j = 0 183 | for i in xrange(0, len(str)): 184 | 185 | def get_next(pattern, index): # index 范围为 1 到 len(pattern),算上 len(pattern) 186 | 187 | def next_index(pattern): # 返回一个列表,表示对应索引的下一跳索引 188 | next_list = [0, 0] 189 | for _ in xrange(2, len(pattern)): 190 | temp_list = [0] 191 | for i in xrange(1, _): 192 | if pattern[0:i] == pattern[_ - i:_]: 193 | temp_list.append(i) 194 | next_list.append(max(temp_list)) 195 | return next_list 196 | 197 | next_list = next_index(pattern) 198 | return next_list[index] 199 | 200 | j = get_next(pattern, j) 201 | while j < len(pattern): 202 | if i < len(str) and str[i] == pattern[j]: 203 | i += 1 204 | j += 1 205 | else: 206 | break 207 | if j == len(pattern): 208 | index.append(i - j) 209 | j = 0 210 | return index 211 | 212 | def min_distance_1(self, strs, str1, str2): 213 | ''' 214 | 数组中两个字符串的最小距离,这是方法1,时间复杂度 o(n^2) 215 | :param strs: 给定的数组中存放有多个字符串 216 | :param str1: 第一个字符串 217 | :param str2: 第二个字符串 218 | :return: 如果其中给定的一个字符串不在数组 strs 中,那么返回-1,否则返回两个字符串之间的最小间距 219 | ''' 220 | if str1 not in strs or str2 not in strs: 221 | return -1 222 | if str1 == str2: 223 | return 0 224 | dist, min = 1, len(strs) 225 | pos1, pos2 = 0, len(strs) 226 | for i in xrange(0, len(strs)): 227 | if str1 == strs[i]: 228 | pos1 = i 229 | for j in xrange(0, len(strs)): 230 | if str2 == strs[j]: 231 | pos2 = j 232 | dist = abs(pos1 - pos2) 233 | if dist < min: 234 | min = dist 235 | return min 236 | 237 | def min_distance_2(self, strs, str1, str2): 238 | ''' 239 | 数组中两个字符串的最小距离,这是方法2,如果查询的次数非常多,把每次查询的时间复杂度下降到 o(1)。 240 | Python 的内置 dict 类型就是哈希表,实现方法也是hash 表,其查询的时间复杂度就是 o(1)。哈希表的构造也分很多种: 241 | 比如,构造 Hash 表,key值是strs中的每一个字符串,value值是一个hash表,里面存放着该字符串到其它字符串的最小距离。 242 | 写成代码就是:hash_table = {"*":{"3":1, "5":1, "10":2, "9":3, "7":2, "1":1}} 243 | 当然这种方法的空间复杂度是 o(n^2) 244 | :param strs: 给定的数组中存放有多个字符串['*','3','*','5','10','9','7','1','*'] 245 | :param str1: 第一个字符串, '*' 246 | :param str2: 第二个字符串, '9' 247 | :return: 如果其中给定的一个字符串不在数组 strs 中,那么返回-1,否则返回两个字符串之间的最小间距 248 | ''' 249 | if str1 not in strs or str2 not in strs: 250 | return -1 251 | if str1 == str2: 252 | return 0 253 | 254 | def create_hash(strs): # 创建 Hash 表的过程,其实创建完后就不再更改了,这里为了方便说明就具体写了一下 255 | strs_set = list(set(strs)) 256 | dist_hash = {} 257 | for i in xrange(0, len(strs_set)): 258 | temp = {} 259 | for j in xrange(0, len(strs_set)): 260 | if strs_set[i] != strs_set[j]: 261 | dist = self.min_distance_1(strs, strs_set[i], strs_set[j]) 262 | temp[strs_set[j]] = dist 263 | dist_hash[strs_set[i]] = temp 264 | return dist_hash 265 | 266 | return create_hash(strs)[str1][str2] 267 | 268 | def brackets_is_valid_1(self, str): 269 | ''' 270 | 判断括号字符串是不是有效括号字符串,比如:"()()"为 True,"(()())"为 True,"())","(())"等等均为 False 271 | 方法1:建立一个栈操作,时间复杂度为一遍遍历,但是空间复杂度较高。 272 | :param str: 括号字符串 273 | :return: True 或者 False 274 | ''' 275 | stack = [] 276 | for i in xrange(0, len(str)): 277 | if str[i] != "(" and str[i] != ")": 278 | return False 279 | if str[i] == "(": 280 | stack.append("(") 281 | else: 282 | stack.pop() 283 | if stack != []: 284 | return False 285 | else: 286 | return True 287 | 288 | def brackets_is_valid_2(self, str): 289 | ''' 290 | 判断括号字符串是不是有效括号字符串,比如:"()()"为 True,"(()())"为 True,"())","(())"等等均为 False 291 | 方法2:时间复杂度不变,仍是一遍遍历的,但是空间复杂度只有o(1)。 292 | :param str: 括号字符串 293 | :return: True 或者 False 294 | ''' 295 | num1, num2 = 0, 0 296 | for i in xrange(0, len(str)): 297 | if str[i] != "(" and str[i] != ")": 298 | return False 299 | if str[i] == "(": 300 | num1 += 1 301 | else: 302 | num2 += 1 303 | if num1 < num2: 304 | return False 305 | if num1 == num2: 306 | return True 307 | else: 308 | return False 309 | 310 | def longest_sub_brackets(self, str): 311 | ''' 312 | 给定一个括号字符串 str,返回最长的有效括号字符串 313 | 方法:动态规划求解,做到时间复杂度 o(n),空间复杂度 o(n)。创建一个与字符串同等长度的数组 dp[], 314 | 其含义是对应 str[i]结尾的字符串的最长有效子串的长度。然后即可开始求解。 315 | :param str: 给定的括号字符串 316 | :return: 最长有效子串 317 | ''' 318 | print str 319 | dp = [] 320 | for _ in xrange(0, len(str)): 321 | dp.append(0) 322 | for i in xrange(0, len(str)): 323 | if str[i] == "(": 324 | dp[i] = 0 325 | if str[i] == ")": 326 | if i != 0: 327 | pre = i - dp[i - 1] - 1 328 | if str[pre] == "(": 329 | dp[i] = dp[i - 1] + 2 + dp[pre - 1] 330 | return max(dp) 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | str_algo = StringAlgorithm() 339 | # print "is_deformation: ", str_algo. is_deformation("fefdw", "wef") 340 | # print "sum_of_num: ", str_algo.sum_of_num("a12b--2fe.8") 341 | # print "remove_k_zeros: ", str_algo.remove_k_zeros("0000fw300001203h000ui0000re3_0000", 4) 342 | # print "is_rotation: ", str_algo.is_rotation("bhfj", "jbfh") 343 | # print "str_2_int: ", str_algo.str_2_int("-3761aa29"), type(str_algo.str_2_int("376129")) 344 | # print "replace_str: ", str_algo.replace_str() 345 | print "get_count_str: ", str_algo.get_count_str("fffjkk99999022____") 346 | print "get_char_at: ", str_algo.get_char_at(str_algo.get_count_str("fffjkk99999022"), 14) 347 | print "is_unique_1: ", str_algo.is_unique_1("hdslegtr") 348 | print "is_unique_2: ", str_algo.is_unique_2("hdslecvb") 349 | print "get_index: ", str_algo.get_index(['a', None, 'b', None, 'd', 'd', None, 'k', 'm'], 'd') 350 | print "replace_space: ", str_algo.replace_space([' ', 'a', ' ', 'b', ' ', ' ', 'g']) 351 | print "replace_stars: ", str_algo.replace_stars(['*', '3', '*', '5', '9', '1', '*']) 352 | 353 | # print "min_distance_1: ", str_algo.min_distance_1(['*','3','*','5','10','9','7','1','*'], '*', '7') 354 | # print "min_distance_2: ", str_algo.min_distance_2(['*','3','*','5','10','9','7','1','*'], '*', '7') 355 | print "brackets_is_valid: ", str_algo.brackets_is_valid_1("(()(())())") 356 | print "brackets_is_valid: ", str_algo.brackets_is_valid_2("(()(())())") 357 | print "longest_sub_brackets", str_algo.longest_sub_brackets("(()(((&))(())") 358 | -------------------------------------------------------------------------------- /linkedlist_algorithms/linkedlist_algorithms.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | 3 | import math 4 | from stack_algorithms import Stack 5 | 6 | 7 | class Node(object): 8 | def __init__(self, value, next=0): 9 | self.value = value 10 | self.next = next # 指针 11 | 12 | 13 | class LinkedList(object): 14 | # 链表的数据结构 15 | def __init__(self): 16 | self.head = 0 # 头部 17 | 18 | def __getitem__(self, key): 19 | if self.is_empty(): 20 | print 'Linked list is empty.' 21 | return 22 | elif key < 0 or key > self.get_length(): 23 | print 'The given key is wrong.' 24 | return 25 | else: 26 | return self.get_elem(key) 27 | 28 | def __setitem__(self, key, value): 29 | if self.is_empty(): 30 | print 'Linked list is empty.' 31 | return 32 | elif key < 0 or key > self.get_length(): 33 | print 'The given key is wrong.' 34 | return 35 | else: 36 | return self.set_elem(key, value) 37 | 38 | def init_list(self, data): # 按列表给出 data 39 | self.head = Node(data[0]) 40 | p = self.head # 指针指向头结点 41 | for i in data[1:]: 42 | p.next = Node(i) # 确定指针指向下一个结点 43 | p = p.next # 指针滑动向下一个位置 44 | 45 | def get_length(self): 46 | p, length = self.head, 0 47 | while p != 0: 48 | length += 1 49 | p = p.next 50 | return length 51 | 52 | def is_empty(self): 53 | if self.head == 0: 54 | return True 55 | else: 56 | return False 57 | 58 | def insert_node(self, index, value): 59 | if index < 0 or index > self.get_length(): 60 | print 'Can not insert node into the linked list.' 61 | elif index == 0: 62 | temp = self.head 63 | self.head = Node(value, temp) 64 | else: 65 | p, post = self.head, self.head 66 | for i in xrange(index): 67 | post = p 68 | p = p.next 69 | temp = p 70 | post.next = Node(value, temp) 71 | 72 | def delete_node(self, index): 73 | if index < 0 or index > self.get_length()-1: 74 | print "Wrong index number to delete any node." 75 | elif self.is_empty(): 76 | print "No node can be deleted." 77 | elif index == 0: 78 | temp = self.head 79 | self.head = temp.next 80 | elif index == self.get_length(): 81 | p = self.head 82 | for i in xrange(self.get_length()-2): 83 | p = p.next 84 | p.next = 0 85 | else: 86 | p = self.head 87 | for i in xrange(index-1): 88 | p = p.next 89 | p.next = p.next.next 90 | 91 | def show_linked_list(self): # 打印链表中的所有元素 92 | if self.is_empty(): 93 | print 'This is an empty linked list.' 94 | else: 95 | p, container = self.head, [] 96 | for _ in xrange(self.get_length()-1): # 97 | container.append(p.value) 98 | p = p.next 99 | container.append(p.value) 100 | print container 101 | 102 | def clear_linked_list(self): # 将链表置空 103 | p = self.head 104 | for _ in xrange(0, self.get_length()-1): 105 | post = p 106 | p = p.next 107 | del post 108 | self.head = 0 109 | 110 | def get_elem(self, index): 111 | if self.is_empty(): 112 | print "The linked list is empty. Can not get element." 113 | elif index < 0 or index > self.get_length()-1: 114 | print "Wrong index number to get any element." 115 | else: 116 | p = self.head 117 | for _ in xrange(index): 118 | p = p.next 119 | return p.value 120 | 121 | def set_elem(self, index, value): 122 | if self.is_empty(): 123 | print "The linked list is empty. Can not set element." 124 | elif index < 0 or index > self.get_length()-1: 125 | print "Wrong index number to set element." 126 | else: 127 | p = self.head 128 | for _ in xrange(index): 129 | p = p.next 130 | p.value = value 131 | 132 | def get_index(self, value): 133 | p = self.head 134 | for i in xrange(self.get_length()): 135 | if p.value == value: 136 | return i 137 | else: 138 | p = p.next 139 | return -1 140 | 141 | def reverse_linked_list(self): # 将链表逆序反转,返回的是头指针。 142 | if self.head == 0: # 空链表 143 | return self.head 144 | length = self.get_length() 145 | if length == 1: 146 | return self.head 147 | p = self.head 148 | head = self.head 149 | pre = 0 150 | while head != 0: 151 | p = p.next 152 | head.next = pre 153 | pre = head 154 | head = p 155 | self.head = pre 156 | return pre 157 | 158 | def reverse_part_linked_list(self, a, b): 159 | p = self.head 160 | if a == 0: 161 | tail, head = p, p 162 | pre = 0 163 | for _ in xrange(a, b+1): 164 | p = p.next 165 | head.next = pre 166 | pre = head 167 | head = p 168 | self.head = pre 169 | tail.next = p 170 | else: 171 | for _ in xrange(1, a): 172 | p = p.next 173 | front = p 174 | p = p.next 175 | tail = p 176 | pre = 0 177 | head = p 178 | for _ in xrange(a+1, b+2): 179 | p = p.next 180 | head.next = pre 181 | pre = head 182 | head = p 183 | front.next = pre 184 | tail.next = p 185 | 186 | 187 | l = LinkedList() 188 | print "The length of linked list now is: ", l.get_length() 189 | print l.is_empty() 190 | l.init_list([1, 5, 12, "fjd", 45, 999]) 191 | print "The length of linked list now is: ", l.get_length() 192 | print l.is_empty() 193 | l.insert_node(4, 100) 194 | l.insert_node(6, "cecil") 195 | #l.show_linked_list() 196 | print "The value of index 0 is: ", l.get_elem(0) 197 | l.set_elem(0,1000) 198 | #l.show_linked_list() 199 | print "the index of *** is: ", l.get_index(1009) 200 | print "The length of linked list now is: ", l.get_length() 201 | l.delete_node(3) 202 | #l.clear_linked_list() 203 | #l.reverse_linked_list() 204 | #l.show_linked_list() 205 | l.reverse_part_linked_list(4, 6) 206 | #l.show_linked_list() 207 | print "****************************************" 208 | 209 | 210 | class RingLinkedList(object): 211 | # 链表的数据结构 212 | def __init__(self): 213 | self.head = 0 # 头部 214 | 215 | def __getitem__(self, key): 216 | if self.is_empty(): 217 | print 'Linked list is empty.' 218 | return 219 | elif key < 0 or key > self.get_length(): 220 | print 'The given key is wrong.' 221 | return 222 | else: 223 | return self.get_elem(key) 224 | 225 | def __setitem__(self, key, value): 226 | if self.is_empty(): 227 | print 'Linked list is empty.' 228 | return 229 | elif key < 0 or key > self.get_length(): 230 | print 'The given key is wrong.' 231 | return 232 | else: 233 | return self.set_elem(key, value) 234 | 235 | def init_list(self, data): # 按列表给出 data 236 | self.head = Node(data[0]) 237 | p = self.head # 指针指向头结点 238 | for i in data[1:]: 239 | p.next = Node(i) # 确定指针指向下一个结点 240 | p = p.next # 指针滑动向下一个位置 241 | p.next = self.head 242 | 243 | def get_length(self): 244 | p, length = self.head, 0 245 | while p != 0: 246 | length += 1 247 | p = p.next 248 | if p == self.head: 249 | break 250 | return length 251 | 252 | def is_empty(self): 253 | if self.head == 0: 254 | return True 255 | else: 256 | return False 257 | 258 | def insert_node(self, index, value): 259 | length = self.get_length() 260 | if index < 0 or index > length: 261 | print 'Can not insert node into the linked list.' 262 | elif index == 0: 263 | temp = self.head 264 | self.head = Node(value, temp) 265 | p = self.head 266 | for _ in xrange(0, length): 267 | p = p.next 268 | #print "p.value", p.value 269 | p.next = self.head 270 | elif index == length: 271 | elem = self.get_elem(length-1) 272 | elem.next = Node(value) 273 | elem.next.next = self.head 274 | else: 275 | p, post = self.head, self.head 276 | for i in xrange(index): 277 | post = p 278 | p = p.next 279 | temp = p 280 | post.next = Node(value, temp) 281 | 282 | def delete_node(self, index): 283 | if index < 0 or index > self.get_length()-1: 284 | print "Wrong index number to delete any node." 285 | elif self.is_empty(): 286 | print "No node can be deleted." 287 | elif index == 0: 288 | tail = self.get_elem(self.get_length()-1) 289 | temp = self.head 290 | self.head = temp.next 291 | tail.next = self.head 292 | elif index == self.get_length()-1: 293 | p = self.head 294 | for i in xrange(self.get_length()-2): 295 | p = p.next 296 | p.next = self.head 297 | else: 298 | p = self.head 299 | for i in xrange(index-1): 300 | p = p.next 301 | p.next = p.next.next 302 | 303 | def show_linked_list(self): # 打印链表中的所有元素 304 | if self.is_empty(): 305 | print 'This is an empty linked list.' 306 | else: 307 | p, container = self.head, [] 308 | for _ in xrange(self.get_length()-1): # 309 | container.append(p.value) 310 | p = p.next 311 | container.append(p.value) 312 | print container 313 | 314 | def clear_linked_list(self): # 将链表置空 315 | p = self.head 316 | for _ in xrange(0, self.get_length()-1): 317 | post = p 318 | p = p.next 319 | del post 320 | self.head = 0 321 | 322 | def get_elem(self, index): 323 | if self.is_empty(): 324 | print "The linked list is empty. Can not get element." 325 | elif index < 0 or index > self.get_length()-1: 326 | print "Wrong index number to get any element." 327 | else: 328 | p = self.head 329 | for _ in xrange(index): 330 | p = p.next 331 | return p 332 | 333 | def set_elem(self, index, value): 334 | if self.is_empty(): 335 | print "The linked list is empty. Can not set element." 336 | elif index < 0 or index > self.get_length()-1: 337 | print "Wrong index number to set element." 338 | else: 339 | p = self.head 340 | for _ in xrange(index): 341 | p = p.next 342 | p.value = value 343 | 344 | def get_index(self, value): 345 | p = self.head 346 | for i in xrange(self.get_length()): 347 | if p.value == value: 348 | return i 349 | else: 350 | p = p.next 351 | return -1 352 | 353 | ring = RingLinkedList() 354 | ring.init_list([2,5,9,12,37,49,88,91,100]) 355 | ring.show_linked_list() 356 | ring.insert_node(0, 100) 357 | ring.show_linked_list() 358 | ring.delete_node(3) 359 | ring.show_linked_list() 360 | ring.clear_linked_list() 361 | ring.show_linked_list() 362 | print "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" 363 | 364 | 365 | class LinkedListAlgorithms(object): 366 | # 所有的链表算法题 367 | def __init__(self): 368 | pass 369 | 370 | def print_common_part(self, head1, head2): # 给定两个链表的头指针,打印出公共的部分 371 | if head1.next == 0 or head2.next == 0: 372 | print 'No common part between two linked lists.' 373 | common = [] 374 | while head1 is not None and head2 is not None: 375 | if head1.value > head2.value: 376 | head2 = head2.next 377 | elif head1.value < head2.value: 378 | head1 = head1.next 379 | else: 380 | common.append(head1.value) 381 | head1, head2 = head1.next, head2.next 382 | if head1 == 0 or head2 == 0: 383 | break 384 | print 'Common part: ', common 385 | 386 | def rm_last_kth_node(self, k, linked_list): # 删除倒数第 K 个节点,针对单链表的 387 | if linked_list.is_empty(): 388 | print 'The given linked_list is empty.' 389 | if k < 1 or k > linked_list.get_length(): 390 | print 'Wrong kth number out of index.' 391 | k = linked_list.get_length() - k 392 | if k == 0: 393 | p = linked_list.head 394 | linked_list.head = p.next 395 | else: 396 | p = linked_list.head 397 | for i in xrange(k-1): 398 | p = p.next 399 | p.next = p.next.next 400 | 401 | def rm_mid_node(self, head): # 给定一个链表,删除它的中间的一个结点,返回头指针 402 | if head == 0: 403 | return head 404 | p = head 405 | length = 1 406 | while p != 0: 407 | length += 1 408 | p = p.next 409 | if length == 1: 410 | return head 411 | mid = length / 2 412 | for _ in xrange(1, mid-1): 413 | head = head.next 414 | head.next = head.next.next 415 | return head 416 | 417 | def rm_by_ratio(self, head, a, b): # 给定一个链表,删除它按比例的 a/b处的一个结点,返回头指针 418 | if a > b: 419 | print "The given a should be lower than b." 420 | return head 421 | if head == 0: 422 | return head 423 | p = head 424 | length = 1 425 | while p != 0: 426 | length += 1 427 | p = p.next 428 | if length == 1: 429 | return head 430 | ratio = length*float(a)/float(b) 431 | for _ in xrange(1, int(math.ceil(ratio))-1): 432 | head = head.next 433 | head.next = head.next.next 434 | return head 435 | 436 | def reverse_linked_list(self, head): # 将链表逆序反转,返回头指针 437 | if head == 0: # 空链表 438 | return head 439 | p = head 440 | length = 1 441 | while p != 0: 442 | length += 1 443 | p = p.next 444 | if length == 1: 445 | return head 446 | p = head 447 | pre = 0 448 | while head != 0: 449 | p = p.next 450 | head.next = pre 451 | pre = head 452 | head = p 453 | return pre 454 | 455 | def reverse_part_linked_list(self, head, a, b): # 反转部分链表结点,a, b分别为索引值 456 | if head == 0: 457 | print "Empty linked list. No need to reverse." 458 | return head 459 | p = head 460 | length = 1 461 | while p != 0: 462 | length += 1 463 | p = p.next 464 | if length == 1: 465 | print "No need to reverse." 466 | return head 467 | if a < 0 or b > length-1 or a >= b: 468 | raise Exception("The given 'from' value and 'to' value is wrong.") 469 | p = head 470 | 471 | if a == 0: # 由于 for 循环中 xrange 的范围问题,我就分情况写了。 472 | tail, head = p, p 473 | pre = 0 474 | for _ in xrange(a, b+1): 475 | p = p.next 476 | head.next = pre 477 | pre = head 478 | head = p 479 | tail.next = p 480 | return head 481 | else: 482 | for _ in xrange(1, a): 483 | p = p.next 484 | front, tail, head = p, p, p 485 | p = p.next 486 | pre = 0 487 | for _ in xrange(a+1, b+2): 488 | p = p.next 489 | head.next = pre 490 | pre = head 491 | head = p 492 | front.next = pre 493 | tail.next = p 494 | return head 495 | 496 | def is_palindrome1(self, head): 497 | ''' 498 | 判断一个链表是否是回文结构,如果是返回 True,否则返回 False 499 | 方法1:时间复杂度o(n),空间复杂度o(n) 500 | ''' 501 | if head == 0 or head.next == 0: 502 | return True 503 | p = head 504 | stack = Stack() # 自定义的 Stack 数据结构,也可以使用python内置的 list来实现 505 | while p != 0: 506 | stack.push(p.value) 507 | p = p.next 508 | p = head 509 | while p != 0: 510 | if p.value != stack.pop(): 511 | return False 512 | p = p.next 513 | return True 514 | 515 | def is_palindrome2(self, head): 516 | ''' 517 | 判断一个链表是否是回文结构,如果是返回 True,否则返回 False 518 | 方法2:时间复杂度o(n),空间复杂度o(1),方法是将链表的右半边逆序反转一下,然后逐个比较,如果有不同,则返回错误 519 | ''' 520 | p, length = head, 0 521 | while p != 0: 522 | length += 1 523 | p = p.next 524 | 525 | mid = (length+1) / 2 - 1 526 | p = head 527 | for _ in xrange(mid): 528 | p = p.next 529 | 530 | mid_node = p # 获取中间节点 531 | tail = mid_node.next 532 | mid_node.next = 0 533 | 534 | pre = mid_node 535 | while tail != 0: 536 | p = tail.next 537 | tail.next = pre 538 | pre = tail 539 | tail = p 540 | 541 | p = head 542 | tail = pre 543 | #print "p.value", p.value, p.next.value, p.next.next.value, p.next.next.next 544 | #print "tail.value:", pre.value, pre.next.value, pre.next.next.value, pre.next.next.next.value 545 | while p != 0: 546 | #print "p.value:", p.value, "tail.value:", tail.value 547 | if p.value != pre.value: # 分别从两头遍历链表,如果遍历到0,则说明已经遍历到头了。 548 | pre = 0 549 | while tail != 0: 550 | next = tail.next 551 | tail.next = pre 552 | pre = tail 553 | tail = next 554 | mid_node.next = pre.next 555 | return False 556 | else: 557 | p = p.next 558 | pre = pre.next 559 | pre = 0 560 | while tail != 0: 561 | next = tail.next 562 | tail.next = pre 563 | pre = tail 564 | tail = next 565 | mid_node.next = pre.next 566 | return True 567 | 568 | def josephus_kill_1(self, head, m): 569 | ''' 570 | 环形单链表,使用 RingLinkedList 数据结构,约瑟夫问题。 571 | :param head:给定一个环形单链表的头结点,和第m个节点被杀死 572 | :return:返回最终剩下的那个结点 573 | 本方法比较笨拙,就是按照规定的路子进行寻找,时间复杂度为o(m*len(ringlinkedlist)) 574 | ''' 575 | if head == 0: 576 | print "This is an empty ring linked list." 577 | return head 578 | if m < 2: 579 | print "Wrong m number to play this game." 580 | return head 581 | p = head 582 | while p.next != p: 583 | for _ in xrange(0, m-1): 584 | post = p 585 | p = p.next 586 | #print post.next.value 587 | post.next = post.next.next 588 | p = post.next 589 | return p 590 | 591 | def list_partition(self, head, pivot): 592 | ''' 593 | 将单向链表按某个值划分成左边小,中间相等,右边大的形式 594 | :param head:单向普通链表的头结点 595 | :param pivot:中间相等的那个数值 596 | :return:返回新的单向链表,左边和右边的结点顺序和原链表中的顺序一致。 597 | 方法1:最常见的算法就是重新开辟三个链表,分别存储small ,middle,和large的值,时间复杂度为链表长度,空间复杂度也是 598 | 方法2:把空间复杂度降到o(1),就是需要不停地调整链表中的位置 599 | ''' 600 | if head == 0: 601 | print "This is an empty linked list." 602 | return 603 | sh, st, eh, et, lh, lt = 0, 0, 0, 0, 0, 0 604 | p = head 605 | while p != 0: 606 | if p.value < pivot: 607 | if sh == 0: 608 | sh = p 609 | st = p 610 | else: 611 | st.next = p 612 | st = st.next 613 | elif p.value == pivot: 614 | if eh == 0: 615 | eh = p 616 | et = p 617 | else: 618 | et.next = p 619 | et = et.next 620 | else: 621 | if lh == 0: 622 | lh = p 623 | lt = p 624 | else: 625 | lt.next = p 626 | lt = lt.next 627 | p = p.next 628 | 629 | if sh != 0 and eh != 0 and lh != 0: 630 | head = sh 631 | st.next = eh 632 | et.next = lh 633 | lt.next = 0 634 | elif sh == 0 and eh != 0 and lh != 0: 635 | head = eh 636 | et.next = lh 637 | lh.next = 0 638 | elif sh == 0 and eh == 0 and lh != 0: 639 | head = lh 640 | lh.next = 0 641 | elif sh == 0 and eh != 0 and lh == 0: 642 | head = eh 643 | et.next = 0 644 | elif sh != 0 and eh == 0 and lh == 0: 645 | head = sh 646 | st.next = 0 647 | elif sh != 0 and eh != 0 and lh == 0: 648 | head = sh 649 | st.next = eh 650 | et.next = 0 651 | elif sh != 0 and eh == 0 and lh != 0: 652 | head = sh 653 | st.next = lh 654 | lt.next = 0 655 | return head 656 | 657 | def copy_list_with_random_pointer(self, head): 658 | ''' 659 | 一种特殊类型的链表结构,每一个节点都有一个随机指针域,指针指向该链表中随机一个结点,也有可能指向0. 660 | 复制含有随机指针结点的链表,比如一个单链表中有1->2->3->None,其中1还指向了3,2指向了None。 661 | 将整个数据结构都复制一遍,并返回头结点head,时间复杂度为一遍遍历该种链表,空间复杂度为o(1)。 662 | ''' 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | l1 = LinkedList() 673 | l1.init_list([1, 5, 12, 33, 45, 171, 999, 1001, 2000]) 674 | l2 = LinkedList() 675 | l2.init_list([2, 5, 12,198, 12, 5, 2]) 676 | l3 = LinkedList() 677 | l3.init_list([21, 5, 120, 19, 72, 50, 312]) 678 | 679 | ll_algo = LinkedListAlgorithms() 680 | # ll_algo.print_common_part(l1.head, l2.head) 681 | # ll_algo.rm_last_kth_node(6, l1) 682 | # ll_algo.rm_mid_node(l2.head) 683 | # ll_algo.rm_by_ratio(l1.head, 41, 170) 684 | print "999" 685 | #l1.show_linked_list() 686 | # ll_algo.reverse_linked_list(l1.head) 687 | print "is palindrome1: ", ll_algo.is_palindrome1(l2.head) 688 | print "is palindrome2: ", ll_algo.is_palindrome2(l2.head) 689 | print "888" 690 | l2.show_linked_list() 691 | print "##################################################" 692 | 693 | ring = RingLinkedList() 694 | ring.init_list([2,5,9,12,37,49,88,91,100]) 695 | print ll_algo.josephus_kill_1(ring.head, 4).value 696 | ll_algo.list_partition(l3.head, 100) 697 | l3.show_linked_list() 698 | 699 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 39 | 40 | 41 | 61 | 62 | 63 | 64 | 65 | true 66 | DEFINITION_ORDER 67 | 68 | 69 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 97 | 98 | 101 | 102 | 103 | 104 | 107 | 108 | 111 | 112 | 113 | 114 | 117 | 118 | 121 | 122 | 125 | 126 | 127 | 128 | 131 | 132 | 135 | 136 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 161 | 162 | 163 | 164 | 182 | 183 | 201 | 202 | 220 | 221 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 270 | 271 | 284 | 285 | 303 | 304 | 316 | 317 | project 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 352 | 353 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 384 | 385 | 386 | 387 | 1509879019950 388 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 430 | 431 | 433 | 434 | 435 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | --------------------------------------------------------------------------------