C语言数据结构-线性表

一.线性表的定义

线性表(List):零个或多个数据元素的有限序列。

  • 它是一个序列,元素之间是有顺序的,第一个元素无前驱,最后一个元素无后继,其他每个元素都有且只有一个前驱和后继。
  • 它是有限的。
  • 线性表元素的个数 n (n≥0) 定义为线性表的长度,当 n=0 时,称为空表。
  • 复杂的线性表中,一个数据元素可以由若干个数据项组成。

二.线性表的抽象数据类型

定义如下:

ADT  线性表(List)
Data
		    线性表的数据对象集合为{a~1~,a~2~,……,a~n~},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素a~n~外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
	InitList(*L):  初始化操作,建立一个空的线性表L。
	ListEmpty(L):  若线性表为空,返回true,否则返回false。	
	ClearList(*L): 将线性表清空。
	GetElem(L,i,*e):	将线性表L中的第i个位置元素值返回给e。
	LocateElem(L,e):	    在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
	ListInsert(*L,i,e): 在线性表L中的第i个位置插入新元素e。
	ListDelete(*L,i,*e):删除线性表L中第i个位置元素,并用e返回其值。
	ListLength(L):	    返回线性表L的元素个数。
endADT

三.线性表的顺序存储结构

1.定义

线性表的顺序存储结构:指的是用一段地址连续的存储单元依次存储线性表的数据元素。
以下为线性表的顺序存储结构示意图:
在这里插入图片描述

2.存储

#define MAXSIZE 20            //存储空间初始分配量
typedef int ElemType;        //ElemType类型根据实际情况而定,这里假设为 int
typedef struct
{
	ElemType datta[MAXSIZE]//数组存储数据元素,最大值为 MAXSIZE
	int length;              //线性表当前长度
}SqList;

描述顺序存储结构需要三个属性:

  • 存储空间的起始位置 :数组data,它的存储位置就是存储空间的存储位置。
  • 线性表的最大存储容量 :数组长度MaxSize。
  • 线性表的当前长度 :length。

3.数据长度与线性表长度区别

  • 数组长度是存放线性表的存储空间的长度,存储分配后这个量一般是不变的。(已经分配好的长度)(可以用编程手段实现动态分配数组,但会带来性能上的损耗)
  • 线性表长度是线性表中数据元素的个数,随着插入和删除操作,这个量是变化的。(已经用了的长度)
  • 线性表长度 ≤ 数组长度

4.地址计算方法

存储器中的每个存储单元都有自己的编号,这个编号称为地址
线性表中可以进行插入和删除操作,因此分配的数组空间要大于等于当前线性表的长度。
在这里插入图片描述
(C语言中的数组的下标是从0开始的)
地址计算方法:LOC(ai) = LOC(ai) + (i -1) * c (c为一个数据元素需要的存储单元)

5.优缺点

  • 优点
    1.无须为表示表中元素之间的逻辑关系而增加额外的存储空间。
    2.可以快速地存取表中任一位置的元素。
  • 缺点
    1.插入和删除操作需要移动大量元素。
    2.当线性表长度变化较大时,难以确定存储空间的容量。
    3.造成存储空间的“碎片”。

四.顺序表代码化

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
 
//函数状态码定义
#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE -1
#define OVERFLOW   -2
 
//顺序表的存储结构定义
#define LIST_INIT_SIZE  100 //初始分配存储空间
#define LISTINCREMENT   10  //存储空间分配增量
typedef struct
{
    int *elem;    //存储空间基地址
    int length;   //表中元素的个数
    int listsize; //表容量大小
}SqList;          //顺序表类型定义

int ListInsert_Sq(SqList &L, int pos, ElemType e);
int ListDelete_Sq(SqList &L, int pos, ElemType &e);
int ListLocate_Sq(SqList L, ElemType e);
void ListPrint_Sq(SqList L);
 
