數據結構和算法-鏈表

鏈表分類

  • 單向鏈表
  • 雙向鏈表
    優點:
    • 刪除某個節點更加高效, 能夠快速找到前驅節點
    • 能夠方便的在某個節點前插入元素
  • 循環鏈表
    當要處理的數據具備環形結構的時候, 適合循環鏈表. 如約瑟夫環問題node

  • 雙向循環鏈表python

數組的缺點是大小固定, 一旦聲明長度就要佔用連續的內存空間, 當空間不夠用時更換更大的空間, 此時就須要將原數組的全部數據遷移過去, 比較費時. 鏈表則能夠動態擴容.算法

數組在查詢上能夠更快, 鏈表在插入和刪除上更快, 爲告終合數組和鏈表的優勢, 有同時使用的狀況, 好比一個網站的用戶註冊, 能夠以A-Z爲數組, 在每一個字母后面加入鏈表, 這樣能夠在添加新用戶的時候能快速找到要添加的鏈表並進行插入, 同時在查詢用戶的時候也能縮短查詢時間數組

常見邊界條件

  • 鏈表爲空
  • 只有一個節點
  • 只有兩個節點
  • 指針在頭結點和尾節點的處理

常見問題

  • 單鏈表反轉
  • 檢測鏈表中是否有環
  • 兩個有序鏈表合併
  • 刪除鏈表倒數第n個節點
  • 求鏈表的中間節點

單向鏈表

"""
單鏈表
"""


class Node(object):
    def __init__(self, data, next_=None):
        self.data = data
        self.next_ = next_


class SingleLinkedList(object):
    def __init__(self):
        self.head = None

    def is_empty(self):
        return self.head == None

    def size(self):
        current = self.head
        num = 0
        while current != None:
            current = current.next_
            num += 1
        return num

    def prepend(self, value):
        """
        在頭部添加節點
        :param value:
        :return:
        """
        self.head = Node(value, self.head)

    def append(self, value):
        """
        在尾部追加節點
        :param value:
        :return:
        """
        node = Node(value)
        if self.is_empty():
            self.head = node
        else:
            current = self.head
            while current.next_ != None:
                current = current.next_
            current.next_ = node

    def insert(self, position, value):
        """
        指定位置插入節點, 從1開始計數
        :param position:
        :param value:
        :return:
        """
        if position <= 1:
            self.prepend(value)
        elif position > self.size():
            self.append(value)
        else:
            node = Node(value)
            tmp_pos = 1
            pre_node = None
            current = self.head
            while tmp_pos < position:
                pre_node = current
                current = current.next_
                tmp_pos += 1
            node.next_ = current
            pre_node.next_ = node

    def delete(self, value):
        if self.is_empty():
            raise Exception("empty")
        pre_node = None
        current = self.head
        while current != None:
            if current.data == value:
                # 判斷刪除的元素是否是第一個
                if not pre_node:
                    self.head = current.next_
                else:
                    pre_node.next_ = current.next_
                break
            else:
                pre_node = current
                current = current.next_

    def pop_first(self):
        if self.is_empty():
            raise Exception("empty")
        data = self.head.data
        self.head = self.head.next_
        return data

    def pop_last(self):
        if self.is_empty():
            raise Exception("empty")
        pre_node = None
        current = self.head
        while current.next_ != None:
            pre_node = current
            current = current.next_
        data = current.data
        if pre_node == None:
            self.head = None
        else:
            pre_node.next = None
        return data

    def find(self, value):
        status = False
        current = self.head
        while current != None and not status:
            if current.data == value:
                status = True
            else:
                current = current.next_
        return status

單鏈表反轉

# coding:utf-8

"""
單鏈表反轉
"""


class Node(object):
    def __init__(self, data, next_=None):
        self.data = data
        self.next_ = next_


def reverse(linked_list):
    head = linked_list
    pre = None
    while head != None:
        current = head
        head = current.next_
        current.next_ = pre
        pre = current
    return pre


def output(linked_list):
    current = linked_list
    res = []
    while current != None:
        res.append(current.data)
        current = current.next_
    print(res)


if __name__ == '__main__':
    link = Node(1, Node(2, Node(3, Node(4, Node(5, Node(6, Node(7, Node(8, Node(9)))))))))
    output(link)
    root = reverse(link)
    output(root)

"""
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
"""

鏈表成對調換

# coding:utf-8

"""
鏈表成對調換

1 -> 2 -> 3 -> 4
調換後爲
2 -> 1 -> 4 -> 3
"""


class Node(object):
    def __init__(self, data, next_=None):
        self.data = data
        self.next_ = next_


def swap(head):
    if head != None and head.next_ != None:
        after = head.next_
        head.next_ = swap(after.next_)
        after.next_ = head
        return after
    return head


def output(linked_list):
    current = linked_list
    res = []
    while current != None:
        res.append(current.data)
        current = current.next_
    print(res)


