算法之python建立鏈表實現cache

算法之python建立鏈表實現cache

本節內容

  1. 問題由來
  2. 解決思路
  3. 實現代碼
  4. 總結

1. 問題由來

問題原由於朋友的一次面試題,面試公司直接給出兩道題,要求四十八小時以內作出來,語言不限,作出來以後才能參加接下來的面試。
因而,朋友拿到這套題給咱們看看,本人看到這道題以後,感受挺好玩的,恰好這幾天正處在入職前的無聊時期,閒着也是閒着,因而花了兩個小時,簡單弄了弄。下面是原題目:node

  • 對Cache進行程序模擬操做, Cache最多容納100個Item,進行較特別的新增和淘汰的處理邏輯。
  • Item: Cache item爲單向鏈表結構;每秒鐘全部Item的age加1 ;
  • 新增:每秒鐘在隊列的隨機位置新增一個Item ;
  • 淘汰:每秒鐘只能淘汰一個item,淘汰條件是 要麼item的age大於10;要麼Cache已滿又無{age>10}的item,則淘汰第一個item。
  • 程序需求:
  • Cache單向鏈表中已有50個Item, 寫簡單程序模擬新增和淘汰的過程,至少需模擬200個item的新增或淘汰。

2. 解決思路

因爲python中不像C語言或者C++裏面那樣有結構體這個東西,因此。。。目前的解決辦法就是使用類去實現相似於結構體這樣的東西。(可是我的感受用類去實現相似結構體的東西有點像是大炮打蚊子。。。佔用資源會不會比結構體誇張不少?)python

下面是用類實現一個結構體,模擬鏈表中的item:

class item:
    def __init__(self,data=None,next=None,age=0):
        self.data=data  # 節點數據
        self.next=next  # 節點的下一個地址
        self.age=age  # 節點的age

上面是對這個問題的第一層抽象,也是最底層抽象(定義好數據結構)面試

對鏈表的抽象

可是光有這個抽象還不夠,咱們還得本身抽象一層鏈表的操做出來,對於鏈表的基本操做有:算法

  1. 統計鏈表長度
  2. 鏈表追加數據
  3. 鏈表插入數據
  4. 鏈表刪除數據

固然,這只是對鏈表的一個基本操做的抽象,可能還有一些抽象沒有實現,可是基本能夠完成這個題目中的功能。數據結構

對cache的抽象

上面抽象出來了一個鏈表的基本操做,可是這個cache還須要知足必定的邏輯:app

  • 對Cache進行程序模擬操做, Cache最多容納100個Item,進行較特別的新增和淘汰的處理邏輯。
  • Item: Cache item爲單向鏈表結構;每秒鐘全部Item的age加1 ;
  • 新增:每秒鐘在隊列的隨機位置新增一個Item ;
  • 淘汰:每秒鐘只能淘汰一個item,淘汰條件是 要麼item的age大於10;要麼Cache已滿又無{age>10}的item,則淘汰第一個item。

處理這些邏輯還須要抽抽象出一層cache來比較好實現。dom

完成上面這三步抽象,剩下的就不難了。函數

3. 實現代碼

# item節點抽象
class item:
    def __init__(self,data=None,next=None,age=0):
        self.data=data  # 節點數據
        self.next=next  # 節點的下一個節點
        self.age=age  # 節點age


