算法和數據結構

day01

課程的重要性

  • 應付面試
    • 數據結構和算法
  • 講解的內容
    • 數據結構
      • 棧,隊列,鏈表,二叉樹
    • 算法:
      • 二分查找
      • 選擇排序
      • 冒泡排序
      • 插入排序
      • 希爾排序
      • 快速排序
      • 二叉樹排序

什麼是計算機科學?

  • 首先明確的一點就是計算機科學不只僅是對計算機的研究,雖然計算機在科學發展的過程當中發揮了重大的做用,可是它只是一個工具,一個沒有靈魂的工具而已。所謂的計算機科學其實是對問題、解決問題以及解決問題的過程當中產生產生的解決方案的研究。例如給定一個問題,計算機科學家的目標是開發一個算法來處理該問題,最終獲得該問題的解、或者最優解。因此說計算機科學也能夠被認爲是對算法的研究。所以咱們也能夠感覺到,所謂的算法就是對問題進行處理且求解的一種實現思路或者思想。

如何形象化的理解算法

  • 一個常勝將軍在做戰以前都會進行戰略的制定,目的是爲了可以在最短的時間切成本消耗最低的狀況下獲取最終的勝利。若是將編碼做爲戰場,則程序員就是這場戰役的指揮官,你如何能夠將你的程序能夠在最短且消耗資源最小的狀況下獲取最終的執行結果呢?算法就是咱們的策略!

意義

  • 數據結構和算法思想的通用性異常的強大,在任何語言中都被使用,它們將會是咱們編碼生涯中伴隨咱們最長久利器(左膀右臂)。有必定經驗的程序員最終拼的就是算法和數據結構。
  • 數據結構和算法思想也能夠幫助咱們拓展和歷練編碼的思惟,可讓咱們更好的融入到編程世界的角角落落。

什麼是算法分析?

  • 剛接觸編程的學生常常會將本身編寫的程序和別人的程序作比對,或許在比對的過程當中會發現雙方編寫的程序很類似但又各不相同。那麼就會出現一個有趣的現象:兩組程序都是用來解決同一個問題的,可是兩組程序看起來又各不相同,那麼哪一組程序更好呢?
  • a+b+c = 1000 a2 + b2 = c**2 (a,b,c均爲天然數),求出a,b,c可能的組合?
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)
for a in range(0,1001):
    for b in range(0,1001):
        c = 1000-a-b
        if a+b+c == 1000 and a**2+b**2 == c**2:
                print(a,b,c)

評判程序優劣方法

  • 消耗計算機資源和執行效率(沒法直觀)
  • 計算算法執行的耗時(不推薦,由於會受機器和執行環境的影響)
  • 時間複雜度(推薦)

時間複雜度:衡量算法性能的好壞優劣

  • 評判規則:量化算法執行的操做/執行步驟的數量
  • 最重要的項:時間複雜度表達式中最有意義的項
    • 使用大O記法來標準化的表示執行步驟的數量
    • O(量化步驟表達式中最有意義的項)
def sumOfN(n):
    theSum = 0  #1
    for i in range(1,n+1):
        theSum = theSum + i #n

    return theSum #1

print(sumOfN(10))

#n+2
#O(n)
  • 案例:計算下列算法的時間複雜度
a=5
b=6
c=10
for i in range(n):
   for j in range(n):
      x = i * i
      y = j * j
      z = i * j
for k in range(n):
   w = a*k + 45
   v = b*b
d = 33

#4+3n**2+2n
#O(n**2)
  • 常見的時間複雜度:
    • O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

數據結構

  • 概念:對於數據(基本類型的數據(int,float,char))的組織方式就被稱做爲數據結構。數據結構解決的就是一組數據如何進行保存,保存形式是怎樣的。html

  • 案例: 須要存儲一些學生的學生信息(name,score),那麼這些數據應該如何組織呢?查詢某一個具體學生的時間複雜度是什麼呢?(三種組織方式)node

[{
    'name':'xxx',
    'score':'xxx'
},{
    'name':'xxx',
    'score':'xxx'
},{
    'name':'xxx',
    'score':'xxx'
}]
[{'name': 'xxx', 'score': 'xxx'},
 {'name': 'xxx', 'score': 'xxx'},
 {'name': 'xxx', 'score': 'xxx'}]
