[数据结构]——线性表

一、线性表的顺序存储结构

1、头文件

// 防止头文件重复包含
#ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H

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

//线性表的顺序存储:用一块连续的存储空间

//动态增长内存,将真正存放数据的内存放到堆上
//动态数据 5个元素,空间不足,申请内存,拷贝数据,释放旧存储 capacity
//实现先申请capacity空间的容量,不够了再申请


typedef struct DYNAMICARRAY{
	int* paddr; //存放数据的地址
	int size; //当前有多少个元素
	int capacity; //容器的容量
} DynamicArray;

//撰写一系列对此结构体进行操作的函数
//初始化(创建操作)
DynamicArray* initArray();
//插入操作
void push_back(DynamicArray* arr, int value);
//删除操作(根据位置删除)
void removeByPos(DynamicArray* arr, int pos);
//根据值删除(值第一次出现的位置)
void removeByValue(DynamicArray* arr, int value);
//释放动态数组的内存
void freeSpace(DynamicArray* arr);
//查找
int findByValue(DynamicArray* arr, int value);
//打印数组
void printArray(DynamicArray* arr);
//清空数组
void clearArray(DynamicArray* arr);
//capacity 这块内存空间一共可以存放多少元素
int capacityArray(DynamicArray* arr);
//size 记录当前数组中具体的元素个数
int sizeArray(DynamicArray* arr);
//根据位置获取每个位置信息
int atIndex(DynamicArray* arr, int pos);

#endif

2、实现部分

#include "DynamicArray.h"

//撰写一系列对此结构体进行操作的函数
//初始化(创建操作)
DynamicArray* initArray() {
	//申请内存
	DynamicArray* myArray = (DynamicArray*)malloc(sizeof(DynamicArray));
	//初始化
	myArray->size = 0;
	myArray->capacity = 20;
	myArray->paddr = (int*)malloc(sizeof(int) * myArray->capacity);

	return myArray;
}

//插入操作
void push_back(DynamicArray* arr, int value) {
	if (arr == NULL) {
		return;
	}

	//判断空间是否足够
	if (arr->size == arr->capacity) {
		//进行空间的扩展,新空间是旧空间的两倍
		int* newSpace = (int*)malloc(sizeof(int) * arr->capacity * 2);
		//拷贝数据到新的空间
		memcpy(newSpace, arr->paddr, arr->capacity*sizeof(int));
		//释放旧空间内存
		free(arr->paddr);

		//更新容量
		arr->capacity = arr->capacity * 2;
		arr->paddr = newSpace;
	}

	//插入新元素
	arr->paddr[arr->size] = value;
	arr->size++;
}

//删除操作(根据位置删除)
void removeByPos(DynamicArray* arr, int pos) {
	if (arr == NULL) {
		return;
	}

	//判断位置是否有效
	if (pos < 0 || pos >= arr->size) {
		return;
	}
	//删除元素
	for (int i = pos; i < arr->size-1; i++) {
		arr->paddr[i] = arr->paddr[i + 1];
	}

	arr->size--;
}

//根据值删除(值第一次出现的位置)
void removeByValue(DynamicArray* arr, int value) {
	if (arr == NULL) {
		return;
	}
	
	//找到值的位置
	int pos = findByValue(arr, value);

	if (pos != -1) {
		removeByPos(arr, pos);
	}
}

//释放动态数组的内存
void freeSpace(DynamicArray* arr) {
	if (arr == NULL) {
		return;
	}

	if (arr->paddr != NULL) {
		free(arr->paddr);
	}
	free(arr);
}

//查找
int findByValue(DynamicArray* arr, int value) {
	if (arr == NULL) {
		return -1;
	}
		
	//找到值得位置
	int pos = -1;
	for (int i = 0; i < arr->size; i++) {
		if (arr->paddr[i] == value) {
			pos = i;
			break;
		}
	}

	return pos;
}

//打印数组
void printArray(DynamicArray* arr) {
	if (arr == NULL) {
		return;
	}

	for (int i = 0; i < arr->size; i++) {
		printf("%d ", arr->paddr[i]);
	}
	printf("\n");
}

//清空数组
void clearArray(DynamicArray* arr) {
	if (arr == NULL) {
		return;
	}
	
	//空间值赋0,代表清空数组
	arr->size = 0;
}

//capacity 这块内存空间一共可以存放多少元素
int capacityArray(DynamicArray* arr) {
	if (arr == NULL) {
		return -1;
	}
	return arr->capacity;
}

//size 记录当前数组中具体的元素个数
int sizeArray(DynamicArray* arr) {
	if (arr == NULL) {
		return -1;
	}
	return arr->size;
}

//根据位置获取每个位置信息
int atIndex(DynamicArray* arr, int pos) {
	if (arr == NULL) {
		return -1;
	}
	return arr->paddr[pos];
}

 3、测试

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DynamicArray.h"

void test01() {
	//初始化动态数组
	DynamicArray* myArray = initArray();
	printf("数组容量: %d\n", capacityArray(myArray));
	printf("数组元素个数:%d\n", sizeArray(myArray));
	//插入元素
	for (int i = 0; i < 100; i++) {
		push_back(myArray, i);
	}

	//打印
	printArray(myArray);
	printf("数组容量: %d\n", capacityArray(myArray));
	printf("数组元素个数:%d\n", sizeArray(myArray));
	//销毁
	freeSpace(myArray);
}

int main(void) {
	test01();

	system("pause");
	return 0;
}

二、线性表的链式存储

1、头文件

#ifndef SINGLELIST_H
#define SINGLELIST_H

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

