单链表

16 篇文章 0 订阅
5 篇文章 0 订阅

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

1、构造一个单链表

class Node(object):
    """节点类"""
    __slots__ = ['_item', '_next']  # 限定 Node 实例的属性

    def __init__(self, item):
        self._item = item   # Node 的值
        self._next = None   # Node 的默认指向 None

    def getItem(self):   # 得到 Node 的值
        return self._item

    def getNext(self):   # 得到下一个 Node
        return self._next

    def setItem(self, newitem):   # 设置 Node 的值
        self._item = newitem

    def setNext(self, newnext):   # 设置下一个 Node
        self._next = newnext


class SingleLinkedList(object):
    """单链表类"""
    def __init__(self):
        self._head = None  # 初始化链表为空 始终指向链表的头部
        self._size = 0     # 链表长度

    def size(self):
        """返回链表的长度"""
        current = self._head
        count = 0
        while current is not None:
            count += 1
            current = current.getNext()
        return count

    def travel(self):
        """遍历链表(不能有环)"""
        current = self._head
        while current is not None:
            print(current.getItem(), end='==>')
            current = current.getNext()

    def isEmpty(self):
        """检查链表是否为空"""
        return self._head is None

    def add(self, item):
        """在链表头添加元素"""
        temp = Node(item)         # 创建新的 Node
        temp.setNext(self._head)  # 新 Node 的 next 指向 _head
        self._head = temp         # _head 指向新创建的 Node

    def append(self, item):
        """在链表尾添加元素"""
        temp = Node(item)
        if self.isEmpty():
            self._head = temp   # 若为空表就直接插入
        else:
            current = self._head
            while current.getNext() is not None:
                current = current.getNext()    # 遍历列表,得到尾 Node
            current.setNext(temp)              # 此时current为链表最后的元素,在末尾插入

    def search(self, item):
        """检索元素是否在链表中"""
        current = self._head
        founditem = False
        while current is not None and not founditem:
            if current.getItem() == item:
                founditem = True
            else:
                current = current.getNext()
        return founditem

    def index(self, item):
        """索引元素在链表中的位置"""
        current = self._head
        count = 0
        found = None
        while current is not None and not found:
            count += 1
            if current.getItem() == item:
                found = True
            else:
                current = current.getNext()
        if found:
            return count
        else:
            return -1  # 返回-1表示不存在

    def remove(self, item):
        """删除链表中的某节点"""
        current = self._head
        pre = None
        while current is not None:
            if current.getItem() == item:
                if not pre:
                    self._head = current.getNext()
                else:
                    pre.setNext(current.getNext())
                break
            else:
                pre = current
                current = current.getNext()

    def insert(self, pos, item):
        """在链表任意位置插入元素"""
        if pos <= 1:
            self.add(item)
        elif pos > self.size():
            self.append(item)
        else:
            temp = Node(item)
            count = 1
            pre = None
            current = self._head
            while count < pos:
                count += 1
                pre = current
                current = current.getNext()
            pre.setNext(temp)
            temp.setNext(current)

if __name__ == '__main__':
    sla = SingleLinkedList()
    for i in range(1, 10):
        sla.append(i)
    print('链表的大小', sla.size())
    print(sla.travel())
    print(sla.search(6))
    print(sla.index(5))
    sla.remove(4)
    print(sla.travel())
    sla.insert(4, 100)
    print(sla.travel())

2、在链表中添加环

class SingleLinkedLoop(SingleLinkedList):
    """继承 SingleLinkedList 类"""
    def setCycle(self, pos):
        """将链表的第 pos 个节点设为环的入口"""
        current = self._head
        pre = self._head
        for i in range(1, self.size()):
            pre = pre.getNext()               # 得到尾 Node

        if pos < 1 or pos > self.size():
            return -1
        else:
            for i in range(1, pos):
                current = current.getNext()   # 得到环入口 Node
            pre.setNext(current)              # 设置环


if __name__ == '__main__':
    slb = SingleLinkedLoop()
    for i in range(1, 10):
        slb.append(i)
    print(slb.travel())
    slb.setCycle(4)

3、判断链表中是否有环