[
    ('name','score'),
    ('name','score'),
    ('name','score')
]
[('name', 'score'), ('name', 'score'), ('name', 'score')]
{
    'zhangsan':{'score':'xxx'},
    'lisi':{'score':'xxx'}
}
{'zhangsan': {'score': 'xxx'}, 'lisi': {'score': 'xxx'}}
  • 三種組織形式基於查詢的時間複雜度?python

  • 使用不一樣的形式組織數據,在基於查詢時的時間複雜度是不同的。所以認爲算法是爲了解決實際問題而設計的,數據結構是算法須要處理問題的載體。程序員

目標

  • 本節的目標是告訴你們Python列表和字典操做的 大O 性能。而後咱們將作一些基於時間的實驗來講明每一個數據結構的花銷和使用這些數據結構的好處

實操

  • 在列表的操做有一個很是常見的編程任務就是是增長一個列表。咱們立刻想到的有兩種方法能夠建立更長的列表,可使用 append 方法或拼接運算符。可是這兩種方法那種效率更高呢。這對你來講很重要,由於它能夠幫助你經過選擇合適的工具來提升你本身的程序的效率。web

  • 實例化一個空列表,而後將0-n範圍的數據添加到列表中。(四種方式)面試

def test01():
    alist = []
    for i in range(0,100):
        alist += [i]
    return alist

def test02():
    alist = []
    for i in range(0,100):
        alist.append(i)
    return alist

if __name__ == '__main__':
    pass
  • timeit模塊:該模塊能夠用來測試一段python代碼的執行速度/時長。算法

  • Timer類:該類是timeit模塊中專門用於測量python代碼的執行速度/時長的。原型爲:class timeit.Timer(stmt='pass',setup='pass')。編程

    • stmt參數:表示即將進行測試的代碼塊語句。數組

    • setup:運行代碼塊語句時所須要的設置。瀏覽器

    • timeit函數:timeit.Timer.timeit(number=100000),該函數返回代碼塊語句執行number次的平均耗時。

from timeit import Timer
def test01():
    alist = []
    for i in range(0,100):
        alist += [i]
    return alist

def test02():
    alist = []
    for i in range(0,100):
        alist.append(i)
    return alist

if __name__ == '__main__':
    timer = Timer('test01()','from __main__ import test01')
    print(timer.timeit(1000))
    
    t = Timer('test02()','from __main__ import test02')
    print(t.timeit(1000))
0.02650774001085665
0.02491867200296838
  • 四種方式中哪一種方式添加列表元素的效率最高呢?
    • 計算運行平均耗時

  • 特性:先進後出的數據結構

  • 棧頂,棧尾

  • 應用:每一個 web 瀏覽器都有一個返回按鈕。當你瀏覽網頁時,這些網頁被放置在一個棧中(實際是網頁的網址)。你如今查看的網頁在頂部,你第一個查看的網頁在底部。若是按‘返回’按鈕,將按相反的順序瀏覽剛纔的頁面。

  • Stack() 建立一個空的新棧。 它不須要參數,並返回一個空棧。

  • push(item)將一個新項添加到棧的頂部。它須要 item 作參數並不返回任何內容。

  • pop() 從棧中刪除頂部項。它不須要參數並返回 item 。棧被修改。

  • isEmpty() 測試棧是否爲空。不須要參數,並返回布爾值。

  • size() 返回棧中的 item 數量。不須要參數,並返回一個整數。

class Stack():
    def __init__(self): # 實例化一個空棧
        self.items = [] # 容器
    def push(self,item): # item就是向棧中添加的元素(從棧頂添加到棧底)
        self.items.append(item)
    def pop(self): # 彈出棧頂的元素
        return self.items.pop()
    def isEmpty(self): # 是否爲空
        return self.items == []
    def size(self): # 棧的大小
        return len(self.items)
stack = Stack() # 實例化一個空棧
stack.push(1)
stack.push(2)
stack.push(3)

print(stack.pop())
print(stack.pop())
print(stack.pop())
"""
3
2
1
"""

隊列

  • 隊列:先進先出
  • 應用場景:
    • 咱們的計算機實驗室有 30 臺計算機與一臺打印機聯網。當學生想要打印時,他們的打印任務與正在等待的全部其餘打印任務「一致」。第一個進入的任務是先完成。若是你是最後一個,你必須等待你前面的全部其餘任務打印
  • Queue() 建立一個空的新隊列。 它不須要參數,並返回一個空隊列。
  • enqueue(item) 將新項添加到隊尾。 它須要 item 做爲參數,並不返回任何內容。
  • dequeue() 從隊首移除項。它不須要參數並返回 item。 隊列被修改。
  • isEmpty() 查看隊列是否爲空。它不須要參數,並返回布爾值。
  • size() 返回隊列中的項數。它不須要參數,並返回一個整數。
