数据结构与算法笔记之链表(二)

链表介绍

链表是以结点的方式存储,每个节点含有data域与next域组成:

  • data域:存储本节点的内容信息。
  • next域:存储下一个节点的引用或指针。
class Node:
    def __init__(self, value, next):
        self.value = value
        self.next = next

链表分为带头的链表和不带头的链表。

相关问题总结

链表逆序

给你一个链表,将该链表逆序
空间复杂度为O(n)的我就不说了,下面介绍一下空间复杂度为O(1)的方法。
定义3个变量head,next,reverse,head存储当前要操作的节点,next代表下个要操作的节点,reverse代表逆序链表当前节点。
具体过程可以看教程:https://www.bilibili.com/video/BV1Y64y1c7YJ?from=search&seid=10189832355923554107&spm_id_from=333.337.0.0

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


def reverse_linkedlist(head):
    if not head:
        return
    head_node = head
    next_node = head_node
    reverse_node = None
    while head_node:
        next_node = next_node.next
        head_node.next = reverse_node
        reverse_node = head_node
        head_node = next_node

    return reverse_node

打印两个有序链表的公共部分

给定两个有序链表的头指针head1和head2,打印出链表的公共部分。
比如:
1->2->2->3->5->6
1->2->3->4->5->6
公共部分为1、2、3、5、6

解决思路,算法就很直接,考的纯coding问题,两个指针,如果碰到相同的两个指针加一,如果不相等,就把小值的指针加一。

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


def common_part_for_linkedlist(head1, head2):
    common_part = set()
    while head1 and head2:
        if head1.value == head2.value:
            common_part.add(head1.value)
            head1 = head1.next
            head2 = head2.next
        elif head1.value > head2.value:
            head2 = head2.next
        else:
            head1 = head1.next
    return common_part

回文链表判断

给定一个链表,判断是否为回文链表。
可以使用回文的特性,正向遍历的值与反向遍历的值相同。用栈来保存正向信息,逆序遍历进行弹栈操作,如果栈为空,那么为回文链表。

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


def huiwen(head):
    if not head:
        return False
    stack = []
    tmp = head
    while tmp:
        stack.append(tmp.value)
        tmp = tmp.next
    pop_stack(head, stack)
    if not stack:
        return True
    return False


def pop_stack(head, stack):
    if not head:
        return
    pop_stack(head.next, stack)
    v = stack.pop()
    if head.value != v:
        stack.append(v)

如果要求其空间复杂度为O(1),那要怎么办呢?
定义两个指针,一个往前,一个往后,遍历比较,一旦不同,就是为false,具体实现方式如下:

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


class HuiWen:

    def __init__(self, head):
        self.head = head
        self.pre = head
        self.is_ok = True

    def compare(self, last):
        if not last:
            return
        self.compare(last.next)
        if self.pre.value != last.value:
            self.is_ok = False
        self.pre = self.pre.next

    def huiwen(self):
        if not self.head:
            return False
        self.compare(self.head)
        return self.is_ok

测试用例:

class TestCase:

    def test_case1(self):
        head = Node(1)
        head.next = Node(2)
        head.next.next = Node(3)
        head.next.next.next = Node(2)
        head.next.next.next.next = Node(1)
        n = HuiWen(head)
        print(n.huiwen())

    def test_case2(self):
        head = Node(1)
        head.next = Node(2)
        head.next.next = Node(2)
        head.next.next.next = Node(1)
        n = HuiWen(head)
        print(n.huiwen())

    def test_case3(self):
        head = Node(1)
        head.next = Node(3)
        head.next.next = Node(2)
        head.next.next.next = Node(1)
        n = HuiWen(head)
        print(n.huiwen())

    def run(self):
        self.test_case1()
        self.test_case2()
        self.test_case3()


if __name__ == '__main__':
    TestCase().run()

输出:

True
True
False

链表的荷兰国旗问题

将单向链表按照某值划分成左边小,中间相等,右边大的形式。
有一个链表,定义一个值pivot,要求将链表分类,变成链表左边小于pivot,中间等于pivot,右边大于pivot的形式。
这个其实很简单,定义3个头节点,分别代表小于pivot,等于pivot,大于pivot的链表,分类后将其串起来,要注意链表为空的情况。

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


