线性表:零个或多个数据元素的有限序列
强调几点:
- 首先它是一个序列。也就是说,元素之间是有顺序的,若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他都有一个前驱和后继。
- 其次线性表强调是有限的。
线性表有两种物理结构,第一种是顺序存储结构,另一种是链式存储结构。
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。用c语言的一维数组来实现顺序存储结构。
线性表顺序存储结构的优缺点
优点:
- 可以快速地读取表中任一位置的元素
- 无需为表示表中元素之间的逻辑关系而增加额外的存储空间
缺点:
- 插入和删除操作需要移动大量元素
- 当线性表长度变化较大时,难以确定存储空间的容量,造成存储空间的“破碎”
实际上,顺序存储结构最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费时间。于是我们迎来了线性表的第二种存储结构:链式存储结构
链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。
在顺序存储结构中,每个数据元素之需要存储数据元素信息就可以了。现在链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的地址。
把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域
下面来说说链表的优点在哪里
- 基于结构体指针
- 可动态地分配存储
- 所有结点离散分布,仅由指针联系起来
准备工作
头文件
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
宏定义以及typedef的使用
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20
typedef int Status;
typedef int ElemType;
结构体及其定义
typedef struct Node
{
ElemType data;
struct Node* next;
}Node;
typedef struct Node* LinkList;
malloc函数
这里简单说一下malloc函数的用法
指针自身=(指针类型*)malloc(sizeof(指针类型))
注:
- malloc返回的是无类型的指针,在使用时一定要强制转换为所需类型
- 开辟空间后,一定要释放空间,否则会造成内存泄漏。
- free(p)函数,释放p所指变量的存储空间,彻底删除一个变量
其他注意事项
- 当你传递一个参数给函数的时候,这个参数会不会在函数内被改动决定了使用什么参数形式
- 如果需要被改动,则需要传递指向这个参数的指针
- 如果不需要被改动,可以直接传递这个参数。
请时刻注意这点,不要老是遇到函数一会要指针,一会不要指针的时候一头雾水,搞不明白。
下面说一说单链表基本的操作
单链表的初始化(头结点指针域置为空)
Status InitList(LinkList* L)
{
*L = (LinkList)malloc(sizeof(Node));
if (!(*L))
return ERROR;
(*L)->next = NULL;
return OK;
}
判断链表是否为空(实际上就是根据头结点是否为空。空返回1,非空返回0)
Status ListEmpty(LinkList L)
{
if (L->next)
return FALSE;
else
return TRUE;
清空链表
Status ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
return OK;
}
求链表的表长
int ListLength(LinkList L)
{
int i = 0;
LinkList p = L->next;
while (p)
{
i++;
p = p->next;
}
return i;
}
取值
Status GetElem(LinkList L,int i,ElemType *e)
{
int j;
LinkList p;
p = L->next;
j = 1;
while (p && j<i)
{
p = p->next;
++j;
}
if ( !p || j>i )
return ERROR;
*e = p->data;
return OK;
}
按值查找
int LocateElem(LinkList L,ElemType e)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
if(p->data==e)
return i;
p=p->next;
}
return 0;
}
插入
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j;
LinkList p,s;
p = *L;
j = 1;
while (p && j < i)
{
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
删除
Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j;
LinkList p,q;
p = *L;
j = 1;
while (p->next && j < i)
{
p = p->next;
++j;
}
if (!(p->next) || j > i)
return ERROR;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return OK;
}
单链表的建立——头插法
void CreateListHead(LinkList* L, int n)
{
LinkList p;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
for (i = 0; i < n; i++)
{
p = (LinkList)malloc(sizeof(Node));
p->data = rand() % 100 + 1;
p->next = (*L)->next;
(*L)->next = p;
}
}
单链表的建立——尾插法
void CreateListTail(LinkList* L, int n)
{
LinkList p, r;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
r = *L;
for (i = 0; i < n; i++)
{
p = (Node*)malloc(sizeof(Node));
p->data = rand() % 100 + 1;
r->next = p;
r = p;
}
r->next = NULL;
}
注:关于p->data的数据你也可以改成手动输入,不必让它随机产生
重要的是能理解头插法或者尾插法的算法思想
输出
Status ListTraverse(LinkList L)
{
LinkList p = L->next;
while (p)
{
visit(p->data);
p = p->next;
}
printf("\n");
return OK;
}
Status visit(ElemType c)
{
printf("%d ", c);
return OK;
}
主函数
int main()
{
LinkList L;
ElemType e;
Status i;
int j, k;
i = InitList(&L);
printf("初始化L后:ListLength(L)=%d\n", ListLength(L));
for (j = 1; j <= 5; j++)
i = ListInsert(&L, 1, j);
printf("在L的表头依次插入1~5后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n", ListLength(L));
i = ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)\n", i);
i = ClearList(&L);
printf("清空L后:ListLength(L)=%d\n", ListLength(L));
i = ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)\n", i);
for (j = 1; j <= 10; j++)
ListInsert(&L, j, j);
printf("在L的表尾依次插入1~10后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n", ListLength(L));
ListInsert(&L, 1, 0);
printf("在L的表头插入0后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n", ListLength(L));
GetElem(L, 5, &e);
printf("第5个元素的值为:%d\n", e);
for (j = 3; j <= 4; j++)
{
k = LocateElem(L, j);
if (k)
printf("第%d个元素的值为%d\n", k, j);
else
printf("没有值为%d的元素\n", j);
}
k = ListLength(L);
for (j = k + 1; j >= k; j--)
{
i = ListDelete(&L, j, &e);
if (i == ERROR)
printf("删除第%d个数据失败\n", j);
else
printf("删除第%d个的元素值为:%d\n", j, e);
}
printf("依次输出L的元素:");
ListTraverse(L);
j = 5;
ListDelete(&L, j, &e);
printf("删除第%d个的元素值为:%d\n", j, e);
printf("依次输出L的元素:");
ListTraverse(L);
i = ClearList(&L);
printf("\n清空L后:ListLength(L)=%d\n", ListLength(L));
CreateListHead(&L, 20);
printf("整体创建L的元素(头插法):");
ListTraverse(L);
i = ClearList(&L);
printf("\n删除L后:ListLength(L)=%d\n", ListLength(L));
CreateListTail(&L, 20);
printf("整体创建L的元素(尾插法):");
ListTraverse(L);
return 0;
}
到此这篇关于c语言线性表全面梳理操作方法的文章就介绍到这了,更多相关c语言线性表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!