class Queue():
    def __init__(self):
        self.items = []
    def enqueue(self,item):
        self.items.insert(0,item)
    def dequeue(self):
        return self.items.pop()
    def isEmpty(self):
        return self.items == []
    def size(self):
        return len(self.items)
    
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)

print(q.dequeue())
print(q.dequeue())
print(q.dequeue())

"""
1
2
3
"""

案例:燙手的山芋

  • 燙手山芋遊戲介紹:6個孩子圍城一個圈,排列順序孩子們本身指定。第一個孩子手裏有一個燙手的山芋,須要在計時器計時1秒後將山芋傳遞給下一個孩子,依次類推。規則是,在計時器每計時7秒時,手裏有山芋的孩子退出遊戲。該遊戲直到剩下一個孩子時結束,最後剩下的孩子獲勝。請使用隊列實現該遊戲策略,排在第幾個位置最終會獲勝。
  • 關鍵因素:
    • 必需要保證手裏有山芋的孩子永遠在隊頭的位置
      • 結論:
        • 計時器計時7秒或者山芋被傳遞6次則一輪遊戲結束,淘汰一個孩子,遊戲繼續。
"""
思路:
七秒傳遞六次,
每一次傳遞時都將獲得山芋的孩子移到隊列首(也就是將上一個孩子從隊列中移除放在隊列末),
判斷只剩一個隊列元素時,循環結束
"""

kids = ['A', 'B', 'C', 'D', 'E', 'F'] # 人員
kids_queue = Queue() # 建立隊列
for kid in kids:
    kids_queue.enqueue(kid) # 加入隊列

while kids_queue.size() > 1:
    for i in range(6): # 傳遞次數
        first_kid = kids_queue.dequeue() # 移除
        kids_queue.enqueue(first_kid) # 加入
    kids_queue.dequeue() # 淘汰

print(kids_queue.dequeue())

# E

內存

  • 計算機的做用

    • 存儲和運算二進制的數據。
  • 問題:計算機如何計算1+2?

    • 首先須要將1和2這兩個數值加載存儲到計算機的內存中(必須保證計算機有足夠的內存對數據進行存儲)
    • 基於加法寄存器對相關的內存數據進行加法運算
  • 變量的概念

    • 計算機中的一塊內存空間就是變量
    • 內存空間有兩個默認的屬性
      • 大小

        • 決定這塊內存空間能夠存儲多大的數值
        • 衡量內存空間大小的單位
          • bit(位):1個bit值能夠存放一位二進制的數據
          • byte(字節):8bit
          • kb:1024byte
          • mb:1024kb
          • gb:1024mb
          • t:1024g
      • 地址

        • 指定內存空間的定位,(16進制的數來表示)
  • 理解a=10的內存圖(引用,指向)

    • 引用:變量。a=10,a就是變量或者引用。a實際存儲的是10對應內存空間的地址
      image.png

    • 指向:若是某一個引用存儲的是一塊內存空間的地址,則該引用就指向了該塊內存空間

      • a=10,a指向了10,a存儲的是10所在的內存空間的地址
  • 不一樣數據佔用內存空間的大小

    • a = 10,a=3.3,a='bobo'
    • 整數:4byte
    • 浮點數:8byte
    • 字符串:一個字符佔用一個字節

順序表

  • 集合中存儲的元素是有順序的,順序表的結構能夠分爲兩種形式:單數據類型(數組)和多數據類型(列表)。

  • python中的列表和元組就屬於多數據類型的順序表

  • 單數據類型順序表的內存圖(內存連續開啓)
    image.png

  • 多數據類型順序表的內存圖(內存非連續開闢)

  • 順序表的弊端:順序表的結構須要預先知道數據大小來申請連續的存儲空間,而在進行擴充時又須要進行數據的搬遷。

鏈表:

相對於順序表,鏈表結構能夠充分利用計算機內存空間,實現靈活的內存動態管理且進行擴充時不須要進行數據搬遷。

  • 鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,可是不像順序表同樣連續存儲數據,而是每個結點(數據存儲單元)裏存放下一個結點的信息(即地址)

