🔗用Python 實現 Doubly Linked List 雙向鏈結串列(鍊表)

更新於 2024/08/27閱讀時間約 30 分鐘

在資料結構與演算法裡,

最簡單的線性資料結構除了array之外就是linked list鏈結串列了。

Linked list又有分為單向Singly linked list 和雙向Doubly linked list


在之前的教學中,已經學會了單向鏈結串列Singly linked list

接著,我們將在今天學會更進階的雙向鏈結串列Doubly linked list


定義

每個節點Node只有三個欄位

data: 儲存這個節點的資料

next: 指向下一個節點的reference (在C/C++ 就是指標pointer) 對應到綠色的箭頭

prev: 指向前一個節點的reference (在C/C++ 就是指標pointer) 對應到紫色的箭頭


而雙向連結串列就是由數個節點所構成,形成由頭部逐一串接到尾巴的串列。

並且支援雙向拜訪可以從頭拜訪到尾,也可以從尾巴拜訪到頭部

raw-image

優點

1.在頭部、尾巴、或者已知節點的新增、刪除、修改都是O(1) 常數時間。

2.可以根據需求動態延伸或縮短,有效利用記憶體空間。

3.支援雙向拜訪可以從頭拜訪到尾,也可以從尾巴拜訪到頭部

缺點

1.不支援random acess,不支援Linkedlist[ index ] 這種操作。

2.如果要搜尋或者訪問某個元素,一定要從頭部或者尾巴開始拜訪(或搜尋)整個串列。

run-time成本相當昂貴,需要O(n)線性時間。


節點Node的Python實作

class Node:

  def __init__(self, data):

    # 儲存節點的資料
    self.data = data

    # 指向下一個節點,初始化為空 None
    self.next = None

    # 指向前一個節點,初始化為空 None
    self.prev = None

Doubly Linked List的class定義 與 建構子(初始化函式)

class LinkedList:

  def __init__(self): 

    # 頭部節點初始化為空
    self.head = None

    # 尾巴節點初始化為空
    self.tail = None  

    # 串列長度初始化為0
    self.size = 0

Doubly Linked List常見的操作


1.在頭部插入新節點


實作上的細節要留意,串列是空的還是已經有節點存在了。

如果是空的,直接加入即可。

如果已經有節點存在了,插入新節點之後,記得更新head頭部位置。


時間複雜度: O(1) 常數時間

  def insertAtHead(self,data):

    new_node = Node(data)

    self.size += 1

    # If the list is empty
    if self.tail is None:

      self.head = new_node
      self.tail = new_node

    else:
      # Update linkage
      new_node.next = self.head
      self.head.prev = new_node

      self.head = new_node

    return

2.在尾巴插入新節點


實作上的細節要留意,串列是空的還是已經有節點存在了。

如果是空的,直接加入即可。

如果已經有節點存在了,插入新節點之後,記得更新tail尾巴位置。


時間複雜度: O(1) 常數時間

  def insertAtTail(self, data):

    new_node = Node(data)

    self.size += 1

    # If the list is empty
    if self.head is None:

      self.head = new_node
      self.tail = new_node

    else:
      # Update linkage
      self.tail.next = new_node
      new_node.prev = self.tail

      self.tail = new_node

    return

3.在頭部刪除節點


實作上的細節要留意,串列是空的還是已經有節點存在了。

如果是空的,直接不做事return即可。

如果已經有節點存在了,刪除舊節點之後,記得更新head頭部位置。


時間複雜度: O(1) 常數時間

  def removeAtHead(self):

    if self.head is None:

      return

    else:

      self.size -= 1

      oldHead = self.head


      # Update linkage
      if self.head == self.tail:

        self.tail = None
        self.head = None

      else:

        self.head = self.head.next
     

      # Break linkage
      oldHead.next = None

      if self.head:
        self.head.prev = None

      return oldHead



4.在尾巴刪除節點


