链表基础概念与经典题目(Leetcode题解-Python语言)
所謂鏈表,就是由鏈節(jié)點元素組成的表,那什么是鏈節(jié)點呢?直接上定義:
class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = next很簡單,鏈節(jié)點就是只記錄自身的值 val,還有其指向的下一個鏈節(jié)點的位置 next。
707. 設計鏈表
class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextclass MyLinkedList:def __init__(self):self.size = 0self.head = ListNode(0)def get(self, index: int) -> int:if index < 0 or index >= self.size:return -1curr = self.head# 找到查找的位置for _ in range(index + 1): # 注意是 index + 1curr = curr.nextreturn curr.val def addAtHead(self, val: int) -> None:self.addAtIndex(0, val)def addAtTail(self, val: int) -> None:self.addAtIndex(self.size, val)def addAtIndex(self, index: int, val: int) -> None:if index > self.size:returnif index < 0:index = 0# size加一,并且找到插入位置self.size += 1pred = self.headfor _ in range(index):pred = pred.next# 插入節(jié)點三步走:新建節(jié)點,新節(jié)點指向下一個節(jié)點,新節(jié)點被上一個節(jié)點指向to_add = ListNode(val)to_add.next = pred.nextpred.next = to_adddef deleteAtIndex(self, index: int) -> None:if index < 0 or index >= self.size:return# size減一,并且找到刪除位置self.size -= 1pred = self.headfor _ in range(index):pred = pred.next# 直接跳過節(jié)點即為刪除pred.next = pred.next.next由鏈節(jié)點出發(fā),設計單鏈表的思路如下:初始化的時候,要新建一個頭節(jié)點 head(即第一個節(jié)點),同時為了方便要記錄鏈表的大小 size。然后就是三種操作:查找、插入和刪除。
查找:首先判斷索引是否越界,越界就返回 -1,不越界才繼續(xù)。從頭節(jié)點開始,根據(jù)鏈節(jié)點 的 next 找到其下一個節(jié)點,循環(huán) index 次找到目標節(jié)點,返回其值。注意是寫成 range(index + 1) !!
插入:同樣先判斷是否越界。不越界則可以插入,此時鏈表的大小 size 會 +1,接著循環(huán) index 次找到目標節(jié)點(插入位置),然后是插入三步走:新建節(jié)點,新節(jié)點指向下一個節(jié)點,新節(jié)點被上一個節(jié)點指向。對于從頭部、中間、尾部插入都一樣,只是位置不同而已。
刪除:同樣先判斷是否越界。不越界則可以刪除,此時鏈表的大小 size 會 -1,接著循環(huán) index 次找到目標節(jié)點(刪除位置),然后直接跳過此節(jié)點 next = next.next,即為刪除。
劍指 Offer 22. 鏈表中倒數(shù)第k個節(jié)點(面試題 02.02. 返回倒數(shù)第 k 個節(jié)點)
class Solution:def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:nums = []while head:nums.append(head)head = head.nextreturn nums[-k]遍歷鏈表,返回倒數(shù)第k個節(jié)點的值。當然也可以用雙指針,一個指針先走 k 步,然后再一起走,那先出發(fā)的指針到最后時,后出發(fā)的指針正好在倒數(shù)第 k 個節(jié)點處,代碼如下:
class Solution:def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:fast = headslow = headfor i in range(k):if not fast:return Nonefast = fast.nextwhile fast:fast = fast.nextslow = slow.nextreturn slow劍指 Offer 06. 從尾到頭打印鏈表
class Solution:def reversePrint(self, head: ListNode) -> List[int]:nums = []while head:nums.append(head.val)head = head.nextreturn nums[::-1]遍歷鏈表,倒序返回鏈表所有的值。
876. 鏈表的中間結點
class Solution:def middleNode(self, head: ListNode) -> ListNode:all_nodes = []while head:all_nodes.append(head)head = head.nextreturn all_nodes[len(all_nodes) // 2]遍歷鏈表,記錄鏈節(jié)點,然后返回中間那個。當然,更好地是用快慢指針,慢指針每次移到next,快指針每次移到 next 的 next,循環(huán)結束返回慢指針即為中間節(jié)點。如下代碼所示:
class Solution:def middleNode(self, head: ListNode) -> ListNode:fast = headslow = headwhile fast and fast.next: # 兩個中間結點取后者,就這樣寫fast = fast.next.nextslow = slow.nextreturn slow如果兩個中間結點要取前者,條件就變?yōu)?fast.next and fast.next.next,如下所示:
class Solution:def middleNode(self, head: ListNode) -> ListNode:fast = headslow = headwhile fast.next and fast.next.next: fast = fast.next.nextslow = slow.nextreturn slow141. 環(huán)形鏈表
class Solution:def hasCycle(self, head: ListNode) -> bool:if not head or not head.next:return Falseslow, fast = head, headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextif slow == fast:return Truereturn False快慢指針更典型的例子是環(huán)形鏈表,如果鏈表是有環(huán)的,則快指針一定會從后面追上慢指針,否則它們不會相遇。這是因為如果快慢指針都進入了環(huán),則由于快指針比慢指針速度快了 1,所以它們之間的距離一定是能被縮小到 0 的。
使用快慢指針時要注意:如果初始化兩個指針都是 head,則 while 循環(huán)中必須先賦值再判斷是否相等,而不能先判斷相等(因為初始時就是相等的)。
142. 環(huán)形鏈表 II(劍指 Offer II 022. 鏈表中環(huán)的入口節(jié)點)(面試題 02.08. 環(huán)路檢測)
class Solution:def detectCycle(self, head: ListNode) -> ListNode:if not head or not head.next:return Noneslow, fast = head, headflag = Falsewhile fast and fast.next:slow = slow.nextfast = fast.next.nextif slow == fast:flag = Truebreakif not flag:return Noneans = headwhile ans != slow:ans = ans.nextslow = slow.nextreturn ans這題不僅要判斷是否環(huán)形,還要找出入環(huán)點,作圖分析即可,假設鏈表頭到入環(huán)點距離為 D,慢指針走了 D + S1 的距離,快指針走了 D + S1 + n(S1 + S2) 的距離,其中 S1 + S2 為一圈的長度。顯然,D + S1 + n(S1 + S2) = 2 (D + S1),可得 D = (n - 1)(S1 + S2) + S2,由于慢指針已經(jīng)從入環(huán)點開始走了 S1,因此它只要再走 (n - 1)(S1 + S2) + S2 步,即可到達入環(huán)點,而這個步數(shù)可由另一個慢指針從鏈表頭開始走 D 步來匹配。注意不要漏了 break!
160. 相交鏈表(劍指 Offer II 023. 兩個鏈表的第一個重合節(jié)點)(面試題 02.07. 鏈表相交)
class Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:curA = headAcurB = headBwhile curA != curB:curA = curA.next if curA else headBcurB = curB.next if curB else headAreturn curA同樣是雙指針思路(非快慢),分別從兩個鏈表同時出發(fā),遍歷完一個鏈表就去遍歷另一個鏈表,如果鏈表相交,則兩個指針一定相遇于交點,否則同時為 None。注意必須是 if curA 而不是 if curA.next,才能使得 curA == curB == None,否則它們是不會變?yōu)?None 的。
61. 旋轉鏈表
class Solution:def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:if not head:return Nonecur = headlength = 1while cur.next:cur = cur.nextlength += 1cur.next = headnew_tail = headfor _ in range((length - k - 1) % length):new_tail = new_tail.nextnew_head = new_tail.nextnew_tail.next = Nonereturn new_head這題把鏈表變成環(huán)形之后,找到目標切分點即可。注意到切分點左右分別是新的尾節(jié)點 new_tail 和新的頭節(jié)點 new_head,而新的尾節(jié)點 new_tail 距離原頭節(jié)點 head 有 length - k - 1,且這個距離是隨著 k 增大重復出現(xiàn)的,所以還得對 length 取余。
203. 移除鏈表元素(劍指 Offer 18. 刪除鏈表的節(jié)點)
class Solution:def removeElements(self, head: ListNode, val: int) -> ListNode:dummy_head = ListNode(next=head) cur = dummy_headwhile cur.next:if cur.next.val == val:cur.next = cur.next.nextelse:cur = cur.nextreturn dummy_head.next設置一個虛擬頭節(jié)點 dummy_head,然后判斷 cur.next 是否是目標值,如果是則刪除(cur.next 指向 cur.next.next),如果不是則移動到 cur.next,循環(huán)判斷 cur.next。最后返回虛擬頭節(jié)點的 next ,即真正的頭節(jié)點。
237. 刪除鏈表中的節(jié)點(面試題 02.03. 刪除中間節(jié)點)
class Solution:def deleteNode(self, node):""":type node: ListNode:rtype: void Do not return anything, modify node in-place instead."""node.val = node.next.valnode.next = node.next.next題目給出要刪除的中間節(jié)點 node 而不是頭節(jié)點,那就把該節(jié)點 node 變成其下一個節(jié)點 node.next,然后刪除下一個節(jié)點即可。
83. 刪除排序鏈表中的重復元素
class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:if not head:return headcur = headwhile cur.next:if cur.val == cur.next.val:cur.next = cur.next.nextelse:cur = cur.nextreturn head刪除排序鏈表中的重復元素,判斷當前節(jié)點與下一個節(jié)點是否相等即可。如果下一個節(jié)點的值與當前節(jié)點的值相同,就把當前節(jié)點的 next 指針往后移,直到指針指向了一個與當前節(jié)點值有不同值的節(jié)點,然后跳到這個節(jié)點,重復開始判斷。
由于這題是一定不會刪除頭節(jié)點的,所以不需要創(chuàng)建虛擬頭節(jié)點,但如果創(chuàng)建,必須保證虛擬頭節(jié)點的值在鏈表節(jié)點值的范圍之外。
82. 刪除排序鏈表中的重復元素 II
class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:if not head:return headdummy_head = ListNode(101, head)pre = dummy_headcur = pre.nextwhile cur and cur.next:if cur.val == cur.next.val:while cur.next and cur.val == cur.next.val:cur = cur.nextpre.next = cur.nextcur = cur.nextelse:pre = pre.nextcur = cur.nextreturn dummy_head.next由于要刪除所有重復出現(xiàn)的節(jié)點,所以用 pre 記錄上一個不重復節(jié)點的位置,cur 遇到重復元素則一直到重復區(qū)間的最后。創(chuàng)建虛擬頭節(jié)點,必須保證它的值在鏈表節(jié)點值的范圍之外。
面試題 02.01. 移除重復節(jié)點
class Solution:def removeDuplicateNodes(self, head: ListNode) -> ListNode:if not head:return headnums = {head.val}cur = headwhile cur.next:if cur.next.val in nums:cur.next = cur.next.nextelse:nums.add(cur.next.val)cur = cur.nextreturn head與上一題相比,鏈表不是有序的,所以得用哈希表(集合)記錄出現(xiàn)過的元素,后面如果再次出現(xiàn)則刪除。
19. 刪除鏈表的倒數(shù)第 N 個結點
class Solution:def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:dummyHead = ListNode(0, head)fast = dummyHeadslow = dummyHeadfor _ in range(n):fast = fast.nextwhile fast.next:fast = fast.nextslow = slow.nextslow.next = slow.next.nextreturn dummyHead.next刪除鏈表的倒數(shù)第 n 個節(jié)點,用雙指針法,第一個指針先遍歷 n 次,然后兩個指針一起遍歷,這樣當?shù)谝粋€指針遍歷完之后,第二個指針正好遍歷了(鏈表長度 - n)次,其位置即為要刪除的位置。注意由于可能刪除頭節(jié)點,所以要用 dummyHead。
24. 兩兩交換鏈表中的節(jié)點
class Solution:def swapPairs(self, head: ListNode) -> ListNode:if not head or not head.next:return headdummyHead = ListNode(0, head)pre = dummyHeadcur = dummyHead.nextwhile cur and cur.next:# cur 與 post 進行交換post = cur.nextcur.next = post.nextpost.next = pre.nextpre.next = post# 來到下一個位置pre = curcur = cur.nextreturn dummyHead.nextpre 初始化為 dummyHead,然后看后面是否有兩個節(jié)點,有的話就交換這兩個節(jié)點,并且pre 等于交換后的在后面的節(jié)點(也是交換前的在前面的節(jié)點),繼續(xù)看看后面是否有一對節(jié)點,如果沒有則直接返回。對于交換寫法,可以發(fā)現(xiàn)變量是頭尾相連的,以此作為記憶。
206. 反轉鏈表(劍指 Offer 24. 反轉鏈表)(劍指 Offer II 024. 反轉鏈表)
迭代法
class Solution:def reverseList(self, head: ListNode) -> ListNode:pre = Nonecur = headwhile cur:temp = cur.next # 把下個指針記到 tempcur.next = pre # cur 的 next 指針反過來指向 prepre = cur # 已反向的 cur 變?yōu)?precur = temp # cur 向右移一位return pre使用前一個節(jié)點 pre 和當前節(jié)點 cur。在節(jié)點 cur 時,先把下個指針記到 temp,然后 cur 的 next 指針反過來指向 pre,已反向的 cur 變?yōu)?pre,然后 cur 向右移一位,直到鏈表結束。在寫法上,可以發(fā)現(xiàn)變量是頭尾相連的,以此作為記憶。
遞歸法
class Solution:def reverse(self, pre, cur):if cur == None:return pretemp = cur.nextcur.next = prereturn self.reverse(cur, temp)def reverseList(self, head: ListNode) -> ListNode:return self.reverse(None, head)無非就是用遞歸代替了 pre 與 cur 后移一位的操作而已。
從后往前的遞歸法
class Solution:def reverseList(self, head: ListNode) -> ListNode:if not head or not head.next:return headcur = self.reverseList(head.next) # 找到鏈表尾部head.next.next = head # 把下個節(jié)點的 next 指針指向自己head.next = None # 刪掉自己指向下個節(jié)點的 nextreturn cur先找到鏈表尾部,然后從尾部開始把下個節(jié)點的 next 指針指向自己,同時刪掉自己指向下個節(jié)點的 next,遞歸返回前一個節(jié)點,直到遇到空節(jié)點為止(從尾部到頭部)。
92. 反轉鏈表 II
class Solution:def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:dummy_head = ListNode(-1)dummy_head.next = headpre = dummy_headfor _ in range(left - 1):pre = pre.nextcur = pre.nextfor _ in range(right - left):post = cur.next # post一定是cur的nextcur.next = post.next # 讓cur指向后兩位post.next = pre.next # post指向頭后一位pre.next = post # 頭后一位變?yōu)閜ostreturn dummy_head.next這題要求反轉鏈表中的某一段,頭插法是很不錯的解法,思路詳細看這篇題解。大概就是說,找到要反轉的區(qū)域,然后遍歷其中的元素,每次都把元素移動到區(qū)域的最前面(頭插),這樣遍歷完整個區(qū)域,也就完成了反轉。pre 恒為區(qū)域外左邊第一個節(jié)點;而 cur 是最開始 pre 的后一個節(jié)點,例如示例圖中值為 2 的節(jié)點,只不過 cur 會不斷地往右移位;每次遍歷時 post 作為 cur.next,都會被送到 pre 的右邊(即區(qū)域的最前面)。
25. K 個一組翻轉鏈表
class Solution:def reverseKGroup(self, head: ListNode, k: int) -> ListNode:count = 0cur = headwhile cur:cur = cur.nextcount += 1dummy_head = ListNode(-1)dummy_head.next = headpre = dummy_headcur = pre.nextfor _ in range(count // k):for _ in range(k - 1):temp = cur.nextcur.next = temp.nexttemp.next = pre.nextpre.next = temppre = curcur = pre.nextreturn dummy_head.next一道困難題,借鑒上題的頭插法思路就不難了。首先得到鏈表的長度 count,可知一共要反轉(count // k)組,之后在每組內(nèi)進行頭插法,搞完一組后更新一下 pre 和 cur 即可。
2. 兩數(shù)相加(面試題 02.05. 鏈表求和)
class Solution:def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:sum = l1.val + l2.valcarry = sum // 10head = ListNode(sum % 10)node = headwhile l1.next or l2.next or carry:l1 = l1.next if l1.next else ListNode(0)l2 = l2.next if l2.next else ListNode(0)sum = l1.val + l2.val + carrycarry = sum // 10node.next = ListNode(sum % 10)node = node.nextreturn head兩個鏈表對應位置相加,注意數(shù)字是逆序存儲的,即鏈表頭的數(shù)字是最低位,所以相加時會產(chǎn)生進位。兩個鏈表不等長的話短的那個加0,余數(shù)作為結果鏈表的新節(jié)點,而商數(shù)除以10后作為進位(下一位的加數(shù)之一),最后如果還有一個進位不要漏了。
寫法統(tǒng)一后面那題的話,就是用隊列:
class Solution:def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:q1, q2 = [], []while l1:q1.append(l1.val)l1 = l1.nextwhile l2:q2.append(l2.val)l2 = l2.nexthead = ListNode(0)node = headcarry = 0while q1 or q2 or carry != 0:a = q1.pop(0) if q1 else 0b = q2.pop(0) if q2 else 0sum = a + b + carrycarry = sum // 10newNode = ListNode(sum % 10)node.next = newNodenode = node.nextreturn head.next445. 兩數(shù)相加 II(劍指 Offer II 025. 鏈表中的兩數(shù)相加)
class Solution:def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:s1, s2 = [], []while l1:s1.append(l1.val)l1 = l1.nextwhile l2:s2.append(l2.val)l2 = l2.nexthead = Nonecarry = 0while s1 or s2 or carry != 0:a = s1.pop() if s1 else 0b = s2.pop() if s2 else 0sum = a + b + carrycarry = sum // 10newNode = ListNode(sum % 10)newNode.next = headhead = newNodereturn head這題的兩數(shù)相加是正序的,做三次反轉鏈表也可以。此處用的是兩個棧,注意輸出也得是正序,所以鏈表是從尾部開始生成的,head 每次移動到新節(jié)點,直到結束才知道 head 。
234. 回文鏈表(劍指 Offer II 027. 回文鏈表)(面試題 02.06. 回文鏈表)
回文鏈表最簡單的思路是遍歷鏈表,用數(shù)組記錄所有元素,然后判斷數(shù)組正序是否等于逆序即可。更好的方法是使用快慢指針,如下:
class Solution:def isPalindrome(self, head: ListNode) -> bool: if not head:return True# 找到前半部分鏈表的尾節(jié)點并反轉后半部分鏈表first_half_tail = self.findMiddle(head)second_half_head = first_half_tail.nextsecond_half_head = self.reverseList(second_half_head)# 判斷是否回文flag = Truep1 = headp2 = second_half_headwhile p2:if p1.val != p2.val:flag = Falsebreakp1 = p1.nextp2 = p2.next# 還原鏈表并返回結果second_half_head = self.reverseList(second_half_head)first_half_tail.next = second_half_headreturn flagdef findMiddle(self, head):fast = headslow = headwhile fast.next and fast.next.next:fast = fast.next.nextslow = slow.nextreturn slowdef reverseList(self, head):pre = Nonecur = headwhile cur:temp = cur.nextcur.next = prepre = curcur = tempreturn pre思路很簡單,首先是用快慢指針找到鏈表的中間位置,然后把后半部分的鏈表反轉,接著就可以逐一判斷前后兩部分的元素是否相等,用 result 進行記錄,最后再把后半部分鏈表反轉回去(不改變鏈表結構)。
21. 合并兩個有序鏈表(劍指 Offer 25. 合并兩個排序的鏈表)
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:head = ListNode(0)cur = headwhile l1 and l2:if l1.val < l2.val:cur.next = l1l1 = l1.nextelse:cur.next = l2l2 = l2.nextcur = cur.nextcur.next = l1 if l1 else l2return head.next合并兩個有序的鏈表,方法就是新建一個節(jié)點 head,迭代進行:如果兩個鏈表都非空,就讓新鏈表指向數(shù)值小的節(jié)點,然后移動下一位,直到其中一個鏈表為空,則把另一個鏈表作為新鏈表剩下的部分。
23. 合并K個升序鏈表(劍指 Offer II 078. 合并排序鏈表)
class Solution:def mergeKLists(self, lists: List[ListNode]) -> ListNode:dummy_head = ListNode(0)cur = dummy_headheap = []for i in range(len(lists)): # 將K個鏈表的頭都加入到最小堆中if lists[i] :heapq.heappush(heap, (lists[i].val, i))lists[i] = lists[i].nextwhile heap:val, idx = heapq.heappop(heap) # 彈出最小值cur.next = ListNode(val) # 將最小值加入合并鏈表中cur = cur.nextif lists[idx]: # 若最小值有后續(xù)節(jié)點,則加入到最小堆中,繼續(xù)遍歷heapq.heappush(heap, (lists[idx].val, idx))lists[idx] = lists[idx].nextreturn dummy_head.next合并 K 個有序鏈表,每次合并時要取得 K 個數(shù)中的最小值,使用最小堆即可。將 K 個鏈表的頭都加入到最小堆中,注意元組的成員得是值 val 和索引 i,而不能是值 val 和節(jié)點 ListNode,因為節(jié)點是不支持比較的,無法排序,所以不能放在堆中。
86. 分隔鏈表(面試題 02.04. 分割鏈表)
class Solution:def partition(self, head: ListNode, x: int) -> ListNode:if not head:return headsmall = ListNode(0)small_head = smalllarge = ListNode(0)large_head = largewhile head:if head.val < x:small_head.next = headsmall_head = small_head.nextelse:large_head.next = headlarge_head = large_head.nexthead = head.nextlarge_head.next = Nonesmall_head.next = large.nextreturn small.next這題思路不難,就是用 small 和 large 兩個鏈表分別記錄比 x 小和比 x 大(或等)的節(jié)點,然后 small 拼接上 large,large 末尾指向 None 即可。
328. 奇偶鏈表
class Solution:def oddEvenList(self, head: ListNode) -> ListNode:if not head or not head.next or not head.next.next:return headoddHead = headevenHead = head.nextodd = oddHeadeven = evenHeadwhile even and even.next:odd.next = odd.next.nexteven.next = even.next.nextodd = odd.nexteven = even.nextodd.next = evenHeadreturn oddHead這題固然也可以像上一題那樣,開兩個鏈表來記錄,但是麻煩了。由于都在同一個鏈表中,所以直接兩兩改變鏈接就行了。
143. 重排鏈表(劍指 Offer II 026. 重排鏈表)
class Solution:def reorderList(self, head: ListNode) -> None:"""Do not return anything, modify head in-place instead."""if not head:returnl1 = headmiddle_node = self.middleNode(head)l2 = middle_node.nextmiddle_node.next = Nonel2 = self.reverseNode(l2)self.mergeList(l1, l2)def middleNode(self, head: ListNode) -> ListNode:slow, fast = head, headwhile slow.next and fast.next and fast.next.next:slow = slow.nextfast = fast.next.nextreturn slowdef reverseNode(self, head: ListNode) -> ListNode:pre = Nonecur = headwhile cur:temp = cur.nextcur.next = prepre = curcur = tempreturn predef mergeList(self, l1: ListNode, l2: ListNode) -> ListNode:while l1 and l2:l1_temp = l1.nextl2_temp = l2.next# 讓l1指向l2,l2指向l1.next,即擺動了兩個指針,然后都移動到下一位 l1.next = l2l1 = l1_templ2.next = l1l2 = l2_temp此題思路可以分為三步走:
1、找到原鏈表的中點(876. 鏈表的中間結點)
2、將原鏈表的右半端反轉(206. 反轉鏈表)
3、將原鏈表的兩端合并(21. 合并兩個有序鏈表)
總結
以上是生活随笔為你收集整理的链表基础概念与经典题目(Leetcode题解-Python语言)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高度平衡的二叉搜索树基础概念与经典题目(
- 下一篇: 给飞机插上电话卡是一种怎样的体验