def solution(head, pivot):
    if not head: nn
        return
    ls_head = Node(0)
    eq_head = Node(0)
    gt_head = Node(0)
    tmp1 = ls_head
    tmp2 = eq_head
    tmp3 = gt_head
    tmp = head
    while tmp:
        if tmp.value > pivot:
            tmp3.next = tmp
            tmp = tmp.next
            tmp3 = tmp3.next
            tmp3.next = None
        elif tmp.value < pivot:
            tmp1.next = tmp
            tmp = tmp.next
            tmp1 = tmp1.next
            tmp1.next = None
        else:
            tmp2.next = tmp
            tmp = tmp.next
            tmp2 = tmp2.next
            tmp2.next = None
    new_head = Node(0)
    new_tmp = new_head
    if ls_head.next:
        new_tmp.next = ls_head.next
    while new_tmp.next:
        new_tmp = new_tmp.next
    if eq_head.next:
        new_tmp.next = eq_head.next
    while new_tmp.next:
        new_tmp = new_tmp.next
    if gt_head.next:
        new_tmp.next = gt_head.next
    return new_head.next

判断链表是否有环

题目描述:给定一个链表头部head,判断链表是否有环,并返回入环的节点?
最简单的思路就是把问题转化成给定一个链表,判断该链表中是否存在重复节点,使用set数据结构进行保存。

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

    def __str__(self):
        return str(self.__dict__)


def is_circle(head):
    tmp_set = set()
    tmp_node = head
    cnt = 0
    while tmp_node is not None:
        tmp_set.add(tmp_node)
        cnt += 1
        if cnt > len(tmp_set):
            return tmp_node
        tmp_node = tmp_node.next
    return None

判断两个单链表是否相交

题目描述:给定两个无环单链表,head1和head2,如何判断其是否相交?
首先先明确单链表相交的情况,有且仅有一种情况,下面是一个例子:
1–2--3–
        --5–6--7
2–3--4–
那么明确了这个就有解决思路了
如果是空间复杂度为O(N)的解决方法,就是使用set对node进行存储,这边不再多说,
对于空间复杂度为O(1)的解决方法,首先获取两个链表的长度n1,n2,让长的链表先走abs(n2-n1)步,然后两个链表同时开始走,假如两个指针指向的node相同就说明有相交。

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


def is_intersect(head1, head2):
    n1 = 0
    n2 = 0
    tmp1 = head1
    tmp2 = head2
    while tmp1:
        tmp1 = tmp1.next
        n1 += 1
    while tmp2:
        tmp2 = tmp2.next
        n2 += 1
    tmp1 = head1
    tmp2 = head2
    if n1 > n2:
        point = n1 - n2
        cnt = 0
        while cnt < point:
            tmp1 = tmp1.next
            cnt += 1
    else:
        point = n2 - n1
        cnt = 0
        while cnt < point:
            tmp2 = tmp2.next
            cnt += 1
    while tmp1 and tmp2:
        if tmp1 == tmp2:
            return True
        tmp1 = tmp1.next
        tmp2 = tmp2.next
    return False

判断链表是否相交

首先分为有环与无环
对于无环链表的相交问题就是上一题
对于有环链表分为以下两种情况
在这里插入图片描述
判断这两个区别的地方就是入环节点是否相同。

约瑟夫问题

设编号为1,2,3,4,…,n个人围坐一圈,约定编号为a人从1开始报数,数到m的那个人出列,他的下一位又开始从1报数,数到m的那个人又出列,依次类推,输出最后出局的人的编号。
给定两个int n和m,代表游戏的人数。请返回最后一个出局的人的编号。保证n和m小于等于1000。

这个问题思路就是使用环形链表进行数据结构的存储,算法方面没什么难度。

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


def build_linkedlist(n):
    if n <= 0:
        return None
    head = Node(0)
    tmp_node = head
    for i in range(n):
        tmp_node.next = Node(i + 1)
        tmp_node = tmp_node.next
    tmp_node.next = head.next
    return head.next


def joseph(n, m):
    linkedlist = build_linkedlist(n)
    tmp = linkedlist
    left_node_num = n
    while left_node_num > 1:
        k = 1
        while k < m - 1:
            tmp = tmp.next
            k += 1
        tmp.next = tmp.next.next
        tmp = tmp.next
        left_node_num -= 1
    return tmp.value
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值