Xue 2019.3.30
目录
0,感谢
1,基本概念
1,链表是物理存储单元上非连续的,非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现。
2,每个元素包含两个结点:1,存储元素,数据域(内存空间);2,指向下一个结点地址,指针域。
3,常见结构:单链表,双向链表,循环链表
优点:
1,不需要初始化容量,可以任意加减元素
2,添加或删除元素时只需要改变前后两个元素结点的指针域指向地址即可,能很快的添加/删除。
缺点:
1,因为需要大量的指针域,占用空间大
2,查找元素需要遍历链表,耗时大
适用场景:
数据量小,频繁增加/删除的场景。
1.1,单链表
1.2,双链表
主要优点: 更容易访问相邻结点
1.3,循环链表
主要优点: 从表中任意结点出发都能访问整个链表
2,常见题目
2.1,判断链表是否存在环型链表问题
问题描述: 判断一个链表是否存在环
//通过设置两个指针p1,p2,每次循环p1向前走一步,p2向前走两步。当p1,p2触碰到NULL,或者两者相等时结束。
struct link
{
int data;
link* next;
}
bool IsLoop(link* head)
{
link* p1=head,*p2=head;
if(head == NULL || head->next ==NULL)
return false;
//如果存在环,两者指针存在步差,肯定会在某一时刻相遇,不存在环,触碰到NULL结束返回即可。
do{
p1 = p1->next;
p2 = p2->next->next;
}while(p2 && p2->next && p1!=p2);
if(p1 == p2)
return true;
else
return false;
}
2.2,链表反转问题
问题描述: 将链表反转
方法1(就地反转法): 通过3个辅助指针即可完成,一个指针保留当前指针指向的下一个元素。
struct link{
int data;
link *next;
}
void reverse(link *&head)//*&表示link指针的引用
{
if(head == NULL)
return ;
link *pre,*cur,*ne;
pre=head;
cur=head->next;
//Step1,保留当前结点的接一个结点
//Step2,当前结点的下一个结点,指向前一个结点。将其赋值为初始结点pre
//Step3,当前结点为预留的下一个结点
while(cur)
{
ne = cur->next;
cur->next = pre;
pre = cur;
cur = ne;
}
//头变成尾,此时将结束位设为NULL
head->next = NULL;
head = pre;
return ;
}
方法2(递归法):
link* reverse(link *p,link *&head)
{
if(p == NULL || p->next == NULL)
{
head = p;
return p;
}
else
{
link *tmp = reverse(p->next,head);
tmp->next = p;
return p;
}
}
【LeetCode】反转链表 II
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
//以尝删除
if(head == NULL || head->next == NULL)
return head;
//新建表头
ListNode *dummy = new ListNode(-1);
dummy->next = head;
ListNode *dum = dummy;
for(int i=0;i<m-1;i++)
dum= dum->next;
ListNode *pre = dum->next;
ListNode *cur = pre->next;
//替换
for(int i=0;i<n-m;i++)
{
pre->next = cur->next;
cur->next = dum->next;
dum->next = cur;
cur = pre->next;
}
return dummy->next;
}
};