二叉树相关算法
链表相关知识点:
链表
是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
知识点一:链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
知识点二:相比于线性表顺序结构(数组),操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到 O ( 1 ) O(1) O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要 O ( n ) O(n) O(n)的时间,而线性表和顺序表相应的时间复杂度分别是 O ( l o g n ) O(logn) O(logn)和 O ( 1 ) O(1) O(1)。
知识点三:使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
知识点四:链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。
链表有很多种不同的类型:单向链表,双向链表以及循环链表。
题目
链表中环的入口结点
典型题例:
给定一个链表,若其中包含环,则输出环的入口节点。
若其中不包含环,则输出null。
示例 :
给定如上所示的链表:
[1, 2, 3, 4, 5, 6]
2
注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。
则输出环的入口节点3.
思路
本题的做法比较巧妙。
用两个指针
f
i
r
s
t
first
first,
s
e
c
o
n
d
second
second,分别从起点开始走,
f
i
r
s
t
first
first每次走一步,
s
e
c
o
n
d
second
secondd 每次走两步。
如果过程中
s
e
c
o
n
d
second
second 走到null
,则说明不存在环。否则当
f
i
r
s
t
first
first 和
s
e
c
o
n
d
second
second 相遇后,让
f
i
r
s
t
first
first返回起点,
s
e
c
o
n
d
second
second 待在原地不动,然后两个指针每次分别走一步,当相遇时,相遇点就是环的入口。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *entryNodeOfLoop(ListNode *head) {
//判断参数
if (!head || !head->next) return 0;
//利用慢指针 i 和快指针 j,i走一步j走两步
auto i = head, j = head;
while (i && j){
i = i->next;
j = j->next;
if (j){
j = j->next;
if (!j) return nullptr; //j = NULL
}
//i,j相遇将指针i从头开始走, i,j都一次走一步,相遇的位置就是入口
if (i == j){
i = head; //指针i从头开始走
while (i != j){
i = i->next;
j = j->next;
}
return i; //走到相遇的点就是入口
}
}
return 0;
}
};
反转链表
典型题例:
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
示例 :
输入:1->2->3->4->5->NULL
输出:5->4->3->2->1->NULL
思路
核心:
翻转即将所有节点的next指针指向前驱节点。
由于是单链表,我们在迭代时不能直接找到前驱节点,所以我们需要一个额外的指针保存前驱节点。同时在改变当前节点的next指针前,不要忘记保存它的后继节点。
空间复杂度分析:遍历时只有3个额外变量,所以额外的空间复杂度是
O
(
1
)
O(1)
O(1)。
时间复杂度分析:只遍历一次链表,时间复杂度是
O
(
n
)
O(n)
O(n)。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//递归版
if(!head || !head->next) return head;
auto tail = reverseList(head->next);//从尾节点开始递归
head->next->next = head;
head->next = NULL;
return tail;
}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//迭代版本
if(!head || !head->next) return head;
auto a = head, b = a->next;
while(b){
auto c = b->next;
b->next = a;
a = b, b = c;
}
head->next = NULL;
return a;
}
};
充电站
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习