數據結構與算法(基本數據結構-順序表與鏈表)


一、理解計算機內存

  計算機的作用:對數據進行存儲和運算。首先我們需要知道我們目前使用的計算機都是二進制的計算機,就以為着計算機只可以存儲和運算二進制的數據。例如下載好的一部電影,該電影可以存儲到計算機中,計算機中存儲的是基於二進制的電影數據,然后我們可以通過相關的視頻播放軟件結合相關的硬件對電影的二進制數據進行相關的運算操作,所產生的結果就是我們可以看到電影的畫面和聽到音頻的聲音。

   - 問題:闡述計算機如何計算1+2的結果?

    - 闡述:簡單理解為,首先可以將1和2輸入到計算機中,然后計算機會將1和2轉換成二進制的數據進行數據存儲,然后通過加法器進行兩個二進制數值的計算並返回結果。

   - 分析:上述的闡述中提到,計算機首先需要存儲1和2這兩個數值,那么計算機如何進行數據的存儲呢?那么毫無疑問,計算機可以將數據直接存儲到內存中。

   - 變量:我們在編程世界中,可以將某個數值直接賦值給一個變量,但是最終數值會被存儲到計算機的內存中,因此我們可以理解為,變量表示的就是計算機中進行數據存儲的某一塊內存。

  如何形象化的理解計算機的內存?

   - 舉例:將計算機的內存空間映射到我們現實生活中的話,內存就好比是我們在現實生活中三維立體的空間。生活在北京的北漂們,幾乎都居住的是一個獨立的公寓或者合租在一個幾居室的某一個房間中,那么北漂甲就好比是數據,而他所居住的房間則就是存儲數據的一塊內存空間。

   - 分析:從上述案例中,我們可以得知北漂甲居住的房間會有兩個基本的屬性,其一就是房間空間的大小,其二就是房間的一個位置標識(門牌號)。那么計算機中存儲數據的內存空間也會有這兩個最基本的屬性:內存空間大小和內存空間的地址。內存空間的大小可以表示該空間可以存儲數據值的大小范圍,內存空間的地址(用十六進制數值表示)可以用來通過尋址定位、查找到該內存空間中所存儲的數據值。

   - 如何理解 a = 10 這條賦值語句對應的內存圖呢?

  - 引用:當一個變量中存儲的是某一塊內存空間的地址,則該變量即可成為那塊內存空間的引用。a=10,a就是10所在內存空間的一個引用。

  - 指向:當一個變量中存儲了一塊內存空間的地址,則稱該變量(引用)指向了那塊內存。

  - 不同類型數據占用內存空間的大小:整形(4字節),浮點型(8字節),字符型(1字節)

二、順序表

  集合中存儲的元素是有順序的。順序表的結構可以分為兩種形式:單數據類型和多數據類型。

  單數據類型:例如np.array

  單數據類型:內存連續開辟,在內存中存儲 int a = 10,20,30圖例如下

  

  多數據類型:例如python中的list

  多數據類型:內存非連續開辟,在內存中如何存儲 li = 10,'a',96.5,如何獲取每一個數據值呢?

  

   - 順序表的弊端:順序表的結構需要預先知道數據大小來申請連續的存儲空間,而在進行增加刪除時又需要進行數據的搬遷。

   - Python中的 list 和 tuple 兩種類型采用了順序表的實現技術。

三、鏈表

  相對於順序表,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。

  鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,但是不像順序表一樣連續存儲數據,而是每一個結點(數據存儲單元)里存放下一個結點的信息(即地址):

  

四、單向鏈表:

  單向鏈表也叫單鏈表,是表中最簡單的一種形式,它的每個節點包含兩個域,一個信息域(元素域)和一個鏈接域。這個鏈接指向鏈表中的下一個節點,而最后一個節點的鏈接域則指向一個空值。

    - 表中元素elem用來存放具體的數據。

    - 鏈接域next用來存放下一個節點的位置。

    - 變量p指向鏈表的頭節點(首節點)的位置,從p出發能找到表中的任意節點。

  單向鏈表的抽象數據類型定義:

    . is_empty():鏈表是否為空

    . length():鏈表長度

    . travel():遍歷整個鏈表

    . add(item):鏈表頭部添加元素

    . append(item):鏈表尾部添加元素

    . insert(pos, item):指定位置添加元素

    . remove(item):刪除節點

    . search(item):查找節點是否存在

# 第一步:創建節點(node)
class Node(object):
    def __init__(self,item):
        # 存放新節點的值
        self.item = item
        # 新節點沒有下一個鏈接地址
        self.next = None

# 創建鏈接
class Link(object):
    def __init__(self):
        # _head永遠指向的第一個節點的地址
        self._head = None
        
    # 添加一個鏈接
    def add(self,item):
        # 創建新節點
        node = Node(item)
        # 將新節點的next地址指向 舊節點的_head指向
        node.next = self._head
        # 新節點地址指向新節點內存空間地址
        self._head = node
        
    # 獲取鏈表值
    def travel(self):
        # 獲取第一個節點指針
        cur = self._head
        
        # 判斷鏈表next指向為空時,則為鏈表的最后一個節點
        while cur:
            # 打印節點item屬性值
            print(cur.item)
            # 將新的節點指針賦值給cur變量
            cur = cur.next
            
    def length(self):
        # 獲取第一個節點指針
        cur = self._head
        # 長度計數器
        count = 0
        
        # 判斷鏈表next指向為空時,則為鏈表的最后一個節點
        while cur:
            # 打印節點item屬性值
            count += 1
            # 將新的節點指針賦值給cur變量
            cur = cur.next
            
        # 打印計數器
        print("當前鏈表長度: ",count)
        return count
        


    # 判斷鏈表是否為空
    def isEmpty(self):
        # 判斷鏈表指針為None時,鏈表為空,返回True
        return self._head == None

    
    # 鏈表尾部追加元素
    def append(self,item):
        node = Node(item)

        # 判斷鏈表是否為空
        if self.isEmpty():
            # 鏈表為空,添加新節點
            self._head = node
            return

        # 鏈表不為空,追加節點
        cur = self._head
        # 定義一個變量,保存前一個節點的地址
        pre = None

        # 判斷
        while cur:
            pre = cur
            cur = cur.next

        # 最后next指針指向新加節點
        pre.next = node

    # 查詢鏈表 查詢成功返回True,否則返回False
    def search(self,item):
        find = False
        cur = self._head

        while cur:
            # 查詢成功
            if cur.item == item:
                find = True
                break
            else:
                # 鏈表指針指向下一個鏈表節點
                cur = cur.next

        return find

    # 鏈表的插入操作
    def insert(self,pos,item):
        # 查詢鏈表長度
        length = self.length()
        if pos <=0 or pos >length:
            print("插入位置超出范圍!!!")
            return

        # pos添加的位置規定從1開始
        node = Node(item)
        cur = self._head
        pre = None
        
        # 在位置1插入
        if pos == 1:
            node.next = self._head
            self._head = node
            return 

        # for循環,使鏈表指針指向pos插入位置
        for i in range(1,pos):
            pre = cur
            cur = cur.next

        # 此時鏈表指針指向需要插入數據的位置
        # 此時把pre指向的上一個指針地址指向插入節點的地址
        pre.next = node
        # 把插入節點地址指針指向下一個節點地址
        node.next = cur

    # 刪除指定位置節點
    def remove(self,item):
        # 獲取第一個節點指針
        cur = self._head
        pre = None

        # 此處沒有節點可以刪除
        if self._head == None:
            return

        # 刪除第一個節點
        if cur.item == item:
            self._head = cur.next
            return

        # 刪除其他位置節點
        while cur:
            # 將新的節點指針賦值給下一個節點地址
            if cur.item == item:
                pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next


