diff --git a/.idea/AlgorithmsByPython.iml b/.idea/AlgorithmsByPython.iml index 7715f25..e451288 100644 --- a/.idea/AlgorithmsByPython.iml +++ b/.idea/AlgorithmsByPython.iml @@ -2,7 +2,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index ac543ec..6985f4e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -10,5 +10,5 @@ - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 615fb60..7657e1d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,9 +2,7 @@ - - - + @@ -28,7 +26,48 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -127,6 +168,8 @@ + + @@ -161,107 +204,120 @@ - - - + - + + + + + - - + + - + - + - + - + + + - - - - - + + + + + - - - - - + + + + + @@ -430,26 +486,26 @@ - - + + - + - - + + @@ -489,370 +545,358 @@ - - - - - - - - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - - - - - - + + + + - - - - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - + + - + - - + + - + - - + + diff --git a/Dynamic Programming.py b/Dynamic Programming.py index 02a6c3d..6af6a43 100644 --- a/Dynamic Programming.py +++ b/Dynamic Programming.py @@ -1,12 +1,12 @@ # 解决动态规划中的找零问题 # 输入需要找零的金额和货币的币值向量 # 输出满足找零条件的最少的硬币个数 -def ChangeMaking(coinValueList, change): +def ChangeMaking(coinVal, change): alist = [0]*(change+1) for i in range(1, change+1): temp = change; j = 0 - while j <= len(coinValueList)-1 and i >= coinValueList[j]: - temp = min(alist[i-coinValueList[j]], temp) + while j <= len(coinVal)-1 and i >= coinVal[j]: + temp = min(alist[i-coinVal[j]], temp) j += 1 alist[i] = temp + 1 return alist.pop() diff --git a/QuickSort.py b/QuickSort.py index cd8a673..59b0972 100644 --- a/QuickSort.py +++ b/QuickSort.py @@ -1,3 +1,5 @@ +# coding: utf-8 + def quickSort(alist): quickSortHelper(alist, 0, len(alist)-1) @@ -16,9 +18,9 @@ def partition(alist, first, last): done = False while not done: - while alist[leftmark] <= pivotvlue and leftmark <= rightmark: + while leftmark <= rightmark and alist[leftmark] <= pivotvlue: # bugfix: 先比较index, 不然数组会越界 leftmark += 1 - while alist[rightmark] >= pivotvlue and rightmark >= leftmark: + while rightmark >= leftmark and alist[rightmark] >= pivotvlue: rightmark -= 1 if leftmark > rightmark: @@ -32,3 +34,13 @@ def partition(alist, first, last): alist2 = [1] quickSort(alist2) print(alist2) + + +if __name__ == "__main__": + test_data = [3,2,111,3,-1,0,0,1,0,2,4] + + res_stable = sorted(test_data) + quickSort(test_data) + print(test_data) + print(res_stable) + assert all(map(lambda x: x[0] == x[1], zip(res_stable, test_data))) \ No newline at end of file diff --git a/Target Offer/Singleton.py b/Target Offer/Singleton.py index ac51f23..037ac1e 100644 --- a/Target Offer/Singleton.py +++ b/Target Offer/Singleton.py @@ -24,29 +24,80 @@ class Myclass(Singleton1): # one和two完全相同, 可以用id(), ==, is检测 print(id(one)) print(id(two)) -print(one == two) -print(one is two) +print(one == two) # True +print(one is two) # True + +two.a = 3 +print(one.a) # 3 ''' -方法1的升级版, 使用__metaclass__元类的高级python用法 +方法2:共享属性;所谓单例就是所有引用(实例、对象)拥有相同的的状态(属性)和行为(方法) +同一个类的所有实例天然拥有相同的行为(方法) +只需要保证一个类的所有实例具有相同的状态(属性)即可 +所有实例共享属性的最简单方法就是__dict__属性指向(引用)同一个字典(dict) ''' -class Singleton2(type): - def __init__(cls, name, bases, dict): - super(Singleton2, cls).__init__(name, bases, dict) - cls._instance = None - def __call__(cls, *args, **kwargs): - if cls._instance is None: - cls._instance = super(Singleton2, cls).__call__(*args, **kwargs) - return cls._instance +class Borg(object): + _state = {} + def __new__(cls, *args, **kwargs): + ob = super(Borg, cls).__new__(cls, *args, **kwargs) + ob.__dict__ = cls._state + return ob +class MyClass2(Borg): + a = 1 +one = MyClass2() +two = MyClass2() +two.a = 3 +print(one.a) +# one 和 two 是两个不同的对象,id,==,is对比结果可以看出 +print(id(one)) # 18410480 +print(id(two)) # 18410512 +print(one == two) # False +print(one is two) # False +# 但是one和two具有相同的(同一个)__dict__属性 +print(id(one.__dict__)) # 14194768 +print(id(two.__dict__)) # 14194768 -class Myclass2(object): - __metaclass__ = Singleton2 +''' +方法3:装饰器版本decorator +这是一种更pythonic,更elegant的方法 +单例类本身根本不知道自己是单例的,因为他自己的代码并不是单例的 +''' +def singleton(cls, *args, **kwargs): + instances = {} + def getinstance(): + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + return getinstance +@singleton +class MyClass3(object): a = 1 + def __init__(self, x = 0): + self.x = x -one = Myclass2() -two = Myclass2() +one = MyClass3() +two = MyClass3() +two.a = 3 +print(one.a) # 3 +print(id(one)) # 8842576 +print(id(two)) # 8842576 +print(one == two) # True +print(one is two) # True +one.x = 1 +print(one.x) # 1 +print(two.x) # 1 -print(id(one)) -print(id(two)) -print(one == two) -print(one is two) \ No newline at end of file +''' +方法4:import方法 +python中的模块module在程序中只被加载一次,本身就是单例的 +可以直接写一个模块,将你需要的方法和属性,写在模块中当做函数和模块作用域的全局变量即可,根本不需要写类。 +''' +# mysingleton.py +# class My_Singleton(object): +# def foo(self): +# pass +# my_singleton = My_Singleton() + +# to use +from mysingleton import my_singleton +my_singleton.foo() \ No newline at end of file diff --git "a/Target Offer/\344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.py" "b/Target Offer/\344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.py" index eef1246..0d8d072 100644 --- "a/Target Offer/\344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.py" +++ "b/Target Offer/\344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.py" @@ -6,13 +6,24 @@ # 可能是python的的整型可以无限大的原因, 导致正数和负数的异或操作不断变成更小的负数而不会溢出 # 使用Swift尝试了一下, 还是可以求得正数和负数的位操作相加运算的 # -*- coding:utf-8 -*- +# class Solution: +# def Add(self, num1, num2): +# while num2: +# sum = num1 ^ num2 +# carry = (num1 & num2) << 1 +# num1 = sum +# num2 = carry +# return num1 +# s = Solution() +# print(s.Add(4, 2)) +# -*- coding:utf-8 -*- +# 通过每次对num1进行与操作保证是一个32位的整形 +# 因此最后我们可以判断符号位是否为1做处理 class Solution: def Add(self, num1, num2): - while num2: - sum = num1 ^ num2 - carry = (num1 & num2) << 1 - num1 = sum - num2 = carry - return num1 -s = Solution() -print(s.Add(4, 2)) + # write code here + while num2 != 0: + temp = num1 ^ num2 + num2 = (num1 & num2) << 1 + num1 = temp & 0xFFFFFFFF + return num1 if num1 >> 31 == 0 else num1 - 4294967296 diff --git "a/Target Offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.py" "b/Target Offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.py" index becb654..9aa7552 100644 --- "a/Target Offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.py" +++ "b/Target Offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.py" @@ -10,28 +10,36 @@ def __init__(self, x): self.right = None class Solution: # 返回二维列表,内部每个列表表示找到的路径 - def FindPath(self, root, expectNumber): - if root == None or root.val > expectNumber: + def FindPath(self, root, sum): + if not root: return [] - elif root.val == expectNumber: - if root.left or root.right: - return [] # 因为路径的定义是从根节点到叶节点所经过的结点, 如果是根结点到任意可到达结点的和为待求值, 可以去掉这个判定 + if root.left == None and root.right == None: + if sum == root.val: + return [[root.val]] else: - return [[expectNumber]] - + return [] stack = [] - if root.left: - stackLeft = self.FindPath(root.left, expectNumber-root.val) - for i in stackLeft: - i.insert(0, root.val) - stack.append(i) - if root.right: - stackRight = self.FindPath(root.right, expectNumber-root.val) - for i in stackRight: - i.insert(0, root.val) - stack.append(i) + leftStack = self.pathSum(root.left, sum - root.val) + for i in leftStack: + i.insert(0, root.val) + stack.append(i) + rightStack = self.pathSum(root.right, sum - root.val) + for i in rightStack: + i.insert(0, root.val) + stack.append(i) return stack + # 优化写法 + def pathSum(self, root, sum): + if not root: return [] + if root.left == None and root.right == None: + if sum == root.val: + return [[root.val]] + else: + return [] + a = self.pathSum(root.left, sum - root.val) + self.pathSum(root.right, sum - root.val) + return [[root.val] + i for i in a] + pNode1 = TreeNode(10) pNode2 = TreeNode(5) pNode3 = TreeNode(12) @@ -46,4 +54,6 @@ def FindPath(self, root, expectNumber): S = Solution() -print(S.FindPath(pNode1, 22)) \ No newline at end of file +print(S.FindPath(pNode1, 22)) +# 测试用例:[1,-2,-3,1,3,-2,null,-1] -1 +# 测试用例:[-2, None, -3] -5 \ No newline at end of file diff --git "a/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.py" "b/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.py" index 58c9377..79cffc8 100644 --- "a/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.py" +++ "b/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.py" @@ -16,3 +16,26 @@ def TreeDepth(self, pRoot): return 0 else: return max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1 + # 非递归算法,利用一个栈以及一个标志位栈 + def TreeDepth2(self, pRoot): + if not pRoot: + return 0 + depth = 0 + stack, tag = [], [] + pNode = pRoot + while pNode or stack: + while pNode: + stack.append(pNode) + tag.append(0) + pNode = pNode.left + if tag[-1] == 1: + depth = max(depth, len(stack)) + stack.pop() + tag.pop() + pNode = None + else: + pNode = stack[-1] + pNode = pNode.right + tag.pop() + tag.append(1) + return depth \ No newline at end of file diff --git "a/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.py" "b/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.py" index 6b211ec..2d35cb4 100644 --- "a/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.py" +++ "b/Target Offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.py" @@ -14,16 +14,15 @@ def Mirror(self, root): if root == None: return if root.left == None and root.right == None: - return + return root pTemp = root.left root.left = root.right root.right = pTemp - if root.left: - self.Mirror(root.left) - if root.right: - self.Mirror(root.right) + self.Mirror(root.left) + self.Mirror(root.right) + # 非递归实现 def Mirror2(self, root): if root == None: @@ -58,7 +57,6 @@ def MirrorNoRecursion(self, root): nodeQue.append(pRoot.left) if pRoot.right: nodeQue.append(pRoot.right) - return pNode1 = TreeNode(8) pNode2 = TreeNode(6) diff --git "a/Target Offer/\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276.py" "b/Target Offer/\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276.py" index 3d05286..81239fd 100644 --- "a/Target Offer/\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276.py" +++ "b/Target Offer/\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276.py" @@ -29,6 +29,8 @@ def Find(self, array, target): # 判断非法输入 # 可以换成 isinstance(target, (int, float)) 进行判断 if type(target) == float and type(array[0][0]) == int: + if int(target) == target: + return False target = int(target) elif type(target) == int and type(array[0][0]) == float: target = float(int) diff --git "a/Target Offer/\345\217\245\345\255\220\344\270\255\345\215\225\350\257\215\351\246\226\345\255\227\346\257\215\345\244\247\345\206\231.py" "b/Target Offer/\345\217\245\345\255\220\344\270\255\345\215\225\350\257\215\351\246\226\345\255\227\346\257\215\345\244\247\345\206\231.py" new file mode 100644 index 0000000..942c090 --- /dev/null +++ "b/Target Offer/\345\217\245\345\255\220\344\270\255\345\215\225\350\257\215\351\246\226\345\255\227\346\257\215\345\244\247\345\206\231.py" @@ -0,0 +1,26 @@ +''' +面试题: +一个句子的所有单词的首字母大写,其余小写 +''' +def title(s): + if not s: + return "" + res = "" + diff = ord("a") - ord("A") + for i in range(1, len(s)): + if s[i-1] == " " and s[i] <= "z" and s[i] >= "a": + res += chr(ord(s[i]) - diff) + elif s[i-1] != " " and s[i] <= "Z" and s[i] >= "A": + res += chr(ord(s[i]) + diff) + else: + res += s[i] + if s[0] <= "z" and s[0] >= "a": + res = chr(ord(s[0]) - diff) + res + else: + res = s[0] + res + return res + +def title2(s): + return s.title() + +print(title2(" sDsa sddr jki ")) \ No newline at end of file diff --git "a/Target Offer/\345\255\227\347\254\246\344\270\262\345\205\267\346\234\211\347\233\270\345\220\214\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.py" "b/Target Offer/\345\255\227\347\254\246\344\270\262\345\205\267\346\234\211\347\233\270\345\220\214\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.py" new file mode 100644 index 0000000..7599d56 --- /dev/null +++ "b/Target Offer/\345\255\227\347\254\246\344\270\262\345\205\267\346\234\211\347\233\270\345\220\214\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.py" @@ -0,0 +1,29 @@ +''' +求一个字符串的最长子串,其中子串所有字符相同 +面试题 +''' +def findCommonLCS(s): + if not s: + return "" + if len(s) == 1: + return s + length = len(s) + maxIndex, maxLength = 0, 1 + curIndex = 0 + while curIndex < length: + tempLength = 1 + while curIndex + tempLength < length and s[curIndex] == s[curIndex+tempLength]: + tempLength += 1 + if maxLength < tempLength: + maxLength = tempLength + maxIndex = curIndex + if curIndex + tempLength == length: + break + curIndex += tempLength + if maxLength == 1: + return s[0] + else: + res = s[maxIndex:maxIndex+maxLength] + return res + +print(findCommonLCS("abbbasagsagsgdsaagaaccagcfsccccc")) \ No newline at end of file diff --git "a/Target Offer/\346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.py" "b/Target Offer/\346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.py" index a54e7b5..eead2a2 100644 --- "a/Target Offer/\346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.py" +++ "b/Target Offer/\346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.py" @@ -10,11 +10,10 @@ def __init__(self, x): self.right = None class Solution: # 返回二维列表[[1,2],[4,5]] - def Print(self, pRoot): + def levelOrder(self, pRoot): if pRoot == None: return [] - nodes = [pRoot] - result = [] + nodes, res = [pRoot], [] while nodes: curStack, nextStack = [], [] for node in nodes: @@ -23,9 +22,9 @@ def Print(self, pRoot): nextStack.append(node.left) if node.right: nextStack.append(node.right) - result.append(curStack) + res.append(curStack) nodes = nextStack - return result + return res pNode1 = TreeNode(8) diff --git "a/Target Offer/\346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.py" "b/Target Offer/\346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.py" index e0eb547..b0f096e 100644 --- "a/Target Offer/\346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.py" +++ "b/Target Offer/\346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.py" @@ -10,35 +10,53 @@ def __init__(self, x): self.left = None self.right = None class Solution: + # 存储点的时候按照奇数层和偶数层分别存储 def Print(self, pRoot): - if pRoot == None: + if not pRoot: return [] - result = [] - nodes = [pRoot] - right, left = True, False + result, nodes = [], [pRoot] + right = True while nodes: - currentStack = [] - nextStack = [] + curStack, nextStack = [], [] if right: for node in nodes: - currentStack.append(node.val) - if node.left != None: + curStack.append(node.val) + if node.left: nextStack.append(node.left) - if node.right != None: + if node.right: nextStack.append(node.right) - nextStack.reverse() - elif left: + else: for node in nodes: - currentStack.append(node.val) - if node.right != None: + curStack.append(node.val) + if node.right: nextStack.append(node.right) - if node.left != None: + if node.left: nextStack.append(node.left) - nextStack.reverse() - right, left = left, right - result.append(currentStack) + nextStack.reverse() + right = not right + result.append(curStack) nodes = nextStack return result + # 转换思路,存储的时候一直从左向右存储,打印的时候根据不同的层一次打印 + def zigzagLevelOrder(self, root): + if not root: + return [] + levels, result, leftToRight = [root], [], True + while levels: + curValues, nextLevel = [], [] + for node in levels: + curValues.append(node.val) + if node.left: + nextLevel.append(node.left) + if node.right: + nextLevel.append(node.right) + if not leftToRight: + curValues.reverse() + if curValues: + result.append(curValues) + levels = nextLevel + leftToRight = not leftToRight + return result pNode1 = TreeNode(8) diff --git "a/Target Offer/\346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.py" "b/Target Offer/\346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.py" index b1d441f..dcb2e14 100644 --- "a/Target Offer/\346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.py" +++ "b/Target Offer/\346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.py" @@ -12,10 +12,9 @@ def __init__(self): def Insert(self, num): if self.count & 1 == 0: self.left.append(num) - self.count += 1 else: self.right.append(num) - self.count += 1 + self.count += 1 def GetMedian(self, x): if self.count == 1: diff --git "a/Target Offer/\346\233\277\346\215\242\347\251\272\346\240\274.py" "b/Target Offer/\346\233\277\346\215\242\347\251\272\346\240\274.py" index 72e50f2..bd15a41 100644 --- "a/Target Offer/\346\233\277\346\215\242\347\251\272\346\240\274.py" +++ "b/Target Offer/\346\233\277\346\215\242\347\251\272\346\240\274.py" @@ -6,6 +6,20 @@ # -*- coding:utf-8 -*- class Solution: # s 源字符串 + + # 使用append一次遍历即可替换 + # 由于list的append是O(1)的时间复杂度,除了扩容所导致的时间损耗,该算法复杂度为O(n) + def replaceSpaceByAppend(self, s): + string = list(string) + stringReplace = [] + for item in string: + if item == ' ': + stringReplace.append('%') + stringReplace.append('2') + stringReplace.append('0') + else: + stringReplace.append(item) + return "".join(stringReplace) # 创建新的字符串进行替换 def replaceSpace1(self, s): diff --git "a/Target Offer/\346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260.py" "b/Target Offer/\346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260.py" index b9974b1..bf977d0 100644 --- "a/Target Offer/\346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260.py" +++ "b/Target Offer/\346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260.py" @@ -57,14 +57,21 @@ def GetLeastNumbers(self, tinput, k): if len(output) < k: output.append(number) else: - output = heapq.nsmallest(k, output) - if number >= output[-1]: + # 构造最小堆, 不推荐 + # output = heapq.nsmallest(k, output) + # if number >= output[-1]: + # continue + # else: + # output[-1] = number + # 构造最大堆, 推荐 + output = heapq.nlargest(k, output) + if number >= output[0]: continue else: - output[-1] = number - return output + output[0] = number + return output[::-1] # 最小堆用 return output tinput = [4,5,1,6,2,7,3,8] s = Solution() print(s.GetLeastNumbers_Solution(tinput, 4)) print(s.GetLeastNumbers(tinput, 4)) -print(s.GetLeastNumbers(tinput, 5)) \ No newline at end of file +print(s.GetLeastNumbers(tinput, 5)) diff --git "a/Target Offer/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.py" "b/Target Offer/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.py" index 858dad2..2468122 100644 --- "a/Target Offer/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.py" +++ "b/Target Offer/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.py" @@ -9,7 +9,7 @@ # -*- coding:utf-8 -*- class Solution: def maxInWindows(self, num, size): - if num == None or len(num) <= 0 or size <= 0: + if not num or size <= 0: return [] deque = [] if len(num) >= size: diff --git "a/Target Offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.py" "b/Target Offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.py" index e5659b5..0bf523a 100644 --- "a/Target Offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.py" +++ "b/Target Offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.py" @@ -15,9 +15,7 @@ def pop(self): elif len(self.stack2) == 0: while len(self.stack1) > 0: self.stack2.append(self.stack1.pop()) - return self.stack2.pop() - else: - return self.stack2.pop() + return self.stack2.pop() P = Solution() P.push(10) diff --git "a/Target Offer/\347\224\250\344\270\244\344\270\252\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.py" "b/Target Offer/\347\224\250\344\270\244\344\270\252\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.py" index a3e1e3b..a46d001 100644 --- "a/Target Offer/\347\224\250\344\270\244\344\270\252\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.py" +++ "b/Target Offer/\347\224\250\344\270\244\344\270\252\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.py" @@ -7,9 +7,7 @@ def __init__(self): self.queue1 = [] self.queue2 = [] def push(self, x): - if self.queue1 == [] and self.queue2 == []: - self.queue1.append(x) - elif self.queue1 != [] and self.queue2 == []: + if self.queue2 == []: self.queue1.append(x) else: self.queue2.append(x) @@ -36,4 +34,4 @@ def pop(self): print(P.pop()) print(P.pop()) print(P.pop()) -print(P.pop()) \ No newline at end of file +print(P.pop()) diff --git "a/Target Offer/\351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.py" "b/Target Offer/\351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.py" index 35523ab..e8b7a8c 100644 --- "a/Target Offer/\351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.py" +++ "b/Target Offer/\351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.py" @@ -16,12 +16,12 @@ def MeetingNode(self, pHead): if pSlow == None: return None pFast = pSlow.next - while pSlow != None and pFast != None: + while pFast: if pSlow == pFast: return pSlow pSlow = pSlow.next pFast = pFast.next - if pFast != None: + if pFast: pFast = pFast.next def EntryNodeOfLoop(self, pHead): diff --git "a/Target Offer/\351\233\266\351\222\261\346\211\276\351\233\266\344\273\245\345\217\212\350\277\233\351\230\266" "b/Target Offer/\351\233\266\351\222\261\346\211\276\351\233\266\344\273\245\345\217\212\350\277\233\351\230\266" new file mode 100644 index 0000000..0094fd3 --- /dev/null +++ "b/Target Offer/\351\233\266\351\222\261\346\211\276\351\233\266\344\273\245\345\217\212\350\277\233\351\230\266" @@ -0,0 +1,40 @@ +''' +零钱找零问题,使用动态规划 +''' +def ChangeMaking(coinVal, change): + alist = [0]*(change+1) + for i in range(1, change+1): + temp = change; j = 0 + while j <= len(coinVal)-1 and i >= coinVal[j]: + temp = min(alist[i-coinVal[j]], temp) + j += 1 + alist[i] = temp + 1 + return alist.pop() + +print(ChangeMaking([1, 5, 10, 25], 63)) + +''' +零钱找零问题的进阶 +美团笔试题 +给你六中零钱1,5,10,20,50,100的纸币,给定一个金额,写出所有可能的找零的个数 +输入2,输出1;输入5,输出2 +也是使用动态规划 +''' +import sys +try: + while True: + line = sys.stdin.readline().strip() + if line == '': + break + target = int(line) + coinVal = [1, 5, 10, 20, 50, 100] + alist = [0]*(target+1) + alist[0] = 1 + for i in range(6): + j = coinVal[i] + while j <= target: + alist[j] = alist[j] + alist[j-coinVal[i]] + j += 1 + print(alist[-1]) +except: + pass diff --git a/__pycache__/mysingleton.cpython-35.pyc b/__pycache__/mysingleton.cpython-35.pyc new file mode 100644 index 0000000..20c8509 Binary files /dev/null and b/__pycache__/mysingleton.cpython-35.pyc differ diff --git a/leetcode/101. Symmetric Tree.py b/leetcode/101. Symmetric Tree.py new file mode 100644 index 0000000..8e05077 --- /dev/null +++ b/leetcode/101. Symmetric Tree.py @@ -0,0 +1,75 @@ +''' +Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). + +For example, this binary tree [1,2,2,3,4,4,3] is symmetric: + + 1 + / \ + 2 2 + / \ / \ +3 4 4 3 +But the following [1,2,2,null,3,null,3] is not: + 1 + / \ + 2 2 + \ \ + 3 3 +''' + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + # recursive + def isSymmetric(self, root): + return self._Symmetrical(root, root) + def _Symmetrical(self, pRoot1, pRoot2): + if pRoot1 and pRoot2: + return pRoot1.val == pRoot2.val and self._Symmetrical(pRoot1.left, pRoot2.right) and self._Symmetrical( + pRoot1.right, pRoot2.left) + else: + return pRoot1 == pRoot2 + + #iterative(BFS) + def isSymmetric2(self, root): + if root: + now = [root] + while now: + vals = [i.val if i else None for i in now] + if list(reversed(vals)) != vals: + return False + else: + now = [j for i in now if i for j in (i.left, i.right)] + return True + # modify iterative(BFS) + def isSymmetric_2(self, root): + if root: + nodeStack = [root] + while nodeStack: + vals = [node.val if node else None for node in nodeStack] + if list(reversed(vals)) != vals: + return False + else: + preStack = [node for node in nodeStack if node] + nodeStack = [] + for preNode in preStack: + nodeStack.append(preNode.left) + nodeStack.append(preNode.right) + return True + + # iterative(DFS) + def isSymmetric3(self, root): + if root: + stack = [(root.left, root.right)] + while len(stack) > 0: + p, q = stack.pop() + if p and q and p.val == q.val: + stack.append((p.left, q.right)) + stack.append((p.right, q.left)) + elif p != q: + return False + return True + diff --git a/leetcode/102. Binary Tree Level Order Traversal.py b/leetcode/102. Binary Tree Level Order Traversal.py new file mode 100644 index 0000000..5827175 --- /dev/null +++ b/leetcode/102. Binary Tree Level Order Traversal.py @@ -0,0 +1,36 @@ +''' +Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). + +For example: +Given binary tree [3,9,20,null,null,15,7], + 3 + / \ + 9 20 + / \ + 15 7 +return its level order traversal as: +[ + [3], + [9,20], + [15,7] +] +''' +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def levelOrder(self, root): + if not root: + return [] + res, level = [], [root] + while level: + res.append([node.val for node in level]) + temp = [] + for node in level: + temp.extend([node.left, node.right]) + level = [node for node in temp if node] + return res \ No newline at end of file diff --git a/leetcode/103. Binary Tree Zigzag Level Order Traversal.py b/leetcode/103. Binary Tree Zigzag Level Order Traversal.py new file mode 100644 index 0000000..d7a9946 --- /dev/null +++ b/leetcode/103. Binary Tree Zigzag Level Order Traversal.py @@ -0,0 +1,64 @@ +''' +Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). + +For example: +Given binary tree [3,9,20,null,null,15,7], + 3 + / \ + 9 20 + / \ + 15 7 +return its zigzag level order traversal as: +[ + [3], + [20,9], + [15,7] +] +''' +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution: + def zigzagLevelOrder(self, root): + if not root: + return [] + levels, result, leftToRight = [root], [], True + while levels: + curValues, nextLevel = [], [] + for node in levels: + curValues.append(node.val) + if node.left: + nextLevel.append(node.left) + if node.right: + nextLevel.append(node.right) + if not leftToRight: + curValues.reverse() + if curValues: + result.append(curValues) + levels = nextLevel + leftToRight = not leftToRight + return result + + +pNode1 = TreeNode(8) +pNode2 = TreeNode(6) +pNode3 = TreeNode(10) +pNode4 = TreeNode(5) +pNode5 = TreeNode(7) +pNode6 = TreeNode(9) +pNode7 = TreeNode(11) + +pNode1.left = pNode2 +pNode1.right = pNode3 +pNode2.left = pNode4 +pNode2.right = pNode5 +pNode3.left = pNode6 +pNode3.right = pNode7 + +S = Solution() +aList = S.zigzagLevelOrder(pNode1) +print(aList) \ No newline at end of file diff --git a/leetcode/105. Construct Binary Tree from Preorder and Inorder Traversal.py b/leetcode/105. Construct Binary Tree from Preorder and Inorder Traversal.py new file mode 100644 index 0000000..27eb717 --- /dev/null +++ b/leetcode/105. Construct Binary Tree from Preorder and Inorder Traversal.py @@ -0,0 +1,57 @@ +''' +Given preorder and inorder traversal of a tree, construct the binary tree. +''' +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + # recursion + def buildTree(self, preorder, inorder): + if not preorder or not inorder: + return + root = TreeNode(preorder[0]) + ind = inorder.index(preorder.pop(0)) + root.left = self.buildTree(preorder, inorder[0:ind]) + root.right = self.buildTree(preorder, inorder[ind + 1:]) + return root + # method2, faster!! + def buildTree2(self, preorder, inorder): + self.Ind = 0 + ind = {val: ind for ind, val in enumerate(inorder)} + head = self.build(0, len(preorder) - 1, preorder, inorder, ind) + return head + + def build(self, start, end, preorder, inorder, ind): + if start <= end: + mid = ind[preorder[self.Ind]] + self.Ind += 1 + root = TreeNode(inorder[mid]) + root.left = self.build(start, mid - 1, preorder, inorder, ind) + root.right = self.build(mid + 1, end, preorder, inorder, ind) + return root + # Interative + def buildTreeInter(self, preorder, inorder): + if len(preorder) == 0: + return None + + head = TreeNode(preorder[0]) + stack = [head] + preInd, inInd = 1, 0 + + while preInd < len(preorder): + temp = None + node = TreeNode(preorder[preInd]) + while stack and stack[-1].val == inorder[inInd]: + temp = stack.pop() + inInd += 1 + if temp: + temp.right = node + else: + stack[-1].left = node + stack.append(node) + preInd += 1 + + return head \ No newline at end of file diff --git a/leetcode/106. Construct Binary Tree from Inorder and Postorder Traversal.py b/leetcode/106. Construct Binary Tree from Inorder and Postorder Traversal.py new file mode 100644 index 0000000..c824812 --- /dev/null +++ b/leetcode/106. Construct Binary Tree from Inorder and Postorder Traversal.py @@ -0,0 +1,41 @@ +''' +Given inorder and postorder traversal of a tree, construct the binary tree. +''' + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def buildTree(self, inorder, postorder): + if not inorder or not postorder: + return None + + root = TreeNode(postorder.pop()) + ind = inorder.index(root.val) + + root.right = self.buildTree(inorder[ind + 1:], postorder) + root.left = self.buildTree(inorder[:ind], postorder) + return root + + # method2, faster!! + def buildTree2(self, inorder, postorder): + self.postInd = len(postorder) - 1 + ind = {val: ind for ind, val in enumerate(inorder)} + head = self.build(0, len(postorder) - 1, inorder, postorder, ind) + return head + + def build(self, start, end, inorder, postorder, ind): + if start <= end: + mid = ind[postorder[self.postInd]] + self.postInd -= 1 + root = TreeNode(inorder[mid]) + root.right = self.build(mid + 1, end, inorder, postorder, ind) + root.left = self.build(start, mid - 1, inorder, postorder, ind) + return root + +s = Solution() +print(s.buildTree2([1,2,3,4],[2,4,3,1])) \ No newline at end of file diff --git a/leetcode/112. Path Sum.py b/leetcode/112. Path Sum.py new file mode 100644 index 0000000..d38f7b5 --- /dev/null +++ b/leetcode/112. Path Sum.py @@ -0,0 +1,28 @@ +''' +Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. + +For example: +Given the below binary tree and sum = 22, + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ \ + 7 2 1 +return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22. +''' +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def hasPathSum(self, root, sum): + if not root: + return False + if root.val == sum and root.left is None and root.right is None: + return True + return self.hasPathSum(root.left, sum-root.val) or self.hasPathSum(root.right, sum-root.val) \ No newline at end of file diff --git a/leetcode/113. Path Sum II.py b/leetcode/113. Path Sum II.py new file mode 100644 index 0000000..9189230 --- /dev/null +++ b/leetcode/113. Path Sum II.py @@ -0,0 +1,46 @@ +''' +Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. + +For example: +Given the below binary tree and sum = 22, + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ / \ + 7 2 5 1 +return +[ + [5,4,11,2], + [5,8,4,5] +] +''' + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def pathSum(self, root, sum): + if not root: + return [] + if root.left == None and root.right == None: + if sum == root.val: + return [[root.val]] + else: + return [] + stack = [] + leftStack = self.pathSum(root.left, sum - root.val) + for i in leftStack: + i.insert(0, root.val) + stack.append(i) + rightStack = self.pathSum(root.right, sum - root.val) + for i in rightStack: + i.insert(0, root.val) + stack.append(i) + return stack diff --git a/leetcode/13. Roman to Integer.py b/leetcode/13. Roman to Integer.py new file mode 100644 index 0000000..224c0de --- /dev/null +++ b/leetcode/13. Roman to Integer.py @@ -0,0 +1,22 @@ +# -*- coding:utf-8 -*- +''' +Given a roman numeral, convert it to an integer. + +Input is guaranteed to be within the range from 1 to 3999. + +''' + +class Solution(object): + def romanToInt(self, s): + romanDict = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1} + res, p = 0, 'I' + for ch in s[::-1]: + if romanDict[ch] < romanDict[p]: + res = res - romanDict[ch] + else: + res = res + romanDict[ch] + p = ch + return res + +s = Solution() +print((s.romanToInt("IX"))) \ No newline at end of file diff --git a/leetcode/143. Reorder List.py b/leetcode/143. Reorder List.py new file mode 100644 index 0000000..838b15c --- /dev/null +++ b/leetcode/143. Reorder List.py @@ -0,0 +1,57 @@ +''' +Given a singly linked list L: L0→L1→…→Ln-1→Ln, +reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… + +You must do this in-place without altering the nodes' values. + +For example, +Given {1,2,3,4}, reorder it to {1,4,2,3}. +''' + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def reorderList(self, head): + if not head or not head.next: + return + ahead, behind = self.split(head) + behind = self.reverse(behind) + head = self.reConnect(ahead, behind) + # split the linkedlist in middle + def split(self, head): + fast = head + slow = head + while fast and fast.next: + slow = slow.next + fast = fast.next + fast = fast.next + middle = slow.next + slow.next = None + return head, middle + # reverse the behind half linkedlist + def reverse(self, head): + reHead = None + curNode = head + while curNode: + nextNode = curNode.next + curNode.next = reHead + reHead = curNode + curNode = nextNode + return reHead + # merge the two linkedlist to one + def reConnect(self, first, second): + head = first + tail = first + first = first.next + while second: + tail.next = second + tail = tail.next + second = second.next + if first: + first, second = second, first + return head \ No newline at end of file diff --git a/leetcode/19. Remove Nth Node From End of List.py b/leetcode/19. Remove Nth Node From End of List.py new file mode 100644 index 0000000..18b4249 --- /dev/null +++ b/leetcode/19. Remove Nth Node From End of List.py @@ -0,0 +1,28 @@ +''' +Given a linked list, remove the nth node from the end of list and return its head. +For example, + Given linked list: 1->2->3->4->5, and n = 2. + After removing the second node from the end, the linked list becomes 1->2->3->5. +''' +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def removeNthFromEnd(self, head, n): + if not head and n <= 0: + return None + pNode = ListNode(0) + pNode.next = head + first, second = pNode, pNode + for i in range(n): + if first.next: + first = first.next + else: + return None + while first.next: + first = first.next + second = second.next + second.next = second.next.next + return pNode.next \ No newline at end of file diff --git a/leetcode/192.WordFrequency.sh b/leetcode/192.WordFrequency.sh new file mode 100644 index 0000000..a1fd834 --- /dev/null +++ b/leetcode/192.WordFrequency.sh @@ -0,0 +1,27 @@ +#!/bin/bash +:< 4 -> 3) + (5 -> 6 -> 4) +Output: 7 -> 0 -> 8 +''' +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def addTwoNumbers(self, l1, l2): + pNode = ListNode(0) + pHead = pNode + val = 0 + while l1 or l2 or val: + if l1: + val += l1.val + l1 = l1.next + if l2: + val += l2.val + l2 = l2.next + pNode.next = ListNode(val % 10) + val /= 10 + pNode = pNode.next + return pHead.next diff --git a/leetcode/203. Remove Linked List Elements.py b/leetcode/203. Remove Linked List Elements.py new file mode 100644 index 0000000..0bbc214 --- /dev/null +++ b/leetcode/203. Remove Linked List Elements.py @@ -0,0 +1,26 @@ +''' +Remove all elements from a linked list of integers that have value val. +Example +Given: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6 +Return: 1 --> 2 --> 3 --> 4 --> 5 +''' +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + + def removeElements(self, head, val): + preNode, res = None, head + while head: + if head.val == val: + if not preNode: + res = res.next + else: + preNode.next = head.next + else: + preNode = head + head = head.next + return res \ No newline at end of file diff --git a/leetcode/226. Invert Binary Tree.py b/leetcode/226. Invert Binary Tree.py new file mode 100644 index 0000000..00253d4 --- /dev/null +++ b/leetcode/226. Invert Binary Tree.py @@ -0,0 +1,46 @@ +''' +Invert a binary tree. + + 4 + / \ + 2 7 + / \ / \ +1 3 6 9 +to + 4 + / \ + 7 2 + / \ / \ +9 6 3 1 +''' + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def invertTree(self, root): + if not root: + return + if not root.left and not root.right: + return root + root.left, root.right = self.invertTree(root.right), self.invertTree(root.left) + return root + + def invertTree2(self, root): + if root: + root.left, root.right = self.invertTree(root.right), self.invertTree(root.left) + return root + # interative + def invertTreeInter(self, root): + stack = [root] + while stack: + node = stack.pop() + if node: + node.left, node.right = node.right, node.left + stack += node.left, node.right + return root diff --git a/leetcode/230. Kth Smallest Element in a BST.py b/leetcode/230. Kth Smallest Element in a BST.py new file mode 100644 index 0000000..7dd793e --- /dev/null +++ b/leetcode/230. Kth Smallest Element in a BST.py @@ -0,0 +1,27 @@ +''' +Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. +''' +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def kthSmallest(self, pRoot, k): + if k <= 0 or not pRoot: + return None + treeStack, nodesQue = [], [] + pNode = pRoot + while pNode or len(treeStack): + while pNode: + treeStack.append(pNode) + pNode = pNode.left + if len(treeStack): + pNode = treeStack.pop() + nodesQue.append(pNode) + pNode = pNode.right + if k > len(nodesQue): + return None + return nodesQue[k-1].val \ No newline at end of file diff --git a/leetcode/239. Sliding Window Maximum.py b/leetcode/239. Sliding Window Maximum.py new file mode 100644 index 0000000..448511c --- /dev/null +++ b/leetcode/239. Sliding Window Maximum.py @@ -0,0 +1,39 @@ +''' +Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. + +For example, +Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. + +Window position Max +--------------- ----- +[1 3 -1] -3 5 3 6 7 3 + 1 [3 -1 -3] 5 3 6 7 3 + 1 3 [-1 -3 5] 3 6 7 5 + 1 3 -1 [-3 5 3] 6 7 5 + 1 3 -1 -3 [5 3 6] 7 6 + 1 3 -1 -3 5 [3 6 7] 7 +Therefore, return the max sliding window as [3,3,5,5,6,7]. +''' +class Solution(object): + def maxSlidingWindow(self, nums, k): + if not nums or k <= 0: + return [] + ''' + if you want to modify time complexity, you can write below: + from collections import deque + res, queue = [], deque() + and replace queue.pop(0) with queue.popleft() + ''' + res, queue = [], [] + for ind, val in enumerate(nums): + if queue and queue[0] <= ind - k: + queue.pop(0) + while queue and nums[queue[-1]] < val: + queue.pop() + queue.append(ind) + if ind + 1 >= k: + res.append(nums[queue[0]]) + return res + +s = Solution() +print(s.maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)) \ No newline at end of file diff --git a/leetcode/24. Swap Nodes in Pairs.py b/leetcode/24. Swap Nodes in Pairs.py new file mode 100644 index 0000000..cf49b66 --- /dev/null +++ b/leetcode/24. Swap Nodes in Pairs.py @@ -0,0 +1,43 @@ +''' +Given a linked list, swap every two adjacent nodes and return its head. + +For example, +Given 1->2->3->4, you should return the list as 2->1->4->3. + +Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed. +''' + +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def swapPairs(self, head): + if head == None or head.next == None: + return head + cur = head + head = head.next + while cur and cur.next: + pNext = cur.next.next + cur.next.next = cur + if pNext: + if pNext.next: + cur.next = pNext.next + else: + cur.next = pNext + else: + cur.next = None + cur = pNext + return head + # recursion + def swapPairs2(self, head): + if not head or not head.next: + return head + + first, second = head, head.next + third = second.next + head = second + second.next = first + first.next = self.swapPairs(third) + return head \ No newline at end of file diff --git a/leetcode/297. Serialize and Deserialize Binary Tree.py b/leetcode/297. Serialize and Deserialize Binary Tree.py new file mode 100644 index 0000000..a2630db --- /dev/null +++ b/leetcode/297. Serialize and Deserialize Binary Tree.py @@ -0,0 +1,50 @@ +''' +Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. + +Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure. + +For example, you may serialize the following tree + + 1 + / \ + 2 3 + / \ + 4 5 +as "[1,2,3,null,null,4,5]" +''' +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Codec: + def serialize(self, root): + if not root: + return 'None' + stack, seriStr = [], "" + while root or stack: + while root: + seriStr += str(root.val) + ',' + stack.append(root) + root = root.left + seriStr += 'None,' + root = stack.pop() + root = root.right + seriStr = seriStr[:-1] + return seriStr + + def deserialize(self, data): + serialize = data.split(',') + tree, sp = self.bulidTree(serialize, 0) + return tree + + def bulidTree(self, s, sp): + if sp >= len(s) or s[sp] == 'None': + return None, sp + 1 + node = TreeNode(int(s[sp])) + sp += 1 + node.left, sp = self.bulidTree(s, sp) + node.right, sp = self.bulidTree(s, sp) + return node, sp \ No newline at end of file diff --git a/leetcode/322. Coin Change b/leetcode/322. Coin Change new file mode 100644 index 0000000..f0eb84e --- /dev/null +++ b/leetcode/322. Coin Change @@ -0,0 +1,41 @@ +''' +You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1. + +Example 1: +coins = [1, 2, 5], amount = 11 +return 3 (11 = 5 + 5 + 1) + +Example 2: +coins = [2], amount = 3 +return -1. + +Note: +You may assume that you have an infinite number of each kind of coin. +''' +class Solution(object): + def coinChange(self, coins, amount): + #corner cases + if amount == 0: + return 0 + if len(coins) == 1 and coins[0] > amount: + return -1 + dp = [-1 for i in range(amount + 1)] + for i in range(1, amount + 1): + # if the value matches the coin + if i in coins: + dp[i] = 1 + else: + minV = sys.maxsize + # since the size of coins are much less than the amount, + # we check if for every coin there could be a solution and find the minimum of that + for j in coins: + remain = i - j + # -1 means there is no solution, so we don't need to check if dp[i] is -1 + if remain > 0 and dp[remain] != -1: + minV = min(minV, dp[remain]) + if minV ==sys.maxsize: + dp[i] = -1 + else: + dp[i] = minV + 1 + return dp[-1] + diff --git a/leetcode/337. House Robber III b/leetcode/337. House Robber III new file mode 100644 index 0000000..c5cd083 --- /dev/null +++ b/leetcode/337. House Robber III @@ -0,0 +1,44 @@ +''' +The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night. + +Determine the maximum amount of money the thief can rob tonight without alerting the police. + +Example 1: + 3 + / \ + 2 3 + \ \ + 3 1 +Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. +Example 2: + 3 + / \ + 4 5 + / \ \ + 1 3 1 +Maximum amount of money the thief can rob = 4 + 5 = 9. +''' +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def rob(self, root): + def gain(root): + if root == None: + return 0 + o1 = root.val + if o1 < 0: + return -o1 + if root.left: + o1 += gain(root.left.left) + gain(root.left.right) + if root.right: + o1 += gain(root.right.left) + gain(root.right.right) + o2 = gain(root.left) + gain(root.right) + o = max(o1,o2) + root.val = -1*o + return o + return gain(root) diff --git a/leetcode/4. Median of Two Sorted Arrays b/leetcode/4. Median of Two Sorted Arrays new file mode 100644 index 0000000..0c74b39 --- /dev/null +++ b/leetcode/4. Median of Two Sorted Arrays @@ -0,0 +1,49 @@ +''' +There are two sorted arrays nums1 and nums2 of size m and n respectively. + +Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). + +Example 1: +nums1 = [1, 3] +nums2 = [2] + +The median is 2.0 +Example 2: +nums1 = [1, 2] +nums2 = [3, 4] + +The median is (2 + 3)/2 = 2.5 +''' +class Solution(object): + def findMedianSortedArrays(self, a, b): + n = len(a)+len(b) + if n&1: + return self.kthSmallest(a,b,n//2+1) + else: + return (self.kthSmallest(a,b,n//2+1) + self.kthSmallest(a,b,n//2))/2.0 + + def kthSmallest(self,a,b,k): + if len(a)+len(b) < k: + return None + i=0 + j=0 + flag = True + while k>0: + if i >= len(a): + j+=1 + flag = False + elif j >= len(b): + i+=1 + flag = True + elif a[i] <= b[j]: + i+=1 + flag = True + elif a[i] > b[j]: + j+=1 + flag = False + k-=1 + + if flag: + return a[i-1] + else: + return b[j-1] diff --git a/leetcode/61. Rotate List b/leetcode/61. Rotate List new file mode 100644 index 0000000..55e70a2 --- /dev/null +++ b/leetcode/61. Rotate List @@ -0,0 +1,40 @@ +''' +Given a list, rotate the list to the right by k places, where k is non-negative. + +For example: +Given 1->2->3->4->5->NULL and k = 2, +return 4->5->1->2->3->NULL. +''' +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def rotateRight(self, head, k): + ref = head + length = 0 + + while head: + head = head.next + length += 1 + + if length < 2 or k % length == 0: + return ref + + prev = None + current = ref + for _ in xrange(length - (k % length)): + prev, current = current, current.next + + prev.next = None + new_head = current + + if not current: + return ref + while current.next: + current = current.next + current.next = ref + + return new_head diff --git a/leetcode/7. Reverse Integer QuestionEditorial Solution b/leetcode/7. Reverse Integer QuestionEditorial Solution new file mode 100644 index 0000000..acc9d2d --- /dev/null +++ b/leetcode/7. Reverse Integer QuestionEditorial Solution @@ -0,0 +1,35 @@ +''' +Reverse digits of an integer. + +Example1: x = 123, return 321 +Example2: x = -123, return -321 + +''' + +class Solution(object): + def reverse(self, x): + res = int(str(abs(x))[::-1]) + if x < 0: + res = -res + if res > 2147483647 or res < -2147483648: + res = 0 + return res + # without str() + def reverse2(self, x): from math import log + if x < 10 and x > -10: + return x + flipped = False + if x < 0: + flipped = True + x *= -1 + res = 0 + log10 = int(log(x, 10)) + for i in xrange(log10 + 1): + digit = x % 10 + res += digit * 10**(log10 - i) + x /= 10 + if res > 2**31 - 1 or res < -1 * 2**31 + 1: + return 0 + if flipped: + res *= -1 + return res diff --git a/leetcode/82. Remove Duplicates from Sorted List II.py b/leetcode/82. Remove Duplicates from Sorted List II.py new file mode 100644 index 0000000..51dc02e --- /dev/null +++ b/leetcode/82. Remove Duplicates from Sorted List II.py @@ -0,0 +1,26 @@ +''' +Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. + +For example, +Given 1->2->3->3->4->4->5, return 1->2->5. +Given 1->1->1->2->3, return 2->3. +''' +# -*- coding:utf-8 -*- +class ListNode: + def __init__(self, x): + self.val = x + self.next = None +class Solution: + def deleteDuplication(self, head): + dummy = pre = ListNode(0) + dummy.next = head + while head and head.next: + if head.val == head.next.val: + while head and head.next and head.val == head.next.val: + head = head.next + head = head.next + pre.next = head + else: + pre = pre.next + head = head.next + return dummy.next \ No newline at end of file diff --git a/leetcode/83. Remove Duplicates from Sorted List.py b/leetcode/83. Remove Duplicates from Sorted List.py new file mode 100644 index 0000000..217ab04 --- /dev/null +++ b/leetcode/83. Remove Duplicates from Sorted List.py @@ -0,0 +1,22 @@ +''' +Given a sorted linked list, delete all duplicates such that each element appear only once. + +For example, +Given 1->1->2, return 1->2. +Given 1->1->2->3->3, return 1->2->3. +''' + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def deleteDuplicates(self, head): + pNode = head + while pNode: + while pNode.next and pNode.next.val == pNode.val: + pNode.next = pNode.next.next + pNode = pNode.next + return head \ No newline at end of file diff --git a/leetcode/words.txt b/leetcode/words.txt new file mode 100644 index 0000000..7afcc27 --- /dev/null +++ b/leetcode/words.txt @@ -0,0 +1,2 @@ +the day is sunny +the the the sunny is is \ No newline at end of file diff --git a/mysingleton.py b/mysingleton.py new file mode 100644 index 0000000..1100d8b --- /dev/null +++ b/mysingleton.py @@ -0,0 +1,5 @@ +# 单例模式 +class My_Singleton(object): + def foo(self): + pass +my_singleton = My_Singleton() \ No newline at end of file diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204.md" index 17f567d..da633be 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -1,11 +1,12 @@ -[TOC] # 数据结构 ## 一些概念 -> 数据结构就是研究数据的**逻辑结构**和**物理结构**以及它们之间**相互关系**,并对这种结构定义相应的运算,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。 1. 数据:所有能被输入到计算机中,且能被计算机处理的符号的集合。是计算机操作的对象的总称。 +> 数据结构就是研究数据的**逻辑结构**和**物理结构**以及它们之间**相互关系**,并对这种结构定义相应的运算,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。 + +1. 数据:所有能被输入到计算机中,且能被计算机处理的符号的集合。是计算机操作的对象的总称。 2. 数据元素:数据(集合)中的一个“个体”,数据及结构中讨论的**基本**单位 3. 数据项:数据的不可分割的最小单位。一个数据元素可由若干个数据项组成。 4. 数据类型:在一种程序设计语言中,变量所具有的数据种类。整型、浮点型、字符型等等 - + 1. 逻辑结构:数据之间的相互关系。 * 集合 结构中的数据元素除了同属于一种类型外,别无其它关系。 * 线性结构 数据元素之间一对一的关系 @@ -43,7 +44,9 @@ * 双向链表:在单链表的每个结点里再增加一个指向其直接前趋的指针域prior。这样就形成的链表中有两个方向不同的链。双链表一般由头指针唯一确定的,将头结点和尾结点链接起来构成循环链表,并称之为双向链表。设指针p指向某一结点,则双向链表结构的对称性可用下式描述:p—>prior—>next=p=p—>next—>prior。从两个方向搜索双链表,比从一个方向搜索双链表的方差要小。 * 插入:先搞定插入节点的前驱和后继,再搞定后结点的前驱,最后搞定前结点的后继。 * 在有序双向链表中定位删除一个元素的平均时间复杂度为O(n) - * 可以直接删除当前指针所指向的节点。而不需要像单向链表中,删除一个元素必须找到其前驱。因此在插入数据时,单向链表和双向链表操作复杂度相同,而删除数据时,双向链表的性能优于单向链表 ## 栈和队列 + * 可以直接删除当前指针所指向的节点。而不需要像单向链表中,删除一个元素必须找到其前驱。因此在插入数据时,单向链表和双向链表操作复杂度相同,而删除数据时,双向链表的性能优于单向链表 + +## 栈和队列 ### 栈 栈(Stack)是限制在表的一端进行插入和删除运算的线性表,通常称插入、删除的这一端为栈顶(Top),另一端为栈底(Bottom)。先进后出。top= -1时为空栈,top=0只能说明栈中只有一个元素,并且元素进栈时top应该自增 @@ -56,7 +59,8 @@ 应用,[代码](https://github.com/Jack-Lee-Hiter/AlgorithmsByPython/blob/master/Stack.py): -1. 进制转换 2. 括号匹配的检验 +1. 进制转换 +2. 括号匹配的检验 3. 行编辑程序 4. 迷宫求解:若当前位置“可通”,则纳入路径,继续前进;若当前位置“不可通”,则后退,换方向继续探索;若四周“均无通路”,则将当前位置从路径中删除出去。 5. 表达式求解:前缀、中缀、后缀。 @@ -105,7 +109,9 @@ ### 数组 数组的顺序存储:行优先顺序;列优先顺序。数组中的任一元素可以在相同的时间内存取,即顺序存储的数组是一个随机存取结构。 -关联数组(Associative Array),又称映射(Map)、字典( Dictionary)是一个抽象的数据结构,它包含着类似于(键,值)的有序对。 不是线性表。 矩阵的压缩: +关联数组(Associative Array),又称映射(Map)、字典( Dictionary)是一个抽象的数据结构,它包含着类似于(键,值)的有序对。 不是线性表。 + +矩阵的压缩: 1. 对称矩阵、三角矩阵:直接存储矩阵的上三角或者下三角元素。**注意区分i>=j和i=0,所以可以为空表。广义表的**同级**元素(直属于同一个表中的各元素)具有**线性**关系 2. 广义表的表头为空,并不代表该广义表为空表。广义表()和(())不同。前者是长度为0的空表,对其不能做求表头和表尾的运算;而后者是长度为l的非空表(只不过该表中惟一的一个元素是空表),对其可进行分解,得到的表头和表尾均是空表() @@ -140,7 +151,20 @@ 基本术语: -1. 树结点:包含一个数据元素及若干指向子树的分支; 2. 孩子结点:结点的子树的根称为该结点的孩子; 3. 双亲结点:B结点是A结点的孩子,则A结点是B结点的双亲; 4. 兄弟结点:同一双亲的孩子结点; 5. 堂兄结点:同一层上结点; 6. 结点层次:根结点的层定义为1;根的孩子为第二层结点,依此类推; 7. 树的高(深)度:树中最大的结点层 8. 结点的度:结点子树的个数 9. 树的度: 树中最大的结点度。 10. 叶子结点:也叫终端结点,是度为0的结点; 11. 分枝结点:度不为0的结点(非终端结点); 12. 森林:互不相交的树集合; 13. 有序树:子树有序的树,如:家族树; 14. 无序树:不考虑子树的顺序; +1. 树结点:包含一个数据元素及若干指向子树的分支; +2. 孩子结点:结点的子树的根称为该结点的孩子; +3. 双亲结点:B结点是A结点的孩子,则A结点是B结点的双亲; +4. 兄弟结点:同一双亲的孩子结点; +5. 堂兄结点:同一层上结点; +6. 结点层次:根结点的层定义为1;根的孩子为第二层结点,依此类推; +7. 树的高(深)度:树中最大的结点层 +8. 结点的度:结点子树的个数 +9. 树的度: 树中最大的结点度。 +10. 叶子结点:也叫终端结点,是度为0的结点; +11. 分枝结点:度不为0的结点(非终端结点); +12. 森林:互不相交的树集合; +13. 有序树:子树有序的树,如:家族树; +14. 无序树:不考虑子树的顺序; ### 二叉树 二叉树可以为空。二叉树结点的子树要区分左子树和右子树,即使只有一棵子树也要进行区分,说明它是左子树,还是右子树。这是二叉树与树的最主要的差别。注意区分:二叉树、**二叉查找树/二叉排序树/二叉搜索树**、**二叉平衡(查找)树** @@ -179,24 +203,35 @@ 线索二叉树:对二叉树所有结点做某种处理可在遍历过程中实现;检索(查找)二叉树某个结点,可通过遍历实现;如果能将二叉树线索化,就可以简化遍历算法,提高遍历速度,目的是加快查找结点的前驱或后继的速度。 -如何线索化?以中序遍历为例,若能将中序序列中每个结点前趋、后继信息保存起来,以后再遍历二叉树时就可以根据所保存的结点前趋、后继信息对二叉树进行遍历。对于二叉树的线索化,实质上就是遍历一次二叉树,只是在遍历的过程中,检查当前结点左,右指针域是否为空,若为空,将它们改为指向前驱结点或后继结点的线索。**前驱就是在这一点之前走过的点,不是下一将要去往的点**。 加上结点前趋后继信息(结索)的二叉树称为**线索二叉树**。n个结点的线索二叉树上每个结点有2个指针域(指向左孩子和右孩子),总共有2n个指针域;一个n个结点的树有n-1条边,那么空指针域= 2n - (n-1) = n + 1,即线索数为n+1。指针域tag为0,存放孩子指针,为1,存放前驱/后继节点指针。 +如何线索化?以中序遍历为例,若能将中序序列中每个结点前趋、后继信息保存起来,以后再遍历二叉树时就可以根据所保存的结点前趋、后继信息对二叉树进行遍历。对于二叉树的线索化,实质上就是遍历一次二叉树,只是在遍历的过程中,检查当前结点左,右指针域是否为空,若为空,将它们改为指向前驱结点或后继结点的线索。**前驱就是在这一点之前走过的点,不是下一将要去往的点**。 + +加上结点前趋后继信息(结索)的二叉树称为**线索二叉树**。n个结点的线索二叉树上每个结点有2个指针域(指向左孩子和右孩子),总共有2n个指针域;一个n个结点的树有n-1条边,那么空指针域= 2n - (n-1) = n + 1,即线索数为n+1。指针域tag为0,存放孩子指针,为1,存放前驱/后继节点指针。 线索树下结点x的前驱与后继查找:设结点x相应的左(右)标志是线索标志,则lchild(rchild)就是前驱(后继),否则: * LDR--前驱:左子树中最靠右边的结点;后继:右子树中最靠左边的结点 * LRD--前驱:右子树的根,若无右子树,为左子树跟。后继:x是根,后继是空;x是双亲的右孩子、x是双亲的左孩子,但双亲无右孩子,双亲是后继;x是双亲的左孩子,双亲有右孩子,双亲右子树中最左的叶子是后继 * DLR--对称于LRD线索树---将LRD中所有左右互换,前驱与后继互换,得到DLR的方法。 -* 为简化线索链表的遍历算法,仿照线性链表,为线索链表加上一头结点,约定: * 头结点的lchild域:存放线索链表的根结点指针; * 头结点的rchild域: 中序序列最后一个结点的指针; * 中序序列第一结点lchild域指向头结点; * 中序序列最后一个结点的rchild域指向头结点; +* 为简化线索链表的遍历算法,仿照线性链表,为线索链表加上一头结点,约定: + * 头结点的lchild域:存放线索链表的根结点指针; + * 头结点的rchild域: 中序序列最后一个结点的指针; + * 中序序列第一结点lchild域指向头结点; + * 中序序列最后一个结点的rchild域指向头结点; 中序遍历的线索二叉树以及线索二叉树链表示意图 ![xiansuobinarytree](http://images.cnitblog.com/blog/311549/201309/13230006-d365a5866c094ee7b3897a1675d34716.jpg) 一棵左右子树均不空的二叉树在前序线索化后,其中空的链域的个数是1。**前序和后续线索化后空链域个数都是1,中序是2**。二叉树在线索化后,仍不能有效求解的问题是前序求前序先驱,后序求后序后继。 -中序遍历的顺序为:左、根、右,所以对于每一非空的线索,左子树结点的后继为根结点,右子树结点的前驱为根结点,再递归的执行上面的过程,可得非空线索均指向其祖先结点。**在中序线索二叉树中,每一非空的线索均指向其祖先结点**。 在二叉树上加上结点前趋、后继线索后,可利用线索对二叉树进行遍历,此时,**不需栈,也不需递归**。基本步骤: +中序遍历的顺序为:左、根、右,所以对于每一非空的线索,左子树结点的后继为根结点,右子树结点的前驱为根结点,再递归的执行上面的过程,可得非空线索均指向其祖先结点。**在中序线索二叉树中,每一非空的线索均指向其祖先结点**。 + +在二叉树上加上结点前趋、后继线索后,可利用线索对二叉树进行遍历,此时,**不需栈,也不需递归**。基本步骤: 1. p=T->lchild; p指向线索链表的根结点; -2. 若线索链表非空,循环: * 循环,顺着p左孩子指针找到最左下结点;访问之; * 若p所指结点的右孩子域为线索,p的右孩子结点即为后继结点循环: p=p->rchild; 并访问p所指结点;(在此循环中,顺着后继线索访问二叉树中的结点) * 一旦线索“中断”,p所指结点的右孩子域为右孩子指针,p=p->rchild,使 p指向右孩子结点; +2. 若线索链表非空,循环: + * 循环,顺着p左孩子指针找到最左下结点;访问之; + * 若p所指结点的右孩子域为线索,p的右孩子结点即为后继结点循环: p=p->rchild; 并访问p所指结点;(在此循环中,顺着后继线索访问二叉树中的结点) + * 一旦线索“中断”,p所指结点的右孩子域为右孩子指针,p=p->rchild,使 p指向右孩子结点; ### 树和森林 树的存储结构: @@ -208,16 +243,30 @@ 将树转化成二叉树:右子树一定为空 -1. 加线:在兄弟之间加一连线 2. 抹线:对每个结点,除了其左孩子外,去除其与其余孩子之间的关系 3. 旋转:以树的根结点为轴心,将整树顺时针转45° +1. 加线:在兄弟之间加一连线 +2. 抹线:对每个结点,除了其左孩子外,去除其与其余孩子之间的关系 +3. 旋转:以树的根结点为轴心,将整树顺时针转45° 森林转换成二叉树: -1. 将各棵树分别转换成二叉树 2. 将每棵树的根结点用线相连 3. 以第一棵树根结点为二叉树的根 +1. 将各棵树分别转换成二叉树 +2. 将每棵树的根结点用线相连 +3. 以第一棵树根结点为二叉树的根 + +树与转换后的二叉树的关系:转换后的二叉树的先序对应树的先序遍历;转换后的二叉树的中序对应树的后序遍历 -树与转换后的二叉树的关系:转换后的二叉树的先序对应树的先序遍历;转换后的二叉树的中序对应树的后序遍历 ### 哈弗曼树/霍夫曼树 +### 哈弗曼树/霍夫曼树 一些概念 -1. 路径:从一个祖先结点到子孙结点之间的分支构成这两个结点间的路径; 2. 路径长度:路径上的分支数目称为路径长度; 3. 树的路径长度:从根到每个结点的路径长度之和。 4. 结点的权:根据应用的需要可以给树的结点赋权值; 5. 结点的带权路径长度:从根到该结点的路径长度与该结点权的乘积; 6. 树的带权路径长度=树中所有叶子结点的带权路径之和;通常记作 WPL=∑wi×li 7. 哈夫曼树:假设有n个权值(w1, w2, … , wn),构造有n个叶子结点的二叉树,每个叶子结点有一个 wi作为它的权值。则带权路径长度最小的二叉树称为哈夫曼树。最优二叉树。 前缀码的定义:在一个字符集中,任何一个字符的编码都不是另一个字符编码的前缀。霍夫曼编码就是前缀码,可用于快速判断霍夫曼编码是否正确。霍夫曼树是满二叉树,若有n个节点,则共有(n+1)/2个码子 +1. 路径:从一个祖先结点到子孙结点之间的分支构成这两个结点间的路径; +2. 路径长度:路径上的分支数目称为路径长度; +3. 树的路径长度:从根到每个结点的路径长度之和。 +4. 结点的权:根据应用的需要可以给树的结点赋权值; +5. 结点的带权路径长度:从根到该结点的路径长度与该结点权的乘积; +6. 树的带权路径长度=树中所有叶子结点的带权路径之和;通常记作 WPL=∑wi×li +7. 哈夫曼树:假设有n个权值(w1, w2, … , wn),构造有n个叶子结点的二叉树,每个叶子结点有一个 wi作为它的权值。则带权路径长度最小的二叉树称为哈夫曼树。最优二叉树。 + +前缀码的定义:在一个字符集中,任何一个字符的编码都不是另一个字符编码的前缀。霍夫曼编码就是前缀码,可用于快速判断霍夫曼编码是否正确。霍夫曼树是满二叉树,若有n个节点,则共有(n+1)/2个码子 给定n个权值作为n的叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为霍夫曼树(Huffman Tree)。霍夫曼树是带权路径长度最短的树,权值较大的结点离根较近。 @@ -231,29 +280,45 @@ 图搜索->形成搜索树 1. 穷举法。 -2. 贪心法。多步决策,每步选择使得构成一个问题的可能解,同时满足目标函数。 3. 回溯法。根据题意,选取度量标准,然后将可能的选择方法按度量标准所要求顺序排好,每次处理一个量,得到该意义下的最优解的分解处理。 +2. 贪心法。多步决策,每步选择使得构成一个问题的可能解,同时满足目标函数。 +3. 回溯法。根据题意,选取度量标准,然后将可能的选择方法按度量标准所要求顺序排好,每次处理一个量,得到该意义下的最优解的分解处理。 ## 图 无向图 1. 回路或环:第一个顶点和最后一个顶点相同的路径。 2. 简单回路或简单环:除第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路 -3. 连通:顶点v至v’ 之间有路径存在 4. 连通图:无向图图 G 的任意两点之间都是连通的,则称G是连通图。 5. 连通分量:极大连通子图,子图中包含的顶点个数极大 -6. 所有顶点度的和必须为偶数 有向图: +3. 连通:顶点v至v’ 之间有路径存在 +4. 连通图:无向图图 G 的任意两点之间都是连通的,则称G是连通图。 +5. 连通分量:极大连通子图,子图中包含的顶点个数极大 +6. 所有顶点度的和必须为偶数 -1. 回路或环:第一个顶点和最后一个顶点相同的路径。 2. 简单回路或简单环:除第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路。 3. 连通:顶点v至v’之间有路径存在 4. 强连通图:有向图G的任意两点之间都是连通的,则称G是强连通图。各个顶点间均可达。 5. 强连通分量:极大连通子图 -6. 有向图顶点的度是顶点的入度与出度之和。邻接矩阵中第V行中的1的个数是V的出度 1. 生成树:极小连通子图。包含图的所有n个结点,但只含图的n-1条边。在生成树中添加一条边之后,必定会形成回路或环。 -2. 完全图:有 n(n-1)/2 条边的无向图。其中n是结点个数。必定是连通图。 3. 有向完全图:有n(n-1)条边的有向图。其中n是结点个数。每两个顶点之间都有两条方向相反的边连接的图。 +有向图: + +1. 回路或环:第一个顶点和最后一个顶点相同的路径。 +2. 简单回路或简单环:除第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路。 +3. 连通:顶点v至v’之间有路径存在 +4. 强连通图:有向图G的任意两点之间都是连通的,则称G是强连通图。各个顶点间均可达。 +5. 强连通分量:极大连通子图 +6. 有向图顶点的度是顶点的入度与出度之和。邻接矩阵中第V行中的1的个数是V的出度 + +1. 生成树:极小连通子图。包含图的所有n个结点,但只含图的n-1条边。在生成树中添加一条边之后,必定会形成回路或环。 +2. 完全图:有 n(n-1)/2 条边的无向图。其中n是结点个数。必定是连通图。 +3. 有向完全图:有n(n-1)条边的有向图。其中n是结点个数。每两个顶点之间都有两条方向相反的边连接的图。 4. 一个无向图 G=(V,E) 是连通的,那么边的数目大于等于顶点的数目减一:|E|>=|V|-1,而反之不成立。如果 G=(V,E) 是有向图,那么它是强连通图的必要条件是边的数目大于等于顶点的数目:|E|>=|V|,而反之不成立。没有回路的无向图是连通的当且仅当它是树,即等价于:|E|=|V|-1。 ### 图的存储形式 1. 邻接矩阵和加权邻接矩阵 - * 无权有向图:出度: i行之和;入度: j列之和。 * 无权无向图:i结点的度: i行或i列之和。 - * 加权邻接矩阵:相连为w,不相连为∞ 2. 邻接表 + * 无权有向图:出度: i行之和;入度: j列之和。 + * 无权无向图:i结点的度: i行或i列之和。 + * 加权邻接矩阵:相连为w,不相连为∞ +2. 邻接表 * 用顶点数组表、边(弧)表表示该有向图或无向图 * 顶点数组表:用数组存放所有的顶点。数组大小为图顶点数n * 边表(边结点表):每条边用一个结点进行表示。同一个结点的所有的边形成它的边结点单链表。 - * n个顶点的无向图的邻接表最多有n(n-1)个边表结点。有n个顶点的无向图最多有n\*(n-1)/2条边,此时为完全无向图,而在邻接表中每条边存储两次,所以有n*(n-1)个结点 ### 图的遍历 + * n个顶点的无向图的邻接表最多有n(n-1)个边表结点。有n个顶点的无向图最多有n\*(n-1)/2条边,此时为完全无向图,而在邻接表中每条边存储两次,所以有n*(n-1)个结点 + +### 图的遍历 深度优先搜索利用栈,广度优先搜索利用队列 求一条从顶点i到顶点s的简单路径--深搜。求两个顶点之间的一条长度最短的路径--广搜。当各边上的权值均相等时,BFS算法可用来解决单源最短路径问题。 @@ -261,7 +326,9 @@ ### 生成树和最小生成树 每次遍历一个连通图将图的边分成遍历所经过的边和没有经过的边两部分,将遍历经过的边同图的顶点构成一个子图,该子图称为生成树。因此有DFS生成树和BFS生成树。 -生成树是连通图的极小子图,有n个顶点的连通图的生成树必定有n-1条边,在生成树中任意增加一条边,必定产生回路。若砍去它的一条边,就会把生成树变成非连通子图 最小生成树:生成树中边的权值(代价)之和最小的树。最小生成树问题是构造连通网的最小代价生成树。 +生成树是连通图的极小子图,有n个顶点的连通图的生成树必定有n-1条边,在生成树中任意增加一条边,必定产生回路。若砍去它的一条边,就会把生成树变成非连通子图 + +最小生成树:生成树中边的权值(代价)之和最小的树。最小生成树问题是构造连通网的最小代价生成树。 Kruskal算法:令最小生成树集合T初始状态为空,在有n个顶点的图中选取代价最小的边并从图中删去。若该边加到T中有回路则丢弃,否则留在T中;依此类推,直至T中有n-1条边为止。 @@ -272,7 +339,9 @@ Prim算法、Kruskal算法和Dijkstra算法均属于贪心算法。 3. Bellman-Ford算法解决的是一般情况下的单源最短路径问题,在这里,边的权重可以为负值。该算法返回一个布尔值,以表明是否存在一个从源节点可以到达的权重为负值的环路。如果存在这样一个环路,算法将告诉我们不存在解决方案。如果没有这种环路存在,算法将给出最短路径和它们的权重。 ### 双连通图和关节点 -若从一个连通图中删去任何一个顶点及其相关联的边,它仍为一个连通图的话,则该连通图被称为**重(双)连通图**。 若连通图中的某个顶点和其相关联的边被删去之后,该连通图被分割成两个或两个以上的连通分量,则称此顶点为**关节点**。 +若从一个连通图中删去任何一个顶点及其相关联的边,它仍为一个连通图的话,则该连通图被称为**重(双)连通图**。 + +若连通图中的某个顶点和其相关联的边被删去之后,该连通图被分割成两个或两个以上的连通分量,则称此顶点为**关节点**。 没有关节点的连通图为双连通图 @@ -284,23 +353,34 @@ Prim算法、Kruskal算法和Dijkstra算法均属于贪心算法。 AOV网(Activity On Vertex):用顶点表示活动,边表示活动的优先关系的有向图称为**AOV网**。AOV网中不允许有回路,这意味着某项活动以自己为先决条件。 -拓扑有序序列:把AOV网络中各顶点按照它们相互之间的优先关系排列一个线性序列的过程。若vi是vj前驱,则vi一定在vj之前;对于没有优先关系的点,顺序任意。 拓扑排序:对AOV网络中顶点构造拓扑有序序列的过程。方法: +拓扑有序序列:把AOV网络中各顶点按照它们相互之间的优先关系排列一个线性序列的过程。若vi是vj前驱,则vi一定在vj之前;对于没有优先关系的点,顺序任意。 + +拓扑排序:对AOV网络中顶点构造拓扑有序序列的过程。方法: -1. 在有向图中选一个没有前驱的顶点且输出之 2. 从图中删除该顶点和所有以它为尾的弧 3. 重复上述两步,直至全部顶点均已输出;或者当图中不存在无前驱的顶点为止(此时说明图中有环) +1. 在有向图中选一个没有前驱的顶点且输出之 +2. 从图中删除该顶点和所有以它为尾的弧 +3. 重复上述两步,直至全部顶点均已输出;或者当图中不存在无前驱的顶点为止(此时说明图中有环) 采用**深度优先搜索**或**拓扑排序**算法可以判断出一个有向图中是否有环(回路).深度优先搜索只要在其中记录下搜索的节点数n,当n大于图中节点数时退出,并可以得出有回路。若有回路,则拓扑排序访问不到图中所有的节点,所以也可以得出回路。~~广度优先搜索~~过程中如果访问到一个已经访问过的节点,可能是多个节点指向这个节点,不一定是存在环。 算法描述: - 1. 把邻接表中入度为0的顶点依此进栈 2. 若栈不空,则 * 栈顶元素vj退栈并输出; * 在邻接表中查找vj的直接后继vk,把vk的入度减1;若vk的入度为0则进栈 + +1. 把邻接表中入度为0的顶点依此进栈 +2. 若栈不空,则 + * 栈顶元素vj退栈并输出; + * 在邻接表中查找vj的直接后继vk,把vk的入度减1;若vk的入度为0则进栈 3. 若栈空时输出的顶点个数不是n,则有向图有环;否则,拓扑排序完毕。 -AOE网:带权的**有向无环图**,其中顶点表示事件,弧表示活动,权表示活动持续时间。在工程上常用来表示工程进度计划。 一些定义: +AOE网:带权的**有向无环图**,其中顶点表示事件,弧表示活动,权表示活动持续时间。在工程上常用来表示工程进度计划。 + +一些定义: 1. 事件的最早发生时间(ve(j)):从源点到j结点的最长的路径。意味着事件最早能够发生的时间。 2. 事件的最迟发生时间(vl(j)):不影响工程的如期完工,事件j必须发生的时间。 3. 活动ai由弧表示,持续时间记为 dut,则有: * 活动的最早开始时间:e(i)=ve(j) - * 活动的最迟开始时间:l(i)=vl(k) - dut() 4. 活动余量:l(i)-e(i)的差 + * 活动的最迟开始时间:l(i)=vl(k) - dut() +4. 活动余量:l(i)-e(i)的差 5. 关键活动:活动余量为0的活动 6. 关键路径:从源点到汇点的最长的一条路径,或者全部由关键活动构成的路径。关键活动一定位于关键路径上。 7. 关键活动组成了关键路径,关键路径是图中的最长路径,关键路径长度代表整个工期的最短完成时间,关键活动延期完成,必将导致关键路径长度增加,即整个工期的最短完成时间增加。关键路径并不唯一,当有多条关键路径存在时,其中一条关键路径上的关键活动时间缩短,只能导致本条关键路径变成非关键路径,而无法缩短整个工期,因为其他关键路径没有变化。任何一条关键路径上的关键活动变长了,都会使这条关键路径变成更长的关键路径,并且导致其他关键路径变成非关键路径(如果关键路径不唯一)。关键活动不按期完成就会影响整个工程的完成时间。所有的关键活动提前完成,那么整个工程才会提前完成。关键路径也不能任意缩短,一旦缩短到一定程度,该关键活动可能变成非关键活动了。 @@ -314,63 +394,113 @@ AOE网:带权的**有向无环图**,其中顶点表示事件,弧表示活 分块查找:将表分成几块,块内无序,块间有序,即前一块中的最大值小于后一块中的最小值。并且有一张索引表,每一项存放每一块的最大值和指向该块第一个元素的指针。索引表有序,块内无序。所以,块间查找用二分查找,块内用顺序查找,效率介于顺序和二分之间;先确定待查记录所在块,再在块内查找。因此跟表中元素个数和块中元素个数都有关。 -1. 用数组存放待查记录, 2. 建立索引表,由每块中最大(小)的关键字及所属块位置的信息组成。 -3. 当索引表较大时,可以采用二分查找 4. 在数据量极大时,索引可能很多,可考虑建立索引表的索引,即二级索引,原则上索引不超过三级 +1. 用数组存放待查记录, +2. 建立索引表,由每块中最大(小)的关键字及所属块位置的信息组成。 +3. 当索引表较大时,可以采用二分查找 +4. 在数据量极大时,索引可能很多,可考虑建立索引表的索引,即二级索引,原则上索引不超过三级 + +分块查找平均查找长度:*ASL**bs* = *L**b* + *L**w*。其中,*L**b*是查找索引表确定所在块的平均查找长度, *L**w*是在块中查找元素的平均查找长度。在n一定时,可以通过选择s使ASL尽可能小。当s=sqrt(n)时,ASL最小。 -分块查找平均查找长度:*ASL**bs* = *L**b* + *L**w*。其中,*L**b*是查找索引表确定所在块的平均查找长度, *L**w*是在块中查找元素的平均查找长度。在n一定时,可以通过选择s使ASL尽可能小。当s=sqrt(n)时,ASL最小。 1. 时间:顺序查找最差,二分最好,分块介于两者之间 2. 空间:分块最大,需要增加索引数据的空间 -3. 顺序查找对表没有特殊要求 4. 分块时数据块之间在物理上可不连续。所以可以达到插入、删除数据只涉及对应的块;另外,增加了索引的维护。 5. 二分查找要求表有序,所以若表的元素的插入与删除很频繁,维持表有序的工作量极大。 6. 在表不大时,一般直接使用顺序查找。 ## 动态查找 +1. 时间:顺序查找最差,二分最好,分块介于两者之间 +2. 空间:分块最大,需要增加索引数据的空间 +3. 顺序查找对表没有特殊要求 +4. 分块时数据块之间在物理上可不连续。所以可以达到插入、删除数据只涉及对应的块;另外,增加了索引的维护。 +5. 二分查找要求表有序,所以若表的元素的插入与删除很频繁,维持表有序的工作量极大。 +6. 在表不大时,一般直接使用顺序查找。 + +## 动态查找 二叉排序树的结点删除: -1. x为叶子结点,则直接删除 2. x只有左子树xL或只有右子树xR ,则令xL或xR直接成为双亲结点f的子树; -3. x即有左子树xL也有右子树xR,在xL中选值最大的代替x,该数据按二叉排序树的性质应在最右边。 平衡二叉树:每个结点的平衡因子都为 1、-1、0 的二叉排序树。或者说每个结点的左右子树的高度最多差1的二叉排序树。 平衡二叉树的平衡: +1. x为叶子结点,则直接删除 +2. x只有左子树xL或只有右子树xR ,则令xL或xR直接成为双亲结点f的子树; +3. x即有左子树xL也有右子树xR,在xL中选值最大的代替x,该数据按二叉排序树的性质应在最右边。 + +平衡二叉树:每个结点的平衡因子都为 1、-1、0 的二叉排序树。或者说每个结点的左右子树的高度最多差1的二叉排序树。 + +平衡二叉树的平衡: 1. 左调整(新结点插入在左子树上的调整): * LL(插入在结点左子树的左子树上):旋转前后高度都为h+1 * LR(新插入结点在左子树的右子树上):旋转前后高度仍为h+1 2. 右调整(新结点插入在右子树上进行的调整): - * RR(插入在的右子树的右子树上):处理方法和 LL对称 + * RR(插入在的右子树的右子树上):处理方法和 LL对称 * RL(插入在的右子树的左子树上):处理方法和 LR对称 平衡树建立方法: -1. 按二叉排序树插入结点 2. 如引起结点平衡因子变为|2|,则确定旋转点,该点是离根最远(或最接近于叶子的点) 3. 确定平衡类型后进行平衡处理,平衡后以平衡点为根的子树高不变 +1. 按二叉排序树插入结点 +2. 如引起结点平衡因子变为|2|,则确定旋转点,该点是离根最远(或最接近于叶子的点) +3. 确定平衡类型后进行平衡处理,平衡后以平衡点为根的子树高不变 4. 最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。 常见的平衡二叉树: -1. 红黑树是平衡二叉树,也就是左右子树是平衡的,高度大概相等。这种情况等价于一块完全二叉树的高度,查找的时间复杂度是树的高度,为logn,插入操作的平均时间复杂度为O(logn),最坏时间复杂度为O(logn) -2. avl树也是自平衡二叉树;红黑树和AVL树查找、插入、删除的时间复杂度相同;包含n个内部结点的红黑树的高度是o(logn); TreeMap 是一个红黑树的实现,能保证插入的值保证排序 +1. 红黑树是平衡二叉树,也就是左右子树是平衡的,高度大概相等。这种情况等价于一块完全二叉树的高度,查找的时间复杂度是树的高度,为logn,插入操作的平均时间复杂度为O(logn),最坏时间复杂度为O(logn) +![红黑树](https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Red-black_tree_example.svg/800px-Red-black_tree_example.svg.png) + * 节点是红色或黑色。 + * 根是黑色。 + * 所有叶子都是黑色(叶子是NIL节点)。 + * 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) + * 从任一节点到其每个叶子的所有简单路径 都包含相同数目的黑色节点。 +2. avl树也是自平衡二叉树;红黑树和AVL树查找、插入、删除的时间复杂度相同;包含n个内部结点的红黑树的高度是o(logn); TreeMap 是一个红黑树的实现,能保证插入的值保证排序 +3. STL和linux多使用红黑树作为平衡树的实现: + 1. 如果插入一个node引起了树的不平衡,AVL和RB-Tree都是最多只需要2次旋转操作,即两者都是O(1);但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root这条路径上所有node的平衡性,因此需要旋转的量级O(logN),而RB-Tree最多只需3次旋转,只需要O(1)的复杂度。 + 2. 其次,AVL的结构相较RB-Tree来说更为平衡,在插入和删除node更容易引起Tree的unbalance,因此在大量数据需要插入或者删除时,AVL需要rebalance的频率会更高。因此,RB-Tree在需要大量插入和删除node的场景下,效率更高。自然,由于AVL高度平衡,因此AVL的search效率更高。 + 3. map的实现只是折衷了两者在search、insert以及delete下的效率。总体来说,RB-tree的统计性能是高于AVL的。 + ## 查找总结 1. 既希望较快的查找又便于线性表动态变化的查找方法是哈希法查找。二叉排序树查找,最优二叉树查找,键树查找,哈希法查找是动态查找。分块、顺序、折半、索引顺序查找均为静态。分块法应该是将整个线性表分成若干块进行保存,若动态变化则可以添加在表的尾部(非顺序结构),时间复杂度是O(1),查找复杂度为O(n);若每个表内部为顺序结构,则可用二分法将查找时间复杂度降至O(logn),但同时动态变化复杂度则变成O(n);顺序法是挨个查找,这种方法最容易实现,不过查找时间复杂度都是O(n),动态变化时可将保存值放入线性表尾部,则时间复杂度为O(1);二分法是基于顺序表的一种查找方式,时间复杂度为O(logn);通过哈希函数将值转化成存放该值的目标地址,O(1) 2. 二叉树的平均查找长度为O(log2n)——O(n).二叉排序树的查找效率与二叉树的高度有关,高度越低,查找效率越高。二叉树的查找成功的平均查找长度ASL不超过二叉树的高度。二叉树的高度与二叉树的形态有关,n个节点的完全二叉树高度最小,高度为[log2n]+1,n个节点的单只二叉树的高度最大,高度为n,此时查找成功的ASL为最大(n+1)/2,因此二叉树的高度范围为[log2n]+1——n. -3. 链式存储不能随机访问,必须是顺序存储 ## B_树的B+树 ### B_树 +3. 链式存储不能随机访问,必须是顺序存储 + +## B_树的B+树 +### B_树 B-树就是B树。m阶B_树满足或空,或为满足下列性质的m叉树: ![B-树](http://img.my.csdn.net/uploads/201106/7/8394323_13074405906V6Q.jpg) -1. 树中每个结点最多有m棵子树 2. 根结点在不是叶子时,至少有两棵子树 3. 除根外,所有非终端结点至少有⎡m/2⎤棵子树 4. 有s个子树的非叶结点具有 n = s-1个关键字,结点的信息组织为:(n,A0,K1,A1,K2,A2 … Kn,An)。这里:n为关键字的个数,ki(i=1,2,…,n)为关键字,且满足Ki小于Ki+1,,Ai(i=0,1,..n)为指向子树的指针。 5. 所有的叶子结点都出现在同一层上,不带信息(可认为外部结点或失败结点)。 +1. 树中每个结点最多有m棵子树 +2. 根结点在不是叶子时,至少有两棵子树 +3. 除根外,所有非终端结点至少有⎡m/2⎤棵子树 +4. 有s个子树的非叶结点具有 n = s-1个关键字,结点的信息组织为:(n,A0,K1,A1,K2,A2 … Kn,An)。这里:n为关键字的个数,ki(i=1,2,…,n)为关键字,且满足Ki小于Ki+1,,Ai(i=0,1,..n)为指向子树的指针。 +5. 所有的叶子结点都出现在同一层上,不带信息(可认为外部结点或失败结点)。 6. 关键字集合分布在整颗树中 7. 任何一个关键字出现且只出现在一个结点中 8. 搜索有可能在非叶子结点结束 9. 其搜索性能等价于在关键字全集内做一次二分查找 10. 只适用于随机检索,不适用于顺序检索。 11. 有结点的平衡因子都为零 -12. M阶B-树中含有N个关键字,最大深度为log⎡m/2⎤(n+1)/2+2 B_树中结点的插入 +12. M阶B-树中含有N个关键字,最大深度为log⎡m/2⎤(n+1)/2+2 -1. m代表B_树的阶,插入总发生在最低层 2. 插入后关键字个数小于等于 m-1,完成。 3. 插入后关键字个数等于m,结点分裂,以中点数据为界一分为二,中点数据放到双亲结点中。这样就有可能使得双亲结点的数据个数为m,引起双亲结点的分裂,最坏情况下一直波及到根,引起根的分裂——B_树长高。 +B_树中结点的插入 + +1. m代表B_树的阶,插入总发生在最低层 +2. 插入后关键字个数小于等于 m-1,完成。 +3. 插入后关键字个数等于m,结点分裂,以中点数据为界一分为二,中点数据放到双亲结点中。这样就有可能使得双亲结点的数据个数为m,引起双亲结点的分裂,最坏情况下一直波及到根,引起根的分裂——B_树长高。 3阶`B_`树的插入。每个结点最多3棵子树,2个数据;最少2棵子树,1个数据。所以3阶B_树也称为2-3树。 B_树中结点的删除 -1. 删除发生在最底层 * 被删关键字所在结点中的关键字数目大于等于 m/2 ,直接删除。 +1. 删除发生在最底层 + * 被删关键字所在结点中的关键字数目大于等于 m/2 ,直接删除。 * 删除后结点中数据为⎡m/2⎤-2,而相邻的左(右)兄弟中数据大于⎡m/2⎤-1,此时左(右兄弟)中最大(小)的数据上移到双亲中,双亲中接(靠)在它后(前)面的数据移到被删数据的结点中 * 其左右兄弟结点中数据都是⎡m/2⎤-1,此时和左(右)兄弟合并,合并时连同双亲中相关的关键字。此时,双亲中少了一项,因此又可能引起双亲的合并,最坏一直到根,使B-树降低一层。 2. 删除不在最底层 - * 在大于被删数据中选最小的代替被删数据,问题转换成在最底层的删除 ### B+树 -在实际的文件系统中,用的是B+树或其变形。有关性质与操作类似与B_树。 ![B+树](http://hi.csdn.net/attachment/201106/7/8394323_1307440587b6WG.jpg) 差异: 1. 有n棵子树的结点中有n个关键字,每个关键字不保存数据,只用来索引,所有数据都保存在叶子节点。 2. 所有叶子结点中包含全部关键字信息,及对应记录位置信息及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。(而B树的叶子节点并没有包括全部需要查找的信息) -3. 所有非叶子为索引,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B树的非终节点也包含需要查找的有效信息) 4. 非叶最底层顺序联结,这样可以进行顺序查找 + * 在大于被删数据中选最小的代替被删数据,问题转换成在最底层的删除 + +### B+树 +在实际的文件系统中,用的是B+树或其变形。有关性质与操作类似与B_树。 + +![B+树](http://hi.csdn.net/attachment/201106/7/8394323_1307440587b6WG.jpg) + +差异: + +1. 有n棵子树的结点中有n个关键字,每个关键字不保存数据,只用来索引,所有数据都保存在叶子节点。 +2. 所有叶子结点中包含全部关键字信息,及对应记录位置信息及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。(而B树的叶子节点并没有包括全部需要查找的信息) +3. 所有非叶子为索引,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B树的非终节点也包含需要查找的有效信息) +4. 非叶最底层顺序联结,这样可以进行顺序查找 B+特性 @@ -378,9 +508,17 @@ B+特性 2. 不可能在非叶子结点命中 3. 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层 4. 更适合文件索引系统 -5. B+树插入操作的平均时间复杂度为O(logn),最坏时间复杂度为O(logn) 查找过程 +5. B+树插入操作的平均时间复杂度为O(logn),最坏时间复杂度为O(logn) + +查找过程 -* 在 B+ 树上,既可以进行缩小范围的查找,也可以进行顺序查找; * 在进行缩小范围的查找时,不管成功与否,都必须查到叶子结点才能结束; * 若在结点内查找时,给定值≤Ki, 则应继续在 Ai 所指子树中进行查找 插入和删除的操作:类似于B_树进行,即必要时,也需要进行结点的“分裂”或“合并”。 为什么说B+tree比B树更适合实际应用中操作系统的文件索引和数据库索引? +* 在 B+ 树上,既可以进行缩小范围的查找,也可以进行顺序查找; +* 在进行缩小范围的查找时,不管成功与否,都必须查到叶子结点才能结束; +* 若在结点内查找时,给定值≤Ki, 则应继续在 Ai 所指子树中进行查找 + +插入和删除的操作:类似于B_树进行,即必要时,也需要进行结点的“分裂”或“合并”。 + +为什么说B+tree比B树更适合实际应用中操作系统的文件索引和数据库索引? 1. B+tree的磁盘读写代价更低 * B+tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。 @@ -388,7 +526,9 @@ B+特性 2. B+tree的查询效率更加稳定 * 由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。 -B树和B+树都是平衡的多叉树。B树和B+树都可用于文件的索引结构。B树和B+树都能有效的支持随机检索。B+树既能索引查找也能顺序查找. ## 哈希表 +B树和B+树都是平衡的多叉树。B树和B+树都可用于文件的索引结构。B树和B+树都能有效的支持随机检索。B+树既能索引查找也能顺序查找. + +## 哈希表 1. 在记录的存储地址和它的关键字之间建立一个确定的对应关系;这样不经过比较,一次存取就能得到元素。 2. 哈希函数——在记录的关键字与记录的存储位置之间建立的一种对应关系。是从关键字空间到存储位置空间的一种映象。 3. 哈希表——应用哈希函数,由记录的关键字确定记录在表中的位置信息,并将记录根据此信息放入表中,这样构成的表叫哈希表。 @@ -397,17 +537,23 @@ B树和B+树都是平衡的多叉树。B树和B+树都可用于文件的索引 6. Hash表等查找复杂依赖于Hash值算法的有效性,在最好的情况下,hash表查找复杂度为O(1)。只有无冲突的hash_table复杂度才是O(1)。一般是O(c),c为哈希关键字冲突时查找的平均长度。插入,删除,查找都是O(1)。平均查找长度不随表中结点数目的增加而增加,而是随负载因子的增大而增大 7. 由于冲突的产生,使得哈希表的查找过程仍然是一个给定值与关键字比较的过程。 -根据抽屉原理,冲突是不可能完全避免的,所以,选择好的**散列函数和冲突处理**方法: 1. 构造一个性能好,冲突少的Hash函数 2. 如何解决冲突 常用的哈希函数 +根据抽屉原理,冲突是不可能完全避免的,所以,选择好的**散列函数和冲突处理**方法: -1. 直接定址法。仅适合于:地址集合的大小 == 关键字集合的大小 2. 数字分析法。对关键字进行分析,取关键字的若干位或其组合作哈希地址。仅适合于:能预先估计出全体关键字的每一位上各种数字出现的频度。 +1. 构造一个性能好,冲突少的Hash函数 +2. 如何解决冲突 + +常用的哈希函数 + +1. 直接定址法。仅适合于:地址集合的大小 == 关键字集合的大小 +2. 数字分析法。对关键字进行分析,取关键字的若干位或其组合作哈希地址。仅适合于:能预先估计出全体关键字的每一位上各种数字出现的频度。 3. 平方取中法。以关键字的平方值的中间几位作为存储地址。 -4. 折叠法。将关键字分割成位数相同的几部分,然后取这几部分的叠加和(舍去进位)做哈希地址。移位叠加/间界叠加。适合于: 关键字的数字位数特别多,且每一位上数字分布大致均匀情况。 +4. 折叠法。将关键字分割成位数相同的几部分,然后取这几部分的叠加和(舍去进位)做哈希地址。移位叠加/间界叠加。适合于: 关键字的数字位数特别多,且每一位上数字分布大致均匀情况。 5. 除留余数法。取关键字被某个不大于哈希表表长m的数p除后所得余数作哈希地址,即H(key)=key%p,p<=m。 6. 随机数法。取关键字的伪随机函数值作哈希地址,即H(key)=random(key),适于关键字长度不等的情况。 冲突解决 -1. 开放定址法。当冲突发生时,形成一个探查序列;沿此序列逐个地址探查,直到找到一个空位置(开放的地址),将发生冲突的记录放到该地址中。即Hi=(H(key)+di) % m,i=1,2,……k(k<=m-1),H(key)哈希函数,m哈希表长,di增量序列。缺点:删除:只能作标记,不能真正删除;溢出;载因子过大、解决冲突的算法选择不好会发生聚集问题。要求装填因子α较小,故当结点规模较大时会浪费很多空间。 +1. 开放定址法。当冲突发生时,形成一个探查序列;沿此序列逐个地址探查,直到找到一个空位置(开放的地址),将发生冲突的记录放到该地址中。即Hi=(H(key)+di) % m,i=1,2,……k(k<=m-1),H(key)哈希函数,m哈希表长,di增量序列。缺点:删除:只能作标记,不能真正删除;溢出;载因子过大、解决冲突的算法选择不好会发生聚集问题。要求装填因子α较小,故当结点规模较大时会浪费很多空间。 * 线性探测再散列:di=1,2,3,...,m-1 * 二次探测再散列:di=12,-12,22,-22,...,±k2(k<=m/2) * 伪随机探测再散列: di为伪随机数序列 @@ -422,7 +568,10 @@ Hash查找效率:装填因子=表中记录数/表容量 有B+Tree/Hash_Map/STL Map三种数据结构。对于内存中数据,查找性能较好的数据结构是Hash_Map,对于磁盘中数据,查找性能较好的数据结构是B+Tree。Hash操作能根据散列值直接定位数据的存储地址,设计良好的hash表能在常数级时间下找到需要的数据,但是更适合于内存中的查找。B+树是一种是一种树状的数据结构,适合做索引,对磁盘数据来说,索引查找是比较高效的。STL_Map的内部实现是一颗红黑树,但是只是一颗在内存中建立二叉树树,不能用于磁盘操作,而其内存查找性能也比不上Hash查找。 ## 内部排序 -1. 内部排序:全部数据可同时放入内存进行的排序。 2. 外部排序:文件中数据太多,无法全部调入内存进行的排序。 插入类: +1. 内部排序:全部数据可同时放入内存进行的排序。 +2. 外部排序:文件中数据太多,无法全部调入内存进行的排序。 + +插入类: 1. 直接插入排序。最坏情况是数据递减序,数据比较和移动量最大,达到O(n2),最好是数据是递增序,比较和移动最少为O(n)。趟数是固定的n-1,即使有序,也要依次从第二个元素开始。排序趟数不等于时间复杂度。 2. 折半插入排序 。由于插入第i个元素到r[1]到r[i-1]之间时,前i个数据是有序的,所以可以用折半查找确定插入位置,然后插入。 @@ -431,11 +580,13 @@ Hash查找效率:装填因子=表中记录数/表容量 交换类: 1. 冒泡排序。O(n2)通常认为冒泡是比较差的,可以加些改进,比如在一趟中无数据的交换,则结束等措施。 - * 在数据已基本有序时,冒泡是一个较好的方法 * 在数据量较少时(15个左右)可以用冒泡 + * 在数据已基本有序时,冒泡是一个较好的方法 + * 在数据量较少时(15个左右)可以用冒泡 2. 快速排序。 * 时间复杂度。最好情况:每次支点总在中间,O(nlog2n),平均O(nlog2n)。最坏,数据已是递增或递减,O(n2)。pivotkey的选择越靠近中央,即左右两个子序列长度越接近,排序速度越快。越无序越快。 * 空间复杂度。需栈空间以实现递归,最坏情况:S(n)=O(n);一般情况:S(n)=O(log2n) - * 在序列已是有序的情况下,时间复杂度最高。原因:支点选择不当。改进:随机选取支点或最左、最右、中间三个元素中的值处于中间的作为支点,通常可以避免最坏情况。所以,快速排序在表已基本有序的情况下不合适。 * 在序列长度已较短时,采用直接插入排序、起泡排序等排序方法。序列的个数通常取10左右。 + * 在序列已是有序的情况下,时间复杂度最高。原因:支点选择不当。改进:随机选取支点或最左、最右、中间三个元素中的值处于中间的作为支点,通常可以避免最坏情况。所以,快速排序在表已基本有序的情况下不合适。 + * 在序列长度已较短时,采用直接插入排序、起泡排序等排序方法。序列的个数通常取10左右。 选择类排序: @@ -443,7 +594,9 @@ Hash查找效率:装填因子=表中记录数/表容量 2. 堆排序。建堆 O(n),筛选排序O(nlogn)。找出若干个数中最大/最小的前K个数,用堆排序是最好。小根堆中最大的数一定是放在叶子节点上,堆本身是个完全二叉树,完全二叉树的叶子节点的位置大于[n/2]。时间复杂度不会因为待排序序列的有序程度而改变,但是待排序序列的有序程度会影响比较次数。 3. 归并排序。时间:与表长成正比,若一个表表长是m,另一个是n,则时间是O(m+n)。单独一个数组归并,时间:O(nlogn),空间:O(n),比较次数介于(nlogn)/2和(nlogn)-n+1,赋值操作的次数是(2nlogn)。归并排序算法比较占用内存,但却是**效率高且稳定**的排序算法。在外排序中使用。归并的趟数是logn。 4. 基数排序。在一般情况下,每个结点有 d 位关键字,必须执行 t = d次分配和收集操作。分配的代价:O(n);收集的代价:O(rd) (rd是基数);总的代价为:O( d ×(n + rd))。适用于以数字和字符串为关键字的情况。 -5. 枚举排序,通常也被叫做秩排序,比较计数排序。对每一个要排序的元素,统计小于它的所有元素的个数,从而得到该元素在整个序列中的位置,时间复杂度为O(n2) 比较法分类的下界:O(nlogn) +5. 枚举排序,通常也被叫做秩排序,比较计数排序。对每一个要排序的元素,统计小于它的所有元素的个数,从而得到该元素在整个序列中的位置,时间复杂度为O(n2) + +比较法分类的下界:O(nlogn) 排序算法的一些特点: @@ -454,14 +607,42 @@ Hash查找效率:装填因子=表中记录数/表容量 5. **不稳定的排序方法:快排,堆排,希尔,选择** 6. 要与关键字的初始排列次序无关,那么就是最好、最坏、一般的情况下排序时间复杂度不变, 总共有堆排序,归并排序,选择排序,基数排序 7. 快速排序、Shell 排序、归并排序、直接插入排序的关键码比较次数与记录的初始排列有关。折半插入排序、选择排序无关。(直接插入排序在完全有序的情况下每个元素只需要与他左边的元素比较一次就可以确定他最终的位置;折半插入排序,比较次数是固定的,与初始排序无关;快速排序,初始排序不影响每次划分时的比较次数,都要比较n次,但是初始排序会影响划分次数,所以会影响总的比较次数,但快排平均比较次数最小;归并排序在归并的时候,如果右路最小值比左路最大值还大,那么只需要比较n次,如果右路每个元素分别比左路对应位置的元素大,那么需要比较2*n-1次,所以与初始排序有关) -8. 精俭排序,即一对数字不进行两次和两次以上的比较,插入和归并是“精俭排序”。插入排序,前面是有序的,后面的每一个元素与前面有序的元素比较,比较过的就是有序的了,不会再比较一次。归并每次合并后,内部都是有序的,内部的元素之间不用再比较。选择排序,每次在后面的元素中找到最小的,找最小元素的过程是在没有排好序的那部分进行,所有肯定会比较多次。堆排序也需比较多次。 ## 外部排序 1. 生成合并段(run):读入文件的部分记录到内存->在内存中进行内部排序->将排好序的这些记录写入外存,形成合并段->再读入该文件的下面的记录,往复进行,直至文件中的记录全部形成合并段为止。 +8. 精俭排序,即一对数字不进行两次和两次以上的比较,插入和归并是“精俭排序”。插入排序,前面是有序的,后面的每一个元素与前面有序的元素比较,比较过的就是有序的了,不会再比较一次。归并每次合并后,内部都是有序的,内部的元素之间不用再比较。选择排序,每次在后面的元素中找到最小的,找最小元素的过程是在没有排好序的那部分进行,所有肯定会比较多次。堆排序也需比较多次。 + +## 外部排序 +1. 生成合并段(run):读入文件的部分记录到内存->在内存中进行内部排序->将排好序的这些记录写入外存,形成合并段->再读入该文件的下面的记录,往复进行,直至文件中的记录全部形成合并段为止。 2. 外部合并:将上一阶段生成的合并段调入内存,进行合并,直至最后形成一个有序的文件。 3. 外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行多路归并排序 4. 不管初始序列是否有序, 冒泡、选择排序时间复杂度是O(n^2),归并、堆排序时间复杂度是O(nlogn) 5. 外部排序的总时间 = 内部排序(产出初始归并段)所需时间 + 外存信息读取时间 + 内部归并所需的时间 -6. 外排中使用置换选择排序的目的,是为了增加初始归并段的长度。减少外存读写次数需要减小归并趟数 1. 根据内存容量设若干个输入缓冲区和一个输出缓冲区。若采用二路归并,用两个输入缓冲。 +6. 外排中使用置换选择排序的目的,是为了增加初始归并段的长度。减少外存读写次数需要减小归并趟数 + + +1. 根据内存容量设若干个输入缓冲区和一个输出缓冲区。若采用二路归并,用两个输入缓冲。 2. 归并的方法类似于归并排序的归并算法。增加的是对缓冲的监视,对于输入,一旦缓冲空,要到相应文件读后续数据,对于输出缓冲,一旦缓冲满,要将缓冲内容写到文件中去。 -3. 外排序和内排序不只是考虑内外排序算法的性能,还要考虑IO数据交换效率的问题,内存存取速度远远高于外存。影响外排序的时间因素主要是内存与外设交换信息的总次数 ## 有效的算法设计 -1. 贪心法。Dijkstra的最短路径(时间复杂度O(n2));Prim求最小生成树邻接表存储时是O(n+e),图O(n2);关键路径及关键活动的求法。 2. 回溯法 3. 分支限界法 4. 分治法。分割、求解、合并。二分查找、归并排序、快速排序。 5. 动态规划。Floyd-Warshall算法求解图中所有点对之间最短路径时间复杂度为O(n3) 动态规划解题的方法是一种高效率的方法,其时间复杂度通常为O(n2),O(n3)等,可以解决相当大的信息量。(数塔在n<=100层时,可以在很短的时间内得到问题解) * 适用的原则:原则为优化原则,即整体优化可以分解为若干个局部优化。 * 动态规划比穷举法具有较少的计算次数 * 递归算法需要很大的栈空间,而动态规划不需要栈空间 贪心和动态规划的差别: 1. 所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。 2. 在动态规划算法中,每步所作的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能作出选择。而在贪心算法中,仅在当前状态下作出最好选择,即局部最优选择。然后再去解作出这个选择后产生的相应的子问题。 -3. 贪心算法所作的贪心选择可以依赖于以往所作过的选择,但决不依赖于将来所作的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为一个规模更小的子问题。 1. P问题,如果它可以通过运行多项式次(即运行时间至多是输入量大小的多项式函数的一种算法获得解决),可以找到一个能在多项式的时间里解决它的算法。----确定性问题 2. NP问题,虽然可以用计算机求解,但是对于任意常数k,它们不能在O(nk)时间内得到解答,可以在多项式的时间里验证一个解的问题。所有的P类问题都是NP问题。 +3. 外排序和内排序不只是考虑内外排序算法的性能,还要考虑IO数据交换效率的问题,内存存取速度远远高于外存。影响外排序的时间因素主要是内存与外设交换信息的总次数 + +## 有效的算法设计 +1. 贪心法。Dijkstra的最短路径(时间复杂度O(n2));Prim求最小生成树邻接表存储时是O(n+e),图O(n2);关键路径及关键活动的求法。 +2. 回溯法 +3. 分支限界法 +4. 分治法。分割、求解、合并。二分查找、归并排序、快速排序。 +5. 动态规划。Floyd-Warshall算法求解图中所有点对之间最短路径时间复杂度为O(n3) + +动态规划解题的方法是一种高效率的方法,其时间复杂度通常为O(n2),O(n3)等,可以解决相当大的信息量。(数塔在n<=100层时,可以在很短的时间内得到问题解) + +* 适用的原则:原则为优化原则,即整体优化可以分解为若干个局部优化。 +* 动态规划比穷举法具有较少的计算次数 +* 递归算法需要很大的栈空间,而动态规划不需要栈空间 + +贪心和动态规划的差别: + +1. 所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。 +2. 在动态规划算法中,每步所作的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能作出选择。而在贪心算法中,仅在当前状态下作出最好选择,即局部最优选择。然后再去解作出这个选择后产生的相应的子问题。 +3. 贪心算法所作的贪心选择可以依赖于以往所作过的选择,但决不依赖于将来所作的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为一个规模更小的子问题。 + +P问题 + +1. P问题,如果它可以通过运行多项式次(即运行时间至多是输入量大小的多项式函数的一种算法获得解决),可以找到一个能在多项式的时间里解决它的算法。----确定性问题 +2. NP问题,虽然可以用计算机求解,但是对于任意常数k,它们不能在O(nk)时间内得到解答,可以在多项式的时间里验证一个解的问题。所有的P类问题都是NP问题。 3. NP完全问题,知道有效的非确定性算法,但是不知道是否存在有效的确定性算法,同时,不能证明这些问题中的任何一个不存在有效的确定性算法。这类问题称为NP完全问题。 \ No newline at end of file