数据结构——单链表的构造、插入、删除、修改、查询

单链表是线性结构的,访问单链表需要从头开始访问,即便是双链表也不可能做到二分查找。

可以实现二分查找的是顺序表,请这位网友确认一下自己的文章的正确性!

https://www.jianshu.com/p/874be30537d7

 

本文如有错误欢迎指正!

目录

单链表

带头节点的单链表

不带头节点的单链表


单链表:单向列表,只能按从头到尾的顺序进行访问

分类: 带头节点的单链表  不带头节点的单链表

一般操作:

带头节点的单链表

一般情况下,我个人比较喜欢使用头节点统计链表长度。没什么需要特殊注意的地方。

【注】代码均在devc++编译器上跑过,可以运行,如果使用vc++这个编译器,需要注意 i 可能重复定义!

【注】因为删除、修改都需要用到查询这个操作,所以不再写查询函数。 

还有,这都什么年代了,还在用 vc++ 6.0 ? 这是我用过最麻烦的一个编译器。

#include
#include
/**
这是一个带头节点的链表,Head作为头节点,Head->value 用于统计链表长度
这是最常见也是最方便的方法,如果不是用头节点统计量表长度,就需要遍历链表来统计链表长度 
*/
typedef struct LNode{
	int value;
	struct LNode *next;
}Lnode,*Linklist;
// *Lnode 等价于 Linklist 

Linklist initList(){
	//初始化链表,给链表开辟一个头节点的空间 
	Lnode *Head = (Linklist)malloc(sizeof(Lnode)); 
	Head->next = NULL;
	Head->value = 0;
	return Head;
} 

//顺序插入 一组数据 
/**
顺序插入可以理解为 给p节点赋值,将 q节点连接到 p节点上,q 节点只是为了给p节点开辟新空间 
*/ 
Linklist Insert_into_List_at_before(Linklist &Head){
	//Inserting elements into List at the before 
	//开辟一个p节点,并将Head与之相连 
	Lnode *p = (Linklist)malloc(sizeof(Lnode)),*q; 
	p->next = NULL;
	Head->next = p; 
	int x;
	while(x != 10){
		// End with 10 as input flag, and 10 is the last element of inputing;
		scanf("%d",&x);
		//开辟 一个 q节点空间 
		q = (Linklist)malloc(sizeof(Lnode));
		//q 空间指向空表示链表结束 
		q->next = NULL;
		// 将数据存入 p 节点 
		p->value = x;
		//将 q 节点与 p 节点连接 
		p->next = q;
		// p 节点后移到了原来 q节点的位置 
		p = p->next;
		// 链表长度 +1 
		Head->value ++;
	} 
	return Head;
}


//逆序插入 一组数据 
/**
逆序插入可以理解为给q 节点赋值,p节点只是起一个连接作用 
*/ 
Linklist Insert_into_List_at_fore(Linklist &Head){
	//Inserting elements into List at fore
	//开辟  p 空间 
	Lnode *p = (Linklist)malloc(sizeof(Lnode)),*q;
	p->next = NULL;
	Head->next = p;
	int x;
	while(x != 10){
		scanf("%d",&x);
		//开辟 q 空间 
		q = (Linklist)malloc(sizeof(Lnode));
		//给 q 节点赋值 
		q->value = x;
		//将 q 连接到 p 上 
		q->next= p->next;
		//p 后移,到了原来 q 上 
		p->next = q;
		printf("%d ",p->next->value);
		//链表长度+1 
		Head->value ++;
	}
	//这一步是将 p节点删除,删除的原因是 p节点上没有存储数据,p->value是一个不确定的数,不是我们要存储的数据  
	Head ->next = p->next;
	return Head;
}

//前插法输出 
void display(Linklist Head){
	// Traversing the list in order
	Linklist p;
	p = Head->next;
	int i = 0;
	// 不断后移直到链表尾 
	while(p->next!=NULL){
		printf("Locate at %d value = %d\n",i,p->value);
		p = p->next;
		i ++;
	}
}

