1. 程式人生 > >python資料結構之連結串列

python資料結構之連結串列

1.功能實現

連結串列(Linked list)是一種常見的基礎資料結構,是一種線性表,但是並不會按線性的順序儲存資料,而是在每一個節點裡存到下一個節點的指標(Pointer)。由於不必須按順序儲存,連結串列在插入的時候可以達到O(1)的複雜度,比另一種線性表順序錶快得多,但是查詢一個節點或者訪問特定編號的節點則需要O(n)的時間,而順序表相應的時間複雜度分別是O(logn)和O(1)。

特點:使用連結串列結構可以克服陣列連結串列需要預先知道資料大小的缺點,連結串列結構可以充分利用計算機記憶體空間,實現靈活的記憶體動態管理。但是連結串列失去了陣列隨機讀取的優點,同時連結串列由於增加了結點的指標域,空間開銷比較大。

基本操作:包括建立連結串列(初始化)、獲取長度、插入、查詢、刪除、遍歷,以及略複雜的連結串列逆序和結點交換。下面將這些操作一一實現並作出解釋。

class ListNode(object):
    def __init__(self,data,p = None):
        self.data = data
        self.next = p
 
class Linklist(object):
    def __init__(self):
        self.head = None
    def set(self):                        # 初始建立
        print('input:')
        data = input()
        if data != "":
            self.head = ListNode(int(data))
            p = self.head
        else:
            print('over!')
            return
        while 1:
            data = input()
            if data != "":
                p.next = ListNode(int(data))
                p = p.next
            else:
                print('over!')
                break
    @property
    def show(self):                        # 遍歷連結串列
        print('連結串列元素如下:')
        p = self.head
        if p == None:
            print('Empty!')
            return
        while  p:
            print(p.data,end=',')
            p = p.next
        print('over!')
        return
    @property
    def isempty(self):                        # 判斷是否空
        p = self.head
        if p == None:
            return True
        else:
            return False
    @property
    def length(self):                        # 獲取長度
        p = self.head
        l = 0
        while p:
            l += 1
            p = p.next
        return l
    def insert(self,data,pos):                # 資料插入
        if self.isempty and pos != 1:
            raise Exception('wrong position!')
        p = self.head
        if pos == 1:
            self.head = ListNode(data)
            self.head.next = p
        n = 2
        while n < pos and p.next != None:
            p = p.next
            n += 1
        if n == pos:
            tmp = p.next
            p.next = ListNode(data)
            p = p.next
            p.next = tmp
        elif n < pos:
            raise Exception('wrong position!')
 
    def delete(self,pos):                        # 刪除操作
        p = self.head
        # 假設位置資訊有效
        if pos == 1:
            return self.head.next
        for i in range(pos - 2):
            p = p.next
        p.next = p.next.next

2.倒數第k結點及連結串列合併

1)題目如下:輸入一個連結串列,輸出該連結串列中倒數第k個結點。

class ListNode(object):
    def __init__(self, x):
        self.val=x
        self.next=None
        
class Solution(object):
    def __init__(self, head, k):
        n=0
        p=head
        while p!=None:
            n+=1
            p=p.next
        if n<k:
            return None
        p=head
        m=1
        while m<n-k+1:
            m+=1
            p=p.next
        return p

2)題目如下:輸入兩個單調遞增的連結串列,輸出兩個連結串列合成後的連結串列,當然我們需要合成後的連結串列滿足單調不減規則。

class ListNode:
    def __init__(self, x):
        self.x=x
        self.next=None
        
class Solution:
    def merge(self, pHead1, pHead2):
        if pHead1 is None:
            return pHead2
        if pHead2 is None:
            return pHead1
        
        if pHead1.val<=pHead2.val:
            pHead1.next=merge(pHead1.nextm pHead2)
        else:
            pHead2.next=merge(pHead1, pHead2.next)
            

3.連結串列右移/連結串列分割/連結串列逆序

1).  61. Rotate List (將連結串列右移K個位置)

Given a list, rotate the list to the right by k places, where k is non-negative.
Example:
Given 1->2->3->4->5->NULL and k = 2,

return 4->5->1->2->3->NULL.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
 
class Solution:
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        
        if head == None:
            return None
        length = 1
        p = head 
        while p.next:
            p = p.next
            length += 1
        if k == length:
            return head
        elif k > length:
            k = k%length
        cn = 1
        q = head
        while cn < (length-k):
            q = q.next
            cn += 1
        p.next = head
        newhead = q.next
        q.next = None
        return newhead

