算法與數據結構


算法引入

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
數據組裝方式 + 所支持的操作

順序表

用連續單元存儲數據(地址連續)

變量名指向起始地址

索引實際是從起始位置的偏移量

  1. 一體存儲 元素內置
  2. 分離存儲 元素外置
  3. 動態順序表(可以數據擴充)

順序表的操作

添加元素 末尾添加 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
排序二叉樹:

鏈式存儲

常見應用場景

  1. xml/html解析
  2. 路由協議
  3. mysql數據庫索引
  4. 文件系統結構

二叉樹

  1. 在二叉樹的第i層上至多有2^(i-1)個結點
  2. 深度為k的二叉樹至多有2^k-1個結點
  3. 對於任意一顆二叉樹, 如果其葉結點數為N, 則度數為2的節點總數為N+1
  4. 具有n個節點的完全二叉樹的深度必為log2(n+1)
  5. 對於完全二叉樹, 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)

根據 先序遍歷+中序遍歷 或 后序+中序 推導出一顆樹

  1. 先從先序/后序中找出root節點

  2. 在中序中找到root節點 分成兩半

  3. 先序中 安裝中序中的兩半 分開

  4. 左右子樹分別重復前三步操作


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM