单链表算法(增删改查、头插法、尾插法、单链表逆置、求倒数第k个节点值、找中间节点、删除单链表中最大值节点、分割单链表)、)

单链表简介:

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

代码:

#include <stdio.h>
#include "stdlib.h"

typedef int ElemType;
typedef struct Node
{

    ElemType data;

    struct Node *next;

}Node,*LinkList;

//初始化  
bool InitList(LinkList &L){
	L=(Node *)malloc(sizeof(Node));//分配一个头节点
	if(L==NULL){
		return false;//内存不足 分配失败 
	}
	L->next=NULL;//建立空的单链表 
	return true;
} 

//判断是否为空 
bool isEmpty(LinkList L){
	if(L->next==NULL){
		return true;
	}else{
	    return false;
	}
} 

//头插法建表 逆向建立单链表 
LinkList creatPreList1(LinkList &L){
	//Node * 强调是一个结点  LinkList强调是链表 
	Node *s;
	int x;
	//创建头节点
	L=(Node *)malloc(sizeof(Node));
	//初始为空链表
	L->next=NULL;
	//输入结点的值
	scanf("%d",&x);
	while(x!=0){
		s=(Node *)malloc(sizeof(Node));//创建新结点
		s->data= x;
		s->next=L->next; //指针域赋值
		L->next=s;
		scanf("%d",&x); 
	}
	return L;
}

//头插
LinkList creatPreList2(LinkList &L,int n){
	//Node * 强调是一个结点  LinkList强调是链表 
	Node *s;
	int x;
	//创建头节点
	L=(Node *)malloc(sizeof(Node));
	//初始为空链表
	L->next=NULL;
	for(int i=0;i<n;i++){
	    s=(Node *)malloc(sizeof(Node));//创建新结点
	    scanf("%d",&x);
		s->data= x;
		s->next=L->next; //指针域赋值
		L->next=s;
	}
	return L;
}
 

//通过尾插法建立单链表
LinkList creatNextList1(LinkList &L){
	//Node * 强调是一个结点  LinkList强调是链表 
	int x;
	//创建头节点
	L=(Node *)malloc(sizeof(Node));
	Node *s,*r=L;//r指针始终指向当前链表的表尾 
	//输入结点的值
	scanf("%d",&x);
	while(x!=0){
		s=(Node *)malloc(sizeof(Node));//创建新结点
		s->data= x;
		r->next=s;
		r=s;   //r指向新的表尾 
		scanf("%d",&x);
	}
	r->next=NULL;//尾结点指针置空 
	return L;
}

//指定结点的后插操作 
bool creatNextList2(Node *p,int e){
	if(p==NULL){
		return false;
	}
	
	Node *s=(Node *)malloc(sizeof(Node));
	if(s==NULL){
		return false;//内存分配失败 
	}
	s->data=e;//用结点s保存数据e
	s->next=p->next;
	p->next=s;
	return true; 
}

//指定结点的前插操作  插入某个元素 e  
//偷天换日的操作 
bool creatPreList3(Node *p,int e){
	if(p==NULL){
		return false;
	}
	
	Node *s=(Node *)malloc(sizeof(Node));
	if(s==NULL){
		return false;//内存分配失败 
	}
	s->next=p->next;
	p->next=s;//新结点s连接到p之后
	s->data=p->data;//将p中元素赋值到s中去 
	p->data=e;
	return true; 
}

//删除第i个位置的元素,并用e返回删除的元素的值 
//头结点可以看作是第0个结点 但不保存数据
bool deleList(LinkList &L,int i,int &e){
	//1 先查找到 i-1  位置 
	if(i<1){
		return false;  
	} 
	Node *p;//指针p指向当前扫描到的结点  p指向头节点 
	int j=0;//当前p指向的第几个结点
	p=L;//L指向头节点,头节点是第0个结点  不存放数据
	
	while(p!=NULL&&j<i-1){
		//查找到第i-1个结点 p指向该结点 
		p=p->next;
		j++;
	}//如果插入的序号是length+1的话  p就指向null了  退出循环 
	
	if(p==NULL){
		//当前p位置为空 表示已找完还没有数到第i个  说明i的插入位置不合理 i>n+1 
		return false;
	}
	if(p->next==NULL){
		//第i-1个位置后 无其他结点 
		return false;
	}
	
	Node *q=p->next; //令q指向被删除元素
	e=q->data;//用e返回元素的值
	p->next=q->next;
	free(q);
	return true; 
} 


//在第i个位置插入e元素 
bool insertList(LinkList &L,int i,int e){
	//先找到第i-1个位置
	if(i<1){
		return false;
	}
	Node *p;//指针p指向当前扫描到的结点 
    int j=0;//当前p指向的第几个结点 
	p=L;//L指向头节点 
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	} 
	
	
	if(p==NULL){
		//i值不合法 
		return false;
	}
	//插入 
	Node *s=(Node *)malloc(sizeof(Node));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true; 
}

