数据结构之线性表基本操作

目录

一、顺序表

二、单链表


一、顺序表

// 基于静态存储方式的线性表
#include <stdio.h>

#define MAX_SIZE 10

typedef int ElemType;

typedef struct {
	ElemType data[MAX_SIZE];
	int length;
} SqList;

/**
	静态存储方式
	初始化顺序表
*/
void ListInit(SqList *L) {
	L->length = 0;
	for (int i = 0; i < L->length; i++) {
		L->data[i] = 0;
	}
}

/**
	向指定位置添加元素
	添加成功,返回true;否则返回false
*/
bool ListInsert(SqList *L, int i, ElemType e) {
	if (i < 1 || i > L->length + 1) {
		return false;
	}
	if (L->length >= MAX_SIZE) {
		return false;
	}
	for (int j = L->length - 1; j >= i - 1; j--) {
		L->data[j + 1] = L->data[j];
	}
	L->data[i - 1] = e;
	L->length++;
	return true;
}

/**
	删除i处元素,并返回被删除的元素
*/
ElemType ListDelete(SqList *L, int i) {
	if (i < 1 || i > L->length) {
		printf("the index is out of length! \n");
		return 0;
	}
	int temp = L->data[i - 1];
	for (int j = i; j < L->length; j++) {
		L->data[j - 1] = L->data[j];
	}
	L->length--;
	return temp;
}

/**
	返回i位置的元素
*/
ElemType LocateElem(SqList L, int i) {
	if (i < 1 || i > L.length) {
		return -1;
	}
	return L.data[i - 1];
}

/**
	查找顺序表中首次匹配的元素所处的位序
*/
int LocateIndexByElem(SqList L, ElemType e) {
	for (int i = 0; i < L.length; i++) {
		if (L.data[i] == e) {
			return i + 1;
		}
	}
	return -1;
}

/**
	返回顺序表的当前长度
*/
int GetLength(SqList L) {
	return L.length;
}

/**
	判断顺序表是否为空
*/
bool Empty(SqList L) {
	if (GetLength(L) == 0) {
		return true;
	}
	return false;
}

/**
	销毁线性表
*/
void DestoryList(SqList *L) {
	for (int i = L->length - 1; i >= 0; i--) {
		L->data[i] = 0;
	}
	L->length = 0;
}

/**
	遍历顺序表
*/
void ListTraverse(SqList L) {
	for (int i = 0; i < L.length; i++) {
		printf("%d ", L.data[i]);
	}
	printf("\n");
}

int main() {
	SqList list;
	ListInit(&list);
	ListInsert(&list, 1, 2);
	ListInsert(&list, 2, 3);
	ListInsert(&list, 3, 5);
	ListInsert(&list, 4, 7);
	ListTraverse(list);
	ListDelete(&list, 5);
	printf("the first element is %d \n", LocateElem(list, 1));
	printf("the last element is %d \n", LocateElem(list, list.length));
	ListTraverse(list);
	DestoryList(&list);
	if (Empty(list) == true) {
		printf("list clear out \n");
	}
	ListInsert(&list, 1, 6);
	ListTraverse(list);
	int ele = LocateElem(list, 3);
	printf("%d", ele);
	return 0;
}

        初始化线性表后,线性表的最大长度确定,如果添加数据的长度大于线性表允许的最大长度,会引发越界异常,显然这种方式是有很大的局限。为了解决这个问题,引出动态存储方式的线性表。

// 基于动态存储方式的线性表
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	int id;
	double score;
} Student;

typedef Student ElemType;

typedef struct {
	ElemType *data;
	int MAX_SIZE, length;
} SqList;

void ListInit(SqList *L) {
	L->MAX_SIZE = 3;
	L->length = 0;
	L->data = (ElemType *)malloc(L->MAX_SIZE * sizeof(ElemType));
}

