leetcode刷题-链表01

链表理论基础

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

链表的结构和类型

链表的头节点(head)(单链表👇)
链表的头结点
双链表:每一个节点有两个指针域,既可以向前查询也可以向后查询
双链表结构
循环链表:链表首位相连
循环链表结构

链表的存储方式

数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。链表是通过指针域的指针链接在内存中各个节点。

链表的定义

C/C++中定义链表节点的方式

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};
// 初始化节点
ListNode* head = new ListNode(5);

// 也可使用默认构造函数初始化节点
ListNode* head = new ListNode();
head->val = 5;

python中定义链表节点的方式

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

链表操作及性能分析

链表操作:删除节点、添加节点
性能分析:与数组的性能对比

插入删除时间复杂度查询时间复杂度
数组O(n) (将所有元素进行修改)O(1) (可通过下标索引)
链表O(1) (直接修改前一个节点和该节点的指向O(n) (需要遍历查询)
(删除节点 C++需要手动释放内存,java,python有内存回收机制)

203.移除链表元素

leetcode题目链接
代码随想录文档讲解

思路:
移除一般节点较好实现,重点考虑移除头节点:head = head.next,实现代码时①判断该节点是不是头节点,②或者使用虚拟头结点可以统一进行删除dummy head

1)原链表删除元素:分情况处理,是头节点和不是头节点
2)使用虚拟头节点:统一处理,一个循环就可以了

1)原链表删除元素

判断要删除的元素是不是头结点
注意: 代码中while 不是 if;新定义一个变量cur指向head进行删除节点,不要对head进行变动
伪代码

while(head!=NULL && haed->val == target) # 删除头节点的情况
	head = head->next
cur = head
while(cur != NULL && cur->next != NULL)
	if(cur->next->val == target)
		cut->next = cur->next->next
	else
		cur = cur->next
return head

python代码

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        while head and head.val == val:
            head = head.next
        cur = head 
        while cur and cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return head

2)使用虚拟头节点

思路
在头节点前添加一个dummyhead,之后可以用while循环统一进行处理,最后返回dummyhead.next
伪代码

dummyhead = new()
dummyhead->next = head
cur = dummyhead
while(cur != NULL && cur->next != NULL)
	if(cur->next->val == target)
		cur->next = cur->next->next
	else
		cur = cur->next
return dummyhead.next

python代码
dummyhead = ListNode() dummuhead.next = head
可简化为:dummyhead = ListNode(next = head)

while cur and cur.next
可简化为:while cur.next

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        dummyhead = ListNode(next = head)

        cur = dummyhead
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummyhead.next

时间复杂度是O(n)

707.设计链表

leetcode题目链接
代码随想录文档讲解

1)获取第n个节点的值

使用虚拟头结点的方法
注意: 边界考虑,while循环中是n,有时候需要改为n-1或者n+1
【这里假设链表中的所有节点下标从 0 开始。】
伪代码

dummyhead = ListNode(next=head)
if index < 0 or index >= self.size:
	return
curr = dummyhead.next
while n:
	curr = curr.next
	n-=1
return curr.val

2)头部插入节点

这里经常出现的错误:顺序问题: 应该先让newnode的下一个节点指向dummyhead的下一个节点,再改变dummyhead下一个的指向,即newnode.next = dummyhead.next在dummyhead.next = newnode之前执行
伪代码

dummyhead = ListNode(next=head)
newnode.next = dummyhead.next
dummyhead.next = newnode
size+=1

3)尾部插入节点

当curr.next为空时,说明已经指向了尾部
注意: curr = dummyhead 而不是 curr = dummyhead.next,因为while中判断了下一个
伪代码

curr = dummyhead
while curr.next != NULL:
	curr = curr.next
curr.next = newnode

4)第n个节点前插入节点

curr要指向第n-1个节点,才能在第n个节点前插入,所以curr = dummyhead而不是curr = dummyhead.next
注意: 边界考虑,while循环中是n,当n取0时成立
伪代码

curr = dummyhead
while n:
	curr = curr.next
	n-=1
newnode.next = curr.next
curr.next = newnode.next

5)删除第n个节点

要对n的合法性进行判断
curr要指向第n-1个节点,才能删掉第n个节点
伪代码

if n<0 or n>size:
return
curr = dummyhead
while n:
	curr = curr.next
	n-=1
curr.next = curr.next.next
size-=1

注意 ①class类的写法,代码中没有dummyhead.next = head,因为在初始化类时先定义了dummyhead,所以它是第一个节点
②对n的合法性进行判断 index<0 or index >= self.size: 不满足情况 ,因为链表中的所有节点下标从 0 开始,size=最后一个节点的下标+1
③curr = self.dummyhead.next 还是 curr = self.dummyhead
if index < 0 or index >= self.size: 还是 if index < 0 or index > self.size:

完整python代码

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val 
        self.next = next
class MyLinkedList:
    def __init__(self):
        self.dummyhead = ListNode()
        self.size = 0

    def get(self, index: int) -> int:
        if index < 0 or index >= self.size:
            return -1
        curr = self.dummyhead.next
        while index:
            curr = curr.next
            index -= 1
        return curr.val

    def addAtHead(self, val: int) -> None:
        newnode = ListNode(val=val, next=self.dummyhead.next)
        self.dummyhead.next = newnode
        self.size += 1

    def addAtTail(self, val: int) -> None:
        curr = self.dummyhead
        while curr.next:
            curr = curr.next
        newnode = ListNode(val=val)
        curr.next = newnode
        self.size += 1

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0 or index > self.size:
            return 
        curr =  self.dummyhead
        while index:
            curr = curr.next
            index -= 1
        newnode = ListNode(val, curr.next)
        curr.next = newnode
        self.size += 1

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return 
        curr =  self.dummyhead
        while index:
            curr = curr.next
            index -= 1
        curr.next = curr.next.next
        self.size -= 1


# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)

206.反转链表【面试易考题】

leetcode题目链接
代码随想录文档讲解

双指针、递归两种解法

1)双指针解法

思路
pre curr两个指针,将这两个指针改变方向,就是一步一步进行反转,这里需要额外定义一个temp指针,保存下一个
具体实现动画

伪代码

curr = head
pre = NULL
while curr:
	temp = curr.next
	curr.next = pre
	pre = curr # 先移动pre再移动curr
	curr = temp
return pre

python代码
注意while中的边界条件需要仔细考虑,while curr而不是while curr.next,因为最后一个是空指针,temp = curr.next

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        curr = head
        pre = None
        while curr:
            temp = curr.next
            curr.next = pre
            pre = curr
            curr = temp
        return pre
# 更简洁版本
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode(float("inf"))
        while head:
            dummy.next, head.next, head = head, dummy.next, head.next
        return dummy.next

时间复杂度:O(n)
空间复杂度:O(1)

2)递归解法

思路
pre curr两个指针,将这两个指针改变方向,就是一步一步进行反转,这里需要额外定义一个temp指针,保存下一个

伪代码

reverse(cur, pre){
	if(curr == NULL)
		return pre
	temp  = curr.next
	curr.next = pre
	reverse(temp, cur)
}

reverselist(head){
	return reverse(head,NULL)
}

python代码

class Solution:
    def reverse(self, curr:ListNode, pre:ListNode):
        if curr == None:
            return pre
        temp = curr.next
        curr.next = pre
        return self.reverse(temp, curr)

    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        return self.reverse(head, None)

时间复杂度:O(n)
空间复杂度:O(n)

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值