头指针
头指针可用于方便访问链表中的结点,创建链表分为带头结点的链表和不带头结点的链表,带头结点链表先开辟一个空间用于存放头结点,访问第一个结点使用head->next
。不带头结点的链表访问第一个结点直接使用head
访问第一个结点,两者各有各好处。
目前大多数都使用带头结点的链表,因为其在增、删、查、改时相比不带头结点的链表更具优势。
-
带头结点的链表创建
先开辟头结点空间(node)
List *createListHead(int n){ List *node,*parentNode,*p; int i; p=(List *) malloc(sizeof(List)); //保存头指针 parentNode=p; for(i=0;i<n;i++){ node=(List *)malloc(sizeof(List)); printf("请输入第 %d 个值:",i+1); scanf("%d",&(node->data)); node->next=NULL; p->next=node; p=p->next; } return parentNode; }
-
不带头结点的链表创建
先对头结点赋值结点(node),然后对node->next->next依次赋值操作
List *createList(int n){ List *node,*parentNode,*p; int i; p=(List *)malloc(sizeof(List)); printf("请输入第 %d 个值:",1); scanf("%d",&(p->data)); p->next=NULL; parentNode=p; for(i=1;i<=n-1;i++){ node=(List *)malloc(sizeof(List)); printf("请输入第 %d 个值:",i+1); scanf("%d",&(node->data)); node->next=NULL; p->next=node; p=p->next; } return parentNode; }
注意需要区分头结点和真正的链表元结点的区别.
简单链表的操作
简单链表的一般操作主要包含插入和删除(修改),以下操作均使用带头结点的链表讲解
1、链表的插入
链表的插入分为三种:
- 在开始处插入结点
- 在中间某处插入结点
- 在结尾处插入结点
其中在结尾处插入就类似于链表的创建,就不做示例
在开始处插入:
// 1、第一个位置前插入 传入HEAD->next
void insertHead(List *head){
List *NEW;
//为新结点分配一个地址空间
NEW=(List *)malloc(sizeof(List));
//假设新结点的值为0
NEW->data=0;
//新结点的next 为原来头结点后的链表,头结点后变为新结点即实现了第一个位置处的插入
NEW->next=head->next;
head->next=NEW;
}
在中间某处插入:
// 3、在中间某处插入链表
// 传入头结点,插入第n个结点处
void insertMiddle(List *head,int n){
List *NEW,*node,*currentNode;
int i;
NEW = (List *)malloc(sizeof(List));
NEW->data=0;
node=head;
for(i=0;i<n;i++){
//保存上一个结点
currentNode=node;
node=node->next;
}
//node 位置为需要插入的地方
//currentNode 为上一个结点位置
NEW->next=node;
currentNode->next=NEW;
}
2、链表的删除
链表的删除也主要为三种:
- 删除开始结点
- 删除中间结点
- 删除结尾结点
删除开始结点:
删除开始结点比较简单,就一句就可解决:head->next=head->next->next;
删除中间某结点:
void deleteMiddleList(List *head,int n){
List *node,*currentNode;
int i;
node = head;
for(i=0;i<n;i++){
currentNode=node;
node=node->next;
}
currentNode->next=node->next;
}
一个人为创建链表并将链表反向的简单例子:
#include<stdio.h>
#include<stdlib.h>
typedef struct Lnode{
int data;
struct Lnode *next;
} List;//typedef定义一个结构体,以后使用可以直接用List *p的方式调用
void print(List *h);
List *reverseprint(List *h);
List *createList(n);
int main(){
int num;
List *HEAD,*reverse=NULL;
printf("输入你要创建链表的数量:");
scanf("%d",&num);
HEAD=createList(num);
printf("你刚才创建的链表为:\n");
print(HEAD->next);//创建链表的时候没有对第一个链表操作,所以打印的时候传递HEAD->next
printf("反向输出链表位:\n");
reverse=reverseprint(HEAD);
print(reverse);
system("pause");
return 0;
}
//创建链表,返回一个链表指针
List *createList(n){
int i;//存储链表数据
List *parentnode,*node,*p;//定义一个父指针
p=(List *)malloc(sizeof(List));//动态分配大小并返回指向改内存的指针
//p=NULL;
parentnode=p;
for(i=1;i<=n;i++){
node=(List *)malloc(sizeof(List));//动态分配大小并返回指向改内存的指针
printf("输入第%d个链表数据:",i);
scanf("%d",&(node->data));
node->next=NULL;
p->next=node;//*p的下一节点为当前节点
p=p->next;//指向下一节点
}
return parentnode;
}
//输出链表
void print(List *h){
List *p=h;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
printf("\n\n");
}
//将链表反向
List *reverseprint(List *h){
List *currentNode, *preNode, *nextNode;
//当前节点为HEAD->next(因为HEAD链表第一个结构体没有做操作)
currentNode =h->next;
preNode = NULL;
nextNode = NULL;
if (currentNode == NULL)//如果为空则返回空
return NULL;
while (currentNode!=NULL)
{
nextNode = currentNode->next;//下一节点为当前节点的下一节点
currentNode->next = preNode;//当前节点的下一节点为上一个节点,如下行,从而实现了逆序
preNode = currentNode;//下一节点为当前节点,在下次循环赋值给下一节点,如上行
currentNode = nextNode;//当前节点为下一节点,做循环判断
}
return preNode;//返回逆序后的链表
}
- 在链表操作时使用一个中间变量结点(node)当做结点每次改变的临时地址存储,在使用p->next重新指向
- 链表指针传递过程p=node–>next–>p=node–>next
箭头和点的区别:
- 指针访问结构体对象时用箭头访问 ->
- 普通结构体访问结构体对象使用点 .