image.png

#節點的數據結構封裝
class Node():
    def __init__(self,item):
        self.item = item
        self.next = None #指向下一個節點的地址
node = Node('A') #實例化了一個節點對象,該節點存儲的數據爲A,next爲None
# 封裝鏈表
class Link():
    def __init__(self): # 構建空鏈表
        self._head = None # 空離鏈表_head指向None
    
    def add(self, item):# 在鏈表頭部插入一個節點
        node = Node(item)
        node.next = self._head # 將新插入的節點的next指向以前的頭節點
        self._head = node # _head永遠指向頭節點
        
    def is_Empty(self):
        return self._head == None
    
    def travel(self): # 遍歷鏈表 循環查找並打印item值, 直到next爲None
        cur = self._head # 新賦值一個臨時變量保存_head的值, 防止改變_head的指向
        while cur:
            print(cur.item)
            cur = cur.next
    
    def length(self): # 統計節點的數量
        count = 0
        cur = self._head
        while cur:
            count += 1
            cur = cur.next
        return count
    
    def search(self, item): # 查看鏈表中是否有此節點,返回布爾值
        cur = self._head
        find = False
        while cur:
            if cur.item == item:
                find = True
                break
            cur = cur.next
        return find
    
    def append(self, item): # 向鏈表尾部添加一個節點
        node = Node(item)
        cur = self._head
        pre = cur # 建立臨時變量用來保存cur前一個節點的信息
        while cur: # 在中止循環時,cur已經指向了None
            pre = cur
            cur = cur.next
        pre.next = node # 將最後一個節點的next指向新的節點
    
    def insert(self,pos,item):#pos用整數值
        node = Node(item)
        if self._head == None:#鏈表爲空
            #將插入的節點做爲鏈表的第一個節點便可
            self._head = node
            return 
        
        
        #若是插入的位置大於鏈表的長度或者小於0
        if pos > self.length():
            self.append(item)
            return
        if pos < 0:
            self.add(item)
            return
        #分析:必須找到插入位置對應的節點和其前一個節點就能夠實現插入了
        pre = None #cur前一個節點
        cur = self._head #當前節點
        
        for i in range(pos): #循環pos次(控制pre和cur向後偏移)
            pre = cur
            cur = cur.next
            
        pre.next = node
        node.next = cur
            
                
    def remove(self,item):
        if self._head == None:
            return #鏈表爲空則直接結束程序
        
        #須要定位到刪除的節點和其前一個節點
        cur = self._head
        pre = None
        
        #若是刪除的是第一個節點則須要單獨作處理
        if cur.item == item:
            self._head = cur.next #讓head指向第二個節點
            return
        #刪除除了第一個節點其餘的節點操做
        while True:
            pre = cur
            cur = cur.next
            if cur == None:#沒有找到刪除的節點.cur==None遍歷到最後一個節點
                return
            if cur.item == item:#找到了刪除的節點
                break 
            
        #定位到了刪除的節點cur和其前一個節點pre
        pre.next = cur.next
            
        
            
link = Link()#構建了一個新的空鏈表
link.add('D')
link.add('C')
link.add('B')
link.add('A')
link.append('E')
link.insert(0, 'F')
link.insert(5, 'G')
link.remove('F')
link.travel()
A
B
C
D
E

. is_empty():鏈表是否爲空

. length():鏈表長度

. travel():遍歷整個鏈表

. add(item):鏈表頭部添加元素

. append(item):鏈表尾部添加元素

. insert(pos, item):指定位置添加元素

. remove(item):刪除節點

. search(item):查找節點是否存在

day02

二叉樹:一個根節點只能夠有兩個子節點(兩個分叉)

  • 節點:
    • 視爲鏈表中的節點
  • 根節點
    • 就是樹種最上面的第一個節點
  • 左葉子節點
  • 右葉子節點
  • 子樹
    • 完整子樹
      • 一個跟節點和兩個完整的子節點
    • 非完整子樹
  • 二叉樹的遍歷
    • 深度遍歷:縱向遍歷。二叉樹中任意的一個節點均可以做爲子樹的根節點。
      • 跟,左,右特值的是子樹中的相關位置的節點
        • 前序:跟左右:二叉樹中全部子樹按照指定順序進行節點的遍歷
        • 中序:左跟右
        • 後序:左右跟
    • 廣度遍歷:
      • 橫向遍歷
