數據結構與算法-ADT-Array

Array ADT

一維數組是連續元素的集合,其中的每一個元素均可以經過惟一的整數下標來存取。數組的大小在建立後不能修改。html

ADT 定義:node

  • Array(size): 建立一個長度爲 size 的一維數組,而且將每一個元素初始化成 None
  • length(): 返回數組中的元素個數
  • getitem(index): 返回指定下標的元素
  • setitem(index, value): 修改數組中 index 位置的元素值
  • clearing(value): 將數組中的全部元素值重置成 value
  • iterator(): 返回迭代器

Array 的實現

ctypes 模塊

Python 中的許多數據類型及類實際上都是基於低層 C 語言中的相關類型實現的。Python 標準庫中的 ctypes 模塊可用來訪問 C 語言中的各類類型及 C 類庫中的功能。使用 ctypes 提供的大多數功能都要求理解一些 C 語言知識。python

相似 Python 實現 string, list, tuple 和 dict,咱們經過 ctypes 建立一個數組,其中的元素都是 Python 對象引用:web

for i in range(5):
    slots[i] = None

這個數組必須先初始化後才能訪問,否則會拋出異常,如 slots[0] 會拋出異常,初始化以下:數組

import ctypes

class Array:
    def __init__( self, size ):
        assert size > 0, "Array size must be > 0"
        self._size = size
        PyArrayType = ctypes.py_object * size
        self._elements = PyArrayType()
        self.clear( None )

    def __len__( self ):
        return self._size

    def __getitem__( self, index ):
        assert index >= 0 and index < len(self), "Array subscript out of range"
        return self._elements[ index ]

    def __setitem__( self, index, val ):
        assert index >= 0 and index < len(self), "Array subscript out of range"
        self._elements[ index ] = val

    def clear( self, val ):
        for i in xrange( self._size ):
            self._elements[ i ] = val

    def __iter__( self ):
        return _ArrayGenerator( self._elements, self._size )

def _ArrayGenerator( elements, size ):
    for i in range( size ):
        yield elements[i]

Array 的實現以下:markdown

pyList = [4, 12, 2, 34, 17]

Python List

Python List 也是經過低層的 C 語言類型實現的,它是一個可修改的序列容器,其大小能夠隨着元素的添加和刪除自動改變。數據結構

建立一個 Python List

pyListA = [34, 12]
pyListB = [4, 6, 31, 9]
pyListA.extend(pyListB)

以上代碼將調用 list() 構造器,構造器將建立一個數組結構用來存儲列表中的元素。實際上初始建立的數組大小會大於所需的容量,這樣便於之後的擴展操做。app

用於存儲列表元素的數組其實是剛纔建立的數組中的一個子數組 subarraylen(lst) 返回該子數組的長度,而整個數組的長度爲 capacity。 用數組實現的 Python List 的抽象和物理視圖以下:ssh

listUsingArrayView.png

追加元素 append

當數組容量足夠時,新元素將追加到數組中,而且 list 的 length 域也相應增長。ide

當數組満時,List 會進行自動擴展,即:

  1. 新建一個更大容量的數組
  2. 將原數組的元素所有複製到新建的數組
  3. 將新建的數組設置爲 List 的數據結構
  4. 銷燬舊的數組

新建數組的大小是根據原數組的大小肯定的,好比說,新數組的大小定爲原數組大小的 2 倍 。 擴展後再在數組後追加元素。

擴充列表 extend

好比:

pyList.insert(3, 79)

當 pyListA 中的數組容量不夠時,List 會自動進行如 append 進行的相似數組擴展操做。

插入 insert

好比:

pyList.pop(0) # remove the first item
pyList.pop() # remove the last item

當位置 3 已經有元素時,位置 3 及其後面的全部元素都將後移一個位置,並在位置 3 插入新元素。當數組容器不夠,會進行和上面類似的數組擴展操做。

刪除 pop

好比:

