經常使用排序

 

 

 

1、幾種排序的比較:

 

2、排序的代碼實現

 

一、冒泡排序

 給定一組隨機數字的數列,將它們按照從小到大的順序從新排列好。就像冒泡同樣,小的數不斷地向上漂浮,直到沒有爲止。html

排序基本思路:循環這個數列,將循環到的數字n1與下一個數字n2做出對比,若是n2>n1,那麼將兩個值換位,如此下去當第一次循環結束時,最小的數已經在最前面,重複這樣的循環就會將這組數按從小到大的順序排列好。node

li = [11,432,5,4576,234,3,324,5,876,456,235432,56]

for m in range(len(li)-1):
    for n in range(m+1, len(li)):
        if li[m] > li[n]:
            tmp = li[m]
            li[m] = li[n]
            li[n] = tmp

print li

測試排列5萬個小於2000的隨機數字,結果發現用時爲:118秒python

 

二、選擇排序

與冒泡排序相似,循環整個數組,將數組中的值與第一個數值作對比,並將較小的值放在第一位。第一次循環就能夠找出最小的一個值,以此類推下去,知道所有排列好。git

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import random,time


def selection_sort(array):
    for i in range(len(array)):
        for j in range(i, len(array)):
            if array[i] > array[j]:
                tmp = array[i]
                array[i] = array[j]
                array[j] = tmp


if __name__ == '__main__':
    # array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = []
    for i in range(50000):
        array.append(random.randrange(100000))


    time_start = time.time()
    selection_sort(array)
    time_end = time.time()
    ret = time_end-time_start
    print array
    print(ret)

 

測試排列5萬個小於2000的隨機數字,結果發現用時爲:115秒算法

選擇排序的優化數據庫

當咱們在循環中,判斷出後面的值與第一個值的大小,那麼先不作出交換,僅僅記錄下這個較小值的下標。當第一次循環完時才把最小值放置在最前面,這樣交換的次數更少了,速度就更快樂了。數組

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import random,time


def selection_sort(array):
    for i in range(len(array)):
        for j in range(i, len(array)):
            if array[i] > array[j]:
                tmp = array[i]
                array[i] = array[j]
                array[j] = tmp


if __name__ == '__main__':
    # array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = []
    for i in range(50000):
        array.append(random.randrange(2000))


    time_start = time.time()
    selection_sort(array)
    time_end = time.time()
    ret = time_end-time_start
    # print array
    print(ret)

測試排列5萬個小於2000的隨機數字,結果發現用時爲:105數據結構

 

 

三、插入排序(Insertion Sort)

排序基本思路:插入排序簡單的理解爲  從一組數據的第二個值n開始循環,經過n與前面數值對比大小,將n插入到前面合適的位置。經過不斷地向後循環對比大小,那麼前面一部分的數據始終是已經排序好的數組。這樣所有循環下來,就獲得了一個從小到大排列OK的數組。app

[77, 92, 67, 8, 6, 84, 55, 85, 43, 67]
[67, 77, 92, 8, 6, 84, 55, 85, 43, 67]
[8, 67, 77, 92, 6, 84, 55, 85, 43, 67]
[6, 8, 67, 77, 92, 84, 55, 85, 43, 67]
[6, 8, 67, 77, 84, 92, 55, 85, 43, 67]
[6, 8, 55, 67, 77, 84, 92, 85, 43, 67]
[6, 8, 55, 67, 77, 84, 85, 92, 43, 67]
[6, 8, 43, 55, 67, 77, 84, 85, 92, 67]
[6, 8, 43, 55, 67, 67, 77, 84, 85, 92]dom

 

# _*_coding:utf:8_*_

import random import time def sort_meth(source): for i in range(1, len(source)): currant_val = source[i]  # 獲取當前循環列表中的值
        position = i    # 獲取當前循環的次數
        while position > 0 and source[position-1] > currant_val:  # 當左邊的數大於大循環中的數時
 source[position] = source[position-1]  # 將小循環此刻的數賦值等於左邊較大的值
            position -= 1 source[position] = currant_val    # 最終小循環結束時,就是大循環的數應該插入的位置


