日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

数据结构与算法之Python实现——循环链表、双向循环链表

發布時間:2023/12/29 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法之Python实现——循环链表、双向循环链表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在前面我們學習了單鏈表,本期將介紹循環鏈與雙鏈表以及它們的相關操作,在最后會給具體案例來實現雙鏈表的應用

🍁 循環鏈表

在單鏈表中,如果我們要遍歷鏈表中的最后一個元素,我們就得從頭結點開始一個一個地遍歷,但當我們遍歷到最后一個元素,這時我們想繼續遍歷前面的結點,就又要手動從頭開始。

為了避免這個麻煩,也就是保持遍歷的“不間斷”,我們希望遍歷到最后一個結點后,下一個結點就是頭結點,然后就可以一直不間斷地遍歷下去。于是循環鏈表就這樣出現了。(em…雖然這樣解釋有點牽強)

那么循環鏈表如何實現呢?首先我們需要解決如何將鏈表初始化的問題,也就是使單鏈表構成一個循環

🍃 循環鏈表的初始化

表的結構和結點的結構還是可以和單鏈表一樣,不用做啥改動

class LNode:def __init__(self,elem):self.elem = elemself.next_ = Noneclass LList:def __init__(self):self.head = None

然后就是所謂的初始化

# 尾插法添加元素def insert_tail(self,elem):# 生成一個新結點node = LNode(elem)# 若頭結點為空,則直接將元素賦給頭結點if self.head is None:self.head = node# 頭結點指向頭結點形成循環node.next_ = self.head# 若頭結點不為空else:cur = self.head# 找到鏈表中最后一個結點while cur.next_ != self.head:cur = cur.next_# 新結點指向頭結點node.next_ = cur.next_# 最后一個結點指向新結點cur.next_ = node

🍃 獲取循環鏈表的長度

def get_length(self):# 若頭結點為空if self.head is None:return 0cur = self.head# 若鏈表中只有一個結點if cur.next_ == self.head:return 1# 計數器count = 0# 若鏈表中元素超過一個,找到最后一個元素,此時并未算上最后一個元素while cur.next_ != self.head:count += 1cur = cur.next_# 算上最后一個元素count += 1return count

🍃 按下標刪除循環鏈表中的元素

def delete_sub(self,pos):# 若表為空if self.head is None:print("The list is empty!")return# 若表中只有一個數據if self.head.next_ == self.head:self.head = None# 若表有5個數據,想刪除第3個,輸入2、輸入7、輸入12,最后pos都為2pos = pos % self.get_length()cur = self.head# 找到被刪除元素的前一個元素for i in range(self.get_length() + pos - 1):cur = cur.next_# 使被刪除元素的其前一個元素指向被刪除元素的下一個元素cur.next_ = cur.next_.next_# 注意!若刪除的是頭結點,那么此時頭結點就沒了,若再次遍歷就會出錯,所以這時需要重設頭結點if pos == 0:self.head = cur.next_
  • 關于pos = pos % self.get_length()

其實自己再草稿本上算一算也可以得到答案的,這里舉個例。若鏈表中有7個元素,我們輸入的是15,則pos=15%7=1pos = 15\%7=1pos=15%7=1,那么就會刪除下標為1的元素。

為什么要這樣做?若輸入下標在長度范圍內,就不用設這一步,因為是循環鏈表,所以我們要考慮一下輸入下標超出長度的情況,這一步也是為了方便后面找到被刪除元素的前一個元素,同樣,也是為了將刪除頭結點的情況結合到一起(若不這樣,刪除頭結點需單獨地操作)

  • 關于self.head = cur.next_


可以看到,head完全脫離了鏈表,如果此時想遍歷再執行cur = l.head操作,就不能得到我們想要的結果。所以要使head指向cur.next_,這樣head就又重新“回到”了鏈表中

🍃 按下標查找循環鏈表中元素的值

def search_sub(self,pos):pos = pos % self.get_length()cur = self.headfor i in range(pos):cur = cur.next_return cur.elem

🍃 驗證循環鏈表的操作

??注意:在添加、刪除操作后,都要重新執行一遍cur = l.head(因為前面也有對cur的操作,所以需要再初始化一遍,因為自己被這個卡了一會0.0)。

data = list(map(int,input("Please input a series of datas and split them by spaces:").split())) l = LList() # 初始化 print('Initiate') for i in range(len(data)):l.insert_tail(data[i])cur = l.head for i in range(l.get_length()):print(cur.elem)cur = cur.next_ print('--------------------------------') # 添加元素 print('Add new elem') l.insert_tail(6) cur = l.head for i in range(l.get_length()):print(cur.elem)cur = cur.next_ print('--------------------------------') # 刪除元素 print('Delete elem') l.delete_sub(0) cur = l.head for i in range(l.get_length()):print(cur.elem)cur = cur.next_ print('--------------------------------') # 查找元素 print('Search elem') print('The value of No.5 is: %d' % l.search_sub(4))