# 鏈表測試 # 實例化鏈表對象 link = Link() # 添加節點 link.add(1) link.add(2) link.add(3) # 顯示鏈表值 link.travel() # 刪除元素2 link.remove(2) link.travel() # 插入在鏈表位置1處插入一個節點6 link.insert(4,8) link.travel()

五、單向循環鏈表

  單鏈表的一個變形是單向循環鏈表,鏈表中最后一個節點的next域不再為None,而是指向鏈表的頭結點。

  基本操作和單鏈表基本一樣,實現代碼如下:

# coding=utf-8
# 單向循環鏈表


class Node:
    """節點"""
    def __init__(self, item):
        self.item = item
        self.next = None

    def __str__(self):
        return str(self.item)


class SinCycLinkedList:
    """單向循環鏈表"""
    def __init__(self):
        self._head = None

    def is_empty(self):
        """判斷鏈表是否為空"""
        return self._head is None

    def length(self):
        """鏈表長度"""
        if self.is_empty():
            return 0
        count = 1
        cur = self._head
        while cur.next != self._head:
            # print("cur", cur.item)
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """遍歷"""
        if self.is_empty():
            return

        cur = self._head
        print(cur.item)
        while cur.next != self._head:
            cur = cur.next
            print(cur.item)

    def add(self, item):
        """在頭部添加一個節點"""
        node = Node(item)
        if self.is_empty():
            self._head = node
            node.next = self._head
        else:
            node.next = self._head
            cur = self._head
            while cur.next != self._head:
                cur = cur.next

            cur.next = node
            self._head = node

    def append(self, item):
        """在尾部添加一個節點"""
        node = Node(item)
        if self.is_empty():
            self._head = node
            node.next = self._head
        else:
            cur = self._head
            # print(type(cur), cur.item, cur.next)
            while cur.next != self._head:
                cur = cur.next

            # print(cur.item)
            cur.next = node
            node.next = self._head

    def insert(self, pos, item):
        """指定位置pos添加節點"""
        if pos <= 0:
            self.add(item)
        elif pos > (self.length() - 1):
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            cur_pos = 0
            while cur.next != self._head:
                if (pos - 1) == cur_pos:
                    node.next = cur.next
                    cur.next = node
                    break
                cur_pos += 1
                cur = cur.next

    def remove(self, item):
        """刪除一個節點"""
        if self.is_empty():
            return

        pre = self._head
        # 刪除首節點
        if pre.item == item:
            cur = pre
            while cur.next != self._head:
                cur = cur.next

            cur.next = pre.next     # 刪除首節點(跳過該節點)
            self._head = pre.next   # 重新指定首節點

        # 刪除其他的節點
        else:
            cur = pre
            while cur.next != self._head:
                if cur.next.item == item:
                    cur.next = cur.next.next
                cur = cur.next

    def search(self, item):
        """查找節點是否存在"""
        if self.is_empty():
            return -1

        cur_pos = 0
        cur = self._head
        if cur.item == item:
            return cur_pos

        while cur.next != self._head:
            if cur.item == item:
                return cur_pos
            cur_pos += 1
            cur = cur.next

        if cur_pos == self.length() - 1:
            return -1


if __name__ == "__main__":
    ll = SinCycLinkedList()
    ll.add(1)       # 1
    ll.add(2)       # 2 1
    # ll.travel()
    ll.append(3)    # 2 1 3
    ll.insert(2, 4) # 2 1 4 3
    ll.insert(4, 5) # 2 1 4 3 5
    ll.insert(0, 6) # 6 2 1 4 3 5
    print("length:", ll.length())        # 6
    ll.travel()                           # 6 2 1 4 3 5
    print("search(3)", ll.search(3))     # 4
    print("search(7)", ll.search(7))     # -1
    print("search(6)", ll.search(6))    # 0
    print("remove(1)")
    ll.remove(1)
    print("length:", ll.length())       # 6 2 4 3 5
    print("remove(6)")
    ll.remove(6)
    ll.travel()

