算法引入
import time
start_time = time.time()
for a in range(0, 1001):
for b in range(0, 1001):
for c in range(0, 1001):
if a+b+c == 1000 and a**2 + b**2 == c**2:
print("a, b, c:%d, %d, %d" % (a, b, c))
end_time = time.time()
print("times:%d" % (end_time -start_time))
print("finished")
執行結果
a, b, c:0, 500, 500
a, b, c:200, 375, 425
a, b, c:375, 200, 425
a, b, c:500, 0, 500
times:286
finished
另一種算法
import time
start_time = time.time()
for a in range(0, 1001):
for b in range(0, 1001):
c = 1000 - a - b
if a**2 + b**2 == c**2:
print("a, b, c:%d, %d, %d" % (a, b, c))
end_time = time.time()
print("times:%d" % (end_time -start_time))
print("finished")
執行結果
a, b, c:0, 500, 500
a, b, c:200, 375, 425
a, b, c:375, 200, 425
a, b, c:500, 0, 500
times:1
finished
算法的概念
算法是獨立存在的一種解決問題的方法和思想
算法的特性:
- 輸入
- 輸出
- 有窮性
- 確定性
- 可行性
算法效率衡量
時間復雜度
基本運算數量個數
T1 = O(n^3)
T2 = O(n^2)
只考慮與n相關的數量級,忽略常量系數(最后的循環中有2個還是10個操作)
最優時間復雜度
最壞時間復雜度
平均時間復雜度
一般時間復雜度指最壞時間復雜度
時間復雜度計算規則:
常見時間復雜度
Python內置類型性能分析
from timeit import Timer
def test1():
li = []
for i in range(10000):
# li += [i] 不完全等於以下語句, 有優化
li = li + [i]
def test2():
li = []
for i in range(10000):
li.append(i)
def test3():
li = [i for i in range(10000)]
def test4():
li = list(range(10000))
def test5():
li = []
for i in range(10000):
li.extend([i])
def test6():
li = []
for i in range(10000):
li.insert(0, i)
timer1 = Timer("test1()", "from __main__ import test1")
print("+:", timer1.timeit(number=1000))
timer2 = Timer("test2()", "from __main__ import test2")
print("append:", timer2.timeit(number=1000))
timer3 = Timer("test3()", "from __main__ import test3")
print("構造器:", timer3.timeit(number=1000))
timer4 = Timer("test4()", "from __main__ import test4")
print("類型轉換:", timer4.timeit(number=1000))
timer5 = Timer("test5()", "from __main__ import test5")
print("extend:", timer5.timeit(number=1000))
timer6 = Timer("test6()", "from __main__ import test6")
print("insert:", timer6.timeit(number=1000))
執行結果
+: 1.9438251314738715
append: 2.0546681004856353
構造器: 0.9054245635968021
類型轉換: 0.5199876041759088
extend: 2.7770162085994707
insert: 42.24719570966755
數據結構
靜態的描述了數據元素之間的關系
程序 = 數據結構+算法
抽象數據類型 ADT
數據組裝方式 + 所支持的操作
順序表
用連續單元存儲數據(地址連續)
變量名指向起始地址
索引實際是從起始位置的偏移量
- 一體存儲 元素內置
- 分離存儲 元素外置
- 動態順序表(可以數據擴充)
順序表的操作
添加元素 末尾添加 O(1) 中間插入O(n) 插入非保序O(1)
刪除元素 末尾刪除 O(1) 中間刪除O(n)
Python的list的基本實現
鏈表
一個節點分為兩部分,數據區和鏈接區, 鏈接區指向下一個節點
單項鏈表
a, b = b, a的本質
python中變量是一塊單獨的空間, 其中保存的是所代表對象的地址
單項鏈表的實現
"""單項鏈表的實現"""
class Node(object):
def __init__(self, elem):
self.elem = elem
self.next = None
def __repr__(self):
return "<Node: {}>".format(self.elem)
class SingleLinkList(object):
def __init__(self, node=None):
self.__head = node
def add(self, item):
node = Node(item)
if self.__head:
self.__head, node.next = node, self.__head
else:
self.__head = node
def append(self, item):
node = Node(item)
if self.__head:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
else:
self.__head = node
def travel(self):
if self.__head:
cur = self.__head
while cur is not None:
print(cur.elem)
cur = cur.next
def remove(self, item):
if self.__head:
cur = self.__head
while cur.next is not None:
if cur.next.elem == item:
cur.next = cur.next.next
break
cur = cur.next
else:
print("Not Exist")
else:
print("Not Exist")
def insert(self, pos, item):
i = 0
cur = self.__head
if pos == 0:
self.add(item)
elif pos + 1 > self.length:
raise ValueError
else:
while cur.next is not None:
i += 1
if i == pos:
new_node = Node(item)
cur.next, new_node.next = new_node, cur.next
break
elif i > pos:
raise ValueError
cur = cur.next
def search(self, item):
if self.__head:
cur = self.__head
while cur is not None:
if cur.elem == item:
print(cur.elem)
break
cur = cur.next
@property
def length(self):
count = 0
if self.__head:
cur = self.__head
while cur is not None:
count += 1
cur = cur.next
return count
def is_empty(self):
return self.__head is None
循環鏈表
循環鏈表的實現
"""循環鏈表的實現"""
class Node(object):
def __init__(self, elem):
self.elem = elem
self.next = None
def __repr__(self):
return "<Node: {}>".format(self.elem)
class CircleLinkList(object):
def __init__(self, node=None):
if node is None:
self._head = None
else:
self._head = node.next = node
def add(self, item):
node = Node(item)
if self._head:
cur = self._head
while cur.next != self._head:
cur = cur.next
last = cur
node.next, self._head = self._head, node
last.next = self._head
else:
self._head = node.next = node
def append(self, item):
node = Node(item)
if self._head:
cur = self._head
while cur.next != self._head:
cur = cur.next
last = cur
last.next, node.next = node, self._head
else:
self._head = node.next = node
def travel(self):
if self._head:
cur = self._head
while cur.next != self._head:
print(cur.elem, end=" ")
cur = cur.next
print(cur.elem)
def remove(self, item): # 單節點remove
if self._head:
cur = self._head
while cur.next != self._head:
cur = cur.next
last = cur
cur = self._head
if cur.elem == item:
self._head = last.next = cur.next
else:
while cur.next != self._head:
if cur.next.elem == item:
cur.next = cur.next.next
break
cur = cur.next
else:
print("Not Exist")
else:
print("Not Exist")
def insert(self, pos, item):
length = self.length
if pos == 0:
self.add(item)
elif pos == length:
self.append(item)
elif pos > length:
raise ValueError
else:
i = 0
cur = self._head
while cur.next != self._head :
i += 1
if i == pos:
node = Node(item)
cur.next, node.next = node, cur.next
break
cur = cur.next
def search(self, item):
if self._head:
cur = self._head
while cur.next != self._head:
if cur.elem == item:
print(cur.elem)
break
cur = cur.next
else:
if cur.elem == item:
print(cur.elem)
@property
def length(self):
count = 0
if self._head:
cur = self._head
while cur.next != self._head:
count += 1
cur = cur.next
count += 1
return count
def is_empty(self):
return self._head is None
if __name__ == '__main__':
s = CircleLinkList()
s.append(0)
s.append(1)
s.append(2)
s.append(3)
s.remove(0)
s.insert(3, 4)
s.search(1)
s.travel()
雙向鏈表
雙向鏈表的實現
"""雙項鏈表的實現"""
class Node(object):
def __init__(self, elem):
self.pre = None
self.elem = elem
self.next = None
def __repr__(self):
return "<Node: {}>".format(self.elem)
class DoubleLinkList(object):
def __init__(self, node=None):
self.__head = node
def add(self, item):
node = Node(item)
if self.__head:
self.__head, self.__head.pre, node.next = node, node, self.__head
else:
self.__head = node
def append(self, item):
node = Node(item)
if self.__head:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next, node.pre = node, cur
else:
self.__head = node
def travel(self):
if self.__head:
cur = self.__head
while cur is not None:
print(cur.elem, end=" ")
cur = cur.next
def remove(self, item):
if self.__head:
cur = self.__head
while cur.next is not None:
if cur.next.elem == item:
cur.next, cur.next.next.pre = cur.next.next, cur
break
cur = cur.next
else:
print("Not Exist")
else:
print("Not Exist")
def insert(self, pos, item):
i = 0
cur = self.__head
if pos == 0:
self.add(item)
elif pos + 1 > self.length:
raise ValueError
else:
while cur.next is not None:
i += 1
if i == pos:
new_node = Node(item)
cur.next, new_node.next, new_node.pre = new_node, cur.next, cur
break
elif i > pos:
raise ValueError
cur = cur.next
def search(self, item):
if self.__head:
cur = self.__head
while cur is not None:
if cur.elem == item:
print(cur.elem)
break
cur = cur.next
@property
def length(self):
count = 0
if self.__head:
cur = self.__head
while cur is not None:
count += 1
cur = cur.next
return count
def is_empty(self):
return self.__head is None
順序表和鏈表統稱線性表
棧
堆和棧一樣嗎?
棧(stack)一般編譯器自動分配釋放
堆(heap)一般由程序員分配釋放,或程序結束后OS釋放
LIFO 后進先出
棧的實現
class Stack(object):
def __init__(self):
self.__list = []
def push(self, item):
self.__list.append(item)
def pop(self):
return self.__list.pop()
def peek(self):
if self.__list:
return self.__list[-1]
else:
return None
def is_empoty(self):
return self.__list == []
def size(self):
return len(self.__list)
if __name__ == '__main__':
s = Stack()
a = "({[({{abc}})][{1}]})2([]){({[]})}[]"
m = {')':'(',']':'[','}':'{'}
for i in a:
if i in '([{':
s.push(i)
elif i in m:
if m[i] != s.pop():
print("fail")
break
else:
print("ok")
隊列
FIFO 先進先出
隊列與雙端隊列的實現
class Queue(object):
def __init__(self):
self.__list = []
def enqueue(self, item):
self.__list.append(item)
def dequeue(self):
return self.__list.pop(0)
def is_empty():
return self.__list == []
def size():
return len(self.__list)
if __name__ == '__main__':
q = Queue()
q.enqueue(1)
class Dqueue(object):
def __init__(self):
self.__list = []
def add_front(self, item):
self.__list.insert(0, item)
def add_rear(item):
self.__list.append(item)
def pop_front(self):
return self.__list.pop(0)
def pop_rear(self):
return self.__list.pop()
def is_empty():
return self.__list == []
def size():
return len(self.__list)
if __name__ == '__main__':
d = Dqueue()
排序與搜索
排序算法的穩定性: [2, 1, 1 , 3], 如果其中有相同元素, 算法不應破壞原有順序
排序算法
l = [4, 2, 1, 8, 5, 2, 3, 0, 9, 8, 10, 4, 3, 2]
def buddle_sort(l):
length = len(l)
for i in range(length-1):
for j in range(length-i-1):
if l[j] > l[j+1]:
l[j], l[j+1] = l[j+1], l[j]
def select_sort(l):
length = len(l)
for j in range(length):
max_index = 0
for i in range(length-j):
if l[i] > l[max_index]:
max_index = i
l[max_index], l[length-j-1] = l[length-j-1], l[max_index]
def quick_sort(l):
length = len(l)
if length < 2:
return l
mid = length // 2
low_part = [i for i in l[1:] if i < l[mid]]
eq_part = [i for i in l[1:] if i == l[mid]]
high_part = [i for i in l[1:] if i > l[mid]]
return quick_sort(low_part) + eq_part + quick_sort(high_part)
def quick_sort2(l, start, end):
low, high = start, end
if low >= high:
return
mid = l[low]
while low < high:
while low < high and l[high] > mid:
high -= 1
l[low] = l[high]
while low< high and l[low] <= mid:
low += 1
l[high] = l[low]
l[low] = mid
quick_sort2(l, start, low-1)
quick_sort2(l, low+1, end)
def insert_sort(l):
for i in range(1, len(l)):
for j in range(i, 0, -1):
if l[j] < l[j-1]:
l[j-1], l[j] = l[j], l[j-1]
# 感覺類似冒泡, 冒泡每次從底層開始冒, 插入每次從有序的后一個往前
# 希爾排序
def shell_sort(l):
n = len(l)
gap = n//2
while gap > 0:
for i in range(gap, n):
j = i
# 插入排序
while j>=gap and l[j-gap] > l[j]:
l[j-gap], l[j] = l[j], l[j-gap]
j -= gap
gap =gap//2
# 歸並排序
def merge(left, right):
'''合並操作'''
l, r = 0, 0
result = []
while l<len(left) and r<len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += left[l:] # 加上剩余的元素
result += right[r:] # 加上剩余的元素
return result
def merge_sort(l):
if len(l) <=1:
return l
num = len(l) // 2
left = merge_sort(l[:num])
right = merge_sort(l[num:])
return merge(left, right)
if __name__ == '__main__':
# buddle_sort(l)
# print(l)
# select_sort(l)
# print(l)
# quick_sort(l)
# print(l)
# quick_sort2(l, 0, len(l)-1)
insert_sort(l)
print(l)
- 冒泡排序: 依次比較相鄰兩個位置,將大的交換到最后, 重復剩余部分
- 選擇排序: 先找到最大(最小)值的索引, 將最大數(最小輸)和最后(最前)一個交換, 重復剩余部分
- 快速排序: 選一個基准數,將小於基准數和大於基准數的分為兩部分,遞歸左右兩部分直到長度小於2
- 插入排序: 從第二位依次向前比較交換,保持前半部分有序
- 希爾排序: 按不同間隔(間隔從n/2到1)進行插入排序
- 歸並排序: 遞歸將兩個有序序列合並成一個新的有序序列
冒泡,插入,歸並是穩定的
樹和二叉樹
完全二叉樹: 除了最下層,每一層都滿了
滿二叉樹: 每一層都滿了
平衡二叉樹: 任意兩個節點的高度差不大於1
排序二叉樹:
鏈式存儲
常見應用場景
- xml/html解析
- 路由協議
- mysql數據庫索引
- 文件系統結構
二叉樹
- 在二叉樹的第i層上至多有2^(i-1)個結點
- 深度為k的二叉樹至多有2^k-1個結點
- 對於任意一顆二叉樹, 如果其葉結點數為N, 則度數為2的節點總數為N+1
- 具有n個節點的完全二叉樹的深度必為log2(n+1)
- 對於完全二叉樹, i節點的左孩子變化為2i, 右孩子為2i+1
二叉樹的節點表示和樹的創建
節點
class Node(object):
def __init__(self, item):
self.elem = item
self.lchild = lchild
self.rchild = rchild
樹
class Tree(object):
def __init__(self, root=None):
self.root = root
def add(self, item):
node = Node(item)
if self.root is None
self.root = node
return
else:
queue = [self.root]
while queue:
cur_node = queue.pop(0)
if cur_node.lchild is None:
cur_node.lchild = node
return
else:
queue.append(cur_node.lchild)
if cur_node.rchild is None:
cur_node.rchild = node
return
else:
queue.append(cur_node.rchild)
# 廣度優先遍歷
def breadth_travel(self):
if self.root is None:
return
queue = [self.root]
while queue:
cur_node = queuq.pop(0)
print(cur_node.elem)
if cur_node.lchild is not None:
quequ.append(cur_node.lchild)
if cur_node.rchild is not None:
queue.append(rchild)
# 先序遍歷
def preorder(self, node):
if node is None:
return
print(node.elem)
self.preorder(node.lchild)
self.preorder(node.rchild)
# 中序遍歷
def inorder(self, node):
if node is None:
return
self.inorder(node.lchild)
print(node.elem)
self.inorder(node.rchild)
# 后序遍歷
def postorder(self, node):
if node is None:
return
self.postorder(node.lchild)
self.postorder(node.rchild)
print(self.elem)
根據 先序遍歷+中序遍歷 或 后序+中序 推導出一顆樹
-
先從先序/后序中找出root節點
-
在中序中找到root節點 分成兩半
-
先序中 安裝中序中的兩半 分開
-
左右子樹分別重復前三步操作