class Node():
    def __init__(self,item):
        self.item = item
        #將left和right當成鏈表中的next
        self.left = None #指向該節點的左葉子節點
        self.right = None#指向該節點的右葉子節點
        
class Tree():
    def __init__(self):#構造一個空樹
        self.root = None#root就能夠視爲鏈表中的head,root永遠要指向二叉樹的根節點
    def addNode(self,item):
        #實例化一個新的節點對象
        node = Node(item)
        
        #若是樹爲空,則插入的節點爲根節點
        if self.root == None:#樹爲空
            self.root = node
            return
        #樹爲非空
        cur = self.root
        node_list = [cur] #將根節點加入到列表中,該列表中存放的爲左右葉子節點部位空的節點
        
        while True:
            p_node = node_list.pop(0)
            #非空的節點須要再次加入到node_list列表中
            if p_node.left != None:
                node_list.append(p_node.left)
            else:
                p_node.left = node
                break
            if p_node.right != None:
                node_list.append(p_node.right)
            else:#當前節點的右葉子節點爲空
                p_node.right = node
                break
                
    def travel(self):
        if self.root == None:#樹爲空
            print('空樹')
            return
        cur = self.root
        node_list = [cur]
        print(cur.item)
        while node_list:
            p_node = node_list.pop(0)

            if p_node.left != None:
                print(p_node.left.item)
                node_list.append(p_node.left)

            if p_node.right != None:
                print(p_node.right.item)
                node_list.append(p_node.right)
    #深度遍歷
    def forward(self,root):#root表示的是某一個子樹的根節點
        #結束遞歸的條件
        if root == None:
            return
        #跟,左,右
        print(root.item)
        #打印子樹的左節點(另外一個子樹的根節點)
        self.forward(root.left) 
        #右
        self.forward(root.right)
        
    def middle(self,root):
        if root == None:
            return
        self.middle(root.left)
        print(root.item)
        self.middle(root.right)
        
    def back(self,root):
        if root == None:
                return
        self.back(root.left)
        self.back(root.right)
        print(root.item)
        
tree = Tree() #實例化了一顆空樹。tree.root == None
tree.addNode(1)
tree.addNode(2)
tree.addNode(3)
tree.addNode(4)
tree.addNode(5)
tree.addNode(6)

tree.back(tree.root)
  • 遞歸的案例:
    • 斐波那契序列
    • n的階乘

排序二叉樹

  • 深度遍歷主要是做用在排序二叉樹種
    • 中序遍歷做用在排序二叉中中,則遍歷出來的結果就是一個有序的數列
class Node():
    def __init__(self,item):
        self.item = item
        #將left和right當成鏈表中的next
        self.left = None #指向該節點的左葉子節點
        self.right = None#指向該節點的右葉子節點
        
class SortTree():
    def __init__(self):
        self.root = None
    def insert(self,item):
        node = Node(item)
        if self.root == None:#樹爲空則插入的第一個節點爲跟節點
            self.root = node
            return
        #樹爲非空
        cur = self.root
        #將插入的節點和根節點進行大小比較
        while True:
            if item >= cur.item:#插入的節點大於根節點,該節點應該插入到根節點的右側
                #再次進行判斷:若是根節點的右葉子節點爲空,則將item插入到右葉子節點的位置
                if cur.right == None:
                    cur.right = node
                    return
                else:#根節點的右葉子節點不爲空
                    cur = cur.right
            else:#插入的節點小於根節點,該節點插入到根節點的左側
                if cur.left == None:
                    cur.left = node
                    return
                else:
                    cur = cur.left
    def middle(self,root):
        if root == None:
            return
        self.middle(root.left)
        print(root.item)
        self.middle(root.right)
        
        
tree = SortTree()
alist = [3,8,5,7,6,2,1,0]
for i in alist:
    tree.insert(i)
tree.middle(tree.root) #只有中序遍歷在排序二叉樹中遍歷的結果爲有序結果

day03

二分查找