如何判断一个单链表是否存在循环,链表数目未知。算法不能破坏链表。有三种常用的解决方法。

(1)存储节点

将所有遍历过的节点用某个结构存储起来,然后每遍历一个节点,都在这个结构中查找是否遍历过,如果找到有重复,则说明该链表存在循环;如果直到遍历结束,则说明链表不存在循环。
这个结构我们可以使用hash来做,hash中存储的值为节点的内存地址。

def hasCycle_hash(singlelinked):
    """接收一个链表,判断链表中是否有环
    用 set 来存储链表的节点id"""
    hash_set = set()                  # 初始化set
    current = singlelinked._head      # 得到链表头节点
    while current is not None:
        if id(current) in hash_set:   # 某个节点已经在 hash_set 中,则链表中存在环
            return True 
        else:
            hash_set.add(id(current))
            current = current.getNext()
    return False                     # 链表中没有环

if __name__ == '__main__':
    print(hasCycle_hash(sla))
    print(hasCycle_hash(slb))

(2)逆转链表

方法比较的特别,是使用反转指针的方法,每过一个节点就把该节点的指针反向
当有环的时候,最后指针会定位到链表的头部,如果到最后,没有回到头部,那说明链表不存在循环。
这个方法会破坏掉链表,所以如果要求是不能破坏链表的话,最后还需要反转一下,再将链表恢复。
这个方法其实是使用了3个指针,用于进行反转。

def hasCycle_reverse(singlelinked):
    """接收一个链表,判断链表中是否有环
    用一个节点来反转链表"""
    dummy = Node(-1)      # 定义一个头节点,用来插入摘下的节点
    current = singlelinked._head
    if current is None:
        return False

    dummy.setNext(current)
    pre, cur = current, current.getNext()

    while cur is not None:
        print(dummy.getNext().getItem(), end='-->')
        temp = cur
        pre.setNext(cur.getNext())          # 把摘链的地方连起来
        cur = pre.getNext()
        temp.setNext(dummy.getNext())       # 反转链表
        dummy.setNext(temp)
        if dummy.getNext() == current:
            pre = dummy.getNext()
            cur = pre.getNext()
            while cur is not None:
                print(dummy.getNext().getItem(), end='>>>')
                temp = cur
                pre.setNext(cur.getNext())  # 把摘链的地方连起来
                cur = pre.getNext()
                temp.setNext(dummy.getNext())  # 恢复有环链表
                dummy.setNext(temp)
                if dummy.getNext() == current:
                    return True
    pre = dummy.getNext()
    cur = pre.getNext()
    while cur is not None:
        print(dummy.getNext().getItem(), end='++>')
        temp = cur
        pre.setNext(cur.getNext())          # 把摘链的地方连起来
        cur = pre.getNext()
        temp.setNext(dummy.getNext())       # 恢复无环链表
        dummy.setNext(temp)
    return False


if __name__ == '__main__':
    print(hasCycle_reverse(sla))
    print(sla.travel())
    print(hasCycle_reverse(slb))
    current = slb._head
    print(current.getItem())
    for i in range(10):
        current = current.getNext()
        print(current.getItem(), end='==>')

(3)快慢指针

快指针pf(f就是fast的缩写)每次移动2个节点,慢指针ps(s为slow的缩写)每次移动1个节点,如果快指针能够追上慢指针,那就说明其中有一个环,否则不存在环。

def hasCycle_f_s(singlelinked):
    """接收一个链表,判断链表中是否有环
    用两个指针来遍历链表"""
    current = singlelinked._head
    if current is None:
        return False
    if current.getNext() is None:
        return False
    slow = current.getNext()
    fast = current.getNext()
    if fast.getNext() is not None:
        fast = fast.getNext()
    while (slow.getNext() is not None) and (fast.getNext() is not None):
        if slow == fast:
            return True
        slow = slow.getNext()
        fast = fast.getNext()
        if fast.getNext() is not None:
            fast = fast.getNext()
    return False

if __name__ == '__main__':
    print(hasCycle_f_s(sla))
    print(hasCycle_f_s(slb))

4、找出环的入口点

(1)与存储节点进行对比

