链表学习简单总结

之前在leetcode上面找了一些链表的题做完了,但是隔一段时间感觉又忘得差不多了。

果然学习还是需要不停的输出和回顾。

数据结构——链表

链表中的每个元素实际上是一个单独的对象,而所有对象都通过每个元素中的引用字段链接在一起。

单链表

                                                                        双链表

单链表中的每个结点不仅包含值,还包含链接到下一个结点的引用字段。通过这种方式,单链表将所有结点按顺序组织起来。

C++中节点的典型定义:

struct SinglyListNode {
    int val;
    SinglyListNode *next;
    SinglyListNode(int x) : val(x), next(NULL) {}
};

python中节点定义

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

与数组不同,我们无法在常量时间内访问单链表中的随机元素。 如果我们想要获得第 i 个元素,我们必须从头结点逐个遍历。 我们按索引来访问元素平均要花费 O(N) 时间,其中 N 是链表的长度。

设计链表

首先需要设计节点

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

然后设计链表

重要的是链表的头是哪个节点,以及链表的的尺寸(判断索引是否超出了范围)

class mylinkednote:
    def __init__(self,head=None,size=0)
        self.head=None
        self.size=size
    def get(self,index:int)->int:
        if index<0 or index>size-1:
            return -1
        cur=self.head
        for i in range(index):
            cur=cur.next
        return cur.val
    def addathead(self,val:int)->None:
        self.head=Node(val,next=self.head)
        self.size+=1
    def addattail(self,val:int)->None:
        if size=0:
            self.head=Node(val)
        else:
            cur=self.head
            for i in range(size):
                cur=cur.next
            cur.next=Node(val)
            self.size+=1
    def addatindex(self,val:int,index:int)->None:
        if index<0 or index>size-1:
            return 
        elif index==0:
            self.addathead(val)
        else:
            cur=self.head
            for i in range(index):
                cur=cur.next
            cur.next=Node(val,cur.next)
            self.size+=1
    def deleteatindex(self,index:val)->None:
        if index<0 or index>size-1:
            return 

        elif index==0:
            self.head=self.head.next
        else:
            cur=self.head
            for i in range(index):
                cur=cur.next
            cur.next=cur.next.next
            self.size-=1

1.检查是否有环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

思路:如果有环,快节点和慢节点一起出发一定会遇上

注意:要保证fast还有下两个节点

def hascycle(self,head)->bool:
    if not head or not head.next:
        return False
    fast=head.next
    slow=head
    while slow!=fast:
        if not fast.next or not fast:
            return False
        fast=fast.next.next
        slow=slow.next
    return True

2.给出环形链表开始循环的地方

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

思路:在他们第一次相遇之后,把其中一个节点放回出发点,他们再次相遇的地方就是循环开始点

def detectcycle(self,head)->:
    #先排除异常
    if not head or not head.next:
        return 
    #寻找第一次相遇点
    fast,slow=head
    while True:
        fast=fast.next.next
        slow=slow.next
        if not fast or fast.next:
            return
        if fast==slow:
            break
    fast=head
    while fast!=slow:
        fast,slow=fast.next,slow.next
    return fast

3.相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

思路:如果两条链相交的话,在一个节点走完一条链之后接着走另外一条链他们一定会相遇


class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        pA, pB = headA, headB
        while pA != pB:
            # 如果节点pA到达链表末尾,则将其移动到链表headB的头节点
            # 否则,移动到下一个节点
            pA = pA.next if pA else headB
            # 如果节点pB到达链表末尾,则将其移动到链表headA的头节点
            # 否则,移动到下一个节点
            pB = pB.next if pB else headA
        # 返回相交节点,如果不存在相交节点,最终pA和pB都会等于None
        return pA

4.删除链表的第倒数第n个节点

思路:双指针法

陷阱:倒数第n个节点可能是head,因此虚拟一个dummy节点,让slow从dummy出发

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode(0, head)
        first = head
        second = dummy
        for i in range(n):
            first = first.next

        while first:
            first = first.next
            second = second.next
        
        second.next = second.next.next
        return dummy.next

双指针问题C++模板

// Initialize slow & fast pointers
ListNode* slow = head;
ListNode* fast = head;
/**
 * Change this condition to fit specific problem.
 * Attention: remember to avoid null-pointer error
 **/
while (slow && fast && fast->next) {
    slow = slow->next;          // move slow pointer one step each time
    fast = fast->next->next;    // move fast pointer two steps each time
    if (slow == fast) {         // change this condition to fit specific problem
        return true;
    }
}
return false;   // change return value to fit specific problem

5.反转链表

这里涉及到递归,先mark一下

6.删除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

注意:

像这种头节点有可能被删掉的情况要创建虚拟头节点简化边界处理

dummy=Node(next=head)

7.奇偶链表

给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

思路,奇偶头节点分别从第一个和第二个节点出发,每次移动两个节点,最后连接上。

class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head:
            return head
        
        evenHead = head.next
        odd, even = head, evenHead
        while even and even.next:
            odd.next = even.next
            odd = odd.next
            even.next = odd.next
            even = even.next
        odd.next = evenHead
        return head

8.回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

思路:把前一半链表翻转过来和后一半链表对比,怎么寻找到一半呢,就是用快慢指针法,快的一次走俩,慢的一次走一个

涉及到递推,先空着

9.双链表

与单链表的区别:单链表只能从前向后,双链表既可以向前,也可以向后

写法上的区别:

1️⃣节点定义里除了定义val,next,还有prev

2️⃣链表初始化里除了head,size,还有tail,而且还要把head和tail联系起来

3️⃣插入和删除的时候看离前边近还是后边近决定往前推还是往后推

class Listnode:

    def __init__(self,val):
        self.val=val
        self.next=None
        self.prev=None

class MyLinkedList:

    def __init__(self):
        self.size=0
        self.head,self.tail=Listnode(0),Listnode(0)
        self.head.next=self.tail
        self.tail.prev=self.head


    def get(self, index: int) -> int:
        if index<0 or index>=self.size:
            return-1
        elif index <= self.size // 2:
            cur=self.head
            for _ in range(index+1):
                cur=cur.next
        else:
            cur=self.tail
            for _ in range (self.size-index):
                cur=cur.prev
        return cur.val



    def addAtHead(self, val: int) -> None:
        self.addAtIndex(0,val)



    def addAtTail(self, val: int) -> None:
        self.addAtIndex(self.size,val)


    def addAtIndex(self, index: int, val: int) -> None:
        if index>self.size:
            return 
        if index <= self.size // 2:
            pred=self.head
            for _ in range(index):
                pred=pred.next
            succ=pred.next
        else:
            succ=self.tail
            for _ in range(self.size-index):
                succ=succ.prev
            pred=succ.prev
        self.size+=1
        to_add=Listnode(val)
        to_add.prev=pred
        to_add.next=succ
        pred.next=to_add
        succ.prev=to_add




    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return 
        if index < self.size - index:
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next.next
        else:
            succ = self.tail
            for _ in range(self.size - index - 1):
                succ = succ.prev
            pred = succ.prev.prev
        self.size -= 1
        pred.next = succ
        succ.prev = pred

还有一些更难的题,等到日后再解决

  • 19
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小知识猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值