//通过位序查找结点
 Node* getElem(LinkList L,int i){
 	//LinkList L,int i 也可以写为 Node *L,int i
 	if(i<0){
	 	return NULL;
	 } 
 	Node *p;//指针p指向当前扫描到的结点 
 	int j=0;//当前p指向的第几个结点 
	p=L;//L指向头节点,头节点是第0个结点(不存放数据)  
 	while(p!=NULL&&j<i){
 		//循环找到第i个结点 
	 	p=p->next;
	 	j++;
	 }
	 return p;
 }
 
 
//封装 插入
bool insert(LinkList &L,int i,int e){
	//先找到第i-1个结点
	Node *q=getElem(L,i-1);
	//q结点后插元素e 
	return creatNextList2(q,e);
} 

//直接在末尾添加元素 
void addTail(LinkList &L,int x){
	//先找到末尾
	 Node *n=L;
	 while(n->next!=NULL){
 		n=n->next;
 	}
 	
 	//创建新结点s 保存元素x 
	Node *s=(Node *)malloc(sizeof(Node));
	s->data=x;
	//并指向新结点s 
	s->next=NULL;
	n->next=s;	
} 
 
 //按值查找
 Node * locateElem(LinkList L,int e){
 	Node *p=L->next;
 	//从第一个结点开始查找 
 	while(p!=NULL&&p->data!=e){
	 	p=p->next;
	 } 
	 return p;//找到后返回该结点指针,否则返回null 
 } 
    
    
//单链表的长度 
int length(LinkList L){ 
	//带头节点的单链表的长度 画图理解哈 
	Node *p;
	p=L->next; 
	int len=0; 
	while(p!=NULL){
		p=p->next;
		len++;
	}
	return len;
} 

//遍历链表
void show(LinkList L){
	
	Node *p;
	p=L->next;
	while(p!=NULL){
		printf("%d",p->data);
		p=p->next;
		printf(" "); 
	}
} 


//清空单链表 
void free(LinkList &L) {
	LinkList p;
	while (L->next) {
		p = L->next;
		L->next = p->next;
		delete(p);
	}
}


//删除链表中最大的值(只有一个最大值) 
void deleMaxNode(LinkList &L){
	Node *pre=L,*p=L->next;
	Node *maxPre,*maxP; 
	maxP=p,maxPre=pre;
	while(p!=NULL){
		if(maxP->data<p->data){//若找到个更大的一个结点 
			maxP=p;   //更改maxP 
			maxPre=pre;   //更改 maxPre	 
		}
		pre=p;
		p=p->next;//pre p同步后移一个结点 	 
	}
	//查找出最大值结点的前驱节点 *maxPre 
	
	maxPre->next=maxP->next;//删除maxP结点 
	delete(maxP);
} 

//将单链表反转 利用头插法  画图理解 
void Reverse(LinkList L){
	Node *p,*q;
	//将L拆分为两个部分 
	p=L->next;
	L->next=NULL;
	while(p!=NULL){
		//向后移动位置 
		q=p;
		p=p->next;
		//头插 
		q->next=L->next;
		L->next=q; 
	}	
}
 
//在一个带有头节点的单链表L(至少有一个结点),使元素递增排列
//一定要画图理解  O(n*n) 
void Sort(LinkList L){
	Node *p,*pre,*q;	
	p=L->next->next; //p指针指向第二个结点 头节点是第0个结点
	L->next->next=NULL;//构造只有一个数据结点的有序表 将L拆分为两个部分 
	while(p!=NULL){
		q=p->next;//q指向p指针的后继结点
		//在有序单链表中查找插入结点的前驱结点 *pre
		pre=L;//从有序表开头开始比较,pre指向插入*p的前驱结点
		while(pre->next!=NULL&&pre->next->data<p->data){
			pre=pre->next;//在有序表中找插入*p的前驱结点*pre
		} 
		//查找完之后 在*pre之后插入*p
	    p->next=pre->next;
	    pre->next=p;
	    p=q;//扫描单链表余下的结点 
	}	 
} 


//有一个带头节点的单链表L,设计算法将其拆分为两个带头节点的单链表L1 L2
//要求L1的头节点是L的头节点
void split(LinkList &L,LinkList &L1,LinkList &L2){
	Node *q,*r1,*p=L->next;//p指向第一个数据结点
	L1=L;//L1利用原来链表的头节点
	r1=L1;//r1始终指向L1的尾结点
	
	L2=(Node *)malloc(sizeof(Node));//建立L2的头节点
	L2->next=NULL;
	
	while(p!=NULL){
		//利用尾插法将p插入L1中
		r1->next=p;
		r1=p;
		
		p=p->next;//p移向下一个结点 
		q=p->next;//q保存*p的后继结点
		//利用头插法将*p插入L2中去
		 p->next=L2->next;
		 L2->next=p;
		 p=q;//p重新指向下一个结点 
	}
	r1->next=NULL;//L1的尾结点置为空 
}

//快慢指针问题 求单链表倒数第k个结点
Node* findK(LinkList L,int k){
	Node *fast=L->next;
	Node *slow=L->next;
	//先让快指针先走k步 
	for(int i=1;i<k;i++){
		fast=fast->next;
	}
	//然后同时让两个指针同时走 
	while(fast->next){
		fast=fast->next;
		slow=slow->next;
	}
	return slow;	
}  

//单链表找中间结点 
Node* findMid(LinkList L){
	Node *p=L->next;
	Node *q=L->next;
	while(q!=NULL&&q->next!=NULL){
		p=p->next;
		q=q->next->next;
	} 
	return p;
} 


 
int menu() 
{
	printf("\n\n\n**************************************菜单**********************************************\n");
	printf("**********************************************************************************\n");
	printf("**********1、	初始化,	                         	 2、判空\n");
	printf("**********3、	前插建表	                         4、后插建表\n");
	printf("**********5、   在第i个位置插入结点		             6、删除第i个位置的元素\n");
	printf("**********7、   删除单链表中最大值的结点(唯一):");
	printf("**********8、   查找第i个位置                        9、按值查找\n");
	printf("**********10、  表长                                 11、遍历\n");
	printf("**********12、  清空链表                             13、将单链表逆置 \n");
	printf("**********14、  让单链表升序排列                     15、将单链表分割为L1 L2:\n");   
	printf("**********16、  找到单链表倒数第k个结点值            17、找到单链表中间结点值      \n");
	printf("**********18、  在单链表末尾添加结点                 19、退出      \n");
	return 0;
}


int main()
{
	LinkList L,L1,L2;
	int i,e;  
	int option;
	menu();
	do
	{
		printf("\n输入选项:\n");
		scanf("%d", &option);
		switch (option)
		{
		case 1:
			InitList(L);
			printf("初始化成功\n");
			break;
		case 2:
			if (isEmpty(L))								  
				printf("线性表为空\n");
			else
				printf("线性表不为空\n");
			break; 
		case 3:
	        printf("输入元素进行前插建表:\n");
	        creatPreList1(L);
	        //creatPreList2(L,4);
	        printf("前插建表成功,链表为:\n");
	        show(L);
			break;
		case 4:
		    printf("输入元素进行后插建表:\n");
	        creatNextList1(L); 
	        printf("后插建表成功,链表为:\n");
	        show(L);
			break;
		case 5:
			printf("在第i个位置插入e:\n");
			scanf("%d%d",&i,&e);
			insert(L,i,e);
			break;
		case 6:
			printf("输入要删除的结点i\n");
			scanf("%d", &i);
			deleList(L,i,e);
			printf("删除第%d个结点后,此链表为:",i);
			show(L); 
			break;
			
		case 7:
			deleMaxNode(L);
			printf("删除第最大值结点后,此链表为:");
			show(L); 
			break;
		case 8:
			printf("请输入查找第i个元素:\n");
			scanf("%d", &i);
		    Node *num = getElem(L,i);
		    if(num==NULL){
    			printf("输入位置不合法\n");
    		}else{
		    	printf("查找的第 %d 个元素是:%d\n",i,num->data);
		    }
			break;
		case 9:
			printf("按值查找:\n");
			scanf("%d",&e);
            locateElem(L,e)==NULL?printf("没有找到该元素:\n"):printf("该元素:%d\n",locateElem(L,e)->data);
			break;
			
		case 10:
			printf("表长为%d:\n",  length(L));
		    break;
        case 11:
			printf("遍历单链表为:\n");
			show(L); 
		    break;
        case 12:
			printf("清空单链表:\n");
			free(L); 
		    break;
	    case 13:
	        Reverse(L);
			printf("将单链表逆置(利用头插法)之后的链表为:\n");
			show(L);
		    break;
        case 14:
	        Sort(L); 
			printf("升序之后的单链表:\n");
			show(L);
		    break;
        case 15:
	        split(L,L1,L2); 
			printf("链表拆分为L1和L2----L1为:");
			show(L1);
			printf("\n"); 
			printf("链表拆分为L1和L2----L2为:");
			show(L2); 
		    break;
        case 16:
            printf("求单链表倒数第k个结点的值:");
            int k;
			scanf("%d", &k);
			printf("单链表倒数第k个结点值为%d:\n",findK(L,k)->data);
		    break;    
     	case 17:
			printf("单链表中间结点值为%d:\n",findMid(L)->data);
		    break; 
        case 18:
			printf("在单链表末尾添加结点:");
			int x;
			scanf("%d",&x);
			addTail(L,x);
		    printf("添加结点后的单链表为:");
		    show(L);
		    break;
		case 19: break;
		default:
			printf("输入选项不合法\n");
			break;
		}
	} while (option != 19);                                    //option==19退出
	return 0;
}


代码运行图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


奥里给:

小编希望 能帮助到大家 我们一起学习进步 有问题可以指出来 欢迎评论区讨论
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值