//尾插法插入一个数据 
/**
待插节点为 r 
尾插法需要找到插入的位置 locate,p 指向 locate的前一个节点,q 指向 locate节点
 r->next = q;
 p->next = r;
 这样子就可以了。
 但是考虑到用户输入的位置可能超过链表长度,所以如果超过链表长度,就直接将 r 节点插入到最后 
 
 [注]: 必须先 r->next= q 后 p->next= r,这是因为顺序反了会造成 q节点后面的部分丢失。
 前插法一样的道理。 
*/ 
Linklist Insert_into_inner_list_as_before(Linklist &Head){
	int x,locate;
	printf("input locate and number\n");
	scanf("%d%d",&locate,&x);
	Lnode *p,*q,*r;
	p = Head;
	r = (Linklist)malloc(sizeof(Lnode));
	r->value = x;
	int i = 0;
	//判断 locate是否超过链表长度 [注]链表长度从 0 开始计数 
	if(locate < (Head->value-1)){	//长度未超过直接插入 
		while(i < locate-1){
			p= p->next;
		}
		q = p->next;
		r->next = q;
		p->next = r;
	}
	else {							//超过链表长度插入到链表尾部 
		while(p->next !=NULL){
			p= p->next;
		}
		r->next = NULL;
		p->next = r;
	}
	Head->value ++;
	return Head;
}


//前插法插入一个数据 
/**
待插节点为 r
前插法需要找到待插位置 locate ,p 指向locate 前一个节点, q 指向locate节点
r->next = q;
p->next = r;
这样子就可以了 。 
考虑用户输入的locate 可能超过链表长度,这时候直接插到尾部
*/ 
Linklist Insert_into_inner_list_as_fore(Linklist &Head){
	int x,locate;
	printf("input locate and number\n");
	scanf("%d%d",&locate,&x);
	Lnode *p,*q,*r;
	p = Head;
	r = (Linklist)malloc(sizeof(Lnode));
	r->value = x;
	int i = 0;
	while(i < locate){
		p= p->next;
		//printf("%d ",p->value);
		i ++;
	}
	r->next = p->next;
	p->next = r;
	Head->value ++;
	return Head;
} 

//按位置删除 一个数据 
/**
按位置和按值删除都是先要找到 locate ,p指向 locate 前一个节点,p指向locate
删除节点就是让 p->next = q->next ,这样就直接越过了locate节点。  
*/ 
Linklist Delete_by_locate(Linklist &Head){
	printf("Head->value =%d\n",Head->value);
	int locate;
	printf("input locate \n");
	scanf("%d",&locate);
	Lnode *p,*q,*r;
	p = Head;
	int i = 0 ;
	if(locate <= 0){	 
		Head->next = Head->next->next;
		printf("delete %d",Head->next->value);
		Head->value --;
		return Head;
	}
	if(locate >= Head->value){
		while(p->next->next != NULL){
			p= p->next;
		}
		printf("delete %d",p->next->value);
		p->next = NULL;
		Head->value --;
		return Head;
	}
	while(inext;
		i++;
	}
	printf("delete %d",p->next->value);
	p->next = p->next->next;
	Head->value --;
	return Head;
	
}

//尾插法输出 
void display_fore(Linklist Head){
	printf("\n-------------------------Here display list in order-------------------------\n");
	int i = 0;
	Linklist p;
	p = Head->next;
	printf("Head->value = %d\n",Head->value);
	while(p!=NULL){
		printf("Local %d value = %d\n",i,p->value);
		p = p->next;
		i ++;
	}
	printf("\n-------------------------Display completed  -------------------------\n\n");
}

//计算链表长度 
/**
如果头节点不计数,就需要这个函数遍历整个链表求链表长度 
*/ 
int get_Element_num(Linklist Head){
	// if head node didn't count the amount, this method Traversing the list in order and count the amount
	Linklist p;
	p = Head->next;
	int i = 0;
	while(p->next!=NULL){
		
		p = p->next;
		i ++;
	}
	printf("length of list = %d\n",i);
	return i;
}

//按数值删除数据
Linklist Delete_by_number(Linklist &Head){
	printf("\n-------------------------Here drop node by numbers-------------------------\n");
	int number;
	printf("input number : ");
	scanf("%d",&number);
	Lnode *p,*q;
	q = Head;
	p = Head->next;
	int i=0,count = Head->value ;
	//q 节点永远在 p节点前面 所以使用 q节点 
	while(q->next != NULL){	
		//将所有等于 x 的节点都删除
		//p 循环是为了删除连续等于 x 的节点 
		while(p->value == number ){  
		//如果 p 是最后一个节点仍然等与 x,那么q作为链表最后一个节点; 
			if(p->next == NULL){	 
				q->next = NULL;
				break;
			}
			else{
				printf("value = %d\n",p->value);	
				q->next = p->next;
				printf("q->next->value = %d\n",q->next->value);
				p = p ->next;
				
			}
			Head->value --;
		}
		//如果不加这个判断程序会崩溃,别问我为什么,我花了一个半小时都没搞懂
		//大概是因为q->next已经指向空,那么循环就会继续下去,不会因为q->next == NULL 退出循环 
		if(q->next == NULL) {
			printf("I arrived at NULL\n");
			 break;
		}
		else{
			q = q->next;
			p = p->next;
		}
	}
	printf("Delete sucessfully \n");
	printf("\n-------------------------Delete sucessfully    -------------------------\n");
	return Head;
} 