//线性表的链式存储
//链表节点
typedef struct LINKNODE {
	void* data; //无类型指针可以指向任何类型数据
	struct LINKNODE* next;
}LinkNode;

//链表结构体
typedef struct LINKLIST {
	LinkNode* head; //头结点
	int size; //链表是按需创建空间,所以不需要实现分配容量
}LinkList;

//打印函数指针
typedef void(*PRINTLINKNODE)(void*);

//初始化链表
LinkList* initLinkList();
//指定位置插入
void insertLinkList(LinkList* list, int pos, void* data);
//删除指定位置的值
void removeByPos(LinkList* list, int pos);
//获取链表的长度
int size(LinkList* list);
//返回第一个节点的数据
void* getFront(LinkList* list);
//释放链表内存
void freeSpaceLinkList(LinkList* list);
//查找某个值
int findByPos(LinkList* list, void* data);
//打印链表节点
void printLinkList(LinkList* list, PRINTLINKNODE print);
#endif

2、实现部分

#include "SingleList.h"

//初始化链表
LinkList* initLinkList() {
	//初始化就是对链表结构体进行操作
	LinkList* list = (LinkList*)malloc(sizeof(LinkList));
	list->size = 0;

	//头节点不保存数据信息,只是为了链表实现起来思路清晰
	list->head = (LinkNode*)malloc(sizeof(LinkNode));
	list->head->data = NULL;
	list->head->next = NULL;

	return list;
}

//指定位置插入
void insertLinkList(LinkList* list, int pos, void* data){
	if (list == NULL) {
		return;
	}
	if (data == NULL) {
		return;
	}
	//友好的处理,pos越界,插入尾部
	if (pos < 0 || pos >= list->size) {
		pos = list->size;
	}

	//插入节点
	//1、创建新的节点
	LinkNode* newnode = (LinkNode*)malloc(sizeof(LinkNode));
	newnode->data = data;
	newnode->next = NULL;
	//2、插入新节点
	//先找到待插入位置节点的前驱
	LinkNode* pCurrent = list->head;
	for (int i = 0; i < pos; i++) {
		pCurrent = pCurrent->next;
	}
	//3、新节点入链表
	newnode->next = pCurrent->next;
	pCurrent->next = newnode;
	list->size++;
}

//删除指定位置的值
void removeByPos(LinkList* list, int pos) {
	if (list == NULL) {
		return;
	}

	if (pos < 0 || pos >= list->size) {
		return;
	}

	//查找删除节点的前驱
	//这里是因为存在一个无数据节点head,所以这种方式可以遍历找到前驱
	LinkNode* pCurrent = list->head;
	for (int i = 0; i < pos; i++) {
		pCurrent = pCurrent->next;
	}
	//缓存待删除节点
	LinkNode* pDel = pCurrent->next;
	pCurrent->next = pDel->next;
	//释放删除节点的内存
	free(pDel);

	list->size--;
}

//获取链表的长度
int size(LinkList* list){
	return list->size;
}

//返回第一个节点的数据
void* getFront(LinkList* list){
	return list->head->next->data;
}

//释放链表内存
void freeSpaceLinkList(LinkList* list){
	if (list == NULL) {
		return;
	}

	//辅助指针
	LinkNode* pCurrent = list->head;
	while (pCurrent != NULL) {
		//先缓存下一个节点,再删除当前节点
		LinkNode* pNext = pCurrent->next;
		free(pCurrent); //释放当前节点
		pCurrent = pNext;
	}

	//释放链表内存
	list->size = 0;
	free(list);
}

//查找某个值
int findByPos(LinkList* list, void* data){
	if (list == NULL) {
		return -1;
	}
	if (data == NULL) {
		return -1;
	}
	//遍历查找
	LinkNode* pCurrent = list->head->next;
	int i = 0;
	while (pCurrent != NULL) {
		if (pCurrent->data == data) {
			break;
		}
		i++;
		pCurrent = pCurrent->next;
	}

	return i; //返回位置
}

//打印链表节点
void printLinkList(LinkList* list, PRINTLINKNODE print){
	if (list == NULL) {
		return;
	}

	//定义辅助指针变量
	LinkNode* pCurrent = list->head->next;
	while (pCurrent != NULL) {
		print(pCurrent->data);
		pCurrent = pCurrent->next;
	}
}

3、测试

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SingleList.h"

//测试数据
typedef struct PERSON {
	char name[64];
	int age;
	int score;
}Person;

//打印函数
void MyPrint(void* data) {
	Person* p = (Person*)data;
	printf("Name: %s, age: %d, score: %d\n", p->name, p->age, p->score);
}

int main(void) {
	//创建链表
	LinkList* list = initLinkList();

	//创建数据
	Person p1 = { "aaa", 18, 100 };
	Person p2 = { "bbb", 28, 90 };
	Person p3 = { "ccc", 38, 80 };
	Person p4 = { "ddd", 48, 110 };

	//数据插入链表
	insertLinkList(list, 0, &p1);
	insertLinkList(list, 0, &p2);
	insertLinkList(list, 0, &p3);
	insertLinkList(list, 0, &p4);

	//打印
	printf("======================\n");
	printLinkList(list, MyPrint);

	//链表第一个节点
	printf("头结点数据为:\n");
	Person* ret = (Person*)getFront(list);
	printf("Name: %s, age: %d, score: %d\n", ret->name, ret->age, ret->score);

	//销毁列表
	freeSpaceLinkList(list);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Star星屹程序设计

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

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

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

打赏作者

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

抵扣说明:

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

余额充值