標籤:小知識點 大於 路徑 search add 廣度 二分尋找 情況下 bin
獨白:
利用演算法進行尋找指定元素,最近學習二分尋找和二叉樹遍曆。二分尋找前提是在有序中進行尋找,二叉樹引入了樹的概念。樹的概念其中有許多小知識點,也是一種新的資料結構。還是之前的感悟,需瞭解其本質才會寫出更好的演算法。
二分尋找
二分尋找又稱折半尋找,優點是比較次數少,尋找速度快,平均效能好;其缺點是要求待查表為有序表,且插入刪除困難。因此,折半尋找方法適用於不經常變動而尋找頻繁的有序列表。首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與尋找關鍵字比較,如果兩者相等,則尋找成功;否則利用中間位置記錄將表分成前、後兩個子表,如果中間位置記錄的關鍵字大於尋找關鍵字,則進一步尋找前一子表,否則進一步尋找後一子表。重複以上過程,直到找到滿足條件的記錄,使尋找成功,或直到子表不存在為止,此時尋找不成功。
‘‘‘二分尋找時間複雜度:O(logn)‘‘‘‘‘‘前提是在一個有序的列表‘‘‘import time## def binary_search(list, item):# ‘‘‘ 非遞迴實現 ‘‘‘## first = 0# last = len(list) - 1# while first <= last :# midpoint = ( first + last ) // 2# if list[midpoint] == item:# return True# elif item < list[midpoint]:# last = midpoint - 1# else:# first = midpoint + 1# return Falsedef binary_search(list, item): """ 遞迴實現 """ print(list) if len(list) == 0: return False else: midpoint = len(list) // 2 if list[midpoint] == item: return True else: if list[midpoint] > item: return binary_search(list[:midpoint], item) else: return binary_search(list[midpoint + 1:], item)if __name__ == ‘__main__‘: # 開始時間 first_time = time.time() # 建立個有序的列表 lis = [1, 2, 5, 6, 7, 8, 9, 17, 156, 678] # 列表排序 print(binary_search(lis, 6)) # 結束時間 last_time = time.time() print("共用時%s" % (last_time - first_time))
樹與樹演算法樹的概念
樹(英語:tree)是一種抽象資料類型(ADT)或是實作這種抽象資料類型的資料結構,用來類比具有樹狀結構性質的資料集合。它是由n(n>=1)個有限節點群組成一個具有層次關係的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:
- 每個節點有零個或多個子節點;
- 沒有父節點的節點稱為根節點;
- 每一個非根節點有且只有一個父節點;
- 除了根節點外,每個子節點可以分為多個不相交的子樹;
樹的術語
- 節點的度:一個節點含有的子樹的個數稱為該節點的度;
- 樹的度:一棵樹中,最大的節點的度稱為樹的度;
- 分葉節點或終端節點:度為零的節點;
- 父親節點或父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點;
- 孩子節點或子節點:一個節點含有的子樹的根節點稱為該節點的子節點;
- 兄弟節點:具有相同父節點的節點互稱為兄弟節點;
- 節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
- 樹的高度或深度:樹中節點的最大層次;
- 堂兄弟節點:父節點在同一層的節點互為堂兄弟;
- 節點的祖先:從根到該節點所經分支上的所有節點;
- 子孫:以某節點為根的子樹中任一節點都稱為該節點的子孫。
- 森林:由m(m>=0)棵互不相交的樹的集合稱為森林;
樹的種類
- 無序樹:樹中任意節點的子節點之間沒有循序關聯性,這種樹稱為無序樹,也稱為自由樹;
- 有序樹:樹中任意節點的子節點之間有循序關聯性,這種樹稱為有序樹;
- 二叉樹:每個節點最多含有兩個子樹的樹稱為二叉樹;
- 完全二叉樹:對於一顆二叉樹,假設其深度為d(d>1)。除了第d層外,其它各層的節點數目均已達最大值,且第d層所有節點從左向右連續地緊密排列,這樣的二叉樹被稱為完全二叉樹,其中滿二叉樹的定義是所有分葉節點都在最底層的完全二叉樹;
- 平衡二叉樹(AVL樹):若且唯若任何節點的兩棵子樹的高度差不大於1的二叉樹;
- 排序二叉樹(二叉尋找樹(英語:Binary Search Tree),也稱二叉搜尋樹、有序二叉樹);
- 霍夫曼樹(用於資訊編碼):帶權路徑最短的二叉樹稱為哈夫曼樹或最優二叉樹;
- B樹:一種對讀寫操作進行最佳化的自平衡的二叉尋找樹,能夠保持資料有序,擁有多餘兩個子樹
二叉樹的基本概念
二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)
二叉樹的性質(特性)
性質1: 在二叉樹的第i層上至多有2^(i-1)個結點(i>0)
性質2: 深度為k的二叉樹至多有2^k - 1個結點(k>0)
性質3: 對於任意一棵二叉樹,如果其葉結點數為N0,而度數為2的結點總數為N2,則N0=N2+1;
性質4:具有n個結點的完全二叉樹的深度必為 log2(n+1)
性質5:對完全二叉樹,若從上至下、從左至右編號,則編號為i 的結點,其左孩子編號必為2i,其右孩子編號必為2i+1;其雙親的編號必為i/2(i=1 時為根,除外)
class Node(object): ‘‘‘節點類‘‘‘ def __init__(self, elem, lchild = None, rchild = None): self.elem = elem self.lchild = lchild self.rchild = rchildclass Tree(object): ‘‘‘樹類‘‘‘ def __init__(self, root = None): self.root = root def add(self, elem): ‘‘‘為樹添加節點‘‘‘ node = Node(elem) # 如果是空樹,對根節點進行賦值 if self.root == None: self.root = node return else: queue = [] queue.append(self.root) # 對已有節點進行層次遍曆 while queue: # 彈出隊列的第一個元素 cur = queue.pop(0) if cur.lchild is None: cur.lchild = node return elif cur.rchild is None: cur.rchild = node return else: # 如果左右節點不為空白,排入佇列繼續判斷 queue.append(cur.lchild) queue.append(cur.rchild)
二叉樹的遍曆
樹的遍曆是樹的一種重要的運算。所謂遍曆是指對樹中所有結點的資訊的訪問,即依次對樹中每個結點訪問一次且僅訪問一次,我們把這種對所有節點的訪問稱為遍曆(traversal)。那麼樹的兩種重要的遍曆模式是深度優先遍曆和廣度優先遍曆,深度優先一般用遞迴,廣度優先一般用隊列。一般情況下能用遞迴實現的演算法大部分也能用堆棧來實現。
深度優先遍曆
對於一顆二叉樹,深度優先搜尋(Depth First Search)是沿著樹的深度遍曆樹的節點,儘可能深的搜尋樹的分支。
那麼深度遍曆有重要的三種方法。這三種方式常被用於訪問樹的節點,它們之間的不同在於訪問每個節點的次序不同。這三種遍曆分別叫做先序遍曆(preorder),中序遍曆(inorder)和後序遍曆(postorder)。我們來給出它們的詳細定義,然後舉例看看它們的應用。
- 先序遍曆 在先序遍曆中,我們先訪問根節點,然後遞迴使用先序遍曆訪問左子樹,再遞迴使用先序遍曆訪問右子樹
根節點->左子樹->右子樹
def preorder(self, root): """遞迴實現先序遍曆""" if root == None: return print(root.elem) self.preorder(root.lchild) self.preorder(root.rchild)
- 中序遍曆 在中序遍曆中,我們遞迴使用中序遍曆訪問左子樹,然後訪問根節點,最後再遞迴使用中序遍曆訪問右子樹 左子樹->根節點->右子樹
def inorder(self, root): """遞迴實現中序遍曆""" if root == None: return self.inorder(root.lchild) print(root.elem) self.inorder(root.rchild)
- 後序遍曆 在後序遍曆中,我們先遞迴使用後序遍曆訪問左子樹和右子樹,最後訪問根節點 左子樹->右子樹->根節點
def postorder(self, root): """遞迴實現後續遍曆""" if root == None: return self.postorder(root.lchild) self.postorder(root.rchild) print (root.elem)
廣度優先遍曆(層次遍曆)
從樹的root開始,從上到下從從左至右遍曆整個樹的節點
def breadth_travel(self, root): """利用隊列實現樹的層次遍曆""" if root == None: return queue = [] queue.append(root) while queue: node = queue.pop(0) print (node.elem) if node.lchild != None: queue.append(node.lchild) if node.rchild != None: queue.append(node.rchild)
在路上---學習篇(一)Python 資料結構和演算法 (5)二分尋找、二叉樹遍曆