if __name__ == '__main__': #array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = [] for i in range(50000): array.append(random.randrange(2000)) time_start = time.time() sort_meth(array) time_end = time.time() ret = time_end-time_start print(ret)
插入排序

測試排列5萬個小於2000的隨機數字,結果發現用時爲:115

 

四、快速排序(quick sort)

 設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(一般選用數組的第一個數)做爲關鍵數據,而後將全部比它小的數都放到它前面,全部比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變更


注:在待排序的文件中,若存在多個關鍵字相同的記錄,通過排序後這些具備相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具備相同關鍵字的記錄之間的相對次序發生改變,則稱這種排序方法是不穩定的
要注意的是,排序算法的穩定性是針對全部輸入實例而言的。即在全部可能的輸入實例中,只要有一個實例使得算法不知足穩定性要求,則該排序算法就是不穩定的。 

 

排序演示

假設用戶輸入了以下數組:
下標
0
1
2
3
4
5
數據
6
2
7
3
8
9
建立變量i=0(指向第一個數據), j=5(指向最後一個數據), k=6(賦值爲第一個數據的值)。
咱們要把全部比k小的數移動到k的左面,因此咱們能夠開始尋找比6小的數,從j開始,從右往左找,不斷遞減變量j的值,咱們找到第一個下標3的數據比6小,因而把數據3移到下標0的位置,把下標0的數據6移到下標3,完成第一次比較:
下標
0
1
2
3
4
5
數據
3
2
7
6
8
9
i=0 j=3 k=6
接着,開始第二次比較,此次要變成找比k大的了,並且要從前日後找了。遞加變量i,發現下標2的數據是第一個比k大的,因而用下標2的數據7和j指向的下標3的數據的6作交換,數據狀態變成下表:
下標
0
1
2
3
4
5
數據
3
2
6
7
8
9
i=2 j=3 k=6
稱上面兩次比較爲一個循環。
接着,再遞減變量j,不斷重複進行上面的循環比較。
在本例中,咱們進行一次循環,就發現i和j「碰頭」了:他們都指向了下標2。因而,第一遍比較結束。獲得結果以下,凡是k(=6)左邊的數都比它小,凡是k右邊的數都比它大:
下標
0
1
2
3
4
5
數據
3
2
6
7
8
9
若是i和j沒有碰頭的話,就遞加i找大的,尚未,就再遞減j找小的,如此反覆,不斷循環。注意判斷和尋找是同時進行的。
 
此時,數值k就將整個數組分割了左右兩份。分別對這左右兩份的數據再次執行上述的過程,直到不能分割爲止。
注意:第一遍快速排序不會直接獲得最終結果,只會把比k大和比k小的數分到k的兩邊。爲了獲得最後結果,須要再次對下標2兩邊的數組分別執行此步驟,而後再分解數組,直到數組不能再分解爲止(只有一個數據),才能獲得正確結果。

 

# _*_coding:utf-8_*_


import random
import time


def quick_sort(array, start, end):
    if start >= end:
        return

    left = start
    right = end
    value = array[left]

    while left < right:

        while left < right and array[right] > value:
            right -= 1

        array[left] = array[right]
        array[right] = value

        while left < right and array[left] <= value:
            left += 1

        array[right] = array[left]
        array[left] = value
    quick_sort(array, start, left-1)
    quick_sort(array, left+1, end)


if __name__ == '__main__':

    # array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = []
    for i in range(50000):
        array.append(random.randrange(2000))

    time_start = time.time()
    quick_sort(array, 0, len(array)-1)
    time_end = time.time()
    ret = time_end-time_start
    print(ret)

 

 測試排列5萬個小於2000的隨機數字,結果發現用時爲:0.233秒,

當看到這個結果,其實我是驚呆了!

 

 

五、二叉樹

樹的特徵和定義


  樹是一種重要的非線性數據結構,直觀地看,它是數據元素(在樹中稱爲結點)按分支關係組織起來的結構,很象天然界中的樹那樣。樹結構在客觀世界中普遍存在,如人類社會的族譜和各類社會組織機構均可用樹形象表示。樹在計算機領域中也獲得普遍應用,如在編譯源程序時,可用樹表示源程序的語法結構。又如在數據庫系統中,樹型結構也是信息的重要組織形式之一。一切具備層次關係的問題均可用樹來描述。
 