def find(alist,item):
    #left&right表示序列的起始位置的下標
    left = 0
    right = len(alist)-1
    
    isFind = False#是否找到的標識
    
    while left <= right:
        mid_index = (right+left)//2 #序列中間元素的下標(序列中元素的個數整除2)

        #須要使用中間元素和要找尋的item進行比較
        if alist[mid_index] > item: #中間元素的值大於了要找尋的item的值。結論:item必定是存在於中間元素的左側
            #將中間元素左側做爲一個新序列
            right = mid_index - 1
        else:#中間元素小於等於找尋的item
            if alist[mid_index] == item:#找到了
                isFind = True
                break
            else:#找的值大於中間元素,找尋的值在中間元素的右側
                left = mid_index + 1 #將中間元素右側做爲了新的子序列
    
    return isFind

alist = [1,2,3,4,5,6]
print(find(alist,41))

冒泡排序

#將亂序序列中的最大值找出逐步偏移(讓兩兩元素進行大小比較,大的元素逐步向後移動)到最後
def sort(alist):
    length = len(alist)
    #讓兩兩元素進行比較(n-1)
    for i in range(0,length-1):
        if alist[i] > alist[i+1]:#第一個元素大於第二個元素
            #兩個元素交換位置
            alist[i],alist[i+1] = alist[i+1],alist[i]
    return alist
        
alist = [3,8,5,2,0,7,6]
print(sort(alist))
[3, 5, 2, 0, 7, 6, 8]
#冒泡排序完整代碼
#上述代碼操做須要做用n-1才能夠將整個序列變成有序的
def sort(alist):
    length = len(alist)
    
    for j in range(0,length-1): 
        #讓兩兩元素進行比較(n-1)
        for i in range(0,length-1-j):
            if alist[i] > alist[i+1]:#第一個元素大於第二個元素
                #兩個元素交換位置
                alist[i],alist[i+1] = alist[i+1],alist[i]
    return alist

alist = [3,8,5,2,0,7,6]
print(sort(alist))
[0, 2, 3, 5, 6, 7, 8]

選擇排序

#將亂序序列中的最大值直接找出,而後將最大值和序列最後一個元素交換位置,最終咱們就將最大值找出放置到了最後的位置
def sort(alist):
    max_index = 0 #表示的是最大值的下標,一開始咱們假設列表中的第0個元素爲最大值
    for i in range(len(alist)-1):#控制比較的次數
        if alist[max_index] < alist[i+1]:
            max_index = i+1 
    #將最大值和最後一個元素交換位置,就能夠將最大值放置到序列的最後位置
    alist[max_index],alist[len(alist)-1] = alist[len(alist)-1],alist[max_index]
    
    return alist
    
    
alist = [3,8,5,2,0,7,6]
print(sort(alist))
[3, 6, 5, 2, 0, 7, 8]
#完整代碼
def sort(alist):
    for j in range(0,len(alist)-1):
        max_index = 0 #表示的是最大值的下標,一開始咱們假設列表中的第0個元素爲最大值
        for i in range(len(alist)-1-j):#控制比較的次數
            if alist[max_index] < alist[i+1]:
                max_index = i+1 
        #將最大值和最後一個元素交換位置,就能夠將最大值放置到序列的最後位置
        alist[max_index],alist[len(alist)-1-j] = alist[len(alist)-1-j],alist[max_index]

    return alist
    
    
alist = [3,8,5,2,0,7,6]
print(sort(alist))
[0, 2, 3, 5, 6, 7, 8]

插入排序

  • 須要將原始的序列僞裝拆分紅兩部分

    • 有序部分:默認爲序列中的第一個元素
    • 無序部分:默認爲序列中除了第一個元素剩下的元素
    • 關鍵:將無序部分的元素逐一插入到有序部分中便可
  • 定義一個變量叫作i

    • i表示的是有序部分元素的個數
    • i還能夠表示無序部分中第一個元素的下標
  • 原始序列:[3,8,5,2,6,10,1]

  • [3, 8,5,2,6,10,1] ==》i=1

  • [3,8, 5,2,6,10,1] ==》i=2

#[3,   8,5,2,6,10,1]  ==》i=1
i = 1 #i表示的是有序部分元素的個數
#alist[i]:無序部分的第一個元素
#alist[i-1]:有序部分的最後一個元素
if alist[i] < alist[i-1]: #有序部分的最後一個元素大於無序部分的第一個元素
    alist[i],alist[i-1] = alist[i-1],alist[i]
else:
    pass
#[6,8,   7,2,6,10,1]  ==》i=2
i = 2 #i表示的是有序部分元素的個數
while i > 0:
    if alist[i] < alist[i-1]:
        alist[i],alist[i-1] = alist[i-1],alist[i]#[6,5,8,    2,6,10,1]
        i -= 1 #i是不能夠爲負數
    else:
        break