六、雙向鏈表

  一種更復雜的鏈表是 "雙向鏈表" 或 "雙面鏈表"。每個節點有兩個鏈接:一個指向前一個節點,當次節點為第一個節點時,指向空值;而另一個指向下一個節點,當此節點為最后一個節點時,指向空值。

  

  代碼實現:

# coding=utf-8
# 雙向鏈表


class Node:
    """節點"""
    def __init__(self, item):
        self.item = item
        self.prev = None
        self.next = None


class DLinkList:
    """雙向鏈表"""
    def __init__(self):
        self._head = None

    def is_empty(self):
        """判斷鏈表是否為空"""
        return self._head is None

    def length(self):
        """獲取鏈表長度"""
        if self.is_empty():
            return 0
        else:
            cur = self._head
            count = 1
            while cur.next is not None:
                count += 1
                cur = cur.next

            return count

    def travel(self):
        """遍歷鏈表"""
        print("↓↓" * 10)
        if self.is_empty():
            print("")

        else:
            cur = self._head
            print(cur.item)
            while cur.next is not None:
                cur = cur.next
                print(cur.item)
        print("↑↑" * 10)

    def add(self, item):
        """鏈表頭部添加節點"""
        node = Node(item)
        if self.is_empty():
            self._head = node
        else:
            cur = self._head

            node.next = cur
            cur.prev = node
            self._head = node

    def append(self, item):
        """鏈表尾部添加節點"""
        node = Node(item)
        if self.is_empty():
            self._head = node
        else:
            cur = self._head
            # 遍歷找到最后一個節點
            while cur.next is not None:
                cur = cur.next

            # 在尾節點添加新的節點
            cur.next = node
            node.prev = cur

    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._head
            cur_pos = 0
            while cur.next is not None:
                if cur_pos == (pos - 1):
                    # 與下一個節點互相指向
                    node.next = cur.next
                    cur.next.prev = node
                    # 與上一個節點互相指向
                    cur.next = node
                    node.prev = cur
                cur_pos += 1
                cur = cur.next

    def remove(self, item):
        """刪除節點"""
        if self.is_empty():
            return
        else:
            cur = self._head
            # 刪除首節點
            if cur.item == item:
                self._head = cur.next
                cur.next.prev = None

            # 刪除其他節點
            else:
                while cur.next is not None:
                    if cur.item == item:
                        # 刪除之前:1 ←→ [2] ←→ 3
                        # 刪除之后:1 ←→ 3
                        cur.prev.next = cur.next
                        cur.next.prev = cur.prev
                    cur = cur.next

                # 刪除尾節點
                if cur.item == item:
                    cur.prev.next = None


    def search(self, item):
        """查找節點是否存在"""
        if self.is_empty():
            return -1
        else:
            cur = self._head
            cur_pos = 0
            while cur.next is not None:
                if cur.item == item:
                    return cur_pos

                cur_pos += 1
                cur = cur.next

            if cur_pos == (self.length() - 1):
                return -1


if __name__ == "__main__":
    ll = DLinkList()
    ll.add(1)       # 1
    ll.add(2)       # 2 1
    ll.append(3)    # 2 1 3
    ll.insert(2, 4) # 2 1 4 3
    ll.insert(4, 5) # 2 1 4 3 5
    ll.insert(0, 6) # 6 2 1 4 3 5
    print("length:", ll.length())   # 6
    ll.travel()                 # 6 2 1 4 3 5
    print("search(3)", ll.search(3))
    print("search(4)", ll.search(4))
    print("search(10)", ll.search(10))
    ll.remove(1)
    print("length:", ll.length())
    ll.travel()
    print("刪除首節點 remove(6):")
    ll.remove(6)
    ll.travel()
    print("刪除尾節點 remove(5):")
    ll.remove(5)
    ll.travel()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM