第2讲 | 数据结构与算法 | 【数组和链表】

- 《数据结构与算法》专栏完整版在公众号【书伟认视界】中查看,转载需联系微信【econe0219


数组和链表

数组:一种线性表(具有前后顺序)数据结构。用一组连续的内存空间,来存储一组具有相同类型的数据。

链表:通过指针将零散的内存快串联在一起。

单向链表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qScuoy6-1597839138292)(C:\Users\Thinkpad\AppData\Roaming\Typora\typora-user-images\image-20200409161201800.png)]

插入、删除操作时,数组的时间复杂度是O(n),而链表不需要保证数据的连贯性,时间复杂度是O(1),但是访问元素时,需要根据指针一个结点一个结点地遍历,直到找到相应元素的节点,所以需要O(n)的时间复杂度。

循环链表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YiyVbDwg-1597839138294)(C:\Users\Thinkpad\AppData\Roaming\Typora\typora-user-images\image-20200409161445293.png)]

循环链表的优点是,从链尾到链头比较方便,适合处理具有环行结构的数据。

双向链表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D0sSc4Yo-1597839138297)(C:\Users\Thinkpad\AppData\Roaming\Typora\typora-user-images\image-20200409173222247.png)]

双向链表遍历更方便灵活,只是多消耗一点点内存(用空间复杂度来换取时间复杂度),使用最广泛。

数组和链表的区别:链表适合插入、删除,时间复杂度O(1);数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。

时间复杂度数组链表
插入操作O(n)O(1)
随机访问O(1)O(n)

写链表代码的注意事项

一、理解指针或引用。(有些语言用“引用”取代指针,如python,都指的是存储所指对象的内存地址

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

二、注意指针丢失和内存泄漏

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgtKVi6x-1597839138304)(C:\Users\Thinkpad\AppData\Roaming\Typora\typora-user-images\image-20200408231332814.png)]

如图,打算在ab节点中间加入x节点。

如下代码:

p->next = x; // 将p的next指针指向x节点
x->next = p->next  // 将x节点的next指针指向b节点

第一步完成之后,就切断了和b节点之间的联系,第二步相当于实现x自己指向自己,导致指针丢失,内存泄漏。正确做法是以上两行代码颠倒顺序写。

三、利用哨兵简化难度

先来看下单链表的常规插入和删除操作

// p节点后插入一个节点
new_node->next = p->next;
p->next = new_node;

// 在空链表中插入第一个节点的逻辑
if(head == null){
    head = new_node;
}

// 删除p节点的后继节点
p->next = p->next->next;

// 删除链表的最后一个节点(=只剩一个节点≠尾节点)
if(head->next == null){
    head = null;
}

由上知对于插入第一个节点和删除最后一个节点需要做特殊处理。如果引入哨兵节点,实现就变得简洁很多。如下如所示。

在任何时候,不管链表是不是空,head指针都会一直指向这个哨兵节点。把这种有哨兵节点得链表叫带头链表。

四、注意边界条件处理

比如检查边界条件有如下几种:

  • 如果链表为空,代码能否正常工作?
  • 如果链表只包含一个节点时,代码是否能正常工作?
  • 如果链表只包含两个节点时,代码能否正常工作?
  • 代码逻辑在处理头节点和尾节点时,时候能正常工作?

······

五、多举例画图,辅助思考

六、练习( 5个常见链表操作)

1)反转单链表

2)检测链表中的环

3)两个有序链表的合并

4)删除列表倒数第n个节点

5)求链表的中间节点

为方便从代码上理解链表,这里给出第一个小问题的代码,后面几个问题朋友们可以自己做一做,Leetcode上都有。这里多说一句,关于链表的代码,就是死记,背会,没有其他办法。没多少思维的考察,就是实现能力。

原理:反转单链表,顾名思义就是把一个单链表反转过来,第一个节点变成最后一个节点。如下:

Input: 1—>2—>3—>4—>5—>NULL
Output: 5—>4—>3—>2—>1—>NULL

首先定义一个指向NULL的前驱节点prev(previous),因为反转之后,头节点变成尾节点之后要指向NULL。然后循环,从前往后改变指针的方向。(如若不理解,文末有相关解析链接[5],或者自己网上搜一下实现逻辑,笔者这里就不详细展开了,因为我主要说数据结构,暂时不专门讲题目)

# 反转单链表
def ReverseList(self, head):
    cur, prev = head, None
    while cur:
        cur.next, prev, cur = prev, cur, cur.next
    rerurn prev    



《数据结构与算法》专栏完整版在公众号【书伟认视界】中查看,转载需联系微信【econe0219

.

在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值