/**
	扩容操作
*/
void expandList(SqList *L, int newLength) {
	if (newLength <= L->MAX_SIZE) {
		return;
	}
	ElemType *newData = (ElemType *)malloc(newLength * sizeof(ElemType));
	if (newData == NULL) {
		return;
	}
	for (int i = 0; i < L->length; i++) {
		newData[i] = L->data[i];
	}
	free(L->data);
	L->data = newData;
	L->MAX_SIZE = newLength;
}

bool ListInsert(SqList *L, ElemType e, int i) {
	if (i < 1 || i > L->length + 1) {
		return false;
	}
	if (L->length >= L->MAX_SIZE) {
		expandList(L, L->MAX_SIZE * 2);  // 当前容量不足时,扩容为原来的两倍
	}
	for (int j = L->length; j >= i; j--) {
		L->data[j] = L->data[j - 1];
	}
	L->data[i - 1] = e;
	L->length++;
	return true;
}

bool ListDelete(SqList *L, int i) {
	if (i < 1 || i > L->length) {
		return false;
	}
	ElemType e = L->data[i - 1];
	for (int j = i; j < L->length; j++) {
		L->data[j - 1] = L->data[j];
	}
	L->length--;
	return true;
}

void ListDestory(SqList *L) {
	if (L->data != NULL) {
		free(L->data);
		L->data = NULL;
	}
	L->MAX_SIZE = 0;
	L->length = 0;
}

void ListTraverse(SqList L) {
	for (int i = 0; i < L.length; i++) {
		Student stu = L.data[i];
		printf("id: %d,score: %.1f\n", stu.id, stu.score);
	}
}


int main() {
	SqList list;
	ListInit(&list);
	Student stu1 = {1, 90.5};
	ListInsert(&list, stu1, 1);
	stu1.id = 2;
	stu1.score = 95;
	ListInsert(&list, stu1, 1);
	stu1.id = 3;
	ListInsert(&list, stu1, 2);
	stu1.id = 4;
	stu1.score = 100;
	ListInsert(&list, stu1, 4);
	printf("%d\n", list.MAX_SIZE);
	ListTraverse(list);
	ListDelete(&list, 3);
	ListTraverse(list);
	ListDestory(&list);
	return 0;
}

         基于动态存储的线性表的扩容方式,听起来很高级。实际上原理很简单。我们设定在调用添加函数时,如果当前长度超过最大长度,那么会将线性表的存储空间翻倍。首先明确线性表的定义,它存储数组的空间是一段连续空间,因而在扩容操作时,从理论上来讲,如果当前数组的末尾地址如果存在足够大的空间,是可以继续接着分配。然而这种直接在原先的地址下继续分配的概率不大,系统一般会寻找一个新的空间来存储扩容数组。那么,我们是如何实现扩容操作的呢?基于数组的特点,我们在开辟一段新的空间后,首先将原先数组复制到新数组,然后紧接着数组序号,将新添加的元素放入此数组中。接下来分析这种方法的时间复杂度和空间复杂度,扩容函数执行最频繁的操作是为新数组的每个位置的元素赋值,因而时间复杂度T(n) = O(n)。假设新扩充的长度为n,那么新开辟的空间应为(ElemType)*(m+n),空间复杂度S(n) = O(n)  。从时间和空间复杂度来看,这种扩容方式的效率是不高的。

二、单链表 

         单链表是通过一组任意的存储单元来存储线性表中的数据元素,为了建立数据元素之间的线性关系,每个链表结点,除了存放元素自身的信息外,还需要存放一个指向其后继的指针。单链表的物理存储方式是一组任意的存储单元,而顺序表则是分配一段连续空间。

// 单链表
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

typedef struct LNode {
	ElemType data;
	LNode *next;
} LNode, *LinkList;