實作上的細節要留意,串列是空的還是已經有節點存在了。

如果是空的,直接不做事return即可。

如果已經有節點存在了,刪除舊節點之後,記得更新tail尾巴位置。


時間複雜度: O(1) 常數時間

  def removeAtTail(self):

if self.tail is None:
return

else:
self.size -= 1
oldTail = self.tail

# Update linkage
if self.tail == self.head:
self.tail = None
self.head = None

else:
self.tail = self.tail.prev

# Break linkage
oldTail.prev = None

if self.tail:
self.tail.next = None

return oldTail

5.順向拜訪整個串列


從頭拜訪整個串列,直到尾巴。

最後順便輸出整個串列長度。


時間複雜度: O(n) 線性時間

  def traverse(self):

print("Forward traversal")
cur = self.head

while cur:
print(cur.data, end=' -> ')
cur = cur.next
print("None")

print(f"Size of singly linked list: {self.size} \n", )
return

6.反向拜訪整個串列


從尾巴反向拜訪整個串列,直到頭部。

最後順便輸出整個串列長度。


時間複雜度: O(n) 線性時間

  def backward_traverse(self):

print("Backward traversal")
cur = self.tail

while cur:
print(cur.data, end=' -> ')
cur = cur.prev
print("None")

print(f"Size of singly linked list: {self.size} \n", )
return

7.查詢某筆資料是否存在


從頭拜訪整個串列,查詢某筆資料是否存在。

如果存在,返回該節點。

如果不存在,返回None(空)。


時間複雜度: O(n) 線性時間

  def search(self, target):

cur = self.head
while cur:
if cur.data == target:
return cur

cur = cur.next

return None

8.刪除已知節點的下一個節點


實作上的細節要留意。

記得把串列長度減一。

並且串接後面剩下的節點。


時間複雜度: O(1) 常數時間

  def removeAfter(self, node):

self.size -= 1

removed = node.next
the_one_after_removal = node.next.next if node.next else None

# Update linkage
if the_one_after_removal:
the_one_after_removal.prev = node

node.next = the_one_after_removal

# Break linkage
if removed:
removed.next = None
removed.prev = None

return removed

9.在已知節點的後面新增一個節點


實作上的細節要留意。

記得把串列長度加一。

並且在新節點之後串接上原本後面的節點。


時間複雜度: O(1) 常數時間

  def insertAfter(self, node, new_node):

self.size += 1

the_one_after_insertion = node.next

# Update linkage
node.next = new_node
new_node.prev = node

the_one_after_insertion.prev = new_node
new_node.next = the_one_after_insertion
return

完整的Doubly Linked List實作和程式碼


class Node:
def __init__(self, data):
# 儲存節點的資料
self.data = data

# 指向下一個節點,初始化為空 None
self.next = None

# 指向前一個節點,初始化為空 None
self.prev = None


class LinkedList:

def __init__(self):

# 頭部節點初始化為空
self.head = None
# 尾巴節點初始化為空
self.tail = None

# 串列長度初始化為0
self.size = 0


def insertAtHead(self,data):

new_node = Node(data)

self.size += 1

# If the list is empty
if self.tail is None:
self.head = new_node
self.tail = new_node
else:

# Update linkage
new_node.next = self.head
self.head.prev = new_node

self.head = new_node
return


def insertAtTail(self, data):

new_node = Node(data)

self.size += 1

# If the list is empty
if self.head is None:
self.head = new_node
self.tail = new_node
else:

# Update linkage
self.tail.next = new_node
new_node.prev = self.tail

self.tail = new_node

return


def removeAtHead(self):

if self.head is None:
return
else:
self.size -= 1
oldHead = self.head

# Update linkage
if self.head == self.tail:
self.tail = None
self.head = None
else:
self.head = self.head.next

# Break linkage
oldHead.next = None

if self.head:
self.head.prev = None

