千灯理解的链表QAQ
指针的概念:
1.指针的意义:
指向地址。
理解:
我们开变量的时候会有一个地址,这个地址存储着变量。
比如:
int x;
↑这样定义了一个int类型的变量x
&x是x的地址
cout<<&x;
↑这样就是输出x的地址
scanf("%d",&x);
↑比如我们常用的scanf,就是直接在地址里面存入变量,因此十分的快
2.定义指针
void *x;
↑定义一个void类型(可以是int,long long,string…)的指针
3.指针的操作
void *x;//伪代码,类型可以自己定的
x=&a;
↑将a的地址给x
void *x;
x=&a;
cout<<*x;
↑输出x地址存储的变量,* 用来调用地址存储的变量 举个例子:
#include<bits/stdc++.h>
using namespace std;int main()
{
int *x;
int a;
cin>>a;
x=&a;
cout<<*x;
}
↑输出结果为a注意,指针只能指向同类型的地址。
int *a;
string x;
cin>>x;
a=&x;
↑错误示范,此乃违法操作。
x->y
↑x必须是一个指针,而且y必须包含在x指向的内容中,输出是y,但是要通过x调取
#include<bits/stdc++.h>
using namespace std;
struct node
{
int *y;
}k;
int main()
{
node *x;
int a;
cin>>a;
x=&k;
x->y=&a;
cout<<*x->y;
}
↑比如这样,x指向一个node类型的变量k,y是node类型中的一个int类型的指针,因此调取y需要x->y;
对于y不要求是什么类型,但是x必须是指针才可以进行此操作。
链表构造:
1.链表的概念:
我们定义一个struct类型代表一个点,这个点包括该点的数据(data,即输入进去的东西),和下一个点的地址 ↓。这是单向的链表,如果想要双向的增加last指针就行
struct point
{
int data;
point* next;
};
↑这样通过->next就依次可以知道所有的点,下面会细讲
2.构建链表:
首先一个链表要有头(head),有尾(end)
在单向的链表里,头指向最先点的前一个,尾指向最后点,这样在调取的时候比较方便
#include<bits/stdc++.h>
using namespace std;
struct point
{
int data;
point* next;
};
int main()
{
int x;
cin >> x;
point *end,*head = (point*)malloc(sizeof(point));
end = head;
for (int i = 1;i <= x;++i)
{
point *tmp = (point*)malloc(sizeof(point));
cin >> tmp->data;
end->next = tmp;
end = tmp;
}
end->next = NULL;//NULL就是0
}
↑end是每一次链表的结尾,因此每一次新加入一个点,即加在未加入结尾时的链表的结尾的后一个,然后end就是新加入的点(最后一个嘛)
上面这句话有点绕,画张图看看↓
1 - 5 - 2 - 9 - 8
h e
这是原来的链表(h=head,e=end)
新读入3,要把3加入链表的最后面
那么就要加在8的后面
1 - 5 - 2 - 9 - 8 - 3
h e e->next
tmp
这样子end->next就指向3,也就是tmp
因为end是指向point类型的指针
新加入3后我们发现end不在结尾上,那么我们要调整
即end=tmp
1 - 5 - 2 - 9 - 8 - 3
h e
要介绍一个新的函数malloc
malloc是个好用的东西,用来给新的节点分配内存(包括一个地址)
那么你想用for每次新开一个point不就行了↓
for(int i=1;i<=x;++i)
{
point* tmp;
}
↑但是很遗憾,每次for开出来地址是一样的QAQ
malloc的使用方法:
void* malloc(size)
↑void可以替换成其他类型
这样每次tmp都会是新的地址,新的内存
3.链表输出:
for (point *i = head->next;i != 0;i = i->next)
cout << i->data << endl;
↑超级简单,不想说了(请参考邻接表存图食用更佳)。
4.单向环型链表
#include<bits/stdc++.h>
using namespace std;
struct point
{
int data;
point* next;
};
int main()
{
int x;
cin >> x;
point* end, * head = (point*)malloc(sizeof(point));
end = head;
for (int i = 1;i <= x;++i)
{
point* tmp = (point*)malloc(sizeof(point));
cin >> tmp->data;
end->next = tmp;
end = tmp;
}
end->next = head->next;
}
↑就是把尾巴的下一个在头接上,注意head指向第一个点的前面。
5.双向链表
#include<bits/stdc++.h>
using namespace std;
struct point
{
int data;
point* next;
point* last;
};
int main()
{
int x;
cin >> x;
point* end, * head = (point*)malloc(sizeof(point));
end = head;
for (int i = 1;i <= x;++i)
{
point* tmp = (point*)malloc(sizeof(point));
cin >> tmp->data;
end->next = tmp;
tmp->last = end;
end = tmp;
}
end->next = NULL;
head->last = NULL;
for (point *i = head->next;i != 0;i = i->next)
cout << i->data << endl;
for (point *i = end;i != 0;i = i->last)
cout << i->data << endl;
}
↑双向链表就是多加一个last记录
关于last我们↓
1 - 5 - 2 - 9 - 8
h e
这是原来的链表(h=head,e=end)
新读入3,要把3加入链表的最后面
那么就要加在8的后面
1 - 5 - 2 - 9 - 8 - 3
h e e->next
tmp->last tmp
因此tmp的last是8的地址
而8的next是3的地址
然后输出还是一样的QAQ
链表修改
1.寻找第n个点
point* find(point* head,int n)
{
point *tmp=head;
for (int i = 1;i <= n;++i)
tmp = tmp->next;
return tmp;
}
↑由于head指第一个点前,所以n=1的时候指向第一个,所以不用考虑一些细节
返回即第n个点的地址↓
cout<<find(head,n)->data;
↑变量名什么的自己改
2.删除第n个点
首先必须寻找到他的地址,所以find放在删除前面
point* find(point* head,int n)
{
point *tmp=head;
for (int i = 1;i <= n;++i)
tmp = tmp->next;
return tmp;
}
void delete_(point* head,int n)
{
point *tmp=find(head, n);
tmp->last->next = tmp->next;
tmp->next->last = tmp->last;
}
↑我们通过find知道了第n个点的位置,那么第n-1个点和第n+1个点接上,就会忽略第n个点,达到了删除效果
所以上一个点的下一个点是第n个点的下一个点
所以下一个点的上一个点是第n个点的上一个点
如图↓
n-1 - n - n+1
tmp->last tmp tmp->next
删除第n个点后
n-1 - n+1
tmp->last tmp->next
所以得到了
tmp->last->next=tmp->next;
tmp->next->last=tmp->last;
这个函数直接在原来的链表上经行修改,所以说delete是很恐怖的。没事别乱删东西。。。
还有关于delete是一个库函数。所以要加下划线,调用请注意
3.在第n个点的前面插入数字k
跟delete一样,先找到地址再插入
point* find(point* head,int n)
{
point *tmp=head;
for (int i = 1;i <= n;++i)
tmp = tmp->next;
return tmp;
}
void insert_(point* head, int n, int k)
{
point *tmp2 = find(head, n);
point *tmp1 = tmp2->last;
point *insert_tmp = (point*)malloc(sizeof(point));
tmp1->next = insert_tmp;
insert_tmp->last = tmp1;
tmp2->last = insert_tmp;
insert_tmp->next = tmp2;
insert_tmp->data = k;
}
↑看上去很复杂其实还行QAQ
tmp2是n的地址,tmp1是n-1的地址,在n前插入k相当于在n-1和n间增加k
如图↓
n-1 - n
tmp1 tmp2
加入k以后
n-1 - k - n
tmp1 insert_tmp tmp2
tmp1->next
tmp2->last
insert_tmp->last insert_tmp->next
没错要存4条信息。。。
于是可以得到:
tmp1->next = insert_tmp;
insert_tmp->last = tmp1;
tmp2->last = insert_tmp;
insert_tmp->next = tmp2;
最后把insert_tmp->data=k就行了。
QAQ基本操作就这些了整理一下完整的代码QAQ
#include<bits/stdc++.h>
using namespace std;
struct point
{
int data;
point* next;
point* last;
};
point* find(point* head,int n)
{
point *tmp=head;
for (int i = 1;i <= n;++i)
tmp = tmp->next;
return tmp;
}
void delete_(point* head,int n)
{
point *tmp = find(head, n);
tmp->last->next = tmp->next;
tmp->next->last = tmp->last;
}
void insert_(point* head, int n, int k)
{
point *tmp2 = find(head, n);
point *tmp1 = tmp2->last;
point *insert_tmp = (point*)malloc(sizeof(point));
tmp1->next = insert_tmp;
insert_tmp->last = tmp1;
tmp2->last = insert_tmp;
insert_tmp->next = tmp2;
insert_tmp->data = k;
}
int main()
{
int x;
cin >> x;
point* end, * head = (point*)malloc(sizeof(point));
end = head;
for (int i = 1;i <= x;++i)
{
point* tmp = (point*)malloc(sizeof(point));
cin >> tmp->data;
end->next = tmp;
tmp->last = end;
end = tmp;
}
end->next = NULL;
head->last = NULL;
}
↑双向链表非环状
QAQ