class Array2D:
    def __init__( self, nrows, ncols ):
        # Create a 1-D array to store an array reference for each row.
        self._theRows = Array( nrows )

        # Create the 1-D arrays for each row of the 2-D array.
        for i in range( nrows ):
            self._theRows[i] = Array( ncols )

    def numRows( self ):
        return len( self._theRows )

    def numCols( self ):
        return len( self._theRows[0] )

    # Clears the array by setting every element to the given value.
    def clear( self, val ):
        for row in range( self.numRows() ):
            self._theRows[row].clear( val )

    def __getitem__( self, xy ):
        assert len( xy ) == 2, "Invalid number of array subscripts."
        row = xy[0]
        col = xy[1]
        assert row >= 0 and row < self.numRows() and \
            col >= 0 and col < self.numCols(), "Array subscript out of range."
        the1arr = self._theRows[row]
        return the1arr[col]

    def __setitem__( self, xy, val ):
        assert len( xy ) == 2, "Invalid number of array subscripts."
        row = xy[0]
        col = xy[1]
        assert row >= 0 and row < self.numRows() and \
            col >= 0 and col < self.numCols(), "Array subscript out of range."
        the1arr = self._theRows[row]
        the1arr[col] = val

刪除後,若是後面還有元素,那麼被刪除元素後面的全部元素將前移一個位置,以便填充刪除後的空位。

固然,若是刪除後的數組空位過多,也會進行相對應的收縮數組操做。

List Slice

Slice 會建立一個新的 List。

二維數組 two-dimensional array

它將數據組織成行和列,相似於表格。每一個元素經過兩個下標來存取。

Array2D ADT

  • Array2D(nrows, ncols): 建立一個 nrows 行,ncols 列的二維數組,並初始化每一個元素爲 None
  • numRows(): 返回行數
  • numCols(): 返回列數
  • clear(value): 將全部元素的值設爲 value
  • getitem(row, col): 經過下標法 y=x[1,2] 來訪問元素
  • setitem(row, col, value): 設置元素值

Array2D 的實現

一般有 2 種數據組織方法:

  • 使用一個一維數組,將行列上的每一個元素位置映射到數組的相應位置
  • 使用數組的數組實現

下面的實現採用了數組的數組方法,將二維數組中的每一行存儲在一維數組中,而後再建立一個數組,用來保存行數組(即該數組是數組的數組)。Array2D 的抽象和物理存儲視圖以下:

array2dView.png

有些語言的實現中,能夠存取每一個行,從而對每一個元素的訪問使用 x[r][c] 進行。爲了隱藏實現細節,咱們的實現不暴露行數組,從而對每一個元素的訪問使用 x[r,c] 進行。

實現以下:

class Array2D:
    def __init__( self, nrows, ncols ):
        # Create a 1-D array to store an array reference for each row.
        self._theRows = Array( nrows )

        # Create the 1-D arrays for each row of the 2-D array.
        for i in range( nrows ):
            self._theRows[i] = Array( ncols )

    def numRows( self ):
        return len( self._theRows )

    def numCols( self ):
        return len( self._theRows[0] )

    # Clears the array by setting every element to the given value.
    def clear( self, val ):
        for row in range( self.numRows() ):
            self._theRows[row].clear( val )

    def __getitem__( self, xy ):
        assert len( xy ) == 2, "Invalid number of array subscripts."
        row = xy[0]
        col = xy[1]
        assert row >= 0 and row < self.numRows() and \
            col >= 0 and col < self.numCols(), "Array subscript out of range."
        the1arr = self._theRows[row]
        return the1arr[col]

    def __setitem__( self, xy, val ):
        assert len( xy ) == 2, "Invalid number of array subscripts."
        row = xy[0]
        col = xy[1]
        assert row >= 0 and row < self.numRows() and \
            col >= 0 and col < self.numCols(), "Array subscript out of range."
        the1arr = self._theRows[row]
        the1arr[col] = val
實現元素的存取

__getitem__(self, index)__setitem__(self, index. value) 這兩個函數定義參數中, 只有一個 index 參數,但這不會限制只能使用一個下標。當使用多個下標時,如 y = x[i,j],多個下標會組合成一個 tuple 做爲 index 參數傳入。

Matrix ADT

矩陣是標量值的集合,這些值以行和列的形式組織成一個固定大小的矩形網格中。

  • Matrix(nrows, ncols): 建立一個 nrows 行和 ncols 列的矩陣
  • numRows(): 返回行數
  • numCols(): 返回列數
  • getitem(row, col): 返回元素
  • setitem(row, col, scalar): 設置元素值
  • scaleBy(scalar): 矩陣中的每一個元素都乘該值,操做後矩陣自己將被修改
  • transpose(): 返回一個轉置矩陣
  • add(rhsMatrix): 建立並返回一個新矩陣。這兩個矩陣大小必須相同
  • subtract(rhsMatrix): 相減
  • multiply(rhsMatrix): 相乘。