2.  86. Partition List(連結串列分割)

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given 1->4->3->2->5->2 and x = 3,

return 1->2->2->4->3->5.
 

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
 
class Solution:
    def partition(self, head, x):
        """
        :type head: ListNode
        :type x: int
        :rtype: ListNode
        """
        if head == None:
            return head
        p1 = ListNode(-1)
        p2 = ListNode(-1)
        h1, h2 = p1, p2
        while head:
            if head.val <x:
                p1.next = head
                p1 = p1.next
            else:
                p2.next = head
                p2 = p2.next
            head = head.next
        p2.next = None
        p1.next = h2.next
        return h1.next

3).  92. Reverse Linked List II(連結串列逆序)

class Solution:
    def reverseBetween(self, head, m, n):
        """
        :type head: ListNode
        :type m: int
        :type n: int
        :rtype: ListNode
        """
        if head == None:
            return head
        elif m == n:
            return head
        new = ListNode(-1)
        new.next = head
        head = new
        
        p = head       
        pre = head
        for i in range(m):
            pre = p
            p = p.next
        q = p
        for i in range(n-m):
            q = q.next
 
        # reverse
        pre_p = q.next
        cur = p
        for i in range(n-m+1):
            h = cur
            tmp = cur.next
            cur.next = pre_p
            pre_p = cur
            cur = tmp            
        pre.next = q        
        return head.next

4.移除重複項及帶隨機指標的連結串列複製

1)82. Remove Duplicates from Sorted List II (從有序列表中移除重複項)

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
For example,
Given 1->2->3->3->4->4->5, return 1->2->5.

Given 1->1->1->2->3, return 2->3.

1)82. Remove Duplicates from Sorted List II (從有序列表中移除重複項)

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
For example,
Given 1->2->3->3->4->4->5, return 1->2->5.

Given 1->1->1->2->3, return 2->3.
 

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
 
class Solution:
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None or head.next == None:
            return head        
        fhead = ListNode(0)
        fhead.next = head
        pre = fhead        
        cur = head
        
        while cur:
            while cur.next and cur.val==cur.next.val:
                cur = cur.next
            if pre.next == cur:          # 如果只此一個值,不刪除,pre後移
                pre = cur
            else:
                pre.next = cur.next      # 如果不只一個,刪除這些結點,pre暫不後移
            cur = cur.next
        return fhead.next

2)138. Copy List with Random Pointer(複製含有隨機指標的連結串列)

# Definition for singly-linked list with a random pointer.
# class RandomListNode(object):
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
 
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: RandomListNode
        :rtype: RandomListNode
        """
        if head == None:
            return None
        p = head        
        while p:
            tmp = p.next
            p.next = RandomListNode(p.label)
            p.next.next = tmp
            p = tmp
        p = head
        while p:
            if p.random:
                p.next.random = p.random.next
            p = p.next.next
        newhead = RandomListNode(0)
        p = head
        q = newhead
        while p:
            tmp = p.next.next
            q.next = p.next
            q = q.next
            p.next = tmp
            p = tmp
        return newhead.next

5.帶環連結串列及交叉連結串列(雙指標法)

1)160.Intersection of Two Linked Lists 兩連結串列交叉

Write a program to find the node at which the intersection of two singly linked lists begins.For example, the following two linked lists:
A:              a1 → a2
                             ↘
                                 c1 → c2 → c3
                             ↗            
B:     b1 → b2 → b3
begin to intersect at node c1.
Notes:
If the two linked lists have no intersection at all, return null.
The linked lists must retain their original structure after the function returns.
You may assume there are no cycles anywhere in the entire linked structure.

Your code should preferably run in O(n) time and use only O(1) memory.
 

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        p1, p2 = headA, headB
        while p1 is not p2:
            p1 = headB if not p1 else p1.next
            p2 = headA if not p2 else p2.next
        return p1

2)142. Linked List Cycle II 帶環的連結串列

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.

Follow up:Can you solve it without using extra space?

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
 
class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None:
            return None
        pslow = head
        pfast = head
        k = 0
        while pfast.next and pfast.next.next:
            pslow = pslow.next
            pfast = pfast.next.next
            k += 1
            if pslow == pfast:
                break
        if pfast.next==None or pfast.next.next==None:
            return None
        # 確定有環,開始尋找
        p1 = head
        p2 = pslow
        while p1!=p2:
            p1 = p1.next
            p2 = p2.next
        return p1