return oldHead


def removeAtTail(self):

if self.tail is None:
return
else:
self.size -= 1
oldTail = self.tail

# Update linkage
if self.tail == self.head:
self.tail = None
self.head = None
else:
self.tail = self.tail.prev

# Break linkage
oldTail.prev = None

if self.tail:
self.tail.next = None

return oldTail


def traverse(self):

print("Forward traversal")
cur = self.head

while cur:
print(cur.data, end=' <-> ')
cur = cur.next
print("None")

print(f"Size of singly linked list: {self.size} \n", )
return


def backward_traverse(self):

print("Backward traversal")
cur = self.tail

while cur:
print(cur.data, end=' <-> ')
cur = cur.prev
print("None")

print(f"Size of singly linked list: {self.size} \n", )
return

def search(self, target):
cur = self.head
while cur:
if cur.data == target:
return cur

cur = cur.next

return None


def removeAfter(self, node):

self.size -= 1

removed = node.next
the_one_after_removal = node.next.next if node.next else None

# Update linkage
if the_one_after_removal:
the_one_after_removal.prev = node

node.next = the_one_after_removal
# Break linkage
if removed:
removed.next = None
removed.prev = None

return removed


def insertAfter(self, node, new_node):

self.size += 1

the_one_after_insertion = node.next

# Update linkage
node.next = new_node
new_node.prev = node

the_one_after_insertion.prev = new_node
new_node.next = the_one_after_insertion
return


def test():

print(f"Empty list\n")
linkedlist = LinkedList()
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Insert 3 on tail\n")
linkedlist.insertAtTail(3)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Insert 4 on tail\n")
linkedlist.insertAtTail(4)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Insert 5 on tail\n")
linkedlist.insertAtTail(5)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Insert 2 on head\n")
linkedlist.insertAtHead(2)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Insert 1 on head\n")
linkedlist.insertAtHead(1)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Search 3 in linked list\n")
result = linkedlist.search(3)
print( result.data )

print("--------------------")
print(f"Remove node 4, which is after node 3\n")
linkedlist.removeAfter(result)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
node_100 = Node(100)
print(f"Insert node 100 after node 3\n")
linkedlist.insertAfter(result, node_100)
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Remove 1 on Head\n")
linkedlist.removeAtHead()
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Remove 2 on Head\n")
linkedlist.removeAtHead()
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Remove 3 on Head\n")
linkedlist.removeAtHead()
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Remove 5 on Tail\n")
linkedlist.removeAtTail()
linkedlist.traverse()
linkedlist.backward_traverse()

print("--------------------")
print(f"Remove 100 on Tail\n")
result = linkedlist.removeAtTail()
print(f"Removed node value = {result.data} \n")
linkedlist.traverse()
linkedlist.backward_traverse()

return

if __name__ == "__main__":
test()

測試輸出

Empty list

Forward traversal
None
Size of singly linked list: 0

Backward traversal
None
Size of singly linked list: 0

--------------------
Insert 3 on tail

Forward traversal
3 <-> None
Size of singly linked list: 1

Backward traversal
3 <-> None
Size of singly linked list: 1

--------------------
Insert 4 on tail

Forward traversal
3 <-> 4 <-> None
Size of singly linked list: 2

Backward traversal
4 <-> 3 <-> None
Size of singly linked list: 2

--------------------
Insert 5 on tail

Forward traversal
3 <-> 4 <-> 5 <-> None
Size of singly linked list: 3

Backward traversal
5 <-> 4 <-> 3 <-> None
Size of singly linked list: 3

--------------------
Insert 2 on head

Forward traversal
2 <-> 3 <-> 4 <-> 5 <-> None
Size of singly linked list: 4

Backward traversal
5 <-> 4 <-> 3 <-> 2 <-> None
Size of singly linked list: 4

--------------------
Insert 1 on head

