线性表的链式表示(单链表)

线性表的链式表示(单链表)

这是在学习数据结构时为方便自己学习个人总结的代码,水平有限,大佬勿喷,有问题欢迎一起讨论.
有很多优化的地方没有写,只有记录一下,总体感觉有点乱,有空在总结下吧!

/* Note:Your choice is C IDE */
#include "stdio.h"
#include "stdlib.h"
/*=======单链表的优缺点================
	优点:不要求使用大片的连续空间,改变容量方便
	缺点:不支持随机存取,存储密度低,查找结点效率低
======================================*/

/*=======带头结点V.S不带头结点======================
	头节点和头指针:不管是否带有头结点,头指针都始终指向链表
		的第一个结点
	两个优点:由于第一个数据结点的位置比存放在头结点的指针域
	封装基本操作:避免重复代码,简洁,容易维护
==================================================*/
/*
	头插法:
	尾插法:在插入的过程中建立一个结点指向尾结点,
		 每次新添加元素将该元素指向s-data=x;r->next=s;r=s;
		 可以将时间复杂度变为O(n)
	不带头指针的插入操作比较繁琐,尤其是尾插法,
	每次需要遍历链表找到尾节点,然后执行插入操作	
*/

#define false 0
#define true 1
#define Maxsize 50
typedef char Elemtype;
int menu();
/*创建一个链表结点类型的结构体*/
typedef struct node{
	//定义每个结点中的数据
	Elemtype data;
	//定义一个指向下一个结点的指针
	struct node *next;
//Lnode *L == linklist L 为方便理解  
}Lnode,*linklist;
//链表一般用linklist定义
//单个结点一般用Lnode

//带头结点单链表的初始化
linklist Initlist(){
	Lnode *L;
	L = (Lnode *)malloc(sizeof(Lnode));
	if(L == NULL){
		printf("内存申请失败\n");
		return false; 	
	}
	//优化可以利用这个结点的数据域存放链表长度
	L->next = NULL; //头节点之后暂时没有结点
	printf("初始化成功\n");
	return L;
}
//判空函数
int Empty(linklist L){
	if(L->next == NULL)
		return true;
	else 
		return false;
}
/*不带头结点插入在1位置应该特殊处理*/
/*
if(i==1){插入在第一个结点与其他操作不同 
	Lnode *s = (Lnode *)malloc(sizeof(Lnode));
	if(s == NULL){
		printf("内存申请失败\n");
		return false; 	
	}
	s->data = e;
	s->next = L;
	L = s;
	return true;
}*/
//按位序插入(带头结点)在表中的第i个位置插入元素e
int ListInsert(linklist L,int i,Elemtype e){
	//插入位置不合法 
	Lnode *p=L;
	Lnode *q;
	int j=0;
	if(i<1){
		return false;
	}
	//这个是i-1是找插入结点的前驱,在GetElem()中是查找该位置
	while(p != NULL && j<i-1){
		p = p->next;
		j++;	
	}
	if(p == NULL){
		printf("没有找到该位置\n");
		return false;	
	}
	q = (Lnode *)malloc(sizeof(Lnode));
	q->data = e;
	//下面这两句顺序不能相反
	q->next = p->next;
	p->next = q;
	
	printf("插入成功\n");
	return true;
}
//后插操作:在结点P之后进行插入元素e
int InsertNextNode(Lnode *p,Elemtype e){
	//首先进行插入位置判断
	Lnode *s;
	if(p==NULL){
		return false;	
	}
	s = (Lnode *)malloc(sizeof(Lnode));
	if(s == NULL)
		return false;
	s->data = e;
	s->next = p->next;
	p->next = s;  //将s结点链接在p之后
	return true;
}
/*
	在p结点之前进行插入:
	两种思路:1、传入单链表的头指针,
			然后进行遍历找到要p结点前一个结点q,
			在q结点进行后插入操作
			2、1·直接进行新建结点s,
			   2·将p的指针域 赋给 s,
			   3·将p的指针域 指向 s,
			   4·之后将将p结点的数据赋给s的data域
			   5·要插入的数据付给p结点的data域
*/
//这是2的代码实现(时间复杂的为O(1))
int InsertPeriorNode(Lnode *p,Elemtype e){
	Lnode *s;
	if(p==NULL){		
		printf("传入的结点p为空\n");
		return false;
	}
	s  = (Lnode *)malloc(sizeof(Lnode));
	if(s == NULL){
		printf("内存申请失败\n");
		return false;	
	}
	//注意以下顺序千万不能弄错
	s->next = p->next;
	p->next = s;
	s->data = p->data;
	p->data = e;
	printf("前插成功"); 
	return true;
}
//指定结点的删除 1、遍历找到p结点的前一个,然后进行删除操作 
//2、结点s为p的后继,将s的data赋给p,然后将s的next域给p的next域,将s释放
int DeleteNode(Lnode *p){
	Lnode *s;
	if(p==NULL){
		printf("结点为空,删除失败\n");
		return false;
	}
	s = p->next;
	//这一段代码有bug 如过p为最后一个结点
	//则s=NULL为空,没有data域,这种情况只能用方法1
	p->data = s->data;
	p->next = s->next;
	free(s); //结点释放
	return true; 
}
//这个这个函数完全满足i=0以及i大于链表长度 
//即通过返回值是不是NULL即可判断是否查找成功
Lnode * GetElem(linklist L,int i){
	Lnode *p=L;
	int j=0;
	if(i<0){
		return NULL;	
	}
	while(p!=NULL && j<i){
		p = p->next;
		j++;
	}
	return p;
}
//按值查找 找到数据域==e的结点
Lnode *LocateElem(linklist L,Elemtype e){
	Lnode *p = L->next;
	//从第一个结点开始查找数据域为e的结点
	while(p != NULL && p->data == e){
		p = p->next;
	}
	//如果是比较两个struct类型是否相等
	return p;
}