//结构初始化与销毁操作
int InitList_Sq(SqList &L)
{
  //初始化L为一个空的有序顺序表
    L.elem=(ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
    if(!L.elem) exit(OVERFLOW); //申请失败
    L.listsize = LIST_INIT_SIZE;
    L.length=0; //表长为0
    return OK;
}
 

//主函数
int main() 
{	
    SqList L; //构造一个表
    if(InitList_Sq(L)!= OK) //初始化失败
	{
        printf("InitList_Sq: 初始化失败!\n");
        return -1;
    }
	printf("1:插入 2:删除 3:查找 4:输出 0:退出\n");
    for(int i=1; i<=10; i++) 
        ListInsert_Sq(L,i,i); //初始化表
	printf("初始顺序表为:\n");
	ListPrint_Sq(L); //输出表
 
    int operationType;  //操作种类
	printf("\n请输入要进行的操作:");
    scanf("%d",&operationType); //输入操作
 
	while(operationType != 0)
	{
        if(operationType == 1) 
		{  //插入操作
            int pos,elem;
			printf("请输入要插入的位置和值:");
            scanf("%d %d",&pos,&elem);
            ListInsert_Sq(L,pos,elem);
			printf("插入后的顺序表为:\n");
			ListPrint_Sq(L);
        } 
		else if(operationType == 2) 
		{  //删除操作
             int pos;int elem;
			 printf("请输入要删除位置:");
             scanf("%d",&pos);
             ListDelete_Sq(L,pos,elem);
			 printf("删除的值为:");
             printf("%d",elem);
			 printf("\n删除后的顺序表为:\n");
			 ListPrint_Sq(L);
        } 
		else if(operationType == 3) 
		{  //查找定位操作
            int elem;
			printf("请输入要查找的值:");
            scanf("%d",&elem);
            int pos = ListLocate_Sq(L,elem);
            if(pos>=1 && pos<=L.length)
			{
				printf("该值的位置是:");
                printf("%d",pos);
			}
            else
                printf("该值不存在!\n");
        } 
		else if(operationType == 4) 
		{  //输出操作
			printf("当前的顺序表为:\n");
            ListPrint_Sq(L);
        }
		else if(operationType == 0)
		{
			return 0;
		}
		printf("\n请输入要进行的操作:");
		scanf("%d",&operationType);//输入操作
		
    }
    system("pause");//防止窗体闪退
	return 0;
}

//插入操作
int ListInsert_Sq(SqList &L,int pos,int e)
{
	int *newbase;//定义扩容后的首地址
	if(pos>=1 && pos<=L.length+1)//判断给的pos是否在顺序表长度之内
	{
		if(L.length >= L.listsize)//如果L.length已经达到或超出设定值,需要扩容
		{
			newbase = (int *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(int));//给newbase动态分配一个长度为LISTINCREMENT的新空间
			if(! newbase)//如果分配失败则返回错误
				return ERROR;
			L.elem = newbase;//新分配空间的基地址
			L.listsize += LISTINCREMENT;//现在的空间长度等于原来空间长度加上新分配的空间长度
		}
		int *p,*q;
		p = &(L.elem[pos-1]);//将原来的pos位置元素的地址分配给指针p
		for(q=&(L.elem[L.length-1]); q>=p; --q)
		{
			*(q+1) = *q; //将原来顺序表最后一个位置数据的地址分配给q,然后从后往前一次将数据向后移动一位
		}
		*p = e;//将数据e放到pos位置
		++L.length;//表长加1
		return OK;
	}
	else//pos不在顺序表的长度之内
		return OVERFLOW;
}

//删除操作
int ListDelete_Sq(SqList &L,int pos,int &e)
{
	if(pos>=1 && pos<=L.length)//判断pos是否在顺序表的长度之内
	{
		int *q;
		e = L.elem[pos-1];//将删掉的值赋给e
		for(q=&(L.elem[pos-1]); q<=&(L.elem[L.length-1]); ++q)
		{
			*q = *(q+1); //将pos位置以后的元素依次向前移动一位
		}
		L.length = L.length-1; //表长减1
	}
	else//pos不在顺序表的长度之内
		return OVERFLOW;
}


//查找定位操作
int ListLocate_Sq(SqList L,int e)
{
	int a=-1;//给a赋初值,无论查找的数据在第几个,都不可能在第-1个
	for(int i=0; i<=L.length-1; i++)
	{
		if(L.elem[i] == e)
		{
			a = i;  //查找成功,退出循环
			break;
		}
	}
	if(a>=0 && a<=L.length-1)
		return a+1; //返回找到的元素的位置
	else
		return ERROR;//查找失败
}


//输出操作
void ListPrint_Sq(SqList L)
{
	for(int i=0; i<=L.length-2; i++)
	{
		printf("%d ",L.elem[i]);
	}
	printf("%d ",L.elem[L.length-1]);
}

在这里插入图片描述

五.链式存储结构

1.定义

线性表的链式存储结构的特点是用一组任意的 存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续 的)。为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针。这两部分信息组成数据元素ai的存储映像。称为结点(Node)。n个结点(ai的存储映像)链结成一个链表,即为线性表的式存储结构。因为此链表的每个结点中只包含一个指针域,故又称线性链表单链表
链表中第一个结点的存储位置叫做头指针,最后一个结点不存在直接后继,规定,线性表的最后一个结点指针为“空”(通常用NULL或“ ^ ”符号表示)。
为了对链表操作更加方便,在单链表的第一个结点前附设一个结点,称为头结点,头结点的数据域不存储任何信息(可以存储表长等附加信息),头结点的指针域存储指向第一个结点的指针。

2.存储

//线性表的单链表存储结构
typedef struct Node
{
	ElemType data;
	struct Node *next;
}Node;
typedef struct Node *LinkList; //定义LinkList

六.单链表代码化

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

typedef struct Node
{
	int data;
	struct Node *next;
}Node,*Linklist;


//创建链表
void List_create(Linklist &L)
{
	int m; //m为结点数
	Linklist p;
	printf("请输入结点数:");
	scanf("%d",&m); //输入结点数
	L = (Linklist)malloc(sizeof(Node));
	p = L;
	for(int i=0; i<m; i++) //循环输入结点数据
	{
		p->next = (Linklist)malloc(sizeof(Node));
		p = p->next;
		printf("请输入第%d个结点的值:",i+1);
		scanf("%d",&p->data); //输入结点数据
	}
	p->next = NULL;
}


//输出链表
void List_display(Linklist L)
{
	Linklist p;
	p = L->next;
	printf("\n现在的链表为:\n");
	printf("%d ",p->data);
	p = p->next;
	while(p)
	{
		printf("%d ",p->data);
		p = p->next;
	}
	printf("\n\n");
}


//查找操作
int List_get(Linklist L,int i)
{
	int j;
	int e;
	Linklist p; //声明一指针p
	p = L->next; //指针p指向链表L的第一个结点
	j = 1; //j是计数器
	while(p && j<i) //*p不为空且计数器j还没有等于i(即还没有到达要查找的那个结点)
	{
		p=p->next; //p指向下一个结点
		j++; //计数器+1
	}
	if(!p || j>i) return -1; //第i个结点不存在
	e = p->data; //读取第i个结点的数据
	return e;
}


//插入操作
int List_insert(Linklist L,int i,int e) 
{//在L的第i个结点位置之前插入新的数据元素e
	int j;
	Linklist p,s;
	p = L;
	j = 1; //j是计数器
	while(p && j<i) //寻找第i-1个结点
	{
		p = p->next;
		j++;
	}
	if(!p || j>i) return -1; //第i个结点不存在
	s = (Linklist)malloc(sizeof(Node)); //生成新结点
	s->data = e; //将要插入的值赋值给s
	s->next = p->next; //将p的后继结点赋值给s的后继
	p->next = s; //将s赋值给p的后继
	return 0;
}


//删除操作
int List_delete(Linklist L,int i)
{//删除L的第i个结点,并用e返回其值
	int e,j; // e记录删去的值
	Linklist p,q; //q为临时变量
	p = L;
	j = 1; //j是计数器
	while(p->next && j<i) //寻找第i-1个结点
	{
		p = p->next;
		j++;
	}
	if(!(p->next) || j>i) return -1; //第i个结点不存在
	q = p->next; 
	p->next = q->next; //将q的后继赋值给p的后继
	e = q->data; //将q结点中的数据传给e
	free(q); //回收此结点,释放内存
	return e;
}


//主函数
int main()
{
	int i,j,e;
	Linklist La;
	List_create(La);
	List_display(La);
	printf("请输入要查找第几个结点:");
	scanf("%d",&i);
	e = List_get(La,i);
	printf("第%d个结点的数据为:%d",i,e);
	printf("\n\n请输入要插入的位置:");
	scanf("%d",&i);
	printf("请输入要插入的值:");
	scanf("%d",&j);
	List_insert(La,i,j);
	List_display(La);
	printf("请输入要删除的位置:");
	scanf("%d",&i);
	e = List_delete(La,i);
	printf("删除的第%d个结点的值为%d\n",i,e);
	List_display(La);
	system("pause"); //防止窗体闪退
	return 0;
}

	

在这里插入图片描述

七.顺序存储结构和单链表的优缺点

  • 顺序存储结构
    1.采用一段连续的存储单元一次存储线性表的数据元素。
    2.需要预分配存储空间,容易造成空间的浪费和上溢。
    3.查找:O(1)、插入和删除:O(n)
    4.插入平均移动(0+1+…+n)/2=n/2个元素
    5.删除平均移动(0+1+…+n-1)/n=(n-1)/2个元素
  • 单链表
    1.采用链式存储结构,用一组任意的存储单元存放线性表的元素。
    2.不需要分配存储空间,元素个数不受限制。
    3.查找:O(n)、插入和删除:O(1)

八.循环链表

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表成为单循环链表,简称循环链表
此时,p->next ≠ 头结点,则循环未结束。和单链表的差别仅在于,判别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。
与单链表相比,优势在于可以从任何一个结点开始,顺序向后访问到达任意结点,但是依然只能在当前结点后插入和删除。

九.双向链表

定义:在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。其实是用空间来换时间。
与单链表相比,优势在于可以从任何结点开始任意向前向后双向访问,可以在当前结点前面或者后面插入,可以删除前趋和后继(包括结点自己)。
存储结构:

typedef struct node
{
	int data;
	struct node *prior; //前驱指针
	struct node *next;  //后继指针
}node,*Linklist;

在结点p和结点p->next之间插入一个值为e的结点s,操作为:

s->prior = p;       //把p赋值给s的前驱
s->next = p->next;  //把p->next赋值给s的后继
p->next->prior = s; //把s赋值给p->next的前驱
p->next = s;		//把s赋值给p的后继

删除一个结点p,操作为:

p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
  • 19
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
以下是一个简单的C语言线性表的实现代码,包括顺序表和链表两种方式: 1. 顺序表实现 ```c #define MAXSIZE 100 // 定义线性表最大长度 typedef struct { int data[MAXSIZE]; // 存储数据元素 int length; // 线性表当前长度 } SqList; // 顺序表类型定义 // 初始化线性表 void InitList(SqList *L) { L->length = 0; } // 插入元素 bool ListInsert(SqList *L, int i, int e) { if (i < 1 || i > L->length + 1 || L->length == MAXSIZE) { 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, int *e) { if (i < 1 || i > L->length) { return false; // 删除位置不合法 } *e = L->data[i - 1]; // 将删除元素的值赋给e for (int j = i; j < L->length; j++) { L->data[j - 1] = L->data[j]; // 将删除位置后的元素前移 } L->length--; // 线性表长度减1 return true; } // 查找元素 int LocateElem(SqList L, int e) { for (int i = 0; i < L.length; i++) { if (L.data[i] == e) { return i + 1; // 返回元素在线性表中的位置 } } return 0; // 未找到元素 } ``` 2. 链表实现 ```c typedef struct LNode { int data; // 数据域 struct LNode *next; // 指针域 } LNode, *LinkList; // 链表类型定义 // 初始化链表 void InitList(LinkList *L) { *L = (LinkList)malloc(sizeof(LNode)); // 创建头结点 (*L)->next = NULL; // 头结点指针域置空 } // 插入元素 bool ListInsert(LinkList L, int i, int e) { LNode *p = L, *s; int j = 0; while (p && j < i - 1) { // 寻找第i-1个结点 p = p->next; j++; } if (!p || j > i - 1) { return false; // 插入位置不合法 } s = (LNode*)malloc(sizeof(LNode)); // 创建新结点 s->data = e; // 新结点赋值 s->next = p->next; // 新结点指向原第i个结点 p->next = s; // 原第i-1个结点指向新结点 return true; } // 删除元素 bool ListDelete(LinkList L, int i, int *e) { LNode *p = L, *q; int j = 0; while (p->next && j < i - 1) { // 寻找第i-1个结点 p = p->next; j++; } if (!p->next || j > i - 1) { return false; // 删除位置不合法 } q = p->next; // q指向第i个结点 *e = q->data; // 将删除元素的值赋给e p->next = q->next; // 将第i-1个结点指向第i+1个结点 free(q); // 释放第i个结点的空间 return true; } // 查找元素 int LocateElem(LinkList L, int e) { LNode *p = L->next; int i = 1; while (p) { if (p->data == e) { return i; // 返回元素在链表中的位置 } p = p->next; i++; } return 0; // 未找到元素 } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值