Forward traversal
1 <-> 2 <-> 3 <-> 4 <-> 5 <-> None
Size of singly linked list: 5

Backward traversal
5 <-> 4 <-> 3 <-> 2 <-> 1 <-> None
Size of singly linked list: 5

--------------------
Search 3 in linked list

3
--------------------
Remove node 4, which is after node 3

Forward traversal
1 <-> 2 <-> 3 <-> 5 <-> None
Size of singly linked list: 4

Backward traversal
5 <-> 3 <-> 2 <-> 1 <-> None
Size of singly linked list: 4

--------------------
Insert node 100 after node 3

Forward traversal
1 <-> 2 <-> 3 <-> 100 <-> 5 <-> None
Size of singly linked list: 5

Backward traversal
5 <-> 100 <-> 3 <-> 2 <-> 1 <-> None
Size of singly linked list: 5

--------------------
Remove 1 on Head

Forward traversal
2 <-> 3 <-> 100 <-> 5 <-> None
Size of singly linked list: 4

Backward traversal
5 <-> 100 <-> 3 <-> 2 <-> None
Size of singly linked list: 4

--------------------
Remove 2 on Head

Forward traversal
3 <-> 100 <-> 5 <-> None
Size of singly linked list: 3

Backward traversal
5 <-> 100 <-> 3 <-> None
Size of singly linked list: 3

--------------------
Remove 3 on Head

Forward traversal
100 <-> 5 <-> None
Size of singly linked list: 2

Backward traversal
5 <-> 100 <-> None
Size of singly linked list: 2

--------------------
Remove 5 on Tail

Forward traversal
100 <-> None
Size of singly linked list: 1

Backward traversal
100 <-> None
Size of singly linked list: 1

--------------------
Remove 100 on Tail

Removed node value = 100

Forward traversal
None
Size of singly linked list: 0

Backward traversal
None
Size of singly linked list: 0


結語


一開始接觸linked list可能會覺得有點卡卡的,不像array 那樣操作起來那麼直覺。


可以透過紙筆追蹤演算法和程式執行邏輯,測試幾個簡單的小範例,

會有更深刻的了解和體會!


Linked List相關的演算法練習題與詳解


經典串列題 合併已排序好的兩條串列 Merge Two Sorted Lists Leetcode #21


串列應用題 移除尾巴數來的第n個節點 Leetcode #19


鍊表應用: 簡化鏈結串列 Remove Zero Sum Nodes_Leetcode #1171


嵌套娃娃 用遞迴解 串列化簡題 Leetcode #2487


串列應用: 合併非零的節點 Merge Nodes in Between Zeros_Leetcode #2181


複製客製化鏈結串列 Copy List w/ Random Pointer_Leetcode #138


一魚多吃 用雙指針找出串列的中點 Middle of Linked list_Leetcode #876


串列應用: 反轉整個串列Reverse Linked List_Leetcode #206_Leetcode 精選75


串列應用: 刪除鏈結串列的中央節點_Leetcode #2095_Leetcode精選75題


鏈結串列中的Twin Sum的最大值_Leetcode #2130_Leetcode 75題精選


重組為奇串列和偶串列 Odd Even Linked List_Leetcode #328 精選75題


合縱連橫: 從鏈結串列應用題 理解 遞回 背後的本質