Matrix 的實現

通常用二維數組來實現矩陣。

實現以下:

from array import Array2D

class Matrix:
    def __init__( self, nrow, ncols ):
        self._theGrid = Array2D( nrow, ncols )
        self._theGrid.clear( 0 )

    def numRows( self ):
        return self._theGrid.numRows()

    def numCols( self ):
        return self._theGrid.numCols()

    def __getitem__( self, xy ):
        return self._theGrid[ xy[0], xy[1] ]

    def __setitem__( self, xy, scalar ):
        self._theGrid[ xy[0], xy[1] ] = scalar

    def scaleBy( self, scalar ):
        for r in xrange( self.numRows() ):
            for c in xrange( self.numCols() ):
                self[r, c] *= scalar

    def transpose( self ):
        newMatrix = Matrix( self.numCols(), self.numRows() )
        for r in xrange( self.numRows() ):
            for c in xrange( self.numCols() ):
                newMatrix[c, r] = self[r, c]
        return newMatrix

    def add( self, rhsMatrix ):
        assert rhsMatrix.numRows() == self.numRows() and \
            rhsMatrix.numCols() == self.numCols(), \
            "Matrix sizes not compatible for the add operation."

        newMatrix = Matrix( self.numRows(), self.numCols() )
        for r in xrange( self.numRows() ):
            for c in xrange( self.numCols() ):
                newMatrix[r, c] = self[r, c] + rhsMatrix[r, c]

        return newMatrix

    def subtract( self, rhsMatrix ):
        assert rhsMatrix.numRows() == self.numRows() and \
            rhsMatrix.numCols() == self.numCols(), \
            "Matrix sizes not compatible for the add operation."

        newMatrix = Matrix( self.numRows(), self.numCols() )
        for r in xrange( self.numRows() ):
            for c in xrange( self.numCols() ):
                newMatrix[r, c] = self[r, c] - rhsMatrix[r, c]

        return newMatrix

    def multiple( self, rhsMatrix ):
        assert self.numCols() == rhsMatrix.numRows(), \
            "Matrix sizes not compatible for the multiple operation."

        newMatrix = Matrix( self.numRows(), rhsMatrix.numCols() )

        for r in xrange( self.numRows() ):
            for rhsC in xrange ( rhsMatrix.numCols() ):
                tmp = 0
                for c in xrange( self.numCols() ):
                    tmp += self[r, c] * rhsMatrix[c, r]
                newMatrix[r, rhsC] = tmp

        return newMatrix

應用: 遊戲人生

The game of Life 是由英國數學家 John H. Conway 發明的,它能模擬生物羣落的興衰更替。該遊戲可用來觀察一個複雜的系統或模式如何能從一組簡單的規則演化而來。

遊戲規則

該遊戲使用一個不限大小的矩形網格,其中的每一個單元格要麼是空的,要麼被一個有機體佔據。被佔據的單元格被視做是活的,而空的單元格被視做是死的。遊戲的每次演進,都會基於當前的單元格佈局,創造新的「一代」。下一代中的每一個單元格狀態是根據如下規則肯定的:

  1. 若某單元格是活的,而且有 2 或 3 個活的鄰居,那麼它在下一代也保持活。每一個單元格有 8 個鄰居。
  2. 若某單元格是活的,但它沒有活的鄰居,或只有一個活鄰居,它在下一代會死於孤立。
  3. 一個活單元格,如有 4 個或更多個活鄰居,它在下一代會死於人口過剩。
  4. 一個死單元格,當且僅當只有 3 個活鄰居時,會在下一代重生。

用戶先初始化配置,即指定哪些單元格是活的,而後運用以上的規則,生成下一代。能夠看到,一些系統可能最終會消亡,而有些最終會進化成 「穩定」 狀態。例如:

gamelife_stable1.png

gamelife_stable2.png

設計方案