//不带头结点的初始化
int Nhead_Initlist(linklist L){
	L = NULL; //空表,暂时没有任何结点	
	return true;
}
//不带头结点的判空函数
int Nhead_Emptylist(linklist L){
	if(L == NULL)
		return true;
	else 
		return false;
}  
void AddHead(Elemtype d,linklist L)
{
	Lnode *p = (Lnode *)malloc(sizeof(Lnode));
	p->data = d;
	p->next = L; //新节点的后继
	L = p; //首结点
}
linklist NHeadInsert(linklist L){
	int x;
	Lnode *s;
	L=(linklist)malloc(sizeof(Lnode));
	scanf("%d",&x);
	L->data=x;
	L->next=NULL;
	while(x!=9999){
		s=(Lnode*)malloc(sizeof(Lnode));
		scanf("%d",&x);
		s->data=x;
		s->next=L;
		L=s;
	} 
	return L;
} 
linklist NoTailInsert(linklist L){
	Elemtype x;Lnode *s,*r=L;
	L=(linklist)malloc(sizeof(Lnode));
	scanf("%c",&x);
	L->data=x;
	L->next=NULL;
	while(x!=9999){
		s=(Lnode*)malloc(sizeof(Lnode));
		scanf("%c",&x);
		s->data=x;
		r->next=s;
		r=s;
	}
	r->next=NULL;
	return L;
} 
//链表逆置
void converse(linklist L)
{
	Lnode *p,*q;
	//p为头结点
	p=L->next;
	//将链表指控(相当于切断)
    L->next=NULL;
	//再往链表L中进行头插逆置
	while(p)
	{
		//此时的p是头结点,赋给q
		q=p;
		//将p指向下一个结点
		p=p->next;
		/*头插法--一直往L结点之后那个位置插入*/
		q->next=L->next;
		//将q结点插入L之后
		L->next=q;
	}
}

linklist L;
void main()
{
	int acount;int i;
	Elemtype str;Lnode *q;
	printf("***********************************\n");
	printf("     欢迎使用单链表操作步骤\n");
	printf("***********************************\n");
	while(1)
	{
		switch(menu())
		{
		case 1:
			  L = Initlist();break;
		case 2:
			 
			  printf("请输入插入元素的个数:");
			  scanf("%d",&acount);
			  printf("请输入要插入的值:");
			  fflush(stdin);
			  for(i=0;i<acount;i++){
			  	scanf("%c",&str);
			  	//这个时间复杂度为O(n2)
			  	ListInsert(L,i+1,str);
			  }
			  for(i=0,q=L->next;q != NULL;i++,q=q->next)
			  	printf("%c",q->data);
			  printf("\n");
			  break;
		case 3:
			printf("请输入要删除的位置:");
			scanf("%d",&acount);
			//调用查找函数,找到位置结点
			q = GetElem(L,acount);
			if(q == NULL){
				printf("位置结点不存在或为空\n");
				break;	
			}
				
			//调用删除函数,删除结点
			if(DeleteNode(q))
				printf("删除成功\n");
			else
				printf("删除失败\n");
			break;
		case 4:
			//等待优化
			printf("4");break;
		case 5:
			//等待优化
			printf("5");break;
		case 6:
		//链表逆置
			converse(L);break;
		case 7:
			for(i=0,q=L->next;q != NULL;i++,q=q->next)
			  	printf("%c",q->data);
			  printf("\n");
			  break;
		case 8:
			printf("感谢使用,再见!\n");
			exit(0);
			
		}
	}
}

int menu()
{
	char str[5];
	int  select;
	printf("\n\n请选择数字进行操作\n");
	printf("1.单链表初始化\n");
	printf("2.单链表尾插法插入\n");
	printf("3.单链表按位删除\n");
	printf("4.单链表结点删除\n");
	printf("5.单链表按值查找\n");
	printf("6.单链表逆置\n");
	printf("7.单链表遍历\n");
	printf("8.退出\n");
	printf("请选择对应数字1--8:");
	while(1)
	{
		//清空缓存
		fflush(stdin);
		gets(str);
		//字符转
		select=atoi(str);
		if(select<1||select>8)
			printf("输入错误,请重新输入:");
		else
			break;
		
	}
	return select;
	
}

2021-5-17数据结构有点难(而已😎)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值