数据结构与算法第一次围剿计划 链表

特别注意:

数组O(1) 是访问指定下标位置元素,这是O(1),  看一个数在不在数字里, 时间是O(n)的。插入和删除都是O(n)的

 

链表:

链表的   查询时间是O(n),  插入删除是O(1)

https://blog.csdn.net/DeskyAki/article/details/100831860

上面僕のブログ实现了链表的构造, 数组转链表, 打印链表, 链表的尾加, 插入,删除, 逆序,两两对调, 去除重复数据的基本功能。

1 如何找出链表中倒数第k个元素

如果是正序那就炒鸡简单,没得商量。

找倒数第k元素, 不假思索的就是两次遍历, 一次遍历统计长度n,在遍历一次找到第n-k个元素,虽然遍历了两次, 但时间复杂度也是o(n)

另一种方法就十分巧妙了,利用快慢指针,快指针比慢指针超前k个位置, 同时往下走, 直到快指针走完,慢指针的位置就是倒数第k个位置, 下面的代码, 放进我的上面的blog直接就能用

    def find_num(self, k):
        """
        :param k: 倒数第k个位置
        :return: 
        """
        p, p_fast = self.head, self.head
        i = 0
        while i < k and p_fast is not None:
            p_fast = p_fast.next
            i += 1
        if i < k:
            print("超出索引")
            return
        while p_fast is not None:
            p_fast = p_fast.next
            p = p.next
        print(p.data)

 

2 将链表右旋k个位置

如 1,2,3,4,5   k=3   结果  3,4,5,1,2

也是快慢指针, 不过和上面有有些区别, 要找到倒数第k+1个数, 要p_fast.next:, 这个条件就能找到倒数第k+1个数

    def change_k(self, k):
        """
        链表右旋k, 1,2,3,4,5 k=2, 结果为 4,5,1,2,3
        :param k:
        :return:
        """
        p, p_fast = self.head, self.head
        i = 0
        while p and i < k and p_fast is not None:
            p_fast = p_fast.next
            i += 1
        if i < k:
            print("超出索引")
            return
        while p_fast.next:
            # 注意是p_fast.next
            p_fast = p_fast.next
            p = p.next
        q = p.next
        p.next = None
        p_fast.next = self.head.next
        # 因为是有头链表, 要想先next
        self.head.next = q

3 判断一个链表是否有环

也是巧妙地利用快慢指针,同时从头节点出发, 慢指针一次走一步, 快指针一次走两步。如果快慢指针相等, 这里的相等肯定不是值相等了, 是实例的内存地址相等, 说明是一个实例,及两个指针相遇, 说明链表有环。

构造完有环链表, 打印的时候一直循环, 还以为敲错了, 一想可不一直打印因为有环, 没有指向None的, 只能是一直打印了。

打印结果 None->1->2->3->4->5->3->4->5->3->4->5->3->4->      在3这个节点一直循环, 因为设置的是p.next = self.head.next.next.next

还有刚才脑子突然蹦出个想法, 如果链表没环会不会出bug? 瞬间就否定了自己的想法,因为是 while p and fast_p:

class LNode(object):
    def __init__(self, data):
        self.data = data
        self.next = None


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

    def list_node(self, arr):
        self.head = LNode(None)
        p = self.head
        for i in arr:
            node = LNode(i)
            p.next = node
            p = p.next
        p.next = self.head.next.next.next
        # 构造有环链表

    def print_node(self):
        p = self.head
        while p:
            print(p.data, end="->")
            p = p.next

    def is_loop(self):
        p, fast_p = self.head, self.head
        while p and fast_p:
            p = p.next
            fast_p = fast_p.next.next
            if fast_p == p:
                print("True")
                return fast_p

if __name__ == "__main__":
    head = LList()
    head.list_node([1, 2, 3, 4, 5])
    head.is_loop()

4 一个链表有环,  确定环的入口

如上题, 环的入口就是3。

这道题的解法就很哲学了, 不是吾等凡人能想出来的,大概思路就是,

利用快慢指针找到它们相遇的节点,在搞两个指针,一个从头节点开始遍历, 一个从相遇的节点开始遍历, 速度都是1, 两个指针相遇的节点,就是入口节点。

我来斗胆说说推理过程:

前面不说了,就说为什么从相遇点开始遍历,在次相遇的点就是入口节点

慢指针走s步, 快指针2s部,第一次相遇时,设快指针在环里走n圈,环长为r,  则

2s = s + nr

所以 s = nr

设整个链表长L, 入口节点与第一次相遇点距离为x, 起点到入口点的距离为a, 则

a + x = nr

a + x = (n-1)r + r + L - a

a = (n-1)r + (L-a-x)

(L-a-x) 为相遇点到入口点的距离,起点到入口点的距离为a = (n-1)r + (L-a-x),不管走了几圈,第一次相遇的节点都是一个位置, 所以就说明起点到入口节点的距离 == 相遇点到入口节点的距离, 下面附图:

 

mmp, 知道了解法, 写起来真是爽,

class LNode(object):
    def __init__(self, data):
        self.data = data
        self.next = None


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

    def list_node(self, arr):
        self.head = LNode(None)
        p = self.head
        for i in arr:
            node = LNode(i)
            p.next = node
            p = p.next
        p.next = self.head.next.next.next
        # 构造有环链表

    def is_loop(self):
        """
        找到相遇点
        :return:
        """
        p, fast_p = self.head, self.head
        while p and fast_p:
            p = p.next
            fast_p = fast_p.next.next
            if fast_p == p:
                return fast_p

    def find_it(self):
        """
        找到入口节点
        :return:
        """
        p = self.head
        # p 是起点
        q = self.is_loop()
        # q 相遇点
        while q is not p:
            q = q.next
            p = p.next
        print(p.data)


if __name__ == "__main__":
    head = LList()
    head.list_node([1, 2, 3, 4, 5])
    head.find_it()

 5 两两对调链表

def swap_node(self):
        """
        链表的两两对调
        1,2,3,4,5 --> None->2->1->4->3->5-> 
        """
        p = self.head
        cur = p.next
        pre = p
        while cur and cur.next:
            next = cur.next.next
            pre.next = cur.next
            cur.next.next = cur
            cur.next = next
            pre = cur
            cur = next
        self.head = p
 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值