avatar-img
90會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
在這次的BMI(身體質量指標)計算教學裡, 將學會如何用python來接收使用者輸入的訊息,並且做一些簡單的四則運算。
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
在程式語言裡,最基本的第一堂課通常就是最簡單也最直接的輸入和輸出, 今天,會從大家耳熟能詳的"Hello Wolrd"這個經典範例開始介紹 Python的基本的輸出print語法,最後以冷笑話的範例作為結尾的Demo。 從電腦的視角來看,什麼叫做Output輸出?
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
賓果的遊戲描述 在一個5x5的方陣上隨機填充1~25的數字。 玩家(使用者) 和 電腦(AI)輪流叫一個號碼,最先占據一整條直線連線的獲勝。 就像小時候玩的bingo 賓果連線遊戲一樣! (可以是占據兩條對角線,可以是占據水平直線,可以是占據垂直直線)
前言 相傳有一個故事, 數學家高斯的小學數學老師出了一道從1+2+3+...+100的習題, 想讓活潑好動的小學生們算一整節課,消耗一下多餘的體力, 結果老師剛說完題目沒過多久,小高斯就算出了答案。 原來,他發現數列兩端可以兩兩配對:1+100,2+99……每一對的和都是101,共有50對,所
在這次的BMI(身體質量指標)計算教學裡, 將學會如何用python來接收使用者輸入的訊息,並且做一些簡單的四則運算。
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
在程式語言裡,最基本的第一堂課通常就是最簡單也最直接的輸入和輸出, 今天,會從大家耳熟能詳的"Hello Wolrd"這個經典範例開始介紹 Python的基本的輸出print語法,最後以冷笑話的範例作為結尾的Demo。 從電腦的視角來看,什麼叫做Output輸出?
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
賓果的遊戲描述 在一個5x5的方陣上隨機填充1~25的數字。 玩家(使用者) 和 電腦(AI)輪流叫一個號碼,最先占據一整條直線連線的獲勝。 就像小時候玩的bingo 賓果連線遊戲一樣! (可以是占據兩條對角線,可以是占據水平直線,可以是占據垂直直線)
前言 相傳有一個故事, 數學家高斯的小學數學老師出了一道從1+2+3+...+100的習題, 想讓活潑好動的小學生們算一整節課,消耗一下多餘的體力, 結果老師剛說完題目沒過多久,小高斯就算出了答案。 原來,他發現數列兩端可以兩兩配對:1+100,2+99……每一對的和都是101,共有50對,所
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
AWS Lambda是? AWS Lambda 就像是開發者的救星,因為它是一種 Serverless 服務,意思是你不用再為架設和維護伺服器煩惱了。對於每天都忙得焦頭爛額的開發者來說,產品開發已經夠讓人抓狂了,還要處理伺服器的設定和維運(e.g. OS、Network、機器管理、防火牆),想想就
紀錄一些,實用的re正則表達式需要用時,就可以直接複製貼上。 檢查是否有中文字
Thumbnail
先上成果圖,如果是螺母的話就標註 is circle來區分。 簡單的用圖表加文字說明AOI辨識 在此文章的範例中: 影像前處理:色彩空間轉換(灰階) -> 二值化閥值處理 演算法:尋找輪廓 數值判斷:長,寬,面積,周長 圖片來源 程式碼 import cv2 import nu
情感分析是一種自然語言處理技術,用於自動識別和分析文本中的情感傾向,通常是正向、負向或中性。 我們可以使用 NLTK 來實現一個基於單純貝斯分類器的情感分析模型。
Thumbnail
初入IT產業的人士在學習Python語言後,IT證照如ITS Python認證是否值得考取?本文以ITS證照特點、實施建議和IT認證考試資訊為主,詳述證照的好處和準備時間。
Thumbnail
本文主要應用deepface的正面(frontal)人臉檢測的預設模型,使用analyze 函數,用於分析一張人臉圖像的情感(emotion)。 在Colab上實現,若用其他平台需稍微修改程式碼。 Deepface Deepface是一個輕量級的Python人臉辨識和臉部屬性分析
Thumbnail
根據泰勒定理,f(x)可以寫成右邊一長串的導數的組合 為了更好理解這個東西我們可以用python實作 首先定義f(x)和定義factorial怎麼算 然後寫泰勒定理 f(x) = f(a) + f'(a)(x-a) ....後面還有一串 注意公式往後面看其實是有規律的 例如從
Thumbnail
在21世紀的技術浪潮中,「Python」不僅是程式設計的代表性語言,更是從初學者到資深工程師的共同選擇。除了在網頁開發、大數據和AI等專業領域中的應用,Python在全球的開發者社群中也建立了一個繁榮的生態系統,推動技術進步。然而,背後還隱藏著許多鮮為人知的故事和趣味,等待著我們去探索與發掘。
Thumbnail
前言 上篇把定位講完,不過實務上很少真的用手刻,大多用錄製或者軟體輔助,先講XPATH主要是讓大家有個底,就像最近的AI風一樣,好玩是一回事,做出來的東西還是要人看得懂知道哪裡可能有問題。 這篇就會著重介紹如何錄製腳本並轉換成可以執行的程式。
Thumbnail
如果,你是個專業的工程師,在外面臨時遇到需要修改或測試專案的情況,但手邊又沒有電腦跟網路,又或是,今天你臨時需要外出一陣子,但又放不下自己的專案,你會怎麼辦呢?
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
AWS Lambda是? AWS Lambda 就像是開發者的救星,因為它是一種 Serverless 服務,意思是你不用再為架設和維護伺服器煩惱了。對於每天都忙得焦頭爛額的開發者來說,產品開發已經夠讓人抓狂了,還要處理伺服器的設定和維運(e.g. OS、Network、機器管理、防火牆),想想就
紀錄一些,實用的re正則表達式需要用時,就可以直接複製貼上。 檢查是否有中文字
Thumbnail
先上成果圖,如果是螺母的話就標註 is circle來區分。 簡單的用圖表加文字說明AOI辨識 在此文章的範例中: 影像前處理:色彩空間轉換(灰階) -> 二值化閥值處理 演算法:尋找輪廓 數值判斷:長,寬,面積,周長 圖片來源 程式碼 import cv2 import nu
情感分析是一種自然語言處理技術,用於自動識別和分析文本中的情感傾向,通常是正向、負向或中性。 我們可以使用 NLTK 來實現一個基於單純貝斯分類器的情感分析模型。
Thumbnail
初入IT產業的人士在學習Python語言後,IT證照如ITS Python認證是否值得考取?本文以ITS證照特點、實施建議和IT認證考試資訊為主,詳述證照的好處和準備時間。
Thumbnail
本文主要應用deepface的正面(frontal)人臉檢測的預設模型,使用analyze 函數,用於分析一張人臉圖像的情感(emotion)。 在Colab上實現,若用其他平台需稍微修改程式碼。 Deepface Deepface是一個輕量級的Python人臉辨識和臉部屬性分析
Thumbnail
根據泰勒定理,f(x)可以寫成右邊一長串的導數的組合 為了更好理解這個東西我們可以用python實作 首先定義f(x)和定義factorial怎麼算 然後寫泰勒定理 f(x) = f(a) + f'(a)(x-a) ....後面還有一串 注意公式往後面看其實是有規律的 例如從
Thumbnail
在21世紀的技術浪潮中,「Python」不僅是程式設計的代表性語言,更是從初學者到資深工程師的共同選擇。除了在網頁開發、大數據和AI等專業領域中的應用,Python在全球的開發者社群中也建立了一個繁榮的生態系統,推動技術進步。然而,背後還隱藏著許多鮮為人知的故事和趣味,等待著我們去探索與發掘。
Thumbnail
前言 上篇把定位講完,不過實務上很少真的用手刻,大多用錄製或者軟體輔助,先講XPATH主要是讓大家有個底,就像最近的AI風一樣,好玩是一回事,做出來的東西還是要人看得懂知道哪裡可能有問題。 這篇就會著重介紹如何錄製腳本並轉換成可以執行的程式。
Thumbnail
如果,你是個專業的工程師,在外面臨時遇到需要修改或測試專案的情況,但手邊又沒有電腦跟網路,又或是,今天你臨時需要外出一陣子,但又放不下自己的專案,你會怎麼辦呢?