数据结构单链表(顺序表,单向链表,静态链表,循环链表,双向链表,及部分链表的代码实现)

    **单链表的抽象数据类型**
ADT 线性表(list)
	Date
	  线性表数据对象集合{a1,a2,......an},每个元素的类型为DateType.
	  数据元素除了第一个没直接前驱最后一个没有直接后继,其余的都有
	  直接前驱和后继,数据元素之间一一对应.
	Operation
	   InitList(*L);//初始化操作,建立一个空的线性表L
	   ListEmpty(L);//若线性表为空返回true,否则返回false
	   ClearList(*L);//将线性表清空
	   GetElem(L,i,*e);//将线性表L中的第i个元素返回给e
	   LocateElem(L,e);//在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功否则返回表示失败
	   ListInsert(*L,i,e);//在线性表的L中的第I个位置插入新元素e
	   ListDelete(*L,i,*e);//删除线性表L中的i位置的元素,并用e返回其值
	   ListLenght(L);//返回线性表的元素个数 
endADT 

1.线性表的顺序存储结构
(1)线性表的顺序存储结构实例

#include<iostream>
#include<cstdio>
#include<string.h>
#define maxSize 100
#define Status int
#define ElemType int//这里的数据类型是存入的信息的统称,也可以是一个结构体 
using namespace std;
typedef struct
{
	ElemType data[maxSize];//这里可以是一个发杂的结构体数组 
	int length;
}SqList;
Status InitList(SqList &L)//初始化单链表 
{
	memset(L.data,0,sizeof(L.data));//此函数在string.h中,初始化数组 
	L.length=0; 
	return 0; 
} 
bool CreatList(SqList &L,int n)//创建顺序表函数,初始化前几个元素 
{
	if(n<0||n>maxSize)
	return false;
	for(int i=0;i<n;i++)
	{
		int temp;
		scanf("%d",&L.data[i]);
		L.length++;
	}
	return true;
}
bool InsertList(SqList &L,int i,ElemType e)//插入一个元素 这里的i是插入的位置如果i是1那么就在数组下标为0的地方,以此类推 
{
	if(i<1||i>L.length+1)
	{
		printf("位置无效\n");
		return false;
	}
	if(L.length>=maxSize)
	{
		printf("当前储存空间已满\n");
		return false;
	}
	for(int j=L.length;j>=i;j--)
	{
		L.data[j]=L.data[j-1];
	}
	L.data[i-1]=e;
	L.length++;//线性表长度加1 
	return true;
}
bool ListDelete(SqList &L,int i)//删除指定位置的元素 
{
	if(i<1||i>L.length)
	{
		printf("位置无效\n");
		return false;
	}
	for(int j=i;j<=L.length-1;j++)
	{
		L.data[j-1]=L.data[j];//把需要删除的元素后边的元素往前移动一位就完成删除的操作 
	}
	L.length--;
	return true;
} 
int LocateElem(SqList L,ElemType e)
{
	for(int i=0;i<L.length;i++)
	{
		if(L.data[i]==e)
		return i+1;
	 } 
	 return 0;
	
}
void printflist(SqList L)
{
	printf("当前顺序表的元素有:\n");
	for(int i=0;i<L.length;i++)
	{
		printf("%d\n",L.data[i]);
	} 
}
void Creat(SqList &L)
{
	int n;
	bool flag;
	printf("请输入顺序表长度!\n");
	scanf("%d",&n);
	printf("请输入顺序表的元素:\n") ;
	flag=CreatList(L,n);
	if(flag)
	{
		printf("创建成功!\n");
		printflist(L);
	}
	else
	printf("创建失败!");
}
void Delete(SqList &L)
{
	int place; bool flag;
	printf("请输入要删除的位置(从1开始):\n");
	scanf("%d", &place);
	flag = ListDelete(L, place);
	if (flag)
	{
		printf("删除成功!!!\n");
		printflist(L);
	}
}
//查找功能函数 调用LocateElem查找元素
void Insert(SqList &L)
{
	int place ;
	ElemType e;
	bool flag;
	printf("请输入插入的位置:\n");
	scanf("%d",&place);
	printf("请输入插入的元素:\n");
	scanf("%d",&e);
	flag=InsertList(L,place,e);
	if(flag)
	{
		printf("插入成功\n");
		printflist(L);
	}
	else
	printf("插入失败\n");
}
void Search(SqList L)
{
	ElemType e;
	int flag;
	printf("请输入要查找的值:");
	scanf("%d",&e);
	flag=LocateElem(L,e);
	if(flag)
	{
		printf("该元素的位置是:%d\n",flag);
	 } 
	 else
	 printf("未找到该元素\n");
}
void menu()
{
	printf("********1.创建    2.插入*********\n");
	printf("********3.删除    4.查找*********\n");
	printf("********7.输出    8.退出*********\n");
}
int main()
{
	SqList L;
	int choice;
	InitList(L);
	while (1)
	{
		menu();
		printf("请输入菜单序号:\n");
		scanf("%d", &choice);
		if (8 == choice) break;
		switch (choice)
		{
		case 1:Creat(L); break;
		case 2:Insert(L); break;
		case 3:Delete(L); break;
		case 4:Search(L); break;
		case 7:printflist(L); break;
		default:printf("输入错误!!!\n");
		}
	}
	return 0;
}