執行結果如下:

🍃 說明

對于循環鏈表的操作有很多種,就添加元素來說,我這里用的是尾插法,你還可以用前插法,一般插法;就刪除元素來說,不僅可以按下標刪除,也可以按值刪除,還可以刪除多個值相同的元素;就查找元素來說,可以按下標查找一個元素的值,也可以按值查找一個元素的下標…

我想說明的是,對于一個循環鏈表,不僅僅是循環鏈表,操作是有很多種的,需要對具體情況設計具體的操作,我們學習這個的目的就是了解這種結構,并且鍛煉自己寫代碼的思維和能力。

不要局限于客觀你所看到的,要發散自己的思維,要舉一反三,將理論與實際相結合,才是王道👍。

🍁 雙向鏈表

有了循環鏈表后,在某些方便的遍歷會方便許多,但有時候還是很麻煩,比如我在想循環鏈表的刪除操作時,我就只能遍歷到要被刪除元素的前一個元素,這就很傷腦筋的說(因為一般的遍歷肯定是直接遍歷到要操作的結點嘛)。

如果遍歷到被刪除元素時,我們能夠在掉過頭來遍歷前一個元素的話,那么就更方便了,于是雙向鏈表就這樣出現了~

雙向嘛,講究的就是一個雙向奔赴,你奔向我,我也奔向你~咳咳,直接一點呢就是在兩個結點間有兩個箭頭,前一個指向后一個,后一個也指向前一個。如下圖:

這里呢,原本雙向鏈表是沒有循環的,我在作圖的時候突發奇想想把這個兩個結合起來試試。那么這個標題看來就應該是雙向循環鏈表了!

🍃 雙向循環鏈表的初始化

對于結點類和表類,可以在循環鏈表上繼承,也可以重新寫。

class DLNode:def __init__(self,elem):self.elem = elemself.next_ = Noneself.last_ = Noneclass DLList:def __init__(self):self.head = None

這里新加了一個指針域last_指向前一個結點。

def insert_tail(self,elem):# 生成一個新結點node = DLNode(elem)# 若頭結點為空if self.head is None:self.head = node# node指向頭結點node.next_ = self.head# 頭結點反過來指向node,這兩句很重要,不然此時頭結點的next_和last_可能為空,不方便后續的添加元素self.head.last_ = node# 若頭結點不為空else:cur = self.head# 找到頭結點的前一個結點while cur.next_ != self.head:cur = cur.next_# cur指向nodecur.next_ = node# node反過來指向curnode.last_ = cur# node又指向頭結點構成單循環node.next_ = self.head# 頭結點又指向node構成雙循環self.head.last_ = node

這里依舊采用的尾插法添加元素

🍃 獲取雙向循環鏈表的長度

這個跟循環鏈表那個操作差不多,當然也可以從其它方面來計算鏈表的長度。

def get_length(self):count = 0# 若頭結點為空,即鏈表中元素個數為0if self.head is None:return countcur = self.head# 找到頭結點的前一個結點while cur.next_ != self.head:count += 1cur = cur.next_# 因為循環的條件,頭結點前一個結點并未記上,所以這里要加一count += 1# 注意要返回,之前調試的時候又是這里錯了~_~return count

🍃 按下標刪除雙向循環鏈表種的元素

def delete_sub(self,pos):cur = self.headpos = pos % self.get_length()# 若鏈表為空if self.head is None:raise Exception('The linked list is none!')# 若鏈表中只有一個元素elif self.get_length() == 1:self.head = None# 若鏈表中有兩個元素elif self.get_length() == 2:# 找到被刪除結點for i in range(pos):cur = cur.next_# 使另一個結點成為頭結點self.head = cur.next_# 將兩個指針域指向自身self.head.next_ = self.headself.head.last_ = self.headelse:# 找到被刪除結點for i in range(pos):cur = cur.next_# 使被刪除結點的上一個結點指向被刪除結點的下一個結點cur.last_.next_ = cur.next_# 使被刪除結點的下一個結點反過來指向被刪除結點的上一個結點cur.next_.last_ = cur.last_# 若刪除的是頭結點,則使頭結點的下一個結點成為頭結點if pos == 0:self.head = cur.next_

🍃 驗證雙向循環鏈表的操作