#i的取值範圍是1-(n-1)
for i in range(1,len(alist)):
#i = 2 #有序部分有兩個元素
    while i > 0:
        if alist[i] < alist[i-1]:
            alist[i],alist[i-1] = alist[i-1],alist[i]#[6,5,8,    2,6,10,1]
            i -= 1 #i是不能夠爲負數
        else:
            break
#完整代碼
def sort(alist):
    #i的取值範圍是1-(n-1)
    for i in range(1,len(alist)):
    #i = 2 #有序部分有兩個元素
        while i > 0:
            if alist[i] < alist[i-1]:
                alist[i],alist[i-1] = alist[i-1],alist[i]#[6,5,8,    2,6,10,1]
                i -= 1 #i是不能夠爲負數
            else:
                break
    return alist
alist = [3,8,5,2,0,7,6]
print(sort(alist))
[0, 2, 3, 5, 6, 7, 8]

希爾排序(擴展)

快速排序

  • alist = [66,13,51,76,81,26,57,69,23]

  • 基數:

    • 默認狀況下序列中第一個元素做爲基數
    • 原始序列中比基數大的值放置到基數右側,比基數小的值放置到基數左側
  • 將列表中第一個元素設定爲基準數字,賦值給mid變量,而後將整個列表中比基準小的數值放在基準的左側,比基準到的數字放在基準右側。而後將基準數字左右兩側的序列在根據此方法進行排放。

  • 定義兩個指針,low指向最左側,high指向最右側

  • 而後對最右側指針進行向左移動,移動法則是,若是指針指向的數值比基準小,則將指針指向的數字移動到基準數字原始的位置,不然繼續移動指針。

  • 若是最右側指針指向的數值移動到基準位置時,開始移動最左側指針,將其向右移動,若是該指針指向的數值大於基準則將該數值移動到最右側指針指向的位置,而後中止移動。

  • 若是左右側指針重複則,將基準放入左右指針重複的位置,則基準左側爲比其小的數值,右側爲比其大的數值。

#原始序列中比基數大的值放置到基數右側,比基數小的值放置到基數左側
def sort(alist):
    #low&hight表示的序列的起始位置的下標
    low = 0
    high = len(alist)-1 
    
    mid = alist[low] #基數
    
    while low < high:
        #將原始序列中比基數小的值放置在基數的左側,比基數大的值放置在基數的右側
        #注意:一開始的時候先將high向左偏移
        while low < high:
            if alist[high] < mid: #high小於基數
                alist[low] = alist[high]
                break
            else:#基數若是小於了high,將high向左偏移
                high -= 1 #high想左偏移了覺得
        #low向右偏移
        while low <high:
            if alist[low] < mid:#若是low小於mid則讓low想右偏移
                low += 1#讓low向右偏移一位
            else:
                alist[high] = alist[low]
                break
    if low == high:#low和high重複,將基數賦值到low或者high的位置
        alist[low] = mid #alist[high] = mid
        
    return alist
#加入遞歸後完整的代碼
def sort(alist,left,right):
    #low&hight表示的序列的起始位置的下標
    low = left
    high = right 
    
    #結束遞歸的條件
    if low > high:
        return
    
    mid = alist[low] #基數
    
    while low < high:
        #將原始序列中比基數小的值放置在基數的左側,比基數大的值放置在基數的右側
        #注意:一開始的時候先將high向左偏移
        while low < high:
            if alist[high] < mid: #high小於基數
                alist[low] = alist[high]
                break
            else:#基數若是小於了high,將high向左偏移
                high -= 1 #high想左偏移了覺得
        #low向右偏移
        while low <high:
            if alist[low] < mid:#若是low小於mid則讓low想右偏移
                low += 1#讓low向右偏移一位
            else:
                alist[high] = alist[low]
                break
    if low == high:#low和high重複,將基數賦值到low或者high的位置
        alist[low] = mid #alist[high] = mid
    
    #指定操做做用到基數左側的子序列中
    sort(alist,left,low-1)
    #指定操做做用到基數右側的子序列中
    sort(alist,high+1,right)
    return alist
alist = [66,13,51,76,81,26,57,69,23]
print(sort(alist,0,len(alist)-1))
相關文章
相關標籤/搜索