def hasCycle_hash(singlelinked):
    """接收一个链表,判断链表中是否有环
    用 set 来存储链表的节点id,当某节点 id 在 set 中时,就是环的入口"""
    hash_set = set()
    current = singlelinked._head
    while current is not None:
        if id(current) in hash_set:
            return current   # 返回环的入口节点
        else:
            hash_set.add(id(current))
            current = current.getNext()
    return False

if __name__ == '__main__':
    in_cycle = hasCycle_hash(slb)          # 接收环入口节点
    print(in_cycle.getItem())              # 入口节点的 值
    print(slb.index(in_cycle.getItem()))   # 入口节点的 位置

(2)重置快慢指针

def hasCycle_f_s(singlelinked):
    """接收一个链表,判断链表中是否有环
    用两个指针来遍历链表"""
    current = singlelinked._head
    if current is None:
        return False
    if current.getNext() is None:
        return False
    slow = current.getNext()
    fast = current.getNext()
    if fast.getNext() is not None:
        fast = fast.getNext()
    while (slow.getNext() is not None) and (fast.getNext() is not None):
        if slow == fast:
            before = slow             # 相遇节点
            after = current           # 头节点
            while True:
                if before == after:
                    return after     # 返回环的入口节点
                before = before.getNext()
                after = after.getNext()
        slow = slow.getNext()
        fast = fast.getNext()
        if fast.getNext() is not None:
            fast = fast.getNext()
    return False

if __name__ == '__main__':
    in_cycle = hasCycle_f_s(slb)          # 接收环入口节点
    print(in_cycle.getItem())              # 入口节点的 值
    print(slb.index(in_cycle.getItem()))   # 入口节点的 位置

5、求出环的长度,链表的长度

(1)用存储节点法,跑一圈环,计算长度

def hasCycle_hash(singlelinked):
    """接收一个链表,判断链表中是否有环
    用 set 来存储链表的节点id"""
    hash_set = set()
    current = singlelinked._head
    while current is not None:
        if id(current) in hash_set:
            len_l = len(hash_set)      # 链表的长度
            temp = current.getNext()
            len_c = 1
            while True:
                if temp == current:
                    return current, len_c, len_l   # 返回环的入口节点, 环的长度,链表的长度
                temp = temp.getNext()
                len_c += 1
        else:
            hash_set.add(id(current))
            current = current.getNext()
    return False

if __name__ == '__main__':
    in_cycle, len_c, len_l = hasCycle_hash(slb)
    print("链表的长度是:", len_l)
    print("环的长度是:", len_c)
    print(in_cycle.getItem())              # 入口节点的 值
    print(slb.index(in_cycle.getItem()))   # 入口节点的 位置

(2)快慢指针法,同时算长度

def hasCycle_f_s(singlelinked):
    """接收一个链表,判断链表中是否有环
    用两个指针来遍历链表"""
    current = singlelinked._head
    if current is None:
        return False
    if current.getNext() is None:
        return False
    slow = current.getNext()
    fast = current.getNext()
    if fast.getNext() is not None:
        fast = fast.getNext()
    while (slow.getNext() is not None) and (fast.getNext() is not None):
        if slow == fast:
            before = slow             # 相遇节点
            after = current           # 头节点
            len_a = 0                 # 头节点到入口节点的长度
            while True:
                if before == after:
                    temp = after.getNext()
                    len_c = 1
                    while True:
                        if temp == after:
                            len_l = len_c + len_a
                            return after, len_c, len_l     # 返回环的入口节点, 环的长度, 链表的长度
                        temp = temp.getNext()
                        len_c += 1
                before = before.getNext()
                after = after.getNext()
                len_a += 1
        slow = slow.getNext()
        fast = fast.getNext()
        if fast.getNext() is not None:
            fast = fast.getNext()
    return False

if __name__ == '__main__':
    in_cycle, len_c, len_l = hasCycle_f_s(slb)
    print("链表的长度是:", len_l)
    print("环的长度是:", len_c)
    print(in_cycle.getItem())              # 入口节点的 值
    print(slb.index(in_cycle.getItem()))   # 入口节点的 位置