(2)线性表顺序储存结构的时间复杂度
查询插入删除的空间复杂度为o(n)。拿查询来说因为最坏的情况就是插入的时候是插入第一个位置,然后后边所有的元素都要往后移动一位,虽然最好的情况是直接插在最后边,这时候空间复杂度为o(1).但是经过推导空间复杂度还是o(n);
(3)线性表顺序存储结构的优缺点
优点:1.可以快速存取任意位置的元素
2.无需为表示表中的元素之间的逻辑关系而额外增加储存空间
缺点:1.插入删除要移动大量的元素
2.当线性表长度变化较大时,难以确定存储空间的容量
3.造成空间的碎片
2:线性表的链式存储结构
(1)例子:

#include<bits/stdc++.h>
typedef struct Node{
	int data;
	struct Node *next;
}*node;
void creatlist(node *head,int n)//这里运用二级指针,*head接受指针的地址,这个功能是创建一个链表 
{
	//第一种方法是头插法 
	node p;
	//for(int i=0;i<n;i++)
//	{
	//	p=(node)malloc(sizeof(node));
	//	p->data=100+i;
	//	p->next=(*head)->next;
	//	(*head)->next=p;
//	}
	//第二种方法是尾插法,取其中一种就行了。
	node q;
	q=*head; 
	while(q->next!=NULL)
	q=q->next;
	for(int i=0;i<n;i++)
	{
      p=(node)malloc(sizeof(node));
      p->data=100+i;
       q->next=p;
       p->next=NULL;
       q=p;
	} 

} 
void print(node *head)
{
	node p;
	p=(*head)->next;
	if(p==NULL)
	printf("111");
	while(p!=NULL)
	{
		printf("%d\n",p->data);
		p=p->next;
	}
}
int Clearlist(node *head) 
{
	node p,q;
	p=(*head)->next;
	while(p)
	{
		q=p->next;
		free(p);
	    p=q; 
	}
	(*head)->next=NULL; 
}
int main()
{
	node head;
	head=(node)malloc(sizeof(node));
    head->next=NULL;
    creatlist(&head,5);
    print(&head);
    Clearlist(&head);
    print(&head);
}               
   

(2)关于头指针和头结点
头指针,只有指针域没有信息域,头指针是指向第一个结点的指针,如果有头节点,那么头指针指向头结点。头指针是链表必须的
头结点,有信息域和指针域,但是信息域不放数据元素,而是存放线性表长度等公共数据或者不放任何东西,信息域无任何意义,有了头节点插入信息和删除信息就和其他结点一样了,头结点不是链表必须的
3.线性表顺序存储结构和链式存储结构的对比:
(1)存储分配方式:
顺序存储结构是用一段连续的储存空间
链式存储结构是用一组任意的存储单元存放元素
(2)时间性能:
1.查找方面(查找某个位置的元素):
顺序结构是o(1)
链式是o(n)
2.插入删除(找到插入或者删除的位置后)
顺序结构平均移动表长一半的元素为o(n)
链式结构为o(n)
(3)空间性能:
顺序结构要预支空间,分大了浪费,小了受限制。
链式结构不需要预支空间而且元素个数不受限制
(4)总结
频繁查找的时候顺序表优势,频繁删改链式好,
如果元素个数变化较大或者不知道有多少元素,那么链表较好。如果提前知道线性表的大致长度,用顺序表较好,二者的好坏根据实际情况而论。
4.静态链表 :