// 头插法
LinkList HeadInsert(LinkList *L) {
	// LinkList *p = &L; 形参 指向LinkList结构体变量的指针指向L的地址
	// 创建头结点
	int x;
	// 声明新结点
	LNode *s;
	*L = (LinkList)malloc(sizeof(LNode));
	(*L)->next = NULL;
	(*L)->data = 0;
	// 创建新结点
	printf("please input node:\n");
	scanf("%d", &x);
	while (x != 1000) {
		s = (LNode *) malloc(sizeof(LNode));
		s->next = (*L)->next;
		s->data = x;
		(*L)->next = s;
		printf("please input next node:\n");
		scanf("%d", &x);
	}
	return *L;
}

// 尾插法
LinkList RailInsert(LinkList *L) {
	// 关键在于记录末尾结点
	int x;
	*L = (LinkList)malloc(sizeof(LNode));
	// 声明尾指针,当前尾指针指向链表的头结点
	LNode *r = *L;
	LNode *s;
	printf("please input node:\n");
	scanf("%d", &x);
	while (x != 1000) {
		s = (LNode *)malloc(sizeof(LNode));
		// -> 运算符是对指针进行解引用并访问成员的操作
		s->data = x;
		r->next = s;
		r = s;
		printf("please input next node:\n");
		scanf("%d", &x);
	}
	r->next = NULL;
	return *L;
}

// 按位序查找结点
LNode *GetElem(LinkList L, int i) {
	if (i < 1)
		return NULL;
	LNode *p = L->next;
	int j = 1;
	while (p != NULL && j < i) {
		p = p->next;
		j++;
	}
	return p;
}

// 按值查找结点
LNode *LocateElem(LinkList L, ElemType e) {
	LNode *p = L->next;
	while (p != NULL) {
		if (p->data == e) {
			return p;
		}
		p = p->next;
	}
	return NULL;
}

// 前插法插入结点
void InsertNode(LinkList L, int i, ElemType e) {
	if (i < 1 || L->next == NULL) {
		return;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	s->data = e;
	if (i == 1) {
		s->next = L->next;
		L->next = s;
		return;
	}
	LNode *p = GetElem(L, i - 1);
	s->next = p->next;
	p->next = s;
}

/**
	删除位序为i的元素
*/
bool DeleteNode(LinkList L, int i) {
	if (i < 1) {
		return false;
	}
	LNode *p = GetElem(L, i - 1);
	if (p != NULL) {
		LNode *s = p->next;
		p->next = s->next;
		free(s);
		return true;
	}
	return false;
}

void TraverseList(LinkList L) {
	while (L->next != NULL) {
		L = L->next;
		printf("%d\n", L->data);
	}
}

int GetLength(LinkList L) {
	LNode *p = L->next;
	int length = 0;
	while (L->next != NULL) {
		L = L->next;
		length++;
	}
	return length;
}

void DestoryList(LinkList *L) {
	LNode *p = (*L)->next;
	LNode *s;
	while (p != NULL) {
		s = p;
		p = p->next;
		free(s);
	}
	free(*L);
	// 避免悬空指针
	*L = NULL;
}

bool IsEmpty(LinkList L) {
	return (L == NULL) ? true : false;
}

int main() {
	LinkList L = NULL;
	HeadInsert(&L);
	// RailInsert(&L);
	LNode *s = GetElem(L, 4);
	if (s == NULL) {
		printf("找不到对应序号的元素!\n");
	} else {
		printf("%d\n", s->data);
	}
	printf("------------\n");
	LNode *p1 = LocateElem(L, 100);
	if (p1 == NULL) {
		printf("表中没有此元素!\n");
	} else {
		printf("查找到的元素为%d", p1->data);
	}
	printf("-------------\n");
	InsertNode(L, 2, 1000);
	TraverseList(L);
	DeleteNode(L, 2);
	TraverseList(L);
	int length = GetLength(L);
	printf("length is %d\n", length);
	DestoryList(&L);
	if (IsEmpty(L)) {
		printf("单链表已清空!");
	} else {
		printf("未清空!");
	}
	return 0;
};

        上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

弘弘弘弘~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值