一個網格 life grid 用來表示和存儲遊戲區。網格包含一組矩形單元格,並分紅有限大小的行和列。

  • LifeGrid(nrows, ncols): 建立一個新的遊戲網格。全部單元格設置爲空(死)。
  • numRows(): 返回網格行數。
  • numCols(): 返回網格列數。
  • configure(coordList): 配置網格以進行下一代的演化。參數是一個 (row, col) 的序列,每個元組表示該位置的單元格是活的。
  • clearCell(row, col): 設置單元格爲空(死)。
  • setCell(row, col): 設置單元格爲活。
  • isLiveCell(row, col): 返回一個布爾值,表示某個單元格是否包含一個活的有機體。
  • numLiveNeighbors(row, col): 返回某個單元格的全部活鄰居個數。對於邊緣的單元格,落在邊緣外的鄰居都認爲是死的。

實現

使用一個二維數組來表示網格。每一個單元格的狀態使用 0 和 1 表示,0 表示死,1 表示活。這樣在統計單元格的活鄰居總數時,只須要將鄰居的狀態相加便可。實現時網格的大小是限定的,若是大小超出了,在運行過程當中能夠從新建立一個新的網格。

# life.py
from array import Array2D

class LifeGrid:
    DEAD_CELL = 0
    LIVE_CELL = 1

    def __init__( self, nrows, ncols ):
        self._grid = Array2D( nrows, ncols )
        self.configure( list() )

    def numRows( self ):
        return self._grid.numRows()

    def numCols( self ):
        return self._grid.numCols()

    def configure( self, coordList ):
        for i in range( self.numRows() ):
            for j in range( self.numCols() ):
                self.clearCell(i, j)

        for coord in coordList:
            self.setCell( coord[0], coord[1] )

    def isLiveCell( self, row, col ):
        return self._grid[ row, col ] == LifeGrid.LIVE_CELL

    def clearCell( self, row, col ):
        self._grid[ row, col ] = LifeGrid.DEAD_CELL

    def setCell( self, row, col ):
        self._grid[ row, col ] = LifeGrid.LIVE_CELL

    def numLiveNeighbors( self, row, col ):
        nrows = self.numRows()
        ncols = self.numCols()

        liveNum = 0
        for i in range( row-1, row+2 ):
            for j in range( col-1, col+2 ):
               if ( 0 <= i < nrows ) and ( 0 <= j < ncols ):
                   liveNum += self._grid[i, j]
        liveNum -= self._grid[ row, col ]

        return liveNum


from life import LifeGrid

# Define the initial configuration of live cells.
INIT_CONFIG = [ (0, 0), (0, 1), (1, 0), (1, 2), (3, 2), (3, 4), (5, 4), (5, 6), (7, 6), (7, 8), (9, 8), (9, 10), (11, 10), (11, 12), (12, 11), (12, 12)]

# Indicate the number of generations
#NUM_GENS = 8

def main():
    GRID_WIDTH = int( raw_input( "Grid width:" ) )
    GRID_HEIGHT = int( raw_input( "Grid height:" ) )
    NUM_GENS = int( raw_input( "Nbr of generations to evolve:" ) )
    grid = LifeGrid( GRID_WIDTH, GRID_HEIGHT )
    grid.configure( INIT_CONFIG )

    # Play the game.
    draw( grid )
    for i in range( NUM_GENS ):
        evolve( grid )
        draw( grid )

def evolve( grid ):
    liveCells = list()

    for i in range( grid.numRows() ):
        for j in range( grid.numCols() ):
            neighbors = grid.numLiveNeighbors( i, j )

            # 1. If a cell is alive and has either two or three live neighbors, the cell remains alive in the next generation. 
            # The neighbors are the eight cells immediately surrounding a cell: vertically, horizontally, and diagonally. 
            # 2. A living cell that has no live neighbors or a single live neighbor dies from isolation in the next generation.
            # 3. A living cell that has four or more live neighbors dies from overpopulation in the next generation.
            # 4. A dead cell with exactly three live neighbors results in a birth and becomes alive in the next generation.
            # All other dead cells remain dead in the next generation.

            if (neighbors == 2 and grid.isLiveCell( i, j )) or \
                (neighbors == 3):
                    liveCells.append( (i, j) )

    grid.configure( liveCells )

def draw( grid ):
    print
    for i in range( grid.numRows() ):
        for j in range( grid.numCols() ):
            if grid.isLiveCell( i, j):
                print '@',
            else:
                print '.',
        print

main()

參考:

相關文章
相關標籤/搜索