if __name__ == '__main__':
    link = Node(1, Node(2, Node(3, Node(4))))
    output(link)
    print('----')
    link1 = swap(link)

    output(link1)

"""
[1, 2, 3, 4]
----
[2, 1, 4, 3]
"""

判斷是否交叉鏈表

  1. 頭指針法
    • 求出兩個鏈表的長度之差sub_size
    • 讓較長鏈表快速走sub_size
    • 最後依次比較兩條鏈表對應的值是否相等, 相等處則是交點
  2. 分別遍歷兩個鏈表, 判斷最後一個節點的值是否同樣
    時間複雜度O(m+n)

判斷鏈表是否有環

定義兩個遊標first和later, first步長是1, later步長是2. 同時向前走, 若是有環必定會遇到. 複雜度O(n)數據結構

# -*- coding:utf-8 -*-

"""
判斷鏈表是否有環
"""


class Node(object):
    def __init__(self, data, next_=None):
        self.data = data
        self.next_ = next_


def check(head):
    current1 = current2 = head
    while True:
        current1 = current1.next_  # 指向第一個節點
        current2 = current2.next_.next_  # 指向第二個節點

        if (not current1) or (not current2):
            break
        if current1.data == current2.data:
            return True

    return False


if __name__ == '__main__':
    node1 = Node(6)
    node2 = Node(2)
    node3 = Node(3)
    node4 = Node(4)
    node5 = Node(5)
    node6 = Node(6)

    node1.next_ = node2
    node2.next_ = node3
    node3.next_ = node4
    node4.next_ = node5
    node5.next_ = node6
    node6.next_ = node3  # 環交點

    assert check(node1) == True

查找單鏈表倒數第k個元素

定義兩個指針first, later都初始化指向頭節點, 而後first先走k步, 再同時走, 當first到尾節點的時候, 讀出later節點的值. 複雜度是O(n)app

# -*- coding:utf-8 -*-

"""
找到鏈表的倒數第K個元素

定義兩個遊標, 第二個遊標先走k-1步, 以後再同時走, 此時第一個遊標停留位置就是倒數第K個元素
"""

class Node(object):
    def __init__(self, data, next_=None):
        self.data = data
        self.next_ = next_



def find_reverse_k(head, k):
    c1 = head
    current = head
    for _ in range(k - 1):
        current = current.next_
    c2 = current

    while c2.next_ != None:
        c2 = c2.next_
        c1 = c1.next_

    return c1.data


if __name__ == '__main__':
    link = Node(1, Node(2, Node(3, Node(4, Node(5, Node(6, Node(7, Node(8, Node(9)))))))))
    assert find_reverse_k(link, 3) == 7
    assert find_reverse_k(link, 1) == 9
    assert find_reverse_k(link, 2) == 8

合併兩個有序鏈表

# coding:utf-8

"""
合併兩個有序單鏈表

輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
"""


class Node(object):
    def __init__(self, data, next_=None):
        self.data = data
        self.next_ = next_


def merge2linkedlist(l1, l2):
    """
    合併兩個有序鏈表
    :param l1:
    :param l2:
    :return:
    """
    if l1 == None and l2 == None:
        raise Exception("None!!!")
    if l1 == None:
        return l2
    if l2 == None:
        return l1

    # 使用head爲輔助節點
    head = Node(0)
    current = head

    while l1 and l2:
        if l1.data <= l2.data:
            current.next_ = l1
            l1 = l1.next_
        elif l1.data > l2.data:
            current.next_ = l2
            l2 = l2.next_
        current = current.next_

    if l1:
        current.next_ = l1
    if l2:
        current.next_ = l2

    return head.next_


if __name__ == "__main__":
    l1 = Node(1, Node(2, Node(4)))
    l2 = Node(1, Node(3, Node(4)))

    tmp = merge2linkedlist(l1, l2)

    res = []
    while tmp:
        res.append(tmp.data)
        tmp = tmp.next_
    print(res)

"""
[1, 1, 2, 3, 4, 4]
"""

合併K個有序單鏈表

  • 方法一
    把K個鏈表2個爲一組進行合併, 不斷分組, 最後合併爲一個有序鏈表
  • 方法二
    遍歷全部鏈表將全部元素放在一個數組中, 而後對數組進行排序, 最後生成一個有序鏈表.
    時間複雜度:
    若是全部鏈表共有n個元素, 遍歷須要O(n), 對數組排序須要O(nlogn), 最後鏈接O(n). 因此總的複雜度是O(n + nlogn + n), 也就是O(nlogn)

注意

咱們說空間複雜度的時候, 是指除了本來的數據存儲空間外, 算法還須要的額外的存儲空間, 即無論原來所佔空間是多少網站

資料

  • < <漫畫算法> >
  • < <大話數據結構> >
  • < <數據結構與算法> >

相關文章
相關標籤/搜索