目錄python
Python內置了不少數據結構(容器),供咱們直接進行使用,在學習結構以前,有一些小的知識點進行補充。算法
In [2]: int(10.12) # 直接取整 Out[2]: 10 In [3]: int(-12) Out[3]: -12 In [4]: float(10) # 轉換爲浮點數 Out[4]: 10.0 In [5]: float(-9) Out[5]: -9.0 In [6]: bool(1) # 1表示True(非0都爲True) Out[6]: True In [7]: bool(0) # 0表示False Out[7]: False
數學之中,除了加減乘除四則運算以外,還有其它更多的運算,好比乘方、開方、對數運算等等,Python 提供了一個專門輔助計算的模塊:math
,模塊方法及常量以下:數據結構
math.ceil
:向上取整math.floor
:向下取整math.pi
:數字常量,圓周率math.pow
:返回x的y次方,即x**ypath.sqrt
:求x的平方根In [22]: import math # 導入模塊 In [23]: math.pi Out[23]: 3.141592653589793 In [24]: math.pow(2,3) # 2**3 Out[24]: 8.0 In [25]: math.ceil(-10.6) Out[25]: -10 In [26]: math.ceil(-10.5) Out[26]: -10 In [27]: math.ceil(12.3) Out[27]: 13 In [28]: math.floor(12.3) Out[28]: 12 In [29]: math.floor(-12.3) Out[29]: -13 In [32]: math.sqrt(10) Out[32]: 3.1622776601683795
注意:app
In [33]: int(-12.1) # 只會取整數部分 Out[33]: -12 In [34]: int(10.5) Out[34]: 10 In [35]: 1//3 # 向下取整 Out[35]: 0 In [36]: 2//6 Out[36]: 0 In [37]: 20//6 Out[37]: 3 In [38]: 10//3 Out[38]: 3
在Python中有一個round函數,用於對小數進行取整,不過在Python中的round有些特別,總結一句話就是4舍6入5取偶
。即當小數點後面的數字小於5
呢,會直接捨去
,大於5
呢,會直接進位
,等於5
呢,會取最近的偶數
。函數
In [39]: round(1.2) Out[39]: 1 In [40]: round(-1.2) Out[40]: -1 In [41]: round(1.5) Out[41]: 2 In [42]: round(-2.5) Out[42]: -2 In [43]: round(0.5) Out[43]: 0 In [44]: round(-5.5) Out[44]: -6 In [45]: round(1.6) Out[45]: 2 In [46]: round(-1.500001) Out[46]: -2
因爲Python是一種強類型語言,在數據比較時,只有相同數據類型,才能夠進行比較。這時咱們就須要知道對象究竟是什麼類型,type
就是用來查看類型的。性能
In [47]: type('123') Out[47]: str In [48]: type(123) Out[48]: int In [49]: type(True) Out[49]: bool
從上面代碼結果能夠看出type
返回的是類型,並非字符串,而在數據判斷時咱們須要的是判斷,好比判斷某個變量是某個類型的,那麼這個時候就須要用到instance
了。學習
# 基本用法 Signature: isinstance(obj, class_or_tuple, /) --> bool # 接受兩個參數 # obj:要判斷的對象 # class_or_tuple:一個類,或者多個類組成的元組 In [52]: isinstance(123,str) Out[52]: False In [53]: isinstance(123,(str,int)) # 判斷123,是str或者是int嗎? Out[53]: True
列表是Python中最基本的數據結構。什麼是序列呢?咱們能夠認爲它是一個隊列,一個排列整齊的隊伍。列表內的個體稱爲元素,它能夠是任意對象(數字、字符串、對象、列表),多個元素組合在一塊兒,使用逗號分隔,中括號括起來,就是列表。它有以下特色:設計
有序的
,可
以使用索引
(下標)獲取,第一個索引是0,第二個索引是1,依此類推。線性的存儲結構
,(從左至右依次存儲)列表是可變的
,咱們能夠對其內的元素進行任意的增刪改查建立一個列表,只要把逗號分隔的不一樣的數據項使用方括號括起來便可。以下所示:code
In [56]: a = [] # 空列表 In [57]: b = list() # 空列表 In [59]: c = list(range(3)) # 接受一個可迭代對象,轉換爲列表。[0, 1, 2]
主要:列表沒辦法在初始化時就指定列表的大小對象
列表的索引有以下特色:
咱們一般會把列表和鏈表拿來作對比,它倆雖然都是有序的能夠被索引,可是內部實現方法以及適用場景有很大區別。
列表在內存中的存儲方式是線性的,而鏈表是非線性的,他們的差異以下:
由上圖咱們能夠獲得以下結論:
注意:列表的增刪若是是在隊伍當中,那麼相對效率比較低,可是若是在尾部增刪,效率很快。鏈表還分爲單向和雙向,表示索引方向而已,這裏不在進行說明
擴展:
下面是其餘基於列表/鏈表特性的實現:
先進先出隊列
和先進後出隊列
及優先級隊列
列表提供了不少的方法,使咱們能夠方便的對它進行查詢、統計等操做。
L.index(value, [start, [stop]]) -> integer -- 在列表中獲取傳入元素的索引值,若是元素不存在,會報異常,若是存在多個,只返回找到的第一個匹配元素的索引值,其中start,stop爲表示查找區間,默認爲整個列表 L.count(value) -> integer -- 統計傳入元素在列表中出現的次數並返回
index和count的時間複雜度都是O(n),即隨着列表的規模增長,效率會依次降低。什麼是時間複雜度? 這是在計算算法優劣時的主要參考值,咱們主要使用大寫的O來表示時間複雜度,因爲index和count函數都須要遍歷列表,因此若是這個列表有n個元素的話,那麼它的時間複雜度就爲O(n),詳細的解釋,建議自行了解,這裏知道這樣表示便可,因爲list[1]經過偏移量進行火速據訪問,能夠理解爲一步到位,因此這種方式的時間複雜度爲O(1),不會隨着規模增大而改變。
In [66]: lst Out[66]: [1, 2, 3, 1, 2, 3, 3, 2, 4, 5, 7] In [67]: lst.index(3) # 獲取元素3的索引值 Out[67]: 2 In [68]: lst.count(2) # 統計元素2出現的次數 Out[68]: 3
擴展:
若是咱們要獲取列表的元素總數,咱們須要什麼設計呢?
很明顯第一個方法的時間複雜度是O(n),而第二個方法因爲事先存儲着列表元素的總數,因此它的時間複雜度是O(1),列表使用的就是方式2,而經過Python內置的len函數
就能夠獲取列表的大小(不只僅針對列表,其餘元素也能夠)
In [69]: lst Out[69]: [1, 2, 3, 1, 2, 3, 3, 2, 4, 5, 7] In [70]: len(lst) Out[70]: 11
咱們使用索引能夠獲取列表中對應索引位置的元素,同時咱們也能夠經過索引直接對對應索引位的元素進行修改
In [71]: lst Out[71]: [1, 2, 3, 1, 2, 3, 3, 2, 4, 5, 7] In [72]: lst[2] = 100 In [73]: lst Out[73]: [1, 2, 100, 1, 2, 3, 3, 2, 4, 5, 7]
須要注意的時,因此不要越界,不然會報異常
列表提供了對其進行追加或插入的函數,即append
和insert
。先來看看這兩個函數的使用方法。
L.append(object) --> None --> 接受一個元素,用於追加到列表的末尾。 L.insert(index, object) --> 接受兩個變量:索引,元素。 在列表中指定的索引位置,插入元素。
說明:
In [78]: lst Out[78]: [1, 2, 3] In [79]: lst.insert(-500,500) In [80]: lst Out[80]: [500, 1, 2, 3] In [81]: lst.insert(500,500) In [82]: lst Out[82]: [500, 1, 2, 3, 500]
不少場景下咱們對列表操做不是一個一個元素的追加,更多的時候,咱們可能須要的是批量的操做,列表提供了一個extend
函數用於知足這種需求。
L.extend(iterable) --> None -- 從一個可迭代對象中把元素擴展追加到當前列表中
說明:
# extend In [86]: lst Out[86]: [500, 1, 2, 3, 500] In [87]: lst.extend('abc') In [88]: lst Out[88]: [500, 1, 2, 3, 500, 'a', 'b', 'c'] # + * In [94]: lst Out[94]: [500, 1, 2, 3, 500] In [95]: lst + ['a','b','c'] Out[95]: [500, 1, 2, 3, 500, 'a', 'b', 'c'] In [96]: lst * 2 Out[96]: [500, 1, 2, 3, 500, 500, 1, 2, 3, 500]
注意:使用+進行列表拼接的時候,因爲返回了新的列表,原來相加的兩個列表可能就沒有用了,而若是這兩個列表很是大,那麼等於重複佔用了新的內存空間,內存資源很寶貴,省着點用哈
咱們使用*能夠快速的生成一些特定的列表形式,好比我須要一個6個元素的列表,每一個元素的值爲1,我就能夠這樣寫 lst = [1]; lst * 6
,這樣寫的確沒什麼問題,可是在某些場景下會產生意想不到的問題,好比在列表嵌套的時候。
In [97]: lst = [[1,2,3]] In [98]: lst1 = lst * 3 In [99]: lst1 Out[99]: [[1, 2, 3], [1, 2, 3], [1, 2, 3]] In [100]: lst1[1][1] = 20 In [101]: lst1 Out[101]: [[1, 20, 3], [1, 20, 3], [1, 20, 3]]
有沒有發現什麼問題?我明明修改的是lst1的第二個元素的第二個值爲20,爲何全都改變了?這是由於在列表是一個引用類型,lst中實際上存儲的是[1,2,3]的內存地址,而咱們使用*3的時候,等於複製了三份這個地址,因此lst1的3個元素,其實都指向了一個內存地址,因此咱們隨便修改一個元素,其餘的也都會跟着被改變(畢竟是1個地址啊),咱們通常稱這種複製爲影子複製(shadow copy)
,知道了緣由,咱們就能夠想辦法解決了,既然你複製的是門牌號,那有沒有辦法複製門牌號裏面的數據呢?答案固然是能夠的,咱們使用copy
模塊的deepcopy
完成,它能夠幫咱們一層一層的找到元素真正的位置,而後進行復制。咱們稱deepcopy爲深拷貝
。
In [102]: import copy # 導入copy模塊 In [103]: lst Out[103]: [[1, 20, 3]] In [104]: lst1 = lst # 複製一個新的列表 In [105]: lst1[0][1] = 1000 # 對新列表進行賦值 In [106]: lst1 Out[106]: [[1, 1000, 3]] # 會同時影響lst和lst1 In [107]: lst Out[107]: [[1, 1000, 3]] In [108]: lst2 = copy.deepcopy(lst) # 這裏使用深copy In [109]: lst2 Out[109]: [[1, 1000, 3]] In [110]: lst2[0][1] == 2000 # 對lst2進行修改後,不會影響原列表,由於已經把元素拷貝過來了 Out[110]: False In [111]: lst2[0][1] = 2000 In [112]: lst2 Out[112]: [[1, 2000, 3]] In [113]: lst1 Out[113]: [[1, 1000, 3]] In [114]: lst Out[114]: [[1, 1000, 3]]
列表對象同時提供了專門的方法用於對列表元素進行刪除:remove
、pop
、clear
。
L.remove(value) --> None -- 刪除列表中匹配value的第一個元素. L.pop([index]) --> item -- 刪除並返回index對應的item,索引超界拋出IndexError錯誤,若是不指定index,默認是列表的最後一個 L.clear() --> None -- 清空列表,不建議進行操做,能夠等待GC自行進行銷燬
當咱們使用remove和pop時,依然須要考慮效率問題
,remove刪除一個元素的時候,它首先須要遍歷這個列表,查找匹配到的元素後移除,它的時間複雜度是O(n),使用pop指定index刪除時,雖然能夠1步定位到元素,可是若是刪除的列表是首部或者中間的元素,那麼將會使列表中的後續數據集體搬家
,但當你使用pop不指定index時,它默認會在列表的默認刪除,這種操做的時間複雜度爲O(1)
。因此建議若是須要頻繁的對列表進行增刪改,建議使用鏈表類型,而若是須要頻繁的查或者只是從末尾彈出,就可使用列表,由於這樣效率更高,以上只是建議。
In [1]: lst = [1,2,3,4] In [2]: lst.remove(2) In [3]: lst Out[3]: [1, 3, 4] In [4]: lst.pop() Out[4]: 4 In [5]: lst Out[5]: [1, 3] In [6]: lst.pop(1) Out[6]: 3 In [7]: lst Out[7]: [1] In [8]: lst.clear() In [9]: lst Out[9]: []
當咱們的列表中存儲的是int類型的數據,而咱們想要對其進行排序,那麼就可使用列表的排序,當咱們想要判斷一個元素是否存在於列表中時,就可使用成員判斷。
L.reverse() -- 原地反轉,原地將列表元素進行反轉 L.sort(key=None, reverse=False) -> None -- 對列表進行原地排序 in: 成員判斷,判斷元素是否在列表中 1 in [1,2,3],因爲要遍歷,因此它的時間複雜度爲O(n)
sort
比較特別,它有兩個參數,其中key
能夠接受一個函數,使列表中的元素按照函數處理事後的類型進行排序,默認爲空,即不處理。reverse
則有兩個值True
和False
,表示正序或者反序,默認爲False
表示正序。
線性數據結構的通病,找元素,須要進行遍歷。
# 列表是順序結構,但凡是 順序不一致,那麼兩個列表就不相等 In [11]: l1 = [1,2,3,[4,5]] In [12]: [4,5] in l1 Out[12]: True In [13]: [5,4] in l1 # 順序改變,因此不相等 Out[13]: False In [10]: lst = [ 2, 1 ,3 ,4 ,5 ] In [11]: lst.reverse() In [12]: lst Out[12]: [5, 4, 3, 1, 2] In [13]: lst.sort() In [14]: lst Out[14]: [1, 2, 3, 4, 5] In [15]: lst.sort(reverse=True) In [16]: lst Out[16]: [5, 4, 3, 2, 1] In [17]