6、求环上任意一个节点的对面节点

  • 使用快慢指针的方法,让slow和fast都指向环上的节点ptr0,slow每次跳一步,fast每次跳两步,当fast = ptr0 或者 fast = prt0->next的时候slow所指向的节点就是ptr0的”对面节点“。
  • 得到环的长度 len_c,对环上的节点ptr0,向前走(len_c//2) 个节点,或者向前走(len_c//2)+1 个节点。所指向的节点就是ptr0的”对面节点“。

7、(扩展)如何判断两个无环链表是否相交,如果相交,求出第一个相交的节点

  • 让其中一个链表的尾节点指向头节点,形成闭环。测试另一个链表中是否有环。[转化为 问题3 ]
  • 第一个相交的节点,就是环的入口节点。[转化为 问题4]


class Node(object):
    """节点类"""
    __slots__ = ['_item', '_next']  # 限定Node实例的属性

    def __init__(self, item):
        self._item = item
        self._next = None  # Node的指针部分默认指向None

    def getItem(self):
        return self._item

    def getNext(self):
        return self._next

    def setItem(self, newitem):
        self._item = newitem

    def setNext(self, newnext):
        self._next = newnext


class SingleLinkedList(object):
    """单链表类"""
    def __init__(self):
        self._head = None  # 初始化链表为空 始终指向链表的头部
        self._size = 0  # 链表大小

    # 返回链表的大小
    def size(self):
        current = self._head
        count = 0
        while current is not None:
            count += 1
            current = current.getNext()
        return count

    # 遍历链表
    def travel(self):
        current = self._head
        while current is not None:
            print(current.getItem(), end='==>')
            current = current.getNext()

    # 检查链表是否为空
    def isEmpty(self):
        return self._head is None

    # 在链表前端添加元素
    def add(self, item):
        temp = Node(item)  # 创建新的节点
        temp.setNext(self._head)  # 新创建的next指针指向_head
        self._head = temp  # _head指向新创建的指针

    # 在链表尾部添加元素
    def append(self, item):
        temp = Node(item)
        if self.isEmpty():
            self._head = temp  # 若为空表就直接插入
        else:
            current = self._head
            while current.getNext() is not None:
                current = current.getNext()  # 遍历列表
            current.setNext(temp)  # 此时current为链表最后的元素,在末尾插入

    # 检索元素是否在链表中
    def search(self, item):
        current = self._head
        founditem = False
        while current is not None and not founditem:
            if current.getItem() == item:
                founditem = True
            else:
                current = current.getNext()
        return founditem

    # 索引元素在链表中的位置
    def index(self, item):
        current = self._head
        count = 0
        found = None
        while current is not None and not found:
            count += 1
            if current.getItem() == item:
                found = True
            else:
                current = current.getNext()
        if found:
            return count
        else:
            return -1  # 返回-1表示不存在

    # 删除链表中的某节点元素
    def remove(self, item):
        current = self._head
        pre = None
        while current is not None:
            if current.getItem() == item:
                if not pre:
                    self._head = current.getNext()
                else:
                    pre.setNext(current.getNext())
                break
            else:
                pre = current
                current = current.getNext()

    # 在链表任意位置插入元素
    def insert(self, pos, item):
        if pos <= 1:
            self.add(item)
        elif pos > self.size():
            self.append(item)
        else:
            temp = Node(item)
            count = 1
            pre = None
            current = self._head
            while count < pos:
                count += 1
                pre = current
                current = current.getNext()
            pre.setNext(temp)
            temp.setNext(current)


class SingleLinkedLoop(SingleLinkedList):
    """继承 SingleLinkedList 类"""
    def setCycle(self, pos):
        """将链表的第 pos 个节点设为环的入口"""
        current = self._head
        pre = self._head
        for i in range(1, self.size()):
            pre = pre.getNext()

        if pos < 1 or pos > self.size():
            return -1
        else:
            for i in range(1, pos):
                current = current.getNext()
            pre.setNext(current)


def hasCycle_hash(singlelinked):
    """接收一个链表,判断链表中是否有环
    用 set 来存储链表的节点id"""
    hash_set = set()
    current = singlelinked._head
    while current is not None:
        if id(current) in hash_set:
            len_l = len(hash_set)      # 链表的长度
            temp = current.getNext()
            len_c = 1
            while True:
                if temp == current:
                    return current, len_c, len_l   # 返回环的入口节点, 环的长度,链表的长度
                temp = temp.getNext()
                len_c += 1
        else:
            hash_set.add(id(current))
            current = current.getNext()
    return False


def hasCycle_reverse(singlelinked):
    """接收一个链表,判断链表中是否有环
    用一个节点来反转链表"""
    dummy = Node(-1)
    current = singlelinked._head
    if current is None:
        return False

    dummy.setNext(current)
    pre, cur = current, current.getNext()

    while cur is not None:
        print(dummy.getNext().getItem(), end='-->')
        temp = cur
        pre.setNext(cur.getNext())          # 把摘链的地方连起来
        cur = pre.getNext()
        temp.setNext(dummy.getNext())       # 反转链表
        dummy.setNext(temp)
        if dummy.getNext() == current:
            pre = dummy.getNext()
            cur = pre.getNext()
            while cur is not None:
                print(dummy.getNext().getItem(), end='>>>')
                temp = cur
                pre.setNext(cur.getNext())  # 把摘链的地方连起来
                cur = pre.getNext()
                temp.setNext(dummy.getNext())  # 恢复链表
                dummy.setNext(temp)
                if dummy.getNext() == current:
                    return True
    pre = dummy.getNext()
    cur = pre.getNext()
    while cur is not None:
        print(dummy.getNext().getItem(), end='++>')
        temp = cur
        pre.setNext(cur.getNext())          # 把摘链的地方连起来
        cur = pre.getNext()
        temp.setNext(dummy.getNext())       # 恢复链表
        dummy.setNext(temp)
    return False


def hasCycle_f_s(singlelinked):
    """接收一个链表,判断链表中是否有环
    用两个指针来遍历链表"""
    current = singlelinked._head
    if current is None:
        return False
    if current.getNext() is None:
        return False
    slow = current.getNext()
    fast = current.getNext()
    if fast.getNext() is not None:
        fast = fast.getNext()
    while (slow.getNext() is not None) and (fast.getNext() is not None):
        if slow == fast:
            before = slow             # 相遇节点
            after = current           # 头节点
            len_a = 0                 # 头节点到入口节点的长度
            while True:
                if before == after:
                    temp = after.getNext()
                    len_c = 1
                    while True:
                        if temp == after:
                            len_l = len_c + len_a
                            return after, len_c, len_l     # 返回环的入口节点, 环的长度, 链表的长度
                        temp = temp.getNext()
                        len_c += 1
                before = before.getNext()
                after = after.getNext()
                len_a += 1
        slow = slow.getNext()
        fast = fast.getNext()
        if fast.getNext() is not None:
            fast = fast.getNext()
    return False


if __name__ == '__main__':
    sla = SingleLinkedList()
    for i in range(1, 10):
        sla.append(i)
    print('链表的大小', sla.size())
    print(sla.travel())
    print(sla.search(6))
    print(sla.index(5))
    sla.remove(4)
    print(sla.travel())
    sla.insert(4, 100)
    print(sla.travel())

    slb = SingleLinkedLoop()
    for i in range(1, 10):
        slb.append(i)
    print(slb.travel())
    slb.setCycle(4)

    print(hasCycle_hash(sla))

    in_cycle, len_c, len_l = hasCycle_hash(slb)
    print("链表的长度是:", len_l)
    print("环的长度是:", len_c)
    print(in_cycle.getItem())
    print(slb.index(in_cycle.getItem()))

    print(hasCycle_reverse(sla))
    print(sla.travel())
    print(hasCycle_reverse(slb))
    current = slb._head
    print(current.getItem())
    for i in range(10):
        current = current.getNext()
        print(current.getItem(), end='==>')

    print()
    print(hasCycle_f_s(sla))
    in_cycle, len_c, len_l = hasCycle_f_s(slb)
    print("链表的长度是:", len_l)
    print("环的长度是:", len_c)
    print(in_cycle.getItem())
    print(slb.index(in_cycle.getItem()))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值