# 注意,鏈表的0位置存儲的是root節點,不做爲實際存儲信息,裏面的data用來存儲該鏈表長度
class linked_list:
    def __init__(self):
        self.root=item(0)  # 初始化鏈表,建立鏈表頭,這時候鏈表長度爲0

    @property
    def len(self):
        # count=0
        # item=self.root
        # while item.next!=None:
        #     item=item.next
        #     count+=1
        # return count  # count是實際節點個數減一,從0開始
        return self.root.data  # 上面在鏈表頭中記錄了鏈表長度,就不須要這裏每次計算統計鏈表長度了,直接讀取數據,時間複雜度由O(N)降到了O(1)

    def append_item(self,data=None):  # 模擬python中的列表append數據,將數據插入到鏈表最後並將鏈表長度+1,時間複雜度O(1)
        append_item_node=item(data)  # 初始化插入節點
        node=self.root
        for i in range(self.len):  # 找到鏈表結尾,並把新節點插入到最後,而後將鏈表長度+1
            node=node.next
        else:
            node.next=append_item_node
            self.root.data += 1
            return append_item_node

    def insert_item(self,num,data=None):  # 模擬python中列表插入數據,在某個位置前面插入數據,時間複雜度o(1)
        insert_item_node=item(data)  # 初始化插入節點
        node = self.root
        if num >self.len:  # 插入位置超過長度,則直接插入到最後一個元素的前面
            num=self.len
        elif num<1:  # 插入位置過小,則直接返回False,插入失敗
            return False
        for i in range(num-1):  #找到相應的位置,在該位置前面插入,並將鏈表長度+1
            node=node.next
        else:
            tmp=node.next
            node.next=insert_item_node
            insert_item_node.next=tmp
            self.root.data += 1
            return insert_item_node

    def remove_item(self,num):  # 模擬python中的列表刪除數據,在某個位置刪除數據以後,後面的數據自動往前補,時間複雜度O(1)
        node=self.root
        if num < 1 or num > self.len:  # 若是刪除位置不在長度範圍內,則返回False,插入失敗
            return False
        for i in range(num-1):  # 找到要刪除的節點前一個節點,先用中間變量接收要刪除節點後一個節點,而後再將要刪除的節點刪掉,再將兩段鏈表接起來,最後,鏈表長度-1
            node = node.next
        else:
            tmp = node.next.next
            del node.next
            node.next = tmp
            self.root.data -= 1
            return True

    def add_allitem_age(self):  # 對全部節點的age自加一(只是爲了適應當前題目加的一個方法,通用鏈表中不須要該方法)
        node=self.root
        for i in range(self.len):
            node=node.next
            node.age+=1

    def __iter__(self):  # 將鏈表改爲一個迭代器,這樣在外部就可使用for循環遍歷鏈表
        self.current_node=self.root
        return self

    def __next__(self):  # 實現迭代器協議
        if self.current_node.next!=None:
            self.current_node=self.current_node.next
            return self.current_node
        raise StopIteration

    def __str__(self):  # 自定義打印改鏈表樣式
        node =self.root.next
        result_str="linked_list:"+str(self.len)+": "+":".join((str(node.data),str(node.age)))
        for i in range(1,self.len):
            node=node.next
            result_str="->".join((result_str,":".join((str(node.data),str(node.age)))))
        return result_str

class cache:
    def __init__(self,max_num=100):  # 初始化cache,生成一個鏈表,並設定好cache最大值
        self.data=linked_list()
        self.max_num=max_num

    @property
    def add_age(self):  # 每隔一秒鐘須要調用該函數,對鏈表中全部節點age自加一
        self.data.add_allitem_age()

    def insert_item(self,num,data):
        if self.data.len<self.max_num:  # 小於最大值時才讓插入
            return self.data.insert_item(num,data)
        return False

    def append_item(self,data):
        if self.data.len<self.max_num:  # 小於最大值時才讓追加
            return self.data.append_item(data)
        return False

    def eliminate(self):
    # 消除某個節點,可能有知足條件的節點,就消除,沒有就不消除
    # 條件:要麼item的age大於10;要麼Cache已滿又無{age>10}的item,則淘汰第一個item
        num=0
        for i in self.data:  #循環鏈表,若是找到age大於10的節點,則刪除該節點
            num+=1
            if i.age>10:
                self.data.remove_item(num)
                return True
        else:  # 不然,判斷是否cache已滿,若滿了,則刪除第一個節點,不然什麼都不幹
            if self.data.len==100:
                self.data.remove_item(1)
                return True
            else:
                return False

import random
import time
def simulate_cache():  # 模擬cache
    cache_obj=cache()  # 建立cache對象
    for i in range(50):  # 放入50個初始化數據到cache中
        cache_obj.append_item(i)
    print(cache_obj.data)  # 打印生成50個最初始的值
    for  i in range(200):  # 進行兩百秒的cache動態添加刪除動做
        num=random.randint(1,cache_obj.data.len)  # 生成隨機數
        cache_obj.insert_item(num,num)  # 在鏈表隨機位置插入剛生成的隨機數建立的節點
        cache_obj.add_age  # 將鏈表中每個item節點的age自加一
        cache_obj.eliminate()  # 調用消除節點方法,自行判斷是否須要刪除某個節點
        print(cache_obj.data)  # 打印此次處理後鏈表中的數據
        time.sleep(1)  # 暫停一秒

simulate_cache()  # 調用模擬cache函數

4. 總結

第一次用python實現鏈表這樣的數據結構,感受很新奇,花了兩個小時完成這道題,在這也是對本身的一個交代吧。工具

PS:這也從側面印證了一個結論,那就是學習開發,並無哪門語言好壞之分,學習的是開發的思想,語言只是工具,思想會了,用不一樣的工具都能造出本身想要的東西。加油,共勉。學習

相關文章
相關標籤/搜索