⊰第三篇⊱ 链表
為什么需要鏈表
順序表的構建需要預先知道數據大小來申請連續的存儲空間,而在進行擴充時又需要進行數據的搬遷,所以使用起來并不是很靈活。
鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。
鏈表的定義
鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,但是不像順序表一樣連續存儲數據,而是在每一個節點(數據存儲單元)里存放下一個節點的位置信息(即地址)。
單向鏈表
單向鏈表也叫單鏈表,是鏈表中最簡單的一種形式,它的每個節點包含兩個域,一個信息域(元素域)和一個鏈接域。這個鏈接指向鏈表中的下一個節點,而最后一個節點的鏈接域則指向一個空值。
- 表元素域elem用來存放具體的數據。
- 鏈接域next用來存放下一個節點的位置(python中的標識)
- 變量p指向鏈表的頭節點(首節點)的位置,從p出發能找到表中的任意節點。
節點實現
class SingleNode(object):"""單鏈表的結點"""def __init__(self,item):# _item存放數據元素self.item = item# _next是下一個節點的標識self.next = None單鏈表的操作
- is_empty() 鏈表是否為空
- length() 鏈表長度
- travel() 遍歷整個鏈表
- add(item) 鏈表頭部添加元素
- append(item) 鏈表尾部添加元素
- insert(pos, item) 指定位置添加元素
- remove(item) 刪除節點
- search(item) 查找節點是否存在
單鏈表的實現
class SingleLinkList(object):"""單鏈表"""def __init__(self):self._head = Nonedef is_empty(self):"""判斷鏈表是否為空"""return self._head == Nonedef length(self):"""鏈表長度"""# cur初始時指向頭節點cur = self._headcount = 0# 尾節點指向None,當未到達尾部時while cur != None:count += 1# 將cur后移一個節點cur = cur.nextreturn countdef travel(self):"""遍歷鏈表"""cur = self._headwhile cur != None:print cur.item,cur = cur.nextprint ""頭部添加元素
def add(self, item):"""頭部添加元素"""# 先創建一個保存item值的節點node = SingleNode(item)# 將新節點的鏈接域next指向頭節點,即_head指向的位置node.next = self._head# 將鏈表的頭_head指向新節點self._head = node
尾部添加元素
def append(self, item):"""尾部添加元素"""node = SingleNode(item)# 先判斷鏈表是否為空,若是空鏈表,則將_head指向新節點if self.is_empty():self._head = node# 若不為空,則找到尾部,將尾節點的next指向新節點else:cur = self._headwhile cur.next != None:cur = cur.nextcur.next = node指定位置添加元素
def insert(self, pos, item):"""指定位置添加元素"""# 若指定位置pos為第一個元素之前,則執行頭部插入if pos <= 0:self.add(item)# 若指定位置超過鏈表尾部,則執行尾部插入elif pos > (self.length()-1):self.append(item)# 找到指定位置else:node = SingleNode(item)count = 0# pre用來指向指定位置pos的前一個位置pos-1,初始從頭節點開始移動到指定位置pre = self._headwhile count < (pos-1):count += 1pre = pre.next# 先將新節點node的next指向插入位置的節點node.next = pre.next# 將插入位置的前一個節點的next指向新節點pre.next = node刪除節點
def remove(self,item):"""刪除節點"""cur = self._headpre = Nonewhile cur != None:# 找到了指定元素if cur.item == item:# 如果第一個就是刪除的節點if not pre:# 將頭指針指向頭節點的后一個節點self._head = cur.nextelse:# 將刪除位置前一個節點的next指向刪除位置的后一個節點pre.next = cur.nextbreakelse:# 繼續按鏈表后移節點pre = curcur = cur.next查找節點是否存在
def search(self,item):"""鏈表查找節點是否存在,并返回True或者False"""cur = self._headwhile cur != None:if cur.item == item:return Truecur = cur.nextreturn False測試
if __name__ == "__main__":ll = SingleLinkList()ll.add(1)ll.add(2)ll.append(3)ll.insert(2, 4)print "length:",ll.length()ll.travel()print ll.search(3)print ll.search(5)ll.remove(1)print "length:",ll.length()ll.travel()鏈表與順序表的對比
鏈表失去了順序表隨機讀取的優點,同時鏈表由于增加了結點的指針域,空間開銷比較大,但對存儲空間的使用要相對靈活。
鏈表與順序表的各種操作復雜度如下所示:
注意雖然表面看起來復雜度都是 O(n),但是鏈表和順序表在插入和刪除時進行的是完全不同的操作。鏈表的主要耗時操作是遍歷查找,刪除和插入操作本身的復雜度是O(1)。順序表查找很快,主要耗時的操作是拷貝覆蓋。因為除了目標元素在尾部的特殊情況,順序表進行插入和刪除時需要對操作點之后的所有元素進行前后移位操作,只能通過拷貝和覆蓋的方法進行。
單向循環鏈表
單鏈表的一個變形是單向循環鏈表,鏈表中最后一個節點的next域不再為None,而是指向鏈表的頭節點。
操作
- is_empty() 判斷鏈表是否為空
- length() 返回鏈表的長度
- travel() 遍歷
- add(item) 在頭部添加一個節點
- append(item) 在尾部添加一個節點
- insert(pos, item) 在指定位置pos添加節點
- remove(item) 刪除一個節點
- search(item) 查找節點是否存在
實現
class Node(object):"""節點"""def __init__(self, item):self.item = itemself.next = Noneclass SinCycLinkedlist(object):"""單向循環鏈表"""def __init__(self):self._head = Nonedef is_empty(self):"""判斷鏈表是否為空"""return self._head == Nonedef length(self):"""返回鏈表的長度"""# 如果鏈表為空,返回長度0if self.is_empty():return 0count = 1cur = self._headwhile cur.next != self._head:count += 1cur = cur.nextreturn countdef travel(self):"""遍歷鏈表"""if self.is_empty():returncur = self._headprint cur.item,while cur.next != self._head:cur = cur.nextprint cur.item,print ""def add(self, item):"""頭部添加節點"""node = Node(item)if self.is_empty():self._head = nodenode.next = self._headelse:#添加的節點指向_headnode.next = self._head# 移到鏈表尾部,將尾部節點的next指向nodecur = self._headwhile cur.next != self._head:cur = cur.nextcur.next = node#_head指向添加node的self._head = nodedef append(self, item):"""尾部添加節點"""node = Node(item)if self.is_empty():self._head = nodenode.next = self._headelse:# 移到鏈表尾部cur = self._headwhile cur.next != self._head:cur = cur.next# 將尾節點指向nodecur.next = node# 將node指向頭節點_headnode.next = self._headdef insert(self, pos, item):"""在指定位置添加節點"""if pos <= 0:self.add(item)elif pos > (self.length()-1):self.append(item)else:node = Node(item)cur = self._headcount = 0# 移動到指定位置的前一個位置while count < (pos-1):count += 1cur = cur.nextnode.next = cur.nextcur.next = nodedef remove(self, item):"""刪除一個節點"""# 若鏈表為空,則直接返回if self.is_empty():return# 將cur指向頭節點cur = self._headpre = None# 若頭節點的元素就是要查找的元素itemif cur.item == item:# 如果鏈表不止一個節點if cur.next != self._head:# 先找到尾節點,將尾節點的next指向第二個節點while cur.next != self._head:cur = cur.next# cur指向了尾節點cur.next = self._head.nextself._head = self._head.nextelse:# 鏈表只有一個節點self._head = Noneelse:pre = self._head# 第一個節點不是要刪除的while cur.next != self._head:# 找到了要刪除的元素if cur.item == item:# 刪除pre.next = cur.nextreturnelse:pre = curcur = cur.next# cur 指向尾節點if cur.item == item:# 尾部刪除pre.next = cur.nextdef search(self, item):"""查找節點是否存在"""if self.is_empty():return Falsecur = self._headif cur.item == item:return Truewhile cur.next != self._head:cur = cur.nextif cur.item == item:return Truereturn Falseif __name__ == "__main__":ll = SinCycLinkedlist()ll.add(1)ll.add(2)ll.append(3)ll.insert(2, 4)ll.insert(4, 5)ll.insert(0, 6)print "length:",ll.length()ll.travel()print ll.search(3)print ll.search(7)ll.remove(1)print "length:",ll.length()ll.travel()雙向鏈表
一種更復雜的鏈表是“雙向鏈表”或“雙面鏈表”。每個節點有兩個鏈接:一個指向前一個節點,當此節點為第一個節點時,指向空值;而另一個指向下一個節點,當此節點為最后一個節點時,指向空值。
操作
- is_empty() 鏈表是否為空
- length() 鏈表長度
- travel() 遍歷鏈表
- add(item) 鏈表頭部添加
- append(item) 鏈表尾部添加
- insert(pos, item) 指定位置添加
- remove(item) 刪除節點
- search(item) 查找節點是否存在
實現
class Node(object):"""雙向鏈表節點"""def __init__(self, item):self.item = itemself.next = Noneself.prev = Noneclass DLinkList(object):"""雙向鏈表"""def __init__(self):self._head = Nonedef is_empty(self):"""判斷鏈表是否為空"""return self._head == Nonedef length(self):"""返回鏈表的長度"""cur = self._headcount = 0while cur != None:count += 1cur = cur.nextreturn countdef travel(self):"""遍歷鏈表"""cur = self._headwhile cur != None:print cur.item,cur = cur.nextprint ""def add(self, item):"""頭部插入元素"""node = Node(item)if self.is_empty():# 如果是空鏈表,將_head指向nodeself._head = nodeelse:# 將node的next指向_head的頭節點node.next = self._head# 將_head的頭節點的prev指向nodeself._head.prev = node# 將_head 指向nodeself._head = nodedef append(self, item):"""尾部插入元素"""node = Node(item)if self.is_empty():# 如果是空鏈表,將_head指向nodeself._head = nodeelse:# 移動到鏈表尾部cur = self._headwhile cur.next != None:cur = cur.next# 將尾節點cur的next指向nodecur.next = node# 將node的prev指向curnode.prev = curdef search(self, item):"""查找元素是否存在"""cur = self._headwhile cur != None:if cur.item == item:return Truecur = cur.nextreturn False指定位置插入節點
def insert(self, pos, item):"""在指定位置添加節點"""if pos <= 0:self.add(item)elif pos > (self.length()-1):self.append(item)else:node = Node(item)cur = self._headcount = 0# 移動到指定位置的前一個位置while count < (pos-1):count += 1cur = cur.next# 將node的prev指向curnode.prev = cur# 將node的next指向cur的下一個節點node.next = cur.next# 將cur的下一個節點的prev指向nodecur.next.prev = node# 將cur的next指向nodecur.next = node刪除元素
def remove(self, item):"""刪除元素"""if self.is_empty():returnelse:cur = self._headif cur.item == item:# 如果首節點的元素即是要刪除的元素if cur.next == None:# 如果鏈表只有這一個節點self._head = Noneelse:# 將第二個節點的prev設置為Nonecur.next.prev = None# 將_head指向第二個節點self._head = cur.nextreturnwhile cur != None:if cur.item == item:# 將cur的前一個節點的next指向cur的后一個節點cur.prev.next = cur.next# 將cur的后一個節點的prev指向cur的前一個節點cur.next.prev = cur.prevbreakcur = cur.next測試
if __name__ == "__main__":ll = DLinkList()ll.add(1)ll.add(2)ll.append(3)ll.insert(2, 4)ll.insert(4, 5)ll.insert(0, 6)print "length:",ll.length()ll.travel()print ll.search(3)print ll.search(4)ll.remove(1)print "length:",ll.length()ll.travel()轉載于:https://www.cnblogs.com/yzls/articles/9551783.html
總結
- 上一篇: IBM的SOA方法论之一——五个切入点和
- 下一篇: 总结一些通用的处理方法