data = list(map(int,input('Please input a series of datas by spaces:').split())) dl = DLList() # 初始化 for i in range(len(data)):dl.insert_tail(data[i]) cur = dl.head # 打印鏈表中的元素 print('The linked list is:') for i in range(dl.get_length()):print(cur.elem,end=' ')cur = cur.next_ print('\n') print('------------------------------------') # 添加元素 dl.insert_tail(6) cur = dl.head print('The new linked list is:') for i in range(dl.get_length()):print(cur.elem,end=' ')cur = cur.next_ print('\n') print('------------------------------------') # 清空鏈表 print('Start to clear up') for i in range(dl.get_length()):dl.delete_sub(0)cur = dl.headfor j in range(dl.get_length()):print(cur.elem,end=' ')cur = cur.next_print('\n') print('The length of the linked list is:%d' % dl.get_length())

執行結果如下:

也許你會問為啥有關雙向循環鏈表的操作這么少,其實吧,我也只是提供一些思路,想到哪些寫哪些~(小聲一點說就是懶了( ′? ??`))。

下面就是具體案例的實現了,讓我們一步一步地來設計!

🍁 案例實現——核酸檢測登記表

首先,要用循環雙鏈表實現核酸檢測登記表(某一天的信息)的話,我們需要先確定我們需要錄入的信息:姓名,性別,年齡,手機號,做核酸的時間,其中姓名、性別、手機號、做核酸時間都用字符串型。那么結點的結構如下:

class PerNode:def __init__(self,name,gender,age,telenum,date):self.name = nameself.gender = genderself.age = ageself.telenum = telenum # 電話號碼self.date = dateself.next_ = Noneself.last_ = None

然后就是鏈表的結構和功能的設計:

  • 錄入信息,也就是添加元素
  • 刪除信息,也就是刪除元素
  • 查詢信息,也就是查找元素

基本的功能就是上面三個,如果還需要其它信息,再作改進。而添加和刪除功能直接將上面代碼套過來就可以了,不用做太大的改動。如下👇:

class PerList:def __init__(self):self.head = Nonedef get_length(self):count = 0if self.head is None:return countcur = self.headwhile cur.next_ != self.head:count += 1cur = cur.next_count += 1return countdef add(self,id,name,gender,age,telenum,date):node = PerNode(id,name,gender,age,telenum,date)if self.head is None:self.head = nodeself.head.next_ = self.headself.head.last_ = self.headelse:cur = self.headwhile cur.next_ != self.head:cur = cur.next_cur.next_ = nodenode.last_ = curnode.next_ = self.headself.head.last_ = nodedef delete(self,pos):cur = self.headpos = pos % self.get_length()if self.head is None:print('The list is empty!!')returnelif self.get_length() == 1:self.head = Noneelif self.get_length() == 2:for i in range(pos):cur = cur.next_self.head = cur.next_self.head.next_ = self.headself.head.last_ = self.headelse:for i in range(pos):cur = cur.next_cur.last_.next_ = cur.next_cur.next_.last_ = cur.last_if pos == 0:self.head = cur.next_def search(self,id):cur = self.headfor i in range(self.get_length()):if cur.id == id:return curcur = cur.next_print('Searching fails.The data does not exist!')

在調試的時候發現一個問題,如果每個人的數據有一個序號的話,刪除一個數據后其它數據的序號是沒有變的,這樣在查找時只有按照刪除前的序號進行查詢,這樣是十分麻煩的,所以我們還需寫一個刪除后重新排列序號的函數,如下:

def rearrange(self,pos):pos = pos % self.get_length()cur = self.head# 若刪除后只剩一個結點,需判斷它的id是否為1,若不為1,也就是為2,則需要減1# 若它的id是1,則不用減if self.get_length() == 1:if cur.id != 1:cur.id -= 1return# 刪除一個結點后剩余結點大于兩個的情況else:# 找到被刪除結點的位置,這個位置可能會被其它結點頂替也可能不存在for i in range(pos):cur = cur.next_# 因為刪除結點后是后面的結點往前補上來,所以后面的結點都要減1(注意這里就理解為單鏈表就行了)cur.id -= 1# 對該操作不作循環,所以按照單鏈表的方式遍歷即可while cur.next_ != self.head:cur = cur.next_cur.id -= 1

然后就是具體操作了,請看👇:

# 檢測表的初始化 tt = PerList() # Test table,檢測表 print('------------------------------Start typing information--------------------------------') # 開始錄入信息 flag = 1 # 用來作為循環的條件 count = 1 # 自動改變每個人員的id,例如增加一個人后它的id就自動加1 while flag == 1:print('No.%d' % count)name = input('Please input the name:') # 輸入人員的姓名gender = input('Please input the gender:') # 輸入人員的性別,male為男性,female為女性age = int(input('Please input the age:')) # 輸入人員的年齡telenum = input('Please input the phone number:') # 輸入人員的電話號碼date = input('Please input the typing date:') # 輸入核酸檢測的時間tt.add(count,name,gender,age,telenum,date)print('If you want to continue ,please input 1,else input 0:----',end=' ') # 如果你想繼續錄入,請輸入1,不想則輸入0flag = int(input())if flag == 1:count += 1print('\n') print('------------------------------The end of the entry------------------------------------') # 錄入結束# 輸出人員信息表 print('\n') print('------------------------------The list personnal information sheet--------------------') # 人員信息表 cur = tt.head print('%-20s%-20s%-20s%-20s%-20s%-20s' % ('id','name','gender','age','telenum','date')) for i in range(tt.get_length()):print('%-20d%-20s%-20s%-20d%-20s%-20s' % (cur.id,cur.name,cur.gender,cur.age,cur.telenum,cur.date))cur = cur.next_ print('\n')# 刪除人員表 print('------------------------------Delete the information------------------------------------------') # 刪除信息 print('Do you want to delete the information? If so input 1,else input 0:',end=' ') # 你是否想刪除信息,是請輸入1,不是輸入0 flag = int(input()) while flag == 1:print('Please input the number of the data that you want to delete:',end=' ') # 請輸入你想刪除人員的idpos = int(input())tt.delete(pos - 1)print('Now the length of the list is: %d,and the list is displayed as below:' % tt.get_length()) # 輸入刪改后表的長度和表的內容cur = tt.headprint('%-20s%-20s%-20s%-20s%-20s%-20s' % ('id', 'name', 'gender', 'age', 'telenum', 'date'))for i in range(tt.get_length()):print('%-20d%-20s%-20s%-20d%-20s%-20s' % (cur.id, cur.name, cur.gender, cur.age, cur.telenum, cur.date))cur = cur.next_print('Do you want to end up deleting?Just input 0,or input 1 to continue:',end=' ') # 你是否想結束刪除操作,是輸入0,繼續輸入1flag = int(input()) cur = tt.head print('%-20s%-20s%-20s%-20s%-20s%-20s' % ('id','name','gender','age','telenum','date')) for i in range(tt.get_length()):print('%-20d%-20s%-20s%-20d%-20s%-20s' % (cur.id,cur.name,cur.gender,cur.age,cur.telenum,cur.date))cur = cur.next_ print('-----------------------------The end of deleting---------------------------------------') # 刪除操作結束 print('\n')# 查找數據 print('-----------------------------Search the date-------------------------------------------') # 查詢表 print('Do you want to search the data?If so input 1,else input 0:',end=' ') # 你是否想查詢表,是輸入1,不是則輸入0 flag = int(input()) while flag == 1:print('Please input the value of the id that you want to search:',end=' ') # 請輸入你想查找人員的idpos = int(input())p = tt.search(pos)if p == None:breakprint('%-20s%-20s%-20s%-20s%-20s%-20s' % ('id', 'name', 'gender', 'age', 'telenum', 'date'))print('%-20d%-20s%-20s%-20d%-20s%-20s' % (p.id, p.name, p.gender, p.age, p.telenum, p.date))print('Do you want to end up searching? Just input 0,or input 1 to continue:',end=' ') # 你是否想結束查找,是輸入0,繼續輸入1flag = int(input()) print('Operation ends.') # 操作結束

咱們來看一看結果:

首先是輸入數據:


然后我們發現Alice的核酸時間明顯有錯,因為在一個時間段做核酸時間肯定是連起來的嘛,所以我們需要將她的信息刪除,如下:

最后就是查詢信息了
這個操作就結束了。

🍁 總結

本篇呢,主要就是講的循環鏈表這樣一個知識以及對它的應用,也許你看到這也會有跟我一樣的一個疑問:貌似就介紹的時候用了一些“循環”吧,其它循環體現在哪里呢?

后來我又仔細想了想,當然是體現在對鏈表的操作上了!本來循環鏈表就是為了某些操作更方便而實現的,且應用的時候具體實現細節別人也看不到呀!所以不用太過糾結這個問題~

這個呢,也是我第一次將代碼的應用寫得這么詳細,寫代碼不難,難就難在調試啊找bug啊,這個真是有苦說不出呀

好在功夫不負有心人,咱終于就是給它搞定了。不過仍有很多不足的地方,像最后的循環鏈表的應用,對于查找,我們可以按照id查找,也可以按照姓名,手機號等等查找,且不僅可以用順序查找,還可以用折半查找、分塊查找等等!

所以說,路還很長,任重而道遠吶

總結

以上是生活随笔為你收集整理的数据结构与算法之Python实现——循环链表、双向循环链表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。