文章目录
单链表操作
大纲
链表形式 |
---|
单项,双向 |
带头(头节点不存有效数据),不带头 |
循环,不循环 |
组合起来有:2x2X2=8种组合方式
核心⭐
- TIPS:通过指针的修改,都是通过形参和实参同时指向一块区域,通过形参简介修改实参,而对于形参本身的赋值,删除无法影响到实参本身;若要修改实参本身,只能通过二级指针的方式;
- 如果有可能第一个节点会发生改变,那就要传二级指针(带头节点可以不传)
1-链表的建立
#include<stdio.h>
#include<stdlib.h>
typedef struct _list {
int num;
struct _list* next;
}list;
list* build();
int main() {
list* head;
head = build();
Print_Stu_Doc(head);
system("pause");
return 0;
}
struct _list* build() {
int val;
list* head, * tail;
head = tail = NULL;
scanf("%d", &val);
if (-1 == val) {
return head;
}
while (-1 != val) {
list* p = (list*)malloc(sizeof(list));
p->num = val;
p->next = NULL;
if (NULL == head) {
head = p;
}
else tail->next = p;
tail = p;
scanf("%d", &val);
}
return head;
}
1.1head,tail,p相关问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I9P5ui1D-1664027324418)(C:\Users\Hello3\AppData\Roaming\Typora\typora-user-images\image-20220418222220080.png)]
head,tail,p属于三个不同的地址,即便三者指向同一区域,三者互不影响;
head=tail=NULL;
head=p;
tail=p
p->num = val;
p->next = NULL;
if (NULL == head) {
head = p;
}
else tail->next = p;
tail = p;
scanf("%d", &val);
由于tail和head同时指向同一个指针域;tail->next所修改的是tail和head同时指向的指针域,也会影响到head->next;
在这里,第一步赋值时候;
head=p;
tail=p
在这里由于head和tail不是同一指针,所以对tail=p;进行修改不会影响到head;
2-链表的打印
void list_print(list* head);
void list_print(list* head) {
list * ptr;
if (head == NULL) {
printf("No Records\n");
return;
}
for (ptr = head; ptr; ptr = ptr->next)
printf("%d ", ptr->num);
printf("\n");
}
3-链表的插入
3.1 尾插法
要改变头指针,传二级指针,不改变头指针,传一级指针
int main() {
...........
list_pushback(&head, 5);
.......
}
void list_pushback(list** pplist, int val);
void list_pushback(list** pplist, int val) {
list* newnode = newchain(val);
if (NULL == *pplist) {//判断是否为空指针
*pplist = newnode;
}
else {
list* tail = *pplist;
while (tail->next) {//定位到尾
tail = tail->next;
}
tail->next = newnode;
}
}
函数:值的传递,并对值进行操作:
一级指针的值:所指向的数据
二级指针的值:所指向的二级指针
3.1.1一级指针无法对空链表进行尾插问题
(实则是函数实参形参问题)
void list_pushback(list* plist, int val) {
list* newnode = newchain(val);
if (NULL == plist) {
plist = newnode;
}
else {
list* tail = plist;
while (tail->next) {//定位到尾
tail = tail->next;
}
tail->next = newnode;
}
}
如果使用一级指针,那么当传入的plist为空的时候,对形参plist的修改无法影响到实参,故无法成功尾插;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8XOe13U-1664027324419)(C:\Users\Hello3\AppData\Roaming\Typora\typora-user-images\image-20220422135115410.png)]
//当为非空指针的时候,是通过tail指向和实参plist一样的区域,在对tail->next赋值的同时修改了实参plist->next指向区域的值!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OAiBbDh9-1664027324419)(C:\Users\Hello3\AppData\Roaming\Typora\typora-user-images\image-20220419170140478.png)]
当head是空时,我们要让实参head被赋值(newcode),是修改head本身
当head不是空时,我们修改的他next指向的值,是修改head里的指针
3.2 头插法
void list_pushfront(list** pplist, int val)
void list_pushfront(list** pplist, int val) {
list* newcode = newchain(val);
newcode->next = *pplist;
*pplist = newcode;
}
头插法即便头指针是空,也能照场头插;
//newcode->next=*pplist(NULL)
3.3 单项链表的节点后插入
void list_insertafter(list* node, int x);
//链表节点后插入 (对于单项链表,节点前前插需要传头地址,效率低麻烦,多不使用)
void list_insertafter(list* node, int x) {
assert(node);//断言,防止node是空 <assert.h>
list *next =newchain(x);
next->next = node->next;
node->next = next;
}
4-链表的删除
4.1 尾删法
void list_popback(list**pplist);
void list_popback(list**pplist) {
if ( *pplist== NULL)return;
else if ((*pplist)->next == NULL) {
free(*pplist);
*pplist = NULL;
}
else {
list* pre = NULL;
list* tail = *pplist;
while (tail->next) {
pre = tail;
tail = tail->next;
}
free(tail);//动态删除空间
tail = NULL;
pre->next = NULL;
}
}
一级指针无法处理没有节点,或者只有一个节点的情况;
只有一个节点时,不存在pre,故无法通过pre使plist指向NULL;
//形参本身=NULL的修改无法影响到实参
4.2 头删法
void list_popfront(list** pplist);
void list_popfront(list** pplist) {
if (*pplist == NULL) {
return;
}
else {
list* next = (*pplist)->next;//保存头节点的下一个地址;
free(*pplist); //便于free掉内存
*pplist = next; //也可以解决*pplist只有一个节点的问题(直接等于*pplist->next(NULL))
}
}
4.3 单向节点的后删除
void list_popafter(list* node);
//链表节点后删 (对于单项链表,节点前前删需要传头地址,效率低麻烦,多不使用)
void list_popafter(list* node) {
list* next = node->next;
if (next) {
node->next = next->next;
free(next);
next = NULL;
}
}
5-链表的查找
list* list_find(list* head, int x);
list* list_find(list* head, int x) {
list* cur = head;
while (cur) {
if (x == cur->num)return cur;
cur = cur->next;
}
return NULL;
}
5.1查找兼具修改功效
代码如下 👇
list* node = list_find(head, 2);
if (node) {
printf("找到了\n");//查找兼具修改作用
node->num = 20;
}
else {
printf("没找到\n");
}
运行如下👇
6-链表的反转
6.1 三指针遍历
void list_reverse(list** head);
void list_reverse(list** head) {
if (*head == NULL || (*head)->next == NULL)return;
list* n1 = NULL, *n2 = (*head), *n3 = (*head)->next;
while (n2) {
n2->next = n1;//反转
n1 = n2;//遍历
n2 = n3;
if (n3)n3 = n3->next;
}
*head = n1;
}
6.2 新链表头插法
void list_reverse2(list** head);
void list_reverse2(list** head) {
list* newh = NULL, * next = *head;
while (next) {
list* p = (list*)malloc(sizeof(list));
p->num = next->num;
p->next = newh;
newh = p;
next = next->next;
}
*head = newh;
}
C解法
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x) {
SListNode* new = (SListNode*)malloc(sizeof(SListNode));
return new;
}
// 单链表打印
void SListPrint(SListNode* plist) {
SListNode* cur = plist;
while (cur) {
printf("%d -", cur->data);
}
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {
SListNode* new = (SListNode*)malloc(sizeof(SListNode));
SListNode* head = *pplist;
new->data == x; new->next = NULL;
if (head == NULL);
*pplist = new;
while (head)head = head->next;
head->next = new;
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x) {
SListNode* new = (SListNode*)malloc(sizeof(SListNode));
new->data == x;
new->next == *pplist;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist) {
SListNode* head = *pplist;
if (head == NULL||head->next==NULL); return;
while (head->next)head = head->next;
free(head->next);
head->next == NULL;
}
// 单链表头删
void SListPopFront(SListNode** pplist) {
if (*pplist == NULL)return;
SListNode* cur = *pplist;
if ((*pplist)->next == NULL)* pplist == NULL;
*pplist = ( * pplist)->next;
free(cur);
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {
SListNode* cur = plist;
int flag = 1;
while (cur) {
if (cur->data == x) { printf("Find !"); flag = 0; break; }
cur = cur->next;
}
if (flag)printf("Not Find");
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x) {
SListNode* new = (SListNode*)malloc(sizeof(SListNode));
new->data = x;
if (pos->next->next) {
new->next = pos->next->next;
pos->next = new;
}
else {
new->next = NULL;
pos->next = new;
}
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos) {
SListNode* cur = pos->next;
if (pos->next)pos->next = pos->next->next;
free(cur);
}
// 单链表的销毁
void SListDestroy(SListNode* plist) {
while (plist) {
SListNode* p = plist;
plist = plist->next;
free(p);
}
}]()
双向链表
双向循环链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L87IvKto-1664027324420)(https://tse4-mm.cn.bing.net/th/id/OIP-C.Gf8hYevZ3hebqu6wvetH5AHaDT?pid=ImgDet&rs=1)]
初始化,创建新链表
typedef struct ListNode {
int val;
struct ListNode *next;
struct ListNode* prev;
}LTNode;
LTNode* ListInit();//初始化新链表 带头
LTNode* NewList(int x);//创建新链表
void ListPushBAck(LTNode* phead, int x);
void PrintNode(LTNode* phead);
LTNode* ListInit() {
LTNode* guard = (LTNode*)malloc(sizeof(LTNode));
if (guard == NULL) {
perror("malloc fail");
exit(-1);
}
guard->next = guard;
guard->prev = guard;
return guard;
}
LTNode* NewList(int x) {
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL) {
perror("node malloc fail");
exit(-1);
}
node->next = NULL;
node->prev = NULL;
node->val = x;
return node;
}
打印
void PrintNode(LTNode* phead) {
LTNode* cur;
cur =phead->next;
printf("phead<->");
while (cur != phead) {
printf("%d<->", cur->val);
cur = cur->next;
}
printf("\n");
}
头插与尾插
void ListPushFront(LTNode* phead, int x) {//头插
LTNode* LastHead = phead->next;
LTNode* NewList = (LTNode*)malloc(sizeof(LTNode));
NewList->val = x;
NewList->next = LastHead;
LastHead->prev = NewList;
phead->next = NewList;
NewList->prev = phead;
//ListInsert(phead->next, x);插入代码实现头插
}
void ListPushBAck(LTNode* phead, int x) {//尾插
// assert(phead);
LTNode* newnode = NewList(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//ListInsert(phead, x);插入代码实现尾插
}
头删与尾删
bool NullList(LTNode* phead) {//判断链表是否为空
return phead->next == phead;
}
void ListPopFront(LTNode* phead) {//头删
if (NullList(phead)) { printf("空链表!\n"); return; }
LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
//ListErase(phead->next);复用删除代码实现头删
}
void ListPopBack(LTNode* phead) {//尾删
if (NullList(phead)) { printf("空链表!\n"); return; }
LTNode* tail = phead->prev;
LTNode* tailpre = tail ->prev;
tailpre->next = phead;
phead->prev = tailpre;
free(tail);
//ListErase(phead->prev);复用删除代码实现尾删
}
插入
void ListInsert( LTNode* pos,int x) {
assert(pos);
LTNode * ret=NewList(x);
LTNode* prev = pos->prev;
prev->next = ret;
ret->prev = prev;
ret->next = pos;
pos->prev = ret;
}
删除
void ListErase(LTNode* pos) {//pop不可是guard
LTNode* pre, * next;
assert(pos);
pre = pos->prev;
next = pos->next;
pre->next = next;
next->prev = pre;
free(pos);
}
链表的插入和删除能够实现尾插头插
链表的销毁
void Destory(LTNode* phead);//链表的销毁
void Destory(LTNode* phead) {
assert(phead);
LTNode *cur = phead->next;
while (cur != phead) {
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(cur);
}
Destory(c);//
c = NULL;//手动滞空节点C
= pos->prev;
prev->next = ret;
ret->prev = prev;
ret->next = pos;
pos->prev = ret;
}
### 删除
```c
void ListErase(LTNode* pos) {//pop不可是guard
LTNode* pre, * next;
assert(pos);
pre = pos->prev;
next = pos->next;
pre->next = next;
next->prev = pre;
free(pos);
}
链表的插入和删除能够实现尾插头插
链表的销毁
void Destory(LTNode* phead);//链表的销毁
void Destory(LTNode* phead) {
assert(phead);
LTNode *cur = phead->next;
while (cur != phead) {
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(cur);
}
Destory(c);//
c = NULL;//手动滞空节点C