无头结点的单链表的一系列操作
没有头结点的链表,C++实现其初始化,建立,插入,删除,查询,计算长度,输出。感觉没有头结点好多操作都变得复杂了那么一丢丢。
目录
初始化
typedef int datatype;
struct nodes{
datatype data;
nodes *next;
};
struct Lists{
nodes *head;
};
//初始化
Lists init(){
Lists List;
List.head=NULL;
return List;
}
建立
尾插法
//无头结点的尾插法
void CreatListI(Lists *List){
datatype num;
cout<<"输入数据元素,当数据元素为-1时结束"<<endl;
while(cin>>num&&num!=-1){
nodes *temp=(nodes*)malloc(sizeof(nodes));
temp->data=num;
temp->next=NULL;
nodes *tail;
if(List->head==NULL){
List->head=temp;
tail=List->head;
}
else {
tail->next=temp;
tail=temp;
}
}
}
尾插法实现链表的建立,尾插法主要是通过一个指向链表最后一个结点的指针tail,每次插入都插在tail->next,并更新tail,注意插入第一个结点时需要特殊操作一波。
头插法
//无头结点的头插法
void CreatListII(Lists *List){
datatype num;
cout<<"输入数据元素,当数据元素为-1时结束输入"<<endl;
while(cin>>num&&num!=-1){
nodes *node=(nodes*)malloc(sizeof(nodes));
node->data=num;
node->next=List->head;
List->head=node;
}
}
头插法实现链表的建立。头插法是把需要插入的结点插到链表的第一个位置,插入完成后链表的顺序和插入的顺序是相反的。头插法和尾插法,感jio就是“茴”的多种写法。。。
查找
按序号查找
//按序号查找,查找第n位并返回第n位的地址,找不到返回NULL
nodes* SearSeque(Lists *List,int n){
nodes *temp=List->head;
int i=1;
while(temp!=NULL){
if(i==n)return temp;
i++;
temp=temp->next;
}
return NULL;
}
按序号查找,查找第n个位置的元素并返回第n位的地址,找不到该位置就返回NULL。因为没有头结点,所以head指针指向的就是第一个结点,计数器i初始化为1.
按值查找
//按值查找,查找数据元素为x的结点并返回找到的第一个结点的地址,找不到返回NULL
nodes *SearValue(Lists *List,datatype x){
nodes *temp=List->head;
while(temp!=NULL){
if(temp->data==x){
return temp;
}
temp=temp->next;
}
return NULL;
}
按值查找,查找数据元素为x的结点并返回找到的第一个结点的地址,找不到返回NULL。把这个按值查找改进为返回找到的第一个结点前一个元素的地址,就可以运用到按值删除里了。
插入
前插法
//前插法插入,插入到第n位前
void InsertListI(Lists *List,int n,datatype x){
nodes *node=(nodes*)malloc(sizeof(nodes));
node->data=x;
if(n==1){
node->next=List->head;
List->head=node;
}
else{
nodes *temp=SearSeque(List,n-1);
if(temp==NULL){
cout<<"error"<<endl;
}
else{
node->next=temp->next;
temp->next=node;
}
}
}
前插法实现插入,把需要插入的结点插入到第n个位置之前,用到了前面的按序号查找函数。当n=1时,即将结点插入到第一个位置,查找函数返回的是head的地址,head指向的是第一个结点 ,而不是前一个结点的地址,所以要特殊操作一下。
后插法
//后插法插入,插入到第n位后
void InsertListII(Lists *List,int n,datatype x){
nodes *node=(nodes*)malloc(sizeof(nodes));
node->data=x;
if(n==0){
node->next=List->head;
List->head=node;
}
else{
nodes *temp=SearSeque(List,n);
if(temp==NULL){
cout<<"error"<<endl;
}
else{
node->next=temp->next;
temp->next=node;
}
}
}
后插法,将需要插入的结点插入到第n个位置的后面,当n=0时,即结点插入到第一个位置,需要特殊操作一波。
删除
按序号删除
//按序号删除数据元素
void DeleSeque(Lists *List,int n){
nodes *temp=SearSeque(List,n);
if(temp==NULL){
cout<<"error"<<endl;
}
else{
if(n==1){
List->head=temp->next;
free(temp);
}
else {
nodes *pretemp=SearSeque(List,n-1);
pretemp->next=temp->next;
free(temp);
}
}
}
按序号删除。删除操作就是把前一个结点直接指向需要删除结点的下一个结点,释放需要删除结点。删除的结点位置可能非法,也有可能前一个结点的位置非法,我觉得在删除的位置不非法的情况下,前一个结点位置非法的可能只有第一个结点了,所以单独讨论了n=1的情况。记得释放空间啊!
按值删除
//将链表中所有数值为num的结点删除
void DeleValue(Lists *List,datatype num){
if(SearValue(List,num)==NULL){
cout<<"no such data to delete!"<<endl;
}
else{
nodes *pretemp=List->head;
while(pretemp!=NULL&&pretemp->data==num){
List->head=pretemp->next;
free(pretemp);
pretemp=List->head;
}
while(pretemp!=NULL&&pretemp->next!=NULL){
nodes *temp=pretemp->next;
if(temp->data==num){
pretemp->next=temp->next;
free(temp);
}
else {
pretemp=pretemp->next;
}
}
}
}
按值删除,删除了链表里所有值为num的结点。pretemp是当前结点,temp是它的下一个结点,看temp的值是否为num,是则删除,pretemp初始化指向第一个结点,因此可能会漏掉第一个结点,因此进行第一个while,将head更新到第一个不是num的结点处。注意第二个while的条件。。。因为下面用到了pretemp->next,所以pretemp不能是空指针,因为用到了temp->data,所以pretemp->next也不能是空指针。
计算链表长度
//输出链表长度
int length(Lists *List){
nodes *temp=List->head;
int len=0;
while(temp!=NULL){
len++;
temp=temp->next;
}
return len;
}
打印链表
//打印链表
void print(Lists *List){
nodes *temp=List->head;
if(temp==NULL){
cout<<"链式表为空!"<<endl;
}
else {
cout<<"链式表的数据元素为 :"<<endl;
while(temp!=NULL){
cout<<temp->data<<" ";
temp=temp->next;
}
cout<<endl;
}
}