关于头节点与头指针的一些说明
头指针:链表中第一个结点的存储位置就叫做头指针
在实现链表操作时,有两种方法去实现:一种是带头结点的,一种是不带头结点的,即只有头指针的情况。
针对于第一种:
这种情况下,头指针是指向头结点的。接着头结点才指向我们的首结点,即真正我们要插入的第一个结点。
头结点的数据域一般是不存任何数据的,但是也可以存储链表的长度之类的。
有了头结点后,对在第一个元素结点插入结点和删除结点,其操作与对其它结点的操作统一了。
针对于第二种情况
对于这种情况,我们的头指针就直接指向了我们要插入的元素的,因此我们可以发现一个问题,即我们插入或删除的时候,需要区分一下要插入或者删除的是否是第一个元素,假如是第一个元素,我们需要改变头指针的值,假如不是第一个元素,我们要改变的值则是前一个元素所指向的next
从这里我们可以看出,头结点的实现相对于头指针的实现会更加简单一些,因为插入和删除的操作统一了,不需要区分是否是第一个元素。
我们这里实现的是更为复杂一点的带头指针的情况
带头指针的链表的实现
首先是结构体的定义:
struct Node{
int data; //一个数据域,一个指针域,next指向下一个结点
struct Node* next;
};
然后还可以做一个简化
typedef struct Node* LList; //将其重命名为LList
//之后创建一个新的结构体可以简化为
LList R;
链表的初始化
void init(struct Node **phead){ //链表的初始化
*phead = NULL;
}
链表的遍历
int getLength(struct Node *head){//遍历链表求链表长度
int len = 0;
while(head){
len++;
head = head->next;
}
return len;
}
打印链表
void printList(struct Node *head){//遍历链表,将链表打印出来
while(head){
printf("%d ",head->data);
head = head->next;
}
}
创建一个指针,为后面插入和删除提供方便,少写点代码
struct Node* createNode(int x){ //创建一个指针
struct Node *t;
t = (struct Node*)malloc(sizeof(struct Node)); //强制类型转换
t->next=NULL; //好习惯,不要让指针处于未赋值状态
t->data = x;
return t;
}
插入一个结点,k表示插入的位置,x为插入的值
这里有几个注意的点:
1.需要判断是否为第一个结点,如果是,需要改变头指针
2.在插入时,有两种方法判断k的合法性
(1)判断k的大小,k<1或者k>getlength ,但是这样不是很好,因为getlength会遍历一遍链表,有n的时间消耗
(2)直接找k-1,看看其是否存在,存在即可插入,不存在即插入不了,相比上面,时间消耗会减少一些,更加优秀一点
int insert(struct Node**phead,int k,int x){ //插入结点
//(1)判断k的大小,k<1或者k>getlength ,但是这样不是很好,因为getlength会遍历一遍链表,有n的时间消耗
//(2)直接找k-1,看看其是否存在,存在即可插入,不存在即插入不了
if(k<1) return 0;
else if(k==1){ //需要改变头指针的情况
struct Node *t;
t = createNode(x);
t->next = *phead;
*phead = t;
return 1; //返回1表示成功,返回0表示失败
}
else{ //不需要改变头指针的情况,正常情况
struct Node *p;
int count = 1;
p = *phead;
while(p && count < k-1 ){ //找到要插入的位置
p = p->next;
count++;
}
if(p){ //p不为空即找到,开始插入
struct Node *t;
t = createNode(x);
t->next = p->next; //栓新绳,解旧绳
p->next = t;
return 1;
}
else{
return 0;
}
}
}
删除一个结点,k表示要删除的位置,*px为被删除元素存放的位置
基本思路与插入相同,同样需要判断是否要改变头指针
int removeNode(struct Node**phead,int k,int *px){
if(k<1) return 0;
else if (k==1){ //需要改变头指针的情况
if(*phead){
*px = (*phead)->data;
*phead = (*phead)->next;
return 1;
}
else return 0;
}
else{ //正常的情况
int count = 1;
struct Node*p;
p = *phead;
while(p&&count<k-1){
p = p->next;
count ++;
}
if(p==NULL||p->next==NULL) return 0;
struct Node*t;
t = p->next;
p->next = t->next;
*px = t->data; //用完记得将t释放掉
free(t);
return 1;
}
}
完整代码
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//带有头指针的链表
struct Node{
int data;
struct Node* next;
};
//typedef struct Node* LList; //简化操作为LList R;
void init(struct Node **phead){ //链表的初始化
*phead = NULL;
}
int getLength(struct Node *head){//遍历链表求链表长度
int len = 0;
while(head){
len++;
head = head->next;
}
return len;
}
void printList(struct Node *head){//遍历链表,将链表打印出来
while(head){
printf("%d ",head->data);
head = head->next;
}
}
struct Node* createNode(int x){ //创建一个指针
struct Node *t;
t = (struct Node*)malloc(sizeof(struct Node));
t->next=NULL; //好习惯,不要让指针处于未赋值状态
t->data = x;
return t;
}
int insert(struct Node**phead,int k,int x){ //插入结点
//(1)判断k的大小,k<1或者k>getlength ,但是这样不是很好,因为getlength会遍历一遍链表,有n的时间消耗
//(2)直接找k-1,看看其是否存在,存在即可插入,不存在即插入不了
if(k<1) return 0;
else if(k==1){
struct Node *t;
t = createNode(x);
t->next = *phead;
*phead = t;
return 1;
}
else{
struct Node *p;
int count = 1;
p = *phead;
while(p && count < k-1 ){
p = p->next;
count++;
}
if(p){
struct Node *t;
t = createNode(x);
t->next = p->next;
p->next = t;
return 1;
}
else{
return 0;
}
}
}
int removeNode(struct Node**phead,int k,int *px){
if(k<1) return 0;
else if (k==1){
if(*phead){
*px = (*phead)->data;
*phead = (*phead)->next;
return 1;
}
else return 0;
}
else{
int count = 1;
struct Node*p;
p = *phead;
while(p&&count<k-1){
p = p->next;
count ++;
}
if(p==NULL||p->next==NULL) return 0;
struct Node*t;
t = p->next;
p->next = t->next;
*px = t->data;
free(t);
return 1;
}
}
int main()
{
struct Node * head;
init(&head);
int k = getLength(head);
int x=0;
insert(&head,1,11);
insert(&head,1,22);
insert(&head,2,44);
removeNode(&head,1,&x);
printf("%d\n",x);
printList(head);
return 0;
}