Linklist update_by_num(Linklist &Head){
	printf("\n-------------------------Here update x by y-------------------------\n");
	Linklist p,q;
	int x;int y;
	printf("input y to replace x: \n");
	scanf("%d",&x);
	scanf("%d",&y);
	q = Head->next;
	while(q->next != NULL){
		if(q->value == x){
			q->value = y;
		}
		q = q->next;
	}
	if(q->value == x) q->value = y;
	printf("Update sucessfully \n");
	printf("\n-------------------------Update sucessfully    -------------------------\n");
	return Head; 
} 

Linklist update_by_locate(Linklist &Head){
	printf("\n-------------------------Here replace node locate at x whith number y-------------------------\n");
	int x,y;
	scanf("%d",&x);
	scanf("%d",&y);
	int i = 0 ; 
	Linklist q = Head->next;
	if(x > Head->value-1) 
	printf("Overflowed ! \nCheck y so that guarantee not to exceed the list length!");
	while(q->next != NULL){
		printf("%d ",q->value);
		if(i == x){
			q->value = y;
			break;
		}
		i ++;
		q = q->next;
	}
	if(i == x){
		q->value = y;
	}
	printf("\n-------------------------Update sucessfully    -------------------------\n");
	return Head;
}
int main(){
	Linklist head = initList();
}

不带头节点的单链表

不带头节点可能比带头单链表稍微麻烦一点,问题也不大。

[注]不带头除了从第一个节点开始存储数据外,其他和带头节点差不多。

[注]构造函数不同,无头单链表构造时,不需要开辟空间,L= NULL即可,而带头单链表构造时,需要给L开辟空间,

并且使L->next = NULL

#include
#include

 typedef struct LNode{
	int value;
	struct LNode *next;
}Lnode,*Linklist;

// 构造第一个节点,指向空 
Linklist initList(Linklist &L){	 
	L = NULL;
	return L;
}
//无头单链表和带头 单链表的最大区别就是无头单链表从第一个节点开始存储数据 ,
//而带头单链表是 从第二个节点开始存储数据 ,其他都一样 
Linklist insert(Linklist &L){
	L = (Linklist)malloc(sizeof(Lnode));
	L->next = NULL;
	int x;
	printf("input\n");
	//第一个节点存储数据 
	scanf("%d",&x);
	L->value = x;
	Linklist p = L;
	while(x!=10){
		printf("input\n");
		scanf("%d",&x);
		Linklist q =(Linklist)malloc(sizeof(Lnode));
		q->value = x;
		q->next = NULL;
		p->next = q;
		p = p->next;
	}
	return L;
}

//注意从第一个节点开始存储 数据 
//尾插法
Linklist insert_single(Linklist &L){
	int x;
	scanf("%d",&x);
	//插入第一个数据时,L节点没有分配内存,需要分配内存,然后直接够L赋值,next指向空 
	if(L == NULL){
		L = (Linklist)malloc(sizeof(Lnode));
		L->value = x;
		L->next = NULL;
		return L;
	}
	//往后插入和带头链表的操作一样 
	else{
		Linklist p = L,q;
		while(p->next != NULL){
			printf("move right p->value = %d\n",p->value);
			p = p->next;
		}
		q = (Linklist)malloc(sizeof(Lnode));
		q->value = x;
		q->next = NULL;
		p->next= q;
	}
	return L;
} 

//前插法
Linklist insert_single_fore(Linklist &L){
	int x;
	scanf("%d",&x);
	//插入第一个数据时,L节点没有分配内存,需要分配内存,然后直接够L赋值,next指向空 
	if(L == NULL){
		L = (Linklist)malloc(sizeof(Lnode));
		L->value = x;
		L->next = NULL;
		return L;
	}
	//往后插入只需要开辟一个p空间,存放数据,next指向 L,在让L等于p就可以了 
	else{
		Linklist p = (Linklist)malloc(sizeof(Lnode)),q = L;
		p->next= q;
		p->value = x;
		L = p;
		return L;
	}
}
void display(Linklist L){
	printf("arrived\n");
	Linklist p = L;
	while(p->next != NULL){
		printf("%d ",p->value);
		p = p->next;
	}
	printf("%d ",p->value);
}
int main(){
	Linklist L;
	initList(L);
	//insert(L);
	//display(L);
	while(1){
		insert_single_fore(L);
		display(L);
	}
	
}

 

你可能感兴趣的:(C++数据结构)