#include<stdio.h>
#include<windows.h>
#define max  10
#define ElemType int
typedef struct
{
	ElemType data;
	int cur;
}con,staticlist[max];
void  initlist(staticlist space)
{
	for(int i=0;i<max-1;i++)
	space[i].cur=i+1; 
	space[max-1].cur=0;
}
int len(staticlist space)
{
    int cnt=0;
    int i=space[max-1].cur;
    while(i!=0)
    {
    	i=space[i].cur;
		cnt++;
	}
	return cnt;
}
void add(staticlist space)
{
	printf("请输入你想添加的数据成员的数量:");
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		printf("请输入第%d个数据成员:",i);
		scanf("%d",&space[i].data);	
	}
	space[0].cur=n+1;
	space[n].cur=0;
	space[max-1].cur=1;
}
int malloc(staticlist space)
{
	int i=space[0].cur;
	if(i!=0)
	{
		space[0].cur=space[i].cur;
	}
	return i;
}

void trav(staticlist space)
{
	int i=space[max-1].cur;
	while(i)
	{
		printf("%d\n",space[i].data);
		i=space[i].cur;
	}
}
int insert(staticlist space,int i,ElemType a)
{
	int j,k,l;
	k=max-1;
	if(i<1||i>len(space)+1)
	{
		printf("此位置不能进行插入\n");
		return  0; 
	}
	j=malloc(space);
	if(j!=0)
	{
		space[j].data=a;
		for(l=1;l<i;l++)
		k=space[k].cur;
		space[j].cur=space[k].cur;
	     space[k].cur=j;
	     return 1;
	}
	return 0; 
}
int free(staticlist space,int i)
{
	space[i].cur=space[0].cur;
	space[0].cur=i;
}
int del(staticlist space,int i)
{
     int k,j;
	 if(i<1||i>len(space))
	 return 0;
	 k=max-1;
	 for(j=1;j<i;j++)
	 k=space[k].cur;
	 j=space[k].cur;      
	 space[k].cur=space[j].cur;
	 free(space,j);
	 return 1;	
}
int main()
{
	staticlist space;
	printf("正在为你初始化静态链表..........!\n");
	Sleep(2000); 
	initlist(space);
    add(space);
    trav(space);
    insert(space,2,4);
    printf("正在为你重新链接链表..........!\n");
	Sleep(2000); 
    trav(space);
    while(1)
    {
	printf("请输入你想删除的下标:");
	int m;
	scanf("%d",&m);
	del(space,m);
	printf("正在为你重新链接链表..........!\n");
	Sleep(2000); 
	trav(space);	
	}
	return 0;
}
             
              

静态链表的核心:
(1)下图为初始化的时候在这里插入图片描述
静态链表头尾不存放数据成员,头存第一个没存数据的数组下标,尾存第一个放数据的数组下标
(2)下图为添加元素的时候
在这里插入图片描述
(3)
下图为插入元素的时候:
在这里插入图片描述
(4)
下图是删除的时候

静态链表的优缺点:
优点:插入和删除的时候只需修改游标,不需要移动元素,不像顺序表一样
缺点:没有解决长度不确定的问题,失去了顺序存储结构的随机存取的特性。
5.循环链表:
将单链表中终端结点的指针端由空指向指向头结点,使得整个链表变成一个环,简称循环链表
这样从任意的结点出发都能访问到全部结点
6.双向链表:
在单链表的基础上在设置一个指向前驱结点的指针,头结点的前驱指针指向尾,尾结点的后继

发布了37 篇原创文章 · 获赞 52 · 访问量 1830

猜你喜欢

转载自blog.csdn.net/qq_45737068/article/details/104327398