樹(Tree)是元素的集合。咱們先以比較直觀的方式介紹樹。下面的數據結構是一個樹:

樹有多個節點(node),用以儲存元素。某些節點之間存在必定的關係,用連線表示,連線稱爲邊(edge)。邊的上端節點稱爲父節點,下端稱爲子節點。樹像是一個不斷分叉的樹根。

每一個節點能夠有多個子節點(children),而該節點是相應子節點的父節點(parent)。好比說,3,5是6的子節點,6是3,5的父節點;1,8,7是3的子節點, 3是1,8,7的父節點。樹有一個沒有父節點的節點,稱爲根節點(root),如圖中的6。沒有子節點的節點稱爲葉節點(leaf),好比圖中的1,8,9,5節點。從圖中還能夠看到,上面的樹總共有4個層次,6位於第一層,9位於第四層。樹中節點的最大層次被稱爲深度。也就是說,該樹的深度(depth)爲4。

 

若是咱們從節點3開始向下看,而忽略其它部分。那麼咱們看到的是一個以節點3爲根節點的樹:

三角形表明一棵樹

再進一步,若是咱們定義孤立的一個節點也是一棵樹的話,原來的樹就能夠表示爲根節點和子樹(subtree)的關係:

 

上述觀察實際上給了咱們一種嚴格的定義樹的方法:

1. 樹是元素的集合。

2. 該集合能夠爲空。這時樹中沒有元素,咱們稱樹爲空樹 (empty tree)。

3. 若是該集合不爲空,那麼該集合有一個根節點,以及0個或者多個子樹。根節點與它的子樹的根節點用一個邊(edge)相連。

上面的第三點是以遞歸的方式來定義樹,也就是在定義樹的過程當中使用了樹自身(子樹)。因爲樹的遞歸特徵,許多樹相關的操做也能夠方便的使用遞歸實現。咱們將在後面看到。

 

樹的實現

樹的示意圖已經給出了樹的一種內存實現方式: 每一個節點儲存元素和多個指向子節點的指針。然而,子節點數目是不肯定的。一個父節點可能有大量的子節點,而另外一個父節點可能只有一個子節點,而樹的增刪節點操做會讓子節點的數目發生進一步的變化。這種不肯定性就可能帶來大量的內存相關操做,而且容易形成內存的浪費。

一種經典的實現方式以下:

樹的內存實現

擁有同一父節點的兩個節點互爲兄弟節點(sibling)。上圖的實現方式中,每一個節點包含有一個指針指向第一個子節點,並有另外一個指針指向它的下一個兄弟節點。這樣,咱們就能夠用統一的、肯定的結構來表示每一個節點。

 

計算機的文件系統是樹的結構,好比Linux文件管理背景知識中所介紹的。在UNIX的文件系統中,每一個文件(文件夾一樣是一種文件),均可以看作是一個節點。非文件夾的文件被儲存在葉節點。文件夾中有指向父節點和子節點的指針(在UNIX中,文件夾還包含一個指向自身的指針,這與咱們上面見到的樹有所區別)。在git中,也有相似的樹狀結構,用以表達整個文件系統的版本變化 (參考版本管理三國志)。

 
 

二叉樹 

二叉樹是由n(n≥0)個結點組成的有限集合、每一個結點最多有兩個子樹的有序樹。它或者是空集,或者是由一個根和稱爲左、右子樹的兩個不相交的二叉樹組成。

特色:

(1)二叉樹是有序樹,即便只有一個子樹,也必須區分左、右子樹;

(2)二叉樹的每一個結點的度不能大於2,只能取0、一、2三者之一;

(3)二叉樹中全部結點的形態有5種:空結點、無左右子樹的結點、只有左子樹的結點、只有右子樹的結點和具備左右子樹的結點。

 

二叉樹(binary)是一種特殊的樹。二叉樹的每一個節點最多隻能有2個子節點:

二叉樹

因爲二叉樹的子節點數目肯定,因此能夠直接採用上圖方式在內存中實現。每一個節點有一個左子節點(left children)和右子節點(right children)。左子節點是左子樹的根節點,右子節點是右子樹的根節點。

 

若是咱們給二叉樹加一個額外的條件,就能夠獲得一種被稱做二叉搜索樹(binary search tree)的特殊二叉樹。二叉搜索樹要求:每一個節點都不比它左子樹的任意元素小,並且不比它的右子樹的任意元素大。

(若是咱們假設樹中沒有重複的元素,那麼上述要求能夠寫成:每一個節點比它左子樹的任意節點大,並且比它右子樹的任意節點小)

二叉搜索樹,注意樹中元素的大小

二叉搜索樹能夠方便的實現搜索算法。在搜索元素x的時候,咱們能夠將x和根節點比較:

1. 若是x等於根節點,那麼找到x,中止搜索 (終止條件)

2. 若是x小於根節點,那麼搜索左子樹

3. 若是x大於根節點,那麼搜索右子樹

二叉搜索樹所須要進行的操做次數最多與樹的深度相等。n個節點的二叉搜索樹的深度最多爲n,最少爲log(n)。

 

二叉樹的遍歷

遍歷即將樹的全部結點訪問且僅訪問一次。按照根節點位置的不一樣分爲前序遍歷,中序遍歷,後序遍歷。

前序遍歷:根節點->左子樹->右子樹

中序遍歷:左子樹->根節點->右子樹

後序遍歷:左子樹->右子樹->根節點

例如:求下面樹的三種遍歷

 

前序遍歷:abdefgc

中序遍歷:debgfac

後序遍歷:edgfbca

 

二叉樹的類型

(1)徹底二叉樹——若設二叉樹的高度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有葉子結點,而且葉子結點都是從左到右依次排布,這就是徹底二叉樹
(2)滿二叉樹——除了葉結點外每個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
(3)平衡二叉樹——平衡二叉樹又被稱爲AVL樹(區別於AVL算法),它是一棵二叉排序樹,且具備如下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹

如何判斷一棵樹是徹底二叉樹?按照定義,

教材上的說法:一個深度爲k,節點個數爲 2^k - 1 的二叉樹爲滿二叉樹。這個概念很好理解,

就是一棵樹,深度爲k,而且沒有空位。

首先對滿二叉樹按照廣度優先遍歷(從左到右)的順序進行編號。

一顆深度爲k二叉樹,有n個節點,而後,也對這棵樹進行編號,若是全部的編號都和滿二叉樹對應,那麼這棵樹是徹底二叉樹。

 

image

 

 

如何判斷平衡二叉樹?

2008111712242127

(b)左邊的圖 左子數的高度爲3,右子樹的高度爲1,相差超過1

(b)右邊的圖 -2的左子樹高度爲0  右子樹的高度爲2,相差超過1

 

二叉樹遍歷實現

class TreeNode(object):
    def __init__(self,data=0,left=0,right=0):
        self.data = data
        self.left = left
        self.right = right
 
class BTree(object):
    def __init__(self,root=0):
        self.root = root
 
 
    def preOrder(self,treenode):
        if treenode is 0:
            return
        print(treenode.data)
        self.preOrder(treenode.left)
        self.preOrder(treenode.right)
    def inOrder(self,treenode):
        if treenode is 0:
            return
        self.inOrder(treenode.left)
        print(treenode.data)
        self.inOrder(treenode.right)
 
    def postOrder(self,treenode):
        if treenode is 0:
            return
        self.postOrder(treenode.left)
        self.postOrder(treenode.right)
        print(treenode.data)
if __name__ == '__main__':
    n1  = TreeNode(data=1)
    n2 = TreeNode(2,n1,0)
    n3 = TreeNode(3)
    n4 = TreeNode(4)
    n5 = TreeNode(5,n3,n4)
    n6 = TreeNode(6,n2,n5)
    n7 = TreeNode(7,n6,0)
    n8 = TreeNode(8)
    root = TreeNode('root',n7,n8)
 
    bt = BTree(root)
    print("preOrder".center(50,'-'))
    print(bt.preOrder(bt.root))
 
    print("inOrder".center(50,'-'))
    print (bt.inOrder(bt.root))
 
    print("postOrder".center(50,'-'))
    print (bt.postOrder(bt.root))
View Code
相關文章
相關標籤/搜索